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
Validation fails to detect invalid SetNull
referential action referencing non-optional fields
#14673
Comments
Is this actually not valid at the database level? We should test first that a |
SQL Reproduction with one-to-many non-optional relationsMySQLTest SQL Statement: CREATE TABLE categories(
categoryId INT AUTO_INCREMENT PRIMARY KEY,
categoryName VARCHAR(100) NOT NULL
) ENGINE=INNODB;
CREATE TABLE products(
productId INT AUTO_INCREMENT PRIMARY KEY,
productName varchar(100) not null,
categoryId INT,
CONSTRAINT fk_category
FOREIGN KEY (categoryId)
REFERENCES categories(categoryId)
ON DELETE ${action}
ON UPDATE ${action}
) ENGINE=INNODB; with type Action
= 'RESTRICT'
| 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
The following only works on
On
MariaDBTest SQL Statement: CREATE TABLE categories(
categoryId INT AUTO_INCREMENT PRIMARY KEY,
categoryName VARCHAR(100) NOT NULL
) ENGINE=INNODB;
CREATE TABLE products(
productId INT AUTO_INCREMENT PRIMARY KEY,
productName varchar(100) not null,
categoryId INT,
CONSTRAINT fk_category
FOREIGN KEY (categoryId)
REFERENCES categories(categoryId)
ON DELETE ${action}
ON UPDATE ${action}
) ENGINE=INNODB; with type Action
= 'RESTRICT'
| 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
The following only works on
On
Postgres / CockroachDBTest SQL Statement: CREATE TABLE categories(
categoryId INT PRIMARY KEY,
categoryName VARCHAR(100) NOT NULL
);
CREATE TABLE products(
productId INT PRIMARY KEY,
productName varchar(100) not null,
categoryId INT,
CONSTRAINT fk_category
FOREIGN KEY (categoryId)
REFERENCES categories(categoryId)
ON DELETE ${action}
ON UPDATE ${action}
); with type Action
= 'RESTRICT'
| 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
Microsoft SQL ServerTest SQL Statement: CREATE TABLE categories(
categoryId INT NOT NULL PRIMARY KEY NONCLUSTERED,
categoryName NVARCHAR(100) NOT NULL
);
CREATE TABLE products(
productId INT NOT NULL PRIMARY KEY NONCLUSTERED,
productName NVARCHAR(100) NOT NULL,
categoryId INT,
CONSTRAINT fk_category
FOREIGN KEY (categoryId)
REFERENCES categories(categoryId)
ON DELETE ${action}
ON UPDATE ${action}
); with type Action
= 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
|
SQL Reproduction with one-to-one optional relationsMySQLTest SQL Statement: CREATE TABLE User (
id INTEGER PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE Profile (
id INTEGER PRIMARY KEY,
userId INTEGER UNIQUE NULL,
FOREIGN KEY (userId) REFERENCES User(id)
ON DELETE ${action}
ON UPDATE ${action}
) ENGINE=InnoDB; with type Action
= 'RESTRICT'
| 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
The following only works on
On
MariaDBTest SQL Statement: CREATE TABLE User (
id INTEGER PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE Profile (
id INTEGER PRIMARY KEY,
userId INTEGER UNIQUE NULL,
FOREIGN KEY (userId) REFERENCES User(id)
ON DELETE ${action}
ON UPDATE ${action}
) ENGINE=InnoDB; with type Action
= 'RESTRICT'
| 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
The following only works on
On
Postgres / CockroachDBTest SQL Statement: CREATE TABLE User (
id INT PRIMARY KEY
);
CREATE TABLE Profile (
id INT PRIMARY KEY,
userId INT UNIQUE NULL,
FOREIGN KEY (userId) REFERENCES User(id)
ON DELETE ${action}
ON UPDATE ${action}
); with type Action
= 'RESTRICT'
| 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
Microsoft SQL ServerTest SQL Statement: CREATE TABLE SomeUser (
id INT PRIMARY KEY NONCLUSTERED
);
CREATE TABLE Profile (
id INT PRIMARY KEY NONCLUSTERED,
userId INT UNIQUE NULL,
FOREIGN KEY (userId) REFERENCES SomeUser(id)
ON DELETE ${action}
ON UPDATE ${action}
); with type Action
= 'CASCADE'
| 'SET NULL'
| 'NO ACTION'
| 'SET DEFAULT'
let action: Action The following SQL referential actions work for both
|
To recap, @tomhoule, the SQL database don't validate the |
Makes sense. One thing to keep in mind that could prevent us from going into that direction is the principle that we should be able to introspect a valid database schema to a valid prisma schema in as many cases as possible, so if it's valid at the database level, we may want to accept it. Maybe warn. |
What we want to do (from Notion) Make schema with non-optional 1:1 relation and Optional but nice DX: Add special error message for PostgreSQL that links to a documentation page that explains why some valid SQL for PostgreSQL can lead to an invalid Prisma schema via Introspection: While the CREATE TABLE works, the INSERT/UPDATE/DELETE on that table will all fail. |
To summarise the output of the tests done in #15728 relevant to this issue:
Optional but nice DX: There's also an odd behavior that may be useful to document and/or validate against:
|
This missed validation triggers a migration error when using MySQL, SQL Server, SQLite, and CockroachDB, but not on Postgres.
Example with MySQL:
We can see that the schema is wrongfully considered valid:
If we attempt a push, we get the following error:
If we try to create/update the
Profile
model via the Prisma client, we get the following migration error:If we exclude the
onUpdate
/onDelete
referential actions, the schema above generates the followingSQL
statements (we can check that viaprisma migrate dev
):On Postgres, oddly,
prisma db push
it doesn't fail, so migration error is thrown:SetNull
on Valid SchemaIf we want
prisma db push
to work on other databases likemysql:8
when using theSetNull
referential action, we'd need to change the schema above as:which generates the following
SQL
statements:The text was updated successfully, but these errors were encountered: