# 2026-03-16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains test cases for handling stale expression indexes - # expression indexes for which the value of the expression is different # from (though usually very close to) the value stored on disk. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix expridx1 # Check that OP_IdxDelete works when: # # 1.1.* The real value in the index is greater than that in the table, # 1.2.* The real value in the index is smaller than that in the table, # 1.3.* Duplicate distorted real values. # do_execsql_test 1.0.1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL); INSERT INTO t1 VALUES(10, 10.0); INSERT INTO t1 VALUES(15, 15.0); INSERT INTO t1 VALUES(20, 20.0); INSERT INTO t1 VALUES(25, 25.0); INSERT INTO t1 VALUES(30, 30.0); CREATE INDEX i1 ON t1((b+0.0)); } # Return a list of rowids from table t1 for which there is no exact # match in the index. set idxcheck { SELECT rowid FROM t1 AS o NOT INDEXED WHERE NOT EXISTS (SELECT 1 FROM t1 WHERE +a=o.a AND b+0.0=o.b+0.0) } set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='i1'}] do_test 1.0.2 { # Create an imposter table for index i1 sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $root db eval { CREATE TABLE x1(b, rowid, PRIMARY KEY(b, rowid)) WITHOUT ROWID; } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 db one { PRAGMA integrity_check } } ok do_execsql_test 1.1.1 { UPDATE x1 SET b=21.0 WHERE rowid=20; } do_execsql_test 1.1.1b { PRAGMA integrity_check; } {{row 3 missing from index i1}} do_execsql_test 1.1.1c $idxcheck {20} do_execsql_test 1.1.2 { DELETE FROM t1 WHERE a=20; } {} do_execsql_test 1.1.3 { PRAGMA integrity_check; } {ok} do_execsql_test 1.1.4 $idxcheck {} do_execsql_test 1.2.1 { UPDATE x1 SET b=26.0 WHERE rowid=25; PRAGMA integrity_check; } { {row 3 missing from index i1} } do_execsql_test 1.2.2 $idxcheck {25} do_execsql_test 1.2.3 { DELETE FROM t1 WHERE a=25; } {} do_execsql_test 1.2.4 { PRAGMA integrity_check; } {ok} do_execsql_test 1.2.5 $idxcheck {} do_execsql_test 1.3.1 { DELETE FROM t1; INSERT INTO t1 VALUES(5, 15.0); INSERT INTO t1 VALUES(10, 20.0); INSERT INTO t1 VALUES(15, 20.0); INSERT INTO t1 VALUES(20, 20.0); INSERT INTO t1 VALUES(25, 20.0); INSERT INTO t1 VALUES(30, 20.0); INSERT INTO t1 VALUES(35, 25.0); UPDATE x1 SET b=19.0 WHERE b=20.0; PRAGMA integrity_check; } { {row 2 missing from index i1} {row 3 missing from index i1} {row 4 missing from index i1} {row 5 missing from index i1} {row 6 missing from index i1} } do_execsql_test 1.3.2 $idxcheck {10 15 20 25 30} foreach {tn a} { 1 15 2 30 3 20 4 10 5 25 } { do_execsql_test 1.3.3.$tn { DELETE FROM t1 WHERE a=$a } } do_execsql_test 1.3.4 { PRAGMA integrity_check } {ok} do_execsql_test 1.3.5 $idxcheck {} #------------------------------------------------------------------------- reset_db set nRow [expr 1000] do_execsql_test 2.0 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)) WITHOUT ROWID; WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow ) INSERT INTO t1 SELECT i, random(), hex(randomblob(50)) FROM s; CREATE INDEX t1c ON t1(+c); } set idxcheck { SELECT a, b FROM t1 AS o NOT INDEXED WHERE NOT EXISTS (SELECT 1 FROM t1 WHERE +a=o.a AND +b=o.b AND +c=o.c) } set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='t1c'}] do_test 2.1 { sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $root db eval { CREATE TABLE x1(a, b, c, PRIMARY KEY(c, a, b)) WITHOUT ROWID; } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 } {} do_execsql_test 2.2 { UPDATE x1 SET c=hex(randomblob(50)) WHERE (a%2)!=0 } do_execsql_test 2.3 " SELECT count(*) FROM ( $idxcheck ) " [expr $nRow/2] do_test 2.4 { for {set ii 1} {$ii<$nRow} {incr ii 2} { execsql { DELETE FROM t1 WHERE a=$ii } } execsql {PRAGMA integrity_check} } {ok} do_execsql_test 2.5 " SELECT count(*) FROM ( $idxcheck ) " 0 #------------------------------------------------------------------------- reset_db do_execsql_test 3.0 { CREATE TABLE y1(a, b, c GENERATED ALWAYS AS (a*b) VIRTUAL); CREATE INDEX i1 ON y1(c); INSERT INTO y1 VALUES(2, 3); INSERT INTO y1 VALUES(4, 5); } set idxcheck { SELECT rowid FROM y1 AS o NOT INDEXED WHERE NOT EXISTS (SELECT 1 FROM y1 WHERE +rowid=o.rowid AND c=o.c) } set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='i1'}] do_test 3.1 { sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $root db eval { CREATE TABLE x1(c, rowid, PRIMARY KEY(c, rowid)) WITHOUT ROWID; } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 } {} do_execsql_test 3.2 { UPDATE x1 SET c=19 WHERE rowid=2; } do_execsql_test 3.3 $idxcheck 2 do_execsql_test 3.4 { DELETE FROM y1 WHERE a=4; } do_execsql_test 3.5 $idxcheck {} #------------------------------------------------------------------------- reset_db do_execsql_test 4.0 { CREATE TABLE z1(a INTEGER PRIMARY KEY, b); CREATE INDEX z1b ON z1(b+0.0); INSERT INTO z1 VALUES(1, 1.0); INSERT INTO z1 VALUES(2, 4.0); INSERT INTO z1 VALUES(3, 4.0); INSERT INTO z1 VALUES(4, 4.0); INSERT INTO z1 VALUES(5, 4.0); INSERT INTO z1 VALUES(6, 4.0); INSERT INTO z1 VALUES(7, 4.0); INSERT INTO z1 VALUES(8, 1.0); } set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='z1b'}] do_test 4.1 { sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $root db eval { CREATE TABLE x1(b, a, PRIMARY KEY(b, a)) WITHOUT ROWID; } sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 } {} do_execsql_test 4.2 { UPDATE x1 SET b=4.000000000000001 WHERE a=2; -- 1 ULP UPDATE x1 SET b=4.000000000000002 WHERE a=3; -- 2 ULP UPDATE x1 SET b=4.000000000000003 WHERE a=4; -- 3 ULP UPDATE x1 SET b=3.9999999999999996 WHERE a=5; -- -1 ULP UPDATE x1 SET b=3.9999999999999992 WHERE a=6; -- -2 ULP UPDATE x1 SET b=3.9999999999999988 WHERE a=7; -- -3 ULP } do_execsql_test 4.3 { PRAGMA integrity_check } { {index z1b stores an imprecise floating-point value for row 2} {index z1b stores an imprecise floating-point value for row 3} {row 4 missing from index z1b} {index z1b stores an imprecise floating-point value for row 5} {index z1b stores an imprecise floating-point value for row 6} {row 7 missing from index z1b} } do_execsql_test 4.4 { UPDATE z1 SET b=-4.0 WHERE b=4.0; PRAGMA integrity_check; } {ok} do_execsql_test 4.5 { UPDATE x1 SET b=-4.000000000000001 WHERE a=2; -- -1 ULP UPDATE x1 SET b=-4.000000000000002 WHERE a=3; -- -2 ULP UPDATE x1 SET b=-4.000000000000003 WHERE a=4; -- -3 ULP UPDATE x1 SET b=-3.9999999999999996 WHERE a=5; -- 1 ULP UPDATE x1 SET b=-3.9999999999999992 WHERE a=6; -- 2 ULP UPDATE x1 SET b=-3.9999999999999988 WHERE a=7; -- 3 ULP } do_execsql_test 4.6 { PRAGMA integrity_check } { {index z1b stores an imprecise floating-point value for row 2} {index z1b stores an imprecise floating-point value for row 3} {row 4 missing from index z1b} {index z1b stores an imprecise floating-point value for row 5} {index z1b stores an imprecise floating-point value for row 6} {row 7 missing from index z1b} } finish_test