Skip to content

Commit

Permalink
Fix #20040: Fix type boolean in MSSQL
Browse files Browse the repository at this point in the history
  • Loading branch information
terabytesoftw committed Oct 25, 2023
1 parent e2773e4 commit 9d3c71d
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 9 deletions.
2 changes: 1 addition & 1 deletion framework/CHANGELOG.md
Expand Up @@ -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)
Expand Down
41 changes: 33 additions & 8 deletions framework/db/mssql/Schema.php
Expand Up @@ -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'];
Expand All @@ -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);
}
}
}
Expand Down Expand Up @@ -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;
}
}
198 changes: 198 additions & 0 deletions tests/framework/db/mssql/type/BooleanTest.php
@@ -0,0 +1,198 @@
<?php
/**
* @link https://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license https://www.yiiframework.com/license/
*/

namespace yiiunit\framework\db\mssql\type;

use yii\db\mssql\Schema;
use yiiunit\framework\db\DatabaseTestCase;

/**
* @group db
* @group mssql
*/
class BooleanTest extends DatabaseTestCase
{
protected $driverName = 'sqlsrv';

public function testBoolean()
{
$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 `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);
}
}

0 comments on commit 9d3c71d

Please sign in to comment.