diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9344d..b36630b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Next + +* [feature] ⭐️ Added `DatabaseSession::getVendorName()`, `DatabaseSession::getVendorVersion()`, + `DatabaseSession::vendorIs()` and `DatabaseSession::vendorVersionIs()` methods. +* [deprecated] ⚠️ Deprecated `Bridge::getServerFlavor()`, `Bridge::getServerVersion()`, + `Bridge::isVersionLessThan()` and `Bridge::isVersionGreaterOrEqualThan()` methods, + they will be removed in 2.0 version. + ## 1.3.3 * [feature] ⭐️ Experimental table index list in schema manager. diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index ec4577d..4170a31 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -56,6 +56,7 @@ export default defineConfig({ { text: 'PDO setup', link: '/introduction/getting-started#pdo-setup' }, { text: 'Symfony setup', link: '/introduction/getting-started#symfony-setup' }, { text: 'Error handling', link: '/bridges/error' }, + { text: 'Vendor conditionals', link: '/introduction/vendor-conditionals' }, ] }, { text: 'Samples usages', link: '/introduction/usage' }, diff --git a/docs/content/bridges/error.md b/docs/content/bridges/error.md index ef963f9..9d45f4e 100644 --- a/docs/content/bridges/error.md +++ b/docs/content/bridges/error.md @@ -44,11 +44,11 @@ $queryBuilder = new DoctrineQueryBuilder($connection); $queryBuilder->disableErrorConverter(); ``` -:::note +:::info This works with all bridges. ::: -:::note +:::info The `makinacorpus/query-builder-bundle` will per default configure the `doctrine/dbal` bridge to disable specific error handling in order to be completely transparent for users that will usually expect Doctrine DBAL errors. diff --git a/docs/content/introduction/getting-started.md b/docs/content/introduction/getting-started.md index 8df5c3e..3c139d9 100644 --- a/docs/content/introduction/getting-started.md +++ b/docs/content/introduction/getting-started.md @@ -107,7 +107,7 @@ See [the value converter documentation](../converter/converter) for supported da Install it using composer: ```sh -composer require makinacorpus/query-builder doctrine/dbal:^3.7 +composer require makinacorpus/query-builder doctrine/dbal:'^3.7|^4.0' ``` ### 2. Setup the query builder diff --git a/docs/content/introduction/vendor-conditionals.md b/docs/content/introduction/vendor-conditionals.md new file mode 100644 index 0000000..8416d38 --- /dev/null +++ b/docs/content/introduction/vendor-conditionals.md @@ -0,0 +1,139 @@ +# Database vendor version conditionals + +## Introduction + +When you write portable code, you might want to dynamically branch your code +depending upon the database server vendor name or version. This library exposes +a few helpers method for doing that gracefully. + +:::info +Version string must always be a semantic version string, in the `x.y.z` +(major, minor, patch) form where `x`, `y` and `z` are integers. +You can reduce it to either `x.y` or simply `x` whenever it suits you, +missing digits will always be replaced by `0`. +::: + +This section documents methods on the `MakinaCorpus\QueryBuilder\DatabaseSession` +interface. All bridges implement it. + +## Compare database vendor name + +Simply use the `DatabaseSession::vendorIs()` method, such as: + +```php +use MakinaCorpus\QueryBuilder\DatabaseSession; +use MakinaCorpus\QueryBuilder\Vendor; + +\assert($session instanceof DatabaseSession); + +if ($session->vendorIs(Vendor::MYSQL)) { + // Write code for MySQL. +} +if ($session->vendorIs(Vendor::POSTGRESQL)) { + // Write code for PostgreSQL. +} +if ($session->vendorIs('postgresql')) { + // Variant with a user given raw string. +} +if ($session->vendorIs('mysql')) { + // Variant with a user given raw string. +} +``` + +And more importantly, all vendor name comparison methods accept a value +array for checking if the vendor is any of: + +```php +use MakinaCorpus\QueryBuilder\DatabaseSession; +use MakinaCorpus\QueryBuilder\Vendor; + +\assert($session instanceof DatabaseSession); + +if ($session->vendorIs([Vendor::MARIADB, Vendor::MYSQL)) { + // Write code for MariaDB and MySQL. +} +``` + +:::info +It is advised to use the `MakinaCorpus\QueryBuilder\Vendor` constants for +checking against the vendor name, yet is not mandatory: user given vendor +name is a raw string, which will be lowercased and reduced to only latin +alphabet characters prior to being compared against. + +For example the `SQL Server 2022` value will be reduced to `sqlserver`. + +Values will be compared against a set of known synonyms in order to be +tolerant to user conventions, though it is not advised to rely upon these +tolerances since the known synonyms are not part of the public API and +might change. +::: + +## Compare database version + +Simply use the `DatabaseSession::vendorVersionIs()` method, such as: + +```php +use MakinaCorpus\QueryBuilder\DatabaseSession; +use MakinaCorpus\QueryBuilder\Vendor; + +\assert($session instanceof DatabaseSession); + +if ($session->vendorVersionIs('1.2.3')) { + // Server version is greater or equal to '1.2.3'. +} +if ($session->vendorVersionIs('1.2.3', '>=')) { + // Server version is greater or equal to '1.2.3' (explicit operator). +} +if ($session->vendorVersionIs('1.2')) { + // Server version is greater or equal to '1.2.0'. +} +if ($session->vendorVersionIs('1')) { + // Server version is greater or equal to '1.0.0'. +} +if ($session->vendorVersionIs('1.2.3', '<')) { + // Server version is less than '1.2.3'. +} +// ... See list of operators below. +``` + +Supported operators are: + + - `<`: server version is lower than given value, + - `<=`: server version is lower or equal than given value, + - `=`: server version is equal to given value, + - `>=`: server version is greater or equal than given value, + - `>`: server version is greater than given value. + +:::info +Version comparison operators are raw user given strings, nevertheless +giving an invalid value will raise an exception. +::: + +:::info +Later, composer version notation such as `~1.2.3` or `^1.2|^2.0` might be +implemented for finer conditions to be written by user. +::: + +## Compare both at once + +Common use case is to check both vendor name and version at once: + +Simply use the `DatabaseSession::vendorIs()` method, such as: + +```php +use MakinaCorpus\QueryBuilder\DatabaseSession; +use MakinaCorpus\QueryBuilder\Vendor; + +\assert($session instanceof DatabaseSession); + +if ($session->vendorIs(Vendor::MYSQL, '8.0')) { + // Server version is MySQL with version greater or equal to '8.0'. +} +if ($session->vendorIs(Vendor::MYSQL, '8.0', '>=')) { + // Server version is MySQL with version greater or equal to '8.0' (explicit operator). +} +``` + +:::info +All operators accepted by the `vendorVersionIs()` methods are supported. +::: diff --git a/docs/content/query/transaction.md b/docs/content/query/transaction.md index 1a53f51..01f27bf 100644 --- a/docs/content/query/transaction.md +++ b/docs/content/query/transaction.md @@ -98,7 +98,7 @@ $someSavepoint->rollback(); In all cases, it's your job to handle errors via exception catching. ::: -:::note +:::info You can nest savepoints. ::: diff --git a/src/Bridge/AbstractBridge.php b/src/Bridge/AbstractBridge.php index abf61e8..c02edcc 100644 --- a/src/Bridge/AbstractBridge.php +++ b/src/Bridge/AbstractBridge.php @@ -15,7 +15,6 @@ use MakinaCorpus\QueryBuilder\Expression\CurrentDatabase; use MakinaCorpus\QueryBuilder\Expression\CurrentSchema; use MakinaCorpus\QueryBuilder\Expression\Raw; -use MakinaCorpus\QueryBuilder\Platform; use MakinaCorpus\QueryBuilder\Platform\Converter\MySQLConverter; use MakinaCorpus\QueryBuilder\Platform\Escaper\MySQLEscaper; use MakinaCorpus\QueryBuilder\Platform\Escaper\StandardEscaper; @@ -36,6 +35,7 @@ use MakinaCorpus\QueryBuilder\Result\Result; use MakinaCorpus\QueryBuilder\Schema\SchemaManager; use MakinaCorpus\QueryBuilder\Transaction\Transaction; +use MakinaCorpus\QueryBuilder\Vendor; use MakinaCorpus\QueryBuilder\Writer\Writer; abstract class AbstractBridge extends DefaultQueryBuilder implements Bridge @@ -46,8 +46,8 @@ abstract class AbstractBridge extends DefaultQueryBuilder implements Bridge private ?string $serverName = null; private bool $serverNameLookedUp = false; private ?string $serverVersion = null; - private bool $serverVersionLookekUp = false; - private ?string $serverFlavor = null; + private bool $serverVersionLookedUp = false; + private ?string $vendorName = null; private ?Transaction $currentTransaction = null; private ?ErrorConverter $errorConverter = null; private ?SchemaManager $schemaManager = null; @@ -93,8 +93,8 @@ public function getConverterPluginRegistry(): ConverterPluginRegistry */ public function setServerInfo(?string $serverName = null, ?string $serverVersion = null): void { - $this->serverName = $serverName; - $this->serverVersion = $serverVersion; + $this->serverNameLookedUp = (null !== ($this->serverName = $serverName)); + $this->serverVersionLookedUp = (null !== ($this->serverVersion = $serverVersion)); } #[\Override] @@ -144,51 +144,46 @@ protected function lookupServerName(): ?string } #[\Override] - public function getServerFlavor(): ?string + public function getVendorName(): string { - if (null !== $this->serverFlavor) { - return $this->serverFlavor; + if ($this->vendorName) { + return $this->vendorName; } $serverName = $this->getServerName(); if (null === $serverName) { - return null; + return $this->vendorName = Vendor::UNKNOWN; } - $serverName = \strtolower($serverName); + $serverName = Vendor::vendorNameNormalize($serverName); if (\str_contains($serverName, 'pg') || \str_contains($serverName, 'postgres')) { - return Platform::POSTGRESQL; + return $this->vendorName = Vendor::POSTGRESQL; } - if (\str_contains($serverName, 'maria')) { - return Platform::MARIADB; + return $this->vendorName = Vendor::MARIADB; } - if (\str_contains($serverName, 'my')) { - return Platform::MYSQL; + return $this->vendorName = Vendor::MYSQL; } - if (\str_contains($serverName, 'sqlite')) { - return Platform::SQLITE; + return $this->vendorName = Vendor::SQLITE; } - if (\str_contains($serverName, 'sqlsrv') || \str_contains($serverName, 'sqlserver')) { - return Platform::SQLSERVER; + return $this->vendorName = Vendor::SQLSERVER; } - return $this->serverFlavor = $serverName; + return $this->vendorName = $serverName; } #[\Override] - public function getServerVersion(): ?string + public function getVendorVersion(): string { - if ($this->serverVersionLookekUp) { + if ($this->serverVersionLookedUp && $this->serverVersion) { return $this->serverVersion; } - - $this->serverVersionLookekUp = true; + $this->serverVersionLookedUp = true; $serverVersion = $this->lookupServerVersion(); @@ -197,33 +192,58 @@ public function getServerVersion(): ?string $serverVersion = $matches[0]; } - return $this->serverVersion = $serverVersion; + return $this->serverVersion = $serverVersion ?? '0.0.0'; } #[\Override] - public function isVersionLessThan(string $version): bool + public function vendorIs(string|array $name, ?string $version = null, string $operator = '>='): bool { - if (!$serverVersion = $this->getServerVersion()) { - throw new \Exception(\sprintf("Database '%s', server version is null", $this->getServerFlavor())); + if (null !== $version && !$this->vendorVersionIs($version, $operator)) { + return false; } - return 0 > \version_compare( - Platform::versionNormalize($serverVersion), - Platform::versionNormalize($version), - ); + $vendorName = $this->getVendorName(); + foreach ((array) $name as $candidate) { + if (Vendor::vendorNameNormalize($candidate) === $vendorName) { + return true; + } + } + + return false; } #[\Override] - public function isVersionGreaterOrEqualThan(string $version): bool + public function vendorVersionIs(string $version, string $operator = '>='): bool { - if (!$serverVersion = $this->getServerVersion()) { - throw new \Exception(\sprintf("Database '%s', server version is null", $this->getServerFlavor())); - } + return Vendor::versionCompare($version, $this->getVendorVersion(), $operator); + } + + /** @deprecated */ + #[\Override] + public function getServerFlavor(): ?string + { + return Vendor::UNKNOWN !== ($value = $this->getVendorName()) ? $value : null; + } + + /** @deprecated */ + #[\Override] + public function getServerVersion(): ?string + { + return '0.0.0' !== ($value = $this->getVendorVersion()) ? $value : null; + } - return 0 <= \version_compare( - Platform::versionNormalize($serverVersion), - Platform::versionNormalize($version), - ); + /** @deprecated */ + #[\Override] + public function isVersionLessThan(string $version): bool + { + return $this->vendorVersionIs($version, '<'); + } + + /** @deprecated */ + #[\Override] + public function isVersionGreaterOrEqualThan(string $version): bool + { + return $this->vendorVersionIs($version, '>='); } #[\Override] @@ -313,13 +333,13 @@ private function findCurrentTransaction(): ?Transaction */ protected function doCreateTransaction(int $isolationLevel = Transaction::REPEATABLE_READ): Transaction { - return match ($this->getServerFlavor()) { - Platform::MARIADB => new MySQLTransaction($this, $isolationLevel), - Platform::MYSQL => new MySQLTransaction($this, $isolationLevel), - Platform::POSTGRESQL => new PostgreSQLTransaction($this, $isolationLevel), - Platform::SQLITE => new SQLiteTransaction($this, $isolationLevel), - Platform::SQLSERVER => new SQLServerTransaction($this, $isolationLevel), - default => throw new QueryBuilderError(\sprintf("Transactions are not supported yet for vendor '%s'", $this->getServerFlavor())), + return match ($this->getVendorName()) { + Vendor::MARIADB => new MySQLTransaction($this, $isolationLevel), + Vendor::MYSQL => new MySQLTransaction($this, $isolationLevel), + Vendor::POSTGRESQL => new PostgreSQLTransaction($this, $isolationLevel), + Vendor::SQLITE => new SQLiteTransaction($this, $isolationLevel), + Vendor::SQLSERVER => new SQLServerTransaction($this, $isolationLevel), + default => throw new QueryBuilderError(\sprintf("Transactions are not supported yet for vendor '%s'", $this->getVendorName())), }; } @@ -363,9 +383,9 @@ protected function lookupServerVersion(): ?string */ protected function getConverter(): Converter { - $ret = match ($this->getServerFlavor()) { - Platform::MARIADB => new MySQLConverter(), - Platform::MYSQL => new MySQLConverter(), + $ret = match ($this->getVendorName()) { + Vendor::MARIADB => new MySQLConverter(), + Vendor::MYSQL => new MySQLConverter(), default => new Converter(), }; @@ -379,9 +399,9 @@ protected function getConverter(): Converter */ protected function createEscaper(): Escaper { - return match ($this->getServerFlavor()) { - Platform::MARIADB => new MySQLEscaper(), - Platform::MYSQL => new MySQLEscaper(), + return match ($this->getVendorName()) { + Vendor::MARIADB => new MySQLEscaper(), + Vendor::MYSQL => new MySQLEscaper(), default => new StandardEscaper(), }; } @@ -391,28 +411,28 @@ protected function createEscaper(): Escaper */ protected function createWriter(Escaper $escaper, Converter $converter): Writer { - $serverFlavor = $this->getServerFlavor(); + $vendorName = $this->getVendorName(); - if (Platform::POSTGRESQL === $serverFlavor) { + if (Vendor::POSTGRESQL === $vendorName) { return new PostgreSQLWriter($escaper, $converter); } - if (Platform::MYSQL === $serverFlavor) { - if (($serverVersion = $this->getServerVersion()) && 0 < \version_compare('8.0', $serverVersion)) { + if (Vendor::MYSQL === $vendorName) { + if (($serverVersion = $this->getVendorVersion()) && 0 < \version_compare('8.0', $serverVersion)) { return new MySQLWriter($escaper, $converter); } return new MySQL8Writer($escaper, $converter); } - if (Platform::MARIADB === $serverFlavor) { + if (Vendor::MARIADB === $vendorName) { return new MariaDBWriter($escaper, $converter); } - if (Platform::SQLITE === $serverFlavor) { + if (Vendor::SQLITE === $vendorName) { return new SQLiteWriter($escaper, $converter); } - if (Platform::SQLSERVER === $serverFlavor) { + if (Vendor::SQLSERVER === $vendorName) { return new SQLServerWriter($escaper, $converter); } @@ -430,29 +450,29 @@ public function getWriter(): Writer */ protected function createSchemaManager(): SchemaManager { - $serverFlavor = $this->getServerFlavor(); + $vendorName = $this->getVendorName(); - if (Platform::POSTGRESQL === $serverFlavor) { + if (Vendor::POSTGRESQL === $vendorName) { return new PostgreSQLSchemaManager($this); } - if (Platform::MYSQL === $serverFlavor) { + if (Vendor::MYSQL === $vendorName) { return new MySQLSchemaManager($this); } - if (Platform::MARIADB === $serverFlavor) { + if (Vendor::MARIADB === $vendorName) { return new MySQLSchemaManager($this); } - if (Platform::SQLITE === $serverFlavor) { + if (Vendor::SQLITE === $vendorName) { return new SQLiteSchemaManager($this); } - if (Platform::SQLSERVER === $serverFlavor) { + if (Vendor::SQLSERVER === $vendorName) { return new SQLServerSchemaManager($this); } - throw new UnsupportedFeatureError(\sprintf("Schema manager is not implemented yet for vendor '%s'", $serverFlavor)); + throw new UnsupportedFeatureError(\sprintf("Schema manager is not implemented yet for vendor '%s'", $vendorName)); } #[\Override] diff --git a/src/Bridge/Bridge.php b/src/Bridge/Bridge.php index 0896b2a..a71cfe4 100644 --- a/src/Bridge/Bridge.php +++ b/src/Bridge/Bridge.php @@ -22,21 +22,33 @@ public function getServerName(): ?string; /** * Get server product type. + * + * @deprecated + * @see DatabaseSession::getVendorName() */ public function getServerFlavor(): ?string; /** * Get server version. + * + * @deprecated + * @see DatabaseSession::getVendorVersion() */ public function getServerVersion(): ?string; /** * Version is less than given. + * + * @deprecated + * @see DatabaseSession::vendorVersionIs() */ public function isVersionLessThan(string $version): bool; /** * Version is greater or equal than given. + * + * @deprecated + * @see DatabaseSession::vendorVersionIs() */ public function isVersionGreaterOrEqualThan(string $version): bool; diff --git a/src/Bridge/Doctrine/DoctrineQueryBuilder.php b/src/Bridge/Doctrine/DoctrineQueryBuilder.php index 17eaa19..8bf52ea 100644 --- a/src/Bridge/Doctrine/DoctrineQueryBuilder.php +++ b/src/Bridge/Doctrine/DoctrineQueryBuilder.php @@ -14,7 +14,7 @@ use MakinaCorpus\QueryBuilder\Converter\Converter; use MakinaCorpus\QueryBuilder\Error\QueryBuilderError; use MakinaCorpus\QueryBuilder\Escaper\Escaper; -use MakinaCorpus\QueryBuilder\Platform; +use MakinaCorpus\QueryBuilder\Vendor; use MakinaCorpus\QueryBuilder\Result\IterableResult; use MakinaCorpus\QueryBuilder\Result\Result; use MakinaCorpus\QueryBuilder\Writer\Writer; @@ -91,9 +91,9 @@ protected function createEscaper(): Escaper { $this->dieIfClosed(); - return match ($this->getServerFlavor()) { - Platform::MARIADB => new DoctrineMySQLEscaper($this->connection), - Platform::MYSQL => new DoctrineMySQLEscaper($this->connection), + return match ($this->getVendorName()) { + Vendor::MARIADB => new DoctrineMySQLEscaper($this->connection), + Vendor::MYSQL => new DoctrineMySQLEscaper($this->connection), default => new DoctrineEscaper($this->connection), }; } diff --git a/src/Bridge/Pdo/PdoQueryBuilder.php b/src/Bridge/Pdo/PdoQueryBuilder.php index 706e707..c1120d4 100644 --- a/src/Bridge/Pdo/PdoQueryBuilder.php +++ b/src/Bridge/Pdo/PdoQueryBuilder.php @@ -15,9 +15,9 @@ use MakinaCorpus\QueryBuilder\Bridge\Pdo\Escaper\PdoMySQLEscaper; use MakinaCorpus\QueryBuilder\Error\QueryBuilderError; use MakinaCorpus\QueryBuilder\Escaper\Escaper; -use MakinaCorpus\QueryBuilder\Platform; use MakinaCorpus\QueryBuilder\Result\IterableResult; use MakinaCorpus\QueryBuilder\Result\Result; +use MakinaCorpus\QueryBuilder\Vendor; class PdoQueryBuilder extends AbstractBridge { @@ -36,12 +36,12 @@ public function __construct( */ protected function createErrorConverter(): ErrorConverter { - return match ($this->getServerFlavor()) { - Platform::MARIADB => new PdoMySQLErrorConverter(), - Platform::MYSQL => new PdoMySQLErrorConverter(), - Platform::POSTGRESQL => new PdoPostgreSQLErrorConverter(), - Platform::SQLITE => new PdoSQLiteErrorConverter(), - Platform::SQLSERVER => new PdoSQLServerErrorConverter(), + return match ($this->getVendorName()) { + Vendor::MARIADB => new PdoMySQLErrorConverter(), + Vendor::MYSQL => new PdoMySQLErrorConverter(), + Vendor::POSTGRESQL => new PdoPostgreSQLErrorConverter(), + Vendor::SQLITE => new PdoSQLiteErrorConverter(), + Vendor::SQLSERVER => new PdoSQLServerErrorConverter(), default => new PassthroughErrorConverter(), }; } @@ -71,12 +71,12 @@ protected function lookupServerVersion(): ?string $this->dieIfClosed(); $rawVersion = $this->connection->getAttribute(\PDO::ATTR_SERVER_VERSION); - $serverFlavor = $this->getServerFlavor(); + $vendorName = $this->getVendorName(); $matches = []; // PostgreSQL Example: 16.0 (Debian 16.0-1.pgdg120+1) - if (Platform::POSTGRESQL === $serverFlavor) { + if (Vendor::POSTGRESQL === $vendorName) { if (\preg_match('@^(\d+\.\d+(\.\d+))@i', $rawVersion, $matches)) { return $matches[1]; } @@ -100,9 +100,9 @@ protected function createEscaper(): Escaper { $this->dieIfClosed(); - return match ($this->getServerFlavor()) { - Platform::MARIADB => new PdoMySQLEscaper($this->connection), - Platform::MYSQL => new PdoMySQLEscaper($this->connection), + return match ($this->getVendorName()) { + Vendor::MARIADB => new PdoMySQLEscaper($this->connection), + Vendor::MYSQL => new PdoMySQLEscaper($this->connection), default => new PdoEscaper($this->connection), }; } diff --git a/src/DatabaseSession.php b/src/DatabaseSession.php index 4a2479a..6770405 100644 --- a/src/DatabaseSession.php +++ b/src/DatabaseSession.php @@ -27,6 +27,48 @@ public function getCurrentDatabase(): string; */ public function getDefaultSchema(): string; + /** + * Get database vendor name. + * + * Returns 'unknown' when unknown. + */ + public function getVendorName(): string; + + /** + * Get database vendor version. + * + * Returns '0.0.0' when unknown. + */ + public function getVendorVersion(): string; + + /** + * Compare vendor name against given name. + * + * Allow to give a version constraint as well. + * + * @param string|string[] $name + * One of the Vendor::* constant, eg. 'mysql', 'postgresql', ... + * Bare string are allowed and it will attempt to overcome typo errors + * but using constants is safer. + * @param null|string $version + * Version to compare against, must be a valid semantic version string. + * @param string $operator + * Operator for version comparison, can be one of: '>=', '>', '=', '<', '<='. + * Any invalid operator will raise an exception. + */ + public function vendorIs(string|array $name, ?string $version = null, string $operator = '>='): bool; + + /** + * Compare vendor version against given one. + * + * @param string $version + * Version to compare against, must be a valid semantic version string. + * @param string $operator + * Operator for version comparison, can be one of: '>=', '>', '=', '<', '<='. + * Any invalid operator will raise an exception. + */ + public function vendorVersionIs(string $version, string $operator = '>='): bool; + /** * Execute query and return result. */ diff --git a/src/Platform.php b/src/Platform.php index c10aea5..347a38a 100644 --- a/src/Platform.php +++ b/src/Platform.php @@ -5,13 +5,10 @@ namespace MakinaCorpus\QueryBuilder; /** - * RDMBS identification. - * - * One could have used an enum here, but we want server identification - * string to be arbitrary in various places in the code, to allow this - * API to remain extensible. + * @deprecated + * @see Vendor */ -final class Platform +final class Platform extends Vendor { public const MARIADB = 'mariadb'; public const MYSQL = 'mysql'; @@ -19,6 +16,7 @@ final class Platform public const POSTGRESQL = 'postgresql'; public const SQLITE = 'sqlite'; public const SQLSERVER = 'sqlsrv'; + public const UNKNOWN = 'unknown'; /** * Normalize version to an x.y.z semantic version string. @@ -29,6 +27,42 @@ public static function versionNormalize(string $version): string if (\preg_match('/(\d+)(\.\d+|)(\.\d+|).*/ims', $version, $matches)) { return $matches[1] . ($matches[2] ?: '.0') . ($matches[3] ?: '.0'); } - throw new \Exception(\sprintf("Database version '%s', is not in 'x.y.z' semantic format", $version)); + throw new \Exception(\sprintf("Version '%s', is not in 'x.y.z' semantic format", $version)); + } + + /** + * Compare a user given version against another one. + */ + public static function versionCompare(string $userGiven, string $serverVersion, string $operator): bool + { + $userGiven = self::versionNormalize($userGiven); + $serverVersion = self::versionNormalize($serverVersion); + + return match ($operator) { + '<' => 0 > \version_compare($userGiven, $serverVersion), + '<=' => 0 >= \version_compare($userGiven, $serverVersion), + '=' => 0 === \version_compare($userGiven, $serverVersion), + '>=' => 0 <= \version_compare($userGiven, $serverVersion), + '>' => 0 < \version_compare($userGiven, $serverVersion), + default => throw new \Exception("Version comparison operator must be one of '<', '<=', '=', '>=', '>'"), + }; + } + + /** + * Attempt vendor name normalization against known constants. + */ + public static function vendorNameNormalize(string $name): string + { + $reduced = \preg_replace('/[^a-z]+/', '', \strtolower($name)); + + return match ($reduced) { + 'maria', 'mariadb' => self::MARIADB, + 'mysql', 'my' => self::MYSQL, + 'oracle' => self::ORACLE, + 'postgresql', 'pgsql', 'postgre', 'postgres', 'pg' => self::POSTGRESQL, + 'sqlsrv', 'sqlserver', 'mssql', => self::SQLSERVER, + 'sqlite' => self::SQLITE, + default => $reduced, + }; } } diff --git a/src/Testing/FunctionalTestCaseTrait.php b/src/Testing/FunctionalTestCaseTrait.php index 89a20c0..7d494c7 100644 --- a/src/Testing/FunctionalTestCaseTrait.php +++ b/src/Testing/FunctionalTestCaseTrait.php @@ -4,9 +4,10 @@ namespace MakinaCorpus\QueryBuilder\Testing; -use MakinaCorpus\QueryBuilder\Expression; use MakinaCorpus\QueryBuilder\Bridge\Bridge; +use MakinaCorpus\QueryBuilder\DatabaseSession; use MakinaCorpus\QueryBuilder\Error\QueryBuilderError; +use MakinaCorpus\QueryBuilder\Expression; use MakinaCorpus\QueryBuilder\Expression\Raw; use MakinaCorpus\QueryBuilder\Result\Result; @@ -109,21 +110,28 @@ protected function executeQuery(string|Expression $query, ?array $arguments = nu } } - - protected function ifDatabase(string $database): bool + /** + * @deprecated + * @see DatabaseSession::vendorIs() + */ + protected function ifDatabase(string|array $database): bool { - return $this->getBridge()->getServerFlavor() === $database; + return $this->getBridge()->vendorIs($database); } - protected function ifDatabaseNot(string $database): bool + /** + * @deprecated + * @see DatabaseSession::vendorIs() + */ + protected function ifDatabaseNot(string|array $database): bool { - return $this->getBridge()->getServerFlavor() !== $database; + return !$this->getBridge()->vendorIs($database); } /** * Skip for given database. */ - protected function skipIfDatabase(string $database, ?string $message = null): void + protected function skipIfDatabase(string|array $database, ?string $message = null): void { if ($this->ifDatabase($database)) { self::markTestSkipped(\sprintf("Test disabled for database '%s'", $database)); @@ -133,7 +141,7 @@ protected function skipIfDatabase(string $database, ?string $message = null): vo /** * Skip for given database. */ - protected function skipIfDatabaseNot(string $database, ?string $message = null): void + protected function skipIfDatabaseNot(string|array $database, ?string $message = null): void { if ($this->ifDatabaseNot($database)) { self::markTestSkipped(\sprintf("Test disabled for database '%s'", $database)); @@ -143,11 +151,11 @@ protected function skipIfDatabaseNot(string $database, ?string $message = null): /** * Skip for given database, and greater than version. */ - protected function skipIfDatabaseGreaterThan(string $database, string $version, ?string $message = null): void + protected function skipIfDatabaseGreaterThan(string|array $database, string $version, ?string $message = null): void { $this->skipIfDatabaseNot($database); - if ($this->getBridge()->isVersionGreaterOrEqualThan($version)) { + if ($this->getBridge()->vendorVersionIs($version)) { self::markTestSkipped($message ?? \sprintf("Test disabled for database '%s' at version >= '%s'", $database, $version)); } } @@ -155,11 +163,11 @@ protected function skipIfDatabaseGreaterThan(string $database, string $version, /** * Skip for given database, and lower than version. */ - protected function skipIfDatabaseLessThan(string $database, string $version, ?string $message = null): void + protected function skipIfDatabaseLessThan(string|array $database, string $version, ?string $message = null): void { $this->skipIfDatabaseNot($database); - if ($this->getBridge()->isVersionLessThan($version)) { + if ($this->getBridge()->vendorVersionIs($version, '<')) { self::markTestSkipped($message ?? \sprintf("Test disabled for database '%s' at version <= '%s'", $database, $version)); } } diff --git a/tests/Bridge/AbstractErrorConverterTestCase.php b/tests/Bridge/AbstractErrorConverterTestCase.php index 64f4810..85ef16b 100644 --- a/tests/Bridge/AbstractErrorConverterTestCase.php +++ b/tests/Bridge/AbstractErrorConverterTestCase.php @@ -4,7 +4,7 @@ namespace MakinaCorpus\QueryBuilder\Tests\Bridge; -use MakinaCorpus\QueryBuilder\Platform; +use MakinaCorpus\QueryBuilder\Vendor; use MakinaCorpus\QueryBuilder\Error\Bridge\AmbiguousIdentifierError; use MakinaCorpus\QueryBuilder\Error\Bridge\ColumnDoesNotExistError; use MakinaCorpus\QueryBuilder\Error\Bridge\ForeignKeyConstraintViolationError; @@ -34,10 +34,10 @@ protected function createSchema(): void ); } catch (\Throwable) {} - switch ($this->getBridge()->getServerFlavor()) { + switch ($this->getBridge()->getVendorName()) { - case Platform::MARIADB: - case Platform::MYSQL: + case Vendor::MARIADB: + case Vendor::MYSQL: $this->getBridge()->executeStatement( <<getBridge()->executeStatement( <<getBridge()->executeStatement( <<getBridge()->executeStatement( diff --git a/tests/Functional/SelectFunctionalTest.php b/tests/Functional/SelectFunctionalTest.php index fe110f2..7fa35d2 100644 --- a/tests/Functional/SelectFunctionalTest.php +++ b/tests/Functional/SelectFunctionalTest.php @@ -12,10 +12,10 @@ use MakinaCorpus\QueryBuilder\Expression\RandomInt; use MakinaCorpus\QueryBuilder\Expression\Raw; use MakinaCorpus\QueryBuilder\Expression\Value; -use MakinaCorpus\QueryBuilder\Platform; use MakinaCorpus\QueryBuilder\Query\Query; use MakinaCorpus\QueryBuilder\Query\Select; use MakinaCorpus\QueryBuilder\Tests\Bridge\Doctrine\DoctrineTestCase; +use MakinaCorpus\QueryBuilder\Vendor; class SelectFunctionalTest extends DoctrineTestCase { @@ -38,10 +38,10 @@ protected function createSchema(): void ); } catch (\Throwable) {} - switch ($this->getBridge()->getServerFlavor()) { + switch ($this->getBridge()->getVendorName()) { - case Platform::MARIADB: - case Platform::MYSQL: + case Vendor::MARIADB: + case Vendor::MYSQL: $this->getBridge()->executeStatement( <<getBridge()->executeStatement( <<executeQuery($select)->fetchRow()->get(0, 'string'); - if ($this->ifDatabase(Platform::SQLITE)) { + if ($this->ifDatabase(Vendor::SQLITE)) { self::assertSame('main', $value); } else { self::assertSame('test_db', $value); @@ -128,7 +128,7 @@ public function testCurrentSchema(): void $value = $this->executeQuery($select)->fetchRow()->get(0, 'string'); - if (!$this->ifDatabase(Platform::SQLSERVER)) { + if (!$this->ifDatabase(Vendor::SQLSERVER)) { self::assertSame('public', $value); } } @@ -273,10 +273,10 @@ public function testFromSelectWhere(): void public function testSelectFromConstantTable(): void { - $this->skipIfDatabase(Platform::MARIADB, 'MariaDB does not support VALUES column aliasing.'); - $this->skipIfDatabase(Platform::SQLITE, 'SQLite requires you to convert VALUES in SELECT to VALUES in an aliased CTE'); - $this->skipIfDatabaseLessThan(Platform::MARIADB, '10.3', 'MariaDB supports VALUES statement since version 10.3.'); - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB does not support VALUES column aliasing.'); + $this->skipIfDatabase(Vendor::SQLITE, 'SQLite requires you to convert VALUES in SELECT to VALUES in an aliased CTE'); + $this->skipIfDatabaseLessThan(Vendor::MARIADB, '10.3', 'MariaDB supports VALUES statement since version 10.3.'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $table = new ConstantTable(); $table->columns(['a', 'b' , 'c']); @@ -326,10 +326,10 @@ public function testJoinSelectOn(): void public function testJoinConstantTable(): void { - $this->skipIfDatabase(Platform::MARIADB, 'MariaDB does not support VALUES column aliasing.'); - $this->skipIfDatabase(Platform::SQLITE, 'SQLite requires you to convert VALUES in SELECT to VALUES in an aliased CTE'); - $this->skipIfDatabaseLessThan(Platform::MARIADB, '10.3', 'MariaDB supports VALUES statement since version 10.3.'); - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB does not support VALUES column aliasing.'); + $this->skipIfDatabase(Vendor::SQLITE, 'SQLite requires you to convert VALUES in SELECT to VALUES in an aliased CTE'); + $this->skipIfDatabaseLessThan(Vendor::MARIADB, '10.3', 'MariaDB supports VALUES statement since version 10.3.'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $table = new ConstantTable(); $table->columns(['a', 'b' , 'c']); @@ -347,7 +347,7 @@ public function testJoinConstantTable(): void public function testWithJoin(): void { - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $nested = new Select('bar'); @@ -363,8 +363,8 @@ public function testWithJoin(): void public function testWithConstantTableJoin(): void { // https://learn.microsoft.com/en-us/sql/t-sql/queries/table-value-constructor-transact-sql?view=sql-server-ver16 - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server only accepts Table Value Constructor in SELECT, FROM and INSERT.'); - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server only accepts Table Value Constructor in SELECT, FROM and INSERT.'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $table = new ConstantTable(); $table->columns(['a', 'b' , 'c']); @@ -397,7 +397,7 @@ public function testAggregateFilter(): void public function testAggregateOverPartitionBy(): void { - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $select = new Select('foo'); @@ -413,7 +413,7 @@ public function testAggregateOverPartitionBy(): void public function testAggregateOverOrderBy(): void { - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $select = new Select('foo'); @@ -430,7 +430,7 @@ public function testAggregateOverOrderBy(): void public function testAggregateOverPartitionByOrderBy(): void { - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $select = new Select('foo'); @@ -448,7 +448,7 @@ public function testAggregateOverPartitionByOrderBy(): void public function testAggregateOverEmpty(): void { - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $select = new Select('foo'); @@ -464,7 +464,7 @@ public function testAggregateOverEmpty(): void public function testAggregateFilterOver(): void { - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $select = new Select('foo'); @@ -481,8 +481,8 @@ public function testAggregateFilterOver(): void public function testWindowAfterFrom(): void { - $this->skipIfDatabase(Platform::SQLSERVER); - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabase(Vendor::SQLSERVER); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $select = new Select('foo'); diff --git a/tests/Functional/TextFunctionalTest.php b/tests/Functional/TextFunctionalTest.php index e0e24c3..371abd9 100644 --- a/tests/Functional/TextFunctionalTest.php +++ b/tests/Functional/TextFunctionalTest.php @@ -4,7 +4,6 @@ namespace MakinaCorpus\QueryBuilder\Tests\Functional; -use MakinaCorpus\QueryBuilder\Platform; use MakinaCorpus\QueryBuilder\Expression\Concat; use MakinaCorpus\QueryBuilder\Expression\Lpad; use MakinaCorpus\QueryBuilder\Expression\Rpad; @@ -12,6 +11,7 @@ use MakinaCorpus\QueryBuilder\Expression\Value; use MakinaCorpus\QueryBuilder\Query\Select; use MakinaCorpus\QueryBuilder\Tests\Bridge\Doctrine\DoctrineTestCase; +use MakinaCorpus\QueryBuilder\Vendor; class TextFunctionalTest extends DoctrineTestCase { @@ -28,8 +28,8 @@ public function testConcat(): void public function testMd5(): void { - $this->skipIfDatabase(Platform::SQLITE, 'SQLite does not have any hash function.'); - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server actually returns a hash, but not the right one ?!'); + $this->skipIfDatabase(Vendor::SQLITE, 'SQLite does not have any hash function.'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server actually returns a hash, but not the right one ?!'); $select = new Select(); $select->columnRaw(new StringHash('foo', 'md5')); @@ -42,9 +42,9 @@ public function testMd5(): void public function testSha1(): void { - $this->skipIfDatabase(Platform::POSTGRESQL, 'pgcrypto extension must be enabled.'); - $this->skipIfDatabase(Platform::SQLITE, 'SQLite does not have any hash function.'); - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server actually returns a hash, but not the right one ?!'); + $this->skipIfDatabase(Vendor::POSTGRESQL, 'pgcrypto extension must be enabled.'); + $this->skipIfDatabase(Vendor::SQLITE, 'SQLite does not have any hash function.'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server actually returns a hash, but not the right one ?!'); $select = new Select(); $select->columnRaw(new StringHash('foo', 'sha1')); diff --git a/tests/Functional/TransactionFunctionalTest.php b/tests/Functional/TransactionFunctionalTest.php index 6b20471..d3c87c4 100644 --- a/tests/Functional/TransactionFunctionalTest.php +++ b/tests/Functional/TransactionFunctionalTest.php @@ -4,12 +4,12 @@ namespace MakinaCorpus\QueryBuilder\Tests\Functional; -use MakinaCorpus\QueryBuilder\Platform; use MakinaCorpus\QueryBuilder\Error\Bridge\TransactionError; use MakinaCorpus\QueryBuilder\Error\Bridge\UniqueConstraintViolationError; use MakinaCorpus\QueryBuilder\Tests\Bridge\Doctrine\DoctrineTestCase; use MakinaCorpus\QueryBuilder\Transaction\Transaction; use MakinaCorpus\QueryBuilder\Transaction\TransactionSavepoint; +use MakinaCorpus\QueryBuilder\Vendor; final class TransactionFunctionalTest extends DoctrineTestCase { @@ -26,10 +26,10 @@ protected function createSchema(): void ); } catch (\Throwable) {} - switch ($bridge->getServerFlavor()) { + switch ($bridge->getVendorName()) { - case Platform::MARIADB: - case Platform::MYSQL: + case Vendor::MARIADB: + case Vendor::MYSQL: $bridge->executeStatement( <<executeStatement( <<executeStatement( <<ifDatabaseNot(Platform::SQLITE) && $this->ifDatabaseNot(Platform::SQLSERVER)) { + if ($this->ifDatabaseNot([Vendor::SQLITE, Vendor::SQLSERVER])) { self::assertSame(4, $result->rowCount()); } self::assertSame('a', $result->fetchRow()->get('bar')); @@ -207,7 +207,7 @@ public function testNestedTransactionCreatesSavepoint() ; // @todo Row count doesn't work with SQLite and SQLServer - if ($this->ifDatabaseNot(Platform::SQLITE) && $this->ifDatabaseNot(Platform::SQLSERVER)) { + if ($this->ifDatabaseNot([Vendor::SQLITE, Vendor::SQLSERVER])) { self::assertSame(2, $result->rowCount()); } self::assertSame('g', $result->fetchRow()->get('bar')); @@ -256,7 +256,7 @@ public function testNestedTransactionRollbackToSavepointTransparently() ; // @todo Row count doesn't work with SQLite and SQLServer - if ($this->ifDatabaseNot(Platform::SQLITE) && $this->ifDatabaseNot(Platform::SQLSERVER)) { + if ($this->ifDatabaseNot([Vendor::SQLITE, Vendor::SQLSERVER])) { self::assertSame(1, $result->rowCount()); } self::assertSame('f', $result->fetchRow()->get('bar')); @@ -268,8 +268,8 @@ public function testNestedTransactionRollbackToSavepointTransparently() public function testImmediateTransactionFail() { // @todo Support IMMEDIATE in the BEGIN statement for SQLite. - self::skipIfDatabase(Platform::SQLITE); - self::skipIfDatabase(Platform::SQLSERVER, 'SQL Server can not deffer constraints'); + self::skipIfDatabase(Vendor::SQLITE); + self::skipIfDatabase(Vendor::SQLSERVER, 'SQL Server can not deffer constraints'); self::expectNotToPerformAssertions(); @@ -317,8 +317,8 @@ public function testImmediateTransactionFail() public function testDeferredTransactionFail() { // @todo Support IMMEDIATE in the BEGIN statement for SQLite. - self::skipIfDatabase(Platform::SQLITE); - self::skipIfDatabase(Platform::SQLSERVER, 'SQL Server can not deffer constraints'); + self::skipIfDatabase(Vendor::SQLITE); + self::skipIfDatabase(Vendor::SQLSERVER, 'SQL Server can not deffer constraints'); self::expectNotToPerformAssertions(); @@ -432,7 +432,7 @@ public function testTransactionRollback() ; // @todo Row count doesn't work with SQLite and SQLServer - if ($this->ifDatabaseNot(Platform::SQLITE) && $this->ifDatabaseNot(Platform::SQLSERVER)) { + if ($this->ifDatabaseNot([Vendor::SQLITE, Vendor::SQLSERVER])) { self::assertSame(3, $result->rowCount()); } else { $count = 0; diff --git a/tests/Functional/UpdateFunctionalTest.php b/tests/Functional/UpdateFunctionalTest.php index 8504724..222bb47 100644 --- a/tests/Functional/UpdateFunctionalTest.php +++ b/tests/Functional/UpdateFunctionalTest.php @@ -4,9 +4,9 @@ namespace MakinaCorpus\QueryBuilder\Tests\Functional; -use MakinaCorpus\QueryBuilder\Platform; use MakinaCorpus\QueryBuilder\Query\Update; use MakinaCorpus\QueryBuilder\Tests\Bridge\Doctrine\DoctrineTestCase; +use MakinaCorpus\QueryBuilder\Vendor; class UpdateFunctionalTest extends DoctrineTestCase { @@ -29,10 +29,10 @@ protected function createSchema(): void ); } catch (\Throwable) {} - switch ($this->getBridge()->getServerFlavor()) { + switch ($this->getBridge()->getVendorName()) { - case Platform::MARIADB: - case Platform::MYSQL: + case Vendor::MARIADB: + case Vendor::MYSQL: $this->getBridge()->executeStatement( <<getBridge()->executeStatement( <<skipIfDatabase(Platform::MARIADB, 'MariaDB does not support RETURNING|OUPUT'); - $this->skipIfDatabase(Platform::MYSQL, 'MariaDB does not support RETURNING|OUPUT'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB does not support RETURNING|OUPUT'); + $this->skipIfDatabase(Vendor::MYSQL, 'MariaDB does not support RETURNING|OUPUT'); $update = new Update('foo'); $update @@ -144,9 +144,9 @@ public function testReturning(): void public function testReturningExpression(): void { - $this->skipIfDatabase(Platform::MARIADB, 'MariaDB does not support RETURNING|OUPUT'); - $this->skipIfDatabase(Platform::MYSQL, 'MariaDB does not support RETURNING|OUPUT'); - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server has its own test for this.'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB does not support RETURNING|OUPUT'); + $this->skipIfDatabase(Vendor::MYSQL, 'MariaDB does not support RETURNING|OUPUT'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server has its own test for this.'); $update = new Update('foo'); $expr = $update->expression(); @@ -166,7 +166,7 @@ public function testReturningExpression(): void public function testReturningExpressionSqlServer(): void { - $this->skipIfDatabaseNot(Platform::SQLSERVER); + $this->skipIfDatabaseNot(Vendor::SQLSERVER); $update = new Update('foo'); $expr = $update->expression(); @@ -186,8 +186,8 @@ public function testReturningExpressionSqlServer(): void public function testReturningStar(): void { - $this->skipIfDatabase(Platform::MARIADB, 'MariaDB does not support RETURNING|OUPUT'); - $this->skipIfDatabase(Platform::MYSQL, 'MariaDB does not support RETURNING|OUPUT'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB does not support RETURNING|OUPUT'); + $this->skipIfDatabase(Vendor::MYSQL, 'MariaDB does not support RETURNING|OUPUT'); $update = new Update('foo'); $update->set('date', new \DateTimeImmutable('2012-08-21 12:32:54')); @@ -211,8 +211,8 @@ public function testWhere(): void public function testFromWith(): void { - $this->skipIfDatabase(Platform::MARIADB); - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabase(Vendor::MARIADB); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $update = new Update('foo'); $expr = $update->expression(); @@ -231,8 +231,8 @@ public function testFromWith(): void public function testFromWithJoin(): void { - $this->skipIfDatabase(Platform::MARIADB); - $this->skipIfDatabaseLessThan(Platform::MYSQL, '8.0'); + $this->skipIfDatabase(Vendor::MARIADB); + $this->skipIfDatabaseLessThan(Vendor::MYSQL, '8.0'); $update = new Update('foo'); $expr = $update->expression(); @@ -267,7 +267,7 @@ public function testFrom(): void public function testFromJoin(): void { - $this->skipIfDatabase(Platform::SQLSERVER, 'Temporarily disabled due to a bug'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'Temporarily disabled due to a bug'); $update = new Update('foo'); $expr = $update->expression(); diff --git a/tests/Platform/Schema/AbstractSchemaTestCase.php b/tests/Platform/Schema/AbstractSchemaTestCase.php index 3f93a36..2864607 100644 --- a/tests/Platform/Schema/AbstractSchemaTestCase.php +++ b/tests/Platform/Schema/AbstractSchemaTestCase.php @@ -4,16 +4,16 @@ namespace MakinaCorpus\QueryBuilder\Tests\Platform\Schema; -use MakinaCorpus\QueryBuilder\Platform; -use MakinaCorpus\QueryBuilder\QueryBuilder; +use MakinaCorpus\QueryBuilder\Error\Bridge\DatabaseObjectDoesNotExistError; use MakinaCorpus\QueryBuilder\Error\QueryBuilderError; use MakinaCorpus\QueryBuilder\Error\UnsupportedFeatureError; -use MakinaCorpus\QueryBuilder\Error\Bridge\DatabaseObjectDoesNotExistError; -use MakinaCorpus\QueryBuilder\Schema\SchemaManager; +use MakinaCorpus\QueryBuilder\QueryBuilder; use MakinaCorpus\QueryBuilder\Schema\Read\ForeignKey; use MakinaCorpus\QueryBuilder\Schema\Read\Index; +use MakinaCorpus\QueryBuilder\Schema\SchemaManager; use MakinaCorpus\QueryBuilder\Tests\FunctionalTestCase; use MakinaCorpus\QueryBuilder\Type\Type; +use MakinaCorpus\QueryBuilder\Vendor; abstract class AbstractSchemaTestCase extends FunctionalTestCase { @@ -42,10 +42,10 @@ protected function createSchema(): void } catch (DatabaseObjectDoesNotExistError) {} } - switch ($bridge->getServerFlavor()) { + switch ($bridge->getVendorName()) { - case Platform::MARIADB: - case Platform::MYSQL: + case Vendor::MARIADB: + case Vendor::MYSQL: $bridge->executeStatement( <<executeStatement( <<executeStatement( <<executeStatement( <<ifDatabase(Platform::MYSQL)) { + if ($this->ifDatabase(Vendor::MYSQL)) { return 'utf8_general_ci'; } - if ($this->ifDatabase(Platform::POSTGRESQL)) { + if ($this->ifDatabase(Vendor::POSTGRESQL)) { // Arbitrary taken from: "SELECT collname FROM pg_collation" and // existing in all tested containers. return 'fr-FR-x-icu'; } - if ($this->ifDatabase(Platform::SQLSERVER)) { + if ($this->ifDatabase(Vendor::SQLSERVER)) { return 'Latin1_General_100_CI_AS_KS_SC_UTF8'; } return 'utf8'; @@ -395,9 +395,9 @@ public function testColumnAddNullable(): void public function testColumnAddIdentity(): void { - $this->skipIfDatabase(Platform::MARIADB, 'MariaDB requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); - $this->skipIfDatabase(Platform::MYSQL, 'MySQL requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); - $this->skipIfDatabase(Platform::SQLITE, 'This will never be implemented in SQLite'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); + $this->skipIfDatabase(Vendor::MYSQL, 'MySQL requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); + $this->skipIfDatabase(Vendor::SQLITE, 'This will never be implemented in SQLite'); $this ->getSchemaManager() @@ -427,9 +427,9 @@ public function testColumnAddIdentity(): void public function testColumnAddSerial(): void { - $this->skipIfDatabase(Platform::MARIADB, 'MariaDB requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); - $this->skipIfDatabase(Platform::MYSQL, 'MySQL requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); - $this->skipIfDatabase(Platform::SQLITE, 'This will never be implemented in SQLite'); + $this->skipIfDatabase(Vendor::MARIADB, 'MariaDB requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); + $this->skipIfDatabase(Vendor::MYSQL, 'MySQL requires AUTO_INCREMENT to be PRIMARY KEY, cannot simply be added this way'); + $this->skipIfDatabase(Vendor::SQLITE, 'This will never be implemented in SQLite'); $this ->getSchemaManager() @@ -519,8 +519,8 @@ public function testColumnAddNotNullWithDefault(): void public function testColumnModifyType(): void { - $this->skipIfDatabase(Platform::SQLITE); - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server cannot change type when there is a default, see testColumnModifyTypeAndDefault().'); + $this->skipIfDatabase(Vendor::SQLITE); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server cannot change type when there is a default, see testColumnModifyTypeAndDefault().'); $this ->getSchemaManager() @@ -550,7 +550,7 @@ public function testColumnModifyType(): void public function testColumnModifyTypeAndDefault(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -581,7 +581,7 @@ public function testColumnModifyTypeAndDefault(): void public function testColumnModifyCollation(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $collation = $this->getTestingCollation(); @@ -614,7 +614,7 @@ public function testColumnModifyCollation(): void public function testColumnModifyDropDefault(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -644,7 +644,7 @@ public function testColumnModifyDropDefault(): void public function testColumnModifyDefault(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -674,7 +674,7 @@ public function testColumnModifyDefault(): void public function testColumnModifyNullable(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -704,7 +704,7 @@ public function testColumnModifyNullable(): void public function testColumnModifyEverything(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $collation = $this->getTestingCollation(); @@ -753,7 +753,7 @@ public function testColumnDrop(): void public function testColumnDropWithDefault(): void { - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server drop a column with constraints'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server drop a column with constraints'); $this ->getSchemaManager() @@ -772,8 +772,8 @@ public function testColumnDropWithDefault(): void public function testColumnDropWhenConstraint(): void { - $this->skipIfDatabase(Platform::SQLITE, 'SQLite cannot drop a column with a default'); - $this->skipIfDatabase(Platform::SQLSERVER, 'SQL Server drop a column with constraints'); + $this->skipIfDatabase(Vendor::SQLITE, 'SQLite cannot drop a column with a default'); + $this->skipIfDatabase(Vendor::SQLSERVER, 'SQL Server drop a column with constraints'); $this ->getSchemaManager() @@ -827,7 +827,7 @@ public function testConstraintRename(): void public function testForeignKeyAdd(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -841,7 +841,7 @@ public function testForeignKeyAdd(): void public function testForeignKeyModify(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); self::markTestIncomplete(); self::expectNotToPerformAssertions(); @@ -849,7 +849,7 @@ public function testForeignKeyModify(): void public function testForeignKeyDrop(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -927,7 +927,7 @@ public function testIndexRename(): void public function testPrimaryKeyAdd(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -947,7 +947,7 @@ public function testPrimaryKeyAdd(): void public function testPrimaryKeyDrop(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -996,7 +996,7 @@ public function testTableCreate(): void public function testTableCreateWithIdentity(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -1020,7 +1020,7 @@ public function testTableCreateWithIdentity(): void public function testTableCreateWithSerial(): void { - $this->skipIfDatabase(Platform::SQLITE); + $this->skipIfDatabase(Vendor::SQLITE); $this ->getSchemaManager() @@ -1158,7 +1158,7 @@ public function testTableDrop(): void ; self::expectException(QueryBuilderError::class); - self::expectExceptionMessageMatches("/Table 'test_db\..*\.user_address' does not exist/"); + self::expectExceptionMessageMatches("/Table '(test_db|main)\..*\.user_address' does not exist/"); $this ->getSchemaManager() ->getTable('user_address') @@ -1182,7 +1182,7 @@ public function testTableRename(): void self::assertSame('renamed_table_new_name', $table->getName()); self::expectException(QueryBuilderError::class); - self::expectExceptionMessageMatches("/Table 'test_db\..*\.renamed_table' does not exist/"); + self::expectExceptionMessageMatches("/Table '(test_db|main)\..*\.renamed_table' does not exist/"); $this ->getSchemaManager() ->getTable('renamed_table') @@ -1322,7 +1322,7 @@ public function testColumnNumeric(): void self::assertSameType(Type::decimal(10, 2), $column->getValueType()); self::assertSame('org', $column->getTable()); self::assertSame($this->getSchemaManager()->getDefaultSchema(), $column->getSchema()); - self::assertMatchesRegularExpression('/^column:test_db\..*\.org.balance$/', $column->toString()); + self::assertMatchesRegularExpression('/^column:(test_db|main)\..*\.org.balance$/', $column->toString()); self::assertFalse($column->isNullable()); } @@ -1354,7 +1354,7 @@ public function testColumnText(): void self::assertSameType(Type::text(), $column->getValueType()); self::assertSame('org', $column->getTable()); self::assertSame($this->getSchemaManager()->getDefaultSchema(), $column->getSchema()); - self::assertMatchesRegularExpression('/^column:test_db\..*\.org.name$/', $column->toString()); + self::assertMatchesRegularExpression('/^column:(test_db|main)\..*\.org.name$/', $column->toString()); self::assertTrue($column->isNullable()); } @@ -1404,7 +1404,7 @@ public function testTableGet(): void self::assertTrue($found); - if ($this->ifDatabaseNot(Platform::SQLITE)) { + if ($this->ifDatabaseNot(Vendor::SQLITE)) { self::assertCount(1, $table->getReverseForeignKeys()); } } @@ -1433,7 +1433,7 @@ public function testTableForeignKeys(): void self::assertSame('users', $foreignKey->getTable()); self::assertSame('org', $foreignKey->getForeignTable()); - if ($this->ifDatabaseNot(Platform::SQLITE)) { + if ($this->ifDatabaseNot(Vendor::SQLITE)) { self::assertNotNull($reverseForeignKey = ($table->getReverseForeignKeys()[0] ?? null)); self::assertSame(['user_id'], $reverseForeignKey->getColumnNames()); self::assertSame(['id'], $reverseForeignKey->getForeignColumnNames());