diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index cda934ef..3edd9461 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,35 +10,46 @@ env: SIMPLETEST_BASE_URL: "http://localhost:8080" PHPUNIT_SKIP_CLASS: '[ "Drupal\\KernelTests\\Core\\Cache\\ApcuBackendTest", - "Drupal\\Tests\\file\\Functional\\FileAddPermissionsUpdateTest", - "Drupal\\Tests\\file\\Functional\\DownloadTest" + "Drupal\\Tests\\file\\Functional\\FileAddPermissionsUpdateTest" ]' jobs: ################################# - sqlite-pdo: - name: "SQLite with PDO" + oracle-oci8: + name: "Oracle on Oci8" runs-on: ubuntu-20.04 env: - DRUDBAL_ENV: "dbal/sqlite/file" - DBAL_URL: "sqlite://localhost/sites/drudbal.sqlite" - SIMPLETEST_DB: "dbal://localhost/sites/drudbal.sqlite?module=drudbal&dbal_driver=pdo_sqlite" + DRUDBAL_ENV: "dbal/oci8" + DBAL_URL: "oci8://DRUDBAL:ORACLE@0.0.0.0:1521/XE" + SIMPLETEST_DB: "dbal://DRUDBAL:ORACLE@0.0.0.0:1521/XE?module=drudbal&dbal_driver=oci8#dru" + + services: + oracle: + image: gvenzl/oracle-xe:slim-faststart + env: + ORACLE_PASSWORD: oracle + ports: + - "1521:1521" + options: >- + --health-cmd healthcheck.sh + --health-interval 20s + --health-timeout 10s + --health-retries 10 strategy: fail-fast: false matrix: php-version: -# - "8.1" - "8.2" test-args: - "--group Database" -# - "--group Entity" -# - "--group Cache,Config" -# - "--group field,Field" -# - "--group file" -# - "--group views" + # - "--group Entity" + # - "--group Cache,Config" + # - "--group field,Field" + # - "--group file" + # - "--group views" steps: - name: Install PHP @@ -46,6 +57,7 @@ jobs: with: php-version: "${{ matrix.php-version }}" coverage: "none" + extensions: "oci8" ini-values: "zend.assertions=1" - name: Checkout Drupal @@ -62,6 +74,7 @@ jobs: - name: Install Composer dependencies run: | composer install --no-progress --ansi + composer config --no-plugins allow-plugins.composer/package-versions-deprecated true - name: Composer require DruDbal from local staging run: | @@ -69,14 +82,20 @@ jobs: composer config repositories.test-run '{"type": "path", "url": "drudbal_staging", "options": {"symlink": false}}' composer require "mondrake/drudbal:dev-test-run-branch" --no-progress --ansi - - name: Install Drupal + - name: Create Oracle schema run: | cp modules/contrib/drudbal/tests/github/install_* . + php install_oracle.php + + - name: Install Drupal + continue-on-error: true + run: | vendor/bin/drush site-install standard --db-url=$SIMPLETEST_DB -y vendor/bin/drush runserver localhost:8080 --default-server=localhost:8080 & - sleep 1s + sleep 5s - name: Report installation + continue-on-error: true run: | php install_report.php vendor/bin/drush core:status diff --git a/src/Driver/Database/dbal/DbalExtension/AbstractExtension.php b/src/Driver/Database/dbal/DbalExtension/AbstractExtension.php index b1323362..61175c7b 100644 --- a/src/Driver/Database/dbal/DbalExtension/AbstractExtension.php +++ b/src/Driver/Database/dbal/DbalExtension/AbstractExtension.php @@ -63,6 +63,10 @@ public function delegateQueryExceptionProcess($query, array $args, array $option throw new \LogicException("Method " . __METHOD__ . "() not implemented."); } + public function delegateClientExecuteStatementException(DbalDriverException $e, string $sql, string $message): void { + throw new \LogicException("Method " . __METHOD__ . "() not implemented."); + } + /** * {@inheritdoc} */ diff --git a/src/Driver/Database/dbal/DbalExtension/DbalExtensionInterface.php b/src/Driver/Database/dbal/DbalExtension/DbalExtensionInterface.php index 7378db10..db484b49 100644 --- a/src/Driver/Database/dbal/DbalExtension/DbalExtensionInterface.php +++ b/src/Driver/Database/dbal/DbalExtension/DbalExtensionInterface.php @@ -287,7 +287,7 @@ public function delegateMapConditionOperator(string $operator): ?array; public function delegateNextId(int $existing_id = 0): int; /** - * Handles a DbalExceptions thrown by Connection::query(). + * Handles DbalExceptions thrown by Drupal's Connection::query(). * * @param string $query * A string containing the failing SQL query. @@ -307,6 +307,18 @@ public function delegateNextId(int $existing_id = 0): int; */ public function delegateQueryExceptionProcess($query, array $args, array $options, $message, DbalDriverException|DatabaseExceptionWrapper $e); + /** + * Handles exceptions thrown by DBAL Connection::executeStatement(). + * + * @param DbalDriverException $e + * The exception thrown by query(). + * @param string $sql + * A string containing the failing SQL query. + * @param string $message + * The message to be re-thrown. + */ + public function delegateClientExecuteStatementException(DbalDriverException $e, string $sql, string $message): void; + /** * Runs a limited-range query. * diff --git a/src/Driver/Database/dbal/DbalExtension/Oci8Extension.php b/src/Driver/Database/dbal/DbalExtension/Oci8Extension.php index 03f31a7c..63c2a33c 100644 --- a/src/Driver/Database/dbal/DbalExtension/Oci8Extension.php +++ b/src/Driver/Database/dbal/DbalExtension/Oci8Extension.php @@ -295,6 +295,20 @@ public function delegateQueryExceptionProcess($query, array $args, array $option } } + public function delegateClientExecuteStatementException(DbalDriverException $e, string $sql, string $message): void { + switch ($e->getCode()) { + // ORA-01408: such column list already indexed. + case 1408: + // Just return, Oracle detected that an index with same columns exists + // already + return; + + default: + throw new DatabaseExceptionWrapper($message, $e->getCode(), $e); + + } + } + /** * {@inheritdoc} */ diff --git a/src/Driver/Database/dbal/ExceptionHandler.php b/src/Driver/Database/dbal/ExceptionHandler.php index 5c1ec809..bc7b5711 100644 --- a/src/Driver/Database/dbal/ExceptionHandler.php +++ b/src/Driver/Database/dbal/ExceptionHandler.php @@ -38,4 +38,17 @@ public function handleExecutionException(\Exception $exception, StatementInterfa $this->connection->getDbalExtension()->delegateQueryExceptionProcess($query_string, $arguments, $options, $message, $exception); } + /** + * Handles exceptions thrown by DBAL Connection::executeStatement(). + * + * @param DbalDriverException $exception + * The exception thrown by executeStatement(). + * @param string $sql + * A string containing the failing SQL query. + */ + public function handleClientExecuteStatementException(DbalDriverException $exception, string $sql): void { + $message = $exception->getMessage() . ": " . $sql . ";"; + $this->connection->getDbalExtension()->delegateClientExecuteStatementException($exception, $sql, $message); + } + } diff --git a/src/Driver/Database/dbal/Schema.php b/src/Driver/Database/dbal/Schema.php index 2e9ae7e2..3954f4e4 100644 --- a/src/Driver/Database/dbal/Schema.php +++ b/src/Driver/Database/dbal/Schema.php @@ -3,6 +3,7 @@ namespace Drupal\drudbal\Driver\Database\dbal; use Doctrine\DBAL\Exception as DbalException; +use Doctrine\DBAL\Exception\DriverException as DbalDriverException; use Doctrine\DBAL\Platforms\AbstractPlatform as DbalAbstractPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager as DbalAbstractSchemaManager; use Doctrine\DBAL\Schema\Column as DbalColumn; @@ -884,7 +885,14 @@ private function dbalExecuteSchemaChange(DbalSchema $to_schema) { if ($this->dbalExtension->getDebugging()) { dump($sql); } - $this->connection()->getDbalConnection()->executeStatement($sql); + try { + $this->connection()->getDbalConnection()->executeStatement($sql); + } + catch (DbalDriverException $e) { + $exceptionHandler = $this->connection()->exceptionHandler(); + assert($exceptionHandler instanceOf ExceptionHandler); + $exceptionHandler->handleClientExecuteStatementException($e, $sql); + } } $this->dbalSetCurrentSchema($to_schema); return TRUE;