blob: e0efa565861fe2ed72ff9c9ef4d804acda023afa [file] [log] [blame] [edit]
# 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