Skip to content
Open
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
16 changes: 15 additions & 1 deletion src/Driver/MySQL/Schema/MySQLColumn.php
Original file line number Diff line number Diff line change
Expand Up @@ -514,13 +514,27 @@ protected function formatDatetime(
string $type,
string|int|\DateTimeInterface $value,
): \DateTimeInterface|FragmentInterface|string {
if ($value === 'current_timestamp()') {
// MySQL reflects the default as CURRENT_TIMESTAMP(6) on a datetime(6), MariaDB as current_timestamp(6).
if (\is_string($value) && \preg_match('/^current_timestamp(?:\(\d*\))?$/i', $value) === 1) {
$value = self::DATETIME_NOW;
}

return parent::formatDatetime($type, $value);
}

#[\Override]
protected function quoteDefault(DriverInterface $driver): string
{
$defaultValue = parent::quoteDefault($driver);

// MySQL requires CURRENT_TIMESTAMP to carry the column's fractional seconds precision.
if ($this->size > 0 && $defaultValue === self::DATETIME_NOW && \in_array($this->type, ['datetime', 'timestamp'], true)) {
return self::DATETIME_NOW . "({$this->size})";
}

return $defaultValue;
}

private function sqlStatementInteger(DriverInterface $driver): string
{
return \sprintf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// phpcs:ignore
use Cycle\Database\Driver\Handler;
use Cycle\Database\Exception\HandlerException;
use Cycle\Database\Injection\Fragment;
use Cycle\Database\Schema\AbstractColumn;
use Cycle\Database\Tests\Functional\Driver\Common\Schema\DatetimeColumnTest as CommonClass;

/**
Expand All @@ -17,6 +19,13 @@ class DatetimeColumnTest extends CommonClass
{
public const DRIVER = 'mysql';

public static function fractionalCurrentTimestampProvider(): iterable
{
yield 'datetime(6)' => ['datetime', 6];
yield 'datetime(3)' => ['datetime', 3];
yield 'timestamp(6)' => ['timestamp', 6];
}

public function testTimestampDatetimeZero(): void
{
$this->expectExceptionMessage(
Expand Down Expand Up @@ -68,4 +77,49 @@ public function testDatetimeWithSize(): void
$this->assertSame('datetime', $schema->column('datetime_data')->getInternalType());
$this->assertSame(3, $schema->column('datetime_data')->getSize());
}

/**
* @dataProvider fractionalCurrentTimestampProvider
*/
public function testCurrentTimestampWithSize(string $type, int $size): void
{
$schema = $this->schema('table');
$schema->primary('id');
$schema->$type('target', size: $size)->nullable(false)->defaultValue(AbstractColumn::DATETIME_NOW);
$schema->save(Handler::DO_ALL);

$this->assertSameAsInDB($schema);

$saved = $this->schema('table');
$this->assertSame($size, $saved->column('target')->getSize());
$this->assertEquals(
new Fragment(AbstractColumn::DATETIME_NOW),
$saved->column('target')->getDefaultValue(),
);

$saved->$type('target', size: $size)->nullable(false)->defaultValue(AbstractColumn::DATETIME_NOW);
$this->assertFalse($saved->getComparator()->hasChanges());

$this->database->table('table')->insertOne(['id' => 1]);
$this->assertNotNull($this->database->table('table')->select('target')->fetchAll()[0]['target']);
}

public function testExistingCurrentTimestampWithSizeIsReflected(): void
{
$this->database->execute(
'CREATE TABLE `table` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`target` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
)',
);

$schema = $this->schema('table');
$this->assertEquals(
new Fragment(AbstractColumn::DATETIME_NOW),
$schema->column('target')->getDefaultValue(),
);

$schema->datetime('target', size: 6)->nullable(false)->defaultValue(AbstractColumn::DATETIME_NOW);
$this->assertFalse($schema->getComparator()->hasChanges());
}
}
38 changes: 38 additions & 0 deletions tests/Database/Unit/Driver/MySQL/Schema/MySQLColumnTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Cycle\Database\Tests\Unit\Driver\MySQL\Schema;

use Cycle\Database\Driver\MySQL\Schema\MySQLColumn;
use Cycle\Database\Injection\Fragment;
use PHPUnit\Framework\TestCase;

final class MySQLColumnTest extends TestCase
{
public static function currentTimestampDefaultProvider(): iterable
{
yield 'MySQL' => ['datetime', 'CURRENT_TIMESTAMP'];
yield 'MySQL with fsp' => ['datetime(6)', 'CURRENT_TIMESTAMP(6)'];
yield 'MySQL timestamp with fsp' => ['timestamp(3)', 'CURRENT_TIMESTAMP(3)'];
yield 'MariaDB' => ['datetime', 'current_timestamp()'];
yield 'MariaDB with fsp' => ['datetime(6)', 'current_timestamp(6)'];
}

/**
* @dataProvider currentTimestampDefaultProvider
*/
public function testReflectedCurrentTimestampDefault(string $type, string $default): void
{
$column = MySQLColumn::createInstance('table', [
'Field' => 'target',
'Type' => $type,
'Comment' => '',
'Null' => 'NO',
'Default' => $default,
'Extra' => 'DEFAULT_GENERATED',
]);

$this->assertEquals(new Fragment(MySQLColumn::DATETIME_NOW), $column->getDefaultValue());
}
}
Loading