Skip to content
Merged
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: 14 additions & 2 deletions src/Db/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -1040,10 +1040,22 @@ public function castToBool($value): mixed
*/
protected function getDefaultValueDefinition(mixed $default, ?string $columnType = null): string
{
$datetimeTypes = [
static::PHINX_TYPE_DATETIME,
static::PHINX_TYPE_TIMESTAMP,
static::PHINX_TYPE_TIME,
static::PHINX_TYPE_DATE,
];

if ($default instanceof Literal) {
$default = (string)$default;
} elseif (is_string($default) && stripos($default, 'CURRENT_TIMESTAMP') !== 0) {
// Ensure a defaults of CURRENT_TIMESTAMP(3) is not quoted.
} elseif (is_string($default) && stripos($default, 'CURRENT_TIMESTAMP') === 0) {
// Only skip quoting CURRENT_TIMESTAMP for datetime-related column types.
// For other types (like string), it should be quoted as a literal string value.
if (!in_array($columnType, $datetimeTypes, true)) {
$default = $this->quoteString($default);
}
} elseif (is_string($default)) {
$default = $this->quoteString($default);
} elseif (is_bool($default)) {
$default = $this->castToBool($default);
Expand Down
3 changes: 2 additions & 1 deletion src/Db/Adapter/MysqlAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,8 @@ static function ($value) {
$extra = ' ' . implode(' ', $extras);

if (($row['Default'] !== null)) {
$extra .= $this->getDefaultValueDefinition($row['Default']);
$phinxTypeInfo = $this->getPhinxType($row['Type']);
$extra .= $this->getDefaultValueDefinition($row['Default'], $phinxTypeInfo['name']);
}
$definition = $row['Type'] . ' ' . $null . $extra . $comment;

Expand Down
129 changes: 129 additions & 0 deletions tests/TestCase/Db/Adapter/AbstractAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Migrations\Test\Db\Adapter;

use Migrations\Db\Adapter\AbstractAdapter;
use Migrations\Db\Literal;
use Migrations\Test\TestCase\Db\Adapter\DefaultAdapterTrait;
use PDOException;
use Phinx\Config\Config;
Expand Down Expand Up @@ -144,4 +145,132 @@ public function fetchAll(string $sql): array

$this->assertEquals([], $adapter->getVersionLog());
}

/**
* Test CURRENT_TIMESTAMP default value quoting behavior (bug #1891).
*
* This test verifies that CURRENT_TIMESTAMP is only treated as a SQL function
* for datetime-related column types. For other column types like string,
* 'CURRENT_TIMESTAMP' should be quoted as a literal string value.
*
* @see https://github.com/cakephp/phinx/issues/1891
*/
#[DataProvider('currentTimestampDefaultValueProvider')]
public function testCurrentTimestampDefaultValueQuoting($default, $columnType, $expected)
{
$adapter = new class (['version_order' => Config::VERSION_ORDER_CREATION_TIME]) extends AbstractAdapter {
use DefaultAdapterTrait;

public function quoteString(string $value): string
{
return "'" . $value . "'";
}

// Expose the protected method for testing
public function testGetDefaultValueDefinition(mixed $default, ?string $columnType = null): string
{
return $this->getDefaultValueDefinition($default, $columnType);
}
};

$actual = $adapter->testGetDefaultValueDefinition($default, $columnType);
$this->assertEquals($expected, $actual);
}

/**
* Data provider for CURRENT_TIMESTAMP quoting test.
*
* @return array
*/
public static function currentTimestampDefaultValueProvider(): array
{
return [
// CURRENT_TIMESTAMP on datetime types should NOT be quoted
'CURRENT_TIMESTAMP on datetime' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_DATETIME,
' DEFAULT CURRENT_TIMESTAMP',
],
'CURRENT_TIMESTAMP on timestamp' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_TIMESTAMP,
' DEFAULT CURRENT_TIMESTAMP',
],
'CURRENT_TIMESTAMP on time' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_TIME,
' DEFAULT CURRENT_TIMESTAMP',
],
'CURRENT_TIMESTAMP on date' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_DATE,
' DEFAULT CURRENT_TIMESTAMP',
],
'CURRENT_TIMESTAMP(3) on datetime' => [
'CURRENT_TIMESTAMP(3)',
AbstractAdapter::PHINX_TYPE_DATETIME,
' DEFAULT CURRENT_TIMESTAMP(3)',
],

// CURRENT_TIMESTAMP on non-datetime types SHOULD be quoted (bug #1891)
'CURRENT_TIMESTAMP on string should be quoted' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_STRING,
" DEFAULT 'CURRENT_TIMESTAMP'",
],
'CURRENT_TIMESTAMP on text should be quoted' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_TEXT,
" DEFAULT 'CURRENT_TIMESTAMP'",
],
'CURRENT_TIMESTAMP on char should be quoted' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_CHAR,
" DEFAULT 'CURRENT_TIMESTAMP'",
],
'CURRENT_TIMESTAMP on integer should be quoted' => [
'CURRENT_TIMESTAMP',
AbstractAdapter::PHINX_TYPE_INTEGER,
" DEFAULT 'CURRENT_TIMESTAMP'",
],

// Regular string defaults should always be quoted
'Regular string default' => [
'default_value',
AbstractAdapter::PHINX_TYPE_STRING,
" DEFAULT 'default_value'",
],
'Regular string on datetime' => [
'some_string',
AbstractAdapter::PHINX_TYPE_DATETIME,
" DEFAULT 'some_string'",
],

// Literal values should not be quoted
'Literal value' => [
Literal::from('NOW()'),
AbstractAdapter::PHINX_TYPE_DATETIME,
' DEFAULT NOW()',
],

// Boolean defaults
'Boolean true' => [
true,
AbstractAdapter::PHINX_TYPE_BOOLEAN,
' DEFAULT 1',
],
'Boolean false' => [
false,
AbstractAdapter::PHINX_TYPE_BOOLEAN,
' DEFAULT 0',
],

// Null columnType (should quote CURRENT_TIMESTAMP when type is unknown)
'CURRENT_TIMESTAMP with null column type' => [
'CURRENT_TIMESTAMP',
null,
" DEFAULT 'CURRENT_TIMESTAMP'",
],
];
}
}
Loading