laravel: 5.7.28
Mysql: 5.7
##先に結論
migrationを書くときには引数に気をつけよう!!
##何があったの
新しいテーブルを作成するため、このようなmigrationファイルを作成し実行したところエラーが出た。
class CreateMConf extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('m_conf', function (Blueprint $table) {
$table->increments('id')->unsigned()->nullable(false);
$table->tinyInteger('type')->unsigned()->nullable(false)->default(1);
$table->integer('watcher_id', 10)->nullable(false);
$table->string('content', 200)->nullable(false);
$table->char('created_from', 36)->nullable(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('m_conf');
}
}
##出力されたエラー
$ php artisan migrate
Migrating: 2019_10_28_000000_create_m_conf
Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key (SQL: create table `m_conf` (`id` int unsigned not null auto_increment primary key, `type` tinyint unsigned not null default '1', `watcher_id` int not null auto_increment primary key, `content` varchar(200) not null, `created_from` char(36) not null, `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci')
at /var/www/witone-prjct01/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664
660| // If an exception occurs when attempting to run a query, we'll format the error
661| // message to include the bindings with SQL, which will make this exception a
662| // lot more helpful to the developer instead of just the database's errors.
663| catch (Exception $e) {
> 664| throw new QueryException(
665| $query, $this->prepareBindings($bindings), $e
666| );
667| }
668|
Exception trace:
1 Doctrine\DBAL\Driver\PDOException::("SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key")
/var/www/witone-prjct01/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:119
2 PDOException::("SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key")
/var/www/witone-prjct01/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:117
Please use the argument -v to see more details.
どうやら、AutoIncrementを複数のカラムに設定しているということでエラーが出ているらしい。
が、AutoIncrementはid
カラムにしか設定していないはず・・・。
##改めて、まずは実行されているCREATE文を確認。
php artisan migrate --pretend
で実行するSQLが確認できる。
CreateMConf: create table `m_conf`
(`id` int unsigned not null auto_increment primary key,
`type` tinyint unsigned not null default '1',
`watcher_id` int not null auto_increment primary key,
`content` varchar(200) not null,
`created_from` char(36) not null,
`created_at` timestamp null,
`updated_at` timestamp null
)
default character set utf8mb4 collate 'utf8mb4_unicode_ci'
やはり、エラー文の通りwatcher_id
にもAutoIncrementが設定されてしまっている。
しかし、どこをどう見てもmigrationファイルでwatcher_id
にはincrementsではなくintegerを設定している。
試しに、カラム名を変更したりnullable()を消したりして、新しいファイルの内容がキャッシュか何かで反映されていないのかも?と確認するも
エラー内容SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key
に変化なし。
##migrationがなにやってるか調べた
laravel公式リファレンスにもmigrationについてあまり詳しく書いてない。
仕方ないので、migrationでintegerカラムを設定するとき内部的にどんな処理をいているのか見てみることにした。
以下、該当箇所のみ抜粋。
/**
* Create a new integer (4-byte) column on the table.
*
* @param string $column
* @param bool $autoIncrement
* @param bool $unsigned
* @return \Illuminate\Database\Schema\ColumnDefinition
*/
public function integer($column, $autoIncrement = false, $unsigned = false)
{
return $this->addColumn('integer', $column, compact('autoIncrement', 'unsigned'));
}
なんと、integer()
の第2引数はAutoIncrementかどうかを判断するものだった!!!
しかも第3引数はunsignedを判断するものであることも判明!
てっきり、string()
やchar()
などの第2引数と同じく長さを設定できると勘違いをしていました。。。
##修正したあとのSQL
CreateMConf: create table `m_conf`
(`id` int unsigned not null auto_increment primary key,
`type` tinyint unsigned not null default '1',
`watcher_id` int not null,
`content` varchar(200) not null,
`created_from` char(36) not null,
`created_at` timestamp null,
`updated_at` timestamp null
)
default character set utf8mb4 collate 'utf8mb4_unicode_ci'
ということで、無事にmigrationを実行することができました。
##反省
- migrationが内部的にどんな処理をしているのか見るまでにとても時間がかかった
- 実際には、色々いじっていく中で同僚が第2変数を削除するとうまくいくことを発見したのが先だった
- 確実な情報を見ないで「こうやって書けばこんな感じで動くはず!」という思い込みが激しかった
- vendorの中身をみるのがちょっと怖かった
とはいえ、同じような事象で困ったという記事が見つからなかったので
同じような事象で困ってしまった人には参考になる記事が書けたかなと思います。