Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions core/translate/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,11 @@ pub fn translate_insert(
connection,
table_references: &mut table_references,
};
// NOT NULL default substitution must happen before index key registers are
// copied in preflight constraint checks. Otherwise the index entry gets NULL
// while the table row gets the default value, causing integrity_check failures.
emit_notnulls(program, &ctx, &insertion, resolver)?;

emit_preflight_constraint_checks(
Comment thread
nightscape marked this conversation as resolved.
program,
&mut ctx,
Expand All @@ -721,8 +726,6 @@ pub fn translate_insert(
&mut preflight_ctx,
)?;

emit_notnulls(program, &ctx, &insertion, resolver)?;

// Create and insert the record
let affinity_str = insertion
.col_mappings
Expand Down
79 changes: 79 additions & 0 deletions testing/runner/tests/insert_not_null_default_index.sqltest
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@database :memory:

# Regression test: INSERT OR REPLACE with a NOT NULL DEFAULT column and a
# secondary UNIQUE index. When a column expression evaluates to NULL at
# runtime (e.g. expr + NULL), the NOT NULL default substitution must happen
# BEFORE index key registers are copied, otherwise the index entry gets NULL
# while the table row gets the default value → integrity_check fails.

setup schema {
CREATE TABLE t (a INTEGER PRIMARY KEY, b INTEGER, c INTEGER NOT NULL DEFAULT 0, d INTEGER CHECK (d > 0));
CREATE UNIQUE INDEX idx ON t (b DESC, c, d DESC);
}

@setup schema
@cross-check-integrity
test insert-or-replace-null-expr-not-null-default {
INSERT OR REPLACE INTO t (a, b, c, d) VALUES (1, NULL, 1892229699219097912 + -NULL, 100);
PRAGMA integrity_check;
}
expect {
ok
}

@setup schema
@cross-check-integrity
test insert-or-replace-null-expr-select-back {
INSERT OR REPLACE INTO t (a, b, c, d) VALUES (1, NULL, 1892229699219097912 + -NULL, 100);
SELECT a, b, c, d FROM t;
}
expect {
1||0|100
}

@setup schema
@cross-check-integrity
test insert-or-replace-null-expr-not-null-default-text {
CREATE TABLE t2 (id INTEGER PRIMARY KEY, x TEXT NOT NULL DEFAULT 'fallback', y INTEGER);
CREATE UNIQUE INDEX idx2 ON t2 (x, y);
INSERT OR REPLACE INTO t2 (id, x, y) VALUES (1, NULL || NULL, 42);
PRAGMA integrity_check;
}
expect {
ok
}

@setup schema
@cross-check-integrity
test insert-or-replace-null-expr-not-null-default-text-select {
CREATE TABLE t2 (id INTEGER PRIMARY KEY, x TEXT NOT NULL DEFAULT 'fallback', y INTEGER);
CREATE UNIQUE INDEX idx2 ON t2 (x, y);
INSERT OR REPLACE INTO t2 (id, x, y) VALUES (1, NULL || NULL, 42);
SELECT id, x, y FROM t2;
}
expect {
1|fallback|42
}

@setup schema
@cross-check-integrity
test insert-not-null-default-middle-column-index {
INSERT OR REPLACE INTO t (a, b, c, d) VALUES (1, 5, NULL, 10);
INSERT OR REPLACE INTO t (a, b, c, d) VALUES (2, 6, 0 * NULL, 20);
PRAGMA integrity_check;
}
expect {
ok
}

@setup schema
@cross-check-integrity
test insert-not-null-default-middle-column-select {
INSERT OR REPLACE INTO t (a, b, c, d) VALUES (1, 5, NULL, 10);
INSERT OR REPLACE INTO t (a, b, c, d) VALUES (2, 6, 0 * NULL, 20);
SELECT a, b, c, d FROM t ORDER BY a;
}
expect {
1|5|0|10
2|6|0|20
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ addr opcode p1 p2 p3 p4 p5 comment
33 Goto 0 35 0 0
34 NewRowid 1 8 0 0 r[8]=rowid
35 Affinity 9 5 0 0 r[9..14] = D, B, E, D, B
36 NotExists 1 38 8 0
37 Halt 1555 0 0 products.id 0
38 Copy 13 15 0 0 r[15]=r[13]
39 Copy 8 16 0 0 r[16]=r[8]
40 HaltIfNull 1299 0 10 products.name 0
41 HaltIfNull 1299 0 11 products.price 0
36 HaltIfNull 1299 0 10 products.name 0
37 HaltIfNull 1299 0 11 products.price 0
38 NotExists 1 40 8 0
39 Halt 1555 0 0 products.id 0
40 Copy 13 15 0 0 r[15]=r[13]
41 Copy 8 16 0 0 r[16]=r[8]
42 MakeRecord 9 5 14 0 r[14]=mkrec(r[9..13])
43 Copy 13 17 0 0 r[17]=r[13]
44 Copy 8 18 0 0 r[18]=r[8]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ addr opcode p1 p2 p3 p4 p5 comment
12 Goto 0 14 0 0
13 NewRowid 2 2 0 0 r[2]=rowid
14 Affinity 3 4 0 0 r[3..7] = D, B, B, B
15 NotExists 2 17 2 0
16 Halt 1555 0 0 items.id 0
17 Copy 5 8 0 0 r[8]=r[5]
18 Copy 2 9 0 0 r[9]=r[2]
19 Affinity 8 1 0 0 r[8..9] = B
20 NoConflict 0 22 8 1 0 key=r[8..8]
21 Halt 2067 0 0 items.barcode 0
22 Copy 4 10 0 0 r[10]=r[4]
23 Copy 2 11 0 0 r[11]=r[2]
24 Affinity 10 1 0 0 r[10..11] = B
25 NoConflict 1 27 10 1 0 key=r[10..10]
26 Halt 2067 0 0 items.sku 0
27 HaltIfNull 1299 0 4 items.sku 0
28 HaltIfNull 1299 0 5 items.barcode 0
15 HaltIfNull 1299 0 4 items.sku 0
16 HaltIfNull 1299 0 5 items.barcode 0
17 NotExists 2 19 2 0
18 Halt 1555 0 0 items.id 0
19 Copy 5 8 0 0 r[8]=r[5]
20 Copy 2 9 0 0 r[9]=r[2]
21 Affinity 8 1 0 0 r[8..9] = B
22 NoConflict 0 24 8 1 0 key=r[8..8]
23 Halt 2067 0 0 items.barcode 0
24 Copy 4 10 0 0 r[10]=r[4]
25 Copy 2 11 0 0 r[11]=r[2]
26 Affinity 10 1 0 0 r[10..11] = B
27 NoConflict 1 29 10 1 0 key=r[10..10]
28 Halt 2067 0 0 items.sku 0
29 MakeRecord 3 4 7 0 r[7]=mkrec(r[3..6])
30 Copy 5 12 0 0 r[12]=r[5]
31 Copy 2 13 0 0 r[13]=r[2]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ addr opcode p1 p2 p3 p4 p5 comment
12 Goto 0 14 0 0
13 NewRowid 2 2 0 0 r[2]=rowid
14 Affinity 3 4 0 0 r[3..7] = D, B, B, B
15 NotExists 2 17 2 0
16 Halt 1555 0 0 items.id 0
17 Copy 5 8 0 0 r[8]=r[5]
18 Copy 2 9 0 0 r[9]=r[2]
19 Affinity 8 1 0 0 r[8..9] = B
20 NoConflict 0 22 8 1 0 key=r[8..8]
21 Halt 2067 0 0 items.barcode 0
22 Copy 4 10 0 0 r[10]=r[4]
23 Copy 2 11 0 0 r[11]=r[2]
24 Affinity 10 1 0 0 r[10..11] = B
25 NoConflict 1 27 10 1 0 key=r[10..10]
26 Halt 2067 0 0 items.sku 0
27 HaltIfNull 1299 0 4 items.sku 0
28 HaltIfNull 1299 0 5 items.barcode 0
15 HaltIfNull 1299 0 4 items.sku 0
16 HaltIfNull 1299 0 5 items.barcode 0
17 NotExists 2 19 2 0
18 Halt 1555 0 0 items.id 0
19 Copy 5 8 0 0 r[8]=r[5]
20 Copy 2 9 0 0 r[9]=r[2]
21 Affinity 8 1 0 0 r[8..9] = B
22 NoConflict 0 24 8 1 0 key=r[8..8]
23 Halt 2067 0 0 items.barcode 0
24 Copy 4 10 0 0 r[10]=r[4]
25 Copy 2 11 0 0 r[11]=r[2]
26 Affinity 10 1 0 0 r[10..11] = B
27 NoConflict 1 29 10 1 0 key=r[10..10]
28 Halt 2067 0 0 items.sku 0
29 MakeRecord 3 4 7 0 r[7]=mkrec(r[3..6])
30 Copy 5 12 0 0 r[12]=r[5]
31 Copy 2 13 0 0 r[13]=r[2]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@ addr opcode p1 p2 p3 p4 p5 comment
12 Goto 0 14 0 0
13 NewRowid 2 2 0 0 r[2]=rowid
14 Affinity 3 4 0 0 r[3..7] = D, B, B, B
15 NotExists 2 18 2 0
16 Copy 2 1 0 0 r[1]=r[2]
17 Goto 0 43 0 0
18 Copy 5 8 0 0 r[8]=r[5]
19 Copy 2 9 0 0 r[9]=r[2]
20 Affinity 8 1 0 0 r[8..9] = B
21 NoConflict 0 24 8 1 0 key=r[8..8]
22 Goto 0 44 0 0
23 Halt 2067 0 0 items.barcode 0
24 Copy 4 10 0 0 r[10]=r[4]
25 Copy 2 11 0 0 r[11]=r[2]
26 Affinity 10 1 0 0 r[10..11] = B
27 NoConflict 1 30 10 1 0 key=r[10..10]
28 Goto 0 44 0 0
29 Halt 2067 0 0 items.sku 0
30 IsNull 4 44 0 0 if (r[4]==NULL) goto 44
31 IsNull 5 44 0 0 if (r[5]==NULL) goto 44
15 IsNull 4 44 0 0 if (r[4]==NULL) goto 44
16 IsNull 5 44 0 0 if (r[5]==NULL) goto 44
17 NotExists 2 20 2 0
18 Copy 2 1 0 0 r[1]=r[2]
19 Goto 0 43 0 0
20 Copy 5 8 0 0 r[8]=r[5]
21 Copy 2 9 0 0 r[9]=r[2]
22 Affinity 8 1 0 0 r[8..9] = B
23 NoConflict 0 26 8 1 0 key=r[8..8]
24 Goto 0 44 0 0
25 Halt 2067 0 0 items.barcode 0
26 Copy 4 10 0 0 r[10]=r[4]
27 Copy 2 11 0 0 r[11]=r[2]
28 Affinity 10 1 0 0 r[10..11] = B
29 NoConflict 1 32 10 1 0 key=r[10..10]
30 Goto 0 44 0 0
31 Halt 2067 0 0 items.sku 0
32 MakeRecord 3 4 7 0 r[7]=mkrec(r[3..6])
33 Copy 5 12 0 0 r[12]=r[5]
34 Copy 2 13 0 0 r[13]=r[2]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ addr opcode p1 p2 p3 p4 p5 comment
11 Goto 0 13 0 0
12 NewRowid 1 2 0 0 r[2]=rowid
13 Affinity 3 4 0 0 r[3..7] = D, B, B, B
14 NotExists 1 17 2 0
15 Copy 2 1 0 0 r[1]=r[2]
16 Goto 0 32 0 0
17 Copy 4 8 0 0 r[8]=r[4]
18 Copy 2 9 0 0 r[9]=r[2]
19 Affinity 8 1 0 0 r[8..9] = B
20 NoConflict 0 23 8 1 0 key=r[8..8]
21 Goto 0 33 0 0
22 Halt 2067 0 0 users.email 0
23 IsNull 4 33 0 0 if (r[4]==NULL) goto 33
24 IsNull 5 33 0 0 if (r[5]==NULL) goto 33
14 IsNull 4 33 0 0 if (r[4]==NULL) goto 33
15 IsNull 5 33 0 0 if (r[5]==NULL) goto 33
16 NotExists 1 19 2 0
17 Copy 2 1 0 0 r[1]=r[2]
18 Goto 0 32 0 0
19 Copy 4 8 0 0 r[8]=r[4]
20 Copy 2 9 0 0 r[9]=r[2]
21 Affinity 8 1 0 0 r[8..9] = B
22 NoConflict 0 25 8 1 0 key=r[8..8]
23 Goto 0 33 0 0
24 Halt 2067 0 0 users.email 0
25 MakeRecord 3 4 7 0 r[7]=mkrec(r[3..6])
26 Copy 4 10 0 0 r[10]=r[4]
27 Copy 2 11 0 0 r[11]=r[2]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ addr opcode p1 p2 p3 p4 p5 comment
8 Goto 0 10 0 0
9 NewRowid 0 2 0 0 r[2]=rowid
10 Affinity 3 3 0 0 r[3..6] = D, D, B
11 NotExists 0 16 2 0
12 Copy 2 1 0 0 r[1]=r[2]
13 SeekRowid 0 1 26 0 if (r[1]!=cursor 0 for table t_defaults.rowid) goto 26
14 Delete 0 0 0 t_defaults 0
15 Goto 0 16 0 0
16 NotNull 4 18 0 0 r[4]!=NULL -> goto 18
17 Integer 42 4 0 0 r[4]=42
18 HaltIfNull 1299 0 4 t_defaults.val 0
19 NotNull 5 21 0 0 r[5]!=NULL -> goto 21
20 String8 0 5 0 unnamed 0 r[5]='unnamed'
21 HaltIfNull 1299 0 5 t_defaults.name 0
11 NotNull 4 13 0 0 r[4]!=NULL -> goto 13
12 Integer 42 4 0 0 r[4]=42
13 HaltIfNull 1299 0 4 t_defaults.val 0
14 NotNull 5 16 0 0 r[5]!=NULL -> goto 16
15 String8 0 5 0 unnamed 0 r[5]='unnamed'
16 HaltIfNull 1299 0 5 t_defaults.name 0
17 NotExists 0 22 2 0
18 Copy 2 1 0 0 r[1]=r[2]
19 SeekRowid 0 1 26 0 if (r[1]!=cursor 0 for table t_defaults.rowid) goto 26
20 Delete 0 0 0 t_defaults 0
21 Goto 0 22 0 0
22 MakeRecord 3 3 6 0 r[6]=mkrec(r[3..5])
23 Insert 0 6 2 t_defaults 2 intkey=r[2] data=r[6]
24 Goto 0 25 0 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ addr opcode p1 p2 p3 p4 p5 comment
9 Goto 0 11 0 0
10 NewRowid 0 2 0 0 r[2]=rowid
11 Affinity 3 3 0 0 r[3..6] = D, D, B
12 NotExists 0 17 2 0
13 Copy 2 1 0 0 r[1]=r[2]
14 SeekRowid 0 1 27 0 if (r[1]!=cursor 0 for table t_defaults.rowid) goto 27
15 Delete 0 0 0 t_defaults 0
16 Goto 0 17 0 0
17 NotNull 4 19 0 0 r[4]!=NULL -> goto 19
18 Integer 42 4 0 0 r[4]=42
19 HaltIfNull 1299 0 4 t_defaults.val 0
20 NotNull 5 22 0 0 r[5]!=NULL -> goto 22
21 String8 0 5 0 unnamed 0 r[5]='unnamed'
22 HaltIfNull 1299 0 5 t_defaults.name 0
12 NotNull 4 14 0 0 r[4]!=NULL -> goto 14
13 Integer 42 4 0 0 r[4]=42
14 HaltIfNull 1299 0 4 t_defaults.val 0
15 NotNull 5 17 0 0 r[5]!=NULL -> goto 17
16 String8 0 5 0 unnamed 0 r[5]='unnamed'
17 HaltIfNull 1299 0 5 t_defaults.name 0
18 NotExists 0 23 2 0
19 Copy 2 1 0 0 r[1]=r[2]
20 SeekRowid 0 1 27 0 if (r[1]!=cursor 0 for table t_defaults.rowid) goto 27
21 Delete 0 0 0 t_defaults 0
22 Goto 0 23 0 0
23 MakeRecord 3 3 6 0 r[6]=mkrec(r[3..5])
24 Insert 0 6 2 t_defaults 2 intkey=r[2] data=r[6]
25 Goto 0 26 0 0
Expand Down
Loading
Loading