Browse Source

Merge pull request #8978 from chinpei215/per-column-collation

Add support for per-column collation
Mark Story 9 years ago
parent
commit
64ca29f6d3

+ 5 - 0
src/Database/Schema/MysqlSchema.php

@@ -361,6 +361,11 @@ class MysqlSchema extends BaseSchema
             $out .= ' UNSIGNED';
         }
 
+        $hasCollate = ['text', 'string'];
+        if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') {
+            $out .= ' COLLATE ' . $data['collate'];
+        }
+
         if (isset($data['null']) && $data['null'] === false) {
             $out .= ' NOT NULL';
         }

+ 7 - 0
src/Database/Schema/PostgresSchema.php

@@ -42,6 +42,7 @@ class PostgresSchema extends BaseSchema
             data_type AS type,
             is_nullable AS null, column_default AS default,
             character_maximum_length AS char_length,
+            c.collation_name,
             d.description as comment,
             ordinal_position,
             pg_get_serial_sequence(attr.attrelid::regclass::text, attr.attname) IS NOT NULL AS has_serial
@@ -153,6 +154,7 @@ class PostgresSchema extends BaseSchema
         $field += [
             'default' => $this->_defaultValue($row['default']),
             'null' => $row['null'] === 'YES' ? true : false,
+            'collate' => $row['collation_name'],
             'comment' => $row['comment']
         ];
         $field['length'] = $row['char_length'] ?: $field['length'];
@@ -370,6 +372,11 @@ class PostgresSchema extends BaseSchema
             }
         }
 
+        $hasCollate = ['text', 'string'];
+        if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') {
+            $out .= ' COLLATE "' . $data['collate'] . '"';
+        }
+
         if ($data['type'] === 'float' && isset($data['precision'])) {
             $out .= '(' . (int)$data['precision'] . ')';
         }

+ 8 - 1
src/Database/Schema/SqlserverSchema.php

@@ -50,7 +50,8 @@ class SqlserverSchema extends BaseSchema
             AC.scale AS [scale],
             AC.is_identity AS [autoincrement],
             AC.is_nullable AS [null],
-            OBJECT_DEFINITION(AC.default_object_id) AS [default]
+            OBJECT_DEFINITION(AC.default_object_id) AS [default],
+            AC.collation_name AS [collation_name]
             FROM sys.[objects] T
             INNER JOIN sys.[schemas] S ON S.[schema_id] = T.[schema_id]
             INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
@@ -162,6 +163,7 @@ class SqlserverSchema extends BaseSchema
         $field += [
             'null' => $row['null'] === '1' ? true : false,
             'default' => $this->_defaultValue($row['default']),
+            'collate' => $row['collation_name'],
         ];
         $table->addColumn($row['name'], $field);
     }
@@ -373,6 +375,11 @@ class SqlserverSchema extends BaseSchema
             $out .= sprintf('%s(%d)', $type, $data['length']);
         }
 
+        $hasCollate = ['text', 'string'];
+        if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') {
+            $out .= ' COLLATE ' . $data['collate'];
+        }
+
         if ($data['type'] === 'float' && isset($data['precision'])) {
             $out .= '(' . (int)$data['precision'] . ')';
         }

+ 4 - 0
src/Database/Schema/Table.php

@@ -137,6 +137,10 @@ class Table
     protected static $_columnExtras = [
         'string' => [
             'fixed' => null,
+            'collate' => null,
+        ],
+        'text' => [
+            'collate' => null,
         ],
         'integer' => [
             'unsigned' => null,

+ 22 - 2
tests/TestCase/Database/Schema/MysqlSchemaTest.php

@@ -181,13 +181,13 @@ class MysqlSchemaTest extends TestCase
             'Type' => $type,
             'Null' => 'YES',
             'Default' => 'Default value',
-            'Collation' => 'Collate information',
+            'Collation' => 'utf8_general_ci',
             'Comment' => 'Comment section',
         ];
         $expected += [
             'null' => true,
             'default' => 'Default value',
-            'collate' => 'Collate information',
+            'collate' => 'utf8_general_ci',
             'comment' => 'Comment section',
         ];
 
@@ -291,6 +291,7 @@ SQL;
                 'precision' => null,
                 'comment' => 'A title',
                 'fixed' => null,
+                'collate' => 'utf8_general_ci',
             ],
             'body' => [
                 'type' => 'text',
@@ -299,6 +300,7 @@ SQL;
                 'length' => null,
                 'precision' => null,
                 'comment' => null,
+                'collate' => 'utf8_general_ci',
             ],
             'author_id' => [
                 'type' => 'integer',
@@ -481,6 +483,11 @@ SQL;
                 ['type' => 'uuid'],
                 '`id` CHAR(36)'
             ],
+            [
+                'title',
+                ['type' => 'string', 'length' => 255, 'null' => false, 'collate' => 'utf8_unicode_ci'],
+                '`title` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL'
+            ],
             // Text
             [
                 'body',
@@ -502,6 +509,11 @@ SQL;
                 ['type' => 'text', 'length' => Table::LENGTH_LONG, 'null' => false],
                 '`body` LONGTEXT NOT NULL'
             ],
+            [
+                'body',
+                ['type' => 'text', 'null' => false, 'collate' => 'utf8_unicode_ci'],
+                '`body` TEXT COLLATE utf8_unicode_ci NOT NULL'
+            ],
             // Blob / binary
             [
                 'body',
@@ -933,6 +945,13 @@ SQL;
                 'type' => 'text',
                 'comment' => ''
             ])
+            ->addColumn('hash', [
+                'type' => 'string',
+                'fixed' => true,
+                'length' => 40,
+                'collate' => 'latin1_bin',
+                'null' => false,
+            ])
             ->addColumn('created', 'datetime')
             ->addConstraint('primary', [
                 'type' => 'primary',
@@ -949,6 +968,7 @@ CREATE TABLE `posts` (
 `id` INTEGER NOT NULL AUTO_INCREMENT,
 `title` VARCHAR(255) NOT NULL COMMENT 'The title',
 `body` TEXT,
+`hash` CHAR(40) COLLATE latin1_bin NOT NULL,
 `created` DATETIME,
 PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci

+ 23 - 0
tests/TestCase/Database/Schema/PostgresSchemaTest.php

@@ -217,11 +217,13 @@ SQL;
             'default' => 'Default value',
             'comment' => 'Comment section',
             'char_length' => null,
+            'collation_name' => 'ja_JP.utf8',
         ];
         $expected += [
             'null' => true,
             'default' => 'Default value',
             'comment' => 'Comment section',
+            'collate' => 'ja_JP.utf8',
         ];
 
         $driver = $this->getMockBuilder('Cake\Database\Driver\Postgres')->getMock();
@@ -299,6 +301,7 @@ SQL;
                 'precision' => null,
                 'comment' => 'a title',
                 'fixed' => null,
+                'collate' => null,
             ],
             'body' => [
                 'type' => 'text',
@@ -307,6 +310,7 @@ SQL;
                 'length' => null,
                 'precision' => null,
                 'comment' => null,
+                'collate' => null,
             ],
             'author_id' => [
                 'type' => 'integer',
@@ -417,6 +421,7 @@ SQL;
                 'precision' => null,
                 'comment' => null,
                 'fixed' => null,
+                'collate' => null,
             ],
             'bio' => [
                 'type' => 'date',
@@ -623,6 +628,11 @@ SQL;
                 ['type' => 'string'],
                 '"title" VARCHAR'
             ],
+            [
+                'title',
+                ['type' => 'string', 'length' => 255, 'null' => false, 'collate' => 'C'],
+                '"title" VARCHAR(255) COLLATE "C" NOT NULL'
+            ],
             // Text
             [
                 'body',
@@ -644,6 +654,11 @@ SQL;
                 ['type' => 'text', 'length' => Table::LENGTH_LONG, 'null' => false],
                 '"body" TEXT NOT NULL'
             ],
+            [
+                'body',
+                ['type' => 'text', 'null' => false, 'collate' => 'C'],
+                '"body" TEXT COLLATE "C" NOT NULL'
+            ],
             // Integers
             [
                 'post_id',
@@ -981,6 +996,13 @@ SQL;
                 'comment' => 'This is the title',
             ])
             ->addColumn('body', ['type' => 'text'])
+            ->addColumn('hash', [
+                'type' => 'string',
+                'fixed' => true,
+                'length' => 40,
+                'collate' => 'C',
+                'null' => false,
+            ])
             ->addColumn('created', 'datetime')
             ->addConstraint('primary', [
                 'type' => 'primary',
@@ -996,6 +1018,7 @@ CREATE TABLE "schema_articles" (
 "id" SERIAL,
 "title" VARCHAR NOT NULL,
 "body" TEXT,
+"hash" CHAR(40) COLLATE "C" NOT NULL,
 "created" TIMESTAMP,
 PRIMARY KEY ("id")
 )

+ 4 - 0
tests/TestCase/Database/Schema/SqliteSchemaTest.php

@@ -296,6 +296,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => null,
             ],
             'body' => [
                 'type' => 'text',
@@ -304,6 +305,7 @@ SQL;
                 'length' => null,
                 'precision' => null,
                 'comment' => null,
+                'collate' => null,
             ],
             'author_id' => [
                 'type' => 'integer',
@@ -339,6 +341,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => null,
             ],
             'field2' => [
                 'type' => 'string',
@@ -348,6 +351,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => null,
             ],
         ];
         $this->assertInstanceOf('Cake\Database\Schema\Table', $result);

+ 29 - 4
tests/TestCase/Database/Schema/SqlserverSchemaTest.php

@@ -61,7 +61,7 @@ SQL;
         $table = <<<SQL
 CREATE TABLE schema_articles (
 id BIGINT PRIMARY KEY,
-title VARCHAR(20),
+title VARCHAR(20) COLLATE Japanese_Unicode_CI_AI,
 body VARCHAR(1000),
 author_id INTEGER NOT NULL,
 published BIT DEFAULT 0,
@@ -237,11 +237,13 @@ SQL;
             'default' => 'Default value',
             'char_length' => $length,
             'precision' => $precision,
-            'scale' => $scale
+            'scale' => $scale,
+            'collation_name' => 'Japanese_Unicode_CI_AI',
         ];
         $expected += [
             'null' => true,
             'default' => 'Default value',
+            'collate' => 'Japanese_Unicode_CI_AI',
         ];
 
         $driver = $this->getMockBuilder('Cake\Database\Driver\Sqlserver')->getMock();
@@ -303,6 +305,7 @@ SQL;
                 'precision' => null,
                 'comment' => null,
                 'fixed' => null,
+                'collate' => 'Japanese_Unicode_CI_AI',
             ],
             'body' => [
                 'type' => 'string',
@@ -312,6 +315,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => 'SQL_Latin1_General_CP1_CI_AS',
             ],
             'author_id' => [
                 'type' => 'integer',
@@ -357,6 +361,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => 'SQL_Latin1_General_CP1_CI_AS',
             ],
             'field2' => [
                 'type' => 'string',
@@ -366,6 +371,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => 'SQL_Latin1_General_CP1_CI_AS',
             ],
             'field3' => [
                 'type' => 'string',
@@ -375,6 +381,7 @@ SQL;
                 'precision' => null,
                 'fixed' => null,
                 'comment' => null,
+                'collate' => 'SQL_Latin1_General_CP1_CI_AS',
             ],
         ];
         $this->assertEquals(['id'], $result->primaryKey());
@@ -505,13 +512,18 @@ SQL;
             [
                 'role',
                 ['type' => 'string', 'length' => 10, 'null' => false, 'default' => 'admin'],
-                "[role] NVARCHAR(10) NOT NULL DEFAULT [admin]"
+                "[role] NVARCHAR(10) NOT NULL DEFAULT 'admin'"
             ],
             [
                 'title',
                 ['type' => 'string'],
                 '[title] NVARCHAR(255)'
             ],
+            [
+                'title',
+                ['type' => 'string', 'length' => 25, 'null' => false, 'collate' => 'Japanese_Unicode_CI_AI'],
+                '[title] NVARCHAR(25) COLLATE Japanese_Unicode_CI_AI NOT NULL'
+            ],
             // Text
             [
                 'body',
@@ -533,6 +545,11 @@ SQL;
                 ['type' => 'text', 'length' => Table::LENGTH_LONG, 'null' => false],
                 '[body] NVARCHAR(MAX) NOT NULL'
             ],
+            [
+                'body',
+                ['type' => 'text', 'null' => false, 'collate' => 'Japanese_Unicode_CI_AI'],
+                '[body] NVARCHAR(MAX) COLLATE Japanese_Unicode_CI_AI NOT NULL'
+            ],
             // Integers
             [
                 'post_id',
@@ -840,6 +857,13 @@ SQL;
                 'null' => false,
             ])
             ->addColumn('body', ['type' => 'text'])
+            ->addColumn('hash', [
+                'type' => 'string',
+                'fixed' => true,
+                'length' => 40,
+                'collate' => 'Latin1_General_BIN',
+                'null' => false,
+            ])
             ->addColumn('created', 'datetime')
             ->addConstraint('primary', [
                 'type' => 'primary',
@@ -855,6 +879,7 @@ CREATE TABLE [schema_articles] (
 [id] INTEGER IDENTITY(1, 1),
 [title] NVARCHAR(255) NOT NULL,
 [body] NVARCHAR(MAX),
+[hash] NCHAR(40) COLLATE Latin1_General_BIN NOT NULL,
 [created] DATETIME,
 PRIMARY KEY ([id])
 )
@@ -929,7 +954,7 @@ SQL;
         $mock->expects($this->any())
             ->method('quote')
             ->will($this->returnCallback(function ($value) {
-                return '[' . $value . ']';
+                return "'$value'";
             }));
         $driver->connection($mock);
         return $driver;

+ 1 - 0
tests/TestCase/Database/Schema/TableTest.php

@@ -217,6 +217,7 @@ class TableTest extends TestCase
             'null' => null,
             'fixed' => null,
             'comment' => null,
+            'collate' => null,
         ];
         $this->assertEquals($expected, $result);