У MySQL оператори визначення схеми (DDL) виконують неявний коміт: перед виконанням вони комітять поточну транзакцію, і самі в транзакцію не потрапляють.
START TRANSACTION;
INSERT INTO settings (name) VALUES ('a');
ALTER TABLE settings ADD COLUMN value TEXT; -- INSERT уже закомічено
ROLLBACK; -- відкочувати нічого
Що викликає неявний коміт (основне):
CREATE,ALTER,DROP,RENAME,TRUNCATE TABLE, створення й видалення індексів, подань, процедур;LOCK TABLESіUNLOCK TABLES(якщо таблиці були заблоковані);START TRANSACTION/BEGIN- комітять попередню незавершену транзакцію;- адміністративні:
ANALYZE TABLE,OPTIMIZE TABLE,CREATE USER,GRANTтощо.
CREATE TEMPORARY TABLE неявного коміту не робить.
Що це означає для Laravel-міграцій:
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->nullable();
});
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->unique()->change(); // впаде на дублікатах
});
}
Якщо другий крок падає, перший уже застосований. Міграцію не позначено виконаною, тож повторний запуск упаде на «колонка вже існує». Доведеться вручну прибрати наполовину застосовані зміни.
Атомарний DDL у MySQL 8 означає інше: окремий оператор DDL або виконується повністю, або не виконується (не лишає «напівстворених» таблиць після збою). Але кілька операторів у транзакцію все одно не об'єднати.
PostgreSQL тут відрізняється: DDL у ньому транзакційний, і Laravel обгортає міграцію в транзакцію ($withinTransaction = true), тож упала - відкотилася повністю.
Практичні висновки для MySQL:
- одна міграція - одна логічна зміна;
- ризиковану зміну (унікальний індекс на дані з можливими дублікатами) - окремою міграцією, після перевірки й очищення даних;
- метод
down()має вміти відкотити зміни, навіть якщо частину з них не застосовано (if (Schema::hasColumn(...))).