Laravel 迁移返回 SQL 错误,但 SQL 有效且有效

问题描述 投票:0回答:1

我的迁移可以在本地运行,但在生产中失败并出现错误:

Migrating: 2024_03_19_145356_tasks

   Illuminate\Database\QueryException 

  SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'send_before' (SQL: create table `tasks` (`id` bigint unsigned not null auto_increment primary key, `subscription_id` bigint unsigned not null, `send_after` timestamp not null, `send_before` timestamp not null, `sent_at` timestamp null comment 'null = not sent', `failed_at` timestamp null, `delivered` tinyint(1) null default '0' comment '0 - not delivered, 1 - delivered, null - unknown', `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci')

我调试失败了,因为 SQL 是有效的,并且实际上在通过 phpMyAdmin 或终端运行时在生产环境中运行良好:

create table `tasks` (`id` bigint unsigned not null auto_increment primary key, `subscription_id` bigint unsigned not null, `send_after` timestamp not null, `send_before` timestamp not null, `sent_at` timestamp null comment 'null = not sent', `failed_at` timestamp null, `delivered` tinyint(1) null default '0' comment '0 - not delivered, 1 - delivered, null - unknown', `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci'

2024_03_19_145356_tasks.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class Tasks extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->id();
            $table->foreignId('subscription_id')->constrained('subscriptions')->cascadeOnDelete();
            $table->timestamp('send_after')->index();
            $table->timestamp('send_before')->index();
            $table->timestamp('sent_at')->nullable()->index()->comment('null = not sent');
            $table->timestamp('failed_at')->nullable();
            $table->boolean('delivered')->default(0)->comment('0 - not delivered, 1 - delivered, null - unknown')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}

服务器版本:5.5.60-MariaDB MariaDB服务器

PHP 7.4.33 (cli)(构建时间:2023 年 12 月 12 日 14:45:16)(NTS)

Laravel 框架 8.83.27

更新1

在迁移中添加了

dump(\DB::select("SELECT @@version, @@sql_mode"));
,我发现迁移期间的
sql_mode
有以下
sql_mode
设置:

array:1 [
  0 => {#1958
    +"@@version": "5.5.60-MariaDB"
    +"@@sql_mode": "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
  }
]

已更新(已解决?)

我找到了问题所在。我不想假装我知道发生了什么,所以我会按原样说: 当迁移有多个列类型

timestamp
其中
is not nullable
时,就会出现问题。以下是一些示例:

作品:

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->timestamp('test1');
    $table->timestamp('test2')->nullable();
    $table->timestamp('test3')->nullable();
});

错误

1067 Invalid default value for 'test2'

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->timestamp('test1');
    $table->timestamp('test2');
    $table->timestamp('test3')->nullable();
});

作品:

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->timestamp('test1')->nullable();
    $table->timestamp('test2')->nullable();
    $table->timestamp('test3');
});

错误

1067 Invalid default value for 'test2'

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->timestamp('test1');
    $table->timestamp('test2');
});

作品:

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->timestamp('test1')->nullable();
    $table->timestamp('test2');
    $table->timestamp('test3')->nullable();
});

而且......如果我使用

dateTime
它总是有效的。所以......问题已经解决了,尽管我不知道发生了什么,我想知道

php laravel mariadb
1个回答
0
投票

时间戳列的隐式默认值(即,未显式定义默认值时的默认值)很复杂。在表的第一个时间戳列之后,它的行为有所不同。真是太奇怪了

如果您未指定任何默认值,则第一个时间戳列将自动给出

DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
选项,并且
explicit_defaults_for_timestamp
为 false。

后续时间戳列不会自动获取这些选项。

NOT NULL
时间戳的隐式默认值是“default default”时间戳值,即 0。

但是Laravel显然设置了一个包含

sql_mode
的会话
NO_ZERO_IN_DATE,NO_ZERO_DATE
,这使得时间戳的0值无效。

所以你的选择是:

  • 允许时间戳列可为空。
  • 为该列声明显式
    DEFAULT
  • 弄清楚如何改变 Laravel 的最佳想法
    sql_mode
    以允许日期为零。我不推荐这样做,因为带零的日期不是真正的日期。禁止它们是个好主意。
© www.soinside.com 2019 - 2024. All rights reserved.