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

Support DateTime instances #308

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/ColumnSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function dbTypecast(mixed $value): mixed
? str_pad(decbin($value), (int) $this->getSize(), '0', STR_PAD_LEFT)
: (string) $value,

default => $this->typecast($value),
default => parent::dbTypecast($value),
};
}

Expand Down Expand Up @@ -191,4 +191,12 @@ public function sequenceName(string|null $sequenceName): void
{
$this->sequenceName = $sequenceName;
}

public function hasTimezone(): bool
{
$dbType = (string) $this->getDbType();

return str_ends_with($dbType, 'tz')
|| str_ends_with($dbType, ' with time zone');
}
}
28 changes: 13 additions & 15 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
* is_autoinc: bool,
* sequence_name: string|null,
* enum_values: string|null,
* numeric_precision: int|null,
* precision: int|null,
* numeric_scale: int|null,
* size: string|null,
* is_pkey: bool|null,
Expand Down Expand Up @@ -148,7 +148,7 @@ final class Schema extends AbstractPdoSchema
'timestamp' => self::TYPE_TIMESTAMP,
'timestamp with time zone' => self::TYPE_TIMESTAMP,
'timestamptz' => self::TYPE_TIMESTAMP,
'abstime' => self::TYPE_TIMESTAMP,
'abstime' => self::TYPE_TIMESTAMP, // shouldn't be used. it's pg internal!
'tsquery' => self::TYPE_STRING,
'tsvector' => self::TYPE_STRING,
'txid_snapshot' => self::TYPE_STRING,
Expand Down Expand Up @@ -699,10 +699,10 @@ protected function findColumns(TableSchemaInterface $table): bool
',')
ELSE NULL
END AS enum_values,
information_schema._pg_numeric_precision(
COALESCE(td.oid, tb.oid, a.atttypid),
information_schema._pg_truetypmod(a, t)
) AS numeric_precision,
COALESCE(
information_schema._pg_numeric_precision(COALESCE(td.oid, tb.oid, a.atttypid), information_schema._pg_truetypmod(a, t)),
information_schema._pg_datetime_precision(COALESCE(td.oid, tb.oid, a.atttypid), information_schema._pg_truetypmod(a, t))
) AS precision,
information_schema._pg_numeric_scale(
COALESCE(td.oid, tb.oid, a.atttypid),
information_schema._pg_truetypmod(a, t)
Expand Down Expand Up @@ -787,7 +787,7 @@ protected function loadColumnSchema(array $info): ColumnSchemaInterface
: null);
$column->unsigned(false); // has no meaning in PG
$column->primaryKey((bool) $info['is_pkey']);
$column->precision($info['numeric_precision']);
$column->precision($info['precision']);
$column->scale($info['numeric_scale']);
$column->size($info['size'] === null ? null : (int) $info['size']);
$column->dimension($info['dimension']);
Expand All @@ -813,6 +813,7 @@ protected function loadColumnSchema(array $info): ColumnSchemaInterface

$column->type($this->typeMap[(string) $column->getDbType()] ?? self::TYPE_STRING);
$column->phpType($this->getColumnPhpType($column));
$column->dateTimeFormat($this->getDateTimeFormat($column));
$column->defaultValue($this->normalizeDefaultValue($defaultValue, $column));

return $column;
Expand Down Expand Up @@ -856,16 +857,13 @@ private function normalizeDefaultValue(string|null $defaultValue, ColumnSchemaIn
return $defaultValue === 'true';
}

if (
in_array($column->getType(), [self::TYPE_TIMESTAMP, self::TYPE_DATE, self::TYPE_TIME], true)
&& in_array(strtoupper($defaultValue), ['NOW()', 'CURRENT_TIMESTAMP', 'CURRENT_DATE', 'CURRENT_TIME'], true)
) {
return new Expression($defaultValue);
}

$value = preg_replace("/^B?['(](.*?)[)'](?:::[^:]+)?$/s", '$1', $defaultValue);
$value = preg_replace("/^B?['(](.*?)[)'](?:::[^:]+)?$/s", '$1', $defaultValue, 1);
$value = str_replace("''", "'", $value);

if ($column->getDateTimeFormat() !== null) {
return date_create_immutable($value) ?: new Expression($defaultValue);
}

if ($column->getType() === self::TYPE_BINARY && str_starts_with($value, '\\x')) {
return hex2bin(substr($value, 2));
}
Expand Down
18 changes: 18 additions & 0 deletions tests/ColumnSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Yiisoft\Db\Pgsql\Tests;

use DateTimeImmutable;
use JsonException;
use PHPUnit\Framework\TestCase;
use Throwable;
Expand Down Expand Up @@ -48,6 +49,11 @@ public function testPhpTypeCast(): void
'char_col3' => null,
'float_col' => 1.234,
'blob_col' => "\x10\x11\x12",
'timestamp_col' => '2023-07-11 14:50:23',
'timestamp_col2' => new DateTimeImmutable('2023-07-11 14:50:23.123456 +02:00'),
'timestamptz_col' => new DateTimeImmutable('2023-07-11 14:50:23.12 -2:30'),
'date_col' => new DateTimeImmutable('2023-07-11'),
'time_col' => new DateTimeImmutable('14:50:23'),
'bool_col' => false,
'bit_col' => 0b0110_0100, // 100
'varbit_col' => 0b1_1100_1000, // 456
Expand All @@ -70,6 +76,12 @@ public function testPhpTypeCast(): void
$charColPhpTypeCast = $tableSchema->getColumn('char_col')?->phpTypecast($query['char_col']);
$floatColPhpTypeCast = $tableSchema->getColumn('float_col')?->phpTypecast($query['float_col']);
$blobColPhpTypeCast = $tableSchema->getColumn('blob_col')?->phpTypecast($query['blob_col']);
$timestampColPhpType = $tableSchema->getColumn('timestamp_col')?->phpTypecast($query['timestamp_col']);
$timestampCol2PhpType = $tableSchema->getColumn('timestamp_col2')?->phpTypecast($query['timestamp_col2']);
$timestamptzColPhpType = $tableSchema->getColumn('timestamptz_col')?->phpTypecast($query['timestamptz_col']);
$dateColPhpType = $tableSchema->getColumn('date_col')?->phpTypecast($query['date_col']);
$timeColPhpType = $tableSchema->getColumn('time_col')?->phpTypecast($query['time_col']);
$tsDefaultPhpType = $tableSchema->getColumn('ts_default')?->phpTypecast($query['ts_default']);
$boolColPhpTypeCast = $tableSchema->getColumn('bool_col')?->phpTypecast($query['bool_col']);
$bitColPhpTypeCast = $tableSchema->getColumn('bit_col')?->phpTypecast($query['bit_col']);
$varbitColPhpTypeCast = $tableSchema->getColumn('varbit_col')?->phpTypecast($query['varbit_col']);
Expand All @@ -86,6 +98,12 @@ public function testPhpTypeCast(): void
$this->assertSame(str_repeat('x', 100), $charColPhpTypeCast);
$this->assertSame(1.234, $floatColPhpTypeCast);
$this->assertSame("\x10\x11\x12", stream_get_contents($blobColPhpTypeCast));
$this->assertEquals(new DateTimeImmutable('2023-07-11 14:50:23'), $timestampColPhpType);
$this->assertEquals(new DateTimeImmutable('2023-07-11 14:50:23.123456 +02:00'), $timestampCol2PhpType);
$this->assertEquals(new DateTimeImmutable('2023-07-11 14:50:23.12 -2:30'), $timestamptzColPhpType);
$this->assertEquals(new DateTimeImmutable('2023-07-11'), $dateColPhpType);
$this->assertEquals(new DateTimeImmutable('14:50:23'), $timeColPhpType);
$this->assertInstanceOf(DateTimeImmutable::class, $tsDefaultPhpType);
$this->assertFalse($boolColPhpTypeCast);
$this->assertSame(0b0110_0100, $bitColPhpTypeCast);
$this->assertSame(0b1_1100_1000, $varbitColPhpTypeCast);
Expand Down
70 changes: 64 additions & 6 deletions tests/Provider/SchemaProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Yiisoft\Db\Pgsql\Tests\Provider;

use DateTimeImmutable;
use Yiisoft\Db\Expression\Expression;

final class SchemaProvider extends \Yiisoft\Db\Tests\Provider\SchemaProvider
Expand Down Expand Up @@ -169,18 +170,75 @@ public static function columns(): array
'scale' => 2,
'defaultValue' => 33.22,
],
'time' => [
'timestamp_col' => [
'type' => 'timestamp',
'dbType' => 'timestamp',
'phpType' => 'string',
'phpType' => 'DateTimeInterface',
'primaryKey' => false,
'allowNull' => false,
'autoIncrement' => false,
'enumValues' => null,
'size' => null,
'precision' => null,
'precision' => 0,
'scale' => null,
'defaultValue' => new DateTimeImmutable('2002-01-01 00:00:00'),
'dateTimeFormat' => 'Y-m-d H:i:s',
],
'timestamp_col2' => [
'type' => 'timestamp',
'dbType' => 'timestamp',
'phpType' => 'DateTimeInterface',
'primaryKey' => false,
'allowNull' => true,
'autoIncrement' => false,
'enumValues' => null,
'size' => null,
'precision' => 6,
'scale' => null,
'defaultValue' => null,
'dateTimeFormat' => 'Y-m-d H:i:s.u',
],
'timestamptz_col' => [
'type' => 'timestamp',
'dbType' => 'timestamptz',
'phpType' => 'DateTimeInterface',
'primaryKey' => false,
'allowNull' => false,
'autoIncrement' => false,
'enumValues' => null,
'size' => null,
'precision' => 2,
'scale' => null,
'defaultValue' => new DateTimeImmutable('2023-06-11 15:24:11.12+0200'),
'dateTimeFormat' => 'Y-m-d H:i:s.vP',
],
'date_col' => [
'type' => 'date',
'dbType' => 'date',
'phpType' => 'DateTimeInterface',
'primaryKey' => false,
'allowNull' => false,
'autoIncrement' => false,
'enumValues' => null,
'size' => null,
'precision' => 0,
'scale' => null,
'defaultValue' => '2002-01-01 00:00:00',
'defaultValue' => new DateTimeImmutable('2023-06-11'),
'dateTimeFormat' => 'Y-m-d',
],
'time_col' => [
'type' => 'time',
'dbType' => 'time',
'phpType' => 'DateTimeInterface',
'primaryKey' => false,
'allowNull' => false,
'autoIncrement' => false,
'enumValues' => null,
'size' => null,
'precision' => 6,
'scale' => null,
'defaultValue' => new DateTimeImmutable('15:24:11.123456'),
'dateTimeFormat' => 'H:i:s.u',
],
'bool_col' => [
'type' => 'boolean',
Expand Down Expand Up @@ -211,13 +269,13 @@ public static function columns(): array
'ts_default' => [
'type' => 'timestamp',
'dbType' => 'timestamp',
'phpType' => 'string',
'phpType' => 'DateTimeInterface',
'primaryKey' => false,
'allowNull' => false,
'autoIncrement' => false,
'enumValues' => null,
'size' => null,
'precision' => null,
'precision' => 6,
'scale' => null,
'defaultValue' => new Expression('now()'),
],
Expand Down
6 changes: 5 additions & 1 deletion tests/Support/Fixture/pgsql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ CREATE TABLE "type" (
float_col2 double precision DEFAULT '1.23',
blob_col bytea DEFAULT 'a binary value',
numeric_col decimal(5,2) DEFAULT '33.22',
time timestamp NOT NULL DEFAULT '2002-01-01 00:00:00',
timestamp_col timestamp(0) NOT NULL DEFAULT '2002-01-01 00:00:00',
timestamp_col2 timestamp,
timestamptz_col timestamptz(2) NOT NULL DEFAULT '2023-06-11 15:24:11.12+0200',
date_col date NOT NULL DEFAULT '2023-06-11',
time_col time NOT NULL DEFAULT '15:24:11.123456',
bool_col boolean NOT NULL,
bool_col2 boolean DEFAULT TRUE,
ts_default TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Expand Down