Skip to content

Commit 5e7a0c4

Browse files
authored
Merge pull request #7195 from morozov/default-expression
Introduce DefaultExpression
2 parents 574651e + 42e278e commit 5e7a0c4

File tree

10 files changed

+132
-37
lines changed

10 files changed

+132
-37
lines changed

UPGRADE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ awareness about deprecated code.
88

99
# Upgrade to 4.4
1010

11+
## Deprecated using current date, time and timestamp SQL expressions as default values
12+
13+
Using SQL expressions for the current date, time, or timestamp as column default values is deprecated. Instead, use an
14+
instance of the `CurrentDate`, `CurrentTime`, or `CurrentTimestamp` class, respectively.
15+
1116
## Deprecated `Schema::createNamespace()` and the `$namespaces` constructor parameter
1217

1318
The `Schema::createNamespace()` method and the `$namespaces` constructor parameter have been deprecated. The schema

src/Platforms/AbstractPlatform.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
2020
use Doctrine\DBAL\Schema\AbstractSchemaManager;
2121
use Doctrine\DBAL\Schema\Column;
22+
use Doctrine\DBAL\Schema\DefaultExpression;
2223
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
2324
use Doctrine\DBAL\Schema\Identifier;
2425
use Doctrine\DBAL\Schema\Index;
@@ -1543,6 +1544,10 @@ public function getDefaultValueDeclarationSQL(array $column): string
15431544

15441545
$default = $column['default'];
15451546

1547+
if ($default instanceof DefaultExpression) {
1548+
return ' DEFAULT ' . $default->toSQL($this);
1549+
}
1550+
15461551
if (! isset($column['type'])) {
15471552
return " DEFAULT '" . $default . "'";
15481553
}
@@ -1554,14 +1559,35 @@ public function getDefaultValueDeclarationSQL(array $column): string
15541559
}
15551560

15561561
if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
1562+
Deprecation::trigger(
1563+
'doctrine/dbal',
1564+
'https://github.com/doctrine/dbal/pull/7195',
1565+
'Using "%s" as a column default value is deprecated. Use a CurrentTimestamp instance instead.',
1566+
$default,
1567+
);
1568+
15571569
return ' DEFAULT ' . $this->getCurrentTimestampSQL();
15581570
}
15591571

15601572
if ($type instanceof Types\PhpTimeMappingType && $default === $this->getCurrentTimeSQL()) {
1573+
Deprecation::trigger(
1574+
'doctrine/dbal',
1575+
'https://github.com/doctrine/dbal/pull/7195',
1576+
'Using "%s" as a column default value is deprecated. Use a CurrentTime instance instead.',
1577+
$default,
1578+
);
1579+
15611580
return ' DEFAULT ' . $this->getCurrentTimeSQL();
15621581
}
15631582

15641583
if ($type instanceof Types\PhpDateMappingType && $default === $this->getCurrentDateSQL()) {
1584+
Deprecation::trigger(
1585+
'doctrine/dbal',
1586+
'https://github.com/doctrine/dbal/pull/7195',
1587+
'Using "%s" as a column default value is deprecated. Use a CurrentDate instance instead.',
1588+
$default,
1589+
);
1590+
15651591
return ' DEFAULT ' . $this->getCurrentDateSQL();
15661592
}
15671593

src/Schema/DefaultExpression.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema;
6+
7+
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
9+
/**
10+
* Represents the default expression of a column.
11+
*/
12+
interface DefaultExpression
13+
{
14+
/**
15+
* Returns the SQL representation of the default expression for the given platform.
16+
*/
17+
public function toSQL(AbstractPlatform $platform): string;
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema\DefaultExpression;
6+
7+
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
use Doctrine\DBAL\Schema\DefaultExpression;
9+
10+
/**
11+
* Represents the "current date" default expression.
12+
*/
13+
final readonly class CurrentDate implements DefaultExpression
14+
{
15+
public function toSQL(AbstractPlatform $platform): string
16+
{
17+
return $platform->getCurrentDateSQL();
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema\DefaultExpression;
6+
7+
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
use Doctrine\DBAL\Schema\DefaultExpression;
9+
10+
/**
11+
* Represents the "current time" default expression.
12+
*/
13+
final readonly class CurrentTime implements DefaultExpression
14+
{
15+
public function toSQL(AbstractPlatform $platform): string
16+
{
17+
return $platform->getCurrentTimeSQL();
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema\DefaultExpression;
6+
7+
use Doctrine\DBAL\Platforms\AbstractPlatform;
8+
use Doctrine\DBAL\Schema\DefaultExpression;
9+
10+
/**
11+
* Represents the "current timestamp" default expression.
12+
*/
13+
final readonly class CurrentTimestamp implements DefaultExpression
14+
{
15+
public function toSQL(AbstractPlatform $platform): string
16+
{
17+
return $platform->getCurrentTimestampSQL();
18+
}
19+
}

tests/Functional/Platform/DefaultExpressionTest.php

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
namespace Doctrine\DBAL\Tests\Functional\Platform;
66

77
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
8-
use Doctrine\DBAL\Platforms\AbstractPlatform;
98
use Doctrine\DBAL\Platforms\OraclePlatform;
109
use Doctrine\DBAL\Schema\Column;
10+
use Doctrine\DBAL\Schema\DefaultExpression;
11+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentDate;
12+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentTime;
13+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp;
1114
use Doctrine\DBAL\Schema\Table;
1215
use Doctrine\DBAL\Tests\FunctionalTestCase;
1316
use Doctrine\DBAL\Types\Types;
@@ -24,9 +27,7 @@ public function testCurrentDate(): void
2427
self::markTestSkipped('Not supported on MySQL');
2528
}
2629

27-
$this->assertDefaultExpression(Types::DATE_MUTABLE, static function (AbstractPlatform $platform): string {
28-
return $platform->getCurrentDateSQL();
29-
});
30+
$this->assertDefaultExpression(Types::DATE_MUTABLE, new CurrentDate());
3031
}
3132

3233
public function testCurrentTime(): void
@@ -41,23 +42,16 @@ public function testCurrentTime(): void
4142
self::markTestSkipped('Not supported on Oracle');
4243
}
4344

44-
$this->assertDefaultExpression(Types::TIME_MUTABLE, static function (AbstractPlatform $platform): string {
45-
return $platform->getCurrentTimeSQL();
46-
});
45+
$this->assertDefaultExpression(Types::TIME_MUTABLE, new CurrentTime());
4746
}
4847

4948
public function testCurrentTimestamp(): void
5049
{
51-
$this->assertDefaultExpression(Types::DATETIME_MUTABLE, static function (AbstractPlatform $platform): string {
52-
return $platform->getCurrentTimestampSQL();
53-
});
50+
$this->assertDefaultExpression(Types::DATETIME_MUTABLE, new CurrentTimestamp());
5451
}
5552

56-
private function assertDefaultExpression(string $typeName, callable $expression): void
53+
private function assertDefaultExpression(string $typeName, DefaultExpression $expression): void
5754
{
58-
$platform = $this->connection->getDatabasePlatform();
59-
$defaultSql = $expression($platform, $this);
60-
6155
$table = Table::editor()
6256
->setUnquotedName('default_expr_test')
6357
->setColumns(
@@ -68,17 +62,19 @@ private function assertDefaultExpression(string $typeName, callable $expression)
6862
Column::editor()
6963
->setUnquotedName('default_value')
7064
->setTypeName($typeName)
71-
->setDefaultValue($defaultSql)
65+
->setDefaultValue($expression)
7266
->create(),
7367
)
7468
->create();
7569

7670
$this->dropAndCreateTable($table);
7771

72+
$platform = $this->connection->getDatabasePlatform();
73+
7874
$this->connection->executeStatement(
7975
sprintf(
8076
'INSERT INTO default_expr_test (actual_value) VALUES (%s)',
81-
$defaultSql,
77+
$expression->toSQL($platform),
8278
),
8379
);
8480

tests/Functional/Schema/MySQLSchemaManagerTest.php

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
namespace Doctrine\DBAL\Tests\Functional\Schema;
66

7-
use DateTime;
87
use Doctrine\DBAL\DriverManager;
98
use Doctrine\DBAL\Exception\DatabaseRequired;
109
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
1110
use Doctrine\DBAL\Platforms\AbstractPlatform;
1211
use Doctrine\DBAL\Platforms\MariaDBPlatform;
1312
use Doctrine\DBAL\Schema\Column;
1413
use Doctrine\DBAL\Schema\ColumnEditor;
14+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp;
1515
use Doctrine\DBAL\Schema\Index;
1616
use Doctrine\DBAL\Schema\Index\IndexedColumn;
1717
use Doctrine\DBAL\Schema\Index\IndexType;
@@ -595,29 +595,30 @@ public function testColumnDefaultCurrentTimestamp(): void
595595
{
596596
$platform = $this->connection->getDatabasePlatform();
597597

598-
$currentTimeStampSql = $platform->getCurrentTimestampSQL();
598+
$currentTimeStamp = new CurrentTimestamp();
599+
$currentTimeStampSQL = $currentTimeStamp->toSQL($platform);
599600

600601
$table = Table::editor()
601602
->setUnquotedName('test_column_defaults_current_timestamp')
602603
->setColumns(
603604
Column::editor()
604605
->setUnquotedName('col_datetime')
605606
->setTypeName(Types::DATETIME_MUTABLE)
606-
->setDefaultValue($currentTimeStampSql)
607+
->setDefaultValue($currentTimeStamp)
607608
->create(),
608609
Column::editor()
609610
->setUnquotedName('col_datetime_nullable')
610611
->setTypeName(Types::DATETIME_MUTABLE)
611-
->setDefaultValue($currentTimeStampSql)
612+
->setDefaultValue($currentTimeStamp)
612613
->create(),
613614
)
614615
->create();
615616

616617
$this->dropAndCreateTable($table);
617618

618619
$onlineTable = $this->schemaManager->introspectTableByUnquotedName('test_column_defaults_current_timestamp');
619-
self::assertSame($currentTimeStampSql, $onlineTable->getColumn('col_datetime')->getDefault());
620-
self::assertSame($currentTimeStampSql, $onlineTable->getColumn('col_datetime_nullable')->getDefault());
620+
self::assertSame($currentTimeStampSQL, $onlineTable->getColumn('col_datetime')->getDefault());
621+
self::assertSame($currentTimeStampSQL, $onlineTable->getColumn('col_datetime_nullable')->getDefault());
621622

622623
self::assertTrue(
623624
$this->schemaManager
@@ -629,16 +630,9 @@ public function testColumnDefaultCurrentTimestamp(): void
629630

630631
public function testColumnDefaultsAreValid(): void
631632
{
632-
$currentTimeStampSql = $this->connection->getDatabasePlatform()->getCurrentTimestampSQL();
633-
634633
$table = Table::editor()
635634
->setUnquotedName('test_column_defaults_are_valid')
636635
->setColumns(
637-
Column::editor()
638-
->setUnquotedName('col_datetime')
639-
->setTypeName(Types::DATETIME_MUTABLE)
640-
->setDefaultValue($currentTimeStampSql)
641-
->create(),
642636
Column::editor()
643637
->setUnquotedName('col_datetime_null')
644638
->setTypeName(Types::DATETIME_MUTABLE)
@@ -681,19 +675,15 @@ public function testColumnDefaultsAreValid(): void
681675
'INSERT INTO test_column_defaults_are_valid () VALUES()',
682676
);
683677

684-
$row = $this->connection->fetchAssociative(
685-
'SELECT *, DATEDIFF(CURRENT_TIMESTAMP(), col_datetime) as diff_seconds FROM test_column_defaults_are_valid',
686-
);
678+
$row = $this->connection->fetchAssociative('SELECT * FROM test_column_defaults_are_valid');
687679
self::assertNotFalse($row);
688680

689-
self::assertInstanceOf(DateTime::class, DateTime::createFromFormat('Y-m-d H:i:s', $row['col_datetime']));
690681
self::assertNull($row['col_datetime_null']);
691682
self::assertSame('2012-12-12', $row['col_date']);
692683
self::assertSame('A', $row['col_string']);
693684
self::assertEquals(1, $row['col_int']);
694685
self::assertEquals(-1, $row['col_neg_int']);
695686
self::assertEquals('-2.300', $row['col_decimal']);
696-
self::assertLessThan(5, $row['diff_seconds']);
697687
}
698688

699689
/**

tests/Platforms/AbstractPlatformTestCase.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use Doctrine\DBAL\Schema\ColumnDiff;
1313
use Doctrine\DBAL\Schema\Comparator;
1414
use Doctrine\DBAL\Schema\ComparatorConfig;
15+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentDate;
16+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp;
1517
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
1618
use Doctrine\DBAL\Schema\Index;
1719
use Doctrine\DBAL\Schema\Name\Identifier;
@@ -338,7 +340,7 @@ public function testGetDefaultValueDeclarationSQLDateTime(): void
338340
' DEFAULT ' . $this->platform->getCurrentTimestampSQL(),
339341
$this->platform->getDefaultValueDeclarationSQL([
340342
'type' => Type::getType($type),
341-
'default' => $this->platform->getCurrentTimestampSQL(),
343+
'default' => new CurrentTimestamp(),
342344
]),
343345
);
344346
}
@@ -365,7 +367,7 @@ public function testGetDefaultValueDeclarationSQLForDateType(): void
365367
' DEFAULT ' . $currentDateSql,
366368
$this->platform->getDefaultValueDeclarationSQL([
367369
'type' => Type::getType($type),
368-
'default' => $currentDateSql,
370+
'default' => new CurrentDate(),
369371
]),
370372
);
371373
}

tests/Platforms/SQLServerPlatformTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Doctrine\DBAL\Schema\ColumnDiff;
1515
use Doctrine\DBAL\Schema\Comparator;
1616
use Doctrine\DBAL\Schema\ComparatorConfig;
17+
use Doctrine\DBAL\Schema\DefaultExpression\CurrentDate;
1718
use Doctrine\DBAL\Schema\Index;
1819
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
1920
use Doctrine\DBAL\Schema\Sequence;
@@ -1087,7 +1088,7 @@ public function testGetDefaultValueDeclarationSQLForDateType(): void
10871088
' DEFAULT CONVERT(date, GETDATE())',
10881089
$this->platform->getDefaultValueDeclarationSQL([
10891090
'type' => Type::getType($type),
1090-
'default' => $currentDateSql,
1091+
'default' => new CurrentDate(),
10911092
]),
10921093
);
10931094
}

0 commit comments

Comments
 (0)