From ce9b4db60c0abe298880881bf569d238cd6a8bbb Mon Sep 17 00:00:00 2001 From: Tigrov Date: Fri, 8 Nov 2024 08:23:32 +0700 Subject: [PATCH 1/7] Use new column definition builder --- src/Column.php | 4 ++ src/Column/ColumnDefinitionBuilder.php | 4 +- src/DDLQueryBuilder.php | 33 ++++++++--------- src/Schema.php | 1 + tests/ColumnSchemaBuilderTest.php | 26 ------------- .../Provider/ColumnSchemaBuilderProvider.php | 28 -------------- tests/Provider/QueryBuilderProvider.php | 6 +-- tests/QueryBuilderTest.php | 37 +++++++++++-------- 8 files changed, 46 insertions(+), 93 deletions(-) delete mode 100644 tests/ColumnSchemaBuilderTest.php delete mode 100644 tests/Provider/ColumnSchemaBuilderProvider.php diff --git a/src/Column.php b/src/Column.php index e59fc0238..3ac53f99f 100644 --- a/src/Column.php +++ b/src/Column.php @@ -21,6 +21,10 @@ * * Provides a fluent interface, which means that the methods can be chained together to create a column schema with * many properties in a single line of code. + * + * @psalm-suppress DeprecatedClass + * + * @deprecated Use {@see StringColumnSchema} or other column classes instead. Will be removed in 2.0.0. */ final class Column extends AbstractColumn { diff --git a/src/Column/ColumnDefinitionBuilder.php b/src/Column/ColumnDefinitionBuilder.php index 0bf498b6f..8532fa65b 100644 --- a/src/Column/ColumnDefinitionBuilder.php +++ b/src/Column/ColumnDefinitionBuilder.php @@ -59,7 +59,7 @@ protected function buildType(ColumnSchemaInterface $column): string protected function getDbType(ColumnSchemaInterface $column): string { /** @psalm-suppress DocblockTypeContradiction */ - return match ($column->getType()) { + return $column->getDbType() ?? match ($column->getType()) { ColumnType::BOOLEAN => 'boolean', ColumnType::BIT => 'varbit', ColumnType::TINYINT => $column->isAutoIncrement() ? 'smallserial' : 'smallint', @@ -71,7 +71,7 @@ protected function getDbType(ColumnSchemaInterface $column): string ColumnType::DECIMAL => 'numeric', ColumnType::MONEY => 'money', ColumnType::CHAR => 'char', - ColumnType::STRING => 'varchar', + ColumnType::STRING => 'varchar(' . ($column->getSize() ?? 255) . ')', ColumnType::TEXT => 'text', ColumnType::BINARY => 'bytea', ColumnType::UUID => 'uuid', diff --git a/src/DDLQueryBuilder.php b/src/DDLQueryBuilder.php index de4442fc9..4d495125c 100644 --- a/src/DDLQueryBuilder.php +++ b/src/DDLQueryBuilder.php @@ -9,10 +9,12 @@ use Yiisoft\Db\QueryBuilder\AbstractDDLQueryBuilder; use Yiisoft\Db\Schema\Builder\ColumnInterface; +use Yiisoft\Db\Schema\Column\ColumnSchemaInterface; use function array_diff; use function array_unshift; use function explode; use function implode; +use function is_string; use function preg_match; use function preg_replace; use function str_contains; @@ -27,38 +29,33 @@ public function addDefaultValue(string $table, string $name, string $column, mix throw new NotSupportedException(__METHOD__ . ' is not supported by PostgreSQL.'); } - public function alterColumn(string $table, string $column, ColumnInterface|string $type): string + public function alterColumn(string $table, string $column, ColumnInterface|ColumnSchemaInterface|string $type): string { $columnName = $this->quoter->quoteColumnName($column); $tableName = $this->quoter->quoteTableName($table); - if ($type instanceof ColumnInterface) { - $type = $type->asString(); - } - /** * @link https://github.com/yiisoft/yii2/issues/4492 * @link https://www.postgresql.org/docs/9.1/static/sql-altertable.html */ - if (preg_match('/^(DROP|SET|RESET|USING)\s+/i', $type)) { - return "ALTER TABLE $tableName ALTER COLUMN $columnName $type"; + if (is_string($type)) { + if (preg_match('/^(DROP|SET|RESET|USING)\s+/i', $type) === 1) { + return "ALTER TABLE $tableName ALTER COLUMN $columnName $type"; + } + + $type = $this->schema->getColumnFactory()->fromDefinition($type); } - /** @psalm-suppress DeprecatedMethod */ - $type = 'TYPE ' . $this->queryBuilder->getColumnType($type); + // $type = 'TYPE ' . $this->queryBuilder->buildColumnDefinition($type); $multiAlterStatement = []; - $constraintPrefix = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column); + $constraintPrefix = preg_replace('/\W/', '', $table . '_' . $column); - if (preg_match('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', $type, $matches)) { - $type = preg_replace('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', '', $type); - $multiAlterStatement[] = "ALTER COLUMN $columnName SET DEFAULT $matches[1]"; + if ($type->hasDefaultValue()) { + $defaultValue = $this->queryBuilder->getColumnDefinitionBuilder()->buildDefaultValue($type); + $multiAlterStatement[] = "ALTER COLUMN $columnName SET DEFAULT $defaultValue"; } - $type = preg_replace('/\s+NOT\s+NULL/i', '', $type, -1, $count); - - if ($count > 0) { - $multiAlterStatement[] = "ALTER COLUMN $columnName SET NOT NULL"; - } else { +{ /** remove extra null if any */ $type = preg_replace('/\s+NULL/i', '', $type, -1, $count); if ($count > 0) { diff --git a/src/Schema.php b/src/Schema.php index f184ff79e..03542f6e8 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -107,6 +107,7 @@ final class Schema extends AbstractPdoSchema /** @deprecated Use {@see ColumnBuilder} instead. Will be removed in 2.0. */ public function createColumn(string $type, array|int|string $length = null): ColumnInterface { + /** @psalm-suppress DeprecatedClass */ return new Column($type, $length); } diff --git a/tests/ColumnSchemaBuilderTest.php b/tests/ColumnSchemaBuilderTest.php deleted file mode 100644 index 38c0d6acf..000000000 --- a/tests/ColumnSchemaBuilderTest.php +++ /dev/null @@ -1,26 +0,0 @@ - 5')"][0] = 'integer CHECK ("col_59" > 5)'; $values['unsigned()'][0] = 'integer'; $values['scale(2)'][0] = 'numeric(10,2)'; $values['integer(8)->scale(2)'][0] = 'integer'; diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 4566c2069..0f175a480 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -13,7 +13,8 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Expression\ExpressionInterface; -use Yiisoft\Db\Pgsql\Column; +use Yiisoft\Db\Pgsql\Column\ColumnBuilder; +use Yiisoft\Db\Pgsql\Tests\Provider\QueryBuilderProvider; use Yiisoft\Db\Pgsql\Tests\Support\TestTrait; use Yiisoft\Db\Query\Query; use Yiisoft\Db\Query\QueryInterface; @@ -33,6 +34,11 @@ final class QueryBuilderTest extends CommonQueryBuilderTest { use TestTrait; + public function getBuildColumnDefinitionProvider(): array + { + return QueryBuilderProvider::buildColumnDefinition(); + } + protected PdoConnectionInterface $db; /** @@ -100,7 +106,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->asString() + ColumnBuilder::string() ), ); @@ -125,7 +131,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->notNull()->asString() + ColumnBuilder::string()->notNull() ), ); @@ -136,7 +142,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->null()->asString() + ColumnBuilder::string()->notNull(false) ), ); @@ -147,7 +153,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->null()->defaultValue('xxx')->asString() + ColumnBuilder::string()->notNull(false)->defaultValue('xxx') ), ); @@ -158,7 +164,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->check('char_length(bar) > 5')->asString() + ColumnBuilder::string()->check('char_length(bar) > 5') ), ); @@ -169,7 +175,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->defaultValue('')->asString() + ColumnBuilder::string()->defaultValue('') ), ); @@ -180,20 +186,19 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 255))->defaultValue('AbCdE')->asString() + ColumnBuilder::string()->defaultValue('AbCdE') ), ); $this->assertSame( <<alterColumn( 'foo1', 'bar', - (new Column(ColumnType::TIMESTAMP)) - ->defaultExpression('CURRENT_TIMESTAMP') - ->asString() + ColumnBuilder::timestamp() + ->defaultValue(new Expression('CURRENT_TIMESTAMP')) ), ); @@ -204,7 +209,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 30))->unique()->asString() + ColumnBuilder::string(30)->unique() ), ); @@ -215,7 +220,7 @@ public function testAlterColumn(): void $qb->alterColumn( 'foo1', 'bar', - (new Column(ColumnType::STRING, 30))->unique() + ColumnBuilder::string(30)->unique() ), ); @@ -368,11 +373,11 @@ public function testCreateTable(): void $this->assertSame( <<createTable( From 83c9748dbaf104578c43be155586b9b64cfbd057 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Sun, 17 Nov 2024 12:07:46 +0700 Subject: [PATCH 2/7] Improve `testAlterColumn()` --- src/Column/ColumnDefinitionBuilder.php | 2 +- src/DDLQueryBuilder.php | 36 +++-- tests/Provider/QueryBuilderProvider.php | 31 +++++ tests/QueryBuilderTest.php | 168 +----------------------- 4 files changed, 51 insertions(+), 186 deletions(-) diff --git a/src/Column/ColumnDefinitionBuilder.php b/src/Column/ColumnDefinitionBuilder.php index 8532fa65b..2cffadba1 100644 --- a/src/Column/ColumnDefinitionBuilder.php +++ b/src/Column/ColumnDefinitionBuilder.php @@ -47,7 +47,7 @@ public function build(ColumnSchemaInterface $column): string . $this->buildExtra($column); } - protected function buildType(ColumnSchemaInterface $column): string + public function buildType(ColumnSchemaInterface $column): string { if ($column instanceof \Yiisoft\Db\Schema\Column\ArrayColumnSchema) { return $this->buildType($column->getColumn()) . str_repeat('[]', $column->getDimension()); diff --git a/src/DDLQueryBuilder.php b/src/DDLQueryBuilder.php index 4d495125c..5db94329f 100644 --- a/src/DDLQueryBuilder.php +++ b/src/DDLQueryBuilder.php @@ -46,38 +46,32 @@ public function alterColumn(string $table, string $column, ColumnInterface|Colum $type = $this->schema->getColumnFactory()->fromDefinition($type); } - // $type = 'TYPE ' . $this->queryBuilder->buildColumnDefinition($type); - $multiAlterStatement = []; - $constraintPrefix = preg_replace('/\W/', '', $table . '_' . $column); + $columnDefinitionBuilder = $this->queryBuilder->getColumnDefinitionBuilder(); + + $multiAlterStatement = ["ALTER COLUMN $columnName TYPE " . $columnDefinitionBuilder->buildType($type) . $columnDefinitionBuilder->buildExtra($type)]; if ($type->hasDefaultValue()) { - $defaultValue = $this->queryBuilder->getColumnDefinitionBuilder()->buildDefaultValue($type); + $defaultValue = $columnDefinitionBuilder->buildDefaultValue($type); $multiAlterStatement[] = "ALTER COLUMN $columnName SET DEFAULT $defaultValue"; } -{ - /** remove extra null if any */ - $type = preg_replace('/\s+NULL/i', '', $type, -1, $count); - if ($count > 0) { - $multiAlterStatement[] = "ALTER COLUMN $columnName DROP NOT NULL"; - } - } + match ($type->isNotNull()) { + true => $multiAlterStatement[] = "ALTER COLUMN $columnName SET NOT NULL", + false => $multiAlterStatement[] = "ALTER COLUMN $columnName DROP NOT NULL", + default => null, + }; - if (preg_match('/\s+CHECK\s+\((.+)\)/i', $type, $matches)) { - $type = preg_replace('/\s+CHECK\s+\((.+)\)/i', '', $type); - $multiAlterStatement[] = "ADD CONSTRAINT {$constraintPrefix}_check CHECK ($matches[1])"; + $check = $type->getCheck(); + if (!empty($check)) { + $constraintPrefix = preg_replace('/\W/', '', $table . '_' . $column); + $multiAlterStatement[] = "ADD CONSTRAINT {$constraintPrefix}_check CHECK ($check)"; } - $type = preg_replace('/\s+UNIQUE/i', '', $type, -1, $count); - - if ($count > 0) { + if ($type->isUnique()) { $multiAlterStatement[] = "ADD UNIQUE ($columnName)"; } - /** add what's left at the beginning */ - array_unshift($multiAlterStatement, "ALTER COLUMN $columnName $type"); - - return 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $multiAlterStatement); + return "ALTER TABLE $tableName " . implode(', ', $multiAlterStatement); } /** diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 7da99d388..b7b7b2938 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -23,6 +23,37 @@ final class QueryBuilderProvider extends \Yiisoft\Db\Tests\Provider\QueryBuilder protected static string $driverName = 'pgsql'; + public static function alterColumn(): array + { + return [ + ['SET NOT null', 'ALTER TABLE "foo1" ALTER COLUMN "bar" SET NOT null'], + ['drop default', 'ALTER TABLE "foo1" ALTER COLUMN "bar" drop default'], + ['reset xyz', 'ALTER TABLE "foo1" ALTER COLUMN "bar" reset xyz'], + ['string', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255)'], + ['varchar(255)', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255)'], + ['string NOT NULL', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET NOT NULL'], + ['string NULL', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" DROP NOT NULL'], + ['string DEFAULT NULL', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT NULL'], + ["string DEFAULT ''", 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT \'\''], + ["timestamp(0) DEFAULT now()", 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE timestamp(0), ALTER COLUMN "bar" SET DEFAULT now()'], + ['string CHECK (char_length(bar) > 5)', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ADD CONSTRAINT foo1_bar_check CHECK (char_length(bar) > 5)'], + ['string(30) UNIQUE', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(30), ADD UNIQUE ("bar")'], + ['varchar(255) USING bar::varchar', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255) USING bar::varchar'], + ['varchar(255) using cast("bar" as varchar)', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255) using cast("bar" as varchar)'], + [ColumnBuilder::string(), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255)'], + [ColumnBuilder::string()->notNull(), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET NOT NULL'], + [ColumnBuilder::string()->null(), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" DROP NOT NULL'], + [ColumnBuilder::string()->defaultValue(null), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT NULL'], + [ColumnBuilder::string()->defaultValue(''), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT \'\''], + [ColumnBuilder::string()->null()->defaultValue('xxx'), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT \'xxx\', ALTER COLUMN "bar" DROP NOT NULL'], + [ColumnBuilder::timestamp()->defaultValue(new Expression('now()')), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE timestamp(0), ALTER COLUMN "bar" SET DEFAULT now()'], + [ColumnBuilder::string()->check('char_length(bar) > 5'), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ADD CONSTRAINT foo1_bar_check CHECK (char_length(bar) > 5)'], + [ColumnBuilder::string(30)->unique(), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(30), ADD UNIQUE ("bar")'], + [ColumnBuilder::string()->extra('USING bar::varchar'), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255) USING bar::varchar'], + [ColumnBuilder::string()->extra('using cast("bar" as varchar)'), 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255) using cast("bar" as varchar)'], + ]; + } + public static function buildCondition(): array { $buildCondition = parent::buildCondition(); diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 0f175a480..f6ff9339b 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -4,8 +4,8 @@ namespace Yiisoft\Db\Pgsql\Tests; +use PHPUnit\Framework\Attributes\DataProviderExternal; use Throwable; -use Yiisoft\Db\Constant\ColumnType; use Yiisoft\Db\Driver\Pdo\PdoConnectionInterface; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\IntegrityException; @@ -61,170 +61,10 @@ public function testAddDefaultValue(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - */ - public function testAlterColumn(): void + #[DataProviderExternal(QueryBuilderProvider::class, 'alterColumn')] + public function testAlterColumn(string|ColumnSchemaInterface $type, string $expected): void { - $db = $this->getConnection(); - - $qb = $db->getQueryBuilder(); - - $this->assertSame( - <<alterColumn('foo1', 'bar', 'varchar(255)'), - ); - - $this->assertSame( - <<alterColumn('foo1', 'bar', 'SET NOT null'), - ); - - $this->assertSame( - <<alterColumn('foo1', 'bar', 'drop default'), - ); - - $this->assertSame( - <<alterColumn('foo1', 'bar', 'reset xyz'), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string() - ), - ); - - $this->assertSame( - <<alterColumn('foo1', 'bar', 'varchar(255) USING bar::varchar'), - ); - - $this->assertSame( - <<alterColumn('foo1', 'bar', 'varchar(255) using cast("bar" as varchar)'), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string()->notNull() - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string()->notNull(false) - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string()->notNull(false)->defaultValue('xxx') - ), - ); - - $this->assertSame( - << 5) - SQL, - $qb->alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string()->check('char_length(bar) > 5') - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string()->defaultValue('') - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string()->defaultValue('AbCdE') - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::timestamp() - ->defaultValue(new Expression('CURRENT_TIMESTAMP')) - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string(30)->unique() - ), - ); - - $this->assertSame( - <<alterColumn( - 'foo1', - 'bar', - ColumnBuilder::string(30)->unique() - ), - ); - - $db->close(); + parent::testAlterColumn($type, $expected); } /** From 4e0a82f4162e538b6b631002a36b99cddb00f8f5 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Wed, 20 Nov 2024 00:21:53 +0700 Subject: [PATCH 3/7] Add `QueryBuilder::prepareBinary()` method --- src/Column/ColumnDefinitionBuilder.php | 8 +++++++- src/DDLQueryBuilder.php | 13 +++++++++---- src/QueryBuilder.php | 7 +++++++ tests/Provider/QueryBuilderProvider.php | 19 +++++++++++++++++++ tests/QueryBuilderTest.php | 15 +++++++++++++-- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/Column/ColumnDefinitionBuilder.php b/src/Column/ColumnDefinitionBuilder.php index 2cffadba1..627faa773 100644 --- a/src/Column/ColumnDefinitionBuilder.php +++ b/src/Column/ColumnDefinitionBuilder.php @@ -47,7 +47,13 @@ public function build(ColumnSchemaInterface $column): string . $this->buildExtra($column); } - public function buildType(ColumnSchemaInterface $column): string + public function buildAlter(ColumnSchemaInterface $column): string + { + return $this->buildType($column) + . $this->buildExtra($column); + } + + protected function buildType(ColumnSchemaInterface $column): string { if ($column instanceof \Yiisoft\Db\Schema\Column\ArrayColumnSchema) { return $this->buildType($column->getColumn()) . str_repeat('[]', $column->getDimension()); diff --git a/src/DDLQueryBuilder.php b/src/DDLQueryBuilder.php index 5db94329f..090caa69c 100644 --- a/src/DDLQueryBuilder.php +++ b/src/DDLQueryBuilder.php @@ -8,10 +8,9 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\QueryBuilder\AbstractDDLQueryBuilder; use Yiisoft\Db\Schema\Builder\ColumnInterface; - use Yiisoft\Db\Schema\Column\ColumnSchemaInterface; + use function array_diff; -use function array_unshift; use function explode; use function implode; use function is_string; @@ -34,6 +33,10 @@ public function alterColumn(string $table, string $column, ColumnInterface|Colum $columnName = $this->quoter->quoteColumnName($column); $tableName = $this->quoter->quoteTableName($table); + if ($type instanceof ColumnInterface) { + $type = $type->asString(); + } + /** * @link https://github.com/yiisoft/yii2/issues/4492 * @link https://www.postgresql.org/docs/9.1/static/sql-altertable.html @@ -48,10 +51,12 @@ public function alterColumn(string $table, string $column, ColumnInterface|Colum $columnDefinitionBuilder = $this->queryBuilder->getColumnDefinitionBuilder(); - $multiAlterStatement = ["ALTER COLUMN $columnName TYPE " . $columnDefinitionBuilder->buildType($type) . $columnDefinitionBuilder->buildExtra($type)]; + $multiAlterStatement = ["ALTER COLUMN $columnName TYPE " . $columnDefinitionBuilder->buildAlter($type)]; if ($type->hasDefaultValue()) { - $defaultValue = $columnDefinitionBuilder->buildDefaultValue($type); + $defaultValue = $type->dbTypecast($type->getDefaultValue()); + $defaultValue = $this->queryBuilder->prepareValue($defaultValue); + $multiAlterStatement[] = "ALTER COLUMN $columnName SET DEFAULT $defaultValue"; } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index e667c9527..b46c021f9 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -11,6 +11,8 @@ use Yiisoft\Db\Schema\QuoterInterface; use Yiisoft\Db\Schema\SchemaInterface; +use function bin2hex; + /** * Implements the PostgreSQL Server specific query builder. */ @@ -57,4 +59,9 @@ public function __construct(QuoterInterface $quoter, SchemaInterface $schema) parent::__construct($quoter, $schema, $ddlBuilder, $dmlBuilder, $dqlBuilder, $columnDefinitionBuilder); } + + protected function prepareBinary(string $binary): string + { + return "'\x" . bin2hex($binary) . "'::bytea"; + } } diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index b7b7b2938..6f81335cb 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -624,4 +624,23 @@ public static function buildColumnDefinition(): array return $values; } + + public static function prepareParam(): array + { + $values = parent::prepareParam(); + + $values['binary'][0] = "'\\x737472696e67'::bytea"; + + return $values; + } + + public static function prepareValue(): array + { + $values = parent::prepareValue(); + + $values['binary'][0] = "'\\x737472696e67'::bytea"; + $values['paramBinary'][0] = "'\\x737472696e67'::bytea"; + + return $values; + } } diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index f6ff9339b..9da1f1544 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -13,7 +13,6 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Expression\ExpressionInterface; -use Yiisoft\Db\Pgsql\Column\ColumnBuilder; use Yiisoft\Db\Pgsql\Tests\Provider\QueryBuilderProvider; use Yiisoft\Db\Pgsql\Tests\Support\TestTrait; use Yiisoft\Db\Query\Query; @@ -632,9 +631,21 @@ public function testOverlapsConditionOperator(iterable|ExpressionInterface $valu $db->close(); } - /** @dataProvider \Yiisoft\Db\Pgsql\Tests\Provider\QueryBuilderProvider::buildColumnDefinition() */ + #[DataProviderExternal(QueryBuilderProvider::class, 'buildColumnDefinition')] public function testBuildColumnDefinition(string $expected, ColumnSchemaInterface|string $column): void { parent::testBuildColumnDefinition($expected, $column); } + + #[DataProviderExternal(QueryBuilderProvider::class, 'prepareParam')] + public function testPrepareParam(string $expected, mixed $value, int $type): void + { + parent::testPrepareParam($expected, $value, $type); + } + + #[DataProviderExternal(QueryBuilderProvider::class, 'prepareValue')] + public function testPrepareValue(string $expected, mixed $value): void + { + parent::testPrepareValue($expected, $value); + } } From 0166a6437dac0747267f4c38412dfa5ad235f3c5 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 1 Dec 2024 02:54:42 +0000 Subject: [PATCH 4/7] Apply fixes from StyleCI --- tests/Provider/QueryBuilderProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 6f81335cb..078ffb8c4 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -35,7 +35,7 @@ public static function alterColumn(): array ['string NULL', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" DROP NOT NULL'], ['string DEFAULT NULL', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT NULL'], ["string DEFAULT ''", 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ALTER COLUMN "bar" SET DEFAULT \'\''], - ["timestamp(0) DEFAULT now()", 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE timestamp(0), ALTER COLUMN "bar" SET DEFAULT now()'], + ['timestamp(0) DEFAULT now()', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE timestamp(0), ALTER COLUMN "bar" SET DEFAULT now()'], ['string CHECK (char_length(bar) > 5)', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255), ADD CONSTRAINT foo1_bar_check CHECK (char_length(bar) > 5)'], ['string(30) UNIQUE', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(30), ADD UNIQUE ("bar")'], ['varchar(255) USING bar::varchar', 'ALTER TABLE "foo1" ALTER COLUMN "bar" TYPE varchar(255) USING bar::varchar'], From 33c11f22253dd87d9b01b8ffb211ae8868d43891 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Wed, 11 Dec 2024 17:32:06 +0700 Subject: [PATCH 5/7] Add `ColumnDefinitionBuilder::getDefaultUuidExpression()` method --- src/Column/ColumnDefinitionBuilder.php | 16 ++++++++++++++-- tests/Provider/QueryBuilderProvider.php | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Column/ColumnDefinitionBuilder.php b/src/Column/ColumnDefinitionBuilder.php index 627faa773..f0ba4a2b4 100644 --- a/src/Column/ColumnDefinitionBuilder.php +++ b/src/Column/ColumnDefinitionBuilder.php @@ -8,10 +8,10 @@ use Yiisoft\Db\QueryBuilder\AbstractColumnDefinitionBuilder; use Yiisoft\Db\Schema\Column\ColumnSchemaInterface; +use function version_compare; + final class ColumnDefinitionBuilder extends AbstractColumnDefinitionBuilder { - protected const GENERATE_UUID_EXPRESSION = 'gen_random_uuid()'; - protected const TYPES_WITH_SIZE = [ 'bit', 'bit varying', @@ -90,4 +90,16 @@ protected function getDbType(ColumnSchemaInterface $column): string default => 'varchar', }; } + + protected function getDefaultUuidExpression(): string + { + $serverVersion = $this->queryBuilder->getServerInfo()->getVersion(); + + if (version_compare($serverVersion, '12', '<')) { + return "uuid_in(overlay(overlay(md5(now()::text || random()::text) placing '4' from 13) placing" + . ' to_hex(floor(4 * random() + 8)::int)::text from 17)::cstring)'; + } + + return 'gen_random_uuid()'; + } } diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 078ffb8c4..ff9d89041 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -16,6 +16,7 @@ use Yiisoft\Db\Tests\Support\TraversableObject; use function array_replace; +use function version_compare; final class QueryBuilderProvider extends \Yiisoft\Db\Tests\Provider\QueryBuilderProvider { @@ -622,6 +623,19 @@ public static function buildColumnDefinition(): array $values['scale(2)'][0] = 'numeric(10,2)'; $values['integer(8)->scale(2)'][0] = 'integer'; + $db = self::getDb(); + $serverVersion = self::getDb()->getServerInfo()->getVersion(); + $db->close(); + + if (version_compare($serverVersion, '12', '<')) { + $uuidExpression = "uuid_in(overlay(overlay(md5(now()::text || random()::text) placing '4' from 13) placing" + . ' to_hex(floor(4 * random() + 8)::int)::text from 17)::cstring)'; + + $values[PseudoType::UUID_PK][0] = "uuid PRIMARY KEY DEFAULT $uuidExpression"; + $values[PseudoType::UUID_PK_SEQ][0] = "uuid PRIMARY KEY DEFAULT $uuidExpression"; + $values['uuidPrimaryKey()'][0] = "uuid PRIMARY KEY DEFAULT $uuidExpression"; + } + return $values; } From 1c743a69b700a43890d2e97ec6c0e9ed591ff01e Mon Sep 17 00:00:00 2001 From: Tigrov Date: Thu, 12 Dec 2024 09:13:16 +0700 Subject: [PATCH 6/7] Fix version for uuid expression --- src/Column/ColumnDefinitionBuilder.php | 2 +- tests/Provider/QueryBuilderProvider.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Column/ColumnDefinitionBuilder.php b/src/Column/ColumnDefinitionBuilder.php index f0ba4a2b4..64419845d 100644 --- a/src/Column/ColumnDefinitionBuilder.php +++ b/src/Column/ColumnDefinitionBuilder.php @@ -95,7 +95,7 @@ protected function getDefaultUuidExpression(): string { $serverVersion = $this->queryBuilder->getServerInfo()->getVersion(); - if (version_compare($serverVersion, '12', '<')) { + if (version_compare($serverVersion, '13', '<')) { return "uuid_in(overlay(overlay(md5(now()::text || random()::text) placing '4' from 13) placing" . ' to_hex(floor(4 * random() + 8)::int)::text from 17)::cstring)'; } diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index ff9d89041..f0dc4d1fc 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -627,7 +627,7 @@ public static function buildColumnDefinition(): array $serverVersion = self::getDb()->getServerInfo()->getVersion(); $db->close(); - if (version_compare($serverVersion, '12', '<')) { + if (version_compare($serverVersion, '13', '<')) { $uuidExpression = "uuid_in(overlay(overlay(md5(now()::text || random()::text) placing '4' from 13) placing" . ' to_hex(floor(4 * random() + 8)::int)::text from 17)::cstring)'; From a7d354b9da730102e7c283e5a969c49c4af19144 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Fri, 13 Dec 2024 10:26:50 +0700 Subject: [PATCH 7/7] Add line to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1730fb3b4..27eb497a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Enh #370: Refactor `Schema::normalizeDefaultValue()` method and move it to `ColumnFactory` class (@Tigrov) - New #373: Override `QueryBuilder::prepareBinary()` method (@Tigrov) - Chg #375: Update `QueryBuilder` constructor (@Tigrov) +- Enh #374: Use `ColumnDefinitionBuilder` to generate table column SQL representation (@Tigrov) ## 1.3.0 March 21, 2024