Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- Enh #386: Remove `getCacheKey()` and `getCacheTag()` methods from `Schema` class (@Tigrov)
- Bug #388: Set empty `comment` and `extra` properties to `null` when loading table columns (@Tigrov)
- Enh #389, #390: Use `DbArrayHelper::arrange()` instead of `DbArrayHelper::index()` method (@Tigrov)
- New #387: Realize `Schema::loadResultColumn()` method (@Tigrov)

## 1.2.0 March 21, 2024

Expand Down
2 changes: 2 additions & 0 deletions src/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ final class ColumnFactory extends AbstractColumnFactory
'binary' => ColumnType::BINARY,
'varbinary' => ColumnType::BINARY,
'blob' => ColumnType::BINARY,
'tinyblob' => ColumnType::BINARY,
'mediumblob' => ColumnType::BINARY,
'longblob' => ColumnType::BINARY,
'year' => ColumnType::DATE,
'date' => ColumnType::DATE,
Expand Down
73 changes: 73 additions & 0 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@
use function str_contains;
use function str_ireplace;
use function str_starts_with;
use function strtolower;
use function substr;
use function trim;

use const PHP_INT_SIZE;

/**
* Implements MySQL, MariaDB specific schema, supporting MySQL Server 5.7, MariaDB Server 10.4 and higher.
*
Expand Down Expand Up @@ -359,6 +362,76 @@ protected function getCreateTableSql(TableSchemaInterface $table): string
return $sql;
}

/**
* @psalm-param array{
* native_type: string,
* pdo_type: int,
* flags: string[],
* table: string,
* name: string,
* len: int,
* precision: int,
* } $metadata
*
* @psalm-suppress MoreSpecificImplementedParamType
*/
protected function loadResultColumn(array $metadata): ColumnInterface|null
{
if (empty($metadata['native_type']) || $metadata['native_type'] === 'NULL') {
return null;
}

$dbType = match ($metadata['native_type']) {
'TINY' => 'tinyint',
'SHORT' => 'smallint',
'INT24' => 'mediumint',
'LONG' => 'int',
'LONGLONG' => $metadata['len'] < 10 ? 'int' : 'bigint',
'NEWDECIMAL' => 'decimal',
'STRING' => 'char',
'VAR_STRING' => 'varchar',
'BLOB' => match ($metadata['len']) {
255 => 'tinyblob',
510, 765, 1020 => 'tinytext',
// 65535 => 'blob',
131070, 196605, 262140 => 'text',
16777215 => 'mediumblob',
33554430, 50331645, 67108860 => 'mediumtext',
4294967295 => 'longblob',
default => 'blob',
},
default => strtolower($metadata['native_type']),
};

$columnInfo = [];

if (!empty($metadata['table'])) {
$columnInfo['table'] = $metadata['table'];
$columnInfo['name'] = $metadata['name'];
} elseif (!empty($metadata['name'])) {
$columnInfo['name'] = $metadata['name'];
}

if (!empty($metadata['len'])) {
$columnInfo['size'] = match ($dbType) {
'decimal' => $metadata['len'] - ($metadata['precision'] === 0 ? 1 : 2),
'time', 'datetime', 'timestamp' => $metadata['precision'],
default => $metadata['len'],
};
}

match ($dbType) {
'float', 'double', 'decimal' => $columnInfo['scale'] = $metadata['precision'],
'bigint' => $metadata['len'] === 20 ? $columnInfo['unsigned'] = true : null,
'int' => $metadata['len'] === 10 && PHP_INT_SIZE !== 8 ? $columnInfo['unsigned'] = true : null,
default => null,
};

$columnInfo['notNull'] = in_array('not_null', $metadata['flags'], true);

return $this->db->getColumnFactory()->fromDbType($dbType, $columnInfo);
}

/**
* Loads the column information into a {@see ColumnInterface} object.
*
Expand Down
146 changes: 125 additions & 21 deletions tests/ColumnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Throwable;
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Mysql\Column\ColumnBuilder;
use Yiisoft\Db\Mysql\Connection;
use Yiisoft\Db\Mysql\Tests\Support\TestTrait;
use Yiisoft\Db\Query\Query;
use Yiisoft\Db\Schema\Column\BinaryColumn;
Expand All @@ -28,6 +29,127 @@ final class ColumnTest extends AbstractColumnTest
{
use TestTrait;

private function insertTypeValues(Connection $db): void
{
$db->createCommand()->insert(
'type',
[
'int_col' => 1,
'bigunsigned_col' => '12345678901234567890',
'char_col' => str_repeat('x', 100),
'char_col3' => null,
'float_col' => 1.234,
'blob_col' => "\x10\x11\x12",
'time' => '2023-07-11 14:50:23',
'bool_col' => false,
'bit_col' => 0b0110_0100, // 100
'json_col' => [['a' => 1, 'b' => null, 'c' => [1, 3, 5]]],
]
)->execute();
}

private function assertResultValues(array $result): void
{
$this->assertSame(1, $result['int_col']);
$this->assertSame('12345678901234567890', $result['bigunsigned_col']);
$this->assertSame(str_repeat('x', 100), $result['char_col']);
$this->assertNull($result['char_col3']);
$this->assertSame(1.234, $result['float_col']);
$this->assertSame("\x10\x11\x12", $result['blob_col']);
$this->assertSame('2023-07-11 14:50:23', $result['time']);
$this->assertFalse($result['bool_col']);
$this->assertSame(0b0110_0100, $result['bit_col']);
$this->assertJsonStringEqualsJsonString('[{"a":1,"b":null,"c":[1,3,5]}]', $result['json_col']);
}

public function testQueryTypecasting(): void
{
$db = $this->getConnection(true);

$this->insertTypeValues($db);

$query = (new Query($db))->from('type')->withTypecasting();

$result = $query->one();

$this->assertResultValues($result);

$result = $query->all();

$this->assertResultValues($result[0]);

$db->close();
}

public function testCommandWithPhpTypecasting(): void
{
$db = $this->getConnection(true);

$this->insertTypeValues($db);

$command = $db->createCommand('SELECT * FROM type')->withPhpTypecasting();

$result = $command->queryOne();

$this->assertResultValues($result);

$result = $command->queryAll();

$this->assertResultValues($result[0]);

$db->close();
}

public function testSelectWithPhpTypecasting(): void
{
$db = $this->getConnection();

$sql = <<<SQL
SELECT
null AS `null`,
1 AS `1`,
2.5 AS `2.5`,
true AS `true`,
false AS `false`,
'string' AS `string`
SQL;

$expected = [
'null' => null,
1 => 1,
'2.5' => 2.5,
'true' => 1,
'false' => 0,
'string' => 'string',
];

$result = $db->createCommand($sql)
->withPhpTypecasting()
->queryOne();

$this->assertSame($expected, $result);

$result = $db->createCommand($sql)
->withPhpTypecasting()
->queryAll();

$this->assertSame([$expected], $result);

$result = $db->createCommand('SELECT 2.5')
->withPhpTypecasting()
->queryScalar();

$this->assertSame(2.5, $result);

$result = $db->createCommand('SELECT 2.5 UNION SELECT 3.3')
->withPhpTypecasting()
->queryColumn();

$this->assertSame([2.5, 3.3], $result);

$db->close();
}

/**
* @dataProvider \Yiisoft\Db\Mysql\Tests\Provider\ColumnProvider::bigIntValue
*
Expand All @@ -51,30 +173,12 @@ public function testColumnBigInt(string $bigint): void
public function testPhpTypeCast(): void
{
$db = $this->getConnection(true);

$command = $db->createCommand();
$schema = $db->getSchema();
$tableSchema = $schema->getTableSchema('type');

$command->insert(
'type',
[
'int_col' => 1,
'bigunsigned_col' => 1234567890,
'char_col' => str_repeat('x', 100),
'char_col3' => null,
'float_col' => 1.234,
'blob_col' => "\x10\x11\x12",
'time' => '2023-07-11 14:50:23',
'bool_col' => false,
'bit_col' => 0b0110_0100, // 100
'json_col' => [['a' => 1, 'b' => null, 'c' => [1, 3, 5]]],
]
);
$command->execute();
$query = (new Query($db))->from('type')->one();
$this->insertTypeValues($db);

$this->assertNotNull($tableSchema);
$query = (new Query($db))->from('type')->one();

$intColPhpType = $tableSchema->getColumn('int_col')?->phpTypecast($query['int_col']);
$bigUnsignedColPhpType = $tableSchema->getColumn('bigunsigned_col')?->phpTypecast($query['bigunsigned_col']);
Expand All @@ -88,7 +192,7 @@ public function testPhpTypeCast(): void
$jsonColPhpType = $tableSchema->getColumn('json_col')?->phpTypecast($query['json_col']);

$this->assertSame(1, $intColPhpType);
$this->assertSame('1234567890', $bigUnsignedColPhpType);
$this->assertSame('12345678901234567890', $bigUnsignedColPhpType);
$this->assertSame(str_repeat('x', 100), $charColPhpType);
$this->assertNull($charCol3PhpType);
$this->assertSame(1.234, $floatColPhpType);
Expand Down
5 changes: 4 additions & 1 deletion tests/CommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,14 @@ public function testDropDefaultValue(): void

public function testDropTableCascade(): void
{
$command = $this->getConnection()->createCommand();
$db = $this->getConnection();
$command = $db->createCommand();

$this->expectException(NotSupportedException::class);
$this->expectExceptionMessage('MySQL doesn\'t support cascade drop table.');
$command->dropTable('{{table}}', cascade: true);

$db->close();
}

/**
Expand Down
Loading
Loading