Test Failed
Push — main ( a2096a...c7561b )
by Bingo
15:21
created

DbSqlSession::isConcurrentModificationException()   B

Complexity

Conditions 9
Paths 4

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 25
rs 8.0555
c 0
b 0
f 0
cc 9
nc 4
nop 2
1
<?php
2
3
namespace Jabe\Engine\Impl\Db\Sql;
4
5
use Doctrine\DBAL\Connection;
6
use Jabe\Engine\ProcessEngineInterface;
7
use Jabe\Engine\Impl\ProcessEngineLogger;
8
use Jabe\Engine\Impl\Cfg\ProcessEngineConfigurationImpl;
9
use Jabe\Engine\Impl\Context\Context;
10
use Jabe\Engine\Impl\Db\{
11
    AbstractPersistenceSession,
12
    DbEntityInterface,
13
    EnginePersistenceLogger,
14
    HasDbReferencesInterface,
15
    HasDbRevisionInterface,
16
    SqlSessionInterface,
17
    SqlSessionFactoryInterface
18
};
19
use Jabe\Engine\Impl\Db\EntityManager\Operation\{
20
    DbBulkOperation,
21
    DbEntityOperation,
22
    DbOperation,
23
    DbOperationState,
24
    DbOperationType
25
};
26
use Jabe\Engine\Impl\Util\{
27
    DatabaseUtil,
28
    EnsureUtil,
29
    ExceptionUtil,
30
    IoUtil,
31
    ReflectUtil
32
};
33
34
abstract class DbSqlSession extends AbstractPersistenceSession
35
{
36
    //protected static final EnginePersistenceLogger LOG = ProcessEngineLogger.PERSISTENCE_LOGGER;
37
    public const DBC_METADATA_TABLE_TYPES = [ "TABLE" ];
38
    public const PG_DBC_METADATA_TABLE_TYPES = [ "TABLE", "PARTITIONED TABLE" ];
39
40
    protected $sqlSession;
41
    protected $dbSqlSessionFactory;
42
43
    protected $connectionMetadataDefaultCatalog = null;
44
    protected $connectionMetadataDefaultSchema = null;
45
46
    public function __construct(DbSqlSessionFactory $dbSqlSessionFactory, Connection $connection = null, string $catalog = null, string $schema = null)
47
    {
48
        $this->dbSqlSessionFactory = $dbSqlSessionFactory;
49
        $sqlSessionFactory = $this->dbSqlSessionFactory->getSqlSessionFactory();
50
        if ($connection !== null) {
51
            $this->sqlSession = ExceptionUtil::doWithExceptionWrapper(function () use ($sqlSessionFactory, $connection) {
52
                return $sqlSessionFactory->openSession($connection);
53
            });
54
            $this->connectionMetadataDefaultCatalog = $catalog;
55
            $this->connectionMetadataDefaultSchema = $schema;
56
        } else {
57
            $this->sqlSession = ExceptionUtil::doWithExceptionWrapper(function () use ($sqlSessionFactory) {
58
                return $sqlSessionFactory->openSession();
59
            });
60
        }
61
    }
62
63
    // select ////////////////////////////////////////////
64
65
    public function selectList(string $statement, array $params = [], array $types = [])
66
    {
67
        $statement = $this->dbSqlSessionFactory->mapStatement($statement);
68
        $resultList = $this->executeSelectList($statement, $params, $types);
69
        foreach ($resultList as $object) {
70
            $this->fireEntityLoaded($object);
71
        }
72
        return $resultList;
73
    }
74
75
    public function executeSelectList(string $statement, array $params = [], array $types = []): array
76
    {
77
        $scope = $this;
78
        return ExceptionUtil::doWithExceptionWrapper(function () use ($scope, $statement, $params, $types) {
79
            return $scope->sqlSession->selectList($statement, $params, $types);
80
        });
81
    }
82
83
    public function selectById(string $type, string $id)
84
    {
85
        $selectStatement = $this->dbSqlSessionFactory->getSelectStatement($type);
86
        $mappedSelectStatement = $this->dbSqlSessionFactory->mapStatement($selectStatement);
87
        EnsureUtil::ensureNotNull("no select statement for " . $type . " in the ibatis mapping files", "selectStatement", $selectStatement);
88
89
        $scope = $this;
90
        $result = ExceptionUtil::doWithExceptionWrapper(function () use ($scope, $mappedSelectStatement, $id) {
91
            return $scope->sqlSession->selectOne($mappedSelectStatement, $id);
92
        });
93
        $this->fireEntityLoaded($result);
94
        return $result;
95
    }
96
97
    public function selectOne(string $statement, array $params = [], array $types = [])
98
    {
99
        $scope = $this;
100
        $mappedStatement = $this->dbSqlSessionFactory->mapStatement($statement);
0 ignored issues
show
Unused Code introduced by
The assignment to $mappedStatement is dead and can be removed.
Loading history...
101
        $result = ExceptionUtil::doWithExceptionWrapper(function () use ($scope, $mappedStatementt, $params, $types) {
0 ignored issues
show
Unused Code introduced by
The import $mappedStatementt is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Comprehensibility Best Practice introduced by
The variable $mappedStatementt does not exist. Did you maybe mean $mappedStatement?
Loading history...
102
            return $scope->sqlSession->selectOne($mappedStatement, $params, $types);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mappedStatement does not exist. Did you maybe mean $mappedStatementt?
Loading history...
103
        });
104
        $this->fireEntityLoaded($result);
105
        return $result;
106
    }
107
108
    // lock ////////////////////////////////////////////
109
110
    public function lock(string $statement, array $params = [], array $types = []): void
111
    {
112
        // do not perform locking if H2 database is used. H2 uses table level locks
113
        // by default which may cause deadlocks if the deploy command needs to get a new
114
        // Id using the DbIdGenerator while performing a deployment.
115
        //
116
        // On CockroachDB, pessimistic locks are disabled since this database uses
117
        // a stricter, SERIALIZABLE transaction isolation which ensures a serialized
118
        // manner of transaction execution, making our use-case of pessimistic locks
119
        // redundant.
120
        /*if (!DatabaseUtil.checkDatabaseType(DbSqlSessionFactory.CRDB, DbSqlSessionFactory.H2)) {
121
            String mappedStatement = dbSqlSessionFactory.mapStatement(statement);
122
            executeSelectForUpdate(mappedStatement, parameter);
123
        } else {
124
            LOG.debugDisabledPessimisticLocks();
125
        }*/
126
        $mappedStatement = $this->dbSqlSessionFactory->mapStatement($statement);
127
        $this->executeSelectForUpdate($mappedStatement, $params, $types);
128
    }
129
130
    abstract protected function executeSelectForUpdate(string $statement, array $params = [], array $types = []): void;
131
132
    protected function entityUpdatePerformed(
133
        DbEntityOperation $operation,
134
        int $rowsAffected,
135
        \Exception $failure = null
136
    ): void {
137
        if ($failure != null) {
138
            $this->configureFailedDbEntityOperation($operation, $failure);
139
        } else {
140
            $dbEntity = $operation->getEntity();
141
142
            if ($dbEntity instanceof HasDbRevisionInterface) {
143
                if ($rowsAffected != 1) {
144
                    // failed with optimistic locking
145
                    $operation->setState(DbOperationState::FAILED_CONCURRENT_MODIFICATION);
146
                } else {
147
                    // increment revision of our copy
148
                    $versionedObject = $dbEntity;
149
                    $versionedObject->setRevision($versionedObject->getRevisionNext());
150
                    $operation->setState(DbOperationState::APPLIED);
151
                }
152
            } else {
153
                $operation->setState(DbOperationState::APPLIED);
154
            }
155
        }
156
    }
157
158
    protected function bulkUpdatePerformed(
159
        DbBulkOperation $operation,
160
        int $rowsAffected,
161
        \Exception $failure = null
162
    ): void {
163
        $this->bulkOperationPerformed($operation, $rowsAffected, $failure);
164
    }
165
166
    protected function bulkDeletePerformed(
167
        DbBulkOperation $operation,
168
        int $rowsAffected,
169
        \Exception $failure = null
170
    ): void {
171
        bulkOperationPerformed($operation, $rowsAffected, $failure);
0 ignored issues
show
Bug introduced by
The function bulkOperationPerformed was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

171
        /** @scrutinizer ignore-call */ 
172
        bulkOperationPerformed($operation, $rowsAffected, $failure);
Loading history...
172
    }
173
174
    protected function bulkOperationPerformed(
175
        DbBulkOperation $operation,
176
        int $rowsAffected,
177
        \Exception $failure = null
178
    ): void {
179
        if ($failure != null) {
180
            $operation->setFailure($failure);
181
            $failedState = DbOperationState::FAILED_ERROR;
182
            /*if (isCrdbConcurrencyConflict(failure)) {
183
                failedState = State.FAILED_CONCURRENT_MODIFICATION_CRDB;
184
            }*/
185
            $operation->setState($failedState);
186
        } else {
187
            $operation->setRowsAffected($rowsAffected);
188
            $operation->setState(DbOperationState::APPLIED);
189
        }
190
    }
191
192
    protected function entityDeletePerformed(
193
        DbEntityOperation $operation,
194
        int $rowsAffected,
195
        \Exception $failure = null
196
    ): void {
197
        if ($failure != null) {
198
            $this->configureFailedDbEntityOperation($operation, $failure);
199
        } else {
200
            $operation->setRowsAffected($rowsAffected);
201
202
            $dbEntity = $operation->getEntity();
203
204
            // It only makes sense to check for optimistic locking exceptions for objects that actually have a revision
205
            if ($dbEntity instanceof HasDbRevisionInterface && $rowsAffected == 0) {
206
                $operation->setState(DbOperationState::FAILED_CONCURRENT_MODIFICATION);
207
            } else {
208
                $operation->setState(DbOperationState::APPLIED);
209
            }
210
        }
211
    }
212
213
    protected function configureFailedDbEntityOperation(DbEntityOperation $operation, \Exception $failure = null): void
214
    {
215
        $operation->setRowsAffected(0);
216
        $operation->setFailure($failure);
0 ignored issues
show
Bug introduced by
It seems like $failure can also be of type null; however, parameter $failure of Jabe\Engine\Impl\Db\Enti...Operation::setFailure() does only seem to accept Exception, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

216
        $operation->setFailure(/** @scrutinizer ignore-type */ $failure);
Loading history...
217
218
        $operationType = $operation->getOperationType();
0 ignored issues
show
Unused Code introduced by
The assignment to $operationType is dead and can be removed.
Loading history...
219
        $dependencyOperation = $operation->getDependentOperation();
220
221
        $failedState = null;
222
        /*if (isCrdbConcurrencyConflict(failure)) {
223
            failedState = State.FAILED_CONCURRENT_MODIFICATION_CRDB;
224
        } else*/
225
        if ($this->isConcurrentModificationException($operation, $failure)) {
226
            $failedState = DbOperationState::FAILED_CONCURRENT_MODIFICATION;
227
        } elseif (
228
            DbOperationType::DELETE == $perationType
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $perationType does not exist. Did you maybe mean $operationType?
Loading history...
229
            && $dependencyOperation != null
230
            && $dependencyOperation->getState() != null
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $dependencyOperation->getState() of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
231
            && $dependencyOperation->getState() != DbOperationState::APPLIED
232
        ) {
233
            // the owning operation was not successful, so the prerequisite for this operation was not given
234
            //LOG.ignoreFailureDuePreconditionNotMet(operation, "Parent database operation failed", dependencyOperation);
235
            $failedState = DbOperationState::NOT_APPLIED;
236
        } else {
237
            $failedState = DbOperationState::FAILED_ERROR;
238
        }
239
        $operation->setState($failedState);
240
    }
241
242
    protected function isConcurrentModificationException(
243
        DbOperation $failedOperation,
244
        \Exception $cause = null
245
    ): bool {
246
        $isConstraintViolation = ExceptionUtil::checkForeignKeyConstraintViolation($cause);
247
        $isVariableIntegrityViolation = ExceptionUtil::checkVariableIntegrityViolation($cause);
248
249
        if ($isVariableIntegrityViolation) {
250
            return true;
251
        } elseif (
252
            $isConstraintViolation
253
            && $failedOperation instanceof DbEntityOperation
254
            && $failedOperation->getEntity() instanceof HasDbReferencesInterface
255
            && ($failedOperation->getOperationType() == DbOperationType::INSERT
256
            || $failedOperation->getOperationType() == DbOperationType::UPDATE)
257
        ) {
258
            $entity = $failedOperation->getEntity();
259
            foreach ($entity->getReferencedEntitiesIdAndClass() as $key => $value) {
260
                $referencedEntity = $this->selectById($value, $key);
261
                if ($referencedEntity == null) {
262
                    return true;
263
                }
264
            }
265
        }
266
        return false;
267
    }
268
269
    /**
270
     * In cases where CockroachDB is used, and a failed operation is detected,
271
     * the method checks if the exception was caused by a CockroachDB
272
     * <code>TransactionRetryException</code>.
273
     *
274
     * @param cause for which an operation failed
275
     * @return true if the failure was due to a CRDB <code>TransactionRetryException</code>.
276
     *          Otherwise, it's false.
277
     */
278
    /*public static function isCrdbConcurrencyConflict(Throwable cause): bool
279
    {
280
        // only check when CRDB is used
281
        if (DatabaseUtil.checkDatabaseType(DbSqlSessionFactory.CRDB)) {
282
            boolean isCrdbTxRetryException = ExceptionUtil.checkCrdbTransactionRetryException(cause);
283
            if (isCrdbTxRetryException) {
284
                return true;
285
            }
286
        }
287
288
        return false;
289
    }*/
290
291
    /**
292
     * In cases where CockroachDB is used, and a failed operation is detected,
293
     * the method checks if the exception was caused by a CockroachDB
294
     * <code>TransactionRetryException</code>. This method may be used when a
295
     * CRDB Error occurs on commit, and a Command Context is not available, as
296
     * it has already been closed. This is the case with Spring/JTA transaction
297
     * interceptors.
298
     *
299
     * @param cause for which an operation failed
300
     * @param configuration of the Process Engine
301
     * @return true if the failure was due to a CRDB <code>TransactionRetryException</code>.
302
     *          Otherwise, it's false.
303
     */
304
    /*public static boolean isCrdbConcurrencyConflictOnCommit(Throwable cause, ProcessEngineConfigurationImpl configuration) {
305
      // only check when CRDB is used
306
      if (DatabaseUtil.checkDatabaseType(configuration, DbSqlSessionFactory.CRDB)) {
307
        // with Java EE (JTA) transactions, the real cause is suppressed,
308
        // and replaced with a RollbackException. We need to look into the
309
        // suppressed exceptions to find the CRDB TransactionRetryError.
310
        List<Throwable> causes = new ArrayList<>(Arrays.asList(cause.getSuppressed()));
311
        causes.add(cause);
312
        for (Throwable throwable : causes) {
313
          if (ExceptionUtil.checkCrdbTransactionRetryException(throwable)) {
314
            return true;
315
          }
316
        }
317
      }
318
      return false;
319
    }*/
320
321
    // insert //////////////////////////////////////////
322
323
    protected function insertEntity(DbEntityOperation $operation): void
324
    {
325
        $dbEntity = $operation->getEntity();
326
327
        // get statement
328
        $insertStatement = $this->dbSqlSessionFactory->getInsertStatement($dbEntity);
0 ignored issues
show
Bug introduced by
It seems like $dbEntity can also be of type null; however, parameter $object of Jabe\Engine\Impl\Db\Sql\...y::getInsertStatement() does only seem to accept Jabe\Engine\Impl\Db\DbEntityInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
        $insertStatement = $this->dbSqlSessionFactory->getInsertStatement(/** @scrutinizer ignore-type */ $dbEntity);
Loading history...
329
        $insertStatement = $this->dbSqlSessionFactory->mapStatement($insertStatement);
330
        EnsureUtil::ensureNotNull("no insert statement for " . get_class($dbEntity) . " in the mapping files", "insertStatement", $insertStatement);
0 ignored issues
show
Bug introduced by
It seems like $dbEntity can also be of type null; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

330
        EnsureUtil::ensureNotNull("no insert statement for " . get_class(/** @scrutinizer ignore-type */ $dbEntity) . " in the mapping files", "insertStatement", $insertStatement);
Loading history...
331
332
        // execute the insert
333
        $this->executeInsertEntity($insertStatement, $dbEntity);
334
    }
335
336
    protected function executeInsertEntity(string $insertStatement, $parameter): void
337
    {
338
        //LOG.executeDatabaseOperation("INSERT", parameter);
339
        try {
340
            $this->sqlSession->insert($insertStatement, $parameter);
341
        } catch (\Exception $e) {
342
            // exception is wrapped later
343
            throw $e;
344
        }
345
    }
346
347
    protected function entityInsertPerformed(
348
        DbEntityOperation $operation,
349
        int $rowsAffected,
0 ignored issues
show
Unused Code introduced by
The parameter $rowsAffected is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

349
        /** @scrutinizer ignore-unused */ int $rowsAffected,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
350
        \Exception $failure = null
351
    ): void {
352
        $entity = $operation->getEntity();
353
354
        if ($failure != null) {
355
            $this->configureFailedDbEntityOperation($operation, $failure);
356
        } else {
357
            // set revision of our copy to 1
358
            if ($entity instanceof HasDbRevisionInterface) {
359
                $versionedObject = $entity;
360
                $versionedObject->setRevision(1);
361
            }
362
363
            $operation->setState(DbOperationState::APPLIED);
364
        }
365
    }
366
367
    // delete ///////////////////////////////////////////
368
369
    protected function executeDelete(string $deleteStatement, $parameter)
370
    {
371
        // map the statement
372
        $mappedDeleteStatement = $this->dbSqlSessionFactory->mapStatement($deleteStatement);
373
        try {
374
            return $this->sqlSession->delete($mappedDeleteStatement, $parameter);
375
        } catch (\Exception $e) {
376
            // Exception is wrapped later
377
            throw $e;
378
        }
379
    }
380
381
    // update ////////////////////////////////////////
382
383
    public function executeUpdate(string $updateStatement, $parameter)
384
    {
385
        $mappedUpdateStatement = $this->dbSqlSessionFactory->mapStatement($updateStatement);
386
        try {
387
            return $this->sqlSession->update($mappedUpdateStatement, $parameter);
388
        } catch (\Exception $e) {
389
            // Exception is wrapped later
390
            throw $e;
391
        }
392
    }
393
394
    public function update(string $updateStatement, $parameter)
395
    {
396
        $scope = $this;
397
        return ExceptionUtil::doWithExceptionWrapper(function () use ($scope, $updateStatement, $parameter) {
0 ignored issues
show
Unused Code introduced by
The import $scope is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
398
            return $this->sqlSession->update($updateStatement, $parameter);
399
        });
400
    }
401
402
    public function executeNonEmptyUpdateStmt(string $updateStmt, $parameter): int
403
    {
404
        $mappedUpdateStmt = $this->dbSqlSessionFactory->mapStatement($updateStmt);
405
406
        //if mapped statement is empty, which can happens for some databases, we have no need to execute it
407
        /*$isMappedStmtEmpty = ExceptionUtil::doWithExceptionWrapper(function () use ($scope, $mappedUpdateStmt, $parameter) {
408
            $configuration = $scope->sqlSession->getConfiguration();
409
            $mappedStatement = $configuration->getMappedStatement($mappedUpdateStmt);
410
            $boundSql = $mappedStatement->getBoundSql($parameter);
411
            $sql = $boundSql->getSql();
412
            return $sql->isEmpty();
413
        });
414
415
        if (isMappedStmtEmpty) {
416
            return 0;
417
        }*/
418
419
        return $this->update($mappedUpdateStmt, $parameter);
420
    }
421
422
    // flush ////////////////////////////////////////////////////////////////////
423
424
    public function flush(): void
425
    {
426
    }
427
428
    public function flushOperations(): void
429
    {
430
        $scope = $this;
431
        ExceptionUtil::doWithExceptionWrapper(function () use ($scope) {
432
            $scope->flushBatchOperations();
433
        });
434
    }
435
436
    public function flushBatchOperations(): array
437
    {
438
        try {
439
            return $this->sqlSession->flushStatements();
440
        } catch (\Exception $ex) {
441
            // exception is wrapped later
442
            throw $ex;
443
        }
444
    }
445
446
    public function close(): void
447
    {
448
        $scope = $this;
449
        ExceptionUtil::doWithExceptionWrapper(function () use ($scope) {
450
            $scope->sqlSession->close();
451
            return null;
452
        });
453
    }
454
455
    public function commit(): void
456
    {
457
        $scope = $this;
458
        ExceptionUtil::doWithExceptionWrapper(function () use ($scope) {
459
            $scope->sqlSession->commit();
460
            return null;
461
        });
462
    }
463
464
    public function rollback(): void
465
    {
466
        $scope = $this;
467
        ExceptionUtil::doWithExceptionWrapper(function () use ($scope) {
468
            $scope->sqlSession->rollback();
469
            return null;
470
        });
471
    }
472
473
    // schema operations ////////////////////////////////////////////////////////
474
475
    public function dbSchemaCheckVersion(): void
476
    {
477
        try {
478
            $dbVersion = $this->getDbVersion();
479
            if (!ProcessEngineInterface::VERSION == $dbVersion) {
480
                //throw LOG.wrongDbVersionException(ProcessEngine.VERSION, dbVersion);
481
                throw new \Exception("wrongDbVersionException");
482
            }
483
484
            /*$missingComponents = [];
485
            if (!$this->isEngineTablePresent()) {
486
                $missingComponents[] = "engine";
487
            }
488
            if ($this->dbSqlSessionFactory->isDbHistoryUsed() && !$this->isHistoryTablePresent()) {
489
                $missingComponents[] = "history";
490
            }
491
            if ($this->dbSqlSessionFactory->isDbIdentityUsed() && !$this->isIdentityTablePresent()) {
492
                $missingComponents[] = "identity";
493
            }
494
            if ($this->dbSqlSessionFactory.isCmmnEnabled() && !isCmmnTablePresent()) {
495
                missingComponents.add("case.engine");
496
            }
497
            if (dbSqlSessionFactory.isDmnEnabled() && !isDmnTablePresent()) {
498
                missingComponents.add("decision.engine");
499
            }
500
501
            if (!empty($missingComponents)) {
502
                //throw LOG.missingTableException(missingComponents);
503
                throw new \Exception("missingTableException");
504
            }*/
505
        } catch (\Exception $e) {
506
            throw $e;
507
            /*if ($this->isMissingTablesException($e)) {
508
                //throw LOG.missingActivitiTablesException();
509
                throw new \Exception("missingActivitiTablesException");
510
            } else {
511
                throw $e;
512
                if ($e instanceof RuntimeException) {
513
                    throw (RuntimeException) e;
514
                } else {
515
                    throw LOG.unableToFetchDbSchemaVersion(e);
516
                }
517
            }*/
518
        }
519
    }
520
521
    protected function getDbVersion(): string
522
    {
523
        $selectSchemaVersionStatement = $this->dbSqlSessionFactory->mapStatement("selectDbSchemaVersion");
0 ignored issues
show
Unused Code introduced by
The assignment to $selectSchemaVersionStatement is dead and can be removed.
Loading history...
524
        $scope = $this;
525
        return ExceptionUtil::doWithExceptionWrapper(function () use ($scope) {
0 ignored issues
show
Unused Code introduced by
The import $scope is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
526
            return $this->sqlSession->selectOne($selectSchemaVersionStatement);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $selectSchemaVersionStatement seems to be never defined.
Loading history...
527
        });
528
    }
529
530
    /*protected function dbSchemaCreateIdentity(): void
531
    {
532
        $this->executeMandatorySchemaResource("create", "identity");
533
    }
534
535
    protected function dbSchemaCreateHistory(): void
536
    {
537
        $this->executeMandatorySchemaResource("create", "history");
538
    }
539
540
    protected function dbSchemaCreateEngine(): void
541
    {
542
        $this->executeMandatorySchemaResource("create", "engine");
543
    }
544
545
    protected function dbSchemaCreateCmmn(): void
546
    {
547
        $this->executeMandatorySchemaResource("create", "case.engine");
548
    }
549
550
    protected function dbSchemaCreateCmmnHistory(): void
551
    {
552
        $this->executeMandatorySchemaResource("create", "case.history");
553
    }
554
555
    protected function dbSchemaCreateDmn(): void
556
    {
557
        executeMandatorySchemaResource("create", "decision.engine");
558
    }
559
560
    protected void dbSchemaCreateDmnHistory() {
561
      executeMandatorySchemaResource("create", "decision.history");
562
    }
563
564
    protected function dbSchemaDropIdentity(): void
565
    {
566
        $this->executeMandatorySchemaResource("drop", "identity");
567
    }
568
569
    protected function dbSchemaDropHistory(): void
570
    {
571
        $this->executeMandatorySchemaResource("drop", "history");
572
    }
573
574
    protected function dbSchemaDropEngine(): void
575
    {
576
        $this->executeMandatorySchemaResource("drop", "engine");
577
    }
578
579
    protected function dbSchemaDropCmmn(): void
580
    {
581
        executeMandatorySchemaResource("drop", "case.engine");
582
    }
583
584
    protected void dbSchemaDropCmmnHistory() {
585
        executeMandatorySchemaResource("drop", "case.history");
586
    }
587
588
    protected function dbSchemaDropDmn(): void
589
    {
590
        executeMandatorySchemaResource("drop", "decision.engine");
591
    }
592
593
    protected void dbSchemaDropDmnHistory() {
594
      executeMandatorySchemaResource("drop", "decision.history");
595
    }
596
597
    public function executeMandatorySchemaResource(string $operation, string $component): void
598
    {
599
        $this->executeSchemaResource($operation, $component, $this->getResourceForDbOperation($operation, $operation, $component), false);
600
    }*/
601
602
    /*public function isEngineTablePresent(): bool
603
    {
604
        return $this->isTablePresent("ACT_RU_EXECUTION");
605
    }
606
607
    public function isHistoryTablePresent(): bool
608
    {
609
        return $this->isTablePresent("ACT_HI_PROCINST");
610
    }
611
612
    public function isIdentityTablePresent(): bool
613
    {
614
        return $this->isTablePresent("ACT_ID_USER");
615
    }
616
617
    public function isCmmnTablePresent(): bool
618
    {
619
        return isTablePresent("ACT_RE_CASE_DEF");
620
    }
621
622
    public boolean isCmmnHistoryTablePresent() {
623
      return isTablePresent("ACT_HI_CASEINST");
624
    }
625
626
    public boolean isDmnTablePresent() {
627
      return isTablePresent("ACT_RE_DECISION_DEF");
628
    }
629
630
    public boolean isDmnHistoryTablePresent() {
631
      return isTablePresent("ACT_HI_DECINST");
632
    }*/
633
634
    public function isTablePresent(string $tableName): bool
635
    {
636
        $connection = $this->sqlSession->getConnection();
637
        $schemaManager = $connection->getSchemaManager();
638
        return $schemaManager->tablesExist([$tableName]);
639
    }
640
641
    public function getTableNamesPresent(): array
642
    {
643
        $connection = $this->sqlSession->getConnection();
644
        $schemaManager = $connection->getSchemaManager();
645
        return $schemaManager->listTableNames();
646
    }
647
648
    /*public String getResourceForDbOperation(String directory, String operation, String component) {
649
        String databaseType = dbSqlSessionFactory.getDatabaseType();
650
        return "org/camunda/bpm/engine/db/" + directory + "/activiti." + databaseType + "." + operation + "."+component+".sql";
651
    }
652
653
    public void executeSchemaResource(String operation, String component, String resourceName, boolean isOptional) {
654
        InputStream inputStream = null;
655
        try {
656
            inputStream = ReflectUtil.getResourceAsStream(resourceName);
657
            if (inputStream == null) {
658
            if (isOptional) {
659
                LOG.missingSchemaResource(resourceName, operation);
660
            } else {
661
                throw LOG.missingSchemaResourceException(resourceName, operation);
662
            }
663
            } else {
664
            executeSchemaResource(operation, component, resourceName, inputStream);
665
            }
666
667
        } finally {
668
            IoUtil.closeSilently(inputStream);
669
        }
670
    }
671
672
    public void executeSchemaResource(String schemaFileResourceName) {
673
        FileInputStream inputStream = null;
674
        try {
675
            inputStream = new FileInputStream(new File(schemaFileResourceName));
676
            executeSchemaResource("schema operation", "process engine", schemaFileResourceName, inputStream);
677
        } catch (FileNotFoundException e) {
678
            throw LOG.missingSchemaResourceFileException(schemaFileResourceName, e);
679
        } finally {
680
            IoUtil.closeSilently(inputStream);
681
        }
682
    }
683
684
    private void executeSchemaResource(String operation, String component, String resourceName, InputStream inputStream) {
685
        String sqlStatement = null;
686
        String exceptionSqlStatement = null;
687
        try {
688
            Connection connection = ExceptionUtil.doWithExceptionWrapper(() -> $this->sqlSession->getConnection());
689
            Exception exception = null;
690
            byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
691
            String ddlStatements = new String(bytes);
692
            BufferedReader reader = new BufferedReader(new StringReader(ddlStatements));
693
            String line = readNextTrimmedLine(reader);
694
695
            List<String> logLines = new ArrayList<>();
696
697
            while (line != null) {
698
                if (line.startsWith("# ")) {
699
                    logLines.add(line.substring(2));
700
                } else if (line.startsWith("-- ")) {
701
                    logLines.add(line.substring(3));
702
                } else if (line.length()>0) {
703
                    if (line.endsWith(";")) {
704
                        sqlStatement = addSqlStatementPiece(sqlStatement, line.substring(0, line.length()-1));
705
                        try {
706
                            Statement jdbcStatement = connection.createStatement();
707
                            // no logging needed as the connection will log it
708
                            logLines.add(sqlStatement);
709
                            jdbcStatement.execute(sqlStatement);
710
                            jdbcStatement.close();
711
                        } catch (Exception e) {
712
                            if (exception == null) {
713
                                exception = e;
714
                                exceptionSqlStatement = sqlStatement;
715
                            }
716
                            LOG.failedDatabaseOperation(operation, sqlStatement, e);
717
                        } finally {
718
                            sqlStatement = null;
719
                        }
720
                    } else {
721
                        sqlStatement = addSqlStatementPiece(sqlStatement, line);
722
                    }
723
                }
724
                line = readNextTrimmedLine(reader);
725
            }
726
            LOG.performingDatabaseOperation(operation, component, resourceName);
727
            LOG.executingDDL(logLines);
728
729
            if (exception != null) {
730
            throw exception;
731
            }
732
733
            LOG.successfulDatabaseOperation(operation, component);
734
        } catch (Exception e) {
735
            throw LOG.performDatabaseOperationException(operation, exceptionSqlStatement, e);
736
        }
737
    }
738
739
    protected String addSqlStatementPiece(String sqlStatement, String line) {
740
        if (sqlStatement==null) {
741
            return line;
742
        }
743
        return sqlStatement + " \n" + line;
744
    }
745
746
    protected function readNextTrimmedLine(BufferedReader reader): string
747
    {
748
        String line = reader.readLine();
749
        if (line!=null) {
750
            line = line.trim();
751
        }
752
        return line;
753
    }*/
754
755
    protected function isMissingTablesException(\Exception $e): bool
756
    {
757
        $cause = method_exists($e, "getCause") ? $e->getCause() : null;
758
        if ($cause != null) {
759
            $exceptionMessage = $cause->getMessage();
760
            if ($exceptionMessage != null) {
761
                // Matches message returned from H2
762
                /*if (str_contains($exceptionMessage, "Table") && (exceptionMessage.contains("not found"))) {
763
                    return true;
764
                }
765
766
                // Message returned from MySQL and Oracle
767
                if ((exceptionMessage.contains("Table") || exceptionMessage.contains("table")) && (exceptionMessage.contains("doesn't exist"))) {
768
                    return true;
769
                }*/
770
771
                // Message returned from Postgres
772
                return (str_contains($exceptionMessage, "relation") || str_contains($exceptionMessage, "table")) && str_contains($exceptionMessage, "does not exist");
773
            }
774
        }
775
        return false;
776
    }
777
778
    protected function getTableTypes(): array
779
    {
780
        // the PostgreSQL JDBC API changed in 42.2.11 and partitioned tables
781
        // are not detected unless the corresponding table type flag is added.
782
        /*if (DatabaseUtil.checkDatabaseType(DbSqlSessionFactory.POSTGRES)) {
783
            return PG_DBC_METADATA_TABLE_TYPES;
784
        }
785
        return DBC_METADATA_TABLE_TYPES;*/
786
        return self::PG_DBC_METADATA_TABLE_TYPES;
787
    }
788
789
    // getters and setters //////////////////////////////////////////////////////
790
791
    public function getSqlSession(): SqlSessionInterface
792
    {
793
        return $this->sqlSession;
794
    }
795
796
    public function getDbSqlSessionFactory(): DbSqlSessionFactory
797
    {
798
        return $this->dbSqlSessionFactory;
799
    }
800
}
801