From 9d3c71d6a712a99f42ae1c5d588312f49514523a Mon Sep 17 00:00:00 2001 From: Wilmer Arambula <42547589+terabytesoftw@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:47:46 -0300 Subject: [PATCH] Fix #20040: Fix type `boolean` in `MSSQL` --- framework/CHANGELOG.md | 2 +- framework/db/mssql/Schema.php | 41 +++- tests/framework/db/mssql/type/BooleanTest.php | 198 ++++++++++++++++++ 3 files changed, 232 insertions(+), 9 deletions(-) create mode 100644 tests/framework/db/mssql/type/BooleanTest.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index ec2923a1e51..42ce39634cd 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -3,7 +3,7 @@ Yii Framework 2 Change Log 2.0.50 under development ------------------------ - +- Bug #20040: Fix type `boolean` in `MSSQL` (terabytesoftw) - Bug #20005: Fix `yii\console\controllers\ServeController` to specify the router script (terabytesoftw) - Bug #19060: Fix `yii\widgets\Menu` bug when using Closure for active item and adding additional tests in `tests\framework\widgets\MenuTest` (atrandafir) - Bug #13920: Fixed erroneous validation for specific cases (tim-fischer-maschinensucher) diff --git a/framework/db/mssql/Schema.php b/framework/db/mssql/Schema.php index 005b1555f78..db7f07c87cb 100644 --- a/framework/db/mssql/Schema.php +++ b/framework/db/mssql/Schema.php @@ -375,6 +375,7 @@ protected function resolveTableNames($table, $name) */ protected function loadColumnSchema($info) { + $isVersion2017orLater = version_compare($this->db->getSchema()->getServerVersion(), '14', '>='); $column = $this->createColumnSchema(); $column->name = $info['column_name']; @@ -393,20 +394,21 @@ protected function loadColumnSchema($info) if (isset($this->typeMap[$type])) { $column->type = $this->typeMap[$type]; } + + if ($isVersion2017orLater && $type === 'bit') { + $column->type = 'boolean'; + } + if (!empty($matches[2])) { $values = explode(',', $matches[2]); $column->size = $column->precision = (int) $values[0]; + if (isset($values[1])) { $column->scale = (int) $values[1]; } - if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { - $column->type = 'boolean'; - } elseif ($type === 'bit') { - if ($column->size > 32) { - $column->type = 'bigint'; - } elseif ($column->size === 32) { - $column->type = 'integer'; - } + + if ($isVersion2017orLater === false) { + $column->type = $this->booleanTypeLegacy($column->size, $type); } } } @@ -813,4 +815,27 @@ public function createColumnSchemaBuilder($type, $length = null) { return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]); } + + /** + * Assigns a type boolean for the column type bit, for legacy versions of MSSQL. + * + * @param int $size column size. + * @param string $type column type. + * + * @return string column type. + */ + private function booleanTypeLegacy($size, $type) + { + if ($size === 1 && ($type === 'tinyint' || $type === 'bit')) { + return 'boolean'; + } elseif ($type === 'bit') { + if ($size > 32) { + return 'bigint'; + } elseif ($size === 32) { + return 'integer'; + } + } + + return $type; + } } diff --git a/tests/framework/db/mssql/type/BooleanTest.php b/tests/framework/db/mssql/type/BooleanTest.php new file mode 100644 index 00000000000..97fd7124519 --- /dev/null +++ b/tests/framework/db/mssql/type/BooleanTest.php @@ -0,0 +1,198 @@ +getConnection(true); + $schema = $db->getSchema(); + $tableName = '{{%boolean}}'; + + if ($db->getTableSchema($tableName)) { + $db->createCommand()->dropTable($tableName)->execute(); + } + + $db->createCommand()->createTable( + $tableName, + [ + 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN), + ] + )->execute(); + + // test type + $column = $db->getTableSchema($tableName)->getColumn('bool_col'); + $this->assertSame('boolean', $column->phpType); + + // test value `false` + $db->createCommand()->insert($tableName, ['bool_col' => false])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar(); + $this->assertEquals(0, $boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertFalse($phpTypeCast); + + // test value `true` + $db->createCommand()->insert($tableName, ['bool_col' => true])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar(); + $this->assertEquals(1, $boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertTrue($phpTypeCast); + } + + public function testBooleanWithValueInteger() + { + $db = $this->getConnection(true); + $schema = $db->getSchema(); + $tableName = '{{%boolean}}'; + + if ($db->getTableSchema($tableName)) { + $db->createCommand()->dropTable($tableName)->execute(); + } + + $db->createCommand()->createTable( + $tableName, + [ + 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN), + ] + )->execute(); + + // test type + $column = $db->getTableSchema($tableName)->getColumn('bool_col'); + $this->assertSame('boolean', $column->phpType); + + // test value 0 + $db->createCommand()->insert($tableName, ['bool_col' => 0])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar(); + $this->assertEquals(0, $boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertFalse($phpTypeCast); + + // test value 1 + $db->createCommand()->insert($tableName, ['bool_col' => 1])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 2")->queryScalar(); + $this->assertEquals(1, $boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertTrue($phpTypeCast); + } + + public function testBooleanValueNegative() + { + $db = $this->getConnection(true); + $schema = $db->getSchema(); + $tableName = '{{%boolean}}'; + + if ($db->getTableSchema($tableName)) { + $db->createCommand()->dropTable($tableName)->execute(); + } + + $db->createCommand()->createTable( + $tableName, + [ + 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN), + ] + )->execute(); + + // test type + $column = $db->getTableSchema($tableName)->getColumn('bool_col'); + $this->assertSame('boolean', $column->phpType); + + // test value 2 + $db->createCommand()->insert($tableName, ['bool_col' => -1])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar(); + $this->assertEquals(1, $boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertTrue($phpTypeCast); + } + + public function testBooleanWithValueNull() + { + $db = $this->getConnection(true); + $schema = $db->getSchema(); + $tableName = '{{%boolean}}'; + + if ($db->getTableSchema($tableName)) { + $db->createCommand()->dropTable($tableName)->execute(); + } + + $db->createCommand()->createTable( + $tableName, + [ + 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN), + ] + )->execute(); + + // test type + $column = $db->getTableSchema($tableName)->getColumn('bool_col'); + $this->assertSame('boolean', $column->phpType); + + // test value `null` + $db->createCommand()->insert($tableName, ['bool_col' => null])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar(); + $this->assertNull($boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertNull($phpTypeCast); + } + + public function testBooleanWithValueOverflow() + { + $db = $this->getConnection(true); + $schema = $db->getSchema(); + $tableName = '{{%boolean}}'; + + if ($db->getTableSchema($tableName)) { + $db->createCommand()->dropTable($tableName)->execute(); + } + + $db->createCommand()->createTable( + $tableName, + [ + 'id' => $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'bool_col' => $schema->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN), + ] + )->execute(); + + // test type + $column = $db->getTableSchema($tableName)->getColumn('bool_col'); + $this->assertSame('boolean', $column->phpType); + + // test value 2 + $db->createCommand()->insert($tableName, ['bool_col' => 2])->execute(); + $boolValue = $db->createCommand("SELECT bool_col FROM $tableName WHERE id = 1")->queryScalar(); + $this->assertEquals(1, $boolValue); + + // test php typecast + $phpTypeCast = $column->phpTypecast($boolValue); + $this->assertTrue($phpTypeCast); + } +}