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

FOREIGN KEY CASCADE ON UPDATE CONTAINING SOME NULL FIELDS AND SOME NON-NULL FIELDS NOT CASCADING [CORE4957] #5248

Open
firebird-automations opened this issue Oct 11, 2015 · 10 comments

Comments

@firebird-automations
Copy link
Collaborator

Submitted by: Tim Kelly (m00bh000)

Votes: 3

The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade take place?

If this is by design then I question the design since in my first example it is now impossible to alter B in either X or Y or both where A = 1;

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

description: SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates:

I think that NULLS should not be cascaded to the FK if all the

=>

The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL.

But I would argue that in the case where multiple keys are used where at least one field in the FK is not null then the cascade should take place.

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

description: The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL.

But I would argue that in the case where multiple keys are used where at least one field in the FK is not null then the cascade should take place.

=>

The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade should take place?

summary: FOREIGN KEY CASCADE CONTAINING NULL COLUMNS NOT CASCADING => FOREIGN KEY CASCADE ON UPDATE CONTAINING SOME NULL COLUMNS NOT CASCADING

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

description: The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade should take place?

=>

The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade take place?

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

summary: FOREIGN KEY CASCADE ON UPDATE CONTAINING SOME NULL COLUMNS NOT CASCADING => FOREIGN KEY CASCADE ON UPDATE CONTAINING SOME NULL FIELDS AND SOME NON-NULL FIELDS NOT CASCADING

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

description: The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade take place?

=>

The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade take place?

If this is by design then I question the design since in my first example it is now impossible to alter B in either X or Y where A = 1;

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

description: The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade take place?

If this is by design then I question the design since in my first example it is now impossible to alter B in either X or Y where A = 1;

=>

The following does not cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,NULL);
SQL> INSERT INTO Y VALUES(1,NULL);
SQL> UPDATE X SET B = 1 WHERE A = 1;
Statement failed, SQLSTATE = 23000
violation of FOREIGN KEY constraint "INTEG_3304" on table "Y"
-Foreign key references are present for the record
-Problematic key value is ("A" = 1, "B" = NULL)

Whereas this does cascade:

SQL> CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
SQL> CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
SQL> ALTER TABLE X ADD UNIQUE(A,B);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(1,0);
SQL> INSERT INTO Y VALUES(1,0);
SQL> UPDATE X SET B = 1 WHERE A = 1;
SQL> SELECT * FROM X;

       A            B

============ ============
1 1

SQL> SELECT * FROM Y;

       A            B

============ ============
1 1

I can see how this problem probably originates, take a case of a single field foreign-key:

SQL> CREATE TABLE X (A INTEGER );
SQL> CREATE TABLE Y (A INTEGER );
SQL> ALTER TABLE X ADD UNIQUE(A);
SQL> ALTER TABLE Y ADD FOREIGN KEY (A) REFERENCES X(A) ON UPDATE CASCADE;
SQL> INSERT INTO X VALUES(NULL);
SQL> INSERT INTO Y VALUES(NULL);
SQL> UPDATE X SET A = 2;
SQL>
SQL> select * from y;

       A

============
<null>

SQL> select * from x:

       A

============
2

In the above case the cascade doesn't travel to the FK since the FK is currently NULL. This is by design, since essentially all the fields in the FK are currently NULL so the FK is inactive.

But in the case where multiple keys are used where at least one field in the FK is not null then shouldn't the cascade take place?

If this is by design then I question the design since in my first example it is now impossible to alter B in either X or Y or both where A = 1;

@firebird-automations
Copy link
Collaborator Author

Commented by: Tim Kelly (m00bh000)

I though I'd see how this scenario is handled in postgres:

Tim=#⁠ CREATE TABLE X (A INTEGER PRIMARY KEY, B INTEGER);
CREATE TABLE
Tim=#⁠ CREATE TABLE Y (A INTEGER PRIMARY KEY, B INTEGER);
CREATE TABLE
Tim=#⁠ ALTER TABLE X ADD UNIQUE(A,B);
ALTER TABLE
Tim=#⁠ ALTER TABLE Y ADD FOREIGN KEY (A,B) REFERENCES X(A,B) ON UPDATE CASCADE;
ALTER TABLE
Tim=#⁠ INSERT INTO X VALUES(1,NULL);
INSERT 0 1
Tim=#⁠ INSERT INTO Y VALUES(1,NULL);
INSERT 0 1
Tim=#⁠ UPDATE X SET B = 1 WHERE A = 1;
UPDATE 1

Tim=#⁠ SELECT * FROM X;
a | b
---+---
1 | 1
(1 row)

Tim=#⁠ SELECT * FROM Y;
a | b
---+---
1 |
(1 row)

So it doesn't cascade, but the FK isn't enforced. Not ideal but at least it is possible to update B.

@firebird-automations
Copy link
Collaborator Author

Commented by: Tim Kelly (m00bh000)

Having considered this further. I would summarise that the current behaviour is that a record bound by a foreign key containing at least one field that is not null and other fields that are null is enforced (ie a link to the unique key exists) but a cascade is not triggered if the index pointed at changes.

It seems to me that this is a contradiction. The key is being enforced as if the two records are linked but cascades aren't.

I would argue that a record bound to a FK containing at least one NULL should either:

A) effectively exclude that record from the constraint completely (ie it doesn't point to any particular record in the Unique key), and consequently no cascade should take place. Therefore in my example: X(1, 2), Y(1,NULL) is allowed.

or

B) The bind should be enforced and the cascade should work. X(1,NULL), Y(1,NULL), change X to (1,2) and Y should automatically change to (1,2).

Nb. In PostgreSQL, option A is taken.

@firebird-automations
Copy link
Collaborator Author

Modified by: Tim Kelly (m00bh000)

Version: 3.0 RC 1 [ 10584 ]

Version: 3.0 Beta 2 [ 10586 ] =>

@firebird-automations
Copy link
Collaborator Author

Commented by: @kattunga

I have the same problem, so I had to use triggers to perform logic control.

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

No branches or pull requests

1 participant