Skip to content

Sqlite enum column check constraints are not preserved after adding foreign key to table #56151

@Pecon

Description

@Pecon

Laravel Version

12.19.3

PHP Version

8.4.8

Database Driver & Version

sqlite3 3.37.2

Description

I have been setting up tests for my project to validate that backed enum types in my project are compatible with the enums generated for my database schemas, and in doing so I discovered inconsistent behavior with the schema generation. When creating the table initially, or when explicitly updating a table's column, an enum-type column will generate as a varchar type with a check constraint that restricts the values of the defined enum. If you later alter this table by adding a foreign key, the resulting sequence of commands that the migration uses to generate the updated table does not preserve the check constraint of the enum columns.

Steps To Reproduce

Using the sqlite database driver, create a table with an enum column.
Image

By running the migration with --pretend, I can verify that it is created with the intended check constraint.

create table "example_table" ("id" integer primary key autoincrement not null, "type" varchar check ("type" in ('a', 'b', 'c')) not null)

Now I want to expand on this table by adding a foreign key to a second table, so I'll create a second table and then add the foreign key to the first example table.
Image

When I run the migration with --pretend, I can see that a few steps occur to copy the existing example_table's contents to a new table that has the foreign key. It then drops the original table and renames the new copy to replace the prior table with one which has the foreign key.

create table "example_table" ("id" integer primary key autoincrement not null, "type" varchar check ("type" in ('a', 'b', 'c')) not null)
create table "other_example_table" ("id" integer primary key autoincrement not null)
select name, type, not "notnull" as "nullable", dflt_value as "default", pk as "primary", hidden as "extra" from pragma_table_xinfo('example_table', 'main') order by cid asc
select "sql" from "main".sqlite_master where type = 'table' and name = 'example_table'
select 'primary' as name, group_concat(col) as columns, 1 as "unique", 1 as "primary" from (select name as col from pragma_table_xinfo('example_table', 'main') where pk > 0 order by pk, cid) group by name union select name, group_concat(col) as columns, "unique", origin = 'pk' as "primary" from (select il.*, ii.name as col from pragma_index_list('example_table', 'main') il, pragma_index_info(il.name, 'main') ii order by il.seq, ii.seqno) group by name, "unique", "primary"
select group_concat("from") as columns, 'main' as foreign_schema, "table" as foreign_table, group_concat("to") as foreign_columns, on_update, on_delete from (select * from pragma_foreign_key_list('example_table', 'main') order by id desc, seq) group by id, "table", on_update, on_delete
pragma foreign_keys
create table "__temp__example_table" (, foreign key("id") references "other_example_table"("id"))
insert into "__temp__example_table" () select from "example_table"
drop table "example_table"
alter table "__temp__example_table" rename to "example_table"

Unfortunately, after running this, I can verify with sqlite3's .schema function that the resulting final table with the foreign key has now lost the check constraint that was originally on the enum column.

CREATE TABLE IF NOT EXISTS "example_table" ("id" integer primary key autoincrement not null, "type" varchar not null, foreign key("id") references "other_example_table"("id"));

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions