Home » Php » Laravel migration with sqllite 'Cannot add a NOT NULL column with default value NULL'

Laravel migration with sqllite 'Cannot add a NOT NULL column with default value NULL'

Posted by: admin November 1, 2017 Leave a comment

Questions:

Why am I getting this warning when using the SQLLITE driver? I have no problems with the MySQL driver but SQLLITE is throwing this error.

It does not make sense to me since I understood the seeding happens after all the migrations are completed so why is it complaining about this issue which would only arise if data was already present in the database.

My two migrations are

FIRST MIGRATION

  public function up() {
    Schema::create('users', function($table) {
      $table->increments('id');
      $table->string('username');
      $table->string('email');
      $table->string('password');
    });
  } 

SECOND MIGRATION

public function up() {
    Schema::table('users', function(Blueprint $table) {
        $table->date('birthday')->after('id');
        $table->string('last_name')->after('id');
        $table->string('first_name')->after('id');
    });
}

ERROR

Exception: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL column with default value NULL (SQL: alter table "users" add column "birthday" date not null)
Answers:

It looks like this is a SQLite oddity. According to a Laracast forum thread about the same issue:

When adding a table from scratch, you can specify NOT NULL. However, you can’t do this when adding a column. SQLite’s specification says you have to have a default for this, which is a poor choice.

Looking further to the SQLite ALTER TABLE docs, I found:

If a NOT NULL constraint is specified, then the column must have a default value other than NULL.

I suppose in the world of SQLite, not providing a default value is the same thing as saying the default value should be NULL (as opposed to meaning there is no default value for this non-nullable column, so a value must be provided for it on each insert).

It seems SQLite simply leaves you in a bad state if you need to add a non-nullable column to an existing table, which column should also not have a default value.

Questions:
Answers:

I’m not familiar with Laravel, but apparently the use of the after method to specify the order of columns appears to specifically mention ONLY MySQL (Laravel) and a discussion (GitHub) seems to point to difficulties in it’s use with SQLite. It may likely be incompatible because its function: “…use the after method to specify the order of columns” design runs up against the limitation in SQLite’s documentation (SQLite) for adding columns… which reads: "The new column is always appended to the end of the list of existing columns." I can’t say whether assigning default values with ->default($value) can get you by or not.

Questions:
Answers:

If you don’t want the column to be nullable – then you need to let Laravel know what the default should be.

One option is an empty string "" like this

public function up() {
    Schema::create('users', function($table) {
      $table->date('birthday')->after('id')->default('');
      $table->string('last_name')->after('id')->default('');
      $table->string('first_name')->after('id')->default('');

    });
  } 

Questions:
Answers:

you have to add

->nullable()

for the columns that may have a null value

Questions:
Answers:

A workaround I’ve used successfully is to check which database driver is being used and slightly modify the migration for SQLite.

For example:

class MyMigration extends Migration
{
    public function up()
    {
        $driver = Schema::connection($this->getConnection())->getConnection()->getDriverName();

        Schema::table('invoices', function (Blueprint $table) use ($driver) {
            $table->string('stripe_invoice')->nullable()->change();

            if ('sqlite' === $driver) {
                $table->string('stripe_invoice_number')->default('');
            } else {
                $table->string('stripe_invoice_number')->after('stripe_invoice');
            }
        });
    }
}