Skip to content

Commit

Permalink
sql: clarify UPDATE test cases with intermediate constraint violations
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgrinaker committed Jul 18, 2024
1 parent b6caf02 commit 73c9d61
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 43 deletions.
4 changes: 1 addition & 3 deletions src/sql/engine/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,7 @@ impl<E: storage::Engine> super::Transaction for Transaction<E> {
}
if let Some(source_id) = source_ids.into_iter().next() {
return errinput!(
"row referenced by {}.{} for {}.{}={source_id}",
source.name,
column.name,
"primary key referenced by {}.{}={source_id}",
source.name,
source.columns[source.primary_key].name
);
Expand Down
6 changes: 3 additions & 3 deletions src/sql/testscripts/writes/delete_reference
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ ok
!> DELETE FROM ref
!> DELETE FROM ref WHERE id = 1
---
Error: invalid input: row referenced by name.ref_id for name.id=1
Error: invalid input: row referenced by name.ref_id for name.id=1
Error: invalid input: primary key referenced by name.id=1
Error: invalid input: primary key referenced by name.id=1

> SELECT * FROM ref
---
Expand Down Expand Up @@ -97,7 +97,7 @@ ok
# Deleting a referenced row errors.
!> DELETE FROM self WHERE id = 2
---
Error: invalid input: row referenced by self.self_id for self.id=3
Error: invalid input: primary key referenced by self.id=3

# Deleting an unreferenced row works.
> DELETE FROM self WHERE id = 4
Expand Down
20 changes: 8 additions & 12 deletions src/sql/testscripts/writes/update_expression
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,19 @@ ok
2, 3, 12

# This is also true with primary key updates.
# TODO: fix this
!> UPDATE test SET id = id + 1, value = id, quantity = value
> UPDATE test SET id = id - 1, value = id, quantity = value
> SELECT * FROM test
---
Error: invalid input: primary key 1 already exists
0, 1, 10
1, 2, 11
2, 3, 12
-1, 0, 1
0, 1, 2
1, 2, 3

# UPDATE expressions respect constraints.
> UPDATE test SET value = NULL WHERE id = 1
> UPDATE test SET value = NULL WHERE id = 0
> SELECT * FROM test
---
0, 1, 10
1, NULL, 11
2, 3, 12

!> UPDATE test SET quantity = value
---
-1, 0, 1
0, NULL, 2
1, 2, 3
Error: invalid input: NULL value not allowed for column quantity
29 changes: 6 additions & 23 deletions src/sql/testscripts/writes/update_primary_key
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ Error: invalid input: primary key ABC already exists
Error: invalid input: primary key Hi! 👋 already exists
Error: invalid input: invalid primary key NULL

# UPDATE can modify multiple primary keys whose intermediate keys conflict,
# as long as the final set doesn't conflict.
# TODO: fix this
# Primary key updates error if intermediate row updates violate primary key
# uniqueness, even if the final state wouldn't violate the constraints. This is
# also true with Postgres.
> SELECT * FROM "int"
---
0
Expand All @@ -148,26 +148,9 @@ Error: invalid input: invalid primary key NULL
---
Error: invalid input: primary key 1 already exists

# UPDATE can modify multiple primary keys that have foreign key references
# as long as the final set of primary keys still satisfy the references.
# We test this by swapping primary keys 0 <-> 1 in a single UPDATE.
# TODO: this shouldn't error.
> CREATE TABLE ref (id INT PRIMARY KEY, int_id INTEGER REFERENCES "int")
> INSERT INTO ref VALUES (0, 0), (1, 1)
---
ok

!> UPDATE "int" SET id = 1 - id
# The updates happen in primary key order, so the reverse update does work.
> UPDATE "int" SET id = id - 1
> SELECT * FROM "int"
---
Error: invalid input: row referenced by ref.int_id for ref.id=0
0
1

# If the UPDATE leaves a foreign key dangling, it should error.
!> UPDATE "int" SET id = id - 1
> SELECT * FROM "int"
---
Error: invalid input: row referenced by ref.int_id for ref.id=0
-1
0
1
24 changes: 22 additions & 2 deletions src/sql/testscripts/writes/update_reference
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,31 @@ storage delete mvcc:TxnActive(26) ["\x01\x00\x00\x00\x00\x00\x00\x00\x1a"]
!> UPDATE self SET id = 4 WHERE id = 1
!> UPDATE self SET id = 4 WHERE id = 2
---
Error: invalid input: row referenced by self.self_id for self.id=2
Error: invalid input: row referenced by self.self_id for self.id=3
Error: invalid input: primary key referenced by self.id=2
Error: invalid input: primary key referenced by self.id=3

# Not even when only this row points to itself.
> UPDATE self SET self_id = NULL WHERE id > 1
!> UPDATE self SET id = 4 WHERE id = 1
---
Error: invalid input: reference 1 not in table self

# Updates can't violate foreign key references in intermediate states even if
# the final state retains foreign key integrity. Postgres can't either.
> SELECT * FROM "int"
---
-1
0
1

> INSERT INTO name (id, "int") VALUES (2, -1), (3, 0), (4, 1)
> SELECT * FROM name
---
1, NULL, NULL, NULL, NULL
2, NULL, -1, NULL, NULL
3, NULL, 0, NULL, NULL
4, NULL, 1, NULL, NULL

!> UPDATE "int" SET id = -id
---
Error: invalid input: primary key referenced by name.id=2
12 changes: 12 additions & 0 deletions src/sql/testscripts/writes/update_unique
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,15 @@ storage delete mvcc:TxnActive(23) ["\x01\x00\x00\x00\x00\x00\x00\x00\x17"]
---
1, NULL, NULL, inf, case
2, NULL, NULL, -inf, CaSe

# An UPDATE errors if intermediate states violate the uniqueness constraints,
# even if the final state wouldn't. This is the same in Postgres.
> UPDATE "unique" SET "bool" = id = 2
> SELECT * FROM "unique"
---
1, FALSE, NULL, inf, case
2, TRUE, NULL, -inf, CaSe

!> UPDATE "unique" SET "bool" = NOT "bool"
---
Error: invalid input: value TRUE already in unique column bool

0 comments on commit 73c9d61

Please sign in to comment.