Search code examples
laravellaravel-migrations

Laravel Migration Rollback of Index with Column Change


I'm trying to test a migration rollback after changing a column type and setting the index.

     /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('attachments', function(Blueprint $table) {
            $table->dropIndex('attachment_id');
            $table->text('attachment_id')->nullable()->change();
        });
    }
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('attachments', function(Blueprint $table) {
            $table->unsignedBigInteger('attachment_id')->nullable()->change();
            $table->index('attachment_id');
        });
    }

The migration runs fine and assigns the index. But when I run the rollback I run into the following error:

SQLSTATE[42000]: Syntax error or access violation: 1170 BLOB/TEXT column 'attachment_id' used in key specification without a key length (SQL: ALTER TABLE attachments CHANGE attachment_id attachment_id TEXT DEFAULT NULL)

This table was originally set up incorrectly (without an index and without the correct column type). I'm trying to update this but I also want to make sure if any issues occur in production due to the change it can be rolled back quickly.

How are you meant to get around this? The index is already meant to be dropped?

I've tried changing the order of operations and also running separate Schema functions. Also tried changing it to a string instead of text with a set length and no luck.

The below doesn't work:

public function down()
    {
        Schema::table('attachments', function(Blueprint $table) {
            $table->dropIndex('attachment_id');
        });

        Schema::table('attachments', function(Blueprint $table) {
            $table->string('attachment_id', 255)->change();
        });
    }

public function down()
    {
        Schema::table('attachments', function(Blueprint $table) {
            $table->dropIndex('attachment_id');
            $table->string('attachment_id', 255)->change();
        });
    }

public function down()
    {
        Schema::table('attachments', function(Blueprint $table) {
            $table->string('attachment_id', 255)->change();
            $table->dropIndex('attachment_id');
        });
    }


Solution

  • Well apparently I missed something because putting the dropIndex inside an array worked.

    /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::table('attachments', function(Blueprint $table) {
                $table->dropIndex(['attachment_id']);
            });
    
            Schema::table('attachments', function(Blueprint $table) {
                $table->string('attachment_id', 255)->change();
            });
        }
    
    

    Answer found here: https://github.com/laravel/framework/issues/20501#issuecomment-321814806

    That's because your the name of your index is test_table_user_id_index in this case. That happens in the createIndexName method in Blueprint.php: $index = strtolower($this->table.''.implode('', $columns).'_'.$type); When you pass a string as a parameter, the method will not convert it to a name. Only when you pass an array:

    protected function dropIndexCommand($command, $type, $index)
        {
            $columns = [];
    
            // If the given "index" is actually an array of columns, the developer means
            // to drop an index merely by specifying the columns involved without the
            // conventional name, so we will build the index name from the columns.
            if (is_array($index)) {
                $index = $this->createIndexName($type, $columns = $index);
            }
    
            return $this->indexCommand($command, $columns, $index);
        }