From cdd38cbe05238d1e546e8fe1388a283543daa06e Mon Sep 17 00:00:00 2001 From: SirPL Date: Thu, 30 Jun 2022 14:30:35 +0200 Subject: [PATCH 1/2] Support for BOOLEAN column type --- src/DataIntegrity.php | 3 ++ src/DataType.php | 1 + src/Parser/CreateTableParser.php | 1 + src/Processor/CreateProcessor.php | 3 ++ src/Schema/Column/Boolean.php | 52 ++++++++++++++++++++++++++++ tests/EndToEndTest.php | 18 ++++++++++ tests/fixtures/bulk_enemy_insert.sql | 10 +++--- tests/fixtures/create_table.sql | 1 + 8 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 src/Schema/Column/Boolean.php diff --git a/src/DataIntegrity.php b/src/DataIntegrity.php index 677de218..9c871d6e 100644 --- a/src/DataIntegrity.php +++ b/src/DataIntegrity.php @@ -251,6 +251,9 @@ public static function coerceValueToColumn( return (string) $value; + case 'boolean': + return (int) $value; + default: throw new \Exception( "DataIntegrity::coerceValueToSchema found unknown type for field: '{$php_type}'" diff --git a/src/DataType.php b/src/DataType.php index 876b0963..9dc52277 100644 --- a/src/DataType.php +++ b/src/DataType.php @@ -37,6 +37,7 @@ final class DataType const TIMESTAMP = 'TIMESTAMP'; const DECIMAL = 'DECIMAL'; const NUMERIC = 'NUMERIC'; + const BOOLEAN = 'BOOLEAN'; private function __construct() { diff --git a/src/Parser/CreateTableParser.php b/src/Parser/CreateTableParser.php index 6d24a881..23d8dd17 100644 --- a/src/Parser/CreateTableParser.php +++ b/src/Parser/CreateTableParser.php @@ -478,6 +478,7 @@ public static function parseFieldType(array &$tokens, bool $allowArbitaryType = case 'BLOB': case 'MEDIUMBLOB': case 'LONGBLOB': + case 'BOOLEAN': break; case 'TINYINT': case 'SMALLINT': diff --git a/src/Processor/CreateProcessor.php b/src/Processor/CreateProcessor.php index cb1acf80..e2d32950 100644 --- a/src/Processor/CreateProcessor.php +++ b/src/Processor/CreateProcessor.php @@ -147,6 +147,7 @@ private static function getDefinitionColumn(Query\MysqlColumnType $stmt) : Colum case DataType::BIT: case DataType::MEDIUMINT: case DataType::BIGINT: + case DataType::BOOLEAN: if ($stmt->null === null) { $stmt->null = true; } @@ -248,6 +249,8 @@ private static function getIntegerDefinitionColumn(Query\MysqlColumnType $stmt) $display_width = (int) $stmt->length; switch (strtoupper($stmt->type)) { + case DataType::BOOLEAN: + return new Column\Boolean($unsigned, $display_width); case DataType::TINYINT: return new Column\TinyInt($unsigned, $display_width); diff --git a/src/Schema/Column/Boolean.php b/src/Schema/Column/Boolean.php new file mode 100644 index 00000000..78f603e3 --- /dev/null +++ b/src/Schema/Column/Boolean.php @@ -0,0 +1,52 @@ +hasDefault()) { + $default = '->setDefault(' + . ($this->getDefault() === null + ? 'null' + : '\'' . $this->getDefault() . '\'') + . ')'; + } + + $output = '(new \\' . static::class . '(' + . ($this->unsigned ? 'true' : 'false') + . ', ' . $this->integer_display_width + . '))' + . $default + . $this->getNullablePhp(); + + var_export($output); + return $output; + } +} diff --git a/tests/EndToEndTest.php b/tests/EndToEndTest.php index a594140e..6e65adc5 100644 --- a/tests/EndToEndTest.php +++ b/tests/EndToEndTest.php @@ -1092,6 +1092,24 @@ public function testSelectNullableFields() ); } + public function testBoolean() + { + $pdo = self::getConnectionToFullDB(false, false); + + $query = $pdo->prepare("SELECT `id`, `enabled` FROM `enemies`"); + $query->execute(); + + $this->assertSame( + [ + ['id' => 1, 'enabled' => 1], + ['id' => 2, 'enabled' => 0], + ['id' => 3, 'enabled' => 0], + ['id' => 4, 'enabled' => 1] + ], + $query->fetchAll(\PDO::FETCH_ASSOC) + ); + } + private static function getPdo(string $connection_string, bool $strict_mode = false) : \PDO { $options = $strict_mode ? [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode="STRICT_ALL_TABLES"'] : []; diff --git a/tests/fixtures/bulk_enemy_insert.sql b/tests/fixtures/bulk_enemy_insert.sql index 596329eb..2991f7a5 100644 --- a/tests/fixtures/bulk_enemy_insert.sql +++ b/tests/fixtures/bulk_enemy_insert.sql @@ -1,7 +1,7 @@ INSERT INTO `enemies` - (`id`, `character_id`, `enemy_id`) + (`id`, `character_id`, `enemy_id`, `enabled`) VALUES - (1, 1, 5), - (2, 2, 5), - (3, 3, 6), - (4, 1, 11) \ No newline at end of file + (1, 1, 5, true), + (2, 2, 5, false), + (3, 3, 6, false), + (4, 1, 11, true) \ No newline at end of file diff --git a/tests/fixtures/create_table.sql b/tests/fixtures/create_table.sql index f207482c..edd4604f 100644 --- a/tests/fixtures/create_table.sql +++ b/tests/fixtures/create_table.sql @@ -28,6 +28,7 @@ CREATE TABLE `enemies` ( `id` int(10) NOT NULL AUTO_INCREMENT, `character_id` int(10) NOT NULL, `enemy_id` int(10) NOT NULL, + `enabled` boolean NOT NULL DEFAULT true, PRIMARY KEY (`id`), KEY `character_id` (`character_id`), KEY `enemy_id` (`enemy_id`) From 5271da6287aba878c7fb2925fa96d334684f8816 Mon Sep 17 00:00:00 2001 From: SirPL Date: Thu, 30 Jun 2022 20:42:54 +0200 Subject: [PATCH 2/2] Support for BOOLEAN column type Fixed most of Psalm issues Boolean values are internally stored as integers --- src/DataIntegrity.php | 4 +--- src/Parser/SQLParser.php | 10 ++++++++++ src/Processor/CreateProcessor.php | 5 +++-- src/Schema/Column.php | 2 +- src/Schema/Column/Boolean.php | 17 +++++++++-------- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/DataIntegrity.php b/src/DataIntegrity.php index 9c871d6e..e212c649 100644 --- a/src/DataIntegrity.php +++ b/src/DataIntegrity.php @@ -204,6 +204,7 @@ public static function coerceValueToColumn( } switch ($php_type) { + case 'boolean': case 'int': return (int) $value; @@ -251,9 +252,6 @@ public static function coerceValueToColumn( return (string) $value; - case 'boolean': - return (int) $value; - default: throw new \Exception( "DataIntegrity::coerceValueToSchema found unknown type for field: '{$php_type}'" diff --git a/src/Parser/SQLParser.php b/src/Parser/SQLParser.php index b30b3ed7..90b3ad1f 100644 --- a/src/Parser/SQLParser.php +++ b/src/Parser/SQLParser.php @@ -225,6 +225,16 @@ private static function buildTokenListFromLexemes(array $tokens) continue; } + if ($token === 'true') { + $out[] = new Token(TokenType::NUMERIC_CONSTANT, '1', $token, $start); + continue; + } + + if ($token === 'false') { + $out[] = new Token(TokenType::NUMERIC_CONSTANT, '0', $token, $start); + continue; + } + if (\preg_match('/^[0-9\.]+$/', $token)) { $out[] = new Token(TokenType::NUMERIC_CONSTANT, $token, $token, $start); continue; diff --git a/src/Processor/CreateProcessor.php b/src/Processor/CreateProcessor.php index e2d32950..c98015ef 100644 --- a/src/Processor/CreateProcessor.php +++ b/src/Processor/CreateProcessor.php @@ -240,7 +240,7 @@ private static function getDefinitionColumn(Query\MysqlColumnType $stmt) : Colum } /** - * @return Column\BigInt|Column\IntColumn|Column\MediumInt|Column\SmallInt|Column\TinyInt + * @return Column\BigInt|Column\IntColumn|Column\MediumInt|Column\SmallInt|Column\TinyInt|Column\Boolean */ private static function getIntegerDefinitionColumn(Query\MysqlColumnType $stmt) { @@ -250,7 +250,8 @@ private static function getIntegerDefinitionColumn(Query\MysqlColumnType $stmt) switch (strtoupper($stmt->type)) { case DataType::BOOLEAN: - return new Column\Boolean($unsigned, $display_width); + return new Column\Boolean(); + case DataType::TINYINT: return new Column\TinyInt($unsigned, $display_width); diff --git a/src/Schema/Column.php b/src/Schema/Column.php index 4247d08b..c9e155d3 100644 --- a/src/Schema/Column.php +++ b/src/Schema/Column.php @@ -28,7 +28,7 @@ public function getNullablePhp() : string } /** - * @return 'int'|'string'|'float'|'null' + * @return 'int'|'string'|'float'|'null'|'boolean' */ abstract public function getPhpType() : string; diff --git a/src/Schema/Column/Boolean.php b/src/Schema/Column/Boolean.php index 78f603e3..cce990fa 100644 --- a/src/Schema/Column/Boolean.php +++ b/src/Schema/Column/Boolean.php @@ -22,16 +22,22 @@ public function getMinValue() return 0; } + /** + * @return 'boolean' + */ public function getPhpType(): string { return 'boolean'; } - public function getPhpCode(): string + /** + * @return string + */ + public function getPhpCode() : string { $default = ''; - if ($this instanceof Defaultable && $this->hasDefault()) { + if ($this->hasDefault()) { $default = '->setDefault(' . ($this->getDefault() === null ? 'null' @@ -39,14 +45,9 @@ public function getPhpCode(): string . ')'; } - $output = '(new \\' . static::class . '(' - . ($this->unsigned ? 'true' : 'false') - . ', ' . $this->integer_display_width + return '(new \\' . static::class . '(' . '))' . $default . $this->getNullablePhp(); - - var_export($output); - return $output; } }