diff --git a/CHANGELOG.md b/CHANGELOG.md index b8490e25c..3aec7e22a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ - Enh #420: Provide `yiisoft/db-implementation` virtual package (@vjik) - Enh #424, #425, #428: Adapt to conditions refactoring in `yiisoft/db` package (@vjik) - Enh #431: Remove `TableSchema` class and refactor `Schema` class (@Tigrov) +- Enh #433: Support column's collation (@Tigrov) ## 1.3.0 March 21, 2024 diff --git a/src/Column/ColumnDefinitionBuilder.php b/src/Column/ColumnDefinitionBuilder.php index 43df8c576..7d2693f40 100644 --- a/src/Column/ColumnDefinitionBuilder.php +++ b/src/Column/ColumnDefinitionBuilder.php @@ -7,6 +7,7 @@ use Yiisoft\Db\Constant\ColumnType; use Yiisoft\Db\QueryBuilder\AbstractColumnDefinitionBuilder; use Yiisoft\Db\Schema\Column\AbstractArrayColumn; +use Yiisoft\Db\Schema\Column\CollatableColumnInterface; use Yiisoft\Db\Schema\Column\ColumnInterface; use function str_repeat; @@ -45,6 +46,7 @@ public function build(ColumnInterface $column): string . $this->buildUnique($column) . $this->buildDefault($column) . $this->buildCheck($column) + . $this->buildCollate($column) . $this->buildReferences($column) . $this->buildExtra($column); } @@ -74,6 +76,16 @@ public function buildType(ColumnInterface $column): string return parent::buildType($column); } + protected function buildCollate(ColumnInterface $column): string + { + if (!$column instanceof CollatableColumnInterface || empty($column->getCollation())) { + return ''; + } + + /** @psalm-suppress PossiblyNullArgument */ + return ' COLLATE ' . $this->queryBuilder->getQuoter()->quoteColumnName($column->getCollation()); + } + protected function getDbType(ColumnInterface $column): string { $dbType = $column->getDbType(); diff --git a/src/Column/ColumnFactory.php b/src/Column/ColumnFactory.php index 3cb928183..d474e897e 100644 --- a/src/Column/ColumnFactory.php +++ b/src/Column/ColumnFactory.php @@ -20,6 +20,7 @@ * @psalm-type ColumnInfo = array{ * auto_increment?: bool|string, * check?: string|null, + * collation?: string|null, * column?: ColumnInterface, * columns?: array, * comment?: string|null, diff --git a/src/Schema.php b/src/Schema.php index 210d97855..25a5b5c7f 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -48,6 +48,8 @@ * scale: int|string|null, * contype: string|null, * dimension: int|string, + * collation: string|null, + * collation_schema: string, * schema: string, * table: string * } @@ -308,7 +310,9 @@ protected function findColumns(TableSchemaInterface $table): bool a.atttypmod ) AS scale, ct.contype, - COALESCE(NULLIF(a.attndims, 0), NULLIF(t.typndims, 0), (t.typcategory='A')::int) AS dimension + COALESCE(NULLIF(a.attndims, 0), NULLIF(t.typndims, 0), (t.typcategory='A')::int) AS dimension, + co.collname AS collation, + nco.nspname AS collation_schema FROM pg_class c LEFT JOIN pg_attribute a ON a.attrelid = c.oid @@ -328,6 +332,8 @@ protected function findColumns(TableSchemaInterface $table): bool || ct.conrelid || ' :resorigcol (?:' || replace(substr(ct.conkey::text, 2, length(ct.conkey::text) - 2), ',', '|') || ') .*') ) + LEFT JOIN (pg_collation co JOIN pg_namespace nco ON co.collnamespace = nco.oid) + ON a.attcollation = co.oid AND (nco.nspname != 'pg_catalog' OR co.collname != 'default') WHERE a.attnum > 0 AND t.typname != '' AND NOT a.attisdropped AND c.relname = :tableName @@ -497,9 +503,13 @@ private function loadColumn(array $info): ColumnInterface { $columnFactory = $this->db->getColumnFactory(); $dbType = $this->resolveFullName($info['data_type'], $info['type_scheme']); + $collation = !empty($info['collation']) + ? $this->resolveFullName($info['collation'], $info['collation_schema']) + : null; $columnInfo = [ 'autoIncrement' => (bool) $info['is_autoinc'], + 'collation' => $collation, 'comment' => $info['column_comment'], 'dbType' => $dbType, 'enumValues' => $info['enum_values'] !== null diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index 16b911f5d..ff6e7fd51 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -444,6 +444,10 @@ public static function buildColumnDefinition(): array $values['unsigned()'][0] = 'integer'; $values['scale(2)'][0] = 'numeric(10,2)'; $values['integer(8)->scale(2)'][0] = 'integer'; + $values["collation('collation_name')"] = [ + 'varchar(255) COLLATE "C"', + ColumnBuilder::string()->collation('C'), + ]; $db = self::getDb(); $serverVersion = self::getDb()->getServerInfo()->getVersion(); diff --git a/tests/Provider/SchemaProvider.php b/tests/Provider/SchemaProvider.php index a663369f1..633721d9c 100644 --- a/tests/Provider/SchemaProvider.php +++ b/tests/Provider/SchemaProvider.php @@ -58,6 +58,7 @@ public static function columns(): array dbType: 'varchar', size: 100, defaultValue: 'some\'thing', + collation: 'C', ), 'char_col3' => new StringColumn( ColumnType::TEXT, diff --git a/tests/Support/Fixture/pgsql.sql b/tests/Support/Fixture/pgsql.sql index 5bb5cc4bb..ebe84e1be 100644 --- a/tests/Support/Fixture/pgsql.sql +++ b/tests/Support/Fixture/pgsql.sql @@ -145,7 +145,7 @@ CREATE TABLE "type" ( tinyint_col smallint DEFAULT '1', smallint_col smallint DEFAULT '1', char_col char(100) NOT NULL, - char_col2 varchar(100) DEFAULT 'some''thing', + char_col2 varchar(100) DEFAULT 'some''thing' COLLATE "C", char_col3 text, char_col4 character varying DEFAULT E'first line\nsecond line', float_col double precision NOT NULL,