Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve column type #230

Merged
merged 13 commits into from
Sep 2, 2023
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 1.1.1 under development

- no changes in this release.
- Enh #230: Improve column type #230 (@Tigrov)

## 1.1.0 July 24, 2023

Expand Down
12 changes: 6 additions & 6 deletions src/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ final class QueryBuilder extends AbstractQueryBuilder
SchemaInterface::TYPE_SMALLINT => 'NUMBER(5)',
SchemaInterface::TYPE_INTEGER => 'NUMBER(10)',
SchemaInterface::TYPE_BIGINT => 'NUMBER(20)',
SchemaInterface::TYPE_FLOAT => 'NUMBER',
SchemaInterface::TYPE_DOUBLE => 'NUMBER',
SchemaInterface::TYPE_DECIMAL => 'NUMBER',
SchemaInterface::TYPE_DATETIME => 'TIMESTAMP',
SchemaInterface::TYPE_TIMESTAMP => 'TIMESTAMP',
SchemaInterface::TYPE_TIME => 'TIMESTAMP',
SchemaInterface::TYPE_FLOAT => 'BINARY_FLOAT',
SchemaInterface::TYPE_DOUBLE => 'BINARY_DOUBLE',
SchemaInterface::TYPE_DECIMAL => 'NUMBER(10,0)',
SchemaInterface::TYPE_DATETIME => 'TIMESTAMP(0)',
SchemaInterface::TYPE_TIMESTAMP => 'TIMESTAMP(0)',
darkdef marked this conversation as resolved.
Show resolved Hide resolved
SchemaInterface::TYPE_TIME => 'INTERVAL DAY(0) TO SECOND(0)',
darkdef marked this conversation as resolved.
Show resolved Hide resolved
SchemaInterface::TYPE_DATE => 'DATE',
SchemaInterface::TYPE_BINARY => 'BLOB',
SchemaInterface::TYPE_BOOLEAN => 'NUMBER(1)',
Expand Down
63 changes: 51 additions & 12 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
use function is_array;
use function md5;
use function preg_match;
use function preg_replace;
use function serialize;
use function str_replace;
use function strtolower;
use function trim;

/**
Expand Down Expand Up @@ -64,6 +66,38 @@
*/
final class Schema extends AbstractPdoSchema
{
/**
* @var array The mapping from physical column types (keys) to abstract column types (values).
*
* @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html
*
* @psalm-var string[]
*/
private array $typeMap = [
'char' => self::TYPE_CHAR,
'nchar' => self::TYPE_CHAR,
'varchar2' => self::TYPE_STRING,
'nvarchar2' => self::TYPE_STRING,
'clob' => self::TYPE_TEXT,
'nclob' => self::TYPE_TEXT,
'blob' => self::TYPE_BINARY,
'bfile' => self::TYPE_BINARY,
'long raw' => self::TYPE_BINARY,
'raw' => self::TYPE_BINARY,
'number' => self::TYPE_DECIMAL,
'binary_float' => self::TYPE_FLOAT, // 32 bit
'binary_double' => self::TYPE_DOUBLE, // 64 bit
'float' => self::TYPE_DOUBLE, // 126 bit
darkdef marked this conversation as resolved.
Show resolved Hide resolved
'timestamp' => self::TYPE_TIMESTAMP,
'timestamp with time zone' => self::TYPE_TIMESTAMP,
'timestamp with local time zone' => self::TYPE_TIMESTAMP,
'date' => self::TYPE_DATE,
'interval day to second' => self::TYPE_TIME,

/** Deprecated */
'long' => self::TYPE_TEXT,
];

public function __construct(protected ConnectionInterface $db, SchemaCache $schemaCache, string $defaultSchema)
{
$this->defaultSchema = $defaultSchema;
Expand Down Expand Up @@ -614,18 +648,23 @@ public function findUniqueIndexes(TableSchemaInterface $table): array
*/
private function extractColumnType(ColumnSchemaInterface $column): string
{
$dbType = explode('(', (string) $column->getDbType(), 2)[0];

return match ($dbType) {
'FLOAT', 'DOUBLE' => self::TYPE_DOUBLE,
'NUMBER' => $column->getScale() === null || $column->getScale() > 0
? self::TYPE_DECIMAL
: self::TYPE_INTEGER,
'BLOB' => self::TYPE_BINARY,
'CLOB' => self::TYPE_TEXT,
'TIMESTAMP' => self::TYPE_TIMESTAMP,
default => self::TYPE_STRING,
};
$dbType = strtolower((string) $column->getDbType());

if ($dbType === 'number') {
return match ($column->getScale()) {
null => self::TYPE_DOUBLE,
0 => self::TYPE_INTEGER,
default => self::TYPE_DECIMAL,
};
}

$dbType = preg_replace('/\([^)]+\)/', '', $dbType);

if ($dbType === 'interval day to second' && $column->getPrecision() > 0) {
return self::TYPE_STRING;
}

return $this->typeMap[$dbType] ?? self::TYPE_STRING;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/ColumnSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function testPhpTypeCast(): void
$this->assertSame(1.234, $floatColPhpType);
$this->assertSame("\x10\x11\x12", stream_get_contents($blobColPhpType));
$this->assertEquals(false, $boolColPhpType);
$this->assertEquals(0b0110_0110, $bitColPhpType);
$this->assertSame(0b0110_0110, $bitColPhpType);

$db->close();
}
Expand Down
48 changes: 37 additions & 11 deletions tests/Provider/SchemaProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static function columns(): array
'defaultValue' => 1,
],
'char_col' => [
'type' => 'string',
'type' => 'char',
'dbType' => 'CHAR',
'phpType' => 'string',
'primaryKey' => false,
Expand Down Expand Up @@ -184,8 +184,34 @@ public static function columns(): array
'scale' => 6,
'defaultValue' => "to_timestamp('2002-01-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')",
],
'bool_col' => [
'time_col' => [
'type' => 'time',
'dbType' => 'INTERVAL DAY(0) TO SECOND(0)',
'phpType' => 'string',
'primaryKey' => false,
'allowNull' => true,
'autoIncrement' => false,
'enumValues' => null,
'size' => 11,
'precision' => 0,
'scale' => 0,
'defaultValue' => "INTERVAL '0 10:33:21' DAY(0) TO SECOND(0)",
],
'interval_day_col' => [
'type' => 'string',
'dbType' => 'INTERVAL DAY(1) TO SECOND(0)',
'phpType' => 'string',
'primaryKey' => false,
'allowNull' => true,
'autoIncrement' => false,
'enumValues' => null,
'size' => 11,
'precision' => 1,
'scale' => 0,
'defaultValue' => "INTERVAL '2 04:56:12' DAY(1) TO SECOND(0)",
],
'bool_col' => [
'type' => 'char',
'dbType' => 'CHAR',
'phpType' => 'string',
'primaryKey' => false,
Expand All @@ -198,7 +224,7 @@ public static function columns(): array
'defaultValue' => null,
],
'bool_col2' => [
'type' => 'string',
'type' => 'char',
'dbType' => 'CHAR',
'phpType' => 'string',
'primaryKey' => false,
Expand All @@ -224,17 +250,17 @@ public static function columns(): array
'defaultValue' => new Expression('CURRENT_TIMESTAMP'),
],
'bit_col' => [
'type' => 'string',
'dbType' => 'CHAR',
'phpType' => 'string',
'type' => 'integer',
'dbType' => 'NUMBER',
'phpType' => 'integer',
'primaryKey' => false,
'allowNull' => false,
'autoIncrement' => false,
'enumValues' => null,
'size' => 3,
'precision' => null,
'scale' => null,
'defaultValue' => '130', // b'10000010'
'size' => 22,
'precision' => 3,
'scale' => 0,
'defaultValue' => 130, // b'10000010'
],
],
'type',
Expand Down Expand Up @@ -276,7 +302,7 @@ public static function columns(): array
public static function columnsTypeChar(): array
{
return [
['char_col', 'string', 100, 'CHAR'],
['char_col', 'char', 100, 'CHAR'],
['char_col2', 'string', 100, 'VARCHAR2'],
['char_col3', 'string', 4000, 'VARCHAR2'],
];
Expand Down
2 changes: 1 addition & 1 deletion tests/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ public function testCreateTable(): void
\t"name" VARCHAR2(255) NOT NULL,
\t"email" VARCHAR2(255) NOT NULL,
\t"status" NUMBER(10) NOT NULL,
\t"created_at" TIMESTAMP NOT NULL
\t"created_at" TIMESTAMP(0) NOT NULL
)
SQL,
$qb->createTable(
Expand Down
4 changes: 3 additions & 1 deletion tests/Support/Fixture/oci.sql
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,12 @@ CREATE TABLE "type" (
"blob_col" blob DEFAULT NULL,
"numeric_col" decimal(5,2) DEFAULT 33.22,
"timestamp_col" timestamp DEFAULT to_timestamp('2002-01-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss') NOT NULL,
"time_col" interval day (0) to second(0) DEFAULT INTERVAL '0 10:33:21' DAY(0) TO SECOND(0),
"interval_day_col" interval day (1) to second(0) DEFAULT INTERVAL '2 04:56:12' DAY(1) TO SECOND(0),
"bool_col" char NOT NULL check ("bool_col" in (0,1)),
"bool_col2" char DEFAULT 1 check("bool_col2" in (0,1)),
"ts_default" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
"bit_col" char(3) DEFAULT 130 NOT NULL
"bit_col" number(3) DEFAULT 130 NOT NULL
);

CREATE TABLE "bool_values" (
Expand Down