From 9367af4b1523741bba1ebb3bd5788c835fd49f1e Mon Sep 17 00:00:00 2001 From: DarkDef Date: Sun, 25 Apr 2021 20:19:14 +0300 Subject: [PATCH 01/13] Fix for issue #18604 --- framework/db/mssql/QueryBuilder.php | 5 +++-- tests/framework/db/mssql/QueryBuilderTest.php | 32 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index df39971..66e169c 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -184,9 +184,10 @@ class QueryBuilder extends \yii\db\QueryBuilder $type = $this->getColumnType($type); if (preg_match('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', $type, $matches)) { + $value = strtolower($matches[1]) === 'null' ? null : $matches[1]; $type = preg_replace('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', '', $type); $sqlAfter[] = $this->dropConstraintsForColumn($table, $column, 'D'); - $sqlAfter[] = $this->addDefaultValue("DF_{$constraintBase}", $table, $column, $matches[1]); + $sqlAfter[] = $this->addDefaultValue("DF_{$constraintBase}", $table, $column, $value); } else { $sqlAfter[] = $this->dropConstraintsForColumn($table, $column, 'D'); } @@ -213,7 +214,7 @@ class QueryBuilder extends \yii\db\QueryBuilder public function addDefaultValue($name, $table, $column, $value) { return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT ' - . $this->db->quoteColumnName($name) . ' DEFAULT ' . $this->db->quoteValue($value) . ' FOR ' + . $this->db->quoteColumnName($name) . ' DEFAULT ' . ($value === null ? 'NULL' : $this->db->quoteValue($value)) . ' FOR ' . $this->db->quoteColumnName($column); } diff --git a/tests/framework/db/mssql/QueryBuilderTest.php b/tests/framework/db/mssql/QueryBuilderTest.php index 624ca72..df16edc 100644 --- a/tests/framework/db/mssql/QueryBuilderTest.php +++ b/tests/framework/db/mssql/QueryBuilderTest.php @@ -656,6 +656,38 @@ ALTER TABLE [foo1] ADD CONSTRAINT [UQ_foo1_bar] UNIQUE ([bar])"; $this->assertEquals(false, $schema->getColumn('bar')->allowNull); } + public function testAlterColumnWithNull() + { + $qb = $this->getQueryBuilder(); + + $expected = "ALTER TABLE [foo1] ALTER COLUMN [bar] int NULL +DECLARE @tableName VARCHAR(MAX) = '[foo1]' +DECLARE @columnName VARCHAR(MAX) = 'bar' + +WHILE 1=1 BEGIN + DECLARE @constraintName NVARCHAR(128) + SET @constraintName = (SELECT TOP 1 OBJECT_NAME(cons.[object_id]) + FROM ( + SELECT sc.[constid] object_id + FROM [sys].[sysconstraints] sc + JOIN [sys].[columns] c ON c.[object_id]=sc.[id] AND c.[column_id]=sc.[colid] AND c.[name]=@columnName + WHERE sc.[id] = OBJECT_ID(@tableName) + UNION + SELECT object_id(i.[name]) FROM [sys].[indexes] i + JOIN [sys].[columns] c ON c.[object_id]=i.[object_id] AND c.[name]=@columnName + JOIN [sys].[index_columns] ic ON ic.[object_id]=i.[object_id] AND i.[index_id]=ic.[index_id] AND c.[column_id]=ic.[column_id] + WHERE i.[is_unique_constraint]=1 and i.[object_id]=OBJECT_ID(@tableName) + ) cons + JOIN [sys].[objects] so ON so.[object_id]=cons.[object_id] + WHERE so.[type]='D') + IF @constraintName IS NULL BREAK + EXEC (N'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT [' + @constraintName + ']') +END +ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT NULL FOR [bar]"; + $sql = $qb->alterColumn('foo1', 'bar', $this->integer()->null()->defaultValue(NULL)); + $this->assertEquals($expected, $sql); + } + public function testAlterColumnWithCheckConstraintOnDb() { $connection = $this->getConnection(); From 4fba562cd0775183fe1c890b0c23df5475416c33 Mon Sep 17 00:00:00 2001 From: DarkDef Date: Mon, 26 Apr 2021 19:24:14 +0300 Subject: [PATCH 02/13] New attemp --- framework/db/ColumnSchemaBuilder.php | 37 ++++++++----- framework/db/mssql/ColumnSchemaBuilder.php | 77 +++++++++++++++++++++++++++ framework/db/mssql/QueryBuilder.php | 41 +++++++------- framework/db/mssql/Schema.php | 8 +++ tests/framework/db/mssql/QueryBuilderTest.php | 38 +++++++++++-- 5 files changed, 162 insertions(+), 39 deletions(-) create mode 100644 framework/db/mssql/ColumnSchemaBuilder.php diff --git a/framework/db/ColumnSchemaBuilder.php b/framework/db/ColumnSchemaBuilder.php index edb28ef..cb0e849 100644 --- a/framework/db/ColumnSchemaBuilder.php +++ b/framework/db/ColumnSchemaBuilder.php @@ -331,35 +331,46 @@ class ColumnSchemaBuilder extends BaseObject } /** - * Builds the default value specification for the column. - * @return string string with default value of column. + * Return the default value for the column. + * @return string|null string with default value of column. */ - protected function buildDefaultString() + protected function buildDefaultValue() { if ($this->default === null) { - return $this->isNotNull === false ? ' DEFAULT NULL' : ''; + return $this->isNotNull === false ? 'NULL' : null; } - $string = ' DEFAULT '; switch (gettype($this->default)) { - case 'integer': - $string .= (string) $this->default; - break; case 'double': // ensure type cast always has . as decimal separator in all locales - $string .= StringHelper::floatToString($this->default); + $defaultValue = StringHelper::floatToString($this->default); break; case 'boolean': - $string .= $this->default ? 'TRUE' : 'FALSE'; + $defaultValue = $this->default ? 'TRUE' : 'FALSE'; break; + case 'integer': case 'object': - $string .= (string) $this->default; + $defaultValue = (string) $this->default; break; default: - $string .= "'{$this->default}'"; + $defaultValue = "'{$this->default}'"; + } + + return $defaultValue; + } + + /** + * Builds the default value specification for the column. + * @return string string with default value of column. + */ + protected function buildDefaultString() + { + $defaultValue = $this->buildDefaultValue(); + if ($defaultValue === null) { + return ''; } - return $string; + return ' DEFAULT ' . $defaultValue; } /** diff --git a/framework/db/mssql/ColumnSchemaBuilder.php b/framework/db/mssql/ColumnSchemaBuilder.php new file mode 100644 index 0000000..c6f8f54 --- /dev/null +++ b/framework/db/mssql/ColumnSchemaBuilder.php @@ -0,0 +1,77 @@ + + * @since 2.0.8 + */ +class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder +{ + protected $format = '{type}{length}{notnull}{unique}{default}{check}{append}'; + + /** + * Builds the full string for the column's schema. + * @return string + */ + public function __toString() + { + switch ($this->getTypeCategory()) { + case self::CATEGORY_PK: + $format = '{type}{check}{comment}{append}'; + break; + default: + $format = $this->format; + } + + return $this->buildCompleteString($format); + } + + /** + * Changes default format string for MSSQL ALTER COMMAND + */ + public function isAlterColumn() + { + $this->format = '{type}{length}{notnull}{append}'; + } + + /** + * Getting the `Default` value for constraint + * @return string|Expression|null + */ + public function getDefaultValue() + { + if ($this->default instanceof Expression) { + return $this->default; + } + + return $this->buildDefaultValue(); + } + + /** + * Get the `Check` value for constraint + * @return string|null + */ + public function getCheckValue() + { + return $this->check !== null ? "{$this->check}" : null; + } + + /** + * @return bool + */ + public function isUnique() + { + return $this->isUnique; + } +} diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 66e169c..ddf752d 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -174,32 +174,28 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public function alterColumn($table, $column, $type) { - $sqlAfter = []; + $sqlAfter[] = $this->dropConstraintsForColumn($table, $column, 'D'); - $columnName = $this->db->quoteColumnName($column); - $tableName = $this->db->quoteTableName($table); + if ($type instanceof \yii\db\mssql\ColumnSchemaBuilder) { + $type->isAlterColumn(); - $constraintBase = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column); + $columnName = $this->db->quoteColumnName($column); + $tableName = $this->db->quoteTableName($table); + $constraintBase = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column); - $type = $this->getColumnType($type); - - if (preg_match('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', $type, $matches)) { - $value = strtolower($matches[1]) === 'null' ? null : $matches[1]; - $type = preg_replace('/\s+DEFAULT\s+(["\']?\w*["\']?)/i', '', $type); - $sqlAfter[] = $this->dropConstraintsForColumn($table, $column, 'D'); - $sqlAfter[] = $this->addDefaultValue("DF_{$constraintBase}", $table, $column, $value); - } else { - $sqlAfter[] = $this->dropConstraintsForColumn($table, $column, 'D'); - } + $defaultValue = $type->getDefaultValue(); + if ($defaultValue !== null) { + $sqlAfter[] = $this->addDefaultValue("DF_{$constraintBase}", $table, $column, $defaultValue instanceof Expression ? $defaultValue : new Expression($defaultValue)); + } - if (preg_match('/\s+CHECK\s+\((.+)\)/i', $type, $matches)) { - $type = preg_replace('/\s+CHECK\s+\((.+)\)/i', '', $type); - $sqlAfter[] = "ALTER TABLE {$tableName} ADD CONSTRAINT " . $this->db->quoteColumnName("CK_{$constraintBase}") . " CHECK ({$matches[1]})"; - } + $checkValue = $type->getCheckValue(); + if ($checkValue !== null) { + $sqlAfter[] = "ALTER TABLE {$tableName} ADD CONSTRAINT " . $this->db->quoteColumnName("CK_{$constraintBase}") . " CHECK ({$checkValue})"; + } - $type = preg_replace('/\s+UNIQUE/i', '', $type, -1, $count); - if ($count) { - $sqlAfter[] = "ALTER TABLE {$tableName} ADD CONSTRAINT " . $this->db->quoteColumnName("UQ_{$constraintBase}") . " UNIQUE ({$columnName})"; + if ($type->isUnique()) { + $sqlAfter[] = "ALTER TABLE {$tableName} ADD CONSTRAINT " . $this->db->quoteColumnName("UQ_{$constraintBase}") . " UNIQUE ({$columnName})"; + } } return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN ' @@ -214,7 +210,7 @@ class QueryBuilder extends \yii\db\QueryBuilder public function addDefaultValue($name, $table, $column, $value) { return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT ' - . $this->db->quoteColumnName($name) . ' DEFAULT ' . ($value === null ? 'NULL' : $this->db->quoteValue($value)) . ' FOR ' + . $this->db->quoteColumnName($name) . ' DEFAULT ' . $this->db->quoteValue($value) . ' FOR ' . $this->db->quoteColumnName($column); } @@ -662,5 +658,4 @@ END"; return $this->dropConstraintsForColumn($table, $column) . "\nALTER TABLE " . $this->db->quoteTableName($table) . " DROP COLUMN " . $this->db->quoteColumnName($column); } - } diff --git a/framework/db/mssql/Schema.php b/framework/db/mssql/Schema.php index 365d96e..a2ad449 100644 --- a/framework/db/mssql/Schema.php +++ b/framework/db/mssql/Schema.php @@ -804,4 +804,12 @@ SQL; return $result; } + + /** + * {@inheritdoc} + */ + public function createColumnSchemaBuilder($type, $length = null) + { + return new ColumnSchemaBuilder($type, $length, $this->db); + } } diff --git a/tests/framework/db/mssql/QueryBuilderTest.php b/tests/framework/db/mssql/QueryBuilderTest.php index df16edc..3a17357 100644 --- a/tests/framework/db/mssql/QueryBuilderTest.php +++ b/tests/framework/db/mssql/QueryBuilderTest.php @@ -552,7 +552,7 @@ WHILE 1=1 BEGIN IF @constraintName IS NULL BREAK EXEC (N'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT [' + @constraintName + ']') END -ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT '''''' FOR [bar]"; +ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT '' FOR [bar]"; $sql = $qb->alterColumn('foo1', 'bar', $this->string(255)->defaultValue('')); $this->assertEquals($expected, $sql); @@ -579,7 +579,7 @@ WHILE 1=1 BEGIN IF @constraintName IS NULL BREAK EXEC (N'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT [' + @constraintName + ']') END -ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT '''AbCdE''' FOR [bar]"; +ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT 'AbCdE' FOR [bar]"; $sql = $qb->alterColumn('foo1', 'bar', $this->string(255)->defaultValue('AbCdE')); $this->assertEquals($expected, $sql); @@ -606,7 +606,7 @@ WHILE 1=1 BEGIN IF @constraintName IS NULL BREAK EXEC (N'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT [' + @constraintName + ']') END -ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT 'CURRENT_TIMESTAMP' FOR [bar]"; +ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT CURRENT_TIMESTAMP FOR [bar]"; $sql = $qb->alterColumn('foo1', 'bar', $this->timestamp()->defaultExpression('CURRENT_TIMESTAMP')); $this->assertEquals($expected, $sql); @@ -688,6 +688,38 @@ ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT NULL FOR [bar]"; $this->assertEquals($expected, $sql); } + public function testAlterColumnWithExpression() + { + $qb = $this->getQueryBuilder(); + + $expected = "ALTER TABLE [foo1] ALTER COLUMN [bar] int NULL +DECLARE @tableName VARCHAR(MAX) = '[foo1]' +DECLARE @columnName VARCHAR(MAX) = 'bar' + +WHILE 1=1 BEGIN + DECLARE @constraintName NVARCHAR(128) + SET @constraintName = (SELECT TOP 1 OBJECT_NAME(cons.[object_id]) + FROM ( + SELECT sc.[constid] object_id + FROM [sys].[sysconstraints] sc + JOIN [sys].[columns] c ON c.[object_id]=sc.[id] AND c.[column_id]=sc.[colid] AND c.[name]=@columnName + WHERE sc.[id] = OBJECT_ID(@tableName) + UNION + SELECT object_id(i.[name]) FROM [sys].[indexes] i + JOIN [sys].[columns] c ON c.[object_id]=i.[object_id] AND c.[name]=@columnName + JOIN [sys].[index_columns] ic ON ic.[object_id]=i.[object_id] AND i.[index_id]=ic.[index_id] AND c.[column_id]=ic.[column_id] + WHERE i.[is_unique_constraint]=1 and i.[object_id]=OBJECT_ID(@tableName) + ) cons + JOIN [sys].[objects] so ON so.[object_id]=cons.[object_id] + WHERE so.[type]='D') + IF @constraintName IS NULL BREAK + EXEC (N'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT [' + @constraintName + ']') +END +ALTER TABLE [foo1] ADD CONSTRAINT [DF_foo1_bar] DEFAULT CAST(GETDATE() AS INT) FOR [bar]"; + $sql = $qb->alterColumn('foo1', 'bar', $this->integer()->null()->defaultValue(new Expression('CAST(GETDATE() AS INT)'))); + $this->assertEquals($expected, $sql); + } + public function testAlterColumnWithCheckConstraintOnDb() { $connection = $this->getConnection(); From 01a9fef96262d85a5171890b7131ed656a9cafd4 Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Tue, 27 Apr 2021 12:07:38 +0300 Subject: [PATCH 03/13] Update framework/db/mssql/ColumnSchemaBuilder.php Co-authored-by: Bizley --- framework/db/mssql/ColumnSchemaBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/mssql/ColumnSchemaBuilder.php b/framework/db/mssql/ColumnSchemaBuilder.php index c6f8f54..6ad4304 100644 --- a/framework/db/mssql/ColumnSchemaBuilder.php +++ b/framework/db/mssql/ColumnSchemaBuilder.php @@ -11,7 +11,7 @@ use yii\db\ColumnSchemaBuilder as AbstractColumnSchemaBuilder; use yii\db\Expression; /** - * ColumnSchemaBuilder is the schema builder for MySQL databases. + * ColumnSchemaBuilder is the schema builder for MSSQL databases. * * @author Chris Harris * @since 2.0.8 From a1cc22292792b3a8611e05de0cfa5c98ba4f3d3e Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Tue, 27 Apr 2021 12:08:06 +0300 Subject: [PATCH 04/13] Update framework/db/mssql/QueryBuilder.php Co-authored-by: Bizley --- framework/db/mssql/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index ddf752d..9cfc65b 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -174,7 +174,7 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public function alterColumn($table, $column, $type) { - $sqlAfter[] = $this->dropConstraintsForColumn($table, $column, 'D'); + $sqlAfter = [$this->dropConstraintsForColumn($table, $column, 'D')]; if ($type instanceof \yii\db\mssql\ColumnSchemaBuilder) { $type->isAlterColumn(); From dbc4d1ecea006948aee06a299e54a8f6f9f2afb2 Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Tue, 27 Apr 2021 12:08:15 +0300 Subject: [PATCH 05/13] Update framework/db/mssql/ColumnSchemaBuilder.php Co-authored-by: Bizley --- framework/db/mssql/ColumnSchemaBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/mssql/ColumnSchemaBuilder.php b/framework/db/mssql/ColumnSchemaBuilder.php index 6ad4304..f642f4e 100644 --- a/framework/db/mssql/ColumnSchemaBuilder.php +++ b/framework/db/mssql/ColumnSchemaBuilder.php @@ -38,7 +38,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder } /** - * Changes default format string for MSSQL ALTER COMMAND + * Changes default format string to MSSQL ALTER COMMAND */ public function isAlterColumn() { From 98b0c5b665b03c3f74d4b12dac9aaf464dfe9781 Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Tue, 27 Apr 2021 12:08:22 +0300 Subject: [PATCH 06/13] Update framework/db/mssql/ColumnSchemaBuilder.php Co-authored-by: Bizley --- framework/db/mssql/ColumnSchemaBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/mssql/ColumnSchemaBuilder.php b/framework/db/mssql/ColumnSchemaBuilder.php index f642f4e..618973f 100644 --- a/framework/db/mssql/ColumnSchemaBuilder.php +++ b/framework/db/mssql/ColumnSchemaBuilder.php @@ -40,7 +40,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder /** * Changes default format string to MSSQL ALTER COMMAND */ - public function isAlterColumn() + public function setAlterColumnFormat() { $this->format = '{type}{length}{notnull}{append}'; } From a0552f16802ffd0ae63369a3eff46c32694e41b9 Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Tue, 27 Apr 2021 12:09:14 +0300 Subject: [PATCH 07/13] Update framework/db/mssql/QueryBuilder.php Co-authored-by: Bizley --- framework/db/mssql/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 9cfc65b..8f930e5 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -177,7 +177,7 @@ class QueryBuilder extends \yii\db\QueryBuilder $sqlAfter = [$this->dropConstraintsForColumn($table, $column, 'D')]; if ($type instanceof \yii\db\mssql\ColumnSchemaBuilder) { - $type->isAlterColumn(); + $type->setAlterColumnFormat(); $columnName = $this->db->quoteColumnName($column); $tableName = $this->db->quoteTableName($table); From 12ccd2addef6b51c9052570fef82023fbb43b708 Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Tue, 27 Apr 2021 12:09:32 +0300 Subject: [PATCH 08/13] Update framework/db/mssql/QueryBuilder.php Co-authored-by: Bizley --- framework/db/mssql/QueryBuilder.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 8f930e5..d2727ab 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -185,7 +185,12 @@ class QueryBuilder extends \yii\db\QueryBuilder $defaultValue = $type->getDefaultValue(); if ($defaultValue !== null) { - $sqlAfter[] = $this->addDefaultValue("DF_{$constraintBase}", $table, $column, $defaultValue instanceof Expression ? $defaultValue : new Expression($defaultValue)); + $sqlAfter[] = $this->addDefaultValue( + "DF_{$constraintBase}", + $table, + $column, + $defaultValue instanceof Expression ? $defaultValue : new Expression($defaultValue) + ); } $checkValue = $type->getCheckValue(); From 2d76f33dd43d992a3547054a95446facf910eefa Mon Sep 17 00:00:00 2001 From: DarkDef Date: Tue, 27 Apr 2021 12:15:31 +0300 Subject: [PATCH 09/13] Fixes after review --- framework/db/mssql/ColumnSchemaBuilder.php | 6 +++--- framework/db/mssql/QueryBuilder.php | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/framework/db/mssql/ColumnSchemaBuilder.php b/framework/db/mssql/ColumnSchemaBuilder.php index 618973f..2939be8 100644 --- a/framework/db/mssql/ColumnSchemaBuilder.php +++ b/framework/db/mssql/ColumnSchemaBuilder.php @@ -13,8 +13,8 @@ use yii\db\Expression; /** * ColumnSchemaBuilder is the schema builder for MSSQL databases. * - * @author Chris Harris - * @since 2.0.8 + * @author Valerii Gorbachev + * @since 2.0.42 */ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder { @@ -64,7 +64,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder */ public function getCheckValue() { - return $this->check !== null ? "{$this->check}" : null; + return $this->check !== null ? (string) $this->check : null; } /** diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index d2727ab..b964513 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -176,12 +176,13 @@ class QueryBuilder extends \yii\db\QueryBuilder { $sqlAfter = [$this->dropConstraintsForColumn($table, $column, 'D')]; + $columnName = $this->db->quoteColumnName($column); + $tableName = $this->db->quoteTableName($table); + $constraintBase = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column); + if ($type instanceof \yii\db\mssql\ColumnSchemaBuilder) { $type->setAlterColumnFormat(); - $columnName = $this->db->quoteColumnName($column); - $tableName = $this->db->quoteTableName($table); - $constraintBase = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column); $defaultValue = $type->getDefaultValue(); if ($defaultValue !== null) { @@ -195,7 +196,9 @@ class QueryBuilder extends \yii\db\QueryBuilder $checkValue = $type->getCheckValue(); if ($checkValue !== null) { - $sqlAfter[] = "ALTER TABLE {$tableName} ADD CONSTRAINT " . $this->db->quoteColumnName("CK_{$constraintBase}") . " CHECK ({$checkValue})"; + $sqlAfter[] = "ALTER TABLE {$tableName} ADD CONSTRAINT " . + $this->db->quoteColumnName("CK_{$constraintBase}") . + " CHECK (" . ($defaultValue instanceof Expression ? $checkValue : new Expression($checkValue)) . ")"; } if ($type->isUnique()) { From 662c54b47c8fada1639ec331c14b0e600d8a598f Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Wed, 28 Apr 2021 18:27:30 +0300 Subject: [PATCH 10/13] Update framework/db/mssql/ColumnSchemaBuilder.php Co-authored-by: Bizley --- framework/db/mssql/ColumnSchemaBuilder.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/framework/db/mssql/ColumnSchemaBuilder.php b/framework/db/mssql/ColumnSchemaBuilder.php index 2939be8..35a8ac1 100644 --- a/framework/db/mssql/ColumnSchemaBuilder.php +++ b/framework/db/mssql/ColumnSchemaBuilder.php @@ -26,12 +26,10 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder */ public function __toString() { - switch ($this->getTypeCategory()) { - case self::CATEGORY_PK: - $format = '{type}{check}{comment}{append}'; - break; - default: - $format = $this->format; + if ($this->getTypeCategory() === self::CATEGORY_PK) { + $format = '{type}{check}{comment}{append}'; + } else { + $format = $this->format; } return $this->buildCompleteString($format); From a7ee99ce92345c5f5bab17e6db49606ca575115f Mon Sep 17 00:00:00 2001 From: Valerii Gorbachev Date: Wed, 28 Apr 2021 18:28:20 +0300 Subject: [PATCH 11/13] Update framework/db/mssql/QueryBuilder.php Co-authored-by: Bizley --- framework/db/mssql/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index b964513..a846026 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -190,7 +190,7 @@ class QueryBuilder extends \yii\db\QueryBuilder "DF_{$constraintBase}", $table, $column, - $defaultValue instanceof Expression ? $defaultValue : new Expression($defaultValue) + $defaultValue instanceof Expression ? $defaultValue : new Expression($defaultValue) ); } From f14b2ccd890e532919324e2d37228ae1a9e5f628 Mon Sep 17 00:00:00 2001 From: DarkDef Date: Wed, 28 Apr 2021 18:32:43 +0300 Subject: [PATCH 12/13] remove duplicate code --- framework/db/mssql/QueryBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index a846026..8e250a5 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -206,8 +206,8 @@ class QueryBuilder extends \yii\db\QueryBuilder } } - return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN ' - . $this->db->quoteColumnName($column) . ' ' + return 'ALTER TABLE ' . $tableName . ' ALTER COLUMN ' + . $columnName . ' ' . $this->getColumnType($type) . "\n" . implode("\n", $sqlAfter); } From 4f74d2a2dbbc580b8d6c2e3bc94749e24747a596 Mon Sep 17 00:00:00 2001 From: DarkDef Date: Thu, 29 Apr 2021 19:47:01 +0300 Subject: [PATCH 13/13] Add line to changelog --- framework/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 871ec89..ef9e84a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -25,6 +25,7 @@ Yii Framework 2 Change Log - Enh #18569: Add `NumberValidator::$allowArray` (raidkon) - Bug #18613: Do not call static methods non-statically in `BaseActiveRecord` (samdark) - Bug #18619: Do not modify `yii\web\Cookie::$path` on `yii\web\Response::sendCookies()` (mikk150) +- Bug #18604: Function alterColumn for MSSQL build incorrect query with default values `NULL` and other expressions (darkdef) 2.0.41.1 March 04, 2021