From 1bfc5ddbff625fd9d65b3eb8e63a5267fbb9a7a3 Mon Sep 17 00:00:00 2001 From: mringler Date: Thu, 24 Nov 2022 14:59:35 +0100 Subject: [PATCH 1/8] added type to store UUIDs as binary (UUID_BINARY) --- .../Builder/Om/AbstractOMBuilder.php | 38 ++++++ .../Generator/Builder/Om/ObjectBuilder.php | 64 +++++++-- .../Generator/Builder/Om/QueryBuilder.php | 8 ++ src/Propel/Generator/Model/Column.php | 10 ++ src/Propel/Generator/Model/PropelTypes.php | 9 ++ src/Propel/Generator/Model/Table.php | 65 +++++++++ .../Generator/Platform/MssqlPlatform.php | 1 + .../Generator/Platform/MysqlPlatform.php | 5 +- .../Generator/Platform/OraclePlatform.php | 1 + .../Generator/Platform/PgsqlPlatform.php | 1 + .../Generator/Platform/PlatformInterface.php | 7 + .../Generator/Platform/SqlitePlatform.php | 1 + src/Propel/Runtime/Map/ColumnMap.php | 2 +- src/Propel/Runtime/Util/UuidConverter.php | 92 +++++++++++++ tests/Fixtures/bookstore/schema.xml | 4 + .../Tests/Generator/Model/ColumnTest.php | 25 +++- .../Platform/DefaultPlatformTest.php | 3 +- .../Generator/Platform/MssqlPlatformTest.php | 25 +++- .../MysqlPlatformMigrationMyISAMTest.php | 5 +- .../Platform/MysqlPlatformMigrationTest.php | 7 +- .../Platform/MysqlPlatformMyISAMTest.php | 3 +- .../Generator/Platform/MysqlPlatformTest.php | 22 ++- .../Platform/OraclePlatformMigrationTest.php | 5 +- .../Generator/Platform/OraclePlatformTest.php | 27 +++- .../Platform/PgsqlPlatformMigrationTest.php | 23 +++- .../Generator/Platform/PgsqlPlatformTest.php | 35 ++++- .../PlatformMigrationTestProvider.php | 12 ++ .../Platform/PlatformTestProvider.php | 23 ++++ .../Generator/Platform/SqlitePlatformTest.php | 24 +++- .../Runtime/Adapter/Pdo/OracleAdapterTest.php | 36 ++--- .../Runtime/{ => TypeTests}/TypeTest.php | 2 +- .../Runtime/TypeTests/UuidBinaryTypeTest.php | 127 ++++++++++++++++++ .../UuidConverterMysqlCompatibilityTest.php | 89 ++++++++++++ .../Tests/Runtime/Util/UuidConverterTest.php | 90 +++++++++++++ tests/Propel/Tests/TestCase.php | 3 +- 35 files changed, 827 insertions(+), 67 deletions(-) create mode 100644 src/Propel/Runtime/Util/UuidConverter.php rename tests/Propel/Tests/Runtime/{ => TypeTests}/TypeTest.php (99%) create mode 100644 tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php create mode 100644 tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php create mode 100644 tests/Propel/Tests/Runtime/Util/UuidConverterTest.php diff --git a/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php b/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php index dbba714a7e..48ed8c98ec 100644 --- a/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php +++ b/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php @@ -14,10 +14,12 @@ use Propel\Generator\Exception\InvalidArgumentException; use Propel\Generator\Exception\LogicException; use Propel\Generator\Exception\RuntimeException; +use Propel\Generator\Exception\SchemaException; use Propel\Generator\Model\Column; use Propel\Generator\Model\CrossForeignKeys; use Propel\Generator\Model\ForeignKey; use Propel\Generator\Model\Table; +use Propel\Generator\Model\VendorInfo; /** * Baseclass for OM-building classes. @@ -1189,4 +1191,40 @@ abstract protected function addClassBody(string &$script): void; * @return void */ abstract protected function addClassClose(string &$script): void; + + /** + * Returns the vendor info from the table for the configured platform. + * + * @return \Propel\Generator\Model\VendorInfo + */ + protected function getVendorInfo(): VendorInfo + { + $dbVendorId = $this->getPlatform()->getDatabaseType(); + + return $this->getTable()->getVendorInfoForType($dbVendorId); + } + + /** + * Returns the value for the uuid swap flag as set in the vendor information + * block in schema.xml as a literal ('true' or 'false'). + * + * @psalm-return 'true'|'false' + * + * @see \Propel\Runtime\Util\UuidConverter::uuidToBin() + * + * @throws \Propel\Generator\Exception\SchemaException + * + * @return string + */ + protected function getUuidSwapFlagLiteral(): string + { + $vendorInformation = $this->getVendorInfo(); + + $uuidSwapFlag = $vendorInformation->getParameter('UuidSwapFlag') ?? 'true'; + if (!in_array($uuidSwapFlag, ['true', 'false'], true)) { + throw new SchemaException('Value for /database/vendor/parameter[name="UuidSwapFlag"] must be "true" or "false", but it is ' . $uuidSwapFlag); + } + + return $uuidSwapFlag; + } } diff --git a/src/Propel/Generator/Builder/Om/ObjectBuilder.php b/src/Propel/Generator/Builder/Om/ObjectBuilder.php index 08e06cab4e..ee1f65085c 100644 --- a/src/Propel/Generator/Builder/Om/ObjectBuilder.php +++ b/src/Propel/Generator/Builder/Om/ObjectBuilder.php @@ -323,6 +323,12 @@ protected function addClassBody(string &$script): void } $table = $this->getTable(); + + $additionalModelClasses = $table->getAdditionalModelClassImports(); + if ($additionalModelClasses) { + $this->declareClasses(...$additionalModelClasses); + } + if (!$table->isAlias()) { $this->addConstants($script); $this->addAttributes($script); @@ -1709,6 +1715,13 @@ protected function addLazyLoaderBody(string &$script, Column $column): void } elseif ($column->isPhpObjectType()) { $script .= " \$this->$clo = (\$firstColumn !== null) ? new " . $column->getPhpType() . '($firstColumn) : null;'; + } elseif ($column->getType() === PropelTypes::UUID_BINARY) { + $uuidSwapFlag = $this->getUuidSwapFlagLiteral(); + $script .= " + if (is_resource(\$firstColumn)) { + \$firstColumn = stream_get_contents(\$firstColumn); + } + \$this->$clo = (\$firstColumn) ? UuidConverter::binToUuid(\$firstColumn, $uuidSwapFlag) : null;"; } else { $script .= " \$this->$clo = \$firstColumn;"; @@ -2716,6 +2729,13 @@ protected function addHydrateBody(string &$script): void } $script .= " \$this->$clo = (null !== \$col) ? PropelDateTime::newInstance(\$col, null, '$dateTimeClass') : null;"; + } elseif ($col->isUuidBinaryType()) { + $uuidSwapFlag = $this->getUuidSwapFlagLiteral(); + $script .= " + if (is_resource(\$col)) { + \$col = stream_get_contents(\$col); + } + \$this->$clo = (\$col) ? UuidConverter::binToUuid(\$col, $uuidSwapFlag) : null;"; } elseif ($col->isPhpPrimitiveType()) { $script .= " \$this->$clo = (null !== \$col) ? (" . $col->getPhpType() . ') $col : null;'; @@ -2745,8 +2765,8 @@ protected function addHydrateBody(string &$script): void if ($this->getBuildProperty('generator.objectModel.addSaveMethod')) { $script .= " - \$this->resetModified(); -"; + + \$this->resetModified();"; } $script .= " @@ -2950,10 +2970,11 @@ protected function addBuildCriteriaBody(string &$script): void \$criteria = new Criteria(" . $this->getTableMapClass() . "::DATABASE_NAME); "; foreach ($this->getTable()->getColumns() as $col) { - $clo = $col->getLowercasedName(); + $accessValueStatement = $this->getAccessValueStatement($col); + $columnConstant = $this->getColumnConstant($col); $script .= " - if (\$this->isColumnModified(" . $this->getColumnConstant($col) . ")) { - \$criteria->add(" . $this->getColumnConstant($col) . ", \$this->$clo); + if (\$this->isColumnModified($columnConstant)) { + \$criteria->add($columnConstant, $accessValueStatement); }"; } } @@ -6599,12 +6620,15 @@ protected function addDoInsertBodyRaw(): string \$stmt = \$con->prepare(\$sql); foreach (\$modifiedColumns as \$identifier => \$columnName) { switch (\$columnName) {"; + + $tab = ' '; foreach ($table->getColumns() as $column) { $columnNameCase = var_export($this->quoteIdentifier($column->getName()), true); + $accessValueStatement = $this->getAccessValueStatement($column); + $bindValueStatement = $platform->getColumnBindingPHP($column, '$identifier', $accessValueStatement, $tab); $script .= " - case $columnNameCase:"; - $script .= $platform->getColumnBindingPHP($column, '$identifier', '$this->' . $column->getLowercasedName(), ' '); - $script .= " + case $columnNameCase:$bindValueStatement + break;"; } $script .= " @@ -6645,6 +6669,30 @@ protected function addDoInsertBodyRaw(): string return $script; } + /** + * Get the statement how a column value is accessed in the script. + * + * Note that this is not necessarily just the getter. If the value is + * stored on the model in an encoded format, the statement returned by + * this method includes the statement to decode the value. + * + * @param \Propel\Generator\Model\Column $column + * + * @return string + */ + protected function getAccessValueStatement(Column $column): string + { + $columnName = $column->getLowercasedName(); + + if ($column->isUuidBinaryType()) { + $uuidSwapFlag = $this->getUuidSwapFlagLiteral(); + + return "(\$this->$columnName) ? UuidConverter::uuidToBin(\$this->$columnName, $uuidSwapFlag) : null"; + } + + return "\$this->$columnName"; + } + /** * get the doUpdate() method code * diff --git a/src/Propel/Generator/Builder/Om/QueryBuilder.php b/src/Propel/Generator/Builder/Om/QueryBuilder.php index 0ed7d6c36d..8eb3965eb0 100644 --- a/src/Propel/Generator/Builder/Om/QueryBuilder.php +++ b/src/Propel/Generator/Builder/Om/QueryBuilder.php @@ -174,6 +174,10 @@ protected function addClassBody(string &$script): void ); $this->declareClassFromBuilder($this->getStubQueryBuilder(), 'Child'); $this->declareClassFromBuilder($this->getTableMapBuilder()); + $additionalModelClasses = $table->getAdditionalModelClassImports(); + if ($additionalModelClasses) { + $this->declareClasses(...$additionalModelClasses); + } // apply behaviors $this->applyBehaviorModifier('queryAttributes', $script, ' '); @@ -1147,6 +1151,10 @@ public function filterBy$colPhpName(\$$variableName = null, ?string \$comparison if (is_string(\$$variableName)) { \$$variableName = in_array(strtolower(\$$variableName), array('false', 'off', '-', 'no', 'n', '0', '')) ? false : true; }"; + } elseif ($col->isUuidBinaryType()) { + $uuidSwapFlag = $this->getUuidSwapFlagLiteral(); + $script .= " + \$$variableName = UuidConverter::uuidToBinRecursive(\$$variableName, $uuidSwapFlag);"; } $script .= " diff --git a/src/Propel/Generator/Model/Column.php b/src/Propel/Generator/Model/Column.php index ff86938874..139be489ca 100644 --- a/src/Propel/Generator/Model/Column.php +++ b/src/Propel/Generator/Model/Column.php @@ -1333,6 +1333,16 @@ public function isUuidType(): bool return PropelTypes::isUuidType($this->getType()); } + /** + * Returns whether this column is a uuid bin type. + * + * @return bool + */ + public function isUuidBinaryType(): bool + { + return $this->getType() === PropelTypes::UUID_BINARY; + } + /** * Returns whether the column is an array column. * diff --git a/src/Propel/Generator/Model/PropelTypes.php b/src/Propel/Generator/Model/PropelTypes.php index 276232a0e3..5431c9076a 100644 --- a/src/Propel/Generator/Model/PropelTypes.php +++ b/src/Propel/Generator/Model/PropelTypes.php @@ -351,6 +351,11 @@ class PropelTypes */ public const UUID_NATIVE_TYPE = 'string'; + /** + * @var string + */ + public const UUID_BINARY = 'UUID_BINARY'; + /** * Propel mapping types. * @@ -392,6 +397,7 @@ class PropelTypes self::SET, self::JSON, self::UUID, + self::UUID_BINARY, ]; /** @@ -433,6 +439,7 @@ class PropelTypes self::GEOMETRY => self::GEOMETRY, self::JSON => self::JSON_TYPE, self::UUID => self::UUID_NATIVE_TYPE, + self::UUID_BINARY => self::UUID_NATIVE_TYPE, ]; /** @@ -479,6 +486,7 @@ class PropelTypes self::BU_TIMESTAMP => PDO::PARAM_STR, self::JSON => PDO::PARAM_STR, self::UUID => PDO::PARAM_STR, + self::UUID_BINARY => PDO::PARAM_LOB, ]; /** @@ -639,6 +647,7 @@ public static function isUuidType(string $type): bool { return in_array($type, [ self::UUID, + self::UUID_BINARY, ], true); } diff --git a/src/Propel/Generator/Model/Table.php b/src/Propel/Generator/Model/Table.php index 0b29d5472a..727a25b41d 100644 --- a/src/Propel/Generator/Model/Table.php +++ b/src/Propel/Generator/Model/Table.php @@ -18,6 +18,7 @@ use Propel\Generator\Platform\MysqlPlatform; use Propel\Generator\Platform\PlatformInterface; use Propel\Runtime\Exception\RuntimeException; +use Propel\Runtime\Util\UuidConverter; /** * Data about a table used in an application. @@ -1858,6 +1859,31 @@ public function getDatabase(): ?Database return $this->database; } + /** + * Returns a VendorInfo object by its vendor type id (i.e. "mysql"). + * + * Vendor information is set in schema.xml for the table or the whole + * database. The method returns database-wide vendor information extended + * and possibly overridden by table vendor information. + * + * @see \Propel\Generator\Model\MappingModel::getVendorInfoForType() + * + * @param string $type Vendor id, i.e. "mysql" + * + * @return \Propel\Generator\Model\VendorInfo + */ + public function getVendorInfoForType(string $type): VendorInfo + { + $tableVendorInfo = parent::getVendorInfoForType($type); + $db = $this->getDatabase(); + if (!$db) { + return $tableVendorInfo; + } + $databaseVendorInfo = $db->getVendorInfoForType($type); + + return $databaseVendorInfo->getMergedVendorInfo($tableVendorInfo); + } + /** * Get the database that contains this table. * @@ -2205,4 +2231,43 @@ public function setIdentifierQuoting(?bool $identifierQuoting): void { $this->identifierQuoting = $identifierQuoting; } + + /** + * Check if this table contains columns of the given type. + * + * @param string $type The type to check for, i.e. PropelTypes::BOOLEAN + * + * @return bool + */ + public function containsColumnsOfType(string $type): bool + { + foreach ($this->columns as $column) { + if ($column->getType() === $type) { + return true; + } + } + + return false; + } + + /** + * Get additional class imports for model and query classes needed by the columns. + * + * @psalm-return array + * + * @see \Propel\Generator\Builder\Om\ObjectBuilder::addClassBody() + * @see \Propel\Generator\Builder\Om\QueryBuilder::addClassBody() + * + * @return array|null + */ + public function getAdditionalModelClassImports(): ?array + { + if ($this->containsColumnsOfType(PropelTypes::UUID_BINARY)) { + return [ + UuidConverter::class, + ]; + } + + return null; + } } diff --git a/src/Propel/Generator/Platform/MssqlPlatform.php b/src/Propel/Generator/Platform/MssqlPlatform.php index ff2cfcb637..c6d0fe8130 100644 --- a/src/Propel/Generator/Platform/MssqlPlatform.php +++ b/src/Propel/Generator/Platform/MssqlPlatform.php @@ -58,6 +58,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::ENUM, 'TINYINT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'INT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID, 'UNIQUEIDENTIFIER')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BINARY(16)')); } /** diff --git a/src/Propel/Generator/Platform/MysqlPlatform.php b/src/Propel/Generator/Platform/MysqlPlatform.php index 435de420ae..f9ae08817c 100644 --- a/src/Propel/Generator/Platform/MysqlPlatform.php +++ b/src/Propel/Generator/Platform/MysqlPlatform.php @@ -66,6 +66,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::ENUM, 'TINYINT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'INT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::REAL, 'DOUBLE')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BINARY(16)')); } /** @@ -331,9 +332,7 @@ public function getAddTableDDL(Table $table): string */ protected function getTableOptions(Table $table): array { - $dbVI = $table->getDatabase()->getVendorInfoForType('mysql'); - $tableVI = $table->getVendorInfoForType('mysql'); - $vi = $dbVI->getMergedVendorInfo($tableVI); + $vi = $table->getVendorInfoForType('mysql'); $tableOptions = []; // List of supported table options // see http://dev.mysql.com/doc/refman/5.5/en/create-table.html diff --git a/src/Propel/Generator/Platform/OraclePlatform.php b/src/Propel/Generator/Platform/OraclePlatform.php index 13567f2a5d..1862dbaccc 100644 --- a/src/Propel/Generator/Platform/OraclePlatform.php +++ b/src/Propel/Generator/Platform/OraclePlatform.php @@ -61,6 +61,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::ENUM, 'NUMBER', 3, 0)); $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'NUMBER')); $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID, 'UUID')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'RAW(16)')); } /** diff --git a/src/Propel/Generator/Platform/PgsqlPlatform.php b/src/Propel/Generator/Platform/PgsqlPlatform.php index fe13006646..e5db9cf8ba 100755 --- a/src/Propel/Generator/Platform/PgsqlPlatform.php +++ b/src/Propel/Generator/Platform/PgsqlPlatform.php @@ -63,6 +63,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::DECIMAL, 'NUMERIC')); $this->setSchemaDomainMapping(new Domain(PropelTypes::DATETIME, 'TIMESTAMP')); $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID, 'uuid')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BYTEA')); } /** diff --git a/src/Propel/Generator/Platform/PlatformInterface.php b/src/Propel/Generator/Platform/PlatformInterface.php index d7f0aeb971..de6c06acb9 100644 --- a/src/Propel/Generator/Platform/PlatformInterface.php +++ b/src/Propel/Generator/Platform/PlatformInterface.php @@ -338,4 +338,11 @@ public function isIdentifierQuotingEnabled(): bool; * @return void */ public function setIdentifierQuoting(bool $enabled): void; + + /** + * @param \Propel\Generator\Model\Table $table + * + * @return string + */ + public function getAddTableDDL(Table $table): string; } diff --git a/src/Propel/Generator/Platform/SqlitePlatform.php b/src/Propel/Generator/Platform/SqlitePlatform.php index 4cfb174b60..be81614fc3 100644 --- a/src/Propel/Generator/Platform/SqlitePlatform.php +++ b/src/Propel/Generator/Platform/SqlitePlatform.php @@ -74,6 +74,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::PHP_ARRAY, 'MEDIUMTEXT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::ENUM, 'TINYINT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'INT')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BLOB')); } /** diff --git a/src/Propel/Runtime/Map/ColumnMap.php b/src/Propel/Runtime/Map/ColumnMap.php index 13d13938a0..4baae5cd68 100644 --- a/src/Propel/Runtime/Map/ColumnMap.php +++ b/src/Propel/Runtime/Map/ColumnMap.php @@ -301,7 +301,7 @@ public function isText(): bool */ public function isUuid(): bool { - return $this->type === PropelTypes::UUID; + return PropelTypes::isUuidType($this->type); } /** diff --git a/src/Propel/Runtime/Util/UuidConverter.php b/src/Propel/Runtime/Util/UuidConverter.php new file mode 100644 index 0000000000..68f9707c61 --- /dev/null +++ b/src/Propel/Runtime/Util/UuidConverter.php @@ -0,0 +1,92 @@ + self::uuidToBinRecursive($uuidItem, $swapFlag), $uuid); + } + + /** + * @param array|string|null $bin + * @param bool $swapFlag + * + * @return array|string|null + */ + public static function binToUuidRecursive($bin, bool $swapFlag = true) + { + if (!$bin) { + return $bin; + } + if (is_string($bin)) { + return self::binToUuid($bin, $swapFlag); + } + + return array_map(fn ($binItem) => self::binToUuidRecursive($binItem, $swapFlag), $bin); + } +} diff --git a/tests/Fixtures/bookstore/schema.xml b/tests/Fixtures/bookstore/schema.xml index 282252bab6..986c7abb5a 100644 --- a/tests/Fixtures/bookstore/schema.xml +++ b/tests/Fixtures/bookstore/schema.xml @@ -347,6 +347,10 @@ + + + + diff --git a/tests/Propel/Tests/Generator/Model/ColumnTest.php b/tests/Propel/Tests/Generator/Model/ColumnTest.php index 533248e63b..db3bfb6634 100644 --- a/tests/Propel/Tests/Generator/Model/ColumnTest.php +++ b/tests/Propel/Tests/Generator/Model/ColumnTest.php @@ -476,7 +476,8 @@ public function providePdoTypes() ['ENUM', PDO::PARAM_INT], ['BU_DATE', PDO::PARAM_STR], ['BU_TIMESTAMP', PDO::PARAM_STR], - ['UUID', PDO::PARAM_STR], + [PropelTypes::UUID, PDO::PARAM_STR], + [PropelTypes::UUID_BINARY, PDO::PARAM_LOB], ]; } @@ -711,30 +712,42 @@ public function provideMappingNumericTypes() } /** + * @dataProvider provideMappingUuidTypes + * * @return void */ - public function testUuidType() + public function testUuidType(string $columnType, string $phpType) { $domain = $this->getDomainMock(); $domain ->expects($this->once()) ->method('setType') - ->with($this->equalTo(PropelTypes::UUID)); + ->with($this->equalTo($columnType)); $domain ->expects($this->any()) ->method('getType') - ->will($this->returnValue(PropelTypes::UUID)); + ->will($this->returnValue($columnType)); $column = new Column(''); $column->setDomain($domain); - $column->setType(PropelTypes::UUID); + $column->setType($columnType); - $this->assertSame('string', $column->getPhpType()); + $this->assertSame($phpType, $column->getPhpType()); $this->assertTrue($column->isPhpPrimitiveType()); $this->assertTrue($column->isUuidType()); } + public function provideMappingUuidTypes() + { + return [ + // column type, php type, + [PropelTypes::UUID, 'string'], + [PropelTypes::UUID_BINARY, 'string'], + ]; + } + + /** * @dataProvider provideMappingTextTypes * diff --git a/tests/Propel/Tests/Generator/Platform/DefaultPlatformTest.php b/tests/Propel/Tests/Generator/Platform/DefaultPlatformTest.php index 4912fc1388..dfff740ffd 100644 --- a/tests/Propel/Tests/Generator/Platform/DefaultPlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/DefaultPlatformTest.php @@ -11,6 +11,7 @@ use Propel\Generator\Model\Column; use Propel\Generator\Model\PropelTypes; use Propel\Generator\Platform\DefaultPlatform; +use Propel\Generator\Platform\PlatformInterface; use Propel\Tests\TestCase; class DefaultPlatformTest extends TestCase @@ -22,7 +23,7 @@ class DefaultPlatformTest extends TestCase * * @return \Propel\Generator\Platform\PlatformInterface */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { if (null === $this->platform) { $this->platform = new DefaultPlatform(); diff --git a/tests/Propel/Tests/Generator/Platform/MssqlPlatformTest.php b/tests/Propel/Tests/Generator/Platform/MssqlPlatformTest.php index c150b1f045..7e0de0ebe5 100644 --- a/tests/Propel/Tests/Generator/Platform/MssqlPlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/MssqlPlatformTest.php @@ -14,6 +14,7 @@ use Propel\Generator\Model\IdMethodParameter; use Propel\Generator\Model\Table; use Propel\Generator\Platform\MssqlPlatform; +use Propel\Generator\Platform\PlatformInterface; class MssqlPlatformTest extends PlatformTestProvider { @@ -22,7 +23,7 @@ class MssqlPlatformTest extends PlatformTestProvider * * @return \Propel\Generator\Platform\MssqlPlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { return new MssqlPlatform(); } @@ -705,7 +706,6 @@ public function testGetCommentBlockDDL() */ public function testCreateSchemaWithUuidColumns($schema) { - $table = $this->getTableFromSchema($schema); $expected = " CREATE TABLE [foo] ( @@ -714,6 +714,25 @@ public function testCreateSchemaWithUuidColumns($schema) CONSTRAINT [foo_pk] PRIMARY KEY ([uuid]) ); "; - $this->assertEquals($expected, $this->getPlatform()->getAddTableDDL($table)); + + $this->assertCreateTableMatches($expected, $schema); + } + + /** + * @dataProvider providerForTestCreateSchemaWithUuidBinaryColumns + * + * @return void + */ + public function testCreateSchemaWithUuidBinaryColumns($schema) + { + $expected = " +CREATE TABLE [foo] +( + [uuid-bin] BINARY(16) DEFAULT vendor_specific_default() NOT NULL, + [other_uuid-bin] BINARY(16) NULL, + CONSTRAINT [foo_pk] PRIMARY KEY ([uuid-bin]) +); +"; + $this->assertCreateTableMatches($expected, $schema); } } diff --git a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationMyISAMTest.php b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationMyISAMTest.php index e83dfae190..4bf6badd1c 100644 --- a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationMyISAMTest.php +++ b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationMyISAMTest.php @@ -10,6 +10,7 @@ use Propel\Generator\Config\GeneratorConfig; use Propel\Generator\Platform\MysqlPlatform; +use Propel\Generator\Platform\PlatformInterface; use Propel\Generator\Util\VfsTrait; class MysqlPlatformMigrationMyISAMTest extends PlatformMigrationTestProvider @@ -21,9 +22,9 @@ class MysqlPlatformMigrationMyISAMTest extends PlatformMigrationTestProvider /** * Get the Platform object for this class * - * @return \Propel\Generator\Platform\PlatformInterface + * @return \Propel\Generator\Platform\MysqlPlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { if (!$this->platform) { $this->platform = new MysqlPlatform(); diff --git a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php index 234a8fe649..8bda8a48fc 100644 --- a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php +++ b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php @@ -11,6 +11,7 @@ use Propel\Generator\Config\GeneratorConfig; use Propel\Generator\Model\Diff\DatabaseComparator; use Propel\Generator\Platform\MysqlPlatform; +use Propel\Generator\Platform\PlatformInterface; use Propel\Generator\Util\VfsTrait; class MysqlPlatformMigrationTest extends MysqlPlatformMigrationTestProvider @@ -18,16 +19,16 @@ class MysqlPlatformMigrationTest extends MysqlPlatformMigrationTestProvider use VfsTrait; /** - * @var \Propel\Generator\Platform\PlatformInterface|null + * @var \Propel\Generator\Platform\MysqlPlatform|null */ protected $platform; /** * Get the Platform object for this class * - * @return \Propel\Generator\Platform\PlatformInterface + * @return \Propel\Generator\Platform\MysqlPlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { if (!$this->platform) { $this->platform = new MysqlPlatform(); diff --git a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMyISAMTest.php b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMyISAMTest.php index 43b2cdd2b9..532f97de5b 100644 --- a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMyISAMTest.php +++ b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMyISAMTest.php @@ -18,6 +18,7 @@ use Propel\Generator\Model\Table; use Propel\Generator\Model\VendorInfo; use Propel\Generator\Platform\MysqlPlatform; +use Propel\Generator\Platform\PlatformInterface; class MysqlPlatformMyISAMTest extends PlatformTestProvider { @@ -26,7 +27,7 @@ class MysqlPlatformMyISAMTest extends PlatformTestProvider * * @return \Propel\Generator\Platform\MysqlPlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { static $platform; diff --git a/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php b/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php index ba61fdbcb8..a071b98402 100644 --- a/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php @@ -19,6 +19,7 @@ use Propel\Generator\Model\VendorInfo; use Propel\Generator\Platform\MysqlPlatform; use Propel\Generator\Model\PropelTypes; +use Propel\Generator\Platform\PlatformInterface; class MysqlPlatformTest extends PlatformTestProvider { @@ -27,7 +28,7 @@ class MysqlPlatformTest extends PlatformTestProvider * * @return \Propel\Generator\Platform\MysqlPlatform */ - protected function getPlatform() + protected function getPlatform():PlatformInterface { static $platform; @@ -1003,4 +1004,23 @@ public function testCreateSchemaWithUuidColumns($schema) $table = $this->getTableFromSchema($schema); } + + /** + * @dataProvider providerForTestCreateSchemaWithUuidBinaryColumns + * + * @return void + */ + public function testCreateSchemaWithUuidBinaryColumns($schema) + { + $expected = " +CREATE TABLE `foo` +( + `uuid-bin` BINARY(16) DEFAULT vendor_specific_default() NOT NULL, + `other_uuid-bin` BINARY(16), + PRIMARY KEY (`uuid-bin`) +) ENGINE=InnoDB; +"; + + $this->assertCreateTableMatches($expected, $schema); + } } diff --git a/tests/Propel/Tests/Generator/Platform/OraclePlatformMigrationTest.php b/tests/Propel/Tests/Generator/Platform/OraclePlatformMigrationTest.php index 0f2645d0c3..841a321ef6 100644 --- a/tests/Propel/Tests/Generator/Platform/OraclePlatformMigrationTest.php +++ b/tests/Propel/Tests/Generator/Platform/OraclePlatformMigrationTest.php @@ -10,15 +10,16 @@ use Propel\Generator\Model\Diff\DatabaseComparator; use Propel\Generator\Platform\OraclePlatform; +use Propel\Generator\Platform\PlatformInterface; class OraclePlatformMigrationTest extends PlatformMigrationTestProvider { /** * Get the Platform object for this class * - * @return \Propel\Generator\Platform\PlatformInterface + * @return \Propel\Generator\Platform\OraclePlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { return new OraclePlatform(); } diff --git a/tests/Propel/Tests/Generator/Platform/OraclePlatformTest.php b/tests/Propel/Tests/Generator/Platform/OraclePlatformTest.php index 2fa7840e8c..72c9278970 100644 --- a/tests/Propel/Tests/Generator/Platform/OraclePlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/OraclePlatformTest.php @@ -14,15 +14,16 @@ use Propel\Generator\Model\IdMethodParameter; use Propel\Generator\Model\Table; use Propel\Generator\Platform\OraclePlatform; +use Propel\Generator\Platform\PlatformInterface; class OraclePlatformTest extends PlatformTestProvider { /** * Get the Platform object for this class * - * @return \Propel\Generator\Platform\PlatformInterface + * @return \Propel\Generator\Platform\OraclePlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { return new OraclePlatform(); } @@ -659,7 +660,6 @@ public function testGetOracleBlockStorageDDL() */ public function testCreateSchemaWithUuidColumns($schema) { - $table = $this->getTableFromSchema($schema); $expected = " CREATE TABLE foo ( @@ -669,6 +669,25 @@ public function testCreateSchemaWithUuidColumns($schema) ALTER TABLE foo ADD CONSTRAINT foo_pk PRIMARY KEY (uuid); "; - $this->assertEquals($expected, $this->getPlatform()->getAddTableDDL($table)); + $this->assertCreateTableMatches($expected, $schema); + } + + /** + * @dataProvider providerForTestCreateSchemaWithUuidBinaryColumns + * + * @return void + */ + public function testCreateSchemaWithUuidBinaryColumns($schema) + { + $expected = " +CREATE TABLE foo +( + uuid-bin RAW(16) DEFAULT vendor_specific_default() NOT NULL, + other_uuid-bin RAW(16) +); + +ALTER TABLE foo ADD CONSTRAINT foo_pk PRIMARY KEY (uuid-bin); +"; + $this->assertCreateTableMatches($expected, $schema); } } diff --git a/tests/Propel/Tests/Generator/Platform/PgsqlPlatformMigrationTest.php b/tests/Propel/Tests/Generator/Platform/PgsqlPlatformMigrationTest.php index 112bc4910b..3a2838be91 100755 --- a/tests/Propel/Tests/Generator/Platform/PgsqlPlatformMigrationTest.php +++ b/tests/Propel/Tests/Generator/Platform/PgsqlPlatformMigrationTest.php @@ -12,18 +12,18 @@ use Propel\Generator\Model\Column; use Propel\Generator\Model\ColumnDefaultValue; use Propel\Generator\Model\Diff\ColumnComparator; -use Propel\Generator\Model\Diff\TableDiff; use Propel\Generator\Model\Table; use Propel\Generator\Platform\PgsqlPlatform; +use Propel\Generator\Platform\PlatformInterface; class PgsqlPlatformMigrationTest extends PlatformMigrationTestProvider { /** * Get the Platform object for this class * - * @return PgsqlPlatform + * @return \Propel\Generator\Platform\PgsqlPlatform */ - protected function getPlatform(): PgsqlPlatform + protected function getPlatform(): PlatformInterface { return new PgsqlPlatform(); } @@ -463,6 +463,23 @@ public function testMigrateToUUIDColumn($tableDiff) ALTER TABLE "foo" ALTER COLUMN "id" SET DEFAULT vendor_specific_uuid_generator_function(); +END; + $this->assertEquals($expected, $this->getPlatform()->getModifyTableColumnsDDL($tableDiff)); + } + + /** + * @dataProvider providerForTestMigrateToUuidBinColumn + * + * @return void + */ + public function testMigrateToUuidBinColumn($tableDiff) + { + $expected = <<assertEquals($expected, $this->getPlatform()->getModifyTableColumnsDDL($tableDiff)); } diff --git a/tests/Propel/Tests/Generator/Platform/PgsqlPlatformTest.php b/tests/Propel/Tests/Generator/Platform/PgsqlPlatformTest.php index 952ee58669..d75950adfc 100755 --- a/tests/Propel/Tests/Generator/Platform/PgsqlPlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/PgsqlPlatformTest.php @@ -17,6 +17,7 @@ use Propel\Generator\Model\Table; use Propel\Generator\Model\Unique; use Propel\Generator\Platform\PgsqlPlatform; +use Propel\Generator\Platform\PlatformInterface; class PgsqlPlatformTest extends PlatformTestProvider { @@ -25,7 +26,7 @@ class PgsqlPlatformTest extends PlatformTestProvider * * @return \Propel\Generator\Platform\PgsqlPlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { return new PgsqlPlatform(); } @@ -835,6 +836,15 @@ public function testGetCommentBlockDDL() $this->assertEquals($expected, $this->getPlatform()->getCommentBlockDDL('foo bar')); } + /** + * @return void + */ + public function assertCreateTableMatches(string $expected, $schema, ?string $tableName = 'foo' ) + { + $table = $this->getTableFromSchema($schema, $tableName); + $this->assertEquals($expected, $this->getPlatform()->getAddTableDDL($table)); + } + /** * @dataProvider providerForTestCreateSchemaWithUuidColumns * @@ -842,7 +852,6 @@ public function testGetCommentBlockDDL() */ public function testCreateSchemaWithUuidColumns($schema) { - $table = $this->getTableFromSchema($schema); $expected = <<< 'EOT' CREATE TABLE "foo" @@ -853,6 +862,26 @@ public function testCreateSchemaWithUuidColumns($schema) ); EOT; - $this->assertEquals($expected, $this->getPlatform()->getAddTableDDL($table)); + $this->assertCreateTableMatches($expected, $schema); + } + + /** + * @dataProvider providerForTestCreateSchemaWithUuidBinaryColumns + * + * @return void + */ + public function testCreateSchemaWithUuidBinaryColumns($schema) + { + $expected = <<< 'EOT' + +CREATE TABLE "foo" +( + "uuid-bin" BYTEA DEFAULT vendor_specific_default() NOT NULL, + "other_uuid-bin" BYTEA, + PRIMARY KEY ("uuid-bin") +); + +EOT; + $this->assertCreateTableMatches($expected, $schema); } } diff --git a/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php b/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php index 4dce38d5cc..6b2abafd07 100644 --- a/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php +++ b/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php @@ -620,4 +620,16 @@ public function providerForTestMigrateToUUIDColumn() return [[$this->buildTableDiff('foo', $tableColumnsFrom, $tableColumnsTo)]]; } + + public function providerForTestMigrateToUuidBinColumn() + { + $tableColumnsFrom = << +EOF; + $tableColumnsTo = << +EOF; + + return [[$this->buildTableDiff('foo', $tableColumnsFrom, $tableColumnsTo)]]; + } } diff --git a/tests/Propel/Tests/Generator/Platform/PlatformTestProvider.php b/tests/Propel/Tests/Generator/Platform/PlatformTestProvider.php index fb43f71649..5bbfae9221 100644 --- a/tests/Propel/Tests/Generator/Platform/PlatformTestProvider.php +++ b/tests/Propel/Tests/Generator/Platform/PlatformTestProvider.php @@ -21,6 +21,15 @@ */ abstract class PlatformTestProvider extends PlatformTestBase { + /** + * @return void + */ + public function assertCreateTableMatches(string $expected, $schema, ?string $tableName = 'foo' ) + { + $table = $this->getTableFromSchema($schema, $tableName); + $this->assertEquals($expected, $this->getPlatform()->getAddTableDDL($table)); + } + /** * @return string[][] */ @@ -370,6 +379,20 @@ public function providerForTestCreateSchemaWithUuidColumns() +EOF; + + return [[$schema]]; + } + + public function providerForTestCreateSchemaWithUuidBinaryColumns() + { + $schema = << + + + +
+ EOF; return [[$schema]]; diff --git a/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php b/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php index e0adbf0414..b7179209aa 100644 --- a/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php @@ -14,6 +14,7 @@ use Propel\Generator\Model\IdMethodParameter; use Propel\Generator\Model\PropelTypes; use Propel\Generator\Model\Table; +use Propel\Generator\Platform\PlatformInterface; use Propel\Generator\Platform\SqlitePlatform; use Propel\Runtime\Adapter\AdapterFactory; use Propel\Runtime\Connection\ConnectionFactory; @@ -23,9 +24,9 @@ class SqlitePlatformTest extends PlatformTestProvider /** * Get the Platform object for this class * - * @return \Propel\Generator\Platform\PlatformInterface + * @return \Propel\Generator\Platform\SqlitePlatform */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { return new SqlitePlatform(); } @@ -459,4 +460,23 @@ public function testCreateSchemaWithUuidColumns($schema) $table = $this->getTableFromSchema($schema); } + + /** + * @dataProvider providerForTestCreateSchemaWithUuidBinaryColumns + * + * @return void + */ + public function testCreateSchemaWithUuidBinaryColumns($schema) + { + $expected = " +CREATE TABLE [foo] +( + [uuid-bin] BLOB DEFAULT vendor_specific_default() NOT NULL, + [other_uuid-bin] BLOB, + PRIMARY KEY ([uuid-bin]) +); +"; + + $this->assertCreateTableMatches($expected, $schema); + } } diff --git a/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php b/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php index b671129162..ccac73bf5c 100644 --- a/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php +++ b/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php @@ -31,19 +31,23 @@ protected function getDriver() { return 'oracle'; } + + protected function createOracleSql(Criteria $query, &$params = []): string + { + Propel::getServiceContainer()->setAdapter('oracle', new OracleAdapter()); + $query->setDbName('oracle'); + return $query->createSelectSql($params); + } /** * @return void */ public function testApplyLimitSimple() { - Propel::getServiceContainer()->setAdapter('oracle', new OracleAdapter()); $c = new Criteria(); - $c->setDbName('oracle'); BookTableMap::addSelectColumns($c); $c->setLimit(1); - $params = []; - $sql = $c->createSelectSql($params); + $sql = $this->createOracleSql($c); $this->assertEquals('SELECT B.* FROM (SELECT A.*, rownum AS PROPEL_ROWNUM FROM (SELECT book.id, book.title, book.isbn, book.price, book.publisher_id, book.author_id FROM book) A ) B WHERE B.PROPEL_ROWNUM <= 1', $sql, 'applyLimit() creates a subselect with the original column names by default'); } @@ -52,14 +56,11 @@ public function testApplyLimitSimple() */ public function testApplyLimitDuplicateColumnName() { - Propel::getServiceContainer()->setAdapter('oracle', new OracleAdapter()); $c = new Criteria(); - $c->setDbName('oracle'); BookTableMap::addSelectColumns($c); AuthorTableMap::addSelectColumns($c); $c->setLimit(1); - $params = []; - $sql = $c->createSelectSql($params); + $sql = $this->createOracleSql($c); $this->assertEquals('SELECT B.* FROM (SELECT A.*, rownum AS PROPEL_ROWNUM FROM (SELECT book.id AS ORA_COL_ALIAS_0, book.title AS ORA_COL_ALIAS_1, book.isbn AS ORA_COL_ALIAS_2, book.price AS ORA_COL_ALIAS_3, book.publisher_id AS ORA_COL_ALIAS_4, book.author_id AS ORA_COL_ALIAS_5, author.id AS ORA_COL_ALIAS_6, author.first_name AS ORA_COL_ALIAS_7, author.last_name AS ORA_COL_ALIAS_8, author.email AS ORA_COL_ALIAS_9, author.age AS ORA_COL_ALIAS_10 FROM book, author) A ) B WHERE B.PROPEL_ROWNUM <= 1', $sql, 'applyLimit() creates a subselect with aliased column names when a duplicate column name is found'); } @@ -68,16 +69,13 @@ public function testApplyLimitDuplicateColumnName() */ public function testApplyLimitDuplicateColumnNameWithColumn() { - Propel::getServiceContainer()->setAdapter('oracle', new OracleAdapter()); $c = new Criteria(); - $c->setDbName('oracle'); BookTableMap::addSelectColumns($c); AuthorTableMap::addSelectColumns($c); $c->addAsColumn('BOOK_PRICE', BookTableMap::COL_PRICE); $c->setLimit(1); - $params = []; $asColumns = $c->getAsColumns(); - $sql = $c->createSelectSql($params); + $sql = $this->createOracleSql($c); $this->assertEquals('SELECT B.* FROM (SELECT A.*, rownum AS PROPEL_ROWNUM FROM (SELECT book.id AS ORA_COL_ALIAS_0, book.title AS ORA_COL_ALIAS_1, book.isbn AS ORA_COL_ALIAS_2, book.price AS ORA_COL_ALIAS_3, book.publisher_id AS ORA_COL_ALIAS_4, book.author_id AS ORA_COL_ALIAS_5, author.id AS ORA_COL_ALIAS_6, author.first_name AS ORA_COL_ALIAS_7, author.last_name AS ORA_COL_ALIAS_8, author.email AS ORA_COL_ALIAS_9, author.age AS ORA_COL_ALIAS_10, book.price AS BOOK_PRICE FROM book, author) A ) B WHERE B.PROPEL_ROWNUM <= 1', $sql, 'applyLimit() creates a subselect with aliased column names when a duplicate column name is found'); $this->assertEquals($asColumns, $c->getAsColumns(), 'createSelectSql supplementary add alias column'); } @@ -87,15 +85,11 @@ public function testApplyLimitDuplicateColumnNameWithColumn() */ public function testCreateSelectSqlPart() { - Propel::getServiceContainer()->setAdapter('oracle', new OracleAdapter()); - $db = Propel::getServiceContainer()->getAdapter(); $c = new Criteria(); $c->addSelectColumn(BookTableMap::COL_ID); $c->addAsColumn('book_ID', BookTableMap::COL_ID); - $fromClause = []; - $selectSql = $db->createSelectSqlPart($c, $fromClause); - $this->assertEquals('SELECT book.id, book.id AS book_ID', $selectSql, 'createSelectSqlPart() returns a SQL SELECT clause with both select and as columns'); - $this->assertEquals(['book'], $fromClause, 'createSelectSqlPart() adds the tables from the select columns to the from clause'); + $selectSql = $this->createOracleSql($c); + $this->assertEquals('SELECT book.id, book.id AS book_ID FROM book', $selectSql, 'createSelectSqlPart() returns a SQL SELECT clause with both select and as columns'); } /** @@ -109,8 +103,7 @@ public function testSimpleLock(): void $c->addSelectColumn(BookTableMap::COL_ID); $c->lockForShare(); - $params = []; - $result = $c->createSelectSql($params); + $result = $this->createOracleSql($c); $expected = 'SELECT book.id FROM book LOCK IN SHARE MODE'; @@ -128,8 +121,7 @@ public function testComplexLock(): void $c->addSelectColumn(BookTableMap::COL_ID); $c->lockForUpdate([BookTableMap::TABLE_NAME], true); - $params = []; - $result = $c->createSelectSql($params); + $result = $this->createOracleSql($c); $expected = 'SELECT book.id FROM book FOR UPDATE'; diff --git a/tests/Propel/Tests/Runtime/TypeTest.php b/tests/Propel/Tests/Runtime/TypeTests/TypeTest.php similarity index 99% rename from tests/Propel/Tests/Runtime/TypeTest.php rename to tests/Propel/Tests/Runtime/TypeTests/TypeTest.php index 028a279655..7bfc450fbd 100644 --- a/tests/Propel/Tests/Runtime/TypeTest.php +++ b/tests/Propel/Tests/Runtime/TypeTests/TypeTest.php @@ -6,7 +6,7 @@ * file that was distributed with this source code. */ -namespace Propel\Tests\Runtime; +namespace Propel\Tests\Runtime\TypeTest; use Propel\Tests\Bookstore\Map\TypeObjectTableMap; use Propel\Tests\Bookstore\TypeObject; diff --git a/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php b/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php new file mode 100644 index 0000000000..6f35ee1888 --- /dev/null +++ b/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php @@ -0,0 +1,127 @@ +book){ + Book2Query::create()->deleteAll(); + $this->book = new Book2(); + $this->book->setUuidBin($this->uuid)->save(); + } + Book2TableMap::clearInstancePool(); + } + + /** + * @return void + */ + public function testModelRestoresUuid() + { + $retrievedBook = Book2Query::create()->findOneById($this->book->getId()); + $this->assertSame($this->uuid, $retrievedBook->getUuidBin()); + } + + public function uuidFilterDataProvider(): array + { + return [ + // description, uuid value + ['single uuid', + 'b41a29db-cf78-4d43-83a9-4cd3e1e1b41a' + ], + ['uuid array', [ + 'b41a29db-cf78-4d43-83a9-4cd3e1e1b41a', + '5875b237-21a2-4e7c-a976-73c6f0f6af4e', + 'b1b838f9-0212-4638-b065-9c1ba291f55f'] + ], + ['uuid array with null', [ + 'b41a29db-cf78-4d43-83a9-4cd3e1e1b41a', + null, + '5875b237-21a2-4e7c-a976-73c6f0f6af4e', + 'b1b838f9-0212-4638-b065-9c1ba291f55f'] + ], + ]; + } + + /** + * @dataProvider uuidFilterDataProvider + * @return void + */ + public function testQueryConvertsUuidParamToBin(string $description, $uuidValue) + { + $params = []; + Book2Query::create()->filterByUuidBin($uuidValue)->createSelectSql($params); + $paramValue = $params[0]['value']; + + $expectedBin = UuidConverter::uuidToBinRecursive($uuidValue, true); + $this->assertSame($expectedBin, $paramValue, $description . ' - Uuid query params should be converted'); + } + + public function queryConfiguratorDataProvider(){ + $uuidBin = UuidConverter::uuidToBin($this->uuid, true); + + return [ + // description, configurator + //['where string', fn(Book2Query $query) => $query->where("book2.uuid_bin = '$uuidBin'")], + ['where with param', fn(Book2Query $query) => $query->where("book2.uuid_bin = ?", $uuidBin, \PDO::PARAM_LOB)], + ['filterBy', fn(Book2Query $query) => $query->filterByUuidBin($this->uuid)], + ]; + } + + /** + * @dataProvider queryConfiguratorDataProvider + * + * @return void + */ + public function testQueryResolvesUuidFilter(string $description, $queryConfigurator) + { + $bookQuery = Book2Query::create(); + $queryConfigurator($bookQuery); + $result = $bookQuery->find(); + + $this->assertEquals(1, $result->count()); + $loadedBook = $result[0]; + $this->assertSame($this->book->getId(), $loadedBook->getId(), 'should retrive data trough '.$description); + } + + /** + * @return void + */ + public function testModelCanUpdateUuid() + { + $book = new Book2(); + $book->save(); + + $updateUuid = 'a6afa354-27d1-458c-aee1-7118d08ab063'; + + $book->setUuidBin($updateUuid)->save(); + $book->reload(); + + $this->assertSame($updateUuid, $book->getUuidBin()); + } +} diff --git a/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php b/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php new file mode 100644 index 0000000000..57b3ce4bfb --- /dev/null +++ b/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php @@ -0,0 +1,89 @@ +isAtLeastMysql8 === null) { + $this->isAtLeastMysql8 = $this->getMySqlVersionAtLeast8(); + } + if (!$this->isAtLeastMysql8) { + $this->markTestSkipped('Test can only be run on MySQL version >= 8'); + } + } + + protected function getMySqlVersionAtLeast8(): bool + { + $con = Propel::getServiceContainer()->getConnection(); + $query = "SELECT VERSION() NOT LIKE '%MariaDB%' AND VERSION() >= 8"; + $result = $con->query($query)->fetchColumn(0); + + return (bool) $result; + } + + public function operationsDataProvider(): array + { + return [ + // description, mysql function , converter callback, input value, input bin + ['uuid to bin without swap', 'SELECT UUID_TO_BIN(?, false)', fn($uuid) => UuidConverter::uuidToBin($uuid, false), false], + ['uuid to bin with swap', 'SELECT UUID_TO_BIN(?, true)', fn($uuid) => UuidConverter::uuidToBin($uuid, true), false], + + ['bin to uuid without swap', 'SELECT BIN_TO_UUID(?, false)', fn($uuid) => UuidConverter::binToUuid($uuid, false), true], + ['bin to uuid with swap', 'SELECT BIN_TO_UUID(?, true)', fn($uuid) => UuidConverter::binToUuid($uuid, true), true], + + ]; + } + + /** + * @dataProvider operationsDataProvider + */ + public function testBinToUuidBehavesLikeInMysql($description, $sqlStatement, $callback, $inputBin) + { + $value = ($inputBin) + ? hex2bin('aab5d5fd70c111e5a4fbb026b977eb28') + : 'aab5d5fd-70c1-11e5-a4fb-b026b977eb28' + ; + $mysqlBin = $this->executeStatement($sqlStatement, $value); + + $propelBin = $callback($value); + $this->assertSame($mysqlBin, $propelBin, $description . ' should match between Propel and MySQL'); + } + + public function executeStatement(string $statement, string $value) + { + $con = Propel::getServiceContainer()->getConnection(); + $ps = $con->prepare($statement); + $ps->bindParam(1, $value, PDO::PARAM_STR); + $ps->execute(); + $result = $ps->fetch()[0]; + $ps->closeCursor(); + + return $result; + } + + +} + diff --git a/tests/Propel/Tests/Runtime/Util/UuidConverterTest.php b/tests/Propel/Tests/Runtime/Util/UuidConverterTest.php new file mode 100644 index 0000000000..c88bfadf74 --- /dev/null +++ b/tests/Propel/Tests/Runtime/Util/UuidConverterTest.php @@ -0,0 +1,90 @@ +assertBinaryEquals($hexWithSwap, $result); + } + + /** + * @dataProvider uuidDataProvider + * @return void + */ + public function testUuidToBinWithoutSwap($uuid, $hex, $hexWithSwap) + { + $result = UuidConverter::uuidToBin($uuid, false); + $this->assertBinaryEquals($hex, $result); + } + + /** + */ + public function assertBinaryEquals(string $expected, $result) + { + $expected = hex2bin($expected); + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider uuidDataProvider + * @return void + */ + public function testBinToUuidWithSwap($uuid, $hex, $hexWithSwap) + { + $bin = hex2bin($hexWithSwap); + $result = UuidConverter::binToUuid($bin, true); + $this->assertEquals($uuid, $result); + } + + /** + * @dataProvider uuidDataProvider + * @return void + */ + public function testBinToUuidWithoutSwap($uuid, $hex, $hexWithSwap) + { + $bin = hex2bin($hex); + $result = UuidConverter::binToUuid($bin, false); + $this->assertEquals($uuid, $result); + } + + public function testFasterUuidToBin(){ + $this->markTestSkipped(); + $uuid = []; + + for($i = 0; $i < 100000; $i++){ + $uuids[] = $this->guidv4(); + } + $swapFlag = true; + $regexDuration = $this->measure([UuidConverter::class, 'uuidToBinRegex'], $uuids, $swapFlag); + $regularDuration = $this->measure([UuidConverter::class, 'uuidToBin'], $uuids, $swapFlag); + + echo "regular took $regularDuration, regex took $regexDuration"; + $this->assertLessThanOrEqual($regexDuration, $regularDuration, "regular took $regularDuration, regex took $regexDuration"); + } + +} diff --git a/tests/Propel/Tests/TestCase.php b/tests/Propel/Tests/TestCase.php index 77fdfe34bf..bc738a7bc3 100644 --- a/tests/Propel/Tests/TestCase.php +++ b/tests/Propel/Tests/TestCase.php @@ -9,6 +9,7 @@ namespace Propel\Tests; use PHPUnit\Framework\TestCase as PHPUnitTestCase; +use Propel\Generator\Platform\PlatformInterface; class TestCase extends PHPUnitTestCase { @@ -104,7 +105,7 @@ protected function runningOnMSSQL() /** * @return \Propel\Generator\Platform\PlatformInterface */ - protected function getPlatform() + protected function getPlatform(): PlatformInterface { $className = sprintf('\\Propel\\Generator\\Platform\\%sPlatform', ucfirst($this->getDriver())); From 071b388c27f098e8a9bbf3a06fa534253b391d23 Mon Sep 17 00:00:00 2001 From: mringler Date: Thu, 1 Dec 2022 16:36:52 +0100 Subject: [PATCH 2/8] use UUID_BINARY as default column for UUID --- .../Generator/Platform/DefaultPlatform.php | 9 +-- .../Generator/Platform/MysqlPlatform.php | 4 +- .../Generator/Platform/SqlitePlatform.php | 5 +- tests/Fixtures/bookstore/schema.xml | 1 + .../Generator/Platform/MysqlPlatformTest.php | 28 +++----- .../Generator/Platform/SqlitePlatformTest.php | 28 +++----- .../Runtime/TypeTests/UuidBinaryTypeTest.php | 1 - .../Tests/Runtime/TypeTests/UuidTypeTest.php | 70 +++++++++++++++++++ 8 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 tests/Propel/Tests/Runtime/TypeTests/UuidTypeTest.php diff --git a/src/Propel/Generator/Platform/DefaultPlatform.php b/src/Propel/Generator/Platform/DefaultPlatform.php index fe93cc9553..6274bd2f57 100644 --- a/src/Propel/Generator/Platform/DefaultPlatform.php +++ b/src/Propel/Generator/Platform/DefaultPlatform.php @@ -1495,13 +1495,8 @@ public function getColumnBindingPHP(Column $column, string $identifier, string $ }"; } - $script .= sprintf( - " -\$stmt->bindValue(%s, %s, %s);", - $identifier, - $columnValueAccessor, - PropelTypes::getPdoTypeString($column->getType()), - ); + $pdoType = PropelTypes::getPdoTypeString($column->getType()); + $script .= "\n\$stmt->bindValue($identifier, $columnValueAccessor, $pdoType);"; return preg_replace('/^(.+)/m', $tab . '$1', $script); } diff --git a/src/Propel/Generator/Platform/MysqlPlatform.php b/src/Propel/Generator/Platform/MysqlPlatform.php index f9ae08817c..ff3f62d599 100644 --- a/src/Propel/Generator/Platform/MysqlPlatform.php +++ b/src/Propel/Generator/Platform/MysqlPlatform.php @@ -53,7 +53,6 @@ class MysqlPlatform extends DefaultPlatform protected function initialize(): void { parent::initialize(); - unset($this->schemaDomainMap[PropelTypes::UUID]); $this->setSchemaDomainMapping(new Domain(PropelTypes::BOOLEAN, 'TINYINT', 1)); $this->setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, 'DECIMAL')); $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, 'TEXT')); @@ -67,6 +66,9 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'INT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::REAL, 'DOUBLE')); $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BINARY(16)')); + + // no native UUID type, use UUID_BINARY + $this->schemaDomainMap[PropelTypes::UUID] = $this->schemaDomainMap[PropelTypes::UUID_BINARY]; } /** diff --git a/src/Propel/Generator/Platform/SqlitePlatform.php b/src/Propel/Generator/Platform/SqlitePlatform.php index be81614fc3..91a1525625 100644 --- a/src/Propel/Generator/Platform/SqlitePlatform.php +++ b/src/Propel/Generator/Platform/SqlitePlatform.php @@ -59,8 +59,6 @@ protected function initialize(): void $this->foreignKeySupport = version_compare($version, '3.6.19') >= 0; - unset($this->schemaDomainMap[PropelTypes::UUID]); - $this->setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, 'DECIMAL')); $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, 'MEDIUMTEXT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::DATE, 'DATETIME')); @@ -75,6 +73,9 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::ENUM, 'TINYINT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'INT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BLOB')); + + // no native UUID type, use UUID_BINARY + $this->schemaDomainMap[PropelTypes::UUID] = $this->schemaDomainMap[PropelTypes::UUID_BINARY]; } /** diff --git a/tests/Fixtures/bookstore/schema.xml b/tests/Fixtures/bookstore/schema.xml index 986c7abb5a..e9798e9399 100644 --- a/tests/Fixtures/bookstore/schema.xml +++ b/tests/Fixtures/bookstore/schema.xml @@ -347,6 +347,7 @@ + diff --git a/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php b/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php index a071b98402..845ff5f0e1 100644 --- a/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/MysqlPlatformTest.php @@ -975,23 +975,6 @@ public function testTypeMapping(string $propelDataType, string $expectedMysqlDat $actualMysqlDataType = $this->getPlatform()->getDomainForType($propelDataType)->getSqlType(); $this->assertEquals($expectedMysqlDataType, $actualMysqlDataType); } - - public function unavailableTypesDataProvider() - { - return [ - [PropelTypes::UUID], - ]; - } - - /** - * @dataProvider unavailableTypesDataProvider - */ - public function testExceptionOnAccessOfUnavailableType(string $propelDataType) - { - $this->expectException(\Propel\Generator\Exception\EngineException::class); - - $this->getPlatform()->getDomainForType($propelDataType); - } /** * @dataProvider providerForTestCreateSchemaWithUuidColumns @@ -1000,9 +983,16 @@ public function testExceptionOnAccessOfUnavailableType(string $propelDataType) */ public function testCreateSchemaWithUuidColumns($schema) { - $this->expectException(\Propel\Generator\Exception\EngineException::class); + $expected = " +CREATE TABLE `foo` +( + `uuid` BINARY(16) DEFAULT vendor_specific_default() NOT NULL, + `other_uuid` BINARY(16), + PRIMARY KEY (`uuid`) +) ENGINE=InnoDB; +"; - $table = $this->getTableFromSchema($schema); + $this->assertCreateTableMatches($expected, $schema); } /** diff --git a/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php b/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php index b7179209aa..fd22da16e4 100644 --- a/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php +++ b/tests/Propel/Tests/Generator/Platform/SqlitePlatformTest.php @@ -431,23 +431,6 @@ public function testGetCommentBlockDDL() "; $this->assertEquals($expected, $this->getPlatform()->getCommentBlockDDL('foo bar')); } - - public function unavailableTypesDataProvider() - { - return [ - [PropelTypes::UUID], - ]; - } - - /** - * @dataProvider unavailableTypesDataProvider - */ - public function testExceptionOnAccessOfUnavailableType(string $propelDataType) - { - $this->expectException(\Propel\Generator\Exception\EngineException::class); - - $this->getPlatform()->getDomainForType($propelDataType); - } /** * @dataProvider providerForTestCreateSchemaWithUuidColumns @@ -456,9 +439,16 @@ public function testExceptionOnAccessOfUnavailableType(string $propelDataType) */ public function testCreateSchemaWithUuidColumns($schema) { - $this->expectException(\Propel\Generator\Exception\EngineException::class); + $expected = " +CREATE TABLE [foo] +( + [uuid] BLOB DEFAULT vendor_specific_default() NOT NULL, + [other_uuid] BLOB, + PRIMARY KEY ([uuid]) +); +"; - $table = $this->getTableFromSchema($schema); + $this->assertCreateTableMatches($expected, $schema); } /** diff --git a/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php b/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php index 6f35ee1888..95cd7968fa 100644 --- a/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php +++ b/tests/Propel/Tests/Runtime/TypeTests/UuidBinaryTypeTest.php @@ -8,7 +8,6 @@ namespace Propel\Tests\Runtime\TypeTest; -use Propel\Runtime\Propel; use Propel\Runtime\Util\UuidConverter; use Propel\Tests\Bookstore\Base\Book2Query; use Propel\Tests\Bookstore\Book2; diff --git a/tests/Propel/Tests/Runtime/TypeTests/UuidTypeTest.php b/tests/Propel/Tests/Runtime/TypeTests/UuidTypeTest.php new file mode 100644 index 0000000000..cc172f78c7 --- /dev/null +++ b/tests/Propel/Tests/Runtime/TypeTests/UuidTypeTest.php @@ -0,0 +1,70 @@ +book) { + Book2Query::create()->deleteAll(); + $this->book = new Book2(); + $this->book->setUuid($this->uuid)->save(); + } + Book2TableMap::clearInstancePool(); + } + + /** + * @return void + */ + public function testModelRestoresUuid() + { + $retrievedBook = Book2Query::create()->findOneById($this->book->getId()); + $this->assertSame($this->uuid, $retrievedBook->getUuid()); + } + + /** + * @return void + */ + public function testModelCanUpdateUuid() + { + $book = new Book2(); + $book->save(); + + $updateUuid = '42a79e51-511a-4662-8956-cc89cf43f764'; + + $book->setUuid($updateUuid)->save(); + $book->reload(); + + $this->assertSame($updateUuid, $book->getUuid()); + } +} From 360a67867336cd8c1e6178676aa00cdbc5983d75 Mon Sep 17 00:00:00 2001 From: mringler Date: Tue, 6 Dec 2022 13:51:16 +0100 Subject: [PATCH 3/8] fix typo --- tests/bin/setup.mysql.bat | 2 +- tests/bin/setup.pgsql.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bin/setup.mysql.bat b/tests/bin/setup.mysql.bat index aeaf0f22ee..214aeaaa5a 100644 --- a/tests/bin/setup.mysql.bat +++ b/tests/bin/setup.mysql.bat @@ -12,7 +12,7 @@ if "%mysql%" == "" ( ) if "%mysql%" == "" ( - echo Can not find mysql binary. Is it installed? + echo Cannot find mysql binary. Is it installed? exit /B 1 ) diff --git a/tests/bin/setup.pgsql.bat b/tests/bin/setup.pgsql.bat index 8bd7841e08..11be90634e 100644 --- a/tests/bin/setup.pgsql.bat +++ b/tests/bin/setup.pgsql.bat @@ -9,7 +9,7 @@ set psql= @for %%e in (%PATHEXT%) do @for %%i in (psql%%e) do @if NOT "%%~$PATH:i"=="" set psql=%%~$PATH:i if "%psql%" == "" ( - echo Can not find psql binary. Is it installed? + echo Cannot find psql binary. Is it installed? exit /B 1 ) From 5e52a9c597a76209da5efa33558a1b4967afb3cc Mon Sep 17 00:00:00 2001 From: mringler Date: Tue, 6 Dec 2022 18:31:59 +0100 Subject: [PATCH 4/8] Recreate FK if column type changes --- .../Model/Diff/ForeignKeyComparator.php | 70 ++++--- .../Generator/Model/Diff/TableComparator.php | 21 +- src/Propel/Generator/Model/ForeignKey.php | 4 +- .../Model/Diff/ForeignKeyComparatorTest.php | 179 ++++++++---------- .../PropelTableForeignKeyComparatorTest.php | 116 ++++-------- 5 files changed, 172 insertions(+), 218 deletions(-) diff --git a/src/Propel/Generator/Model/Diff/ForeignKeyComparator.php b/src/Propel/Generator/Model/Diff/ForeignKeyComparator.php index 3987bbd198..56ada28852 100644 --- a/src/Propel/Generator/Model/Diff/ForeignKeyComparator.php +++ b/src/Propel/Generator/Model/Diff/ForeignKeyComparator.php @@ -8,6 +8,7 @@ namespace Propel\Generator\Model\Diff; +use Propel\Generator\Model\Column; use Propel\Generator\Model\ForeignKey; /** @@ -30,43 +31,29 @@ class ForeignKeyComparator public static function computeDiff(ForeignKey $fromFk, ForeignKey $toFk, bool $caseInsensitive = false): bool { // Check for differences in local and remote table - $test = $caseInsensitive ? + $fromDifferentTable = $caseInsensitive ? strtolower($fromFk->getTableName()) !== strtolower($toFk->getTableName()) : $fromFk->getTableName() !== $toFk->getTableName(); - if ($test) { + if ($fromDifferentTable) { return true; } - $fromFkForeignTableName = !$fromFk->getForeignTableName() - ? '' - : strtolower($fromFk->getForeignTableName()); - - $toFkForeignTableName = !$toFk->getForeignTableName() - ? '' - : strtolower($toFk->getForeignTableName()); - - $test = $caseInsensitive ? - $fromFkForeignTableName !== $toFkForeignTableName : + $toDifferentTable = $caseInsensitive ? + strtolower($fromFk->getForeignTableName() ?? '') !== strtolower($toFk->getForeignTableName() ?? '') : $fromFk->getForeignTableName() !== $toFk->getForeignTableName(); - if ($test) { + if ($toDifferentTable) { return true; } // compare columns - $fromFkLocalColumns = $fromFk->getLocalColumns(); - sort($fromFkLocalColumns); - $toFkLocalColumns = $toFk->getLocalColumns(); - sort($toFkLocalColumns); - if (array_map('strtolower', $fromFkLocalColumns) !== array_map('strtolower', $toFkLocalColumns)) { - return true; - } - $fromFkForeignColumns = $fromFk->getForeignColumns(); - sort($fromFkForeignColumns); - $toFkForeignColumns = $toFk->getForeignColumns(); - sort($toFkForeignColumns); - if (array_map('strtolower', $fromFkForeignColumns) !== array_map('strtolower', $toFkForeignColumns)) { + if ( + !static::stringArrayEqualsCaseInsensitive($fromFk->getLocalColumns(), $toFk->getLocalColumns()) + || !static::stringArrayEqualsCaseInsensitive($fromFk->getForeignColumns(), $toFk->getForeignColumns()) + || !static::columnTypesEquals($fromFk->getLocalColumnObjects(), $toFk->getLocalColumnObjects()) + || !static::columnTypesEquals($fromFk->getForeignColumnObjects(), $toFk->getForeignColumnObjects()) + ) { return true; } @@ -85,4 +72,37 @@ public static function computeDiff(ForeignKey $fromFk, ForeignKey $toFk, bool $c // compare skipSql return $fromFk->isSkipSql() !== $toFk->isSkipSql(); } + + /** + * @param array $array1 + * @param array $array2 + * + * @return bool + */ + protected static function stringArrayEqualsCaseInsensitive(array $array1, array $array2): bool + { + sort($array1); + sort($array2); + + return array_map('strtolower', $array1) === array_map('strtolower', $array2); + } + + /** + * @param array $columns1 + * @param array $columns2 + * + * @return bool + */ + protected static function columnTypesEquals(array $columns1, array $columns2): bool + { + $byNameSorter = fn (Column $column1, Column $column2) => strcmp($column1->getName(), $column2->getName()); + usort($columns1, $byNameSorter); + usort($columns2, $byNameSorter); + + $toSqlTypeNameMapper = fn (Column $column) => $column->getSqlType(); + $columnTypes1 = array_map($toSqlTypeNameMapper, $columns1); + $columnTypes2 = array_map($toSqlTypeNameMapper, $columns2); + + return $columnTypes1 === $columnTypes2; + } } diff --git a/src/Propel/Generator/Model/Diff/TableComparator.php b/src/Propel/Generator/Model/Diff/TableComparator.php index 2f3f7bc21f..0d8991973e 100644 --- a/src/Propel/Generator/Model/Diff/TableComparator.php +++ b/src/Propel/Generator/Model/Diff/TableComparator.php @@ -304,18 +304,17 @@ public function compareForeignKeys(bool $caseInsensitive = false): int $sameName = $caseInsensitive ? strtolower($fromTableFk->getName()) == strtolower($toTableFk->getName()) : $fromTableFk->getName() == $toTableFk->getName(); - if ($sameName && !$toTableFk->isPolymorphic()) { - if (ForeignKeyComparator::computeDiff($fromTableFk, $toTableFk, $caseInsensitive) === false) { - unset($fromTableFks[$fromTableFkPos]); - unset($toTableFks[$toTableFkPos]); - } else { - // same name, but different columns - $this->tableDiff->addModifiedFk($fromTableFk->getName(), $fromTableFk, $toTableFk); - unset($fromTableFks[$fromTableFkPos]); - unset($toTableFks[$toTableFkPos]); - $fkDifferences++; - } + if (!$sameName || $toTableFk->isPolymorphic()) { + continue; + } + $hasChanged = ForeignKeyComparator::computeDiff($fromTableFk, $toTableFk, $caseInsensitive); + if ($hasChanged) { + // same name, but different columns + $this->tableDiff->addModifiedFk($fromTableFk->getName(), $fromTableFk, $toTableFk); + $fkDifferences++; } + unset($fromTableFks[$fromTableFkPos]); + unset($toTableFks[$toTableFkPos]); } } diff --git a/src/Propel/Generator/Model/ForeignKey.php b/src/Propel/Generator/Model/ForeignKey.php index 76fb18d13d..c53bda8a33 100644 --- a/src/Propel/Generator/Model/ForeignKey.php +++ b/src/Propel/Generator/Model/ForeignKey.php @@ -648,7 +648,7 @@ public function clearReferences(): void /** * Returns an array of local column names. * - * @return array + * @return array */ public function getLocalColumns(): array { @@ -781,7 +781,7 @@ public function getMappedLocalColumn(string $foreign): ?string /** * Returns an array of foreign column names. * - * @return array + * @return array */ public function getForeignColumns(): array { diff --git a/tests/Propel/Tests/Generator/Model/Diff/ForeignKeyComparatorTest.php b/tests/Propel/Tests/Generator/Model/Diff/ForeignKeyComparatorTest.php index 63814d77a4..cf686e486b 100644 --- a/tests/Propel/Tests/Generator/Model/Diff/ForeignKeyComparatorTest.php +++ b/tests/Propel/Tests/Generator/Model/Diff/ForeignKeyComparatorTest.php @@ -9,6 +9,7 @@ namespace Propel\Tests\Generator\Model\Diff; use Propel\Generator\Model\Column; +use Propel\Generator\Model\Database; use Propel\Generator\Model\Diff\ForeignKeyComparator; use Propel\Generator\Model\ForeignKey; use Propel\Generator\Model\Table; @@ -19,23 +20,51 @@ */ class ForeignKeyComparatorTest extends TestCase { + + public static function createForeignKey(array $columns, string $refTableName = 'RefTableName', string $fkTableName = 'FkTableName'): ForeignKey + { + + $fkTable = new Table($fkTableName); + $refTable = new Table($refTableName); + + $database = new Database(); + $database->addTable($refTable); + $database->addTable($fkTable); + $fk = new ForeignKey(); + $fk->setForeignTableCommonName($refTableName); + $fkTable->addForeignKey($fk); + + + foreach($columns as $fkColumnName => $refColumnName){ + $refCol = static::createColumn($refColumnName); + $refTable->addColumn($refCol); + + $fkCol = static::createColumn($fkColumnName); + $fkTable->addColumn($fkCol); + + $fk->addReference($fkCol, $refCol); + } + + return $fk; + } + + public static function createColumn(string $columnName, string $columnType = 'Le type'): Column + { + $col = new Column($columnName); + $col->setName($columnName); + $col->getDomain()->setSqlType($columnType); + + return $col; + } + /** * @return void */ public function testCompareNoDifference() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo'); - $c4 = new Column('Bar'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + $fk1 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk2 = static::createForeignKey(['FkCol' => 'RefCol']); + $this->assertFalse(ForeignKeyComparator::computeDiff($fk1, $fk2)); } @@ -44,18 +73,9 @@ public function testCompareNoDifference() */ public function testCompareCaseInsensitive() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('fOO'); - $c4 = new Column('bAR'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2 = new Table('bAZ'); - $t2->addForeignKey($fk2); + $fk1 = static::createForeignKey(['fkcol' => 'refcol'], 'reftable', 'fktable'); + $fk2 = static::createForeignKey(['FKCOL' => 'REFCOL'], 'REFTABLE', 'FKTABLE'); + $this->assertFalse(ForeignKeyComparator::computeDiff($fk1, $fk2, true)); } @@ -64,18 +84,9 @@ public function testCompareCaseInsensitive() */ public function testCompareLocalColumn() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo2'); - $c4 = new Column('Bar'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + $fk1 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk2 = static::createForeignKey(['FkCol' => 'NotRefCol']); + $this->assertTrue(ForeignKeyComparator::computeDiff($fk1, $fk2)); } @@ -84,18 +95,9 @@ public function testCompareLocalColumn() */ public function testCompareForeignColumn() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo'); - $c4 = new Column('Bar2'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + $fk1 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk2 = static::createForeignKey(['NotFkCol' => 'RefCol']); + $this->assertTrue(ForeignKeyComparator::computeDiff($fk1, $fk2)); } @@ -104,21 +106,10 @@ public function testCompareForeignColumn() */ public function testCompareColumnMappings() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo'); - $c4 = new Column('Bar'); - $c5 = new Column('Foo2'); - $c6 = new Column('Bar2'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $fk2->addReference($c5, $c6); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + + $fk1 = static::createForeignKey(['FkCol1' => 'RefCol1']); + $fk2 = static::createForeignKey(['FkCol1' => 'RefCol1', 'FkCol2' => 'RefCol2']); + $this->assertTrue(ForeignKeyComparator::computeDiff($fk1, $fk2)); } @@ -127,20 +118,12 @@ public function testCompareColumnMappings() */ public function testCompareOnUpdate() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); + $fk1 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk2 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk1->setOnUpdate(ForeignKey::SETNULL); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo'); - $c4 = new Column('Bar'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); $fk2->setOnUpdate(ForeignKey::RESTRICT); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + $this->assertTrue(ForeignKeyComparator::computeDiff($fk1, $fk2)); } @@ -149,20 +132,12 @@ public function testCompareOnUpdate() */ public function testCompareOnDelete() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); + $fk1 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk2 = static::createForeignKey(['FkCol' => 'RefCol']); + $fk1->setOnDelete(ForeignKey::SETNULL); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo'); - $c4 = new Column('Bar'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); $fk2->setOnDelete(ForeignKey::RESTRICT); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + $this->assertTrue(ForeignKeyComparator::computeDiff($fk1, $fk2)); } @@ -171,20 +146,22 @@ public function testCompareOnDelete() */ public function testCompareSort() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $c3 = new Column('Baz'); - $c4 = new Column('Faz'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c3); - $fk1->addReference($c2, $c4); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $fk2 = new ForeignKey(); - $fk2->addReference($c2, $c4); - $fk2->addReference($c1, $c3); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); + $fk1 = static::createForeignKey(['FkCol1' => 'RefCol1', 'FkCol2' => 'RefCol2']); + $fk2 = static::createForeignKey(['FkCol2' => 'RefCol2', 'FkCol1' => 'RefCol1']); + $this->assertFalse(ForeignKeyComparator::computeDiff($fk1, $fk2)); } + + /** + * @return void + */ + public function testCompareColumnType() + { + $fk1 = static::createForeignKey(['FkCol1' => 'RefCol1', 'FkCol2' => 'RefCol2']); + $fk2 = static::createForeignKey(['FkCol1' => 'RefCol1', 'FkCol2' => 'RefCol2']); + + $fk2->getForeignColumnObjects()[1]->getDomain()->setSqlType('Le updated type'); + + $this->assertTrue(ForeignKeyComparator::computeDiff($fk1, $fk2)); + } } diff --git a/tests/Propel/Tests/Generator/Model/Diff/PropelTableForeignKeyComparatorTest.php b/tests/Propel/Tests/Generator/Model/Diff/PropelTableForeignKeyComparatorTest.php index 0bc26cb525..ab6d4532cd 100644 --- a/tests/Propel/Tests/Generator/Model/Diff/PropelTableForeignKeyComparatorTest.php +++ b/tests/Propel/Tests/Generator/Model/Diff/PropelTableForeignKeyComparatorTest.php @@ -30,25 +30,24 @@ public function setUp(): void $this->platform = new MysqlPlatform(); } + + public function createForeignKey(array $columns, string $refTableName = 'RefTableName', string $fkTableName = 'FkTableName'): ForeignKey + { + $fk = ForeignKeyComparatorTest::createForeignKey($columns, $refTableName, $fkTableName); + $fk->getTable()->getDatabase()->setPlatform($this->platform); + + return $fk; + } + /** * @return void */ public function testCompareSameFks() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $c3 = new Column('Foo'); - $c4 = new Column('Bar'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); - - $this->assertFalse(TableComparator::computeDiff($t1, $t2)); + $fk1 = $this->createForeignKey(['FkCol' => 'RefCol']); + $fk2 = $this->createForeignKey(['FkCol' => 'RefCol']); + + $this->assertFalse(TableComparator::computeDiff($fk1->getTable(), $fk2->getTable())); } /** @@ -56,16 +55,10 @@ public function testCompareSameFks() */ public function testCompareNotSameFks() { - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); + $fk1 = $this->createForeignKey(['FkCol' => 'RefCol'], 'RefTable', 'FkTable'); + $t2 = new Table('FkTable'); - $t2 = new Table('Baz'); - - $diff = TableComparator::computeDiff($t1, $t2); + $diff = TableComparator::computeDiff($fk1->getTable(), $t2); $this->assertTrue($diff instanceof TableDiff); } @@ -74,21 +67,10 @@ public function testCompareNotSameFks() */ public function testCaseInsensitive() { - $t1 = new Table('Baz'); - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1->addForeignKey($fk1); - - $t2 = new Table('bAZ'); - $c3 = new Column('fOO'); - $c4 = new Column('bAR'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2->addForeignKey($fk2); - - $diff = TableComparator::computeDiff($t1, $t2, true); + $fk1 = $this->createForeignKey(['fkcol' => 'refcol'], 'reftable', 'fktable'); + $fk2 = $this->createForeignKey(['FKCOL' => 'REFCOL'], 'REFTABLE', 'FKTABLE'); + + $diff = TableComparator::computeDiff($fk1->getTable(), $fk2->getTable(), true); $this->assertFalse($diff); } @@ -99,18 +81,11 @@ public function testCompareAddedFks() { $db1 = new Database(); $db1->setPlatform($this->platform); - $t1 = new Table('Baz'); + $t1 = new Table('FkTable'); $db1->addTable($t1); - $db2 = new Database(); - $db2->setPlatform($this->platform); - $c3 = new Column('Foo'); - $c4 = new Column('Bar'); - $fk2 = new ForeignKey(); - $fk2->addReference($c3, $c4); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); - $db2->addTable($t2); + $fk2 = $this->createForeignKey(['FkCol' => 'RefCol'], 'RefTable', 'FkTable'); + $t2 = $fk2->getTable(); $tc = new TableComparator(); $tc->setFromTable($t1); @@ -119,7 +94,7 @@ public function testCompareAddedFks() $tableDiff = $tc->getTableDiff(); $this->assertEquals(1, $nbDiffs); $this->assertEquals(1, count($tableDiff->getAddedFks())); - $this->assertEquals(['Baz_fk_9c94ed' => $fk2], $tableDiff->getAddedFks()); + $this->assertEquals(['FkTable_fk_77b9fa' => $fk2], $tableDiff->getAddedFks()); } /** @@ -127,19 +102,13 @@ public function testCompareAddedFks() */ public function testCompareRemovedFks() { - $db1 = new Database(); - $db1->setPlatform($this->platform); - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey(); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $db1->addTable($t1); + + $fk1 = $this->createForeignKey(['FkCol' => 'RefCol'], 'RefTable', 'FkTable'); + $t1 = $fk1->getTable(); $db2 = new Database(); $db2->setPlatform($this->platform); - $t2 = new Table('Baz'); + $t2 = new Table('FkTable'); $db2->addTable($t2); $tc = new TableComparator(); @@ -149,7 +118,7 @@ public function testCompareRemovedFks() $tableDiff = $tc->getTableDiff(); $this->assertEquals(1, $nbDiffs); $this->assertEquals(1, count($tableDiff->getRemovedFks())); - $this->assertEquals(['Baz_fk_9c94ed' => $fk1], $tableDiff->getRemovedFks()); + $this->assertEquals(['FkTable_fk_77b9fa' => $fk1], $tableDiff->getRemovedFks()); } /** @@ -157,25 +126,14 @@ public function testCompareRemovedFks() */ public function testCompareModifiedFks() { - $db1 = new Database(); - $db1->setPlatform($this->platform); - $c1 = new Column('Foo'); - $c2 = new Column('Bar'); - $fk1 = new ForeignKey('my_foreign_key'); - $fk1->addReference($c1, $c2); - $t1 = new Table('Baz'); - $t1->addForeignKey($fk1); - $db1->addTable($t1); + $fk1 = $this->createForeignKey(['FkCol' => 'RefCol']); + $fk2 = $this->createForeignKey(['FkCol' => 'NotRefCol']); - $db2 = new Database(); - $db2->setPlatform($this->platform); - $c3 = new Column('Foo'); - $c4 = new Column('Bar2'); - $fk2 = new ForeignKey('my_foreign_key'); - $fk2->addReference($c3, $c4); - $t2 = new Table('Baz'); - $t2->addForeignKey($fk2); - $db2->addTable($t2); + $fk1->setName('my_foreign_key'); + $fk2->setName('my_foreign_key'); + + $t1 = $fk1->getTable(); + $t2 = $fk2->getTable(); $tc = new TableComparator(); $tc->setFromTable($t1); @@ -183,7 +141,7 @@ public function testCompareModifiedFks() $nbDiffs = $tc->compareForeignKeys(); $tableDiff = $tc->getTableDiff(); $this->assertEquals(1, $nbDiffs); - $this->assertEquals(1, count($tableDiff->getModifiedFks())); + $this->assertCount(1, $tableDiff->getModifiedFks()); $this->assertEquals(['my_foreign_key' => [$fk1, $fk2]], $tableDiff->getModifiedFks()); } } From a7d57f1b4712948a5220a04eaffd5a18ba73e575 Mon Sep 17 00:00:00 2001 From: mringler Date: Tue, 6 Dec 2022 21:31:19 +0100 Subject: [PATCH 5/8] Automatically migrate UUID columns in MySQL --- .../Builder/Om/AbstractOMBuilder.php | 17 +- src/Propel/Generator/Model/Column.php | 26 ++ src/Propel/Generator/Model/ForeignKey.php | 2 +- src/Propel/Generator/Model/Schema.php | 11 +- src/Propel/Generator/Model/Table.php | 34 +- src/Propel/Generator/Model/VendorInfo.php | 24 ++ .../Generator/Platform/DefaultPlatform.php | 39 +-- .../Generator/Platform/MysqlPlatform.php | 101 ++++-- .../Generator/Platform/PlatformInterface.php | 10 + .../Util/AlterTableStatementMerger.php | 132 ++++++++ .../Util/MysqlUuidMigrationBuilder.php | 178 ++++++++++ .../Generator/Reverse/MysqlSchemaParser.php | 4 +- .../Tests/Generator/Migration/BaseTest.php | 8 +- .../Generator/Migration/MigrationTestCase.php | 17 +- .../Migration/MysqlMigrateUuidColumnTest.php | 311 ++++++++++++++++++ .../Platform/MysqlPlatformMigrationTest.php | 61 ++++ .../PlatformMigrationTestProvider.php | 14 +- .../Propel/Tests/Helpers/CheckMysql8Trait.php | 47 +++ .../UuidConverterMysqlCompatibilityTest.php | 21 +- 19 files changed, 923 insertions(+), 134 deletions(-) create mode 100644 src/Propel/Generator/Platform/Util/AlterTableStatementMerger.php create mode 100644 src/Propel/Generator/Platform/Util/MysqlUuidMigrationBuilder.php create mode 100644 tests/Propel/Tests/Generator/Migration/MysqlMigrateUuidColumnTest.php create mode 100644 tests/Propel/Tests/Helpers/CheckMysql8Trait.php diff --git a/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php b/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php index 48ed8c98ec..0a157ce31d 100644 --- a/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php +++ b/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php @@ -14,7 +14,6 @@ use Propel\Generator\Exception\InvalidArgumentException; use Propel\Generator\Exception\LogicException; use Propel\Generator\Exception\RuntimeException; -use Propel\Generator\Exception\SchemaException; use Propel\Generator\Model\Column; use Propel\Generator\Model\CrossForeignKeys; use Propel\Generator\Model\ForeignKey; @@ -1205,26 +1204,14 @@ protected function getVendorInfo(): VendorInfo } /** - * Returns the value for the uuid swap flag as set in the vendor information - * block in schema.xml as a literal ('true' or 'false'). - * * @psalm-return 'true'|'false' * - * @see \Propel\Runtime\Util\UuidConverter::uuidToBin() - * - * @throws \Propel\Generator\Exception\SchemaException + * @see \Propel\Generator\Model\VendorInfo::getUuidSwapFlagLiteral() * * @return string */ protected function getUuidSwapFlagLiteral(): string { - $vendorInformation = $this->getVendorInfo(); - - $uuidSwapFlag = $vendorInformation->getParameter('UuidSwapFlag') ?? 'true'; - if (!in_array($uuidSwapFlag, ['true', 'false'], true)) { - throw new SchemaException('Value for /database/vendor/parameter[name="UuidSwapFlag"] must be "true" or "false", but it is ' . $uuidSwapFlag); - } - - return $uuidSwapFlag; + return $this->getVendorInfo()->getUuidSwapFlagLiteral(); } } diff --git a/src/Propel/Generator/Model/Column.php b/src/Propel/Generator/Model/Column.php index 139be489ca..5fe7c0c71d 100644 --- a/src/Propel/Generator/Model/Column.php +++ b/src/Propel/Generator/Model/Column.php @@ -1243,6 +1243,18 @@ public function getType(): string return $this->getDomain()->getType(); } + /** + * Returns the SQL type as a string. + * + * @see Domain::getSqlType() + * + * @return string + */ + public function getSqlType(): string + { + return $this->getDomain()->getSqlType(); + } + /** * Returns the column PDO type integer for this column's mapping type. * @@ -1723,4 +1735,18 @@ public static function generatePhpSingularName(string $phpName): string { return rtrim($phpName, 's'); } + + /** + * Checks if xml attributes from schema.xml matches expected content declaration. + * + * @param string $content + * + * @return bool + */ + public function isContent(string $content): bool + { + $contentAttribute = $this->getAttribute('content'); + + return $contentAttribute && strtoupper($contentAttribute) === strtoupper($contentAttribute); + } } diff --git a/src/Propel/Generator/Model/ForeignKey.php b/src/Propel/Generator/Model/ForeignKey.php index c53bda8a33..c4e299366b 100644 --- a/src/Propel/Generator/Model/ForeignKey.php +++ b/src/Propel/Generator/Model/ForeignKey.php @@ -781,7 +781,7 @@ public function getMappedLocalColumn(string $foreign): ?string /** * Returns an array of foreign column names. * - * @return array + * @return array */ public function getForeignColumns(): array { diff --git a/src/Propel/Generator/Model/Schema.php b/src/Propel/Generator/Model/Schema.php index a51ec68f58..40335fc9eb 100644 --- a/src/Propel/Generator/Model/Schema.php +++ b/src/Propel/Generator/Model/Schema.php @@ -193,16 +193,15 @@ public function getDatabase(?string $name = null, bool $doFinalInitialization = return $this->databases[0]; } - $db = null; foreach ($this->databases as $database) { - if ($database->getName() === $name) { - $db = $database; - - break; + if ($database->getName() !== $name) { + continue; } + + return $database; } - return $db; + return null; } /** diff --git a/src/Propel/Generator/Model/Table.php b/src/Propel/Generator/Model/Table.php index 727a25b41d..f9e94ecfdd 100644 --- a/src/Propel/Generator/Model/Table.php +++ b/src/Propel/Generator/Model/Table.php @@ -1037,6 +1037,20 @@ public function hasIndex(string $name): bool return false; } + /** + * Get indexes on a column + * + * @param \Propel\Generator\Model\Column $column + * + * @return array<\Propel\Generator\Model\Index> + */ + public function getIndexesOnColumn(Column $column): array + { + $columnName = $column->getName(); + + return array_filter($this->indices, fn ($idx) => $idx->hasColumn($columnName)); + } + /** * Adds a new index to the indices list and set the * parent table of the column to the current table. @@ -1804,14 +1818,9 @@ public function getColumnByPhpName(string $phpName): ?Column */ public function getForeignKeysReferencingTable(string $tableName): array { - $matches = []; - foreach ($this->foreignKeys as $fk) { - if ($fk->getForeignTableName() === $tableName) { - $matches[] = $fk; - } - } + $filter = fn (ForeignKey $fk) => $fk->getForeignTableName() === $tableName; - return $matches; + return array_values(array_filter($this->foreignKeys, $filter)); } /** @@ -1827,14 +1836,9 @@ public function getForeignKeysReferencingTable(string $tableName): array */ public function getColumnForeignKeys(string $column): array { - $matches = []; - foreach ($this->foreignKeys as $fk) { - if (in_array($column, $fk->getLocalColumns())) { - $matches[] = $fk; - } - } + $filter = fn (ForeignKey $fk) => in_array($column, $fk->getLocalColumns(), true); - return $matches; + return array_values(array_filter($this->foreignKeys, $filter)); } /** @@ -1926,7 +1930,7 @@ public function getPlatform(): ?PlatformInterface public function quoteIdentifier(string $text): string { if (!$this->getPlatform()) { - throw new RuntimeException('No platform specified. Can not quote without knowing which platform this table\'s database is using.'); + throw new RuntimeException('No platform specified. Cannot quote without knowing which platform this table\'s database is using.'); } if ($this->isIdentifierQuotingEnabled()) { diff --git a/src/Propel/Generator/Model/VendorInfo.php b/src/Propel/Generator/Model/VendorInfo.php index 63b6336be6..ab7bf8b674 100644 --- a/src/Propel/Generator/Model/VendorInfo.php +++ b/src/Propel/Generator/Model/VendorInfo.php @@ -8,6 +8,8 @@ namespace Propel\Generator\Model; +use Propel\Generator\Exception\SchemaException; + /** * Object to hold vendor specific information. * @@ -161,4 +163,26 @@ protected function setupObject(): void { $this->type = $this->getAttribute('type'); } + + /** + * Returns the value for the uuid swap flag as set in the vendor information + * block in schema.xml as a literal ('true' or 'false'). + * + * @psalm-return 'true'|'false' + * + * @see \Propel\Runtime\Util\UuidConverter::uuidToBin() + * + * @throws \Propel\Generator\Exception\SchemaException + * + * @return string + */ + public function getUuidSwapFlagLiteral(): string + { + $uuidSwapFlag = $this->getParameter('UuidSwapFlag') ?? 'true'; + if (!in_array($uuidSwapFlag, ['true', 'false'], true)) { + throw new SchemaException('Value for `/database/vendor/parameter[name="UuidSwapFlag"]` must be `true` or `false`, but it is `' . $uuidSwapFlag . '`'); + } + + return $uuidSwapFlag; + } } diff --git a/src/Propel/Generator/Platform/DefaultPlatform.php b/src/Propel/Generator/Platform/DefaultPlatform.php index 6274bd2f57..06fcc53c37 100644 --- a/src/Propel/Generator/Platform/DefaultPlatform.php +++ b/src/Propel/Generator/Platform/DefaultPlatform.php @@ -23,6 +23,7 @@ use Propel\Generator\Model\PropelTypes; use Propel\Generator\Model\Table; use Propel\Generator\Model\Unique; +use Propel\Generator\Platform\Util\AlterTableStatementMerger; use Propel\Runtime\Connection\ConnectionInterface; use ReflectionClass; @@ -896,41 +897,7 @@ public function getModifyTableDDL(TableDiff $tableDiff): string $columnChangeString .= $this->getAddPrimaryKeyDDL($tableDiff->getToTable()); } - if ($columnChangeString) { - //merge column changes into one command. This is more compatible especially with PK constraints. - - $changes = explode(';', $columnChangeString); - $columnChanges = []; - - foreach ($changes as $change) { - if (!trim($change)) { - continue; - } - $isCompatibleCall = preg_match( - sprintf('/ALTER TABLE %s (?!RENAME)/', $this->quoteIdentifier($toTable->getName())), - $change, - ); - if ($isCompatibleCall) { - $columnChanges[] = preg_replace( - sprintf('/ALTER TABLE %s /', $this->quoteIdentifier($toTable->getName())), - "\n\n ", - trim($change), - ); - } else { - $ret .= $change . ";\n"; - } - } - - if (0 < count($columnChanges)) { - $ret .= sprintf( - " -ALTER TABLE %s%s; -", - $this->quoteIdentifier($toTable->getName()), - implode(',', $columnChanges), - ); - } - } + $ret .= AlterTableStatementMerger::merge($toTable, $columnChangeString); // create indices, foreign keys foreach ($tableDiff->getModifiedIndices() as $indexModification) { @@ -1278,7 +1245,7 @@ protected function disconnectedEscapeText(string $text): string * * @return string Quoted identifier. */ - protected function quoteIdentifier(string $text): string + public function quoteIdentifier(string $text): string { return $this->isIdentifierQuotingEnabled() ? $this->doQuoting($text) : $text; } diff --git a/src/Propel/Generator/Platform/MysqlPlatform.php b/src/Propel/Generator/Platform/MysqlPlatform.php index ff3f62d599..bc5ba77dd3 100644 --- a/src/Propel/Generator/Platform/MysqlPlatform.php +++ b/src/Propel/Generator/Platform/MysqlPlatform.php @@ -21,6 +21,7 @@ use Propel\Generator\Model\PropelTypes; use Propel\Generator\Model\Table; use Propel\Generator\Model\Unique; +use Propel\Generator\Platform\Util\MysqlUuidMigrationBuilder; /** * MySql PlatformInterface implementation. @@ -56,8 +57,8 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::BOOLEAN, 'TINYINT', 1)); $this->setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, 'DECIMAL')); $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, 'TEXT')); - $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, 'BLOB')); - $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, 'MEDIUMBLOB')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, 'BINARY')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, 'VARBINARY')); $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, 'LONGBLOB')); $this->setSchemaDomainMapping(new Domain(PropelTypes::CLOB, 'LONGTEXT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::OBJECT, 'MEDIUMBLOB')); @@ -65,7 +66,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::ENUM, 'TINYINT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::SET, 'INT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::REAL, 'DOUBLE')); - $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BINARY(16)')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::UUID_BINARY, 'BINARY', 16)); // no native UUID type, use UUID_BINARY $this->schemaDomainMap[PropelTypes::UUID] = $this->schemaDomainMap[PropelTypes::UUID_BINARY]; @@ -435,11 +436,8 @@ public function getColumnDDL(Column $col): string } $ddl = [$this->quoteIdentifier($col->getName())]; - if ($this->hasSize($sqlType) && $col->isDefaultSqlType($this)) { - $ddl[] = $sqlType . $col->getSizeDefinition(); - } else { - $ddl[] = $sqlType; - } + $ddl[] = $this->getSqlTypeExpression($col); + $colinfo = $col->getVendorInfoForType($this->getDatabaseType()); if ($colinfo->hasParameter('Unsigned')) { $unsigned = $colinfo->getParameter('Unsigned'); @@ -491,6 +489,45 @@ public function getColumnDDL(Column $col): string return implode(' ', $ddl); } + /** + * Returns the SQL type as a string. + * + * @see Domain::getSqlType() + * + * @param \Propel\Generator\Model\Column $column + * + * @return string + */ + public function getSqlTypeExpression(Column $column): string + { + $sqlType = $column->getSqlType(); + $hasSize = $this->hasSize($sqlType) && $column->isDefaultSqlType($this); + + return (!$hasSize) ? $sqlType : $sqlType . $column->getSizeDefinition(); + } + + /** + * @param \Propel\Generator\Model\Column $fromColumn + * @param \Propel\Generator\Model\Column $toColumn + * + * @return string + */ + protected function getChangeColumnToUuidBinaryType(Column $fromColumn, Column $toColumn): string + { + return MysqlUuidMigrationBuilder::create($this)->buildMigration($fromColumn, $toColumn, true); + } + + /** + * @param \Propel\Generator\Model\Column $fromColumn + * @param \Propel\Generator\Model\Column $toColumn + * + * @return string + */ + protected function getChangeColumnFromUuidBinaryType(Column $fromColumn, Column $toColumn): string + { + return MysqlUuidMigrationBuilder::create($this)->buildMigration($fromColumn, $toColumn, false); + } + /** * Creates a comma-separated list of column names for the index. * For MySQL unique indexes there is the option of specifying size, so we cannot simply use @@ -504,7 +541,8 @@ protected function getIndexColumnListDDL(Index $index): string { $list = []; foreach ($index->getColumns() as $col) { - $list[] = $this->quoteIdentifier($col) . ($index->hasColumnSize($col) ? '(' . $index->getColumnSize($col) . ')' : ''); + $size = $index->hasColumnSize($col) ? '(' . $index->getColumnSize($col) . ')' : ''; + $list[] = $this->quoteIdentifier($col) . $size; } return implode(', ', $list); @@ -523,14 +561,9 @@ public function getDropPrimaryKeyDDL(Table $table): string return ''; } - $pattern = " -ALTER TABLE %s DROP PRIMARY KEY; -"; + $tableName = $this->quoteIdentifier($table->getName()); - return sprintf( - $pattern, - $this->quoteIdentifier($table->getName()), - ); + return "\nALTER TABLE $tableName DROP PRIMARY KEY;\n"; } /** @@ -792,7 +825,20 @@ public function getRenameColumnDDL(Column $fromColumn, Column $toColumn): string */ public function getModifyColumnDDL(ColumnDiff $columnDiff): string { - return $this->getChangeColumnDDL($columnDiff->getFromColumn(), $columnDiff->getToColumn()); + $fromColumn = $columnDiff->getFromColumn(); + $toColumn = $columnDiff->getToColumn(); + + if ($fromColumn->isTextType() && $toColumn->isUuidBinaryType()) { + return $this->getChangeColumnToUuidBinaryType($fromColumn, $toColumn); + } + + // binary column from database does not know it is a UUID column + $fromBinaryColumn = in_array($fromColumn->getType(), [PropelTypes::BINARY, PropelTypes::UUID_BINARY], true); + if ($fromBinaryColumn && $toColumn->isTextType() && $toColumn->isContent('UUID')) { + return $this->getChangeColumnFromUuidBinaryType($fromColumn, $toColumn); + } + + return $this->getChangeColumnDDL($fromColumn, $toColumn); } /** @@ -805,16 +851,12 @@ public function getModifyColumnDDL(ColumnDiff $columnDiff): string */ public function getChangeColumnDDL(Column $fromColumn, Column $toColumn): string { - $pattern = " -ALTER TABLE %s CHANGE %s %s; -"; + $tableName = $this->quoteIdentifier($fromColumn->getTable()->getName()); + $columnName = $this->quoteIdentifier($fromColumn->getName()); + $columnDefinition = $this->getColumnDDL($toColumn); + $pattern = "\nALTER TABLE %s CHANGE %s %s;\n"; - return sprintf( - $pattern, - $this->quoteIdentifier($fromColumn->getTable()->getName()), - $this->quoteIdentifier($fromColumn->getName()), - $this->getColumnDDL($toColumn), - ); + return sprintf($pattern, $tableName, $columnName, $columnDefinition); } /** @@ -826,12 +868,9 @@ public function getChangeColumnDDL(Column $fromColumn, Column $toColumn): string */ public function getModifyColumnsDDL(array $columnDiffs): string { - $ret = ''; - foreach ($columnDiffs as $columnDiff) { - $ret .= $this->getModifyColumnDDL($columnDiff); - } + $modifyColumnStatements = array_map([$this, 'getModifyColumnDDL'], $columnDiffs); - return $ret; + return implode('', $modifyColumnStatements); } /** diff --git a/src/Propel/Generator/Platform/PlatformInterface.php b/src/Propel/Generator/Platform/PlatformInterface.php index de6c06acb9..95a75b0e94 100644 --- a/src/Propel/Generator/Platform/PlatformInterface.php +++ b/src/Propel/Generator/Platform/PlatformInterface.php @@ -345,4 +345,14 @@ public function setIdentifierQuoting(bool $enabled): void; * @return string */ public function getAddTableDDL(Table $table): string; + + /** + * Quotes identifiers used in database SQL if isIdentifierQuotingEnabled is true. + * Calls doQuoting() when identifierQuoting is enabled. + * + * @param string $text + * + * @return string Quoted identifier. + */ + public function quoteIdentifier(string $text): string; } diff --git a/src/Propel/Generator/Platform/Util/AlterTableStatementMerger.php b/src/Propel/Generator/Platform/Util/AlterTableStatementMerger.php new file mode 100644 index 0000000000..1abca18b2f --- /dev/null +++ b/src/Propel/Generator/Platform/Util/AlterTableStatementMerger.php @@ -0,0 +1,132 @@ +mergeStatements($sql); + } + + /** + * @param \Propel\Generator\Model\Table $table + */ + public function __construct(Table $table) + { + $this->table = $table; + + // quoting needs to come from platform, not from table (bug?) + //$this->quotedTableName = $this->table->quoteIdentifier($this->table->getName()); + $this->quotedTableName = $this->table->getPlatform()->quoteIdentifier($this->table->getName()); + } + + /** + * Merges column changes into one command. This is more compatible + * especially with PK constraints. + * + * @param string $sql + * + * @return string + */ + public function mergeStatements(string $sql): string + { + $statements = explode(';', $sql); + $blocks = []; + $currentBlock = []; + + foreach ($statements as $statement) { + $statement = trim($statement); + + if (!$statement) { + continue; + } + + $canMerge = $this->canMergeStatement($statement); + if ($canMerge) { + $currentBlock[] = $statement; + + continue; + } + + if ($currentBlock) { + $blocks[] = $this->mergeAlterTableStatements($currentBlock); + $currentBlock = []; + } + + if ("\n;$statement;\n" === static::NO_MERGE_ACROSS_THIS_LINE) { + continue; + } + $blocks[] = $statement . ';'; + } + if ($currentBlock) { + $blocks[] = $this->mergeAlterTableStatements($currentBlock); + } + + return "\n" . implode("\n\n", $blocks); + } + + /** + * @param array $statements + * + * @return string + */ + protected function mergeAlterTableStatements(array $statements): string + { + if (!$statements) { + return ''; + } + $alterTableExpression = "ALTER TABLE {$this->quotedTableName} "; + $changeStatements = array_map(fn ($statement) => str_replace($alterTableExpression, '', $statement), $statements); + $mergedStatements = "\n\n " . implode(",\n\n ", $changeStatements); + + return "ALTER TABLE {$this->quotedTableName}$mergedStatements;\n"; + } + + /** + * @param string $statement + * + * @return bool + */ + protected function canMergeStatement(string $statement): bool + { + $canMergeStatementRegex = "/ALTER TABLE {$this->quotedTableName} (?!RENAME)/"; // alter table statements but not rename + + return (bool)preg_match($canMergeStatementRegex, $statement); + } +} diff --git a/src/Propel/Generator/Platform/Util/MysqlUuidMigrationBuilder.php b/src/Propel/Generator/Platform/Util/MysqlUuidMigrationBuilder.php new file mode 100644 index 0000000000..85daff0ec1 --- /dev/null +++ b/src/Propel/Generator/Platform/Util/MysqlUuidMigrationBuilder.php @@ -0,0 +1,178 @@ +platform = $platform; + } + + /** + * @param \Propel\Generator\Platform\MysqlPlatform $platform + * + * @return static + */ + public static function create(MysqlPlatform $platform) + { + return new static($platform); + } + + /** + * @param \Propel\Generator\Model\Column $fromColumn + * @param \Propel\Generator\Model\Column $toColumn + * @param bool $toUuidBinary + * + * @return string + */ + public function buildMigration(Column $fromColumn, Column $toColumn, bool $toUuidBinary): string + { + $sqlBlock = [AlterTableStatementMerger::NO_MERGE_ACROSS_THIS_LINE]; + $sqlBlock[] = $this->getMigrateUuidNotification($toColumn); + $sqlBlock[] = AlterTableStatementMerger::NO_MERGE_ACROSS_THIS_LINE; + + // remove constraints and indexes + $indexes = $this->getSharedIndexes($fromColumn, $toColumn); + foreach ($indexes as $index) { + $sqlBlock[] = $this->platform->getDropIndexDDL($index); + } + $movePrimaryKey = $fromColumn->isPrimaryKey() && $toColumn->isPrimaryKey(); + if ($movePrimaryKey) { + $sqlBlock[] = $this->platform->getDropPrimaryKeyDDL($fromColumn->getTable()); + } + + $sqlBlock[] = $this->buildUuidMigrationStatements($toColumn, $toUuidBinary); + + // restore column constraints + if ($movePrimaryKey) { + $sqlBlock[] = $this->platform->getAddPrimaryKeyDDL($toColumn->getTable()); + } + foreach ($indexes as $index) { + $sqlBlock[] = $this->platform->getAddIndexDDL($index); + } + + $sqlBlock[] = "# END migration of UUIDs in column '{$fromColumn->getName()}'\n"; + $sqlBlock[] = AlterTableStatementMerger::NO_MERGE_ACROSS_THIS_LINE; + + return implode('', $sqlBlock); + } + + /** + * Get foreign keys in both given columns. + * + * @param \Propel\Generator\Model\Column $fromColumn + * @param \Propel\Generator\Model\Column $toColumn + * + * @return array + */ + protected function getSharedFks(Column $fromColumn, Column $toColumn): array + { + $sharedKeys = []; + + foreach (['getReferrers', 'getForeignKeys'] as $getFk) { + $oldFks = $fromColumn->$getFk(); + $newFks = $toColumn->$getFk(); + + foreach ($oldFks as $oldFk) { + foreach ($newFks as $newFk) { + if ($oldFk->equals($newFk)) { + continue; + } + $sharedKeys[] = $oldFk; + } + } + } + + return $sharedKeys; + } + + /** + * Get indexes in both given columns. + * + * @param \Propel\Generator\Model\Column $fromColumn + * @param \Propel\Generator\Model\Column $toColumn + * + * @return array + */ + protected function getSharedIndexes(Column $fromColumn, Column $toColumn): array + { + $toIndexes = $toColumn->getTable()->getIndexesOnColumn($toColumn); + $fromTable = $fromColumn->getTable(); + + return array_filter($toIndexes, fn (Index $index) => $fromTable->hasIndex($index->getName())); + } + + /** + * @param \Propel\Generator\Model\Column $column + * + * @return string + */ + protected function getMigrateUuidNotification(Column $column) + { + $columnName = $column->getName(); + $tableName = $column->getTable()->getName(); + + return <<< EOT +# START migration of UUIDs in column '$tableName.$columnName'. +# This can break your DB. Validate and edit these statements as you see fit. +# Please be aware of Propel's ABSOLUTELY NO WARRANTY policy! + +EOT; + } + + /** + * @param \Propel\Generator\Model\Column $column + * @param bool $toUuidBinary + * + * @return string + */ + protected function buildUuidMigrationStatements(Column $column, bool $toUuidBinary): string + { + $tableName = $this->quoteIdentifier($column->getTable()->getName()); + $columnName = $this->quoteIdentifier($column->getName()); + $tmpColumnName = $this->quoteIdentifier($column->getName() . '_' . bin2hex(random_bytes(4))); + $swapFlag = $column->getTable()->getVendorInfoForType('mysql')->getUuidSwapFlagLiteral(); + $columnDefinition = $this->platform->getColumnDDL($column); + $sqlType = $this->platform->getSqlTypeExpression($column); + + $convertFunction = ($toUuidBinary) ? 'UUID_TO_BIN' : 'BIN_TO_UUID'; + + return <<< EOT +ALTER TABLE $tableName ADD COLUMN $tmpColumnName $sqlType AFTER $columnName; +UPDATE $tableName SET $tmpColumnName = $convertFunction($columnName, $swapFlag); +ALTER TABLE $tableName DROP COLUMN $columnName; +ALTER TABLE $tableName CHANGE COLUMN $tmpColumnName $columnDefinition; +EOT; + } + + /** + * @param string $identifier + * + * @return string + */ + protected function quoteIdentifier(string $identifier): string + { + return $this->platform->quoteIdentifier($identifier); + } +} diff --git a/src/Propel/Generator/Reverse/MysqlSchemaParser.php b/src/Propel/Generator/Reverse/MysqlSchemaParser.php index 8041162150..9ace2a0af1 100644 --- a/src/Propel/Generator/Reverse/MysqlSchemaParser.php +++ b/src/Propel/Generator/Reverse/MysqlSchemaParser.php @@ -59,7 +59,7 @@ class MysqlSchemaParser extends AbstractSchemaParser 'timestamp' => PropelTypes::TIMESTAMP, 'tinyblob' => PropelTypes::BINARY, 'blob' => PropelTypes::BLOB, - 'mediumblob' => PropelTypes::VARBINARY, + 'mediumblob' => PropelTypes::OBJECT, 'longblob' => PropelTypes::LONGVARBINARY, 'longtext' => PropelTypes::CLOB, 'tinytext' => PropelTypes::VARCHAR, @@ -67,6 +67,8 @@ class MysqlSchemaParser extends AbstractSchemaParser 'text' => PropelTypes::LONGVARCHAR, 'enum' => PropelTypes::CHAR, 'set' => PropelTypes::CHAR, + 'binary' => PropelTypes::BINARY, + 'varbinary' => PropelTypes::VARBINARY, ]; /** diff --git a/tests/Propel/Tests/Generator/Migration/BaseTest.php b/tests/Propel/Tests/Generator/Migration/BaseTest.php index d51dcb1ea8..4ae0fa41e1 100644 --- a/tests/Propel/Tests/Generator/Migration/BaseTest.php +++ b/tests/Propel/Tests/Generator/Migration/BaseTest.php @@ -237,8 +237,8 @@ public function testColumnTypeChangeMoreComplex() - - + + @@ -265,10 +265,10 @@ public function testColumnTypeChangeMoreComplex() - + - + diff --git a/tests/Propel/Tests/Generator/Migration/MigrationTestCase.php b/tests/Propel/Tests/Generator/Migration/MigrationTestCase.php index 0d71b57612..22f0f56b12 100644 --- a/tests/Propel/Tests/Generator/Migration/MigrationTestCase.php +++ b/tests/Propel/Tests/Generator/Migration/MigrationTestCase.php @@ -121,12 +121,14 @@ public function applyXml($xml, $changeRequired = false) $stmt = $this->con->prepare($statement); $stmt->execute(); } catch (Exception $e) { - throw new BuildException(sprintf( - "Can not execute SQL: \n%s\nFrom database: \n%s\n\nTo database: \n%s\n", + $message = sprintf( + "Cannot execute SQL: \n%s\nError: %s\nFrom database: \n%s\n\nTo database: \n%s\n", $statement, + $e->getMessage(), $this->database, $database - ), null, $e); + ); + throw new BuildException($message, 0, $e); } } $this->con->commit(); @@ -156,18 +158,19 @@ public function readDatabase() * * @return void */ - public function migrateAndTest($originXml, $targetXml) + public function migrateAndTest($originXml, $targetXml, ?string $description = null) { + $messagePrefix = $description ? $description . ': ' : ''; try { $this->applyXmlAndTest($originXml); } catch (BuildException $e) { - $this->fail("Failed to apply the first/original schema:\n\n" . $e->getMessage()); + $this->fail("{$messagePrefix}Failed to apply the first/original schema:\n\n" . $e->getMessage()); } try { $this->applyXmlAndTest($targetXml, true); } catch (BuildException $e) { - $this->fail("Failed to apply the second/target schema:\n\n" . $e->getMessage()); + $this->fail("{$messagePrefix}Failed to apply the second/target schema:\n\n" . $e->getMessage()); } } @@ -198,7 +201,7 @@ public function compareCurrentDatabase(Database $database) { $this->readDatabase(); $diff = DatabaseComparator::computeDiff($this->database, $database); - if (false !== $diff) { + if ($diff !== false) { $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); throw new BuildException(sprintf( diff --git a/tests/Propel/Tests/Generator/Migration/MysqlMigrateUuidColumnTest.php b/tests/Propel/Tests/Generator/Migration/MysqlMigrateUuidColumnTest.php new file mode 100644 index 0000000000..0e710c10fd --- /dev/null +++ b/tests/Propel/Tests/Generator/Migration/MysqlMigrateUuidColumnTest.php @@ -0,0 +1,311 @@ +checkMysqlVersionAtLeast8('migration')) { + $this->markTestSkipped('Test can only be run on MySQL version >= 8'); + + return; + } + $this->con->exec('DROP TABLE IF EXISTS migration.table_with_uuid;'); + } + + /** + * @dataProvider migrationDataProvider + * + * @param string $description + * @param string $fromColumns + * @param string $toColumns + * @param array|null $values + * + * @return void + */ + public function testMigrations(string $description, string $fromColumns, string $toColumns, ?array $values) + { + $this->applyWithFail($description . ' - failed to apply initial schema', $fromColumns, false); + $values && $this->insertValues($values['in']); + + $this->applyWithFail($description . ' - failed to apply migration', $toColumns, true); + $values && $this->assertTableDataMatches($values['out'], $description); + } + + /** + * @param array $values + * + * @return void + */ + protected function insertValues(array $values) + { + $valueS = "'" . implode("','", $values) . "'"; + $this->con->exec("INSERT INTO migration.table_with_uuid VALUES($valueS);"); + } + + /** + * @param array $values + * @param string $description + * + * @return void + */ + protected function assertTableDataMatches(array $values, string $description) + { + $row = $this->con->query('SELECT * FROM migration.table_with_uuid;')->fetch(); + //var_dump($values, $row); + $this->assertEquals($values, $row, $description); + } + + /** + * @param string $description + * @param string $columns + * @param bool $changeRequired + * + * @return void + */ + protected function applyWithFail(string $description, string $columns, bool $changeRequired) + { + $databaseXml = $this->buildDatabaseTableXml($columns); + + try { + $this->applyXmlAndTest($databaseXml, $changeRequired); + } catch (BuildException $e) { + $this->fail($description . "\n\n" . $e->getMessage()); + } + } + + /** + * @param string $columnDef + * + * @return string + */ + protected function buildDatabaseTableXml(string $columnDef): string + { + return <<< EOF + + + $columnDef +
+
+EOF; + } + + /** + * @return array + */ + public function migrationDataProvider(): array + { + $uuid = '6cb1a126-2b34-4856-9a39-455d8b5efd29'; + $bin = hex2bin('48562b346cb1a1269a39455d8b5efd29'); + + $uuid2 = 'a3b98dbb-2bb3-4319-a010-21e2301e7d3c'; + $bin2 = hex2bin('43192bb3a3b98dbba01021e2301e7d3c'); + + return [ + [ + 'From varchar to uuid', + '', + '', + ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'From char to uuid', + '', + '', + ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'From uuid to varchar', + '', + '', + ['in' => [$bin], 'out' => [$uuid]], + ], [ + 'From uuid to char', + '', + '', + ['in' => [$bin], 'out' => [$uuid]], + ], [ + 'Preserve column order', + ' + + + + ', ' + + + + ', + null, + ], [ + 'Change column order', + ' + + + + + ', ' + + + + + ', + null, + ], [ + 'Drop primary key', + '', + '', + ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'Add primary key', + '', + '', + ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'Swap uuid and varchar in complex primary key', + ' + + + ', ' + + + ', [ + 'in' => [$uuid, $bin2], 'out' => [$bin, $uuid2], + ], + ], [ + 'Swap uuid and varchar and drop complex primary key', + ' + + + ', ' + + + ', ['in' => [$uuid, $bin2], 'out' => [$bin, $uuid2]], + ], [ + 'Swap uuid and varchar and add complex primary key', + ' + + + ', ' + + + ', ['in' => [$uuid, $bin2], 'out' => [$bin, $uuid2]], + ], [ + 'Preserve complex PK', + ' + + + ', ' + + + ', ['in' => [$uuid, 'le title'], 'out' => [$bin, 'le title']], + ], [ + 'Preserve index', + ' + + + + + ', ' + + + + + ', ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'Drop index', + ' + + + + + ', ' + + ', ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'Add index', + ' + + ', ' + + + + + ', ['in' => [$uuid], 'out' => [$bin]], + ], [ + 'Preserve index on multiple columns', + ' + + + + + + + ', ' + + + + + + + ', ['in' => [$uuid, 'le title'], 'out' => [$bin, 'le title']], + ], [ + 'Preserve FK', + ' + + + + + + ', ' + + + + + + ', ['in' => [$uuid, $uuid], 'out' => [$bin, $bin]], + ], [ + 'Add FK', + ' + + + ', ' + + + + + + ', ['in' => [$uuid, $uuid], 'out' => [$bin, $bin]], + ], [ + 'Remove FK', + ' + + + + + + ', ' + + + ', ['in' => [$uuid, $uuid], 'out' => [$bin, $bin]], + ], + ]; + } +} diff --git a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php index 8bda8a48fc..34f3f5a6be 100644 --- a/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php +++ b/tests/Propel/Tests/Generator/Platform/MysqlPlatformMigrationTest.php @@ -459,4 +459,65 @@ public function testTableRenaming() 'Table `Foo2` should not renamed to `foo_bla` since we have already renamed a table to this name.' ); } + + /** + * @dataProvider providerForTestMigrateToUuidBinColumn + * + * @return void + */ + public function testGetMigrateToUuidBinaryColumn($tableDiff) + { + $expected = <<<'EOT' + +;--sql statement block; +# START migration of UUIDs in column 'foo.id'. +# This can break your DB. Validate and edit these statements as you see fit. +# Please be aware of Propel's ABSOLUTELY NO WARRANTY policy! + +;--sql statement block; + +ALTER TABLE `foo` DROP PRIMARY KEY; +ALTER TABLE `foo` ADD COLUMN `id_%x` BINARY(16) AFTER `id`; +UPDATE `foo` SET `id_%x` = UUID_TO_BIN(`id`, true); +ALTER TABLE `foo` DROP COLUMN `id`; +ALTER TABLE `foo` CHANGE COLUMN `id_%x` `id` BINARY(16) DEFAULT vendor_specific_uuid_generator_function() NOT NULL; +ALTER TABLE `foo` ADD PRIMARY KEY (`id`); +# END migration of UUIDs in column 'id' + +;--sql statement block; + +EOT; + $actual = $this->getPlatform()->getModifyTableColumnsDDL($tableDiff); + $this->assertStringMatchesFormat($expected, $actual); + } + + /** + * @dataProvider providerForTestMigrateFromUuidBinColumn + * + * @return void + */ + public function testGetMigrateFromUuidBinaryColumn($tableDiff) + { + $expected = <<getPlatform()->getModifyTableColumnsDDL($tableDiff); + $this->assertStringMatchesFormat($expected, $actual); + } } diff --git a/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php b/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php index 6b2abafd07..8c048786ee 100644 --- a/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php +++ b/tests/Propel/Tests/Generator/Platform/PlatformMigrationTestProvider.php @@ -624,7 +624,7 @@ public function providerForTestMigrateToUUIDColumn() public function providerForTestMigrateToUuidBinColumn() { $tableColumnsFrom = << + EOF; $tableColumnsTo = << @@ -632,4 +632,16 @@ public function providerForTestMigrateToUuidBinColumn() return [[$this->buildTableDiff('foo', $tableColumnsFrom, $tableColumnsTo)]]; } + + public function providerForTestMigrateFromUuidBinColumn() + { + $tableColumnsFrom = << +EOF; + $tableColumnsTo = << +EOF; + + return [[$this->buildTableDiff('foo', $tableColumnsFrom, $tableColumnsTo)]]; + } } diff --git a/tests/Propel/Tests/Helpers/CheckMysql8Trait.php b/tests/Propel/Tests/Helpers/CheckMysql8Trait.php new file mode 100644 index 0000000000..49bde01175 --- /dev/null +++ b/tests/Propel/Tests/Helpers/CheckMysql8Trait.php @@ -0,0 +1,47 @@ +queryMySqlVersionAtLeast8($connectionName); + } + + return static::$isAtLeastMysql8; + } + + /** + * @param string|null $connectionName + * + * @return bool + */ + protected function queryMySqlVersionAtLeast8(?string $connectionName = null): bool + { + $con = Propel::getServiceContainer()->getConnection($connectionName); + $query = "SELECT VERSION() NOT LIKE '%MariaDB%' AND VERSION() >= 8"; + $result = $con->query($query)->fetchColumn(0); + + return (bool)$result; + } +} diff --git a/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php b/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php index 57b3ce4bfb..5d08eee685 100644 --- a/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php +++ b/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php @@ -12,6 +12,7 @@ use Propel\Runtime\Propel; use Propel\Runtime\Util\UuidConverter; use Propel\Tests\Helpers\Bookstore\BookstoreTestBase; +use Propel\Tests\Helpers\CheckMysql8Trait; /** * @group mysql @@ -19,31 +20,17 @@ */ class UuidConverterMysqlCompatibilityTest extends BookstoreTestBase { - /** - * @var ?bool - */ - protected $isAtLeastMysql8 = null; + use CheckMysql8Trait; protected function setUp(): void { parent::setUp(); - if ($this->isAtLeastMysql8 === null) { - $this->isAtLeastMysql8 = $this->getMySqlVersionAtLeast8(); - } - if (!$this->isAtLeastMysql8) { + if(!$this->checkMysqlVersionAtLeast8()){ $this->markTestSkipped('Test can only be run on MySQL version >= 8'); + return; } } - protected function getMySqlVersionAtLeast8(): bool - { - $con = Propel::getServiceContainer()->getConnection(); - $query = "SELECT VERSION() NOT LIKE '%MariaDB%' AND VERSION() >= 8"; - $result = $con->query($query)->fetchColumn(0); - - return (bool) $result; - } - public function operationsDataProvider(): array { return [ From 4f89150d219eb29b01530e3b77abd68814bf87df Mon Sep 17 00:00:00 2001 From: mringler Date: Tue, 6 Dec 2022 21:42:29 +0100 Subject: [PATCH 6/8] Switch Github tests from MariaDB to MySQL --- .github/workflows/ci.yml | 82 +++++++++++++++++++++++----------------- tests/bin/setup.mysql.sh | 69 +++++++++++++++------------------ tests/bin/setup.pgsql.sh | 8 ++-- 3 files changed, 81 insertions(+), 78 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8db32060a8..bb7b9e59b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,33 +24,40 @@ jobs: php-version: '7.4' - symfony-version: '6-max' php-version: '7.4' + env: + DB_NAME: 'propel_tests' + DB_USER: 'propel' + DB_PW: 'propel' steps: - name: Install PostgreSQL latest - if: matrix.db-type == 'pgsql' && matrix.php-version != '7.3' + if: matrix.db-type == 'pgsql' && matrix.php-version != '7.4' uses: CasperWA/postgresql-action@v1.2 with: - postgresql db: 'propel-tests' - postgresql user: 'postgres' - postgresql password: 'postgres' + postgresql db: $DB_NAME + postgresql user: $DB_USER + postgresql password: $DB_PW - name: Install PostgreSQL min - if: matrix.db-type == 'pgsql' && matrix.php-version == '7.3' + if: matrix.db-type == 'pgsql' && matrix.php-version == '7.4' uses: CasperWA/postgresql-action@v1.2 with: postgresql version: 9 - postgresql db: 'propel-tests' - postgresql user: 'postgres' - postgresql password: 'postgres' + postgresql db: $DB_NAME + postgresql user: $DB_USER + postgresql password: $DB_PW - - name: Install MariaDb latest - if: matrix.db-type == 'mysql' && matrix.php-version != '7.3' - uses: getong/mariadb-action@v1.1 + - name: Install MySQL latest + if: matrix.db-type == 'mysql' && matrix.php-version != '7.4' + uses: mirromutth/mysql-action@v1.1 + with: + mysql root password: $DB_PW - name: Install MariaDb min - if: matrix.db-type == 'mysql' && matrix.php-version == '7.3' + if: matrix.db-type == 'mysql' && matrix.php-version == '7.4' uses: getong/mariadb-action@v1.1 with: mariadb version: '10.2' + mysql root password: $DB_PW - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 @@ -79,34 +86,39 @@ jobs: - name: Composer install (Symfony version ${{ matrix.symfony-version }}) run: composer install --no-progress --prefer-dist --optimize-autoloader - - name: Setup Postgresql database for test suite - if: matrix.db-type == 'pgsql' - run: tests/bin/setup.pgsql.sh + - name: Wait for MySQL server to load + if: matrix.db-type == 'mysql' + run: | + bash -c " + for i in {1..10}; do + mysqladmin -h 127.0.0.1 -u root status >/dev/null 2>&1 && exit 0 || sleep 6 + echo 'trying again' + done; + echo 'could not establish connection after 10 tries' + exit 1 + " env: - DB_NAME: 'propel-tests' - DB_USER: 'postgres' - DB_PW: 'postgres' + MYSQL_PWD: ${{ env.DB_PW }} - - name: Setup the database for test suite - if: matrix.db-type != 'agnostic' && matrix.db-type != 'pgsql' - run: tests/bin/setup.${{ matrix.db-type }}.sh - - - name: Run PostgreSQL tests - if: matrix.db-type == 'pgsql' - shell: 'script -q -e -c "bash {0}"' + - name: Create MySQL Propel user + if: matrix.db-type == 'mysql' run: | - if [[ ${{ matrix.php-version }} == '7.4' && ${{ matrix.symfony-version == '5-max' }} ]]; then - export CODECOVERAGE=1 && vendor/bin/phpunit -c tests/pgsql.phpunit.xml --verbose --coverage-clover=tests/coverage.xml - else - vendor/bin/phpunit -c tests/pgsql.phpunit.xml - fi + mysql -h 127.0.0.1 -u root -e " + CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PW'; + CREATE USER '$DB_USER'@'%' IDENTIFIED BY '$DB_PW'; + GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'localhost'; + GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'%'; + FLUSH PRIVILEGES; + " env: - DB_NAME: 'propel-tests' - DB_USER: 'postgres' - DB_PW: 'postgres' + MYSQL_PWD: ${{ env.DB_PW }} + + - name: Setup database for test suite + if: matrix.db-type != 'agnostic' + run: tests/bin/setup.${{ matrix.db-type }}.sh - - name: Run ${{ matrix.db-type }} tests - if: matrix.db-type != 'pgsql' + - name: Run database tests + if: matrix.db-type != 'agnostic' shell: 'script -q -e -c "bash {0}"' run: | if [[ ${{ matrix.php-version }} == '7.4' && ${{ matrix.symfony-version == '5-max' }} ]]; then diff --git a/tests/bin/setup.mysql.sh b/tests/bin/setup.mysql.sh index 97d71507bb..6c429c76df 100755 --- a/tests/bin/setup.mysql.sh +++ b/tests/bin/setup.mysql.sh @@ -1,11 +1,6 @@ #!/bin/sh -mysql=`which mysql`; -if [ "$mysql" = "" ]; then - mysql=`which mysql5`; -fi - -if [ "$mysql" = "" ]; then +if ! command -v mysql > /dev/null; then echo "Cannot find mysql binary. Is it installed?"; exit 1; fi @@ -15,42 +10,38 @@ if [ "$DB_USER" = "" ]; then DB_USER="root"; fi -pw_option="" -if [ "$DB_PW" != "" ]; then - pw_option=" -p$DB_PW" +if [ "$DB_NAME" = "" ]; then + echo "\$DB_NAME not set. Using 'test'."; + DB_NAME="test"; fi DB_HOSTNAME=${DB_HOSTNAME-127.0.0.1}; -DB_NAME=${DB_NAME-test}; - -"$mysql" --version; - -retry_count=0 -while true; do - "$mysql" --host="$DB_HOSTNAME" -u"$DB_USER" $pw_option -e " -SET FOREIGN_KEY_CHECKS = 0; -DROP DATABASE IF EXISTS $DB_NAME; -DROP SCHEMA IF EXISTS second_hand_books; -DROP SCHEMA IF EXISTS contest; -DROP SCHEMA IF EXISTS bookstore_schemas; -DROP SCHEMA IF EXISTS migration; -SET FOREIGN_KEY_CHECKS = 1; -" - if [ $? -eq 0 ] || [ $retry_count -ge 6 ]; then break; fi - retry_count=$((retry_count + 1)) - sleep "$(awk "BEGIN{print 2 ^ $retry_count}")" -done - -"$mysql" --host="$DB_HOSTNAME" -u"$DB_USER" $pw_option -e " -SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); -CREATE DATABASE $DB_NAME; -CREATE SCHEMA bookstore_schemas; -CREATE SCHEMA contest; -CREATE SCHEMA second_hand_books; -CREATE SCHEMA migration; -"; - - +DB_PW=${DB_PW-$MYSQL_PWD}; + +( + export MYSQL_PWD="$DB_PW" + + echo "Dropping existing databases and schemas" + mysql --host="$DB_HOSTNAME" -u"$DB_USER" -e " + SET FOREIGN_KEY_CHECKS = 0; + DROP DATABASE IF EXISTS $DB_NAME; + DROP SCHEMA IF EXISTS second_hand_books; + DROP SCHEMA IF EXISTS contest; + DROP SCHEMA IF EXISTS bookstore_schemas; + DROP SCHEMA IF EXISTS migration; + SET FOREIGN_KEY_CHECKS = 1; + " + + echo "Creating existing databases and schemas" + mysql --host="$DB_HOSTNAME" -u"$DB_USER" -e " + SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); + CREATE DATABASE $DB_NAME; + CREATE SCHEMA bookstore_schemas; + CREATE SCHEMA contest; + CREATE SCHEMA second_hand_books; + CREATE SCHEMA migration; + "; +) DIR=`dirname $0`; dsn="mysql:host=$DB_HOSTNAME;dbname=$DB_NAME"; diff --git a/tests/bin/setup.pgsql.sh b/tests/bin/setup.pgsql.sh index bd6a3821c7..66adbdb36c 100755 --- a/tests/bin/setup.pgsql.sh +++ b/tests/bin/setup.pgsql.sh @@ -25,15 +25,15 @@ else fi ( - export PGPASSWORD=$DB_PW; + export PGPASSWORD="$DB_PW"; - echo "removing existing test db" + echo "Dropping existing test db" dropdb --host="$DB_HOSTNAME" --username="$DB_USER" $NO_PWD "$DB_NAME"; - echo "creating new test db" + echo "Creating new test db" createdb --host="$DB_HOSTNAME" --username="$DB_USER" $NO_PWD "$DB_NAME"; - echo "creating schema" + echo "Creating schemas" psql --host="$DB_HOSTNAME" --username="$DB_USER" $NO_PWD -c ' CREATE SCHEMA bookstore_schemas; CREATE SCHEMA contest; From ce0a677d354f52a2ff13f7e75ce2629e24335830 Mon Sep 17 00:00:00 2001 From: mringler Date: Wed, 7 Dec 2022 23:47:36 +0100 Subject: [PATCH 7/8] review changes --- .../Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php | 11 ++++++++--- .../Util/UuidConverterMysqlCompatibilityTest.php | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php b/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php index ccac73bf5c..7a4d476ff8 100644 --- a/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php +++ b/tests/Propel/Tests/Runtime/Adapter/Pdo/OracleAdapterTest.php @@ -32,8 +32,9 @@ protected function getDriver() return 'oracle'; } - protected function createOracleSql(Criteria $query, &$params = []): string + protected function createOracleSql(Criteria $query): string { + $params = []; Propel::getServiceContainer()->setAdapter('oracle', new OracleAdapter()); $query->setDbName('oracle'); return $query->createSelectSql($params); @@ -88,8 +89,12 @@ public function testCreateSelectSqlPart() $c = new Criteria(); $c->addSelectColumn(BookTableMap::COL_ID); $c->addAsColumn('book_ID', BookTableMap::COL_ID); - $selectSql = $this->createOracleSql($c); - $this->assertEquals('SELECT book.id, book.id AS book_ID FROM book', $selectSql, 'createSelectSqlPart() returns a SQL SELECT clause with both select and as columns'); + + $adapter = new OracleAdapter(); + $fromClause = []; + $selectSql = $adapter->createSelectSqlPart($c, $fromClause); + $this->assertEquals('SELECT book.id, book.id AS book_ID', $selectSql, 'createSelectSqlPart() returns a SQL SELECT clause with both select and as columns'); + $this->assertEquals(['book'], $fromClause, 'createSelectSqlPart() adds the tables from the select columns to the from clause'); } /** diff --git a/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php b/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php index 5d08eee685..28e498513f 100644 --- a/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php +++ b/tests/Propel/Tests/Runtime/Util/UuidConverterMysqlCompatibilityTest.php @@ -59,7 +59,7 @@ public function testBinToUuidBehavesLikeInMysql($description, $sqlStatement, $ca $this->assertSame($mysqlBin, $propelBin, $description . ' should match between Propel and MySQL'); } - public function executeStatement(string $statement, string $value) + protected function executeStatement(string $statement, string $value) { $con = Propel::getServiceContainer()->getConnection(); $ps = $con->prepare($statement); From 7692e7384314484077bb8d6c320af402cb9658ae Mon Sep 17 00:00:00 2001 From: mringler Date: Thu, 8 Dec 2022 23:17:22 +0100 Subject: [PATCH 8/8] removed changes to varbinary MySQL type --- src/Propel/Generator/Platform/MysqlPlatform.php | 2 +- src/Propel/Generator/Reverse/MysqlSchemaParser.php | 3 +-- tests/Propel/Tests/Generator/Migration/BaseTest.php | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Propel/Generator/Platform/MysqlPlatform.php b/src/Propel/Generator/Platform/MysqlPlatform.php index bc5ba77dd3..901e33fcad 100644 --- a/src/Propel/Generator/Platform/MysqlPlatform.php +++ b/src/Propel/Generator/Platform/MysqlPlatform.php @@ -58,7 +58,7 @@ protected function initialize(): void $this->setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, 'DECIMAL')); $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, 'TEXT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, 'BINARY')); - $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, 'VARBINARY')); + $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, 'MEDIUMBLOB')); $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, 'LONGBLOB')); $this->setSchemaDomainMapping(new Domain(PropelTypes::CLOB, 'LONGTEXT')); $this->setSchemaDomainMapping(new Domain(PropelTypes::OBJECT, 'MEDIUMBLOB')); diff --git a/src/Propel/Generator/Reverse/MysqlSchemaParser.php b/src/Propel/Generator/Reverse/MysqlSchemaParser.php index 9ace2a0af1..b473faa359 100644 --- a/src/Propel/Generator/Reverse/MysqlSchemaParser.php +++ b/src/Propel/Generator/Reverse/MysqlSchemaParser.php @@ -59,7 +59,7 @@ class MysqlSchemaParser extends AbstractSchemaParser 'timestamp' => PropelTypes::TIMESTAMP, 'tinyblob' => PropelTypes::BINARY, 'blob' => PropelTypes::BLOB, - 'mediumblob' => PropelTypes::OBJECT, + 'mediumblob' => PropelTypes::VARBINARY, 'longblob' => PropelTypes::LONGVARBINARY, 'longtext' => PropelTypes::CLOB, 'tinytext' => PropelTypes::VARCHAR, @@ -68,7 +68,6 @@ class MysqlSchemaParser extends AbstractSchemaParser 'enum' => PropelTypes::CHAR, 'set' => PropelTypes::CHAR, 'binary' => PropelTypes::BINARY, - 'varbinary' => PropelTypes::VARBINARY, ]; /** diff --git a/tests/Propel/Tests/Generator/Migration/BaseTest.php b/tests/Propel/Tests/Generator/Migration/BaseTest.php index 4ae0fa41e1..0231ba70c2 100644 --- a/tests/Propel/Tests/Generator/Migration/BaseTest.php +++ b/tests/Propel/Tests/Generator/Migration/BaseTest.php @@ -238,7 +238,7 @@ public function testColumnTypeChangeMoreComplex() - + @@ -265,7 +265,7 @@ public function testColumnTypeChangeMoreComplex() - +