Веб

Yii1: исправить ошибку DATETIME с CURRENT_TIMESTAMP

Решение ошибки Incorrect datetime value 'current_timestamp()' в Yii1 при вставке в DATETIME. Не присваивайте строку — используйте CDbExpression('NOW()') или DEFAULT в схеме.

В Yii1 при добавлении записи без указания значений полей типа DATETIME вставляется строка ‘current_timestamp()’ вместо актуального значения, что вызывает ошибку: CDbCommand не удалось исполнить SQL-запрос: SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: ‘current_timestamp()’. Как исправить эту проблему?

DDL полей:

date_create DATETIME DEFAULT CURRENT_TIMESTAMP,
date_update DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

Проект унаследован, код писал не я.

В Yii1 проблема с полями DATETIME возникает потому, что приложение подставляет строку ‘current_timestamp()’ (в кавычках) вместо SQL‑выражения, из‑за чего MySQL бросает ошибку 1292. Исправление — не передавать строковый литерал и/или использовать CDbExpression (например, new CDbExpression(‘NOW()’) или new CDbExpression(‘CURRENT_TIMESTAMP’)) в правилах или beforeSave, либо позволить СУБД проставлять DEFAULT CURRENT_TIMESTAMP корректно на уровне схемы. После этого INSERT будет содержать CURRENT_TIMESTAMP без кавычек и ошибка исчезнет.


Содержание


Причина: почему в Yii1 DATETIME получает ‘current_timestamp()’

Коротко: причина в том, что в INSERT в базу попадает строка ‘current_timestamp()’ (с кавычками), а не SQL‑функция CURRENT_TIMESTAMP. Почему так происходит? Чаще всего потому, что в коде модели или в правилах/контроллере атрибуту присвоили строковое значение “current_timestamp()” или генерировали DDL/миграцию, где DEFAULT указан в кавычках. Тогда PDO/Yii привязывают значение как строку и MySQL пытается распарсить её как дату — получает ошибку SQLSTATE[22007]: 1292.

Такое поведение обсуждалось в репозитории Yii (рекомендация — использовать CDbExpression для выражений) — см. обсуждение на GitHub: https://github.com/yiisoft/yii/issues/2993. Кроме того, есть багрепорты и примеры, когда неверно сгенерирован DDL (кавычки вокруг current_timestamp()), что тоже даёт похожую ошибку (см. https://bugs.mysql.com/bug.php?id=95466 и https://github.com/phpmyadmin/phpmyadmin/issues/13999).

Что делать сначала? Проверьте код на наличие буквальной строки “current_timestamp” и вывод SQL (ниже — как отладить).


Исправление в коде: используем CDbExpression в Yii1 для DATETIME

Есть два безопасных подхода — дать СУБД проставлять значение по умолчанию или явно подставлять SQL‑выражение из приложения (без кавычек).

  1. Не назначать значение в модели, чтобы сработал DEFAULT базы.
  • Убедитесь, что в PHP вы не присваиваете полю строку ‘current_timestamp()’.
  • Если код раньше выставлял такое значение (в контроллере/правилах/миграциях) — удалите это присвоение.
  1. Если хотите ставить метку времени из приложения — используйте CDbExpression, тогда Yii не обернёт выражение в кавычки и в SQL попадёт корректное выражение (NOW() или CURRENT_TIMESTAMP). Пример решения рекомендуется в сообществе и в вики Yii: https://www.yiiframework.com/wiki/10/how-to-automate-timestamps-in-activerecord-models и на StackOverflow (см. примеры: https://stackoverflow.com/questions/31743439/insert-automatically-datetime-in-yii).

Главная идея: вместо

  • $model->date_create = ‘current_timestamp()’;

поставить

  • $model->date_create = new CDbExpression(‘NOW()’);

или использовать правило default с CDbExpression (см. снизу).


Примеры: rules() и beforeSave() в Yii1

Пример через правила модели (recommended):

php
public function rules()
{
 return array(
 // при создании выставляем NOW()
 array('date_create, date_update', 'default',
 'value' => new CDbExpression('NOW()'),
 'setOnEmpty' => false,
 'on' => 'insert'),
 // при обновлении обновляем date_update
 array('date_update', 'default',
 'value' => new CDbExpression('NOW()'),
 'setOnEmpty' => false,
 'on' => 'update'),
 // ... другие правила
 );
}

Альтернатива — переопределить beforeSave():

php
protected function beforeSave()
{
 if (parent::beforeSave()) {
 if ($this->isNewRecord) {
 $this->date_create = new CDbExpression('NOW()');
 }
 $this->date_update = new CDbExpression('NOW()');
 return true;
 }
 return false;
}

Или подключить поведение (CTimestampBehavior или аналог) и задать timestampExpression => new CDbExpression(‘NOW()’) — так код станет компактнее. Примеры и обсуждения поведения есть в вики и на форумах Yii (см. https://www.yiiframework.com/wiki/10/how-to-automate-timestamps-in-activerecord-models и документацию Yii2 для идеи поведения: https://www.yiiframework.com/doc/api/2.0/yii-behaviors-timestampbehavior).

Почему CDbExpression помогает? Потому что Yii вставляет выражение в SQL “как есть” (без кавычек), поэтому MySQL получает CURRENT_TIMESTAMP или NOW(), а не строковый литерал.


Проверка DDL и MySQL: DATETIME vs TIMESTAMP

Ваша DDL:

date_create DATETIME DEFAULT CURRENT_TIMESTAMP,
date_update DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

Важно знать: поддержка DEFAULT CURRENT_TIMESTAMP для полей DATETIME появилась в MySQL 5.6. Если у вас версия ниже — DATETIME не поддерживает такой default (а TIMESTAMP — да). Проверьте версию сервера:

sql
SELECT VERSION();

Проверьте, как реально записан default в схеме:

sql
SHOW CREATE TABLE your_table \G

Обратите внимание: должно быть именно DEFAULT CURRENT_TIMESTAMP (без кавычек). Если в SHOW CREATE TABLE вы видите DEFAULT ‘current_timestamp()’ — это ошибка (кавычки), и нужно изменить схему. Пример ALTER для MySQL >= 5.6:

sql
ALTER TABLE your_table
 MODIFY date_create DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 MODIFY date_update DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

Если MySQL < 5.6 и вы не хотите апгрейдить, можно заменить DATETIME на TIMESTAMP:

sql
ALTER TABLE your_table
 MODIFY date_create TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 MODIFY date_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

Учтите разницу: TIMESTAMP хранит значение с учётом временной зоны соединения (может происходить конвертация), DATETIME — просто набор значений года/месяца/дня/времени (без конверсии). Выберите тип, исходя из требований проекта.

Подробности и примеры ошибок при некорректном прописывании DEFAULT — в багрепортах MySQL и обсуждениях (см. https://bugs.mysql.com/bug.php?id=95466 и https://github.com/phpmyadmin/phpmyadmin/issues/13999).


Отладка и тесты — как убедиться, что всё починили

Найдите в кодовой базе все вхождения “current_timestamp” (чтобы понять, кто подставляет строку):

bash
grep -R --exclude-dir=vendor -n "current_timestamp" .

Включите логирование параметров и профайлинг в конфиге DB (для проверки реального SQL):

php
'db' => array(
 'connectionString' => 'mysql:host=...;dbname=...',
 'username' => '...',
 'password' => '...',
 'enableParamLogging' => true,
 'enableProfiling' => true,
),

После этого посмотрите runtime/logs/app.log или используйте дебаг-панель (если есть). Неправильный SQL выглядит так:

INSERT INTO t (date_create) VALUES (‘current_timestamp()’);

Правильный — так:

INSERT INTO t (date_create) VALUES (CURRENT_TIMESTAMP);

Также протестируйте сценарии:

  • Создание записи без явного присвоения поля — СУБД должна проставить значение.
  • Создание записи с CDbExpression — проверьте, что в SQL попадает выражение.

Наконец, прогоните основные рабочие сценарии и проверьте отображение времени (возможны нюансы с часовыми поясами при смене TIMESTAMP/DATETIME).


Источники


Заключение

Если кратко: не подавайте в INSERT строку ‘current_timestamp()’ — либо уберите присвоение и дайте СУБД проставить DEFAULT CURRENT_TIMESTAMP, либо в коде используйте CDbExpression (new CDbExpression(‘NOW()’) / new CDbExpression(‘CURRENT_TIMESTAMP’)), чтобы Yii не экранировал выражение. Проверьте SHOW CREATE TABLE и версию MySQL (DATETIME DEFAULT CURRENT_TIMESTAMP требует MySQL ≥ 5.6) и выполните тесты после правок — проблема исчезнет.

Авторы
Проверено модерацией
Модерация