Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.x] Fix laravel migrations when migrating to sql server (dropColumn with default value) #31229

Merged
merged 8 commits into from
Jan 28, 2020

Conversation

joelharkes
Copy link
Contributor

@joelharkes joelharkes commented Jan 24, 2020

Currently when running the following script would work in mysql (and mariadb) but would fail in sqlserver.

Schema::create('foo', function($table)
{
     $table->boolean('bar')->default(false);
});
Schema::table('foo', function($table)
{
    $table->dropColumn('bar'); // Don't works!
});

this would throw error: [Illuminate\Database\QueryException] SQLSTATE[42000]: [Microsoft][SQL Server Native Client 11.0][SQL Server]The object 'DF__foo__bar__322242A7' is dependent on column 'bar'. (SQL: alter table "foo" drop column "bar")

The problem: on dropColumn() your fluent code doesn't know if the column had a default constraint. This code added would retrieve wether a default constraint exists on dropped columns and remove them before dropping the column as well, and thus not triggering a is dependent on column constraint QueryException.

This solution

Creates a script before drop column is called to get the Default Constraints and drop these before the column is dropped.

In this case it would run script:

DECLARE @sql NVARCHAR(MAX) = '';
SELECT @sql += 'ALTER TABLE [dbo].[foo] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' "
FROM SYS.COLUMNS
WHERE [object_id] = OBJECT_ID('[dbo].[foo') AND [name] in ('bar');
EXEC(@sql)

Which creates the following sql and executes it:

ALTER TABLE [dbo].[foo] DROP CONSTRAINT DF__foo__bar__322242A7;

Before running sql

alter table foo drop column bar

End result

migrating from mysql to sqlserver becomes almost seamless, no need to specify dropping default constraints manually like proposed in other issues like: #4402

Tests

I couldn't create e2e tests because this laravel travis setup has no sql server?
But locally this fix has worked great for me.

@yaroslavmo
Copy link
Contributor

I faced the same issue today. Really hope they will merge your fix!

@GrahamCampbell GrahamCampbell changed the title [6.0.] Fix laravel migrations when migrating to sql server (dropColumn with default value) [6.0] Fix laravel migrations when migrating to sql server (dropColumn with default value) Jan 27, 2020
@taylorotwell taylorotwell merged commit 812ab55 into laravel:6.x Jan 28, 2020
@GrahamCampbell GrahamCampbell changed the title [6.0] Fix laravel migrations when migrating to sql server (dropColumn with default value) [6.x] Fix laravel migrations when migrating to sql server (dropColumn with default value) Jan 28, 2020
@cirolosapio
Copy link
Contributor

I have the same problem.
With a table under the dbo schema the problem is solved, but with a table under a custom schema (example schema.table) the query fails

@joelharkes
Copy link
Contributor Author

I'll see to find time to make laravel tests for SQL server (which are currently missing) using the SQL server Development Docker image.

to test these cases, sorry for the inconvenience guys.

@joelharkes
Copy link
Contributor Author

@heartbeatLV i'm testing this now can you please provide a small piece reproducable piece of code so i can unit test this?

@cirolosapio
Copy link
Contributor

@heartbeatLV i'm testing this now can you please provide a small piece reproducable piece of code so i can unit test this?

public function up() {
    // check schema exists
    // SELECT * FROM sys.schemas WHERE name = 'example'
    DB::statement('CREATE SCHEMA example');

    Schema::create('example.tests', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });

    Schema::table('example.tests', function (Blueprint $table) {
        $table->boolean('testBool')->default(false);
    });

    Schema::table('example.tests', function (Blueprint $table) {
        $table->dropColumn('testBool');
    });
}

public function down() {
    // this will not work because the "existing" check fails
    // Schema::dropIfExists('example.tests');
    // SQL: if exists (select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'example.tests') drop table "example"."tests"

    // workaround
    DB::statement('DROP TABLE IF EXISTS example.tests');

    DB::statement('DROP SCHEMA IF EXISTS example');
}

hope it helps

@joelharkes
Copy link
Contributor Author

Thanks this is perfect, im testing now. hope to finalize a PR coming week into laravel :)

@joelharkes
Copy link
Contributor Author

@heartbeatLV it will be fixed in next version of laravel once my PR gets merged: #32957

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants