From 08119c361d1181b3e8f1abb429236e488a664753 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 13 Aug 2013 15:42:54 -0400 Subject: Imported Upstream version 2.2.1 --- test/8_3_names.test | 4 +- test/aggnested.test | 160 +++++++++++++++++ test/analyze6.test | 4 +- test/analyze7.test | 2 +- test/auth.test | 31 +++- test/auth2.test | 12 +- test/autoindex1.test | 124 +++++++++++++ test/autovacuum.test | 2 +- test/backcompat.test | 2 + test/backup4.test | 104 +++++++++++ test/backup_ioerr.test | 4 +- test/bigfile.test | 1 + test/bigfile2.test | 1 + test/btreefault.test | 58 ++++++ test/cache.test | 2 +- test/capi2.test | 14 +- test/check.test | 39 ++++ test/close.test | 79 ++++++++ test/closure01.test | 226 +++++++++++++++++++++++ test/collate1.test | 1 + test/collate3.test | 98 ++++++++++ test/collate4.test | 27 +-- test/collate5.test | 4 +- test/conflict.test | 4 + test/corruptD.test | 4 +- test/corruptE.test | 2 +- test/coveridxscan.test | 93 ++++++++++ test/crash5.test | 2 +- test/crash7.test | 34 ++++ test/crypto.test | 52 +++++- test/dbstatus2.test | 11 +- test/descidx3.test | 6 +- test/distinct.test | 24 ++- test/e_createtable.test | 4 +- test/e_fkey.test | 57 +++--- test/e_insert.test | 25 +-- test/e_select.test | 20 +-- test/e_uri.test | 6 +- test/enc2.test | 2 +- test/eqp.test | 16 +- test/errmsg.test | 2 + test/exclusive2.test | 8 + test/filectrl.test | 6 + test/filefmt.test | 35 ++++ test/fkey2.test | 36 +++- test/fkey4.test | 2 + test/fkey5.test | 310 ++++++++++++++++++++++++++++++++ test/fkey_malloc.test | 3 +- test/fts3ai.test | 5 + test/fts3aux1.test | 66 ++++++- test/fts3conf.test | 42 +++++ test/fts3expr3.test | 210 ++++++++++++++++++++++ test/fts3matchinfo.test | 19 ++ test/fts3near.test | 14 ++ test/fts3tok1.test | 117 ++++++++++++ test/fts3tok_err.test | 49 +++++ test/fts4content.test | 101 +++++++++++ test/fts4unicode.test | 17 +- test/full.test | 20 +++ test/func.test | 29 ++- test/fuzzer1.test | 9 +- test/fuzzerfault.test | 10 +- test/hook.test | 1 + test/in5.test | 138 ++++++++++++++ test/incrblob.test | 10 +- test/incrvacuum3.test | 154 ++++++++++++++++ test/incrvacuum_ioerr.test | 7 +- test/index5.test | 8 +- test/instr.test | 210 ++++++++++++++++++++++ test/interrupt.test | 4 +- test/intpkey.test | 2 +- test/io.test | 77 +++++++- test/ioerr6.test | 92 ++++++++++ test/like.test | 6 +- test/limit.test | 147 +++++++++++++++ test/loadext.test | 16 +- test/lock.test | 23 +++ test/malloc.test | 2 +- test/malloc3.test | 82 ++++++--- test/mallocG.test | 5 + test/malloc_common.tcl | 2 +- test/memdb.test | 2 +- test/minmax.test | 92 +++++++++- test/minmax2.test | 2 +- test/misc7.test | 28 +++ test/mmap1.test | 331 ++++++++++++++++++++++++++++++++++ test/mmap2.test | 87 +++++++++ test/notify2.test | 4 +- test/notnull.test | 27 ++- test/numcast.test | 46 +++++ test/orderby1.test | 438 +++++++++++++++++++++++++++++++++++++++++++++ test/orderby2.test | 117 ++++++++++++ test/orderby3.test | 123 +++++++++++++ test/orderby4.test | 56 ++++++ test/pager1.test | 336 +++++++++++++++++++++++++++++++++- test/pager2.test | 19 +- test/pagerfault.test | 299 +++++++++++++++++++++++++++++++ test/pageropt.test | 7 +- test/permutations.test | 17 +- test/pragma.test | 68 ++++++- test/regexp1.test | 211 ++++++++++++++++++++++ test/releasetest.tcl | 39 ++-- test/resolver01.test | 39 ++++ test/selectA.test | 20 +-- test/selectD.test | 174 ++++++++++++++++++ test/selectE.test | 95 ++++++++++ test/shared9.test | 230 ++++++++++++++++++++++++ test/sharedA.test | 188 +++++++++++++++++++ test/shared_err.test | 5 +- test/shell1.test | 78 ++++++-- test/speed1p.test | 3 +- test/spellfix.test | 64 ++++++- test/stat.test | 15 +- test/subquery.test | 2 +- test/subquery2.test | 21 +++ test/syscall.test | 2 +- test/sysfault.test | 32 +++- test/tclsqlite.test | 4 +- test/temptable.test | 2 +- test/tester.tcl | 128 +++++++++---- test/thread001.test | 4 +- test/tkt-2d1a5c67d.test | 2 +- test/tkt-31338dca7e.test | 2 +- test/tkt-385a5b56b9.test | 3 +- test/tkt-4dd95f6943.test | 151 ++++++++++++++++ test/tkt-5d863f876e.test | 2 + test/tkt-6bfb98dfc0.test | 61 +++++++ test/tkt-78e04e52ea.test | 2 +- test/tkt-7a31705a7e6.test | 26 +++ test/tkt-80ba201079.test | 2 +- test/tkt-a7b7803e.test | 84 +++++++++ test/tkt-cbd054fa6b.test | 4 +- test/tkt-fc7bd6358f.test | 78 ++++++++ test/tkt2409.test | 4 +- test/tkt2822.test | 17 +- test/tkt3457.test | 14 ++ test/tkt3527.test | 8 +- test/tkt3762.test | 4 +- test/transitive1.test | 50 ++++++ test/trigger1.test | 57 +++--- test/trigger3.test | 6 + test/triggerA.test | 7 + test/triggerC.test | 58 +++++- test/unique.test | 8 + test/unordered.test | 2 +- test/view.test | 35 ++++ test/vtab1.test | 86 ++++++++- test/wal.test | 13 +- test/wal5.test | 12 +- test/wal8.test | 1 + test/wal9.test | 94 ++++++++++ test/walfault.test | 40 ++++- test/where.test | 68 +++++-- test/where2.test | 45 ++++- test/where8.test | 32 ++++ test/where9.test | 41 ++++- test/whereD.test | 2 +- test/whereE.test | 62 +++++++ test/whereF.test | 115 ++++++++++++ test/win32lock.test | 1 + test/zerodamage.test | 56 +++--- 161 files changed, 7843 insertions(+), 424 deletions(-) create mode 100644 test/backup4.test create mode 100644 test/btreefault.test create mode 100644 test/close.test create mode 100644 test/closure01.test create mode 100644 test/coveridxscan.test create mode 100644 test/fkey5.test create mode 100644 test/fts3expr3.test create mode 100644 test/fts3tok1.test create mode 100644 test/fts3tok_err.test create mode 100644 test/full.test create mode 100644 test/in5.test create mode 100644 test/incrvacuum3.test create mode 100644 test/instr.test create mode 100644 test/ioerr6.test create mode 100644 test/mmap1.test create mode 100644 test/mmap2.test create mode 100644 test/numcast.test create mode 100644 test/orderby1.test create mode 100644 test/orderby2.test create mode 100644 test/orderby3.test create mode 100644 test/orderby4.test create mode 100644 test/regexp1.test create mode 100644 test/resolver01.test create mode 100644 test/selectD.test create mode 100644 test/selectE.test create mode 100644 test/shared9.test create mode 100644 test/sharedA.test create mode 100644 test/tkt-4dd95f6943.test create mode 100644 test/tkt-6bfb98dfc0.test create mode 100644 test/tkt-7a31705a7e6.test create mode 100644 test/tkt-a7b7803e.test create mode 100644 test/tkt-fc7bd6358f.test create mode 100644 test/transitive1.test create mode 100644 test/wal9.test create mode 100644 test/whereE.test create mode 100644 test/whereF.test (limited to 'test') diff --git a/test/8_3_names.test b/test/8_3_names.test index b53e28a..1d63f5d 100644 --- a/test/8_3_names.test +++ b/test/8_3_names.test @@ -150,7 +150,7 @@ db close forcedelete test.db do_test 8_3_names-5.0 { sqlite3 db file:./test.db?8_3_names=1 - register_wholenumber_module db + load_static_extension db wholenumber db eval { PRAGMA journal_mode=WAL; CREATE TABLE t1(x); @@ -160,7 +160,7 @@ do_test 8_3_names-5.0 { UPDATE t1 SET x=x*2; } sqlite3 db2 file:./test.db?8_3_names=1 - register_wholenumber_module db2 + load_static_extension db2 wholenumber db2 eval { BEGIN; SELECT sum(x) FROM t1; diff --git a/test/aggnested.test b/test/aggnested.test index 22f0fb6..6e2fd65 100644 --- a/test/aggnested.test +++ b/test/aggnested.test @@ -68,4 +68,164 @@ do_test aggnested-2.0 { } {A,B,B 3 33 333 3333} db2 close +##################### Test cases for ticket [bfbf38e5e9956ac69f] ############ +# +# This first test case is the original problem report: +do_test aggnested-3.0 { + db eval { + CREATE TABLE AAA ( + aaa_id INTEGER PRIMARY KEY AUTOINCREMENT + ); + CREATE TABLE RRR ( + rrr_id INTEGER PRIMARY KEY AUTOINCREMENT, + rrr_date INTEGER NOT NULL, + rrr_aaa INTEGER + ); + CREATE TABLE TTT ( + ttt_id INTEGER PRIMARY KEY AUTOINCREMENT, + target_aaa INTEGER NOT NULL, + source_aaa INTEGER NOT NULL + ); + insert into AAA (aaa_id) values (2); + insert into TTT (ttt_id, target_aaa, source_aaa) + values (4469, 2, 2); + insert into TTT (ttt_id, target_aaa, source_aaa) + values (4476, 2, 1); + insert into RRR (rrr_id, rrr_date, rrr_aaa) + values (0, 0, NULL); + insert into RRR (rrr_id, rrr_date, rrr_aaa) + values (2, 4312, 2); + SELECT i.aaa_id, + (SELECT sum(CASE WHEN (t.source_aaa == i.aaa_id) THEN 1 ELSE 0 END) + FROM TTT t + ) AS segfault + FROM + (SELECT curr.rrr_aaa as aaa_id + FROM RRR curr + -- you also can comment out the next line + -- it causes segfault to happen after one row is outputted + INNER JOIN AAA a ON (curr.rrr_aaa = aaa_id) + LEFT JOIN RRR r ON (r.rrr_id <> 0 AND r.rrr_date < curr.rrr_date) + GROUP BY curr.rrr_id + HAVING r.rrr_date IS NULL + ) i; + } +} {2 1} + +# Further variants of the test case, as found in the ticket +# +do_test aggnested-3.1 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1 ( + id1 INTEGER PRIMARY KEY AUTOINCREMENT, + value1 INTEGER + ); + INSERT INTO t1 VALUES(4469,2),(4476,1); + CREATE TABLE t2 ( + id2 INTEGER PRIMARY KEY AUTOINCREMENT, + value2 INTEGER + ); + INSERT INTO t2 VALUES(0,1),(2,2); + SELECT + (SELECT sum(value2==xyz) FROM t2) + FROM + (SELECT curr.value1 as xyz + FROM t1 AS curr LEFT JOIN t1 AS other + GROUP BY curr.id1); + } +} {1 1} +do_test aggnested-3.2 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1 ( + id1 INTEGER, + value1 INTEGER, + x1 INTEGER + ); + INSERT INTO t1 VALUES(4469,2,98),(4469,1,99),(4469,3,97); + CREATE TABLE t2 ( + value2 INTEGER + ); + INSERT INTO t2 VALUES(1); + SELECT + (SELECT sum(value2==xyz) FROM t2) + FROM + (SELECT value1 as xyz, max(x1) AS pqr + FROM t1 + GROUP BY id1); + } +} {0} +do_test aggnested-3.3 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(id1, value1); + INSERT INTO t1 VALUES(4469,2),(4469,1); + CREATE TABLE t2 (value2); + INSERT INTO t2 VALUES(1); + SELECT (SELECT sum(value2=value1) FROM t2), max(value1) + FROM t1 + GROUP BY id1; + } +} {0 2} + +# A batch of queries all doing approximately the same operation involving +# two nested aggregate queries. +# +do_test aggnested-3.11 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(id1, value1); + INSERT INTO t1 VALUES(4469,12),(4469,11),(4470,34); + CREATE INDEX t1id1 ON t1(id1); + CREATE TABLE t2 (value2); + INSERT INTO t2 VALUES(12),(34),(34); + INSERT INTO t2 SELECT value2 FROM t2; + + SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=max(value1)) + FROM t1 + GROUP BY id1; + } +} {12 2 34 4} +do_test aggnested-3.12 { + db eval { + SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=value1) + FROM t1 + GROUP BY id1; + } +} {12 2 34 4} +do_test aggnested-3.13 { + db eval { + SELECT value1, (SELECT sum(value2=value1) FROM t2) + FROM t1; + } +} {12 2 11 0 34 4} +do_test aggnested-3.14 { + db eval { + SELECT value1, (SELECT sum(value2=value1) FROM t2) + FROM t1 + WHERE value1 IN (SELECT max(value1) FROM t1 GROUP BY id1); + } +} {12 2 34 4} +do_test aggnested-3.15 { + # FIXME: If case 3.16 works, then this case really ought to work too... + catchsql { + SELECT max(value1), (SELECT sum(value2=max(value1)) FROM t2) + FROM t1 + GROUP BY id1; + } +} {1 {misuse of aggregate function max()}} +do_test aggnested-3.16 { + db eval { + SELECT max(value1), (SELECT sum(value2=value1) FROM t2) + FROM t1 + GROUP BY id1; + } +} {12 2 34 4} + + finish_test diff --git a/test/analyze6.test b/test/analyze6.test index 74b7ec7..eaa9d73 100644 --- a/test/analyze6.test +++ b/test/analyze6.test @@ -61,14 +61,14 @@ do_test analyze6-1.0 { # do_test analyze6-1.1 { eqp {SELECT count(*) FROM ev, cat WHERE x=y} -} {0 0 1 {SCAN TABLE cat (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} +} {0 0 1 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} # The same plan is chosen regardless of the order of the tables in the # FROM clause. # do_test analyze6-1.2 { eqp {SELECT count(*) FROM cat, ev WHERE x=y} -} {0 0 0 {SCAN TABLE cat (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} +} {0 0 0 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} # Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30 diff --git a/test/analyze7.test b/test/analyze7.test index 5bdb04d..46ec39e 100644 --- a/test/analyze7.test +++ b/test/analyze7.test @@ -26,7 +26,7 @@ ifcapable {!analyze||!vtab} { # Generate some test data # do_test analyze7-1.0 { - register_wholenumber_module db + load_static_extension db wholenumber execsql { CREATE TABLE t1(a,b,c,d); CREATE INDEX t1a ON t1(a); diff --git a/test/auth.test b/test/auth.test index 211ae7e..fd402b1 100644 --- a/test/auth.test +++ b/test/auth.test @@ -2262,7 +2262,9 @@ do_test auth-4.3 { SQLITE_SELECT {} {} {} v1 \ SQLITE_READ t2 a main v1 \ SQLITE_READ t2 b main v1 \ - SQLITE_SELECT {} {} {} {} \ + SQLITE_READ v1 x main v1 \ + SQLITE_READ v1 x main v1 \ + SQLITE_SELECT {} {} {} v1 \ SQLITE_READ v1 x main v1 \ SQLITE_INSERT v1chng {} main r2 \ SQLITE_READ v1 x main r2 \ @@ -2288,7 +2290,9 @@ do_test auth-4.5 { SQLITE_SELECT {} {} {} v1 \ SQLITE_READ t2 a main v1 \ SQLITE_READ t2 b main v1 \ - SQLITE_SELECT {} {} {} {} \ + SQLITE_READ v1 x main v1 \ + SQLITE_READ v1 x main v1 \ + SQLITE_SELECT {} {} {} v1 \ SQLITE_READ v1 x main v1 \ SQLITE_INSERT v1chng {} main r3 \ SQLITE_READ v1 x main r3 \ @@ -2364,6 +2368,29 @@ ifcapable trigger { } {1} } +# Ticket [0eb70d77cb05bb22720]: Invalid pointer passsed to the authorizer +# callback when updating a ROWID. +# +do_test auth-6.1 { + execsql { + CREATE TABLE t6(a,b,c,d,e,f,g,h); + INSERT INTO t6 VALUES(1,2,3,4,5,6,7,8); + } +} {} +set ::authargs [list] +proc auth {args} { + eval lappend ::authargs $args + return SQLITE_OK +} +do_test auth-6.2 { + execsql {UPDATE t6 SET rowID=rowID+100} + set ::authargs +} [list SQLITE_READ t6 ROWID main {} \ + SQLITE_UPDATE t6 ROWID main {} \ +] +do_test auth-6.3 { + execsql {SELECT rowid, * FROM t6} +} {101 1 2 3 4 5 6 7 8} rename proc {} rename proc_real proc diff --git a/test/auth2.test b/test/auth2.test index f5dba14..9343fd6 100644 --- a/test/auth2.test +++ b/test/auth2.test @@ -131,12 +131,12 @@ do_test auth2-2.3 { } set ::authargs } {SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 a main {} -SQLITE_READ v2 b main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 a main {} +SQLITE_READ v2 b main {} SQLITE_SELECT {} {} {} v2 } do_test auth2-2.4 { @@ -149,20 +149,20 @@ do_test auth2-2.4 { } set ::authargs } {SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 b main {} -SQLITE_READ v2 a main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 -SQLITE_SELECT {} {} {} v2 -SQLITE_SELECT {} {} {} {} SQLITE_READ v2 b main {} SQLITE_READ v2 a main {} +SQLITE_SELECT {} {} {} v2 +SQLITE_SELECT {} {} {} {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 b main {} +SQLITE_READ v2 a main {} SQLITE_SELECT {} {} {} v2 } db2 close diff --git a/test/autoindex1.test b/test/autoindex1.test index 6c8d54d..54ff82a 100644 --- a/test/autoindex1.test +++ b/test/autoindex1.test @@ -257,5 +257,129 @@ do_execsql_test autoindex1-700 { 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } +# The following checks a performance issue reported on the sqlite-dev +# mailing list on 2013-01-10 +# +do_execsql_test autoindex1-800 { + CREATE TABLE accounts( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + account_name TEXT, + account_type TEXT, + data_set TEXT + ); + CREATE TABLE data( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + package_id INTEGER REFERENCES package(_id), + mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL, + raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL, + is_read_only INTEGER NOT NULL DEFAULT 0, + is_primary INTEGER NOT NULL DEFAULT 0, + is_super_primary INTEGER NOT NULL DEFAULT 0, + data_version INTEGER NOT NULL DEFAULT 0, + data1 TEXT, + data2 TEXT, + data3 TEXT, + data4 TEXT, + data5 TEXT, + data6 TEXT, + data7 TEXT, + data8 TEXT, + data9 TEXT, + data10 TEXT, + data11 TEXT, + data12 TEXT, + data13 TEXT, + data14 TEXT, + data15 TEXT, + data_sync1 TEXT, + data_sync2 TEXT, + data_sync3 TEXT, + data_sync4 TEXT + ); + CREATE TABLE mimetypes( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + mimetype TEXT NOT NULL + ); + CREATE TABLE raw_contacts( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + account_id INTEGER REFERENCES accounts(_id), + sourceid TEXT, + raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0, + version INTEGER NOT NULL DEFAULT 1, + dirty INTEGER NOT NULL DEFAULT 0, + deleted INTEGER NOT NULL DEFAULT 0, + contact_id INTEGER REFERENCES contacts(_id), + aggregation_mode INTEGER NOT NULL DEFAULT 0, + aggregation_needed INTEGER NOT NULL DEFAULT 1, + custom_ringtone TEXT, + send_to_voicemail INTEGER NOT NULL DEFAULT 0, + times_contacted INTEGER NOT NULL DEFAULT 0, + last_time_contacted INTEGER, + starred INTEGER NOT NULL DEFAULT 0, + display_name TEXT, + display_name_alt TEXT, + display_name_source INTEGER NOT NULL DEFAULT 0, + phonetic_name TEXT, + phonetic_name_style TEXT, + sort_key TEXT, + sort_key_alt TEXT, + name_verified INTEGER NOT NULL DEFAULT 0, + sync1 TEXT, + sync2 TEXT, + sync3 TEXT, + sync4 TEXT, + sync_uid TEXT, + sync_version INTEGER NOT NULL DEFAULT 1, + has_calendar_event INTEGER NOT NULL DEFAULT 0, + modified_time INTEGER, + is_restricted INTEGER DEFAULT 0, + yp_source TEXT, + method_selected INTEGER DEFAULT 0, + custom_vibration_type INTEGER DEFAULT 0, + custom_ringtone_path TEXT, + message_notification TEXT, + message_notification_path TEXT + ); + CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1); + CREATE INDEX data_raw_contact_id ON data (raw_contact_id); + CREATE UNIQUE INDEX mime_type ON mimetypes (mimetype); + CREATE INDEX raw_contact_sort_key1_index ON raw_contacts (sort_key); + CREATE INDEX raw_contact_sort_key2_index ON raw_contacts (sort_key_alt); + CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id); + CREATE INDEX raw_contacts_source_id_account_id_index + ON raw_contacts (sourceid, account_id); + ANALYZE sqlite_master; + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contact_sort_key2_index','1600 4'); + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contact_sort_key1_index','1600 4'); + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contacts_source_id_account_id_index', + '1600 1600 1600'); + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contacts_contact_id_index','1600 1'); + INSERT INTO sqlite_stat1 VALUES('mimetypes','mime_type','12 1'); + INSERT INTO sqlite_stat1 + VALUES('data','data_mimetype_data1_index','9819 2455 3'); + INSERT INTO sqlite_stat1 VALUES('data','data_raw_contact_id','9819 7'); + INSERT INTO sqlite_stat1 VALUES('accounts',NULL,'1'); + DROP TABLE IF EXISTS sqlite_stat3; + ANALYZE sqlite_master; + + EXPLAIN QUERY PLAN + SELECT * FROM + data JOIN mimetypes ON (data.mimetype_id=mimetypes._id) + JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id) + JOIN accounts ON (raw_contacts.account_id=accounts._id) + WHERE mimetype_id=10 AND data14 IS NOT NULL; +} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/} +do_execsql_test autoindex1-801 { + EXPLAIN QUERY PLAN + SELECT * FROM + data JOIN mimetypes ON (data.mimetype_id=mimetypes._id) + JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id) + JOIN accounts ON (raw_contacts.account_id=accounts._id) + WHERE mimetypes._id=10 AND data14 IS NOT NULL; +} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/} finish_test diff --git a/test/autovacuum.test b/test/autovacuum.test index 1aef18f..bba40e3 100644 --- a/test/autovacuum.test +++ b/test/autovacuum.test @@ -114,7 +114,7 @@ foreach delete_order $delete_orders { } do_test autovacuum-1.$tn.($delete).3 { execsql { - select a from av1 + select a from av1 order by rowid } } $::tbl_data } diff --git a/test/backcompat.test b/test/backcompat.test index 509dfe5..dd40fed 100644 --- a/test/backcompat.test +++ b/test/backcompat.test @@ -213,7 +213,9 @@ unset ::incompatible # do_allbackcompat_test { if {[code1 {sqlite3 -version}] >= "3.7.0" + && [code1 {set ::sqlite_options(wal)}] && [code2 {sqlite3 -version}] >= "3.7.0" + && [code2 {set ::sqlite_options(wal)}] } { do_test backcompat-2.1.1 { sql1 { diff --git a/test/backup4.test b/test/backup4.test new file mode 100644 index 0000000..a1a7735 --- /dev/null +++ b/test/backup4.test @@ -0,0 +1,104 @@ +# 2012 October 13 +# +# 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. +# +#*********************************************************************** +# +# The tests in this file verify that if an empty database (zero bytes in +# size) is used as the source of a backup operation, the final destination +# database is one page in size. +# +# The destination must consist of at least one page as truncating a +# database file to zero bytes is equivalent to resetting the database +# schema cookie and change counter. Doing that could cause other clients +# to become confused and continue using out-of-date cache data. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix backup4 + +#------------------------------------------------------------------------- +# At one point this test was failing because [db] was using an out of +# date schema in test case 1.2. +# +do_execsql_test 1.0 { + CREATE TABLE t1(x, y, UNIQUE(x, y)); + INSERT INTO t1 VALUES('one', 'two'); + SELECT * FROM t1 WHERE x='one'; + PRAGMA integrity_check; +} {one two ok} + +do_test 1.1 { + sqlite3 db1 :memory: + db1 backup test.db + sqlite3 db1 test.db + db1 eval { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES('one', 'two'); + } + db1 close +} {} + +do_execsql_test 1.2 { + SELECT * FROM t1 WHERE x='one'; + PRAGMA integrity_check; +} {one two ok} + +db close +forcedelete test.db +forcedelete test.db2 +sqlite3 db test.db + +#------------------------------------------------------------------------- +# Test that if the source is zero bytes, the destination database +# consists of a single page only. +# +do_execsql_test 2.1 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); +} +do_test 2.2 { file size test.db } [expr $AUTOVACUUM ? 4096 : 3072] + +do_test 2.3 { + sqlite3 db1 test.db2 + db1 backup test.db + db1 close + file size test.db +} {1024} + +do_test 2.4 { file size test.db2 } 0 + +db close +forcedelete test.db +forcedelete test.db2 +sqlite3 db test.db + +#------------------------------------------------------------------------- +# Test that if the destination has a page-size larger than the implicit +# page-size of the source, the final destination database still consists +# of a single page. +# +do_execsql_test 3.1 { + PRAGMA page_size = 4096; + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); +} +do_test 3.2 { file size test.db } [expr $AUTOVACUUM ? 16384 : 12288] + +do_test 3.3 { + sqlite3 db1 test.db2 + db1 backup test.db + db1 close + file size test.db +} {1024} + +do_test 3.4 { file size test.db2 } 0 + +finish_test + diff --git a/test/backup_ioerr.test b/test/backup_ioerr.test index 313cff3..ca3fd32 100644 --- a/test/backup_ioerr.test +++ b/test/backup_ioerr.test @@ -115,7 +115,7 @@ proc clear_ioerr_simulation {} { # reported, then the backup process is concluded with a call to # backup_finish(). # -# Test that if an IO error occurs, or if one occured while updating +# Test that if an IO error occurs, or if one occurred while updating # the backup database during step 4, then the conditions listed # under step 3 are all true. # @@ -214,7 +214,7 @@ for {set iError 1} {$bStop == 0} {incr iError} { set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb] if {[lindex $rc 0] && $::sqlite_io_error_persist==0} { - # The IO error occured while updating the source database. In this + # The IO error occurred while updating the source database. In this # case the backup should be able to continue. set rc [B step 5000] if { $rc != "SQLITE_IOERR_UNLOCK" } { diff --git a/test/bigfile.test b/test/bigfile.test index 59e9f18..31c6323 100644 --- a/test/bigfile.test +++ b/test/bigfile.test @@ -16,6 +16,7 @@ # if {[file exists skip-big-file]} return +if {$tcl_platform(os)=="Darwin"} return set testdir [file dirname $argv0] source $testdir/tester.tcl diff --git a/test/bigfile2.test b/test/bigfile2.test index 1f0ea85..b67cb34 100644 --- a/test/bigfile2.test +++ b/test/bigfile2.test @@ -14,6 +14,7 @@ # if {[file exists skip-big-file]} return +if {$tcl_platform(os)=="Darwin"} return set testdir [file dirname $argv0] source $testdir/tester.tcl diff --git a/test/btreefault.test b/test/btreefault.test new file mode 100644 index 0000000..9b42240 --- /dev/null +++ b/test/btreefault.test @@ -0,0 +1,58 @@ +# 2013 April 02 +# +# 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 fault injection tests designed to test the btree.c +# module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set testprefix btreefault + +# This test will not work with an in-memory journal, as the database will +# become corrupt if an error is injected into a transaction after it starts +# writing data out to the db file. +if {[permutation]=="inmemory_journal"} { + finish_test + return +} + +do_test 1-pre1 { + execsql { + PRAGMA auto_vacuum = incremental; + PRAGMA journal_mode = DELETE; + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES(randomblob(1000), randomblob(100)); + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + DELETE FROM t1 WHERE rowid%2; + } + faultsim_save_and_close +} {} + +do_faultsim_test 1 -prep { + faultsim_restore_and_reopen + set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY a" -1 DUMMY] + sqlite3_step $::STMT + sqlite3_step $::STMT +} -body { + execsql { PRAGMA incremental_vacuum = 10 } +} -test { + sqlite3_finalize $::STMT + faultsim_test_result {0 {}} + faultsim_integrity_check +} + +finish_test + diff --git a/test/cache.test b/test/cache.test index f81948b..ffc25c4 100644 --- a/test/cache.test +++ b/test/cache.test @@ -46,7 +46,7 @@ do_test cache-1.2 { # leaked, but would not be reused until the pager-cache was full (i.e. # 2000 pages by default). # -# This tests that once the pager-cache is initialised, it can be locked +# This tests that once the pager-cache is initialized, it can be locked # and unlocked repeatedly without internally allocating any new pages. # set cache_size [pager_cache_size db] diff --git a/test/capi2.test b/test/capi2.test index 8b36ac6..20ee340 100644 --- a/test/capi2.test +++ b/test/capi2.test @@ -235,8 +235,9 @@ do_test capi2-3.13 { do_test capi2-3.13b {db changes} {0} do_test capi2-3.14 { - list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] -} {SQLITE_CONSTRAINT {column a is not unique}} + list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \ + [sqlite3_extended_errcode $DB] +} {SQLITE_CONSTRAINT {column a is not unique} SQLITE_CONSTRAINT_UNIQUE} do_test capi2-3.15 { set VM [sqlite3_prepare $DB {CREATE TABLE t2(a NOT NULL, b)} -1 TAIL] set TAIL @@ -258,8 +259,9 @@ do_test capi2-3.18 { [get_column_names $VM] } {SQLITE_ERROR 0 {} {}} do_test capi2-3.19 { - list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] -} {SQLITE_CONSTRAINT {t2.a may not be NULL}} + list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \ + [sqlite3_extended_errcode $DB] +} {SQLITE_CONSTRAINT {t2.a may not be NULL} SQLITE_CONSTRAINT_NOTNULL} do_test capi2-3.20 { execsql { @@ -278,8 +280,8 @@ do_test capi2-3.23 { sqlite3_finalize $VM } {SQLITE_CONSTRAINT} do_test capi2-3.24 { - sqlite3_errcode $DB -} {SQLITE_CONSTRAINT} + list [sqlite3_errcode $DB] [sqlite3_extended_errcode $DB] +} {SQLITE_CONSTRAINT SQLITE_CONSTRAINT_UNIQUE} # Two or more virtual machines exists at the same time. # diff --git a/test/check.test b/test/check.test index bf0b770..99b72ac 100644 --- a/test/check.test +++ b/test/check.test @@ -15,6 +15,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix check # Only run these tests if the build includes support for CHECK constraints ifcapable !check { @@ -413,4 +414,42 @@ do_test check-6.15 { } +#-------------------------------------------------------------------------- +# If a connection opens a database that contains a CHECK constraint that +# uses an unknown UDF, the schema should not be considered malformed. +# Attempting to modify the table should fail (since the CHECK constraint +# cannot be tested). +# +reset_db +proc myfunc {x} {expr $x < 10} +db func myfunc myfunc + +do_execsql_test 7.1 { CREATE TABLE t6(a CHECK (myfunc(a))) } +do_execsql_test 7.2 { INSERT INTO t6 VALUES(9) } +do_catchsql_test 7.3 { INSERT INTO t6 VALUES(11) } {1 {constraint failed}} + +do_test 7.4 { + sqlite3 db2 test.db + execsql { SELECT * FROM t6 } db2 +} {9} + +do_test 7.5 { + catchsql { INSERT INTO t6 VALUES(8) } db2 +} {1 {unknown function: myfunc()}} + +do_test 7.6 { + catchsql { CREATE TABLE t7(a CHECK (myfunc(a))) } db2 +} {1 {no such function: myfunc}} + +do_test 7.7 { + db2 func myfunc myfunc + execsql { INSERT INTO t6 VALUES(8) } db2 +} {} + +do_test 7.8 { + db2 func myfunc myfunc + catchsql { INSERT INTO t6 VALUES(12) } db2 +} {1 {constraint failed}} + + finish_test diff --git a/test/close.test b/test/close.test new file mode 100644 index 0000000..355a886 --- /dev/null +++ b/test/close.test @@ -0,0 +1,79 @@ +# 2013 May 14 +# +# 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. +# +#*********************************************************************** +# +# Test some specific circumstances to do with shared cache mode. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix close + +do_execsql_test 1.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('one'); + INSERT INTO t1 VALUES('two'); + INSERT INTO t1 VALUES('three'); +} +db close + +do_test 1.1 { + set DB [sqlite3_open test.db] + sqlite3_close_v2 $DB +} {SQLITE_OK} + +do_test 1.2.1 { + set DB [sqlite3_open test.db] + set STMT [sqlite3_prepare $DB "SELECT * FROM t1" -1 dummy] + sqlite3_close_v2 $DB +} {SQLITE_OK} +do_test 1.2.2 { + sqlite3_finalize $STMT +} {SQLITE_OK} + +do_test 1.3.1 { + set DB [sqlite3_open test.db] + set STMT [sqlite3_prepare $DB "SELECT * FROM t1" -1 dummy] + sqlite3_step $STMT + sqlite3_close_v2 $DB +} {SQLITE_OK} + +do_test 1.3.2 { + sqlite3_column_text $STMT 0 +} {one} + +do_test 1.3.3 { + sqlite3_finalize $STMT +} {SQLITE_OK} + +do_test 1.4.1 { + set DB [sqlite3_open test.db] + set STMT [sqlite3_prepare $DB "SELECT * FROM t1" -1 dummy] + sqlite3_step $STMT + sqlite3_close_v2 $DB +} {SQLITE_OK} + +do_test 1.4.2 { + list [sqlite3_step $STMT] [sqlite3_column_text $STMT 0] +} {SQLITE_ROW two} + +do_test 1.4.3 { + list [catch { + sqlite3_prepare $DB "SELECT * FROM sqlite_master" -1 dummy + } msg] $msg +} {1 {(21) library routine called out of sequence}} + +do_test 1.4.4 { + sqlite3_finalize $STMT +} {SQLITE_OK} + +finish_test + diff --git a/test/closure01.test b/test/closure01.test new file mode 100644 index 0000000..5dac87a --- /dev/null +++ b/test/closure01.test @@ -0,0 +1,226 @@ +# 2013-04-25 +# +# 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. +# +#*********************************************************************** +# +# Test cases for transitive_closure virtual table. + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix closure01 + +ifcapable !vtab { finish_test ; return } + +load_static_extension db closure + +do_execsql_test 1.0 { + BEGIN; + CREATE TABLE t1(x INTEGER PRIMARY KEY, y INTEGER); + CREATE INDEX t1y ON t1(y); + INSERT INTO t1(x) VALUES(1),(2); + INSERT INTO t1(x) SELECT x+2 FROM t1; + INSERT INTO t1(x) SELECT x+4 FROM t1; + INSERT INTO t1(x) SELECT x+8 FROM t1; + INSERT INTO t1(x) SELECT x+16 FROM t1; + INSERT INTO t1(x) SELECT x+32 FROM t1; + INSERT INTO t1(x) SELECT x+64 FROM t1; + INSERT INTO t1(x) SELECT x+128 FROM t1; + INSERT INTO t1(x) SELECT x+256 FROM t1; + INSERT INTO t1(x) SELECT x+512 FROM t1; + INSERT INTO t1(x) SELECT x+1024 FROM t1; + INSERT INTO t1(x) SELECT x+2048 FROM t1; + INSERT INTO t1(x) SELECT x+4096 FROM t1; + INSERT INTO t1(x) SELECT x+8192 FROM t1; + INSERT INTO t1(x) SELECT x+16384 FROM t1; + INSERT INTO t1(x) SELECT x+32768 FROM t1; + INSERT INTO t1(x) SELECT x+65536 FROM t1; + UPDATE t1 SET y=x/2 WHERE x>1; + COMMIT; + CREATE VIRTUAL TABLE cx + USING transitive_closure(tablename=t1, idcolumn=x, parentcolumn=y); +} {} + +# The entire table +do_execsql_test 1.1 { + SELECT count(*), depth FROM cx WHERE root=1 GROUP BY depth ORDER BY 1; +} {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/} + +# descendents of 32768 +do_execsql_test 1.2 { + SELECT * FROM cx WHERE root=32768 ORDER BY id; +} {32768 0 65536 1 65537 1 131072 2} + +# descendents of 16384 +do_execsql_test 1.3 { + SELECT * FROM cx WHERE root=16384 AND depth<=2 ORDER BY id; +} {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2} + +# children of 16384 +do_execsql_test 1.4 { + SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx + WHERE root=16384 + AND depth=1 + ORDER BY id; +} {32768 1 {} t1 x y 32769 1 {} t1 x y} + +# great-grandparent of 16384 +do_execsql_test 1.5 { + SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx + WHERE root=16384 + AND depth=3 + AND idcolumn='Y' + AND parentcolumn='X'; +} {2048 3 {} t1 Y X} + +# depth<5 +do_execsql_test 1.6 { + SELECT count(*), depth FROM cx WHERE root=1 AND depth<5 + GROUP BY depth ORDER BY 1; +} {1 0 2 1 4 2 8 3 16 4} + +# depth<=5 +do_execsql_test 1.7 { + SELECT count(*), depth FROM cx WHERE root=1 AND depth<=5 + GROUP BY depth ORDER BY 1; +} {1 0 2 1 4 2 8 3 16 4 32 5} + +# depth==5 +do_execsql_test 1.8 { + SELECT count(*), depth FROM cx WHERE root=1 AND depth=5 + GROUP BY depth ORDER BY 1; +} {32 5} + +# depth BETWEEN 3 AND 5 +do_execsql_test 1.9 { + SELECT count(*), depth FROM cx WHERE root=1 AND depth BETWEEN 3 AND 5 + GROUP BY depth ORDER BY 1; +} {8 3 16 4 32 5} + +# depth==5 with min() and max() +do_execsql_test 1.10 { + SELECT count(*), min(id), max(id) FROM cx WHERE root=1 AND depth=5; +} {32 32 63} + +# Create a much smaller table t2 with only 32 elements +db eval { + CREATE TABLE t2(x INTEGER PRIMARY KEY, y INTEGER); + INSERT INTO t2 SELECT x, y FROM t1 WHERE x<32; + CREATE INDEX t2y ON t2(y); + CREATE VIRTUAL TABLE c2 + USING transitive_closure(tablename=t2, idcolumn=x, parentcolumn=y); +} + +# t2 full-table +do_execsql_test 2.1 { + SELECT count(*), min(id), max(id) FROM c2 WHERE root=1; +} {31 1 31} +# t2 root=10 +do_execsql_test 2.2 { + SELECT id FROM c2 WHERE root=10; +} {10 20 21} +# t2 root=11 +do_execsql_test 2.3 { + SELECT id FROM c2 WHERE root=12; +} {12 24 25} +# t2 root IN [10,12] +do_execsql_test 2.4 { + SELECT id FROM c2 WHERE root IN (10,12) ORDER BY id; +} {10 12 20 21 24 25} +# t2 root IN [10,12] (sorted) +do_execsql_test 2.5 { + SELECT id FROM c2 WHERE root IN (10,12) ORDER BY +id; +} {10 12 20 21 24 25} + +# t2 c2up from 20 +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE c2up USING transitive_closure( + tablename = t2, + idcolumn = y, + parentcolumn = x + ); + SELECT id FROM c2up WHERE root=20; +} {1 2 5 10 20} + +# cx as c2up +do_execsql_test 3.1 { + SELECT id FROM cx + WHERE root=20 + AND tablename='t2' + AND idcolumn='y' + AND parentcolumn='x'; +} {1 2 5 10 20} + +# t2 first cousins of 20 +do_execsql_test 3.2 { + SELECT DISTINCT id FROM c2 + WHERE root IN (SELECT id FROM c2up + WHERE root=20 AND depth<=2) + ORDER BY id; +} {5 10 11 20 21 22 23} + +# t2 first cousins of 20 +do_execsql_test 3.3 { + SELECT id FROM c2 + WHERE root=(SELECT id FROM c2up + WHERE root=20 AND depth=2) + AND depth=2 + EXCEPT + SELECT id FROM c2 + WHERE root=(SELECT id FROM c2up + WHERE root=20 AND depth=1) + AND depth<=1 + ORDER BY id; +} {22 23} + +# missing tablename. +do_test 4.1 { + catchsql { + SELECT id FROM cx + WHERE root=20 + AND tablename='t3' + AND idcolumn='y' + AND parentcolumn='x'; + } +} {1 {no such table: t3}} + +# missing idcolumn +do_test 4.2 { + catchsql { + SELECT id FROM cx + WHERE root=20 + AND tablename='t2' + AND idcolumn='xyz' + AND parentcolumn='x'; + } +} {1 {no such column: t2.xyz}} + +# missing parentcolumn +do_test 4.3 { + catchsql { + SELECT id FROM cx + WHERE root=20 + AND tablename='t2' + AND idcolumn='x' + AND parentcolumn='pqr'; + } +} {1 {no such column: t2.pqr}} + +# generic closure +do_execsql_test 5.1 { + CREATE VIRTUAL TABLE temp.closure USING transitive_closure; + SELECT id FROM closure + WHERE root=1 + AND depth=3 + AND tablename='t1' + AND idcolumn='x' + AND parentcolumn='y' + ORDER BY id; +} {8 9 10 11 12 13 14 15} + +finish_test diff --git a/test/collate1.test b/test/collate1.test index ac2c75b..b493ee8 100644 --- a/test/collate1.test +++ b/test/collate1.test @@ -75,6 +75,7 @@ do_test collate1-1.1 { } } {{} 0x119 0x2D} do_test collate1-1.2 { +breakpoint execsql { SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex; } diff --git a/test/collate3.test b/test/collate3.test index c4dbfbe..2c051cb 100644 --- a/test/collate3.test +++ b/test/collate3.test @@ -55,6 +55,104 @@ execsql { DROP TABLE collate3t1; } +proc caseless {a b} { string compare -nocase $a $b } +do_test collate3-1.4 { + db collate caseless caseless + execsql { + CREATE TABLE t1(a COLLATE caseless); + INSERT INTO t1 VALUES('Abc2'); + INSERT INTO t1 VALUES('abc1'); + INSERT INTO t1 VALUES('aBc3'); + } + execsql { SELECT * FROM t1 ORDER BY a } +} {abc1 Abc2 aBc3} + +do_test collate3-1.5 { + db close + sqlite3 db test.db + catchsql { SELECT * FROM t1 ORDER BY a } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.6.1 { + db collate caseless caseless + execsql { CREATE INDEX i1 ON t1(a) } + execsql { SELECT * FROM t1 ORDER BY a } +} {abc1 Abc2 aBc3} + +do_test collate3-1.6.2 { + db close + sqlite3 db test.db + catchsql { SELECT * FROM t1 ORDER BY a } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.6.3 { + db close + sqlite3 db test.db + catchsql { PRAGMA integrity_check } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.6.4 { + db close + sqlite3 db test.db + catchsql { REINDEX } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.1 { + db collate caseless caseless + execsql { + DROP TABLE t1; + CREATE TABLE t1(a); + CREATE INDEX i1 ON t1(a COLLATE caseless); + INSERT INTO t1 VALUES('Abc2'); + INSERT INTO t1 VALUES('abc1'); + INSERT INTO t1 VALUES('aBc3'); + SELECT * FROM t1 ORDER BY a COLLATE caseless; + } +} {abc1 Abc2 aBc3} + +do_test collate3-1.7.2 { + db close + sqlite3 db test.db + catchsql { SELECT * FROM t1 ORDER BY a COLLATE caseless} +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.4 { + db close + sqlite3 db test.db + catchsql { REINDEX } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.3 { + db close + sqlite3 db test.db + catchsql { PRAGMA integrity_check } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.4 { + db close + sqlite3 db test.db + catchsql { REINDEX } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.5 { + db close + sqlite3 db test.db + db collate caseless caseless + catchsql { PRAGMA integrity_check } +} {0 ok} + +proc needed {nm} { db collate caseless caseless } +do_test collate3-1.7.6 { + db close + sqlite3 db test.db + db collation_needed needed + catchsql { PRAGMA integrity_check } +} {0 ok} + +do_test collate3-1.8 { + execsql { DROP TABLE t1 } +} {} + # # Create a table with a default collation sequence, then close # and re-open the database without re-registering the collation diff --git a/test/collate4.test b/test/collate4.test index 12bc16e..5ae1e7b 100644 --- a/test/collate4.test +++ b/test/collate4.test @@ -60,7 +60,7 @@ proc cksort {sql} { # # Because these tests also exercise all the different ways indices # can be created, they also serve to verify that indices are correctly -# initialised with user-defined collation sequences when they are +# initialized with user-defined collation sequences when they are # created. # # Tests named collate4-1.1.* use indices with a single column. Tests @@ -94,7 +94,7 @@ do_test collate4-1.1.5 { cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE TEXT} } {{} A B a b nosort} do_test collate4-1.1.6 { - cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE NOCASE} + cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE NOCASE, rowid} } {{} a A b B sort} do_test collate4-1.1.7 { @@ -171,13 +171,13 @@ do_test collate4-1.1.21 { } } {} do_test collate4-1.1.22 { - cksort {SELECT a FROM collate4t4 ORDER BY a} + cksort {SELECT a FROM collate4t4 ORDER BY a, rowid} } {{} a A b B sort} do_test collate4-1.1.23 { - cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE NOCASE} + cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE NOCASE, rowid} } {{} a A b B sort} do_test collate4-1.1.24 { - cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE TEXT} + cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE TEXT, rowid} } {{} A B a b nosort} do_test collate4-1.1.25 { cksort {SELECT b FROM collate4t4 ORDER BY b} @@ -222,7 +222,7 @@ do_test collate4-1.2.4 { cksort {SELECT a FROM collate4t1 ORDER BY a, b} } {{} A a B b nosort} do_test collate4-1.2.5 { - cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE nocase} + cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE nocase, rowid} } {{} a A b B sort} do_test collate4-1.2.6 { cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE text} @@ -271,10 +271,10 @@ do_test collate4-1.2.14 { } } {} do_test collate4-1.2.15 { - cksort {SELECT a FROM collate4t3 ORDER BY a} + cksort {SELECT a FROM collate4t3 ORDER BY a, rowid} } {{} a A b B sort} do_test collate4-1.2.16 { - cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE nocase} + cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE nocase, rowid} } {{} a A b B sort} do_test collate4-1.2.17 { cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text} @@ -364,7 +364,8 @@ do_test collate4-2.1.4 { CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT); } count { - SELECT * FROM collate4t2, collate4t1 WHERE a = b; + SELECT * FROM collate4t2, collate4t1 WHERE a = b + ORDER BY collate4t2.rowid, collate4t1.rowid } } {A a A A 19} do_test collate4-2.1.5 { @@ -375,7 +376,8 @@ do_test collate4-2.1.5 { ifcapable subquery { do_test collate4-2.1.6 { count { - SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2); + SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2) + ORDER BY rowid } } {a A 10} do_test collate4-2.1.7 { @@ -384,7 +386,8 @@ ifcapable subquery { CREATE INDEX collate4i1 ON collate4t1(a); } count { - SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2); + SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2) + ORDER BY rowid } } {a A 6} do_test collate4-2.1.8 { @@ -398,7 +401,7 @@ ifcapable subquery { CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT); } count { - SELECT a FROM collate4t1 WHERE a IN ('z', 'a'); + SELECT a FROM collate4t1 WHERE a IN ('z', 'a') ORDER BY rowid; } } {a A 9} } diff --git a/test/collate5.test b/test/collate5.test index e5bea7a..5f8697e 100644 --- a/test/collate5.test +++ b/test/collate5.test @@ -221,7 +221,7 @@ do_test collate5-3.0 { execsql { SELECT a FROM collate5t1 UNION ALL SELECT a FROM collate5t2 ORDER BY 1; } -} {a A a A b B b B n N} +} {/[aA] [aA] [aA] [aA] [bB] [bB] [bB] [bB] [nN] [nN]/} do_test collate5-3.1 { execsql { SELECT a FROM collate5t2 UNION ALL SELECT a FROM collate5t1 ORDER BY 1; @@ -282,7 +282,7 @@ do_test collate5-4.2 { execsql { SELECT a, b, count(*) FROM collate5t1 GROUP BY a, b ORDER BY a, b; } -} {A 1.0 2 b 2 1 B 3 1} +} {/[aA] 1(.0)? 2 [bB] 2 1 [bB] 3 1/} do_test collate5-4.3 { execsql { DROP TABLE collate5t1; diff --git a/test/conflict.test b/test/conflict.test index 17c7263..6576959 100644 --- a/test/conflict.test +++ b/test/conflict.test @@ -580,6 +580,7 @@ do_test conflict-9.19 { SELECT * FROM t2; } } {1 {column e is not unique}} +verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE do_test conflict-9.20 { catch {execsql {COMMIT}} execsql {SELECT * FROM t3} @@ -592,6 +593,7 @@ do_test conflict-9.21 { SELECT * FROM t2; } } {1 {column e is not unique}} +verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE do_test conflict-9.22 { catch {execsql {COMMIT}} execsql {SELECT * FROM t3} @@ -781,6 +783,7 @@ do_test conflict-12.3 { UPDATE t5 SET a=a+1 WHERE a=1; } } {1 {PRIMARY KEY must be unique}} +verify_ex_errcode conflict-12.3b SQLITE_CONSTRAINT_PRIMARYKEY do_test conflict-12.4 { execsql { UPDATE OR REPLACE t5 SET a=a+1 WHERE a=1; @@ -802,6 +805,7 @@ do_test conflict-13.1 { REPLACE INTO t13 VALUES(2); } } {1 {constraint failed}} +verify_ex_errcode conflict-13.1b SQLITE_CONSTRAINT_CHECK do_test conflict-13.2 { execsql { REPLACE INTO t13 VALUES(3); diff --git a/test/corruptD.test b/test/corruptD.test index 393d41e..2423cd4 100644 --- a/test/corruptD.test +++ b/test/corruptD.test @@ -107,12 +107,12 @@ proc restore_file {} { do_test corruptD-1.1.1 { incr_change_counter hexio_write test.db [expr 1024+1] FFFF - catchsql { SELECT * FROM t1 } + catchsql { SELECT * FROM t1 ORDER BY rowid } } {1 {database disk image is malformed}} do_test corruptD-1.1.2 { incr_change_counter hexio_write test.db [expr 1024+1] [hexio_render_int32 1021] - catchsql { SELECT * FROM t1 } + catchsql { SELECT * FROM t1 ORDER BY rowid } } {1 {database disk image is malformed}} #------------------------------------------------------------------------- diff --git a/test/corruptE.test b/test/corruptE.test index 507721d..48292ab 100644 --- a/test/corruptE.test +++ b/test/corruptE.test @@ -49,7 +49,7 @@ do_test corruptE-1.1 { INSERT OR IGNORE INTO t1 SELECT x*17,y FROM t1; INSERT OR IGNORE INTO t1 SELECT x*19,y FROM t1; CREATE INDEX t1i1 ON t1(x); - CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0; + CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0 ORDER BY rowid; COMMIT; } } {} diff --git a/test/coveridxscan.test b/test/coveridxscan.test new file mode 100644 index 0000000..7b3c0b0 --- /dev/null +++ b/test/coveridxscan.test @@ -0,0 +1,93 @@ +# 2012 September 17 +# +# 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. +# +#*********************************************************************** +# +# Tests for the optimization which attempts to use a covering index +# for a full-table scan (under the theory that the index will be smaller +# and require less I/O and hence will run faster.) +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix coveridxscan + +do_test 1.1 { + db eval { + CREATE TABLE t1(a,b,c); + INSERT INTO t1 VALUES(5,4,3), (4,8,2), (3,2,1); + CREATE INDEX t1ab ON t1(a,b); + CREATE INDEX t1b ON t1(b); + SELECT a FROM t1; + } + # covering index used for the scan, hence values are increasing +} {3 4 5} + +do_test 1.2 { + db eval { + SELECT a, c FROM t1; + } + # There is no covering index, hence the values are in rowid order +} {5 3 4 2 3 1} + +do_test 1.3 { + db eval { + SELECT b FROM t1; + } + # Choice of two indices: use the one with fewest columns +} {2 4 8} + +do_test 2.1 { + optimization_control db cover-idx-scan 0 + db eval {SELECT a FROM t1} + # With the optimization turned off, output in rowid order +} {5 4 3} +do_test 2.2 { + db eval {SELECT a, c FROM t1} +} {5 3 4 2 3 1} +do_test 2.3 { + db eval {SELECT b FROM t1} +} {4 8 2} + +db close +sqlite3_shutdown +sqlite3_config_cis 0 +sqlite3 db test.db + +do_test 3.1 { + db eval {SELECT a FROM t1} + # With the optimization configured off, output in rowid order +} {5 4 3} +do_test 3.2 { + db eval {SELECT a, c FROM t1} +} {5 3 4 2 3 1} +do_test 3.3 { + db eval {SELECT b FROM t1} +} {4 8 2} + +db close +sqlite3_shutdown +sqlite3_config_cis 1 +sqlite3 db test.db + +# The CIS optimization is enabled again. Covering indices are once again +# used for all table scans. +do_test 4.1 { + db eval {SELECT a FROM t1} +} {3 4 5} +do_test 4.2 { + db eval {SELECT a, c FROM t1} +} {5 3 4 2 3 1} +do_test 4.3 { + db eval {SELECT b FROM t1} +} {2 4 8} + + +finish_test diff --git a/test/crash5.test b/test/crash5.test index a786712..83d1647 100644 --- a/test/crash5.test +++ b/test/crash5.test @@ -65,7 +65,7 @@ for {set ii 0} {$ii < 10} {incr ii} { # puts "$n $msg ac=[sqlite3_get_autocommit db]" # If the transaction is still active (it may not be if the malloc() - # failure occured in the OS layer), write to the database. Make sure + # failure occurred in the OS layer), write to the database. Make sure # page 4 is among those written. # if {![sqlite3_get_autocommit db]} { diff --git a/test/crash7.test b/test/crash7.test index 4554a2a..482999c 100644 --- a/test/crash7.test +++ b/test/crash7.test @@ -13,6 +13,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix crash7 ifcapable !crashtest { finish_test @@ -79,4 +80,37 @@ foreach f [list test.db test.db-journal] { } } +db close +forcedelete test.db +sqlite3 db test.db +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, UNIQUE(a, b)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM t1; + DELETE FROM t1 WHERE rowid%2; +} +db_save_and_close + +for {set i 0} {$i < 20} {incr i} { + db_restore_and_reopen + do_test 2.[expr $i+1].1 { + crashsql -file test.db -seed $i {VACUUM} + } {1 {child process exited abnormally}} + do_execsql_test 2.[expr $i+1].2 { PRAGMA integrity_check } {ok} +} + + finish_test diff --git a/test/crypto.test b/test/crypto.test index e8bbe85..8a58fe0 100644 --- a/test/crypto.test +++ b/test/crypto.test @@ -62,6 +62,32 @@ proc setup {file key} { db close } +proc get_cipher_provider {} { + sqlite_orig db test.db + return [execsql { + PRAGMA key = 'test'; + PRAGMA cipher_provider; + }]; +} + +proc if_built_with_openssl {name cmd expected} { + if {[get_cipher_provider] == "openssl"} { + do_test $name $cmd $expected + } +} + +proc if_built_with_libtomcrypt {name cmd expected} { + if {[get_cipher_provider] == "libtomcrypt"} { + do_test $name $cmd $expected + } +} + +proc if_built_with_commoncrypto {name cmd expected} { + if {[get_cipher_provider] == "commoncrypto"} { + do_test $name $cmd $expected + } +} + # The database is initially empty. # set an hex key create some basic data # create table and insert operations should work @@ -1392,7 +1418,7 @@ do_test verify-pragma-cipher-version { execsql { PRAGMA cipher_version; } -} {2.1.1} +} {2.2.1} db close file delete -force test.db @@ -1735,7 +1761,7 @@ file delete -force test.db # verify the pragma cipher # reports the default value -do_test verify-pragma-cipher-default { +if_built_with_openssl verify-pragma-cipher-default { sqlite_orig db test.db execsql { PRAGMA key = 'test'; @@ -1747,7 +1773,7 @@ file delete -force test.db # verify the pragma cipher # reports a change in value -do_test verify-pragma-cipher-changed { +if_built_with_openssl verify-pragma-cipher-changed { sqlite_orig db test.db execsql { PRAGMA key = 'test'; @@ -1856,4 +1882,24 @@ do_test 2.0-beta-to-2.0-migration { db close file delete -force test.db +if_built_with_libtomcrypt verify-default-cipher { + sqlite_orig db test.db + execsql { + PRAGMA key='test'; + PRAGMA cipher; + } +} {rijndael} +db close +file delete -force test.db + +if_built_with_commoncrypto verify-default-cipher { + sqlite_orig db test.db + execsql { + PRAGMA key='test'; + PRAGMA cipher; + } +} {aes-256-cbc} +db close +file delete -force test.db + finish_test diff --git a/test/dbstatus2.test b/test/dbstatus2.test index b2ec156..2541a1a 100644 --- a/test/dbstatus2.test +++ b/test/dbstatus2.test @@ -40,6 +40,7 @@ proc db_write {db {reset 0}} { do_test 1.1 { db close sqlite3 db test.db + execsql { PRAGMA mmap_size = 0 } expr {[file size test.db] / 1024} } 6 @@ -85,10 +86,12 @@ do_test 2.3 { db_write db 1 } {0 4 0} do_test 2.4 { db_write db 0 } {0 0 0} do_test 2.5 { db_write db 1 } {0 0 0} -do_test 2.6 { - execsql { PRAGMA journal_mode = WAL } - db_write db 1 -} {0 1 0} +ifcapable wal { + do_test 2.6 { + execsql { PRAGMA journal_mode = WAL } + db_write db 1 + } {0 1 0} +} do_test 2.7 { execsql { INSERT INTO t1 VALUES(5, randomblob(600)) } db_write db diff --git a/test/descidx3.test b/test/descidx3.test index 3cc87af..c375acc 100644 --- a/test/descidx3.test +++ b/test/descidx3.test @@ -132,11 +132,11 @@ ifcapable subquery { # the IN(...) operator is not available. Hence these tests cannot be # run. do_test descidx3-4.1 { - execsql { + lsort [execsql { UPDATE t1 SET a=2 WHERE i<6; SELECT i FROM t1 WHERE a IN (1,2) AND b>0 AND b<'zzz'; - } - } {8 6 2 4 3} + }] + } {2 3 4 6 8} do_test descidx3-4.2 { execsql { UPDATE t1 SET a=1; diff --git a/test/distinct.test b/test/distinct.test index 3a33544..fcbe4e6 100644 --- a/test/distinct.test +++ b/test/distinct.test @@ -168,17 +168,33 @@ foreach {tn sql temptables res} { 6 "b FROM t1" {hash} {b B} 7 "a FROM t1" {} {A a} 8 "b COLLATE nocase FROM t1" {} {b} - 9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {B} + 9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {b} } { do_execsql_test 2.$tn.1 "SELECT DISTINCT $sql" $res do_temptables_test 2.$tn.2 "SELECT DISTINCT $sql" $temptables } do_execsql_test 2.A { - SELECT (SELECT DISTINCT o.a FROM t1 AS i) FROM t1 AS o; + SELECT (SELECT DISTINCT o.a FROM t1 AS i) FROM t1 AS o ORDER BY rowid; } {a A a A} - - +do_test 3.0 { + db eval { + CREATE TABLE t3(a INTEGER, b INTEGER, c, UNIQUE(a,b)); + INSERT INTO t3 VALUES + (null, null, 1), + (null, null, 2), + (null, 3, 4), + (null, 3, 5), + (6, null, 7), + (6, null, 8); + SELECT DISTINCT a, b FROM t3 ORDER BY +a, +b; + } +} {{} {} {} 3 6 {}} +do_test 3.1 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT DISTINCT a, b FROM t3 ORDER BY +a, +b; + }] +} {0} finish_test diff --git a/test/e_createtable.test b/test/e_createtable.test index 8221828..351a0f7 100644 --- a/test/e_createtable.test +++ b/test/e_createtable.test @@ -1257,7 +1257,7 @@ do_createtable_tests 4.4 { # SQLite allows NULL values in a PRIMARY KEY column. # # If the column is an integer primary key, attempting to insert a NULL -# into the column triggers the auto-increment behaviour. Attempting +# into the column triggers the auto-increment behavior. Attempting # to use UPDATE to set an ipk column to a NULL value is an error. # do_createtable_tests 4.5.1 { @@ -1591,7 +1591,7 @@ foreach {tn tbl res ac data} { " $res do_test e_createtable-4.17.$tn.3 { sqlite3_get_autocommit db } $ac - do_execsql_test 4.17.$tn.4 "SELECT * FROM $tbl" $data + do_execsql_test 4.17.$tn.4 "SELECT * FROM $tbl ORDER BY rowid" $data } catchsql COMMIT diff --git a/test/e_fkey.test b/test/e_fkey.test index 5b27e03..001ba6c 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -627,7 +627,8 @@ proc test_efkey_57 {tn isError sql} { execsql $sql do_test e_fkey-18.$tn { catchsql { INSERT INTO t2 VALUES(NULL) } - } [lindex {{0 {}} {1 {foreign key mismatch}}} $isError] + } [lindex {{0 {}} {/1 {foreign key mismatch - ".*" referencing ".*"}/}} \ + $isError] } test_efkey_57 2 0 { CREATE TABLE t1(x PRIMARY KEY) } test_efkey_57 3 0 { CREATE TABLE t1(x UNIQUE) } @@ -698,16 +699,16 @@ do_test e_fkey-19.2 { } {} do_test e_fkey-19.2 { catchsql { INSERT INTO child4 VALUES('xxx', 5) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child4" referencing "parent"}} do_test e_fkey-19.3 { catchsql { INSERT INTO child5 VALUES('xxx', 6) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child5" referencing "parent"}} do_test e_fkey-19.4 { catchsql { INSERT INTO child6 VALUES(2, 3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child6" referencing "parent"}} do_test e_fkey-19.5 { catchsql { INSERT INTO child7 VALUES(3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child7" referencing "parent"}} #------------------------------------------------------------------------- # Test errors in the database schema that are detected while preparing @@ -765,12 +766,12 @@ do_test e_fkey-20.1 { foreach {tn tbl ptbl err} { 2 c1 {} "no such table: main.nosuchtable" - 3 c2 p2 "foreign key mismatch" - 4 c3 p3 "foreign key mismatch" - 5 c4 p4 "foreign key mismatch" - 6 c5 p5 "foreign key mismatch" - 7 c6 p6 "foreign key mismatch" - 8 c7 p7 "foreign key mismatch" + 3 c2 p2 "foreign key mismatch - \"c2\" referencing \"p2\"" + 4 c3 p3 "foreign key mismatch - \"c3\" referencing \"p3\"" + 5 c4 p4 "foreign key mismatch - \"c4\" referencing \"p4\"" + 6 c5 p5 "foreign key mismatch - \"c5\" referencing \"p5\"" + 7 c6 p6 "foreign key mismatch - \"c6\" referencing \"p6\"" + 8 c7 p7 "foreign key mismatch - \"c7\" referencing \"p7\"" } { do_test e_fkey-20.$tn.1 { catchsql "INSERT INTO $tbl VALUES('a', 'b')" @@ -820,22 +821,22 @@ do_test e_fkey-21.2 { } {} do_test e_fkey-21.3 { catchsql { INSERT INTO child9 VALUES('I') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.4 { catchsql { INSERT INTO child9 VALUES('II') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.5 { catchsql { INSERT INTO child9 VALUES(NULL) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.6 { catchsql { INSERT INTO child10 VALUES('I', 'II', 'III') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} do_test e_fkey-21.7 { catchsql { INSERT INTO child10 VALUES(1, 2, 3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} do_test e_fkey-21.8 { catchsql { INSERT INTO child10 VALUES(NULL, NULL, NULL) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} #------------------------------------------------------------------------- # Test errors that are reported when creating the child table. @@ -1151,7 +1152,7 @@ do_test e_fkey-28.8 { CREATE TABLE c(a, b, FOREIGN KEY(a,b) REFERENCES p); } catchsql {DELETE FROM p} -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c" referencing "p"}} do_test e_fkey-28.9 { drop_all_tables execsql { @@ -1159,7 +1160,7 @@ do_test e_fkey-28.9 { CREATE TABLE c(a REFERENCES p); } catchsql {DELETE FROM p} -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c" referencing "p"}} #------------------------------------------------------------------------- @@ -2060,7 +2061,7 @@ do_test e_fkey-45.1 { do_test e_fkey-45.2 { execsql { DELETE FROM pA WHERE rowid = 3; - SELECT quote(x) FROM pA; + SELECT quote(x) FROM pA ORDER BY rowid; } } {X'0000' X'9999' X'1234'} do_test e_fkey-45.3 { @@ -2069,7 +2070,7 @@ do_test e_fkey-45.3 { do_test e_fkey-45.4 { execsql { UPDATE pA SET x = X'8765' WHERE rowid = 4; - SELECT quote(x) FROM pA; + SELECT quote(x) FROM pA ORDER BY rowid; } } {X'0000' X'9999' X'8765'} do_test e_fkey-45.5 { @@ -2325,7 +2326,7 @@ do_test e_fkey-51.1 { do_test e_fkey-51.2 { execsql { UPDATE parent SET x = 22; - SELECT * FROM parent ; SELECT 'xxx' ; SELECT a FROM child; + SELECT * FROM parent ORDER BY rowid; SELECT 'xxx' ; SELECT a FROM child; } } {22 21 23 xxx 22} do_test e_fkey-51.3 { @@ -2335,7 +2336,7 @@ do_test e_fkey-51.3 { INSERT INTO parent VALUES(-1); INSERT INTO child VALUES(-1); UPDATE parent SET x = 22; - SELECT * FROM parent ; SELECT 'xxx' ; SELECT a FROM child; + SELECT * FROM parent ORDER BY rowid; SELECT 'xxx' ; SELECT a FROM child; } } {22 23 21 xxx 23} @@ -2729,19 +2730,19 @@ do_test e_fkey-60.3 { do_test e_fkey-60.4 { execsql { CREATE TABLE nosuchtable(x PRIMARY KEY) } catchsql { DELETE FROM p } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c2" referencing "p"}} do_test e_fkey-60.5 { execsql { DROP TABLE c1 } catchsql { DELETE FROM p } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c2" referencing "p"}} do_test e_fkey-60.6 { execsql { DROP TABLE c2 } execsql { DELETE FROM p } } {} #------------------------------------------------------------------------- -# Test that the special behaviours of ALTER and DROP TABLE are only -# activated when foreign keys are enabled. Special behaviours are: +# Test that the special behaviors of ALTER and DROP TABLE are only +# activated when foreign keys are enabled. Special behaviors are: # # 1. ADD COLUMN not allowing a REFERENCES clause with a non-NULL # default value. @@ -2836,7 +2837,7 @@ foreach zMatch [list SIMPLE PARTIAL FULL Simple parTIAL FuLL ] { do_test e_fkey-62.$zMatch.2 { execsql { INSERT INTO p VALUES(1, 2, 3) } - # MATCH SIMPLE behaviour: Allow any child key that contains one or more + # MATCH SIMPLE behavior: Allow any child key that contains one or more # NULL value to be inserted. Non-NULL values do not have to map to any # parent key values, so long as at least one field of the child key is # NULL. diff --git a/test/e_insert.test b/test/e_insert.test index ac4361f..951ae24 100644 --- a/test/e_insert.test +++ b/test/e_insert.test @@ -141,8 +141,8 @@ do_insert_tests e_insert-0 { delete_all_data -# EVIDENCE-OF: R-20288-20462 The first form (with the "VALUES" keyword) -# creates a single new row in an existing table. +# EVIDENCE-OF: R-21490-41092 The first form (with the "VALUES" keyword) +# creates one or more new rows in an existing table. # do_insert_tests e_insert-1.1 { 0 "SELECT count(*) FROM a2" {0} @@ -152,11 +152,14 @@ do_insert_tests e_insert-1.1 { 2a "INSERT INTO a2(a, b) VALUES(1, 2)" {} 2b "SELECT count(*) FROM a2" {2} + + 3a "INSERT INTO a2(a) VALUES(3),(4)" {} + 3b "SELECT count(*) FROM a2" {4} } -# EVIDENCE-OF: R-36040-20870 If no column-list is specified then the -# number of values must be the same as the number of columns in the -# table. +# EVIDENCE-OF: R-53616-44976 If no column-list is specified then the +# number of values inserted into each row must be the same as the number +# of columns in the table. # # A test in the block above verifies that if the VALUES list has the # correct number of columns (for table a2, 3 columns) works. So these @@ -171,9 +174,10 @@ do_insert_tests e_insert-1.2 -error { 4 "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5} } -# EVIDENCE-OF: R-04006-57648 In this case the result of evaluating the -# left-most expression in the VALUES list is inserted into the left-most -# column of the new row, and so on. +# EVIDENCE-OF: R-34231-22576 In this case the result of evaluating the +# left-most expression in each term of the VALUES list is inserted into +# the left-most column of the each new row, and forth for each +# subsequent expression. # delete_all_data do_insert_tests e_insert-1.3 { @@ -187,8 +191,9 @@ do_insert_tests e_insert-1.3 { 3b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {2 x y} } -# EVIDENCE-OF: R-62524-00361 If a column-list is specified, then the -# number of values must match the number of specified columns. +# EVIDENCE-OF: R-44710-64652 If a column-list is specified, then the +# number of values in each term of the VALUS list must match the number +# of specified columns. # do_insert_tests e_insert-1.4 -error { %d values for %d columns diff --git a/test/e_select.test b/test/e_select.test index e5949af..ea44aed 100644 --- a/test/e_select.test +++ b/test/e_select.test @@ -1023,10 +1023,10 @@ do_execsql_test e_select-4.9.0 { # do_select_tests e_select-4.9 { 1 "SELECT group_concat(one), two FROM b1 GROUP BY two" { - 4,5 f 1 o 7,6 s 3,2 t + /#,# f 1 o #,# s #,# t/ } 2 "SELECT group_concat(one), sum(one) FROM b1 GROUP BY (one>4)" { - 1,4,3,2 10 5,7,6 18 + 1,2,3,4 10 5,6,7 18 } 3 "SELECT group_concat(one) FROM b1 GROUP BY (two>'o'), one%2" { 4 1,5 2,6 3,7 @@ -1040,7 +1040,7 @@ do_select_tests e_select-4.9 { # values are considered equal. # do_select_tests e_select-4.10 { - 1 "SELECT group_concat(y) FROM b2 GROUP BY x" {0,1 3 2,4} + 1 "SELECT group_concat(y) FROM b2 GROUP BY x" {/#,# 3 #,#/} 2 "SELECT count(*) FROM b2 GROUP BY CASE WHEN y<4 THEN NULL ELSE 0 END" {4 1} } @@ -1227,7 +1227,7 @@ do_select_tests e_select-5.1 { # the entire set of result rows are returned by the SELECT. # # EVIDENCE-OF: R-47911-02086 If neither ALL or DISTINCT are present, -# then the behaviour is as if ALL were specified. +# then the behavior is as if ALL were specified. # # EVIDENCE-OF: R-14442-41305 If the simple SELECT is a SELECT DISTINCT, # then duplicate rows are removed from the set of result rows before it @@ -1745,12 +1745,12 @@ do_select_tests e_select-8.4 { 1 2 7 1 2 8 1 4 93 1 5 -1 } 8 "SELECT z, x FROM d1 ORDER BY 2" { - 3 1 8 1 7 1 -20 1 - 93 1 -1 1 -1 2 93 2 + /# 1 # 1 # 1 # 1 + # 1 # 1 # 2 # 2/ } 9 "SELECT z, x FROM d1 ORDER BY 1" { - -20 1 -1 2 -1 1 3 1 - 7 1 8 1 93 2 93 1 + /-20 1 -1 # -1 # 3 1 + 7 1 8 1 93 # 93 #/ } } @@ -1766,10 +1766,10 @@ do_select_tests e_select-8.5 { 94 94 9 8 4 0 0 -19 } 3 "SELECT z AS x, x AS z FROM d1 ORDER BY z" { - 3 1 8 1 7 1 -20 1 93 1 -1 1 -1 2 93 2 + /# 1 # 1 # 1 # 1 # 1 # 1 # 2 # 2/ } 4 "SELECT z AS x, x AS z FROM d1 ORDER BY x" { - -20 1 -1 2 -1 1 3 1 7 1 8 1 93 2 93 1 + /-20 1 -1 # -1 # 3 1 7 1 8 1 93 # 93 #/ } } diff --git a/test/e_uri.test b/test/e_uri.test index 8c9949e..ac34ed4 100644 --- a/test/e_uri.test +++ b/test/e_uri.test @@ -261,9 +261,9 @@ foreach {tn uri error} " } -# EVIDENCE-OF: R-09651-31805 If "ro" is specified, then the database is +# EVIDENCE-OF: R-43036-46756 If "ro" is specified, then the database is # opened for read-only access, just as if the SQLITE_OPEN_READONLY flag -# had been set in the third argument to sqlite3_prepare_v2(). +# had been set in the third argument to sqlite3_open_v2(). # # EVIDENCE-OF: R-40137-26050 If the mode option is set to "rw", then the # database is opened for read-write (but not create) access, as if @@ -361,7 +361,7 @@ foreach {tn uri error} " # # EVIDENCE-OF: R-19510-48080 If sqlite3_open_v2() is used and the # "cache" parameter is present in a URI filename, its value overrides -# any behaviour requested by setting SQLITE_OPEN_PRIVATECACHE or +# any behavior requested by setting SQLITE_OPEN_PRIVATECACHE or # SQLITE_OPEN_SHAREDCACHE flag. # set orig [sqlite3_enable_shared_cache] diff --git a/test/enc2.test b/test/enc2.test index 415bc0f..3eb3aa2 100644 --- a/test/enc2.test +++ b/test/enc2.test @@ -32,7 +32,7 @@ ifcapable {!utf16} { # enc2.3.*: Simple tests with a UTF-16BE db. # enc2.4.*: Test that attached databases must have the same text encoding # as the main database. -# enc2.5.*: Test the behaviour of the library when a collation sequence is +# enc2.5.*: Test the behavior of the library when a collation sequence is # not available for the most desirable text encoding. # enc2.6.*: Similar test for user functions. # enc2.7.*: Test that the VerifyCookie opcode protects against assuming the diff --git a/test/eqp.test b/test/eqp.test index 0e663f0..454f2af 100644 --- a/test/eqp.test +++ b/test/eqp.test @@ -62,7 +62,7 @@ do_eqp_test 1.3 { do_eqp_test 1.4 { SELECT a FROM t1 ORDER BY +a } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 1.5 { @@ -166,7 +166,7 @@ det 2.3.2 "SELECT min(x) FROM t2" { 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)} } det 2.3.3 "SELECT min(x), max(x) FROM t2" { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} } det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" { @@ -339,7 +339,7 @@ do_eqp_test 4.3.1 { SELECT x FROM t1 UNION SELECT x FROM t2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } @@ -347,7 +347,7 @@ do_eqp_test 4.3.2 { SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 } { 2 0 0 {SCAN TABLE t1 (~1000000 rows)} - 3 0 0 {SCAN TABLE t2 (~1000000 rows)} + 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)} 4 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)} @@ -447,7 +447,7 @@ det 5.8.1 "SELECT c, d FROM t2 ORDER BY c" { det 5.9 { SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2 } { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2} @@ -471,7 +471,7 @@ det 5.10 { # (c=?) (~10 rows) 0|1|1|SCAN TABLE t1 (~1000000 rows) det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" { 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?) (~10 rows)} - 0 1 1 {SCAN TABLE t1 (~1000000 rows)} + 0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} } # EVIDENCE-OF: R-40701-42164 sqlite> EXPLAIN QUERY PLAN SELECT a FROM @@ -479,8 +479,8 @@ det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" { # 2|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|COMPOUND SUBQUERIES 1 AND 2 # USING TEMP B-TREE (UNION) det 5.12 "SELECT a FROM t1 UNION SELECT c FROM t2" { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } diff --git a/test/errmsg.test b/test/errmsg.test index 9f8409b..6b3f3b7 100644 --- a/test/errmsg.test +++ b/test/errmsg.test @@ -80,12 +80,14 @@ do_test 2.2 { SQLITE_ERROR {SQL logic error or missing database} SQLITE_CONSTRAINT {column b is not unique} }] +verify_ex_errcode 2.2b SQLITE_CONSTRAINT_UNIQUE do_test 2.3 { error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')" } [list {*}{ SQLITE_CONSTRAINT {column b is not unique} SQLITE_CONSTRAINT {column b is not unique} }] +verify_ex_errcode 2.3b SQLITE_CONSTRAINT_UNIQUE #------------------------------------------------------------------------- # Test SQLITE_SCHEMA errors. And, for _v2(), test that if the schema diff --git a/test/exclusive2.test b/test/exclusive2.test index 2208da5..54203e3 100644 --- a/test/exclusive2.test +++ b/test/exclusive2.test @@ -25,6 +25,14 @@ ifcapable {!pager_pragmas} { return } +# Tests in this file verify that locking_mode=exclusive causes SQLite to +# use cached pages even if the database is changed on disk. This doesn't +# work with mmap. +if {[permutation]=="mmap"} { + finish_test + return +} + # This module does not work right if the cache spills at unexpected # moments. So disable the soft-heap-limit. # diff --git a/test/filectrl.test b/test/filectrl.test index 1e4ec59..1d878bf 100644 --- a/test/filectrl.test +++ b/test/filectrl.test @@ -36,6 +36,12 @@ do_test filectrl-1.5 { sqlite3 db test_control_lockproxy.db file_control_lockproxy_test db [get_pwd] } {} +do_test filectrl-1.6 { + sqlite3 db test.db + set fn [file_control_tempfilename db] + puts -nonewline \[$fn\] + set fn +} {/etilqs_/} db close forcedelete .test_control_lockproxy.db-conch test.proxy finish_test diff --git a/test/filefmt.test b/test/filefmt.test index 1165cd6..bc1af18 100644 --- a/test/filefmt.test +++ b/test/filefmt.test @@ -213,4 +213,39 @@ do_execsql_test filefmt-3.3 { PRAGMA integrity_check; } {ok} +reset_db +do_execsql_test filefmt-4.1 { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x, y); + CREATE TABLE t2(x, y); + + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; +} + +do_test filefmt-4.2 { + sql36231 { INSERT INTO t2 SELECT * FROM t1 } +} {} + +do_test filefmt-4.3 { + forcedelete bak.db + db backup bak.db +} {} + +do_test filefmt-4.4 { + sqlite3 db2 bak.db + db2 eval { PRAGMA integrity_check } +} {ok} +db2 close + finish_test + diff --git a/test/fkey2.test b/test/fkey2.test index f0cc4d2..3e5b27c 100644 --- a/test/fkey2.test +++ b/test/fkey2.test @@ -139,14 +139,21 @@ set FkeySimpleTests { 4.17 "UPDATE t7 SET a = 10" {0 {}} 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}} - 5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}} + 5.2 "INSERT INTO t10 VALUES(1, 3)" + {1 {foreign key mismatch - "t10" referencing "t9"}} } do_test fkey2-1.1.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { - do_test fkey2-1.1.$tn { catchsql $zSql } $res + do_test fkey2-1.1.$tn.1 { catchsql $zSql } $res + do_test fkey2-1.1.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.1.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.1.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.1.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.1.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.1.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } drop_all_tables @@ -155,6 +162,12 @@ do_test fkey2-1.2.0 { } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.2.$tn { catchsql $zSql } $res + do_test fkey2-1.2.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.2.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.2.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.2.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.2.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.2.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } drop_all_tables @@ -165,6 +178,12 @@ do_test fkey2-1.3.0 { foreach {tn zSql res} $FkeySimpleTests { if {$res == "0 {}"} { set res {0 1} } do_test fkey2-1.3.$tn { catchsql $zSql } $res + do_test fkey2-1.3.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.3.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.3.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.3.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.3.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.3.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } execsql { PRAGMA count_changes = 0 } drop_all_tables @@ -681,7 +700,7 @@ foreach zSql [list { do_test fkey2-10.1.[incr tn] { execsql $zSql catchsql { INSERT INTO c DEFAULT VALUES } - } {1 {foreign key mismatch}} + } {/1 {foreign key mismatch - "c" referencing "."}/} } # "rowid" cannot be used as part of a child or parent key definition @@ -709,7 +728,7 @@ do_test fkey2-10.2.1 { INSERT INTO t1(rowid, a, b) VALUES(1, 1, 1); INSERT INTO t2 VALUES(1, 1); } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "t2" referencing "t1"}} do_test fkey2-10.2.2 { drop_all_tables catchsql { @@ -1223,7 +1242,7 @@ do_test fkey-2.14.3.8 { CREATE TABLE cc(a, b, FOREIGN KEY(a, b) REFERENCES pp(x, z)); } catchsql { INSERT INTO cc VALUES(1, 2) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "cc" referencing "pp"}} do_test fkey-2.14.3.9 { execsql { DROP TABLE cc } } {} @@ -1414,10 +1433,12 @@ do_test fkey2-17.1.2 { set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy] sqlite3_step $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.2b SQLITE_CONSTRAINT_FOREIGNKEY ifcapable autoreset { do_test fkey2-17.1.3 { sqlite3_step $STMT } {SQLITE_CONSTRAINT} + verify_ex_errcode fkey2-17.1.3b SQLITE_CONSTRAINT_FOREIGNKEY } else { do_test fkey2-17.1.3 { sqlite3_step $STMT @@ -1426,6 +1447,7 @@ ifcapable autoreset { do_test fkey2-17.1.4 { sqlite3_finalize $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.4b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-17.1.5 { execsql { INSERT INTO one VALUES(2, 3, 4); @@ -1469,9 +1491,11 @@ do_test fkey2-17.1.12 { do_test fkey2-17.1.13 { sqlite3_step $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.13b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-17.1.14 { sqlite3_finalize $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.14b SQLITE_CONSTRAINT_FOREIGNKEY drop_all_tables do_test fkey2-17.2.1 { @@ -1625,9 +1649,11 @@ do_test fkey2-19.2 { sqlite3_bind_int $S 1 2 sqlite3_step $S } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-19.2b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-19.3 { sqlite3_reset $S } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-19.3b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-19.4 { sqlite3_bind_int $S 1 1 sqlite3_step $S diff --git a/test/fkey4.test b/test/fkey4.test index d6dd2fc..79cf663 100644 --- a/test/fkey4.test +++ b/test/fkey4.test @@ -42,10 +42,12 @@ do_test fkey4-1.2 { set ::STMT1 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] sqlite3_step $::STMT1 } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey4-1.2b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey4-1.3 { set ::STMT2 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] sqlite3_step $::STMT2 } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey4-1.3b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey4-1.4 { db eval {SELECT * FROM t2} } {1 3} diff --git a/test/fkey5.test b/test/fkey5.test new file mode 100644 index 0000000..40a1a5e --- /dev/null +++ b/test/fkey5.test @@ -0,0 +1,310 @@ +# 2012 December 17 +# +# 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 implements regression tests for SQLite library. +# +# This file tests the PRAGMA foreign_key_check command. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable {!foreignkey} { + finish_test + return +} + +do_test fkey5-1.1 { + db eval { + CREATE TABLE p1(a INTEGER PRIMARY KEY); INSERT INTO p1 VALUES(88),(89); + CREATE TABLE p2(a INT PRIMARY KEY); INSERT INTO p2 VALUES(77),(78); + CREATE TABLE p3(a TEXT PRIMARY KEY); + INSERT INTO p3 VALUES(66),(67),('alpha'),('BRAVO'); + CREATE TABLE p4(a TEXT PRIMARY KEY COLLATE nocase); + INSERT INTO p4 VALUES('alpha'),('BRAVO'),('55'),('Delta'),('ECHO'); + CREATE TABLE p5(a INTEGER PRIMARY KEY, b, c, UNIQUE(b,c)); + INSERT INTO p5 VALUES(1,'Alpha','abc'),(2,'beta','def'); + CREATE TABLE p6(a INTEGER PRIMARY KEY, b TEXT COLLATE nocase, + c TEXT COLLATE rtrim, UNIQUE(b,c)); + INSERT INTO p6 VALUES(1,'Alpha','abc '),(2,'bETA','def '); + + CREATE TABLE c1(x INTEGER PRIMARY KEY references p1); + CREATE TABLE c2(x INTEGER PRIMARY KEY references p2); + CREATE TABLE c3(x INTEGER PRIMARY KEY references p3); + CREATE TABLE c4(x INTEGER PRIMARY KEY references p4); + CREATE TABLE c5(x INT references p1); + CREATE TABLE c6(x INT references p2); + CREATE TABLE c7(x INT references p3); + CREATE TABLE c8(x INT references p4); + CREATE TABLE c9(x TEXT UNIQUE references p1); + CREATE TABLE c10(x TEXT UNIQUE references p2); + CREATE TABLE c11(x TEXT UNIQUE references p3); + CREATE TABLE c12(x TEXT UNIQUE references p4); + CREATE TABLE c13(x TEXT COLLATE nocase references p3); + CREATE TABLE c14(x TEXT COLLATE nocase references p4); + CREATE TABLE c15(x, y, FOREIGN KEY(x,y) REFERENCES p5(b,c)); + CREATE TABLE c16(x, y, FOREIGN KEY(x,y) REFERENCES p5(c,b)); + CREATE TABLE c17(x, y, FOREIGN KEY(x,y) REFERENCES p6(b,c)); + CREATE TABLE c18(x, y, FOREIGN KEY(x,y) REFERENCES p6(c,b)); + CREATE TABLE c19(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p5(b,c)); + CREATE TABLE c20(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p5(c,b)); + CREATE TABLE c21(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p6(b,c)); + CREATE TABLE c22(x TEXT COLLATE nocase, y TEXT COLLATE rtrim, + FOREIGN KEY(x,y) REFERENCES p6(c,b)); + + PRAGMA foreign_key_check; + } +} {} +do_test fkey5-1.2 { + db eval { + INSERT INTO c1 VALUES(90),(87),(88); + PRAGMA foreign_key_check; + } +} {c1 87 p1 0 c1 90 p1 0} +do_test fkey5-1.3 { + db eval { + PRAGMA foreign_key_check(c1); + } +} {c1 87 p1 0 c1 90 p1 0} +do_test fkey5-1.4 { + db eval { + PRAGMA foreign_key_check(c2); + } +} {} + +do_test fkey5-2.0 { + db eval { + INSERT INTO c5 SELECT x FROM c1; + DELETE FROM c1; + PRAGMA foreign_key_check; + } +} {c5 1 p1 0 c5 3 p1 0} +do_test fkey5-2.1 { + db eval { + PRAGMA foreign_key_check(c5); + } +} {c5 1 p1 0 c5 3 p1 0} +do_test fkey5-2.2 { + db eval { + PRAGMA foreign_key_check(c1); + } +} {} + +do_test fkey5-3.0 { + db eval { + INSERT INTO c9 SELECT x FROM c5; + DELETE FROM c5; + PRAGMA foreign_key_check; + } +} {c9 1 p1 0 c9 3 p1 0} +do_test fkey5-3.1 { + db eval { + PRAGMA foreign_key_check(c9); + } +} {c9 1 p1 0 c9 3 p1 0} +do_test fkey5-3.2 { + db eval { + PRAGMA foreign_key_check(c5); + } +} {} + +do_test fkey5-4.0 { + db eval { + DELETE FROM c9; + INSERT INTO c2 VALUES(79),(77),(76); + PRAGMA foreign_key_check; + } +} {c2 76 p2 0 c2 79 p2 0} +do_test fkey5-4.1 { + db eval { + PRAGMA foreign_key_check(c2); + } +} {c2 76 p2 0 c2 79 p2 0} +do_test fkey5-4.2 { + db eval { + INSERT INTO c6 SELECT x FROM c2; + DELETE FROM c2; + PRAGMA foreign_key_check; + } +} {c6 1 p2 0 c6 3 p2 0} +do_test fkey5-4.3 { + db eval { + PRAGMA foreign_key_check(c6); + } +} {c6 1 p2 0 c6 3 p2 0} +do_test fkey5-4.4 { + db eval { + INSERT INTO c10 SELECT x FROM c6; + DELETE FROM c6; + PRAGMA foreign_key_check; + } +} {c10 1 p2 0 c10 3 p2 0} +do_test fkey5-4.5 { + db eval { + PRAGMA foreign_key_check(c10); + } +} {c10 1 p2 0 c10 3 p2 0} + +do_test fkey5-5.0 { + db eval { + DELETE FROM c10; + INSERT INTO c3 VALUES(68),(67),(65); + PRAGMA foreign_key_check; + } +} {c3 65 p3 0 c3 68 p3 0} +do_test fkey5-5.1 { + db eval { + PRAGMA foreign_key_check(c3); + } +} {c3 65 p3 0 c3 68 p3 0} +do_test fkey5-5.2 { + db eval { + INSERT INTO c7 SELECT x FROM c3; + INSERT INTO c7 VALUES('Alpha'),('alpha'),('foxtrot'); + DELETE FROM c3; + PRAGMA foreign_key_check; + } +} {c7 1 p3 0 c7 3 p3 0 c7 4 p3 0 c7 6 p3 0} +do_test fkey5-5.3 { + db eval { + PRAGMA foreign_key_check(c7); + } +} {c7 1 p3 0 c7 3 p3 0 c7 4 p3 0 c7 6 p3 0} +do_test fkey5-5.4 { + db eval { + INSERT INTO c11 SELECT x FROM c7; + DELETE FROM c7; + PRAGMA foreign_key_check; + } +} {c11 1 p3 0 c11 3 p3 0 c11 4 p3 0 c11 6 p3 0} +do_test fkey5-5.5 { + db eval { + PRAGMA foreign_key_check(c11); + } +} {c11 1 p3 0 c11 3 p3 0 c11 4 p3 0 c11 6 p3 0} + +do_test fkey5-6.0 { + db eval { + DELETE FROM c11; + INSERT INTO c4 VALUES(54),(55),(56); + PRAGMA foreign_key_check; + } +} {c4 54 p4 0 c4 56 p4 0} +do_test fkey5-6.1 { + db eval { + PRAGMA foreign_key_check(c4); + } +} {c4 54 p4 0 c4 56 p4 0} +do_test fkey5-6.2 { + db eval { + INSERT INTO c8 SELECT x FROM c4; + INSERT INTO c8 VALUES('Alpha'),('ALPHA'),('foxtrot'); + DELETE FROM c4; + PRAGMA foreign_key_check; + } +} {c8 1 p4 0 c8 3 p4 0 c8 6 p4 0} +do_test fkey5-6.3 { + db eval { + PRAGMA foreign_key_check(c8); + } +} {c8 1 p4 0 c8 3 p4 0 c8 6 p4 0} +do_test fkey5-6.4 { + db eval { + INSERT INTO c12 SELECT x FROM c8; + DELETE FROM c8; + PRAGMA foreign_key_check; + } +} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0} +do_test fkey5-6.5 { + db eval { + PRAGMA foreign_key_check(c12); + } +} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0} + +do_test fkey5-7.1 { + db eval { + INSERT OR IGNORE INTO c13 SELECT * FROM c12; + INSERT OR IGNORE INTO C14 SELECT * FROM c12; + DELETE FROM c12; + PRAGMA foreign_key_check; + } +} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0 c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0} +do_test fkey5-7.2 { + db eval { + PRAGMA foreign_key_check(c14); + } +} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0} +do_test fkey5-7.3 { + db eval { + PRAGMA foreign_key_check(c13); + } +} {c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0} + +do_test fkey5-8.0 { + db eval { + DELETE FROM c13; + DELETE FROM c14; + INSERT INTO c19 VALUES('alpha','abc'); + PRAGMA foreign_key_check(c19); + } +} {c19 1 p5 0} +do_test fkey5-8.1 { + db eval { + DELETE FROM c19; + INSERT INTO c19 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c19); + } +} {} +do_test fkey5-8.2 { + db eval { + INSERT INTO c20 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c20); + } +} {c20 1 p5 0} +do_test fkey5-8.3 { + db eval { + DELETE FROM c20; + INSERT INTO c20 VALUES('abc','Alpha'); + PRAGMA foreign_key_check(c20); + } +} {} +do_test fkey5-8.4 { + db eval { + INSERT INTO c21 VALUES('alpha','abc '); + PRAGMA foreign_key_check(c21); + } +} {} +do_test fkey5-8.5 { + db eval { + DELETE FROM c21; + INSERT INTO c19 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c21); + } +} {} +do_test fkey5-8.6 { + db eval { + INSERT INTO c22 VALUES('Alpha','abc'); + PRAGMA foreign_key_check(c22); + } +} {c22 1 p6 0} +do_test fkey5-8.7 { + db eval { + DELETE FROM c22; + INSERT INTO c22 VALUES('abc ','ALPHA'); + PRAGMA foreign_key_check(c22); + } +} {} + + + +finish_test diff --git a/test/fkey_malloc.test b/test/fkey_malloc.test index 4a36f5f..b4b5b4e 100644 --- a/test/fkey_malloc.test +++ b/test/fkey_malloc.test @@ -29,6 +29,7 @@ do_malloc_test fkey_malloc-1 -sqlprep { INSERT INTO t2 VALUES('aaa'); UPDATE t1 SET a = 'bbb'; DELETE FROM t1; + PRAGMA foreign_key_check; } do_malloc_test fkey_malloc-2 -sqlprep { @@ -128,5 +129,3 @@ do_malloc_test fkey_malloc-7 -sqlprep { } finish_test - - diff --git a/test/fts3ai.test b/test/fts3ai.test index 144b4c3..b17b5bd 100644 --- a/test/fts3ai.test +++ b/test/fts3ai.test @@ -19,6 +19,11 @@ ifcapable !fts3 { return } +ifcapable !utf16 { + finish_test + return +} + # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. # NOTE(shess) Copied from capi3.test. diff --git a/test/fts3aux1.test b/test/fts3aux1.test index adda586..ef17949 100644 --- a/test/fts3aux1.test +++ b/test/fts3aux1.test @@ -354,10 +354,10 @@ do_execsql_test 3.1.1 { do_catchsql_test 3.1.2 { CREATE VIRTUAL TABLE terms2 USING fts4aux; -} {1 {wrong number of arguments to fts4aux constructor}} +} {1 {invalid arguments to fts4aux constructor}} do_catchsql_test 3.1.3 { CREATE VIRTUAL TABLE terms2 USING fts4aux(t2, t2); -} {1 {wrong number of arguments to fts4aux constructor}} +} {1 {invalid arguments to fts4aux constructor}} do_execsql_test 3.2.1 { CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) @@ -444,7 +444,6 @@ do_plansql_test 4.5 { # odd name (one that requires quoting for use in SQL statements). And that # the argument to the fts4aux constructor is properly dequoted before use. # -# do_execsql_test 5.1 { CREATE VIRTUAL TABLE "abc '!' def" USING fts4(x, y); INSERT INTO "abc '!' def" VALUES('XX', 'YY'); @@ -458,5 +457,66 @@ do_execsql_test 5.2 { SELECT * FROM "%%^^%%"; } {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} +#------------------------------------------------------------------------- +# Test that we can create an fts4aux table in the temp database. +# +forcedelete test.db2 +do_execsql_test 6.1 { + CREATE VIRTUAL TABLE ft1 USING fts4(x, y); + INSERT INTO ft1 VALUES('a b', 'c d'); + INSERT INTO ft1 VALUES('e e', 'c d'); + INSERT INTO ft1 VALUES('a a', 'b b'); + CREATE VIRTUAL TABLE temp.aux1 USING fts4aux(main, ft1); + SELECT * FROM aux1; +} { + a * 2 3 a 0 2 3 + b * 2 3 b 0 1 1 b 1 1 2 + c * 2 2 c 1 2 2 + d * 2 2 d 1 2 2 + e * 1 2 e 0 1 2 +} + +do_execsql_test 6.2 { + ATTACH 'test.db2' AS att; + CREATE VIRTUAL TABLE att.ft1 USING fts4(x, y); + INSERT INTO att.ft1 VALUES('v w', 'x y'); + INSERT INTO att.ft1 VALUES('z z', 'x y'); + INSERT INTO att.ft1 VALUES('v v', 'w w'); + CREATE VIRTUAL TABLE temp.aux2 USING fts4aux(att, ft1); + SELECT * FROM aux2; +} { + v * 2 3 v 0 2 3 + w * 2 3 w 0 1 1 w 1 1 2 + x * 2 2 x 1 2 2 + y * 2 2 y 1 2 2 + z * 1 2 z 0 1 2 +} + +foreach {tn q res1 res2} { + 1 { SELECT * FROM %%% WHERE term = 'a' } {a * 2 3 a 0 2 3} {} + 2 { SELECT * FROM %%% WHERE term = 'x' } {} {x * 2 2 x 1 2 2} + + 3 { SELECT * FROM %%% WHERE term >= 'y' } + {} {y * 2 2 y 1 2 2 z * 1 2 z 0 1 2} + + 4 { SELECT * FROM %%% WHERE term <= 'c' } + {a * 2 3 a 0 2 3 b * 2 3 b 0 1 1 b 1 1 2 c * 2 2 c 1 2 2} {} +} { + set sql1 [string map {%%% aux1} $q] + set sql2 [string map {%%% aux2} $q] + + do_execsql_test 7.$tn.1 $sql1 $res1 + do_execsql_test 7.$tn.2 $sql2 $res2 +} + +do_test 8.1 { + catchsql { CREATE VIRTUAL TABLE att.aux3 USING fts4aux(main, ft1) } +} {1 {invalid arguments to fts4aux constructor}} + +do_test 8.2 { + execsql {DETACH att} + catchsql { SELECT * FROM aux2 } +} {1 {SQL logic error or missing database}} finish_test + diff --git a/test/fts3conf.test b/test/fts3conf.test index ce41027..e91efbe 100644 --- a/test/fts3conf.test +++ b/test/fts3conf.test @@ -136,4 +136,46 @@ do_execsql_test 2.2.2 { COMMIT } do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}} fts3_integrity 2.2.4 db t1 +do_execsql_test 3.1 { + CREATE VIRTUAL TABLE t3 USING fts4; + REPLACE INTO t3(docid, content) VALUES (1, 'one two'); + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' +} {X'0100000002000000'} + +do_execsql_test 3.2 { + REPLACE INTO t3(docid, content) VALUES (2, 'one two three four'); + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'four' +} {X'0200000003000000'} + +do_execsql_test 3.3 { + REPLACE INTO t3(docid, content) VALUES (1, 'one two three four five six'); + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' +} {X'0200000005000000'} + +do_execsql_test 3.4 { + UPDATE OR REPLACE t3 SET docid = 2 WHERE docid=1; + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' +} {X'0100000006000000'} + +do_execsql_test 3.5 { + UPDATE OR REPLACE t3 SET docid = 3 WHERE docid=2; + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' +} {X'0100000006000000'} + +do_execsql_test 3.6 { + REPLACE INTO t3(docid, content) VALUES (3, 'one two'); + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' +} {X'0100000002000000'} + +do_execsql_test 3.7 { + REPLACE INTO t3(docid, content) VALUES (NULL, 'one two three four'); + REPLACE INTO t3(docid, content) VALUES (NULL, 'one two three four five six'); + SELECT docid FROM t3; +} {3 4 5} + +do_execsql_test 3.8 { + UPDATE OR REPLACE t3 SET docid = 5, content='three four' WHERE docid = 4; + SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' +} {X'0200000002000000'} + finish_test diff --git a/test/fts3expr3.test b/test/fts3expr3.test new file mode 100644 index 0000000..e3cc261 --- /dev/null +++ b/test/fts3expr3.test @@ -0,0 +1,210 @@ +# 2009 January 1 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is testing the part of the FTS3 expression +# parser that rebalances large expressions. +# +# $Id: fts3expr2.test,v 1.2 2009/06/05 17:09:12 drh Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set ::testprefix fts3expr3 + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +set sqlite_fts3_enable_parentheses 1 + +proc strip_phrase_data {L} { + if {[lindex $L 0] eq "PHRASE"} { + return [list P [lrange $L 3 end]] + } + return [list \ + [lindex $L 0] \ + [strip_phrase_data [lindex $L 1]] \ + [strip_phrase_data [lindex $L 2]] \ + ] +} +proc test_fts3expr2 {expr} { + strip_phrase_data [ + db one {SELECT fts3_exprtest_rebalance('simple', $expr, 'a', 'b', 'c')} + ] +} + +proc balanced_exprtree_structure {nEntry} { + set L [list] + for {set i 1} {$i <= $nEntry} {incr i} { + lappend L xxx + } + while {[llength $L] > 1} { + set N [list] + if {[llength $L] % 2} { + foreach {a b} [lrange $L 0 end-1] { lappend N [list AND $a $b] } + lappend N [lindex $L end] + } else { + foreach {a b} $L { lappend N [list AND $a $b] } + } + set L $N + } + return [lindex $L 0] +} + +proc balanced_and_tree {nEntry} { + set query [balanced_exprtree_structure $nEntry] + if {$query == "xxx"} { + return "P 1" + } + for {set i 1} {$i <= $nEntry} {incr i} { + regsub xxx $query "{P $i}" query + } + return $query +} + +proc random_tree_structure {nEntry bParen op} { + set query xxx + for {set i 1} {$i < $nEntry} {incr i} { + set x1 [expr int(rand()*4.0)] + set x2 [expr int(rand()*2.0)] + if {$x1==0 && $bParen} { + set query "($query)" + } + if {$x2} { + set query "xxx $op $query" + } else { + set query "$query $op xxx" + } + } + return $query +} + +proc random_and_query {nEntry {bParen 0}} { + set query [random_tree_structure $nEntry $bParen AND] + for {set i 1} {$i <= $nEntry} {incr i} { + regsub xxx $query $i query + } + return $query +} + +proc random_or_query {nEntry} { + set query [random_tree_structure $nEntry 1 OR] + for {set i 1} {$i <= $nEntry} {incr i} { + regsub xxx $query $i query + } + return $query +} + +proc random_andor_query {nEntry} { + set query [random_tree_structure $nEntry 1 AND] + for {set i 1} {$i <= $nEntry} {incr i} { + regsub xxx $query "([random_or_query $nEntry])" query + } + return $query +} + +proc balanced_andor_tree {nEntry} { + set tree [balanced_exprtree_structure $nEntry] + set node "{[balanced_and_tree $nEntry]}" + regsub -all AND $node OR node + regsub -all xxx $tree $node tree + return $tree +} + +# Test that queries like "1 AND 2 AND 3 AND 4..." are transformed to +# balanced trees by FTS. +# +for {set i 1} {$i < 100} {incr i} { + do_test 1.$i { + test_fts3expr2 [random_and_query $i] + } [balanced_and_tree $i] +} + +# Same again, except with parenthesis inserted at arbitrary points. +# +for {set i 1} {$i < 100} {incr i} { + do_test 2.$i { + test_fts3expr2 [random_and_query $i 1] + } [balanced_and_tree $i] +} + +# Now attempt to balance two AND trees joined by an OR. +# +for {set i 1} {$i < 100} {incr i} { + do_test 3.$i { + test_fts3expr2 "[random_and_query $i 1] OR [random_and_query $i 1]" + } [list OR [balanced_and_tree $i] [balanced_and_tree $i]] +} + +# Try trees of AND nodes with leaves that are themselves trees of OR nodes. +# +for {set i 2} {$i < 64} {incr i 4} { + do_test 3.$i { + test_fts3expr2 [random_andor_query $i] + } [balanced_andor_tree $i] +} + +# These exceed the depth limit. +# +for {set i 65} {$i < 70} {incr i} { + do_test 3.$i { + list [catch {test_fts3expr2 [random_andor_query $i]} msg] $msg + } {1 {Error parsing expression}} +} + +# This also exceeds the depth limit. +# + +do_test 4.1.1 { + set q "1" + for {set i 2} {$i < 5000} {incr i} { + append q " AND $i" + } + list [catch {test_fts3expr2 $q} msg] $msg +} {1 {Error parsing expression}} +do_test 4.1.2 { + set q "1" + for {set i 2} {$i < 4000} {incr i} { + append q " AND $i" + } + catch {test_fts3expr2 $q} +} {0} + +proc create_toggle_tree {nDepth} { + if {$nDepth == 0} { return xxx } + set nNew [expr $nDepth-1] + if {$nDepth % 2} { + return "([create_toggle_tree $nNew]) OR ([create_toggle_tree $nNew])" + } + return "([create_toggle_tree $nNew]) AND ([create_toggle_tree $nNew])" +} + +do_test 4.2 { + list [catch {test_fts3expr2 [create_toggle_tree 17]} msg] $msg +} {1 {Error parsing expression}} + +set query [random_andor_query 12] +set result [balanced_andor_tree 12] +do_faultsim_test fts3expr3-fault-1 -faults oom-* -body { + test_fts3expr2 $::query +} -test { + faultsim_test_result [list 0 $::result] +} + +set sqlite_fts3_enable_parentheses 0 +finish_test + + + + diff --git a/test/fts3matchinfo.test b/test/fts3matchinfo.test index 924db9c..3998c9a 100644 --- a/test/fts3matchinfo.test +++ b/test/fts3matchinfo.test @@ -407,5 +407,24 @@ do_catchsql_test 8.5.3.2 { SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*' } {1 {database disk image is malformed}} +#------------------------------------------------------------------------- +do_execsql_test 8.1 { + CREATE VIRTUAL TABLE t12 USING fts4; + INSERT INTO t12 VALUES('a b c d'); + SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'a NEAR/1 d OR a'; +} {{0 0 0 0 0 0 1 1 1}} +do_execsql_test 8.2 { + INSERT INTO t12 VALUES('a d c d'); + SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'a NEAR/1 d OR a'; +} { + {0 1 1 0 1 1 1 2 2} {1 1 1 1 1 1 1 2 2} +} +do_execsql_test 8.3 { + INSERT INTO t12 VALUES('a d d a'); + SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'a NEAR/1 d OR a'; +} { + {0 3 2 0 3 2 1 4 3} {1 3 2 1 3 2 1 4 3} {2 3 2 2 3 2 2 4 3} +} + finish_test diff --git a/test/fts3near.test b/test/fts3near.test index 9c4409e..9276fa3 100644 --- a/test/fts3near.test +++ b/test/fts3near.test @@ -580,5 +580,19 @@ do_test fts3near-6.5 { } } {3} +# Ticket 38b1ae018f. +# +do_execsql_test fts3near-7.1 { + CREATE VIRTUAL TABLE x USING fts4(y,z); + INSERT INTO x VALUES('aaa bbb ccc ddd', 'bbb ddd aaa ccc'); + SELECT * FROM x where y MATCH 'bbb NEAR/6 aaa'; +} {{aaa bbb ccc ddd} {bbb ddd aaa ccc}} + +do_execsql_test fts3near-7.2 { + CREATE VIRTUAL TABLE t2 USING fts4(a, b); + INSERT INTO t2 VALUES('A B C', 'A D E'); + SELECT * FROM t2 where t2 MATCH 'a:A NEAR E' +} {} + finish_test diff --git a/test/fts3tok1.test b/test/fts3tok1.test new file mode 100644 index 0000000..98e55a0 --- /dev/null +++ b/test/fts3tok1.test @@ -0,0 +1,117 @@ +# 2013 April 22 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is testing the "fts3tokenize" virtual table +# that is part of the FTS3 module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !fts3 { finish_test ; return } +set ::testprefix fts3tok1 + +#------------------------------------------------------------------------- +# Simple test cases. Using the default (simple) tokenizer. +# +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING fts3tokenize(simple); + CREATE VIRTUAL TABLE t2 USING fts3tokenize(); + CREATE VIRTUAL TABLE t3 USING fts3tokenize(simple, '', 'xyz '); +} + +foreach {tn tbl} {1 t1 2 t2 3 t3} { + do_execsql_test 1.$tn.1 "SELECT * FROM $tbl WHERE input = 'one two three'" { + {one two three} one 0 3 0 + {one two three} two 4 7 1 + {one two three} three 8 13 2 + } + + do_execsql_test 1.$tn.2 " + SELECT token FROM $tbl WHERE input = 'OnE tWo tHrEe' + " { + one two three + } +} + +do_execsql_test 1.4 { + SELECT token FROM t3 WHERE input = '1x2x3x' +} {1 2 3} + +do_execsql_test 1.5 { + SELECT token FROM t1 WHERE input = '1x2x3x' +} {1x2x3x} + +do_execsql_test 1.6 { + SELECT token FROM t3 WHERE input = '1''2x3x' +} {1'2 3} + +do_execsql_test 1.7 { + SELECT token FROM t3 WHERE input = '' +} {} + +do_execsql_test 1.8 { + SELECT token FROM t3 WHERE input = NULL +} {} + +do_execsql_test 1.9 { + SELECT * FROM t3 WHERE input = 123 +} {123 123 0 3 0} + +do_execsql_test 1.10 { + SELECT * FROM t1 WHERE input = 'a b c' AND token = 'b'; +} { + {a b c} b 2 3 1 +} + +do_execsql_test 1.11 { + SELECT * FROM t1 WHERE token = 'b' AND input = 'a b c'; +} { + {a b c} b 2 3 1 +} + +do_execsql_test 1.12 { + SELECT * FROM t1 WHERE input < 'b' AND input = 'a b c'; +} { + {a b c} a 0 1 0 + {a b c} b 2 3 1 + {a b c} c 4 5 2 +} + +do_execsql_test 1.13.1 { + CREATE TABLE c1(x); + INSERT INTO c1(x) VALUES('a b c'); + INSERT INTO c1(x) VALUES('d e f'); +} +breakpoint +do_execsql_test 1.13.2 { + SELECT * FROM c1, t1 WHERE input = x AND c1.rowid=t1.rowid; +} { + {a b c} {a b c} a 0 1 0 + {d e f} {d e f} e 2 3 1 +} + + +#------------------------------------------------------------------------- +# Error cases. +# +do_catchsql_test 2.0 { + CREATE VIRTUAL TABLE tX USING fts3tokenize(nosuchtokenizer); +} {1 {unknown tokenizer: nosuchtokenizer}} + +do_catchsql_test 2.1 { + CREATE VIRTUAL TABLE t4 USING fts3tokenize; + SELECT * FROM t4; +} {1 {SQL logic error or missing database}} + + +finish_test + + diff --git a/test/fts3tok_err.test b/test/fts3tok_err.test new file mode 100644 index 0000000..aaa7272 --- /dev/null +++ b/test/fts3tok_err.test @@ -0,0 +1,49 @@ +# 2013 April 22 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is testing the "fts3tokenize" virtual table +# that is part of the FTS3 module. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +ifcapable !fts3 { finish_test ; return } +set ::testprefix fts3tok_err + + +faultsim_save_and_close +do_faultsim_test fts3tok_err-1 -faults oom* -prep { + faultsim_restore_and_reopen +} -body { + execsql { CREATE VIRTUAL TABLE t1 USING fts3tokenize("simple"); } +} -test { + faultsim_test_result {0 {}} +} + +do_test fts3tok_err-2.prep { + faultsim_delete_and_reopen + execsql { CREATE VIRTUAL TABLE t1 USING fts3tokenize("simple"); } + faultsim_save_and_close +} {} + +do_faultsim_test fts3tok_err-2 -faults oom* -prep { + faultsim_restore_and_reopen +} -body { + execsql { SELECT token FROM t1 WHERE input = 'A galaxy far, far away' } +} -test { + faultsim_test_result {0 {a galaxy far far away}} +} + + +finish_test + + diff --git a/test/fts4content.test b/test/fts4content.test index 59c4199..302f14e 100644 --- a/test/fts4content.test +++ b/test/fts4content.test @@ -46,6 +46,8 @@ ifcapable !fts3 { # 8.* - Test that if the content=xxx and prefix options are used together, # the 'rebuild' command still works. # +# 9.* - Test using content=xxx where xxx is a virtual table. +# do_execsql_test 1.1.1 { CREATE TABLE t1(a, b, c); @@ -522,4 +524,103 @@ do_execsql_test 8.4 { SELECT rowid FROM ft10 WHERE a MATCH 'ab*'; } {1 2 3} do_execsql_test 8.5 { SELECT rowid FROM ft10 WHERE b MATCH 'abav*'; } {3} do_execsql_test 8.6 { SELECT rowid FROM ft10 WHERE ft10 MATCH 'abas*'; } {1} +#------------------------------------------------------------------------- +# Test cases 9.* +# +reset_db +register_echo_module [sqlite3_connection_pointer db] + +do_execsql_test 9.1 { + CREATE TABLE tbl1(a, b); + INSERT INTO tbl1 VALUES('a b', 'c d'); + INSERT INTO tbl1 VALUES('e f', 'a b'); + CREATE VIRTUAL TABLE e1 USING echo(tbl1); + CREATE VIRTUAL TABLE ft1 USING fts4(content=e1); + INSERT INTO ft1(ft1) VALUES('rebuild'); +} + +do_execsql_test 9.2 { + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'e' +} {2 {e f} {a b}} + +do_execsql_test 9.3 { + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a' +} {1 {a b} {c d} 2 {e f} {a b}} + +do_execsql_test 9.4 { + DELETE FROM ft1 WHERE docid=1; +} + +do_execsql_test 9.5 { + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a' +} {2 {e f} {a b}} + +do_execsql_test 9.6 { + INSERT INTO ft1(ft1) VALUES('rebuild'); + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a' +} {1 {a b} {c d} 2 {e f} {a b}} + + +#------------------------------------------------------------------------- +# Test cases 10.* +# +reset_db +register_fs_module [sqlite3_connection_pointer db] + +proc write_file {path text} { + set fd [open $path w] + puts -nonewline $fd $text + close $fd +} + +write_file t1.txt {a b c d e f g h i j k l m n o p q r s t u v w x y z} +write_file t2.txt {a b c d e f g h i j k l m a b c d e f g h i j k l m} +write_file t3.txt {n o p q r s t u v w x y z n o p q r s t u v w x y z} + +do_execsql_test 10.1 { + CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT); + INSERT INTO idx VALUES (1, 't1.txt'); + INSERT INTO idx VALUES (2, 't2.txt'); + INSERT INTO idx VALUES (3, 't3.txt'); + + CREATE VIRTUAL TABLE vt USING fs(idx); + SELECT * FROM vt; +} { + 1 {a b c d e f g h i j k l m n o p q r s t u v w x y z} + 2 {a b c d e f g h i j k l m a b c d e f g h i j k l m} + 3 {n o p q r s t u v w x y z n o p q r s t u v w x y z} +} + +do_execsql_test 10.2 { + SELECT * FROM vt WHERE rowid = 2; +} { + 2 {a b c d e f g h i j k l m a b c d e f g h i j k l m} +} + +do_execsql_test 10.3 { + CREATE VIRTUAL TABLE ft USING fts4(content=vt); + INSERT INTO ft(ft) VALUES('rebuild'); +} + +do_execsql_test 10.4 { + SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 'e' +} { + {...c d [e] f g...} {...c d [e] f g...} +} + +do_execsql_test 10.5 { + SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 't' +} { + {...r s [t] u v...} {...r s [t] u v...} +} + +do_execsql_test 10.6 { DELETE FROM ft WHERE docid=2 } + +do_execsql_test 10.7 { + SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 'e' +} { + {...c d [e] f g...} +} + finish_test + diff --git a/test/fts4unicode.test b/test/fts4unicode.test index 0ac60a6..8bd83f6 100644 --- a/test/fts4unicode.test +++ b/test/fts4unicode.test @@ -44,12 +44,12 @@ proc do_unicode_token_test3 {tn args} { } do_unicode_token_test 1.0 {a B c D} {0 a a 1 b B 2 c c 3 d D} -do_unicode_token_test 1.1 {Ä Ö Ü} {0 ä Ä 1 ö Ö 2 ü Ü} -do_unicode_token_test 1.2 {xÄx xÖx xÜx} {0 xäx xÄx 1 xöx xÖx 2 xüx xÜx} +do_unicode_token_test 1.1 {Ä Ö Ãœ} {0 ä Ä 1 ö Ö 2 ü Ãœ} +do_unicode_token_test 1.2 {xÄx xÖx xÃœx} {0 xäx xÄx 1 xöx xÖx 2 xüx xÃœx} # 0x00DF is a small "sharp s". 0x1E9E is a capital sharp s. do_unicode_token_test 1.3 "\uDF" "0 \uDF \uDF" -do_unicode_token_test 1.4 "\u1E9E" "0 ß \u1E9E" +do_unicode_token_test 1.4 "\u1E9E" "0 ß \u1E9E" do_unicode_token_test 1.5 "\u1E9E" "0 \uDF \u1E9E" do_unicode_token_test 1.6 "The quick brown fox" { @@ -60,12 +60,15 @@ do_unicode_token_test 1.7 "The\u00bfquick\u224ebrown\u2263fox" { } do_unicode_token_test2 1.8 {a B c D} {0 a a 1 b B 2 c c 3 d D} -do_unicode_token_test2 1.9 {Ä Ö Ü} {0 a Ä 1 o Ö 2 u Ü} -do_unicode_token_test2 1.10 {xÄx xÖx xÜx} {0 xax xÄx 1 xox xÖx 2 xux xÜx} +do_unicode_token_test2 1.9 {Ä Ö Ãœ} {0 a Ä 1 o Ö 2 u Ãœ} +do_unicode_token_test2 1.10 {xÄx xÖx xÃœx} {0 xax xÄx 1 xox xÖx 2 xux xÃœx} # Check that diacritics are removed if remove_diacritics=1 is specified. # And that they do not break tokens. -do_unicode_token_test2 1.10 "xx\u0301xx" "0 xxxx xx\u301xx" +do_unicode_token_test2 1.11 "xx\u0301xx" "0 xxxx xx\u301xx" + +# Title-case mappings work +do_unicode_token_test 1.12 "\u01c5" "0 \u01c6 \u01c5" #------------------------------------------------------------------------- # @@ -383,5 +386,3 @@ foreach T $tokenizers { finish_test - - diff --git a/test/full.test b/test/full.test new file mode 100644 index 0000000..a8fe371 --- /dev/null +++ b/test/full.test @@ -0,0 +1,20 @@ +# 2012 September 12 +# +# 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 runs the "full" test suite. It is a peer of the quick.test +# and all.test scripts. +# + +set testdir [file dirname $argv0] +source $testdir/permutations.test + +run_test_suite full + +finish_test diff --git a/test/func.test b/test/func.test index e44c44b..4ab7688 100644 --- a/test/func.test +++ b/test/func.test @@ -1273,11 +1273,13 @@ do_test func-29.3 { sqlite3_db_status db CACHE_MISS 1 db eval {SELECT typeof(+x) FROM t29 ORDER BY id} } {integer null real blob text} -do_test func-29.4 { - set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] - if {$x>100} {set x many} - set x -} {many} +if {[permutation] != "mmap"} { + do_test func-29.4 { + set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] + if {$x>100} {set x many} + set x + } {many} +} do_test func-29.5 { db close sqlite3 db test.db @@ -1289,6 +1291,21 @@ do_test func-29.6 { if {$x<5} {set x 1} set x } {1} - + +do_execsql_test func-30.1 {SELECT unicode('$');} 36 +do_execsql_test func-30.2 [subst {SELECT unicode('\u00A2');}] 162 +do_execsql_test func-30.3 [subst {SELECT unicode('\u20AC');}] 8364 +do_execsql_test func-30.4 {SELECT char(36,162,8364);} [subst {$\u00A2\u20AC}] + +for {set i 1} {$i<0xd800} {incr i 13} { + do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i +} +for {set i 57344} {$i<=0xfffd} {incr i 17} { + if {$i==0xfeff} continue + do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i +} +for {set i 65536} {$i<=0x10ffff} {incr i 139} { + do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i +} finish_test diff --git a/test/fuzzer1.test b/test/fuzzer1.test index dc8b445..473d0e1 100644 --- a/test/fuzzer1.test +++ b/test/fuzzer1.test @@ -24,12 +24,7 @@ ifcapable !vtab { set ::testprefix fuzzer1 -# Test of test code. Only here to make the coverage metric better. -do_test 0.1 { - list [catch { register_fuzzer_module a b c } msg] $msg -} {1 {wrong # args: should be "register_fuzzer_module DB"}} - -register_fuzzer_module db +load_static_extension db fuzzer # Check configuration errors. # @@ -1864,5 +1859,3 @@ do_execsql_test 10.3 { } {1 21 41 61 81} finish_test - - diff --git a/test/fuzzerfault.test b/test/fuzzerfault.test index 067da7f..6449612 100644 --- a/test/fuzzerfault.test +++ b/test/fuzzerfault.test @@ -17,7 +17,7 @@ source $testdir/tester.tcl ifcapable !vtab { finish_test ; return } set ::testprefix fuzzerfault -register_fuzzer_module db +load_static_extension db fuzzer do_test 1-pre1 { execsql { @@ -30,7 +30,7 @@ do_test 1-pre1 { } {} do_faultsim_test 1 -prep { faultsim_restore_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer } -body { execsql { CREATE VIRTUAL TABLE x1 USING fuzzer(x1_rules); @@ -43,7 +43,7 @@ do_faultsim_test 1 -prep { do_test 2-pre1 { faultsim_delete_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer execsql { CREATE TABLE x2_rules(ruleset, cFrom, cTo, cost); INSERT INTO x2_rules VALUES(0, 'a', 'x', 1); @@ -56,7 +56,7 @@ do_test 2-pre1 { do_faultsim_test 2 -prep { faultsim_restore_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer } -body { execsql { SELECT count(*) FROM x2 WHERE word MATCH 'abc'; @@ -78,7 +78,7 @@ do_test 3-pre1 { do_faultsim_test 3 -prep { faultsim_restore_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer } -body { execsql { CREATE VIRTUAL TABLE x1 USING fuzzer(x1_rules); diff --git a/test/hook.test b/test/hook.test index a195275..6346cc7 100644 --- a/test/hook.test +++ b/test/hook.test @@ -74,6 +74,7 @@ do_test hook-3.6 { INSERT INTO t2 VALUES(6,7); } } {1 {constraint failed}} +verify_ex_errcode hook-3.6b SQLITE_CONSTRAINT_COMMITHOOK do_test hook-3.7 { set ::commit_cnt } {1 2 2 3 3 4 4 5 5 6 6 7} diff --git a/test/in5.test b/test/in5.test new file mode 100644 index 0000000..8a43b8d --- /dev/null +++ b/test/in5.test @@ -0,0 +1,138 @@ +# 2012 September 18 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test in5-1.1 { + execsql { + CREATE TABLE t1x(x INTEGER PRIMARY KEY); + INSERT INTO t1x VALUES(1),(3),(5),(7),(9); + CREATE TABLE t1y(y INTEGER UNIQUE); + INSERT INTO t1y VALUES(2),(4),(6),(8); + CREATE TABLE t1z(z TEXT UNIQUE); + INSERT INTO t1z VALUES('a'),('c'),('e'),('g'); + CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT, d TEXT); + INSERT INTO t2 VALUES(1,2,'a','12a'),(1,2,'b','12b'), + (2,3,'g','23g'),(3,5,'c','35c'), + (4,6,'h','46h'),(5,6,'e','56e'); + CREATE TABLE t3x AS SELECT x FROM t1x; + CREATE TABLE t3y AS SELECT y FROM t1y; + CREATE TABLE t3z AS SELECT z FROM t1z; + SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z ORDER BY c; + } +} {12a 56e} +do_test in5-1.2 { + execsql { + SELECT d FROM t2 WHERE a IN t1y AND b IN t1x AND c IN t1z ORDER BY d; + } +} {23g} +do_test in5-1.3 { + execsql { + SELECT d FROM t2 WHERE a IN t3x AND b IN t3y AND c IN t3z ORDER BY d; + } +} {12a 56e} + + +do_test in5-2.1 { + execsql { + CREATE INDEX t2abc ON t2(a,b,c); + SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z ORDER BY d; + } +} {12a 56e} +do_test in5-2.2 { + execsql { + SELECT d FROM t2 WHERE a IN t1y AND b IN t1x AND c IN t1z ORDER BY d; + } +} {23g} +do_test in5-2.3 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z + }] +} {0} +do_test in5-2.4 { + execsql { + SELECT d FROM t2 WHERE a IN t3x AND b IN t3y AND c IN t3z ORDER BY d; + } +} {12a 56e} +do_test in5-2.5.1 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t3x AND b IN t1y AND c IN t1z + }] +} {1} +do_test in5-2.5.2 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t3y AND c IN t1z + }] +} {1} +do_test in5-2.5.3 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t3z + }] +} {1} + +do_test in5-3.1 { + execsql { + DROP INDEX t2abc; + CREATE INDEX t2ab ON t2(a,b); + SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z ORDER BY d; + } +} {12a 56e} +do_test in5-3.2 { + execsql { + SELECT d FROM t2 WHERE a IN t1y AND b IN t1x AND c IN t1z ORDER BY d; + } +} {23g} +do_test in5-3.3 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z + }] +} {0} + +do_test in5-4.1 { + execsql { + DROP INDEX t2ab; + CREATE INDEX t2abcd ON t2(a,b,c,d); + SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z ORDER BY d; + } +} {12a 56e} +do_test in5-4.2 { + execsql { + SELECT d FROM t2 WHERE a IN t1y AND b IN t1x AND c IN t1z ORDER BY d; + } +} {23g} +do_test in5-4.3 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z + }] +} {0} + + +do_test in5-5.1 { + execsql { + DROP INDEX t2abcd; + CREATE INDEX t2cbad ON t2(c,b,a,d); + SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z ORDER BY d; + } +} {12a 56e} +do_test in5-5.2 { + execsql { + SELECT d FROM t2 WHERE a IN t1y AND b IN t1x AND c IN t1z ORDER BY d; + } +} {23g} +do_test in5-5.3 { + regexp {OpenEphemeral} [db eval { + EXPLAIN SELECT d FROM t2 WHERE a IN t1x AND b IN t1y AND c IN t1z + }] +} {0} + +finish_test diff --git a/test/incrblob.test b/test/incrblob.test index 1880128..4277e5c 100644 --- a/test/incrblob.test +++ b/test/incrblob.test @@ -123,6 +123,7 @@ foreach AutoVacuumMode [list 0 1] { forcedelete test.db test.db-journal sqlite3 db test.db + execsql "PRAGMA mmap_size = 0" execsql "PRAGMA auto_vacuum = $AutoVacuumMode" do_test incrblob-2.$AutoVacuumMode.1 { @@ -149,6 +150,7 @@ foreach AutoVacuumMode [list 0 1] { # Open and close the db to make sure the page cache is empty. db close sqlite3 db test.db + execsql "PRAGMA mmap_size = 0" # Read the last 20 bytes of the blob via a blob handle. set ::blob [db incrblob blobs v 1] @@ -171,6 +173,7 @@ foreach AutoVacuumMode [list 0 1] { # Open and close the db to make sure the page cache is empty. db close sqlite3 db test.db + execsql "PRAGMA mmap_size = 0" # Write the second-to-last 20 bytes of the blob via a blob handle. # @@ -200,6 +203,7 @@ foreach AutoVacuumMode [list 0 1] { # Open and close the db to make sure the page cache is empty. db close sqlite3 db test.db + execsql { PRAGMA mmap_size = 0 } execsql { SELECT i FROM blobs } } {45} @@ -437,7 +441,7 @@ if {[permutation] != "memsubsys1"} { } {} do_test incrblob-6.2 { execsql { - SELECT rowid FROM blobs + SELECT rowid FROM blobs ORDER BY rowid } } {1 2 3} do_test incrblob-6.3 { @@ -505,7 +509,7 @@ if {[permutation] != "memsubsys1"} { sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) #----------------------------------------------------------------------- -# The following tests verify the behaviour of the incremental IO +# The following tests verify the behavior of the incremental IO # APIs in the following cases: # # 7.1 A row that containing an open blob is modified. @@ -516,7 +520,7 @@ sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) # 7.3 An INCREMENTAL VACUUM moves an overflow page that is part # of an open blob. # -# In the first case above, correct behaviour is for all subsequent +# In the first case above, correct behavior is for all subsequent # read/write operations on the blob-handle to return SQLITE_ABORT. # More accurately, blob-handles are invalidated whenever the table # they belong to is written to. diff --git a/test/incrvacuum3.test b/test/incrvacuum3.test new file mode 100644 index 0000000..f01dc10 --- /dev/null +++ b/test/incrvacuum3.test @@ -0,0 +1,154 @@ +# 2013 Feb 25 +# +# 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 implements regression tests for the SQLite library, focusing +# on the incremental vacuum feature. +# +# The tests in this file were added at the same time as optimizations +# were made to: +# +# * Truncate the database after a rollback mode commit, and +# +# * Avoid moving pages to locations from which they may need to be moved +# a second time if an incremental-vacuum proccess is allowed to vacuum +# the entire database. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix incrvacuum3 + +# If this build of the library does not support auto-vacuum, omit this +# whole file. +ifcapable {!autovacuum || !pragma} { + finish_test + return +} + +proc check_on_disk {} { + + # Copy the wal and journal files for database "test.db" to "test2.db". + forcedelete test2.db test2.db-journal test2.db-wal + if {[file exists test.db-journal]} { + forcecopy test.db-journal test2.db-journal + } + if {[file exists test.db-wal]} { + forcecopy test.db-wal test2.db-wal + } + + # Now copy the database file itself. Do this using open/read/puts + # instead of the [file copy] command in order to avoid attempting + # to read the 512 bytes begining at offset $sqlite_pending_byte. + # + set sz [file size test.db] + set fd [open test.db] + set fd2 [open test2.db w] + fconfigure $fd -encoding binary -translation binary + fconfigure $fd2 -encoding binary -translation binary + if {$sz>$::sqlite_pending_byte} { + puts -nonewline $fd2 [read $fd $::sqlite_pending_byte] + seek $fd [expr $::sqlite_pending_byte+512] + seek $fd2 [expr $::sqlite_pending_byte+512] + } + puts -nonewline $fd2 [read $fd] + close $fd2 + close $fd + + # Open "test2.db" and check it is Ok. + sqlite3 dbcheck test2.db + set ret [dbcheck eval { PRAGMA integrity_check }] + dbcheck close + set ret +} + +# Run these tests once in rollback journal mode, and once in wal mode. +# +foreach {T jrnl_mode} { + 1 delete + 2 wal +} { + catch { db close } + forcedelete test.db test.db-journal test.db-wal + sqlite3 db test.db + db eval { + PRAGMA cache_size = 5; + PRAGMA page_size = 1024; + PRAGMA auto_vacuum = 2; + } + db eval "PRAGMA journal_mode = $jrnl_mode" + + foreach {tn sql} { + 1 { + CREATE TABLE t1(x UNIQUE); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 4 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 8 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 16 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 32 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256 + } + + 2 { + DELETE FROM t1 WHERE rowid%8; + } + + 3 { + BEGIN; + PRAGMA incremental_vacuum = 100; + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256 + ROLLBACK; + } + + 4 { + BEGIN; + SAVEPOINT one; + PRAGMA incremental_vacuum = 100; + SAVEPOINT two; + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256 + } + + 5 { ROLLBACK to two } + + 6 { ROLLBACK to one } + + 7 { + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64 + PRAGMA incremental_vacuum = 1000; + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128 + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256 + ROLLBACK; + } + + 8 { + BEGIN; + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64 + PRAGMA incremental_vacuum = 1000; + INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128 + COMMIT; + } + } { + do_execsql_test $T.1.$tn.1 $sql + do_execsql_test $T.1.$tn.2 {PRAGMA integrity_check} ok + do_test $T.1.$tn.3 { check_on_disk } ok + } + + do_execsql_test $T.1.x.1 { PRAGMA freelist_count } 0 + do_execsql_test $T.1.x.2 { SELECT count(*) FROM t1 } 128 +} + +finish_test + diff --git a/test/incrvacuum_ioerr.test b/test/incrvacuum_ioerr.test index 946925d..50f7fa2 100644 --- a/test/incrvacuum_ioerr.test +++ b/test/incrvacuum_ioerr.test @@ -139,8 +139,9 @@ ifcapable shared_cache { # Figure out how big the database is and how many free pages it # has before running incremental-vacuum. # - set nPage [expr {[file size test.db]/1024}] set nFree [execsql {pragma freelist_count} db1] + set nPage [execsql {pragma page_count} db1] + puts "nFree=$nFree nPage=$nPage" # Now run incremental-vacuum to vacuum 5 pages from the db file. # The iTest'th I/O call is set to fail. @@ -158,11 +159,11 @@ ifcapable shared_cache { set ::sqlite_io_error_hardhit 0 set nFree2 [execsql {pragma freelist_count} db1] - set nPage2 [expr {[file size test.db]/1024}] + set nPage2 [execsql {pragma page_count} db1] do_test incrvacuum-ioerr-4.$iTest.2 { set shrink [expr {$nPage-$nPage2}] - expr {$shrink==0 || $shrink==5} + expr {$shrink==0 || $shrink==5 || ($nFree<5 && $shrink==$nFree)} } {1} do_test incrvacuum-ioerr-4.$iTest.3 { diff --git a/test/index5.test b/test/index5.test index c8e94b3..7895391 100644 --- a/test/index5.test +++ b/test/index5.test @@ -36,11 +36,10 @@ db close testvfs tvfs tvfs filter xWrite tvfs script write_cb -proc write_cb {xCall file handle iOfst} { +proc write_cb {xCall file handle iOfst args} { if {[file tail $file]=="test.db"} { lappend ::write_list [expr $iOfst/1024] } - puts "$xCall $file $args" } do_test 1.2 { @@ -65,11 +64,12 @@ do_test 1.3 { } set iPrev $iNext } + puts -nonewline \ + " (forward=$nForward, back=$nBackward, noncontiguous=$nNoncont)" - expr {$nForward > $nBackward} + expr {$nForward > 2*($nBackward + $nNoncont)} } {1} db close tvfs delete finish_test - diff --git a/test/instr.test b/test/instr.test new file mode 100644 index 0000000..b328cd1 --- /dev/null +++ b/test/instr.test @@ -0,0 +1,210 @@ +# 2012 October 24 +# +# 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 implements regression tests for SQLite library. The +# focus of this file is testing the built-in INSTR() functions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Create a table to work with. +# +do_test instr-1.1 { + db eval {SELECT instr('abcdefg','a');} +} {1} +do_test instr-1.2 { + db eval {SELECT instr('abcdefg','b');} +} {2} +do_test instr-1.3 { + db eval {SELECT instr('abcdefg','c');} +} {3} +do_test instr-1.4 { + db eval {SELECT instr('abcdefg','d');} +} {4} +do_test instr-1.5 { + db eval {SELECT instr('abcdefg','e');} +} {5} +do_test instr-1.6 { + db eval {SELECT instr('abcdefg','f');} +} {6} +do_test instr-1.7 { + db eval {SELECT instr('abcdefg','g');} +} {7} +do_test instr-1.8 { + db eval {SELECT instr('abcdefg','h');} +} {0} +do_test instr-1.9 { + db eval {SELECT instr('abcdefg','abcdefg');} +} {1} +do_test instr-1.10 { + db eval {SELECT instr('abcdefg','abcdefgh');} +} {0} +do_test instr-1.11 { + db eval {SELECT instr('abcdefg','bcdefg');} +} {2} +do_test instr-1.12 { + db eval {SELECT instr('abcdefg','bcdefgh');} +} {0} +do_test instr-1.13 { + db eval {SELECT instr('abcdefg','cdefg');} +} {3} +do_test instr-1.14 { + db eval {SELECT instr('abcdefg','cdefgh');} +} {0} +do_test instr-1.15 { + db eval {SELECT instr('abcdefg','defg');} +} {4} +do_test instr-1.16 { + db eval {SELECT instr('abcdefg','defgh');} +} {0} +do_test instr-1.17 { + db eval {SELECT instr('abcdefg','efg');} +} {5} +do_test instr-1.18 { + db eval {SELECT instr('abcdefg','efgh');} +} {0} +do_test instr-1.19 { + db eval {SELECT instr('abcdefg','fg');} +} {6} +do_test instr-1.20 { + db eval {SELECT instr('abcdefg','fgh');} +} {0} +do_test instr-1.21 { + db eval {SELECT coalesce(instr('abcdefg',NULL),'nil');} +} {nil} +do_test instr-1.22 { + db eval {SELECT coalesce(instr(NULL,'x'),'nil');} +} {nil} +do_test instr-1.23 { + db eval {SELECT instr(12345,34);} +} {3} +do_test instr-1.24 { + db eval {SELECT instr(123456.78,34);} +} {3} +do_test instr-1.25 { + db eval {SELECT instr(123456.78,x'3334');} +} {3} +do_test instr-1.26 { + db eval {SELECT instr('äbcdefg','efg');} +} {5} +do_test instr-1.27 { + db eval {SELECT instr('€xyzzy','xyz');} +} {2} +do_test instr-1.28 { + db eval {SELECT instr('abc€xyzzy','xyz');} +} {5} +do_test instr-1.29 { + db eval {SELECT instr('abc€xyzzy','€xyz');} +} {4} +do_test instr-1.30 { + db eval {SELECT instr('abc€xyzzy','c€xyz');} +} {3} +do_test instr-1.31 { + db eval {SELECT instr(x'0102030405',x'01');} +} {1} +do_test instr-1.32 { + db eval {SELECT instr(x'0102030405',x'02');} +} {2} +do_test instr-1.33 { + db eval {SELECT instr(x'0102030405',x'03');} +} {3} +do_test instr-1.34 { + db eval {SELECT instr(x'0102030405',x'04');} +} {4} +do_test instr-1.35 { + db eval {SELECT instr(x'0102030405',x'05');} +} {5} +do_test instr-1.36 { + db eval {SELECT instr(x'0102030405',x'06');} +} {0} +do_test instr-1.37 { + db eval {SELECT instr(x'0102030405',x'0102030405');} +} {1} +do_test instr-1.38 { + db eval {SELECT instr(x'0102030405',x'02030405');} +} {2} +do_test instr-1.39 { + db eval {SELECT instr(x'0102030405',x'030405');} +} {3} +do_test instr-1.40 { + db eval {SELECT instr(x'0102030405',x'0405');} +} {4} +do_test instr-1.41 { + db eval {SELECT instr(x'0102030405',x'0506');} +} {0} +do_test instr-1.42 { + db eval {SELECT instr(x'0102030405',x'');} +} {1} +do_test instr-1.43 { + db eval {SELECT instr(x'',x'');} +} {1} +do_test instr-1.44 { + db eval {SELECT instr('','');} +} {1} +do_test instr-1.45 { + db eval {SELECT instr('abcdefg','');} +} {1} +unset -nocomplain longstr +set longstr abcdefghijklmonpqrstuvwxyz +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +# puts [string length $longstr] +append longstr Xabcde +do_test instr-1.46 { + db eval {SELECT instr($longstr,'X');} +} {106497} +do_test instr-1.47 { + db eval {SELECT instr($longstr,'Y');} +} {0} +do_test instr-1.48 { + db eval {SELECT instr($longstr,'Xa');} +} {106497} +do_test instr-1.49 { + db eval {SELECT instr($longstr,'zXa');} +} {106496} +set longstr [string map {a ä} $longstr] +do_test instr-1.50 { + db eval {SELECT instr($longstr,'X');} +} {106497} +do_test instr-1.51 { + db eval {SELECT instr($longstr,'Y');} +} {0} +do_test instr-1.52 { + db eval {SELECT instr($longstr,'Xä');} +} {106497} +do_test instr-1.53 { + db eval {SELECT instr($longstr,'zXä');} +} {106496} +do_test instr-1.54 { + db eval {SELECT instr(x'78c3a4e282ac79','x');} +} {1} +do_test instr-1.55 { + db eval {SELECT instr(x'78c3a4e282ac79','y');} +} {4} +do_test instr-1.56 { + db eval {SELECT instr(x'78c3a4e282ac79',x'79');} +} {7} +do_test instr-1.57 { + db eval {SELECT instr('xä€y',x'79');} +} {4} + + +finish_test diff --git a/test/interrupt.test b/test/interrupt.test index b311cbb..92ab4c3 100644 --- a/test/interrupt.test +++ b/test/interrupt.test @@ -166,6 +166,8 @@ for {set i 1} {$i<$max_count-5} {incr i 1} { } {1 interrupted} } +if {0} { # This doesn't work anymore since the collation factor is + # no longer called during schema parsing. # Interrupt during parsing # do_test interrupt-5.1 { @@ -179,5 +181,5 @@ do_test interrupt-5.1 { CREATE INDEX fake ON fake1(a COLLATE fake_collation, b, c DESC); } } {1 interrupt} - +} finish_test diff --git a/test/intpkey.test b/test/intpkey.test index 05b6cdf..db39421 100644 --- a/test/intpkey.test +++ b/test/intpkey.test @@ -376,7 +376,7 @@ do_test intpkey-5.1 { } {0 zero entry 0} do_test intpkey-5.2 { execsql { - SELECT rowid, a FROM t1 + SELECT rowid, a FROM t1 ORDER BY rowid } } {-4 -4 0 0 5 5 6 6 11 11} diff --git a/test/io.test b/test/io.test index 9363b0c..11f9cc8 100644 --- a/test/io.test +++ b/test/io.test @@ -16,6 +16,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix io db close sqlite3_simulate_device @@ -38,6 +39,10 @@ sqlite3 db test.db -vfs devsym # # io-5.* - Test that the default page size is selected and used # correctly. +# +# io-6.* - Test that the pager-cache is not being flushed unnecessarily +# after a transaction that uses the special atomic-write path +# is committed. # set ::nWrite 0 @@ -207,7 +212,7 @@ do_test io-2.5.3 { # Changed 2010-03-27: The size of the database is now stored in # bytes 28..31 and so when a page is added to the database, page 1 # is immediately modified and the journal file immediately comes into -# existance. To fix this test, the BEGIN is changed into a a +# existence. To fix this test, the BEGIN is changed into a a # BEGIN IMMEDIATE and the INSERT is omitted. # do_test io-2.6.1 { @@ -565,5 +570,75 @@ foreach {char sectorsize pgsize} { } $pgsize } +#---------------------------------------------------------------------- +# +do_test io-6.1 { + db close + sqlite3_simulate_device -char atomic + forcedelete test.db + sqlite3 db test.db -vfs devsym + execsql { + PRAGMA mmap_size = 0; + PRAGMA page_size = 1024; + PRAGMA cache_size = 2000; + CREATE TABLE t1(x); + CREATE TABLE t2(x); + CREATE TABLE t3(x); + CREATE INDEX i3 ON t3(x); + INSERT INTO t3 VALUES(randomblob(100)); + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + } + + db_save_and_close +} {} + +foreach {tn sql} { + 1 { BEGIN; + INSERT INTO t1 VALUES('123'); + INSERT INTO t2 VALUES('456'); + COMMIT; + } + 2 { BEGIN; + INSERT INTO t1 VALUES('123'); + COMMIT; + } +} { + + # These tests don't work with memsubsys1, as it causes the effective page + # cache size to become too small to hold the entire db in memory. + if {[permutation] == "memsubsys1"} continue + + db_restore + sqlite3 db test.db -vfs devsym + execsql { + PRAGMA cache_size = 2000; + PRAGMA mmap_size = 0; + SELECT x FROM t3 ORDER BY rowid; + SELECT x FROM t3 ORDER BY x; + } + do_execsql_test 6.2.$tn.1 { PRAGMA integrity_check } {ok} + do_execsql_test 6.2.$tn.2 $sql + + # Corrupt the database file on disk. This should not matter for the + # purposes of the following "PRAGMA integrity_check", as the entire + # database should be cached in the pager-cache. If corruption is + # reported, it indicates that executing $sql caused the pager cache + # to be flushed. Which is a bug. + hexio_write test.db [expr 1024 * 5] [string repeat 00 2048] + do_execsql_test 6.2.$tn.3 { PRAGMA integrity_check } {ok} + db close +} + sqlite3_simulate_device -char {} -sectorsize 0 finish_test + diff --git a/test/ioerr6.test b/test/ioerr6.test new file mode 100644 index 0000000..66c48ad --- /dev/null +++ b/test/ioerr6.test @@ -0,0 +1,92 @@ +# 2012 December 18 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set ::testprefix ioerr6 + +ifcapable !atomicwrite { + puts "skipping tests - not compiled with SQLITE_ENABLE_ATOMIC_WRITE..." + finish_test + return +} + +if {[permutation]=="inmemory_journal"} { + # These tests will not work with in-memory journals (as persistent VFS + # errors commencing after a transaction has started to write to the db + # cannot be recovered from). + finish_test + return +} + +faultsim_save_and_close + +do_test 1.1 { + testvfs shmfault -default true + shmfault devchar atomic + sqlite3 db test.db + execsql { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(2, 4); + INSERT INTO t1 VALUES(3, 6); + INSERT INTO t1 VALUES(4, 8); + } + + # Cause the first call to xWrite() to fail with SQLITE_FULL. + shmfault full 2 1 + catchsql { INSERT INTO t1 VALUES(5, 10) } +} {1 {database or disk is full}} + +do_test 1.2 { + execsql { PRAGMA integrity_check } +} {ok} + +db close +shmfault delete + +do_faultsim_test 2 -faults full* -prep { + shmfault devchar atomic + faultsim_restore + sqlite3 db test.db +} -body { + db eval { + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES('abc'); + } +} -test { + set res [db one { PRAGMA integrity_check }] + if {$res != "ok"} { + error "integrity check: $res" + } +} + +do_faultsim_test 3 -faults full* -prep { + shmfault devchar atomic + faultsim_restore + sqlite3 db test.db +} -body { + db eval { + CREATE TABLE t1(x); + CREATE TABLE t2(x); + } +} -test { + db eval { CREATE TABLE t3(x) } + if {[db one { PRAGMA integrity_check }] != "ok"} { + error "integrity check failed" + } +} + +finish_test + diff --git a/test/like.test b/test/like.test index 767efd5..80ba418 100644 --- a/test/like.test +++ b/test/like.test @@ -406,7 +406,7 @@ do_test like-5.2 { do_test like-5.3 { execsql { CREATE TABLE t2(x TEXT COLLATE NOCASE); - INSERT INTO t2 SELECT * FROM t1; + INSERT INTO t2 SELECT * FROM t1 ORDER BY rowid; CREATE INDEX i2 ON t2(x COLLATE NOCASE); } set sqlite_like_count 0 @@ -662,8 +662,8 @@ ifcapable like_opt&&!icu { set res [sqlite3_exec_hex db { EXPLAIN QUERY PLAN SELECT x FROM t2 WHERE x LIKE '%ff%25' }] - regexp {INDEX i2} $res - } {0} + regexp {SCAN TABLE t2} $res + } {1} } do_test like-9.5.1 { set res [sqlite3_exec_hex db { diff --git a/test/limit.test b/test/limit.test index e5aac70..c5b75c2 100644 --- a/test/limit.test +++ b/test/limit.test @@ -468,5 +468,152 @@ do_test limit-12.4 { } } {1 {no such column: x}} +# Ticket [db4d96798da8b] +# LIMIT does not work with nested views containing UNION ALL +# +do_test limit-13.1 { + db eval { + CREATE TABLE t13(x); + INSERT INTO t13 VALUES(1),(2); + CREATE VIEW v13a AS SELECT x AS y FROM t13; + CREATE VIEW v13b AS SELECT y AS z FROM v13a UNION ALL SELECT y+10 FROM v13a; + CREATE VIEW v13c AS SELECT z FROM v13b UNION ALL SELECT z+20 FROM v13b; + } +} {} +do_test limit-13.2 { + db eval {SELECT z FROM v13c LIMIT 1} +} {1} +do_test limit-13.3 { + db eval {SELECT z FROM v13c LIMIT 2} +} {1 2} +do_test limit-13.4 { + db eval {SELECT z FROM v13c LIMIT 3} +} {1 2 11} +do_test limit-13.5 { + db eval {SELECT z FROM v13c LIMIT 4} +} {1 2 11 12} +do_test limit-13.6 { + db eval {SELECT z FROM v13c LIMIT 5} +} {1 2 11 12 21} +do_test limit-13.7 { + db eval {SELECT z FROM v13c LIMIT 6} +} {1 2 11 12 21 22} +do_test limit-13.8 { + db eval {SELECT z FROM v13c LIMIT 7} +} {1 2 11 12 21 22 31} +do_test limit-13.9 { + db eval {SELECT z FROM v13c LIMIT 8} +} {1 2 11 12 21 22 31 32} +do_test limit-13.10 { + db eval {SELECT z FROM v13c LIMIT 9} +} {1 2 11 12 21 22 31 32} +do_test limit-13.11 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 1} +} {2} +do_test limit-13.12 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 1} +} {2 11} +do_test limit-13.13 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 1} +} {2 11 12} +do_test limit-13.14 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 1} +} {2 11 12 21} +do_test limit-13.15 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 1} +} {2 11 12 21 22} +do_test limit-13.16 { + db eval {SELECT z FROM v13c LIMIT 6 OFFSET 1} +} {2 11 12 21 22 31} +do_test limit-13.17 { + db eval {SELECT z FROM v13c LIMIT 7 OFFSET 1} +} {2 11 12 21 22 31 32} +do_test limit-13.18 { + db eval {SELECT z FROM v13c LIMIT 8 OFFSET 1} +} {2 11 12 21 22 31 32} +do_test limit-13.21 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 2} +} {11} +do_test limit-13.22 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 2} +} {11 12} +do_test limit-13.23 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 2} +} {11 12 21} +do_test limit-13.24 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 2} +} {11 12 21 22} +do_test limit-13.25 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 2} +} {11 12 21 22 31} +do_test limit-13.26 { + db eval {SELECT z FROM v13c LIMIT 6 OFFSET 2} +} {11 12 21 22 31 32} +do_test limit-13.27 { + db eval {SELECT z FROM v13c LIMIT 7 OFFSET 2} +} {11 12 21 22 31 32} +do_test limit-13.31 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 3} +} {12} +do_test limit-13.32 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 3} +} {12 21} +do_test limit-13.33 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 3} +} {12 21 22} +do_test limit-13.34 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 3} +} {12 21 22 31} +do_test limit-13.35 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 3} +} {12 21 22 31 32} +do_test limit-13.36 { + db eval {SELECT z FROM v13c LIMIT 6 OFFSET 3} +} {12 21 22 31 32} +do_test limit-13.41 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 4} +} {21} +do_test limit-13.42 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 4} +} {21 22} +do_test limit-13.43 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 4} +} {21 22 31} +do_test limit-13.44 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 4} +} {21 22 31 32} +do_test limit-13.45 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 4} +} {21 22 31 32} +do_test limit-13.51 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 5} +} {22} +do_test limit-13.52 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 5} +} {22 31} +do_test limit-13.53 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 5} +} {22 31 32} +do_test limit-13.54 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 5} +} {22 31 32} +do_test limit-13.61 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 6} +} {31} +do_test limit-13.62 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 6} +} {31 32} +do_test limit-13.63 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 6} +} {31 32} +do_test limit-13.71 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 7} +} {32} +do_test limit-13.72 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 7} +} {32} +do_test limit-13.81 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 8} +} {} finish_test diff --git a/test/loadext.test b/test/loadext.test index 72eff12..0d5b841 100644 --- a/test/loadext.test +++ b/test/loadext.test @@ -139,20 +139,22 @@ do_test loadext-2.1 { sqlite3_load_extension db "${testextension}xx" } msg] list $rc $msg -} [list 1 [format $dlerror_nosuchfile ${testextension}xx]] +} /[list 1 [format $dlerror_nosuchfile ${testextension}xx.*]]/ # Try to load an extension for which the file is not a shared object # do_test loadext-2.2 { - set fd [open "${testextension}xx" w] + set fd [open "./notasharedlib.so" w] + puts $fd blah + close $fd + set fd [open "./notasharedlib.dll" w] puts $fd blah close $fd set rc [catch { - sqlite3_load_extension db "${testextension}xx" + sqlite3_load_extension db "./notasharedlib" } msg] - set expected_error_pattern [format $dlerror_notadll ${testextension}xx] - list $rc [string match $expected_error_pattern $msg] -} [list 1 1] + list $rc $msg +} /[list 1 [format $dlerror_notadll ./notasharedlib.*]]/ # Try to load an extension for which the file is present but the # entry point is not. @@ -196,7 +198,7 @@ do_test loadext-3.2 { regsub {0x[1234567890abcdefABCDEF]*} $res XXX res } set res -} [list 1 [format $dlerror_nosymbol $testextension sqlite3_extension_init]] +} /[list 1 [format $dlerror_nosymbol $testextension sqlite3_.*_init]]/ do_test loadext-3.3 { catchsql { SELECT load_extension($::testextension,'testloadext_init') diff --git a/test/lock.test b/test/lock.test index 22f359c..6ec85ee 100644 --- a/test/lock.test +++ b/test/lock.test @@ -247,11 +247,34 @@ do_test lock-2.8 { execsql {UPDATE t1 SET a = 0 WHERE 0} catchsql {BEGIN EXCLUSIVE;} db2 } {1 {database is locked}} +do_test lock-2.8b { + db2 eval {PRAGMA busy_timeout} +} {400} do_test lock-2.9 { db2 timeout 0 execsql COMMIT } {} +do_test lock-2.9b { + db2 eval {PRAGMA busy_timeout} +} {0} integrity_check lock-2.10 +do_test lock-2.11 { + db2 eval {PRAGMA busy_timeout(400)} + execsql BEGIN + execsql {UPDATE t1 SET a = 0 WHERE 0} + catchsql {BEGIN EXCLUSIVE;} db2 +} {1 {database is locked}} +do_test lock-2.11b { + db2 eval {PRAGMA busy_timeout} +} {400} +do_test lock-2.12 { + db2 eval {PRAGMA busy_timeout(0)} + execsql COMMIT +} {} +do_test lock-2.12b { + db2 eval {PRAGMA busy_timeout} +} {0} +integrity_check lock-2.13 # Try to start two transactions in a row # diff --git a/test/malloc.test b/test/malloc.test index 0d213d7..5d03aa8 100644 --- a/test/malloc.test +++ b/test/malloc.test @@ -842,7 +842,7 @@ do_malloc_test 36 -sqlprep { SELECT test_agg_errmsg16(), group_concat(a) FROM t1 } -# At one point, if an OOM occured immediately after obtaining a shared lock +# At one point, if an OOM occurred immediately after obtaining a shared lock # on the database file, the file remained locked. This test case ensures # that bug has been fixed.i if {[db eval {PRAGMA locking_mode}]!="exclusive"} { diff --git a/test/malloc3.test b/test/malloc3.test index 2dfde46..f4a6c3b 100644 --- a/test/malloc3.test +++ b/test/malloc3.test @@ -27,6 +27,24 @@ if {!$MEMDEBUG} { return } + +# Do not run these tests with an in-memory journal. +# +# In the pager layer, if an IO or OOM error occurs during a ROLLBACK, or +# when flushing a page to disk due to cache-stress, the pager enters an +# "error state". The only way out of the error state is to unlock the +# database file and end the transaction, leaving whatever journal and +# database files happen to be on disk in place. The next time the current +# (or any other) connection opens a read transaction, hot-journal rollback +# is performed if necessary. +# +# Of course, this doesn't work with an in-memory journal. +# +if {[permutation]=="inmemory_journal"} { + finish_test + return +} + #-------------------------------------------------------------------------- # NOTES ON RECOVERING FROM A MALLOC FAILURE # @@ -147,6 +165,7 @@ if {!$MEMDEBUG} { # ::run_test_script. At the end of this file, the proc [run_test] is used # to execute the program (and all test cases contained therein). # +set ::run_test_sql_id 0 set ::run_test_script [list] proc TEST {id t} {lappend ::run_test_script -test [list $id $t]} proc PREP {p} {lappend ::run_test_script -prep [string trim $p]} @@ -162,13 +181,14 @@ proc DEBUG {s} {lappend ::run_test_script -debug $s} # transaction only. # proc SQL {a1 {a2 ""}} { - # An SQL primitive parameter is a list of two elements, a boolean value - # indicating if the statement may cause transaction rollback when malloc() - # fails, and the sql statement itself. + # An SQL primitive parameter is a list of three elements, an id, a boolean + # value indicating if the statement may cause transaction rollback when + # malloc() fails, and the sql statement itself. + set id [incr ::run_test_sql_id] if {$a2 == ""} { - lappend ::run_test_script -sql [list true [string trim $a1]] + lappend ::run_test_script -sql [list $id true [string trim $a1]] } else { - lappend ::run_test_script -sql [list false [string trim $a2]] + lappend ::run_test_script -sql [list $id false [string trim $a2]] } } @@ -258,7 +278,7 @@ TEST 5 { set sql { BEGIN;DELETE FROM abc; } -for {set i 1} {$i < 15} {incr i} { +for {set i 1} {$i < 100} {incr i} { set a $i set b "String value $i" set c [string repeat X $i] @@ -529,12 +549,13 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { } for {set i 0} {$i < $pcstart} {incr i} { - set k2 [lindex $arglist [expr 2 * $i]] - set v2 [lindex $arglist [expr 2 * $i + 1]] + set k2 [lindex $arglist [expr {2 * $i}]] + set v2 [lindex $arglist [expr {2 * $i + 1}]] set ac [sqlite3_get_autocommit $::DB] ;# Auto-Commit switch -- $k2 { - -sql {db eval [lindex $v2 1]} + -sql {db eval [lindex $v2 2]} -prep {db eval $v2} + -debug {eval $v2} } set nac [sqlite3_get_autocommit $::DB] ;# New Auto-Commit if {$ac && !$nac} {set begin_pc $i} @@ -545,33 +566,39 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { set iFail $iFailStart set pc $pcstart while {$pc*2 < [llength $arglist]} { + # Fetch the current instruction type and payload. + set k [lindex $arglist [expr {2 * $pc}]] + set v [lindex $arglist [expr {2 * $pc + 1}]] # Id of this iteration: - set k [lindex $arglist [expr 2 * $pc]] set iterid "pc=$pc.iFail=$iFail$k" - set v [lindex $arglist [expr 2 * $pc + 1]] switch -- $k { -test { foreach {id script} $v {} + set testid "malloc3-(test $id).$iterid" + eval $script incr pc } -sql { set ::rollback_hook_count 0 + set id [lindex $v 0] + set testid "malloc3-(integrity $id).$iterid" + set ac [sqlite3_get_autocommit $::DB] ;# Auto-Commit sqlite3_memdebug_fail $iFail -repeat 0 - set rc [catch {db eval [lindex $v 1]} msg] ;# True error occurs + set rc [catch {db eval [lindex $v 2]} msg] ;# True error occurs set nac [sqlite3_get_autocommit $::DB] ;# New Auto-Commit if {$rc != 0 && $nac && !$ac} { # Before [db eval] the auto-commit flag was clear. Now it - # is set. Since an error occured we assume this was not a - # commit - therefore a rollback occured. Check that the + # is set. Since an error occurred we assume this was not a + # commit - therefore a rollback occurred. Check that the # rollback-hook was invoked. - do_test malloc3-rollback_hook.$iterid { + do_test malloc3-rollback_hook_count.$iterid { set ::rollback_hook_count } {1} } @@ -582,8 +609,9 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { # calls should be equal to the number of benign failures. # Otherwise a malloc() failed and the error was not reported. # - if {$nFail!=$nBenign} { - error "Unreported malloc() failure" + set expr {$nFail!=$nBenign} + if {[expr $expr]} { + error "Unreported malloc() failure, test \"$testid\", $expr" } if {$ac && !$nac} { @@ -595,24 +623,23 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { incr pc set iFail 1 - integrity_check "malloc3-(integrity).$iterid" + integrity_check $testid } elseif {[regexp {.*out of memory} $msg] || [db errorcode] == 3082} { # Out of memory error, as expected. # - integrity_check "malloc3-(integrity).$iterid" + integrity_check $testid incr iFail if {$nac && !$ac} { - - if {![lindex $v 0] && [db errorcode] != 3082} { - # error "Statement \"[lindex $v 1]\" caused a rollback" + if {![lindex $v 1] && [db errorcode] != 3082} { + # error "Statement \"[lindex $v 2]\" caused a rollback" } for {set i $begin_pc} {$i < $pc} {incr i} { - set k2 [lindex $arglist [expr 2 * $i]] - set v2 [lindex $arglist [expr 2 * $i + 1]] + set k2 [lindex $arglist [expr {2 * $i}]] + set v2 [lindex $arglist [expr {2 * $i + 1}]] set catchupsql "" switch -- $k2 { - -sql {set catchupsql [lindex $v2 1]} + -sql {set catchupsql [lindex $v2 2]} -prep {set catchupsql $v2} } db eval $catchupsql @@ -622,7 +649,8 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { error $msg } - while {[lindex $arglist [expr 2 * ($pc -1)]] == "-test"} { + # back up to the previous "-test" block. + while {[lindex $arglist [expr {2 * ($pc - 1)}]] == "-test"} { incr pc -1 } } @@ -642,7 +670,7 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { } } -# Turn of the Tcl interface's prepared statement caching facility. Then +# Turn off the Tcl interface's prepared statement caching facility. Then # run the tests with "persistent" malloc failures. sqlite3_extended_result_codes db 1 db cache size 0 diff --git a/test/mallocG.test b/test/mallocG.test index eab533b..4f2395d 100644 --- a/test/mallocG.test +++ b/test/mallocG.test @@ -53,6 +53,11 @@ do_malloc_test mallocG-3 -sqlprep { AND x BETWEEN 'i' AND 'm' } +ifcapable !utf16 { + finish_test + return +} + proc utf16 {utf8} { set utf16 [encoding convertto unicode $utf8] append utf16 "\x00\x00" diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl index 5937b95..2ac619b 100644 --- a/test/malloc_common.tcl +++ b/test/malloc_common.tcl @@ -264,7 +264,7 @@ proc faultsim_test_result_int {args} { set t [list $testrc $testresult] set r $args if { ($testnfail==0 && $t != [lindex $r 0]) || [lsearch $r $t]<0 } { - error "nfail=$testnfail rc=$testrc result=$testresult" + error "nfail=$testnfail rc=$testrc result=$testresult list=$r" } } diff --git a/test/memdb.test b/test/memdb.test index 1da3d7c..802fb1a 100644 --- a/test/memdb.test +++ b/test/memdb.test @@ -365,7 +365,7 @@ do_test memdb-6.15 { ifcapable subquery&&vtab { do_test memdb-7.1 { - register_wholenumber_module db + load_static_extension db wholenumber execsql { CREATE TABLE t6(x); CREATE VIRTUAL TABLE nums USING wholenumber; diff --git a/test/minmax.test b/test/minmax.test index 5990245..fb9bbb3 100644 --- a/test/minmax.test +++ b/test/minmax.test @@ -17,6 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix minmax do_test minmax-1.0 { execsql { @@ -299,7 +300,7 @@ ifcapable {compound && subquery} { SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5 ) } - } {1} + } {{}} do_test minmax-9.2 { execsql { SELECT max(rowid) FROM ( @@ -536,7 +537,96 @@ do_test minmax-12.17 { } } {5} +#------------------------------------------------------------------------- +reset_db + +proc do_test_13 {op name sql1 sql2 res} { + set ::sqlite_search_count 0 + uplevel [list do_execsql_test $name.1 $sql1 $res] + set a $::sqlite_search_count + + set ::sqlite_search_count 0 + uplevel [list do_execsql_test $name.2 $sql2 $res] + set b $::sqlite_search_count + + uplevel [list do_test $name.3 [list expr "$a $op $b"] 1] +} + +# Run a test named $name. Check that SQL statements $sql1 and $sql2 both +# return the same result, but that $sql2 increments the $sqlite_search_count +# variable more often (indicating that it is visiting more rows to determine +# the result). +# +proc do_test_13_opt {name sql1 sql2 res} { + uplevel [list do_test_13 < $name $sql1 $sql2 $res] +} + +# Like [do_test_13_noopt], except this time check that the $sqlite_search_count +# variable is incremented the same number of times by both SQL statements. +# +proc do_test_13_noopt {name sql1 sql2 res} { + uplevel [list do_test_13 == $name $sql1 $sql2 $res] +} + +do_execsql_test 13.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES('a', 1, 1); + INSERT INTO t1 VALUES('b', 6, 6); + INSERT INTO t1 VALUES('c', 5, 5); + INSERT INTO t1 VALUES('a', 4, 4); + INSERT INTO t1 VALUES('a', 5, 5); + INSERT INTO t1 VALUES('c', 6, 6); + INSERT INTO t1 VALUES('b', 4, 4); + INSERT INTO t1 VALUES('c', 7, 7); + INSERT INTO t1 VALUES('b', 2, 2); + INSERT INTO t1 VALUES('b', 3, 3); + INSERT INTO t1 VALUES('a', 3, 3); + INSERT INTO t1 VALUES('b', 5, 5); + INSERT INTO t1 VALUES('c', 4, 4); + INSERT INTO t1 VALUES('c', 3, 3); + INSERT INTO t1 VALUES('a', 2, 2); + SELECT * FROM t1 ORDER BY a, b, c; +} {a 1 1 a 2 2 a 3 3 a 4 4 a 5 5 + b 2 2 b 3 3 b 4 4 b 5 5 b 6 6 + c 3 3 c 4 4 c 5 5 c 6 6 c 7 7 +} +do_execsql_test 13.2 { CREATE INDEX i1 ON t1(a, b, c) } + +do_test_13_opt 13.3 { + SELECT min(b) FROM t1 WHERE a='b' +} { + SELECT min(c) FROM t1 WHERE a='b' +} {2} + +do_test_13_opt 13.4 { + SELECT a, min(b) FROM t1 WHERE a='b' +} { + SELECT a, min(c) FROM t1 WHERE a='b' +} {b 2} + +do_test_13_opt 13.4 { + SELECT a||c, max(b)+4 FROM t1 WHERE a='c' +} { + SELECT a||c, max(c)+4 FROM t1 WHERE a='c' +} {c7 11} + +do_test_13_noopt 13.5 { + SELECT a||c, max(b+1) FROM t1 WHERE a='c' +} { + SELECT a||c, max(c+1) FROM t1 WHERE a='c' +} {c7 8} + +do_test_13_noopt 13.6 { + SELECT count(b) FROM t1 WHERE a='c' +} { + SELECT count(c) FROM t1 WHERE a='c' +} {5} +do_test_13_noopt 13.7 { + SELECT min(b), count(b) FROM t1 WHERE a='a'; +} { + SELECT min(c), count(c) FROM t1 WHERE a='a'; +} {1 5} finish_test diff --git a/test/minmax2.test b/test/minmax2.test index 2f504d4..da8fec3 100644 --- a/test/minmax2.test +++ b/test/minmax2.test @@ -289,7 +289,7 @@ ifcapable {compound && subquery} { SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5 ) } - } {1} + } {{}} do_test minmax2-9.2 { execsql { SELECT max(rowid) FROM ( diff --git a/test/misc7.test b/test/misc7.test index 4868c12..72c1cd6 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -488,6 +488,34 @@ do_test misc7-21.1 { list $rc $msg } {1 {unable to open database file}} +# Try to do hot-journal rollback with a read-only connection. The +# error code should be SQLITE_READONLY_ROLLBACK. +# +do_test misc7-22.1 { + db close + forcedelete test.db copy.db-journal + sqlite3 db test.db + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + } + db close + sqlite3 db test.db -readonly 1 + catchsql { + INSERT INTO t1 VALUES(5, 6); + } +} {1 {attempt to write a readonly database}} +do_test misc7-22.2 { execsql { SELECT * FROM t1 } } {1 2 3 4} +do_test misc7-22.3 { + set fd [open test.db-journal w] + puts $fd [string repeat abc 1000] + close $fd + catchsql { SELECT * FROM t1 } +} {1 {attempt to write a readonly database}} +do_test misc7-22.4 { + sqlite3_extended_errcode db +} SQLITE_READONLY_ROLLBACK db close forcedelete test.db diff --git a/test/mmap1.test b/test/mmap1.test new file mode 100644 index 0000000..ece3e02 --- /dev/null +++ b/test/mmap1.test @@ -0,0 +1,331 @@ +# 2013 March 20 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !mmap { + finish_test + return +} +source $testdir/lock_common.tcl +set testprefix mmap1 + +proc nRead {db} { + set bt [btree_from_db $db] + db_enter $db + array set stats [btree_pager_stats $bt] + db_leave $db + # puts [array get stats] + return $stats(read) +} + +proc register_rblob_code {dbname seed} { + return [subst -nocommands { + set ::rcnt $seed + proc rblob {n} { + set ::rcnt [expr (([set ::rcnt] << 3) + [set ::rcnt] + 456) & 0xFFFFFFFF] + set str [format %.8x [expr [set ::rcnt] ^ 0xbdf20da3]] + string range [string repeat [set str] [expr [set n]/4]] 1 [set n] + } + $dbname func rblob rblob + }] +} + +# For cases 1.1 and 1.4, the number of pages read using xRead() is 4 on +# unix and 9 on windows. The difference is that windows only ever maps +# an integer number of OS pages (i.e. creates mappings that are a multiple +# of 4KB in size). Whereas on unix any sized mapping may be created. +# +foreach {t mmap_size nRead c2init} { + 1.1 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 0} + 1.2 { PRAGMA mmap_size = 53248 } 150 {PRAGMA mmap_size = 0} + 1.3 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 0} + 1.4 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 67108864 } + 1.5 { PRAGMA mmap_size = 53248 } 150 {PRAGMA mmap_size = 67108864 } + 1.6 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 67108864 } +} { + + do_multiclient_test tn { + sql1 {PRAGMA cache_size=2000} + sql2 {PRAGMA cache_size=2000} + + sql1 {PRAGMA page_size=1024} + sql1 $mmap_size + sql2 $c2init + + code2 [register_rblob_code db2 0] + + sql2 { + PRAGMA page_size=1024; + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(a, b, UNIQUE(a, b)); + INSERT INTO t1 VALUES(rblob(500), rblob(500)); + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 2 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 4 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 8 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 32 + } + do_test $t.$tn.1 { + sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count" + } {32 ok 77} + + # Have connection 2 shrink the file. Check connection 1 can still read it. + sql2 { DELETE FROM t1 WHERE rowid%2; } + do_test $t.$tn.2 { + sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count" + } {16 ok 42} + + # Have connection 2 grow the file. Check connection 1 can still read it. + sql2 { INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1 } + do_test $t.$tn.3 { + sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count" + } {32 ok 79} + + # Have connection 2 grow the file again. Check connection 1 is still ok. + sql2 { INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1 } + do_test $t.$tn.4 { + sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count" + } {64 ok 149} + + # Check that the number of pages read by connection 1 indicates that the + # "PRAGMA mmap_size" command worked. + do_test $t.$tn.5 { nRead db } $nRead + } +} + +set ::rcnt 0 +proc rblob {n} { + set ::rcnt [expr (($::rcnt << 3) + $::rcnt + 456) & 0xFFFFFFFF] + set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]] + string range [string repeat $str [expr $n/4]] 1 $n +} + +reset_db +db func rblob rblob + +do_execsql_test 2.1 { + PRAGMA auto_vacuum = 1; + PRAGMA mmap_size = 67108864; + PRAGMA journal_mode = wal; + CREATE TABLE t1(a, b, UNIQUE(a, b)); + INSERT INTO t1 VALUES(rblob(500), rblob(500)); + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 2 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 4 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 8 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 32 + PRAGMA wal_checkpoint; +} {67108864 wal 0 103 103} + +do_execsql_test 2.2 { + PRAGMA auto_vacuum; + SELECT count(*) FROM t1; +} {1 32} + +if {[permutation] != "inmemory_journal"} { + do_test 2.3 { + sqlite3 db2 test.db + db2 func rblob rblob + db2 eval { + DELETE FROM t1 WHERE (rowid%4); + PRAGMA wal_checkpoint; + } + db2 eval { + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16 + SELECT count(*) FROM t1; + } + } {16} + + do_execsql_test 2.4 { + PRAGMA wal_checkpoint; + } {0 24 24} + db2 close +} + +reset_db +execsql { PRAGMA mmap_size = 67108864; } +db func rblob rblob +do_execsql_test 3.1 { + PRAGMA auto_vacuum = 1; + + CREATE TABLE t1(a, b, UNIQUE(a, b)); + INSERT INTO t1 VALUES(rblob(500), rblob(500)); + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 2 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 4 + INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 8 + + CREATE TABLE t2(a, b, UNIQUE(a, b)); + INSERT INTO t2 SELECT * FROM t1; +} {} + +do_test 3.2 { + set nRow 0 + db eval {SELECT * FROM t2 ORDER BY a, b} { + if {$nRow==4} { db eval { DELETE FROM t1 } } + incr nRow + } + set nRow +} {8} + +#------------------------------------------------------------------------- +# Ensure that existing cursors using xFetch() pages see changes made +# to rows using the incrblob API. +# +reset_db +execsql { PRAGMA mmap_size = 67108864; } +set aaa [string repeat a 400] +set bbb [string repeat b 400] +set ccc [string repeat c 400] +set ddd [string repeat d 400] +set eee [string repeat e 400] + +do_execsql_test 4.1 { + PRAGMA page_size = 1024; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES($aaa); + INSERT INTO t1 VALUES($bbb); + INSERT INTO t1 VALUES($ccc); + INSERT INTO t1 VALUES($ddd); + SELECT * FROM t1; + BEGIN; +} [list $aaa $bbb $ccc $ddd] + +do_test 4.2 { + set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY rowid" -1 dummy] + sqlite3_step $::STMT + sqlite3_column_text $::STMT 0 +} $aaa + +do_test 4.3 { + foreach r {2 3 4} { + set fd [db incrblob t1 x $r] + puts -nonewline $fd $eee + close $fd + } + + set res [list] + while {"SQLITE_ROW" == [sqlite3_step $::STMT]} { + lappend res [sqlite3_column_text $::STMT 0] + } + set res +} [list $eee $eee $eee] + +do_test 4.4 { + sqlite3_finalize $::STMT +} SQLITE_OK + +do_execsql_test 4.5 { COMMIT } + +#------------------------------------------------------------------------- +# Ensure that existing cursors holding xFetch() references are not +# confused if those pages are moved to make way for the root page of a +# new table or index. +# +reset_db +execsql { PRAGMA mmap_size = 67108864; } +do_execsql_test 5.1 { + PRAGMA auto_vacuum = 2; + PRAGMA page_size = 1024; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES($aaa); + INSERT INTO t1 VALUES($bbb); + INSERT INTO t1 VALUES($ccc); + INSERT INTO t1 VALUES($ddd); + + PRAGMA auto_vacuum; + SELECT * FROM t1; +} [list 2 $aaa $bbb $ccc $ddd] + +do_test 5.2 { + set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY rowid" -1 dummy] + sqlite3_step $::STMT + sqlite3_column_text $::STMT 0 +} $aaa + +do_execsql_test 5.3 { + CREATE TABLE t2(x); + INSERT INTO t2 VALUES('tricked you!'); + INSERT INTO t2 VALUES('tricked you!'); +} + +do_test 5.4 { + sqlite3_step $::STMT + sqlite3_column_text $::STMT 0 +} $bbb + +do_test 5.5 { + sqlite3_finalize $::STMT +} SQLITE_OK + +#------------------------------------------------------------------------- +# Test various mmap_size settings. +# +foreach {tn1 mmap1 mmap2} { + 1 6144 167773 + 2 18432 140399 + 3 43008 401302 + 4 92160 253899 + 5 190464 2 + 6 387072 752431 + 7 780288 291143 + 8 1566720 594306 + 9 3139584 829137 + 10 6285312 793963 + 11 12576768 1015590 +} { + do_multiclient_test tn { + sql1 { + CREATE TABLE t1(a PRIMARY KEY); + CREATE TABLE t2(x); + INSERT INTO t2 VALUES(''); + } + + code1 [register_rblob_code db 0] + code2 [register_rblob_code db2 444] + + sql1 "PRAGMA mmap_size = $mmap1" + sql2 "PRAGMA mmap_size = $mmap2" + + do_test $tn1.$tn { + for {set i 1} {$i <= 100} {incr i} { + if {$i % 2} { + set c1 sql1 + set c2 sql2 + } else { + set c1 sql2 + set c2 sql1 + } + + $c1 { + INSERT INTO t1 VALUES( rblob(5000) ); + UPDATE t2 SET x = (SELECT md5sum(a) FROM t1); + } + + set res [$c2 { + SELECT count(*) FROM t1; + SELECT x == (SELECT md5sum(a) FROM t1) FROM t2; + PRAGMA integrity_check; + }] + if {$res != [list $i 1 ok]} { + do_test $tn1.$tn.$i { + set ::res + } [list $i 1 ok] + } + } + set res 1 + } {1} + } +} + + +finish_test diff --git a/test/mmap2.test b/test/mmap2.test new file mode 100644 index 0000000..1f8346b --- /dev/null +++ b/test/mmap2.test @@ -0,0 +1,87 @@ +# 2013 March 20 +# +# 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 tests the effect of the mmap() or mremap() system calls +# returning an error on the library. +# +# If either mmap() or mremap() fails, SQLite should log an error +# message, then continue accessing the database using read() and +# write() exclusively. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix mmap2 + +if {$::tcl_platform(platform)!="unix" || [test_syscall defaultvfs] != "unix"} { + finish_test + return +} +ifcapable !mmap { + finish_test + return +} + +db close +sqlite3_shutdown +test_sqlite3_log xLog +proc xLog {error_code msg} { + if {[string match os_unix.c* $msg]} { + lappend ::log $msg + } +} + +foreach syscall {mmap mremap} { + test_syscall uninstall + if {[catch {test_syscall install $syscall}]} continue + + for {set i 1} {$i < 20} {incr i} { + reset_db + execsql { PRAGMA mmap_size = 8000000 } + + test_syscall fault $i 1 + test_syscall errno $syscall ENOMEM + set ::log "" + + do_execsql_test 1.$syscall.$i.1 { + CREATE TABLE t1(a, b, UNIQUE(a, b)); + INSERT INTO t1 VALUES(randomblob(1000), randomblob(1000)); + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1; + } + + set nFail [test_syscall fault 0 0] + + do_execsql_test 1.$syscall.$i.2 { + SELECT count(*) FROM t1; + PRAGMA integrity_check; + } {64 ok} + + do_test 1.$syscall.$i.3 { + expr {$nFail==0 || $nFail==1} + } {1} + + do_test 1.$syscall.$i.4.nFail=$nFail { + regexp ".*${syscall}.*" $::log + } [expr $nFail>0] + } +} + +db close +test_syscall uninstall +sqlite3_shutdown +test_sqlite3_log +sqlite3_initialize +finish_test diff --git a/test/notify2.test b/test/notify2.test index 4016b6d..9e40ed6 100644 --- a/test/notify2.test +++ b/test/notify2.test @@ -150,9 +150,9 @@ set sql $zSql # Hit some other kind of error. This is a malfunction. error $msg } else { - # No error occured. Check that any SELECT statements in the transaction + # No error occurred. Check that any SELECT statements in the transaction # returned "1". Otherwise, the invariant was false, indicating that - # some malfunction has occured. + # some malfunction has occurred. foreach r $msg { if {$r != 1} { puts "Invariant check failed: $msg" } } } } diff --git a/test/notnull.test b/test/notnull.test index 240aaba..01738a4 100644 --- a/test/notnull.test +++ b/test/notnull.test @@ -48,6 +48,7 @@ do_test notnull-1.2 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-1.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.3 { catchsql { DELETE FROM t1; @@ -62,6 +63,7 @@ do_test notnull-1.4 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-1.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.5 { catchsql { DELETE FROM t1; @@ -69,6 +71,7 @@ do_test notnull-1.5 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-1.5b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.6 { catchsql { DELETE FROM t1; @@ -104,6 +107,7 @@ do_test notnull-1.10 { SELECT * FROM t1 order by a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-1.10b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.11 { catchsql { DELETE FROM t1; @@ -146,6 +150,7 @@ do_test notnull-1.16 { SELECT * FROM t1 order by a; } } {1 {t1.c may not be NULL}} +verify_ex_errcode notnull-1.16b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.17 { catchsql { DELETE FROM t1; @@ -153,6 +158,7 @@ do_test notnull-1.17 { SELECT * FROM t1 order by a; } } {1 {t1.d may not be NULL}} +verify_ex_errcode notnull-1.17b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.18 { catchsql { DELETE FROM t1; @@ -174,6 +180,7 @@ do_test notnull-1.20 { SELECT * FROM t1 order by a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-1.20b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.21 { catchsql { DELETE FROM t1; @@ -190,6 +197,7 @@ do_test notnull-2.1 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-2.1b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.2 { catchsql { DELETE FROM t1; @@ -198,6 +206,7 @@ do_test notnull-2.2 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-2.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.3 { catchsql { DELETE FROM t1; @@ -214,6 +223,7 @@ do_test notnull-2.4 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-2.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.5 { catchsql { DELETE FROM t1; @@ -222,6 +232,7 @@ do_test notnull-2.5 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-2.6b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.6 { catchsql { DELETE FROM t1; @@ -262,6 +273,7 @@ do_test notnull-2.10 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-2.10b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.0 { execsql { @@ -287,6 +299,7 @@ do_test notnull-3.2 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-3.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.3 { catchsql { DELETE FROM t1; @@ -301,6 +314,7 @@ do_test notnull-3.4 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-3.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.5 { catchsql { DELETE FROM t1; @@ -308,6 +322,7 @@ do_test notnull-3.5 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-3.5b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.6 { catchsql { DELETE FROM t1; @@ -343,6 +358,7 @@ do_test notnull-3.10 { SELECT * FROM t1 order by a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-3.10b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.11 { catchsql { DELETE FROM t1; @@ -385,6 +401,7 @@ do_test notnull-3.16 { SELECT * FROM t1 order by a; } } {1 {t1.c may not be NULL}} +verify_ex_errcode notnull-3.16b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.17 { catchsql { DELETE FROM t1; @@ -392,6 +409,7 @@ do_test notnull-3.17 { SELECT * FROM t1 order by a; } } {1 {t1.d may not be NULL}} +verify_ex_errcode notnull-3.17b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.18 { catchsql { DELETE FROM t1; @@ -413,6 +431,7 @@ do_test notnull-3.20 { SELECT * FROM t1 order by a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-3.20b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.21 { catchsql { DELETE FROM t1; @@ -429,6 +448,7 @@ do_test notnull-4.1 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-4.1b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.2 { catchsql { DELETE FROM t1; @@ -437,6 +457,7 @@ do_test notnull-4.2 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-4.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.3 { catchsql { DELETE FROM t1; @@ -453,6 +474,7 @@ do_test notnull-4.4 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-4.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.5 { catchsql { DELETE FROM t1; @@ -461,6 +483,7 @@ do_test notnull-4.5 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-4.5b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.6 { catchsql { DELETE FROM t1; @@ -501,6 +524,7 @@ do_test notnull-4.10 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-4.10b SQLITE_CONSTRAINT_NOTNULL # Test that bug 29ab7be99f is fixed. # @@ -519,6 +543,7 @@ do_test notnull-5.2 { INSERT INTO t1 SELECT * FROM t2; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-5.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-5.3 { execsql { SELECT * FROM t1 } } {1 2} @@ -531,9 +556,9 @@ do_test notnull-5.4 { COMMIT; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-5.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-5.5 { execsql { SELECT * FROM t1 } } {1 2} finish_test - diff --git a/test/numcast.test b/test/numcast.test new file mode 100644 index 0000000..926bbe4 --- /dev/null +++ b/test/numcast.test @@ -0,0 +1,46 @@ +# 2013 March 20 +# +# 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 implements regression tests for SQLite library. +# This particular file does testing of casting strings into numeric +# values. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +foreach enc {utf8 utf16le utf16be} { + do_test numcast-$enc.0 { + db close + sqlite3 db :memory: + db eval "PRAGMA encoding='$enc'" + set x [db eval {PRAGMA encoding}] + string map {- {}} [string tolower $x] + } $enc + foreach {idx str rval ival} { + 1 12345.0 12345.0 12345 + 2 12345.0e0 12345.0 12345 + 3 -12345.0e0 -12345.0 -12345 + 4 -12345.25 -12345.25 -12345 + 5 { -12345.0} -12345.0 -12345 + 6 { 876xyz} 876.0 876 + 7 { 456Ä·89} 456.0 456 + 8 { Ä  321.5} 0.0 0 + } { + do_test numcast-$enc.$idx.1 { + db eval {SELECT CAST($str AS real)} + } $rval + do_test numcast-$enc.$idx.2 { + db eval {SELECT CAST($str AS integer)} + } $ival + } +} + +finish_test diff --git a/test/orderby1.test b/test/orderby1.test new file mode 100644 index 0000000..f459fc8 --- /dev/null +++ b/test/orderby1.test @@ -0,0 +1,438 @@ +# 2012 Sept 27 +# +# 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 implements regression tests for SQLite library. The +# focus of this file is testing that the optimizations that disable +# ORDER BY clauses when the natural order of a query is correct. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby1 + +# Generate test data for a join. Verify that the join gets the +# correct answer. +# +do_test 1.0 { + db eval { + BEGIN; + CREATE TABLE album( + aid INTEGER PRIMARY KEY, + title TEXT UNIQUE NOT NULL + ); + CREATE TABLE track( + tid INTEGER PRIMARY KEY, + aid INTEGER NOT NULL REFERENCES album, + tn INTEGER NOT NULL, + name TEXT, + UNIQUE(aid, tn) + ); + INSERT INTO album VALUES(1, '1-one'), (2, '2-two'), (3, '3-three'); + INSERT INTO track VALUES + (NULL, 1, 1, 'one-a'), + (NULL, 2, 2, 'two-b'), + (NULL, 3, 3, 'three-c'), + (NULL, 1, 3, 'one-c'), + (NULL, 2, 1, 'two-a'), + (NULL, 3, 1, 'three-a'); + COMMIT; + } +} {} +do_test 1.1a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {one-a one-c two-a two-b three-a three-c} + +# Verify that the ORDER BY clause is optimized out +# +do_test 1.1b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {~/ORDER BY/} ;# ORDER BY optimized out + +# The same query with ORDER BY clause optimization disabled via + operators +# should give exactly the same answer. +# +do_test 1.2a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + } +} {one-a one-c two-a two-b three-a three-c} + +# The output is sorted manually in this case. +# +do_test 1.2b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + } +} {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms + +# The same query with ORDER BY optimizations turned off via built-in test. +# +do_test 1.3a { + optimization_control db order-by-idx-join 0 + db cache flush + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {one-a one-c two-a two-b three-a three-c} +do_test 1.3b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {/ORDER BY/} ;# separate sorting pass due to disabled optimization +optimization_control db all 1 +db cache flush + +# Reverse order sorts +# +do_test 1.4a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + } +} {three-a three-c two-a two-b one-a one-c} +do_test 1.4b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn + } +} {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting +do_test 1.4c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + } +} {~/ORDER BY/} ;# optimized out + + +do_test 1.5a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {one-c one-a two-b two-a three-c three-a} +do_test 1.5b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + } +} {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting +do_test 1.5c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {~/ORDER BY/} ;# optimized out + +do_test 1.6a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + } +} {three-c three-a two-b two-a one-c one-a} +do_test 1.6b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + } +} {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting +do_test 1.6c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + } +} {~/ORDER BY/} ;# ORDER BY optimized-out + + +# Reconstruct the test data to use indices rather than integer primary keys. +# +do_test 2.0 { + db eval { + BEGIN; + DROP TABLE album; + DROP TABLE track; + CREATE TABLE album( + aid INT PRIMARY KEY, + title TEXT NOT NULL + ); + CREATE INDEX album_i1 ON album(title, aid); + CREATE TABLE track( + aid INTEGER NOT NULL REFERENCES album, + tn INTEGER NOT NULL, + name TEXT, + UNIQUE(aid, tn) + ); + INSERT INTO album VALUES(1, '1-one'), (20, '2-two'), (3, '3-three'); + INSERT INTO track VALUES + (1, 1, 'one-a'), + (20, 2, 'two-b'), + (3, 3, 'three-c'), + (1, 3, 'one-c'), + (20, 1, 'two-a'), + (3, 1, 'three-a'); + COMMIT; + } +} {} +do_test 2.1a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {one-a one-c two-a two-b three-a three-c} + +# Verify that the ORDER BY clause is optimized out +# +do_test 2.1b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {~/ORDER BY/} ;# ORDER BY optimized out + +do_test 2.1c { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn + } +} {one-a one-c two-a two-b three-a three-c} +do_test 2.1d { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn + } +} {~/ORDER BY/} ;# ORDER BY optimized out + +# The same query with ORDER BY clause optimization disabled via + operators +# should give exactly the same answer. +# +do_test 2.2a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + } +} {one-a one-c two-a two-b three-a three-c} + +# The output is sorted manually in this case. +# +do_test 2.2b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + } +} {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms + +# The same query with ORDER BY optimizations turned off via built-in test. +# +do_test 2.3a { + optimization_control db order-by-idx-join 0 + db cache flush + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {one-a one-c two-a two-b three-a three-c} +do_test 2.3b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {/ORDER BY/} ;# separate sorting pass due to disabled optimization +optimization_control db all 1 +db cache flush + +# Reverse order sorts +# +do_test 2.4a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + } +} {three-a three-c two-a two-b one-a one-c} +do_test 2.4b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn + } +} {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting +do_test 2.4c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + } +} {~/ORDER BY/} ;# optimized out + + +do_test 2.5a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {one-c one-a two-b two-a three-c three-a} +do_test 2.5b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + } +} {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting +do_test 2.5c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {~/ORDER BY/} ;# optimized out + +do_test 2.6a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + } +} {three-c three-a two-b two-a one-c one-a} +do_test 2.6b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + } +} {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting +do_test 2.6c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + } +} {~/ORDER BY/} ;# ORDER BY optimized out + + +# Generate another test dataset, but this time using mixed ASC/DESC indices. +# +do_test 3.0 { + db eval { + BEGIN; + DROP TABLE album; + DROP TABLE track; + CREATE TABLE album( + aid INTEGER PRIMARY KEY, + title TEXT UNIQUE NOT NULL + ); + CREATE TABLE track( + tid INTEGER PRIMARY KEY, + aid INTEGER NOT NULL REFERENCES album, + tn INTEGER NOT NULL, + name TEXT, + UNIQUE(aid ASC, tn DESC) + ); + INSERT INTO album VALUES(1, '1-one'), (2, '2-two'), (3, '3-three'); + INSERT INTO track VALUES + (NULL, 1, 1, 'one-a'), + (NULL, 2, 2, 'two-b'), + (NULL, 3, 3, 'three-c'), + (NULL, 1, 3, 'one-c'), + (NULL, 2, 1, 'two-a'), + (NULL, 3, 1, 'three-a'); + COMMIT; + } +} {} +do_test 3.1a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {one-c one-a two-b two-a three-c three-a} + +# Verify that the ORDER BY clause is optimized out +# +do_test 3.1b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {~/ORDER BY/} ;# ORDER BY optimized out + +# The same query with ORDER BY clause optimization disabled via + operators +# should give exactly the same answer. +# +do_test 3.2a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + } +} {one-c one-a two-b two-a three-c three-a} + +# The output is sorted manually in this case. +# +do_test 3.2b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + } +} {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms + +# The same query with ORDER BY optimizations turned off via built-in test. +# +do_test 3.3a { + optimization_control db order-by-idx-join 0 + db cache flush + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {one-c one-a two-b two-a three-c three-a} +do_test 3.3b { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + } +} {/ORDER BY/} ;# separate sorting pass due to disabled optimization +optimization_control db all 1 +db cache flush + +# Without the mixed ASC/DESC on ORDER BY +# +do_test 3.4a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {one-a one-c two-a two-b three-a three-c} +do_test 3.4b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + } +} {one-a one-c two-a two-b three-a three-c} ;# verify same order after sorting +do_test 3.4c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + } +} {~/ORDER BY/} ;# optimized out + + +do_test 3.5a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + } +} {three-c three-a two-b two-a one-c one-a} +do_test 3.5b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + } +} {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting +do_test 3.5c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + } +} {~/ORDER BY/} ;# optimzed out + + +do_test 3.6a { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + } +} {three-a three-c two-a two-b one-a one-c} +do_test 3.6b { + db eval { + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn + } +} {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting +do_test 3.6c { + db eval { + EXPLAIN QUERY PLAN + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + } +} {~/ORDER BY/} ;# inverted ASC/DESC is optimized out + + +finish_test diff --git a/test/orderby2.test b/test/orderby2.test new file mode 100644 index 0000000..cfd6477 --- /dev/null +++ b/test/orderby2.test @@ -0,0 +1,117 @@ +# 2012 Sept 27 +# +# 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 implements regression tests for SQLite library. The +# focus of this file is testing that the optimizations that disable +# ORDER BY clauses when the natural order of a query is correct. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby2 + +# Generate test data for a join. Verify that the join gets the +# correct answer. +# +do_test 1.0 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1,11), (2,22); + CREATE TABLE t2(d, e, UNIQUE(d,e)); + INSERT INTO t2 VALUES(10, 'ten'), (11,'eleven'), (12,'twelve'), + (11, 'oneteen'); + } +} {} + +do_test 1.1a { + db eval { + SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY d, e; + } +} {eleven oneteen} +do_test 1.1b { + db eval { + EXPLAIN QUERY PLAN + SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY d, e; + } +} {~/ORDER BY/} + +do_test 1.2a { + db eval { + SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY e; + } +} {eleven oneteen} +do_test 1.2b { + db eval { + EXPLAIN QUERY PLAN + SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY e; + } +} {~/ORDER BY/} + +do_test 1.3a { + db eval { + SELECT e, b FROM t1, t2 WHERE a=1 ORDER BY d, e; + } +} {ten 11 eleven 11 oneteen 11 twelve 11} +do_test 1.3b { + db eval { + EXPLAIN QUERY PLAN + SELECT e, b FROM t1, t2 WHERE a=1 ORDER BY d, e; + } +} {~/ORDER BY/} + +# The following tests derived from TH3 test module cov1/where34.test +# +do_test 2.0 { + db eval { + CREATE TABLE t31(a,b); CREATE INDEX t31ab ON t31(a,b); + CREATE TABLE t32(c,d); CREATE INDEX t32cd ON t32(c,d); + CREATE TABLE t33(e,f); CREATE INDEX t33ef ON t33(e,f); + CREATE TABLE t34(g,h); CREATE INDEX t34gh ON t34(g,h); + + INSERT INTO t31 VALUES(1,4), (2,3), (1,3); + INSERT INTO t32 VALUES(4,5), (3,6), (3,7), (4,8); + INSERT INTO t33 VALUES(5,9), (7,10), (6,11), (8,12), (8,13), (7,14); + INSERT INTO t34 VALUES(11,20), (10,21), (12,22), (9,23), (13,24), + (14,25), (12,26); + SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34 + WHERE c=b AND e=d AND g=f + ORDER BY a ASC, c ASC, e DESC, g ASC; + } +} {1,3,7,10 1,3,7,14 1,3,6,11 1,4,8,12 1,4,8,12 1,4,8,13 1,4,5,9 2,3,7,10 2,3,7,14 2,3,6,11} +do_test 2.1 { + db eval { + SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34 + WHERE c=b AND e=d AND g=f + ORDER BY +a ASC, +c ASC, +e DESC, +g ASC; + } +} {1,3,7,10 1,3,7,14 1,3,6,11 1,4,8,12 1,4,8,12 1,4,8,13 1,4,5,9 2,3,7,10 2,3,7,14 2,3,6,11} +do_test 2.2 { + db eval { + SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34 + WHERE c=b AND e=d AND g=f + ORDER BY a ASC, c ASC, e ASC, g ASC; + } +} {1,3,6,11 1,3,7,10 1,3,7,14 1,4,5,9 1,4,8,12 1,4,8,12 1,4,8,13 2,3,6,11 2,3,7,10 2,3,7,14} +do_test 2.3 { + optimization_control db cover-idx-scan off + db cache flush + db eval { + SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34 + WHERE c=b AND e=d AND g=f + ORDER BY a ASC, c ASC, e ASC, g ASC; + } +} {1,3,6,11 1,3,7,10 1,3,7,14 1,4,5,9 1,4,8,12 1,4,8,12 1,4,8,13 2,3,6,11 2,3,7,10 2,3,7,14} +optimization_control db all on +db cache flush + + + +finish_test diff --git a/test/orderby3.test b/test/orderby3.test new file mode 100644 index 0000000..f005f0d --- /dev/null +++ b/test/orderby3.test @@ -0,0 +1,123 @@ +# 2013 January 09 +# +# 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 implements regression tests for SQLite library. The +# focus of this file is testing that the optimizations that disable +# ORDER BY clauses work correctly on a 3-way join. See ticket +# http://www.sqlite.org/src/956e4d7f89 +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby3 + +# Generate test data for a join. Verify that the join gets the +# correct answer. +# +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY); + CREATE TABLE t2(b INTEGER PRIMARY KEY, c INTEGER); + CREATE TABLE t3(d INTEGER); + + INSERT INTO t1 VALUES(1),(2),(3); + + INSERT INTO t2 VALUES(3, 1); + INSERT INTO t2 VALUES(4, 2); + INSERT INTO t2 VALUES(5, 3); + + INSERT INTO t3 VALUES(4),(3),(5); +} {} +do_execsql_test 1.1.asc { + SELECT t1.a + FROM t1, t2, t3 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.1.desc { + SELECT t1.a + FROM t1, t2, t3 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} +do_execsql_test 1.123.asc { + SELECT t1.a + FROM t1 CROSS JOIN t2 CROSS JOIN t3 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.123.desc { + SELECT t1.a + FROM t1 CROSS JOIN t2 CROSS JOIN t3 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} +do_execsql_test 1.132.asc { + SELECT t1.a + FROM t1 CROSS JOIN t3 CROSS JOIN t2 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.132.desc { + SELECT t1.a + FROM t1 CROSS JOIN t3 CROSS JOIN t2 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} +do_execsql_test 1.213.asc { + SELECT t1.a + FROM t2 CROSS JOIN t1 CROSS JOIN t3 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.213.desc { + SELECT t1.a + FROM t2 CROSS JOIN t1 CROSS JOIN t3 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} +do_execsql_test 1.231.asc { + SELECT t1.a + FROM t2 CROSS JOIN t3 CROSS JOIN t1 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.231.desc { + SELECT t1.a + FROM t2 CROSS JOIN t3 CROSS JOIN t1 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} +do_execsql_test 1.312.asc { + SELECT t1.a + FROM t3 CROSS JOIN t1 CROSS JOIN t2 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.312.desc { + SELECT t1.a + FROM t3 CROSS JOIN t1 CROSS JOIN t2 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} +do_execsql_test 1.321.asc { + SELECT t1.a + FROM t3 CROSS JOIN t2 CROSS JOIN t1 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a; +} {1 2 3} +do_execsql_test 1.321.desc { + SELECT t1.a + FROM t3 CROSS JOIN t2 CROSS JOIN t1 + WHERE t1.a=t2.c AND t2.b=t3.d + ORDER BY t1.a DESC; +} {3 2 1} + +finish_test diff --git a/test/orderby4.test b/test/orderby4.test new file mode 100644 index 0000000..ec6eb04 --- /dev/null +++ b/test/orderby4.test @@ -0,0 +1,56 @@ +# 2013 March 26 +# +# 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 implements regression tests for SQLite library. The +# focus of this file is testing that the optimizations that disable +# ORDER BY clauses work correctly on multi-value primary keys and +# unique indices when only some prefix of the terms in the key are +# used. See ticket http://www.sqlite.org/src/info/a179fe74659 +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby4 + +# Generate test data for a join. Verify that the join gets the +# correct answer. +# +do_execsql_test 1.1 { + CREATE TABLE t1(a, b, PRIMARY KEY(a,b)); + INSERT INTO t1 VALUES(1,1),(1,2); + CREATE TABLE t2(x, y, PRIMARY KEY(x,y)); + INSERT INTO t2 VALUES(3,3),(4,4); + SELECT a, x FROM t1, t2 ORDER BY 1, 2; +} {1 3 1 3 1 4 1 4} +do_execsql_test 1.2 { + SELECT a, x FROM t1 CROSS JOIN t2 ORDER BY 1, 2; +} {1 3 1 3 1 4 1 4} +do_execsql_test 1.3 { + SELECT a, x FROM t2 CROSS JOIN t1 ORDER BY 1, 2; +} {1 3 1 3 1 4 1 4} + +do_execsql_test 2.1 { + CREATE TABLE t3(a); + INSERT INTO t3 VALUES(1),(1); + CREATE INDEX t3a ON t3(a); + CREATE TABLE t4(x); + INSERT INTO t4 VALUES(3),(4); + CREATE INDEX t4x ON t4(x); + SELECT a, x FROM t3, t4 ORDER BY 1, 2; +} {1 3 1 3 1 4 1 4} +do_execsql_test 2.2 { + SELECT a, x FROM t3 CROSS JOIN t4 ORDER BY 1, 2; +} {1 3 1 3 1 4 1 4} +do_execsql_test 2.3 { + SELECT a, x FROM t4 CROSS JOIN t3 ORDER BY 1, 2; +} {1 3 1 3 1 4 1 4} + +finish_test diff --git a/test/pager1.test b/test/pager1.test index 61a0c0c..72e4780 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -15,6 +15,7 @@ source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl +set testprefix pager1 # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). @@ -752,7 +753,7 @@ sqlite3 db test.db -readonly 1 do_catchsql_test pager1.4.5.6 { SELECT * FROM t1; SELECT * FROM t2; -} {1 {disk I/O error}} +} {1 {attempt to write a readonly database}} db close # Snapshot the file-system just before multi-file commit. Save the name @@ -883,12 +884,20 @@ do_execsql_test pager1.4.7.1 { tv filter {} db close tv delete +catch { + test_syscall install fchmod + test_syscall fault 1 1 +} do_test pager1.4.7.2 { faultsim_restore_and_reopen catch {file attributes test.db-journal -permissions r--------} catch {file attributes test.db-journal -readonly 1} catchsql { SELECT * FROM t1 } } {1 {unable to open database file}} +catch { + test_syscall reset + test_syscall fault 0 0 +} do_test pager1.4.7.3 { db close catch {file attributes test.db-journal -permissions rw-rw-rw-} @@ -1365,7 +1374,7 @@ do_test pager1-9.4.1 { } {SQLITE_DONE SQLITE_OK} do_test pager1-9.4.2 { list [file size test.db2] [file size test.db] -} {0 0} +} {1024 0} db2 close #------------------------------------------------------------------------- @@ -1374,6 +1383,7 @@ db2 close # testvfs tv -default 1 foreach sectorsize { + 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 } { @@ -1396,7 +1406,7 @@ foreach sectorsize { COMMIT; } file size test.db-journal - } [expr $sectorsize > 65536 ? 65536 : $sectorsize] + } [expr $sectorsize > 65536 ? 65536 : ($sectorsize<32 ? 512 : $sectorsize)] do_test pager1-10.$sectorsize.2 { execsql { @@ -2235,7 +2245,6 @@ do_test pager1-25-1 { } db close } {} -breakpoint do_test pager1-25-2 { faultsim_delete_and_reopen execsql { @@ -2487,4 +2496,323 @@ do_test pager1-32.1 { # Cleanup 20MB file left by the previous test. forcedelete test.db +#------------------------------------------------------------------------- +# Test that if a transaction is committed in journal_mode=DELETE mode, +# and the call to unlink() returns an ENOENT error, the COMMIT does not +# succeed. +# +if {$::tcl_platform(platform)=="unix"} { + do_test pager1-33.1 { + sqlite3 db test.db + execsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('one'); + INSERT INTO t1 VALUES('two'); + BEGIN; + INSERT INTO t1 VALUES('three'); + INSERT INTO t1 VALUES('four'); + } + forcedelete bak-journal + file rename test.db-journal bak-journal + + catchsql COMMIT + } {1 {disk I/O error}} + + do_test pager1-33.2 { + file rename bak-journal test.db-journal + execsql { SELECT * FROM t1 } + } {one two} +} + +#------------------------------------------------------------------------- +# Test that appending pages to the database file then moving those pages +# to the free-list before the transaction is committed does not cause +# an error. +# +foreach {tn pragma strsize} { + 1 { PRAGMA mmap_size = 0 } 2400 + 2 { } 2400 + 3 { PRAGMA mmap_size = 0 } 4400 + 4 { } 4400 +} { + reset_db + db func a_string a_string + db eval $pragma + do_execsql_test 34.$tn.1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + do_execsql_test 34.$tn.2 { + BEGIN; + INSERT INTO t1 VALUES(2, a_string($strsize)); + DELETE FROM t1 WHERE oid=2; + COMMIT; + PRAGMA integrity_check; + } {ok} +} + +#------------------------------------------------------------------------- +# +reset_db +do_test 35 { + sqlite3 db test.db + + execsql { + CREATE TABLE t1(x, y); + PRAGMA journal_mode = WAL; + INSERT INTO t1 VALUES(1, 2); + } + + execsql { + BEGIN; + CREATE TABLE t2(a, b); + } + + hexio_write test.db-shm [expr 16*1024] [string repeat 0055 8192] + catchsql ROLLBACK +} {0 {}} + +do_multiclient_test tn { + sql1 { + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + } + + do_test 36.$tn.1 { + sql2 { PRAGMA max_page_count = 2 } + list [catch { sql2 { CREATE TABLE t2(x) } } msg] $msg + } {1 {database or disk is full}} + + sql1 { PRAGMA checkpoint_fullfsync = 1 } + sql1 { CREATE TABLE t2(x) } + + do_test 36.$tn.2 { + sql2 { INSERT INTO t2 VALUES('xyz') } + list [catch { sql2 { CREATE TABLE t3(x) } } msg] $msg + } {1 {database or disk is full}} +} + +forcedelete test1 test2 +foreach {tn uri} { + 1 {file:?mode=memory&cache=shared} + 2 {file:one?mode=memory&cache=shared} + 3 {file:test1?cache=shared} + 4 {file:test2?another=parameter&yet=anotherone} +} { + do_test 37.$tn { + catch { db close } + sqlite3_shutdown + sqlite3_config_uri 1 + sqlite3 db $uri + + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + SELECT * FROM t1; + } + } {1} + + do_execsql_test 37.$tn.2 { + VACUUM; + SELECT * FROM t1; + } {1} + + db close + sqlite3_shutdown + sqlite3_config_uri 0 +} + +do_test 38.1 { + catch { db close } + forcedelete test.db + set fd [open test.db w] + puts $fd "hello world" + close $fd + sqlite3 db test.db + catchsql { CREATE TABLE t1(x) } +} {1 {file is encrypted or is not a database}} +do_test 38.2 { + catch { db close } + forcedelete test.db +} {} + +do_test 39.1 { + sqlite3 db test.db + execsql { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('xxx'); + INSERT INTO t1 VALUES('two'); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + BEGIN; + UPDATE t1 SET x = 'one' WHERE rowid=1; + } + set ::stmt [sqlite3_prepare db "SELECT * FROM t1 ORDER BY rowid" -1 dummy] + sqlite3_step $::stmt + sqlite3_column_text $::stmt 0 +} {one} +do_test 39.2 { + execsql { CREATE TABLE t2(x) } + sqlite3_step $::stmt + sqlite3_column_text $::stmt 0 +} {two} +do_test 39.3 { + sqlite3_finalize $::stmt + execsql COMMIT +} {} + +do_execsql_test 39.4 { + PRAGMA auto_vacuum = 2; + CREATE TABLE t3(x); + CREATE TABLE t4(x); + + DROP TABLE t2; + DROP TABLE t3; + DROP TABLE t4; +} +do_test 39.5 { + db close + sqlite3 db test.db + execsql { + PRAGMA cache_size = 1; + PRAGMA incremental_vacuum; + PRAGMA integrity_check; + } +} {ok} + +do_test 40.1 { + reset_db + execsql { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES(randomblob(1200)); + PRAGMA page_count; + } +} {6} +do_test 40.2 { + execsql { + INSERT INTO t1 VALUES(randomblob(1200)); + INSERT INTO t1 VALUES(randomblob(1200)); + INSERT INTO t1 VALUES(randomblob(1200)); + } +} {} +do_test 40.3 { + db close + sqlite3 db test.db + execsql { + PRAGMA cache_size = 1; + CREATE TABLE t2(x); + PRAGMA integrity_check; + } +} {ok} + +do_test 41.1 { + reset_db + execsql { + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES(randomblob(200)); + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + } +} {} +do_test 41.2 { + testvfs tv -default 1 + tv sectorsize 16384; + tv devchar [list] + db close + sqlite3 db test.db + execsql { + PRAGMA cache_size = 1; + DELETE FROM t1 WHERE rowid%4; + PRAGMA integrity_check; + } +} {ok} +db close +tv delete + +set pending_prev [sqlite3_test_control_pending_byte 0x1000000] +do_test 42.1 { + reset_db + execsql { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + db close + sqlite3_test_control_pending_byte 0x0010000 + sqlite3 db test.db + db eval { PRAGMA mmap_size = 0 } + catchsql { SELECT sum(length(y)) FROM t1 } +} {1 {database disk image is malformed}} +do_test 42.2 { + reset_db + execsql { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + db close + + testvfs tv -default 1 + tv sectorsize 16384; + tv devchar [list] + sqlite3 db test.db -vfs tv + execsql { UPDATE t1 SET x = randomblob(200) } +} {} +db close +tv delete +sqlite3_test_control_pending_byte $pending_prev + +do_test 43.1 { + reset_db + execsql { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 2); + CREATE TABLE t3(x, y); + INSERT INTO t3 VALUES(1, 2); + } + db close + sqlite3 db test.db + + db eval { PRAGMA mmap_size = 0 } + db eval { SELECT * FROM t1 } + sqlite3_db_status db CACHE_MISS 0 +} {0 2 0} + +do_test 43.2 { + db eval { SELECT * FROM t2 } + sqlite3_db_status db CACHE_MISS 1 +} {0 3 0} + +do_test 43.3 { + db eval { SELECT * FROM t3 } + sqlite3_db_status db CACHE_MISS 0 +} {0 1 0} + finish_test + diff --git a/test/pager2.test b/test/pager2.test index fa5f7b9..0e2b33b 100644 --- a/test/pager2.test +++ b/test/pager2.test @@ -118,7 +118,6 @@ tv delete #------------------------------------------------------------------------- -# # pager2-2.1: Test a ROLLBACK with journal_mode=off. # pager2-2.2: Test shrinking the database (auto-vacuum) with # journal_mode=off @@ -148,4 +147,22 @@ do_test pager2-2.2 { file size test.db } {3072} +#------------------------------------------------------------------------- +# Test that shared in-memory databases seem to work. +# +db close +do_test pager2-3.1 { + forcedelete test.db + sqlite3_shutdown + sqlite3_config_uri 1 + + sqlite3 db1 {file:test.db?mode=memory&cache=shared} + sqlite3 db2 {file:test.db?mode=memory&cache=shared} + sqlite3 db3 test.db + + db1 eval { CREATE TABLE t1(a, b) } + db2 eval { INSERT INTO t1 VALUES(1, 2) } + list [catch { db3 eval { INSERT INTO t1 VALUES(3, 4) } } msg] $msg +} {1 {no such table: t1}} + finish_test diff --git a/test/pagerfault.test b/test/pagerfault.test index e04e97e..23754fa 100644 --- a/test/pagerfault.test +++ b/test/pagerfault.test @@ -1246,5 +1246,304 @@ do_faultsim_test pagerfault-27 -faults ioerr-persistent -prep { faultsim_integrity_check } + +#------------------------------------------------------------------------- +# +do_test pagerfault-28-pre { + faultsim_delete_and_reopen + db func a_string a_string + execsql { + PRAGMA page_size = 512; + + PRAGMA journal_mode = wal; + PRAGMA wal_autocheckpoint = 0; + PRAGMA cache_size = 100000; + + BEGIN; + CREATE TABLE t2(a UNIQUE, b UNIQUE); + INSERT INTO t2 VALUES( a_string(800), a_string(800) ); + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + COMMIT; + CREATE TABLE t1(a PRIMARY KEY, b); + } + expr {[file size test.db-shm] >= 96*1024} +} {1} +faultsim_save_and_close + +do_faultsim_test pagerfault-28a -faults oom* -prep { + faultsim_restore_and_reopen + execsql { PRAGMA mmap_size=0 } + + sqlite3 db2 test.db + db2 eval { SELECT count(*) FROM t2 } + + db func a_string a_string + execsql { + BEGIN; + INSERT INTO t1 VALUES(a_string(2000), a_string(2000)); + INSERT INTO t1 VALUES(a_string(2000), a_string(2000)); + } + set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY a" -1 DUMMY] + sqlite3_step $::STMT +} -body { + execsql { ROLLBACK } +} -test { + db2 close + sqlite3_finalize $::STMT + catchsql { ROLLBACK } + faultsim_integrity_check +} + +faultsim_restore_and_reopen +sqlite3 db2 test.db +db2 eval {SELECT count(*) FROM t2} +db close + +do_faultsim_test pagerfault-28b -faults oom* -prep { + sqlite3 db test.db +} -body { + execsql { SELECT count(*) FROM t2 } +} -test { + faultsim_test_result {0 2048} + db close +} + +db2 close + +#------------------------------------------------------------------------- +# Try this: +# +# 1) Put the pager in ERROR state (error during rollback) +# +# 2) Next time the connection is used inject errors into all xWrite() and +# xUnlock() calls. This causes the hot-journal rollback to fail and +# the pager to declare its locking state UNKNOWN. +# +# 3) Same again. +# +# 4a) Stop injecting errors. Allow the rollback to succeed. Check that +# the database is Ok. Or, +# +# 4b) Close and reopen the db. Check that the db is Ok. +# +proc custom_injectinstall {} { + testvfs custom -default true + custom filter {xWrite xUnlock} +} +proc custom_injectuninstall {} { + catch {db close} + catch {db2 close} + custom delete +} +proc custom_injectstart {iFail} { + custom ioerr $iFail 1 +} +proc custom_injectstop {} { + custom ioerr +} +set ::FAULTSIM(custom) [list \ + -injectinstall custom_injectinstall \ + -injectstart custom_injectstart \ + -injectstop custom_injectstop \ + -injecterrlist {{1 {disk I/O error}}} \ + -injectuninstall custom_injectuninstall \ +] + +do_test pagerfault-29-pre { + faultsim_delete_and_reopen + db func a_string a_string + execsql { + PRAGMA page_size = 1024; + PRAGMA cache_size = 5; + + BEGIN; + CREATE TABLE t2(a UNIQUE, b UNIQUE); + INSERT INTO t2 VALUES( a_string(800), a_string(800) ); + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + COMMIT; + } + expr {[file size test.db] >= 50*1024} +} {1} +faultsim_save_and_close +foreach {tn tt} { + 29 { catchsql ROLLBACK } + 30 { db close ; sqlite3 db test.db } +} { + do_faultsim_test pagerfault-$tn -faults custom -prep { + faultsim_restore_and_reopen + db func a_string a_string + execsql { + PRAGMA cache_size = 5; + BEGIN; + UPDATE t2 SET a = a_string(799); + } + } -body { + catchsql ROLLBACK + catchsql ROLLBACK + catchsql ROLLBACK + } -test { + eval $::tt + if {"ok" != [db one {PRAGMA integrity_check}]} { + error "integrity check failed" + } + } +} + +do_test pagerfault-31-pre { + sqlite3_shutdown + sqlite3_config_uri 1 +} {SQLITE_OK} +do_faultsim_test pagerfault-31 -faults oom* -body { + sqlite3 db {file:one?mode=memory&cache=shared} + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + SELECT * FROM t1; + } +} -test { + faultsim_test_result {0 1} {1 {}} + catch { db close } +} +sqlite3_shutdown +sqlite3_config_uri 0 + +do_test pagerfault-32-pre { + reset_db + execsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('one'); + } +} {} +faultsim_save_and_close + +do_faultsim_test pagerfault-32 -prep { + faultsim_restore_and_reopen + db eval { SELECT * FROM t1; } +} -body { + execsql { SELECT * FROM t1; } +} -test { + faultsim_test_result {0 one} +} +sqlite3_shutdown +sqlite3_config_uri 0 + +do_faultsim_test pagerfault-33a -prep { + sqlite3 db :memory: + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } +} -body { + execsql { VACUUM } +} -test { + faultsim_test_result {0 {}} +} +do_faultsim_test pagerfault-33b -prep { + sqlite3 db "" + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } +} -body { + execsql { VACUUM } +} -test { + faultsim_test_result {0 {}} +} + +do_test pagerfault-34-pre { + reset_db + execsql { + CREATE TABLE t1(x PRIMARY KEY); + } +} {} +faultsim_save_and_close +do_faultsim_test pagerfault-34 -prep { + faultsim_restore_and_reopen + execsql { + BEGIN; + INSERT INTO t1 VALUES( randomblob(4000) ); + DELETE FROM t1; + } +} -body { + execsql COMMIT +} -test { + faultsim_test_result {0 {}} +} + +do_test pagerfault-35-pre { + faultsim_delete_and_reopen + execsql { + CREATE TABLE t1(x PRIMARY KEY, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + faultsim_save_and_close +} {} +testvfs tv -default 1 +tv sectorsize 8192; +tv devchar [list] +do_faultsim_test pagerfault-35 -prep { + faultsim_restore_and_reopen +} -body { + execsql { UPDATE t1 SET x=randomblob(200) } +} -test { + faultsim_test_result {0 {}} +} +catch {db close} +tv delete + +sqlite3_shutdown +sqlite3_config_uri 1 +do_test pagerfault-36-pre { + faultsim_delete_and_reopen + execsql { + CREATE TABLE t1(x PRIMARY KEY, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + faultsim_save_and_close +} {} +do_faultsim_test pagerfault-36 -prep { + faultsim_restore + sqlite3 db file:test.db?cache=shared + sqlite3 db2 file:test.db?cache=shared + db2 eval { + BEGIN; + SELECT count(*) FROM sqlite_master; + } + db eval { + PRAGMA cache_size = 1; + BEGIN; + UPDATE t1 SET x = randomblob(200); + } +} -body { + execsql ROLLBACK db +} -test { + catch { db eval {UPDATE t1 SET x = randomblob(200)} } + faultsim_test_result {0 {}} + catch { db close } + catch { db2 close } +} + +sqlite3_shutdown +sqlite3_config_uri 0 + finish_test diff --git a/test/pageropt.test b/test/pageropt.test index 8231196..7191661 100644 --- a/test/pageropt.test +++ b/test/pageropt.test @@ -87,12 +87,17 @@ do_test pageropt-1.4 { # But if the other thread modifies the database, then the cache # must refill. # +ifcapable mmap { + set x [expr {[permutation]=="mmap" ? 1 : 6}] +} else { + set x 6 +} do_test pageropt-1.5 { db2 eval {CREATE TABLE t2(y)} pagercount_sql { SELECT hex(x) FROM t1 } -} [list 6 0 0 $blobcontent] +} [list $x 0 0 $blobcontent] do_test pageropt-1.6 { pagercount_sql { SELECT hex(x) FROM t1 diff --git a/test/permutations.test b/test/permutations.test index 2ff77f9..bc3ceb8 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -96,7 +96,7 @@ if {$::tcl_platform(platform)!="unix"} { set alltests [test_set $alltests -exclude { all.test async.test quick.test veryquick.test memleak.test permutations.test soak.test fts3.test - mallocAll.test rtree.test + mallocAll.test rtree.test full.test }] set allquicktests [test_set $alltests -exclude { @@ -138,6 +138,14 @@ test_suite "veryquick" -prefix "" -description { test_set $allquicktests -exclude *malloc* *ioerr* *fault* ] +test_suite "mmap" -prefix "mm-" -description { + Similar to veryquick. Except with memory mapping disabled. +} -presql { + pragma mmap_size = 268435456; +} -files [ + test_set $allquicktests -exclude *malloc* *ioerr* *fault* -include malloc.test +] + test_suite "valgrind" -prefix "" -description { Run the "veryquick" test suite with a couple of multi-process tests (that fail under valgrind) omitted. @@ -178,6 +186,7 @@ test_suite "fts3" -prefix "" -description { fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3atoken.test fts3b.test fts3c.test fts3cov.test fts3d.test fts3defer.test fts3defer2.test fts3e.test fts3expr.test fts3expr2.test + fts3expr3.test fts3near.test fts3query.test fts3shared.test fts3snippet.test fts3sort.test fts3fault.test fts3malloc.test fts3matchinfo.test @@ -234,10 +243,14 @@ lappend ::testsuitelist xxx # Run some tests using pre-allocated page and scratch blocks. # +# mmap1.test is excluded because a good number of its tests depend on +# the page-cache being larger than the database. But this permutation +# causes the effective limit on the page-cache to be just 24 pages. +# test_suite "memsubsys1" -description { Tests using pre-allocated page and scratch blocks } -files [ - test_set $::allquicktests -exclude ioerr5.test malloc5.test + test_set $::allquicktests -exclude ioerr5.test malloc5.test mmap1.test ] -initialize { catch {db close} sqlite3_shutdown diff --git a/test/pragma.test b/test/pragma.test index 3c8d23a..a6d198e 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -534,12 +534,20 @@ do_test pragma-6.2.2 { b DEFAULT (5+3), c TEXT, d INTEGER DEFAULT NULL, - e TEXT DEFAULT '' + e TEXT DEFAULT '', + UNIQUE(b,c,d), + PRIMARY KEY(e,b,c) ); PRAGMA table_info(t5); } -} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0 2 c TEXT 0 <> 0 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 0} +} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 2 2 c TEXT 0 <> 3 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 1} db nullvalue {} +do_test pragma-6.2.3 { + execsql { + CREATE TABLE t2_3(a,b INTEGER PRIMARY KEY,c); + pragma table_info(t2_3) + } +} {0 a {} 0 {} 0 1 b INTEGER 0 {} 1 2 c {} 0 {} 0} ifcapable {foreignkey} { do_test pragma-6.3.1 { execsql { @@ -928,6 +936,16 @@ proc check_temp_store {} { return "unknown" } +# Application_ID +# +do_test pragma-8.3.1 { + execsql { + PRAGMA application_id; + } +} {0} +do_test pragma-8.3.2 { + execsql {PRAGMA Application_ID(12345); PRAGMA application_id;} +} {12345} # Test temp_store and temp_store_directory pragmas # @@ -1618,6 +1636,48 @@ do_test 22.4.3 { execsql { PRAGMA aux.integrity_check; } } {ok} -finish_test - +db close +forcedelete test.db test.db-wal test.db-journal +sqlite3 db test.db +sqlite3 db2 test.db +do_test 23.1 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d); + CREATE INDEX i1 ON t1(b,c); + CREATE INDEX i2 ON t1(c,d); + CREATE TABLE t2(x INTEGER REFERENCES t1); + } + db2 eval {SELECT name FROM sqlite_master} +} {t1 i1 i2 t2} +do_test 23.2 { + db eval { + DROP INDEX i2; + CREATE INDEX i2 ON t1(c,d,b); + } + db2 eval {PRAGMA index_info(i2)} +} {0 2 c 1 3 d 2 1 b} +do_test 23.3 { + db eval { + CREATE INDEX i3 ON t1(d,b,c); + } + db2 eval {PRAGMA index_list(t1)} +} {0 i3 0 1 i2 0 2 i1 0} +do_test 23.4 { + db eval { + ALTER TABLE t1 ADD COLUMN e; + } + db2 eval { + PRAGMA table_info(t1); + } +} {/4 e {} 0 {} 0/} +do_test 23.5 { + db eval { + DROP TABLE t2; + CREATE TABLE t2(x, y INTEGER REFERENCES t1); + } + db2 eval { + PRAGMA foreign_key_list(t2); + } +} {0 0 t1 y {} {NO ACTION} {NO ACTION} NONE} +finish_test diff --git a/test/regexp1.test b/test/regexp1.test new file mode 100644 index 0000000..0e63cd9 --- /dev/null +++ b/test/regexp1.test @@ -0,0 +1,211 @@ +# 2012 December 31 +# +# 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 implements test for the REGEXP operator in test_regexp.c. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test regexp1-1.1 { + load_static_extension db regexp + db eval { + CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT); + INSERT INTO t1 VALUES(1, 'For since by man came death,'); + INSERT INTO t1 VALUES(2, 'by man came also the resurrection of the dead.'); + INSERT INTO t1 VALUES(3, 'For as in Adam all die,'); + INSERT INTO t1 VALUES(4, 'even so in Christ shall all be made alive.'); + + SELECT x FROM t1 WHERE y REGEXP '^For ' ORDER BY x; + } +} {1 3} + +do_execsql_test regexp1-1.2 { + SELECT x FROM t1 WHERE y REGEXP 'by|in' ORDER BY x; +} {1 2 3 4} +do_execsql_test regexp1-1.3 { + SELECT x FROM t1 WHERE y REGEXP 'by|Christ' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.4 { + SELECT x FROM t1 WHERE y REGEXP 'shal+ al+' ORDER BY x; +} {4} +do_execsql_test regexp1-1.5 { + SELECT x FROM t1 WHERE y REGEXP 'shall x*y*z*all' ORDER BY x; +} {4} +do_execsql_test regexp1-1.6 { + SELECT x FROM t1 WHERE y REGEXP 'shallx?y? ?z?all' ORDER BY x; +} {4} +do_execsql_test regexp1-1.7 { + SELECT x FROM t1 WHERE y REGEXP 'r{2}' ORDER BY x; +} {2} +do_execsql_test regexp1-1.8 { + SELECT x FROM t1 WHERE y REGEXP 'r{3}' ORDER BY x; +} {} +do_execsql_test regexp1-1.9 { + SELECT x FROM t1 WHERE y REGEXP 'r{1}' ORDER BY x; +} {1 2 3 4} +do_execsql_test regexp1-1.10 { + SELECT x FROM t1 WHERE y REGEXP 'ur{2,10}e' ORDER BY x; +} {2} +do_execsql_test regexp1-1.11 { + SELECT x FROM t1 WHERE y REGEXP '[Aa]dam' ORDER BY x; +} {3} +do_execsql_test regexp1-1.12 { + SELECT x FROM t1 WHERE y REGEXP '[^Aa]dam' ORDER BY x; +} {} +do_execsql_test regexp1-1.13 { + SELECT x FROM t1 WHERE y REGEXP '[^b-zB-Z]dam' ORDER BY x; +} {3} +do_execsql_test regexp1-1.14 { + SELECT x FROM t1 WHERE y REGEXP 'alive' ORDER BY x; +} {4} +do_execsql_test regexp1-1.15 { + SELECT x FROM t1 WHERE y REGEXP '^alive' ORDER BY x; +} {} +do_execsql_test regexp1-1.16 { + SELECT x FROM t1 WHERE y REGEXP 'alive$' ORDER BY x; +} {} +do_execsql_test regexp1-1.17 { + SELECT x FROM t1 WHERE y REGEXP 'alive.$' ORDER BY x; +} {4} +do_execsql_test regexp1-1.18 { + SELECT x FROM t1 WHERE y REGEXP 'alive\.$' ORDER BY x; +} {4} +do_execsql_test regexp1-1.19 { + SELECT x FROM t1 WHERE y REGEXP 'ma[nd]' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.20 { + SELECT x FROM t1 WHERE y REGEXP '\bma[nd]' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.21 { + SELECT x FROM t1 WHERE y REGEXP 'ma[nd]\b' ORDER BY x; +} {1 2} +do_execsql_test regexp1-1.22 { + SELECT x FROM t1 WHERE y REGEXP 'ma\w' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.23 { + SELECT x FROM t1 WHERE y REGEXP 'ma\W' ORDER BY x; +} {} +do_execsql_test regexp1-1.24 { + SELECT x FROM t1 WHERE y REGEXP '\sma\w' ORDER BY x; +} {1 2 4} +do_execsql_test regexp1-1.25 { + SELECT x FROM t1 WHERE y REGEXP '\Sma\w' ORDER BY x; +} {} +do_execsql_test regexp1-1.26 { + SELECT x FROM t1 WHERE y REGEXP 'alive\S$' ORDER BY x; +} {4} +do_execsql_test regexp1-1.27 { + SELECT x FROM t1 WHERE y REGEXP + '\b(unto|us|son|given|his|name|called|' || + 'wonderful|councelor|mighty|god|everlasting|father|' || + 'prince|peace|alive)\b'; +} {4} + +do_execsql_test regexp1-2.1 { + SELECT 'aaaabbbbcccc' REGEXP 'ab*c', + 'aaaacccc' REGEXP 'ab*c'; +} {1 1} +do_execsql_test regexp1-2.2 { + SELECT 'aaaabbbbcccc' REGEXP 'ab+c', + 'aaaacccc' REGEXP 'ab+c'; +} {1 0} +do_execsql_test regexp1-2.3 { + SELECT 'aaaabbbbcccc' REGEXP 'ab?c', + 'aaaacccc' REGEXP 'ab?c'; +} {0 1} +do_execsql_test regexp1-2.4 { + SELECT 'aaaabbbbbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbbbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbbcccc' REGEXP 'ab{3,5}c', + 'aaaabbcccc' REGEXP 'ab{3,5}c', + 'aaaabcccc' REGEXP 'ab{3,5}c' +} {0 1 1 1 0 0} +do_execsql_test regexp1-2.5 { + SELECT 'aaaabbbbcccc' REGEXP 'a(a|b|c)+c', + 'aaaabbbbcccc' REGEXP '^a(a|b|c){11}c$', + 'aaaabbbbcccc' REGEXP '^a(a|b|c){10}c$', + 'aaaabbbbcccc' REGEXP '^a(a|b|c){9}c$' +} {1 0 1 0} +do_execsql_test regexp1-2.6 { + SELECT 'aaaabbbbcccc' REGEXP '^a(a|bb|c)+c$', + 'aaaabbbbcccc' REGEXP '^a(a|bbb|c)+c$', + 'aaaabbbbcccc' REGEXP '^a(a|bbbb|c)+c$' +} {1 0 1} +do_execsql_test regexp1-2.7 { + SELECT 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){3}c$', + 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){4}c$', + 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){5}c$' +} {0 1 1} + +do_execsql_test regexp1-2.8 { + SELECT 'abc*def+ghi.jkl[mno]pqr' REGEXP 'c.d', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'c\*d', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'f\+g', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'i\.j', + 'abc*def+ghi.jkl[mno]pqr' REGEXP 'l\[mno\]p' +} {1 1 1 1 1} + +do_test regexp1-2.9 { + set v1 "abc\ndef" + db eval {SELECT $v1 REGEXP '^abc\ndef$'} +} {1} +do_test regexp1-2.10 { + set v1 "abc\adef" + db eval {SELECT $v1 REGEXP '^abc\adef$'} +} {1} +do_test regexp1-2.11 { + set v1 "abc\tdef" + db eval {SELECT $v1 REGEXP '^abc\tdef$'} +} {1} +do_test regexp1-2.12 { + set v1 "abc\rdef" + db eval {SELECT $v1 REGEXP '^abc\rdef$'} +} {1} +do_test regexp1-2.13 { + set v1 "abc\fdef" + db eval {SELECT $v1 REGEXP '^abc\fdef$'} +} {1} +do_test regexp1-2.14 { + set v1 "abc\vdef" + db eval {SELECT $v1 REGEXP '^abc\vdef$'} +} {1} +do_execsql_test regexp1-2.15 { + SELECT 'abc\def' REGEXP '^abc\\def', + 'abc(def' REGEXP '^abc\(def', + 'abc)def' REGEXP '^abc\)def', + 'abc*def' REGEXP '^abc\*def', + 'abc.def' REGEXP '^abc\.def', + 'abc+def' REGEXP '^abc\+def', + 'abc?def' REGEXP '^abc\?def', + 'abc[def' REGEXP '^abc\[def', + 'abc$def' REGEXP '^abc\$', + '^def' REGEXP '\^def', + 'abc{4}x' REGEXP '^abc\{4\}x$', + 'abc|def' REGEXP '^abc\|def$' +} {1 1 1 1 1 1 1 1 1 1 1 1} + +do_execsql_test regexp1-2.20 { + SELECT 'abc$¢€xyz' REGEXP '^abc\u0024\u00a2\u20acxyz$', + 'abc$¢€xyz' REGEXP '^abc\u0024\u00A2\u20ACxyz$', + 'abc$¢€xyz' REGEXP '^abc\x24\xa2\u20acxyz$' +} {1 1 1} +do_execsql_test regexp1-2.21 { + SELECT 'abc$¢€xyz' REGEXP '^abc[\u0024][\u00a2][\u20ac]xyz$', + 'abc$¢€xyz' REGEXP '^abc[\u0024\u00A2\u20AC]{3}xyz$', + 'abc$¢€xyz' REGEXP '^abc[\x24][\xa2\u20ac]+xyz$' +} {1 1 1} +do_execsql_test regexp1-2.22 { + SELECT 'abc$¢€xyz' REGEXP '^abc[^\u0025-X][^ -\u007f][^\u20ab]xyz$' +} {1} + +finish_test diff --git a/test/releasetest.tcl b/test/releasetest.tcl index 3b4662c..b635061 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -81,6 +81,22 @@ array set ::Configs { -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 } + "Check-Symbols" { + -DSQLITE_MEMDEBUG=1 + -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 + -DSQLITE_ENABLE_FTS3=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_MEMSYS5=1 + -DSQLITE_ENABLE_MEMSYS3=1 + -DSQLITE_ENABLE_COLUMN_METADATA=1 + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_SECURE_DELETE=1 + -DSQLITE_SOUNDEX=1 + -DSQLITE_ENABLE_ATOMIC_WRITE=1 + -DSQLITE_ENABLE_IOTRACE=1 + -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 + -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + } "Debug-One" { -O2 -DSQLITE_DEBUG=1 @@ -164,7 +180,8 @@ array set ::Configs { array set ::Platforms { Linux-x86_64 { - "Debug-One" "checksymbols test" + "Check-Symbols" checksymbols + "Debug-One" test "Secure-Delete" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test @@ -330,15 +347,17 @@ proc main {argv} { # If the configuration included the SQLITE_DEBUG option, then remove # it and run veryquick.test. If it did not include the SQLITE_DEBUG option # add it and run veryquick.test. - set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] - if {$debug_idx < 0} { - run_test_suite "${zConfig}_debug" test [ - concat $config_options -DSQLITE_DEBUG=1 - ] - } else { - run_test_suite "${zConfig}_ndebug" test [ - lreplace $config_options $debug_idx $debug_idx - ] + if {$target!="checksymbols"} { + set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] + if {$debug_idx < 0} { + run_test_suite "${zConfig}_debug" test [ + concat $config_options -DSQLITE_DEBUG=1 + ] + } else { + run_test_suite "${zConfig}_ndebug" test [ + lreplace $config_options $debug_idx $debug_idx + ] + } } } diff --git a/test/resolver01.test b/test/resolver01.test new file mode 100644 index 0000000..3ca6ace --- /dev/null +++ b/test/resolver01.test @@ -0,0 +1,39 @@ +# 2013-04-13 +# +# 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 tests features of the name resolver (the component that +# figures out what identifiers in the SQL statement refer to) that +# were fixed by ticket [2500cdb9be] +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test resolver01-1.1 { + catchsql { + CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(11,22); + CREATE TABLE t2(y, z); INSERT INTO t2 VALUES(33,44); + SELECT 1 AS y FROM t1, t2 ORDER BY y; + } +} {0 1} +do_test resolver01-1.2 { + catchsql { + SELECT 2 AS y FROM t1, t2 ORDER BY y COLLATE nocase; + } +} {0 2} +do_test resolver01-1.3 { + catchsql { + SELECT 3 AS y FROM t1, t2 ORDER BY +y; + } +} {0 3} + + +finish_test diff --git a/test/selectA.test b/test/selectA.test index 5b86377..5fd2288 100644 --- a/test/selectA.test +++ b/test/selectA.test @@ -281,13 +281,13 @@ do_test selectA-2.34 { do_test selectA-2.35 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-2.36 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE DESC,a,c + ORDER BY y COLLATE NOCASE DESC,x,z } } {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a} do_test selectA-2.37 { @@ -311,7 +311,7 @@ do_test selectA-2.39 { do_test selectA-2.40 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-2.41 { @@ -602,7 +602,7 @@ do_test selectA-2.85 { do_test selectA-2.86 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-2.87 { @@ -632,7 +632,7 @@ do_test selectA-2.90 { do_test selectA-2.91 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-2.92 { @@ -893,13 +893,13 @@ do_test selectA-3.34 { do_test selectA-3.35 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-3.36 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE DESC,a,c + ORDER BY y COLLATE NOCASE DESC,x,z } } {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a} do_test selectA-3.37 { @@ -923,7 +923,7 @@ do_test selectA-3.39 { do_test selectA-3.40 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-3.41 { @@ -1214,7 +1214,7 @@ do_test selectA-3.85 { do_test selectA-3.86 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-3.87 { @@ -1244,7 +1244,7 @@ do_test selectA-3.90 { do_test selectA-3.91 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-3.92 { diff --git a/test/selectD.test b/test/selectD.test new file mode 100644 index 0000000..89f999e --- /dev/null +++ b/test/selectD.test @@ -0,0 +1,174 @@ +# 2012 December 19 +# +# 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 implements regression tests for name resolution in SELECT +# statements that have parenthesized FROM clauses. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + + +for {set i 1} {$i<=2} {incr i} { + db close + forcedelete test$i.db + sqlite3 db test$i.db + if {$i==2} { + optimization_control db query-flattener off + } + do_test selectD-$i.0 { + db eval { + ATTACH ':memory:' AS aux1; + CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1'); + CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2'); + CREATE TEMP TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3'); + CREATE TABLE main.t4(a,b); INSERT INTO main.t4 VALUES(444,'x4'); + CREATE TABLE aux1.t4(a,b); INSERT INTO aux1.t4 VALUES(555,'x5'); + } + } {} + do_test selectD-$i.1 { + db eval { + SELECT * + FROM (t1), (t2), (t3), (t4) + WHERE t4.a=t3.a+111 + AND t3.a=t2.a+111 + AND t2.a=t1.a+111; + } + } {111 x1 222 x2 333 x3 444 x4} + do_test selectD-$i.2.1 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {111 x1 222 x2 333 x3 444 x4} + do_test selectD-$i.2.2 { + db eval { + SELECT t3.a + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {333} + do_test selectD-$i.2.3 { + db eval { + SELECT t3.* + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {333 x3} + do_test selectD-$i.2.3 { + db eval { + SELECT t3.*, t2.* + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111) + ON t3.a=t2.a+111) + ON t2.a=t1.a+111; + } + } {333 x3 222 x2} + do_test selectD-$i.2.4 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (main.t4 JOIN aux1.t4 ON aux1.t4.a=main.t4.a+111) + ON main.t4.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {111 x1 222 x2 444 x4 555 x5} + do_test selectD-$i.2.5 { + db eval { + SELECT * + FROM t1 JOIN (t2 JOIN (main.t4 AS x JOIN aux1.t4 ON aux1.t4.a=x.a+111) + ON x.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {111 x1 222 x2 444 x4 555 x5} + do_test selectD-$i.2.6 { + catchsql { + SELECT * + FROM t1 JOIN (t2 JOIN (main.t4 JOIN aux.t4 ON aux.t4.a=main.t4.a+111) + ON main.t4.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {1 {no such table: aux.t4}} + do_test selectD-$i.2.7 { + db eval { + SELECT x.a, y.b + FROM t1 JOIN (t2 JOIN (main.t4 x JOIN aux1.t4 y ON y.a=x.a+111) + ON x.a=t2.a+222) + ON t2.a=t1.a+111; + } + } {444 x5} + do_test selectD-$i.3 { + db eval { + UPDATE t2 SET a=111; + UPDATE t3 SET a=111; + UPDATE t4 SET a=111; + SELECT * + FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a); + } + } {111 x1 x2 x3 x4} + do_test selectD-$i.4 { + db eval { + UPDATE t2 SET a=111; + UPDATE t3 SET a=111; + UPDATE t4 SET a=111; + SELECT * + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a)) + USING (a)) + USING (a); + } + } {111 x1 x2 x3 x4} + do_test selectD-$i.5 { + db eval { + UPDATE t3 SET a=222; + UPDATE t4 SET a=222; + SELECT * + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } + } {111 x1 x2 222 x3 x4} + do_test selectD-$i.6 { + db eval { + UPDATE t4 SET a=333; + SELECT * + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } + } {111 x1 x2 222 x3 {}} + do_test selectD-$i.7 { + db eval { + SELECT t1.*, t2.*, t3.*, t4.b + FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a)) + ON t1.a=t3.a-111; + } + } {111 x1 111 x2 222 x3 {}} +} + +# The following test was added on 2013-04-24 in order to verify that +# the datatypes and affinities of sub-sub-queries are set prior to computing +# the datatypes and affinities of the parent sub-queries because the +# latter computation depends on the former. +# +do_execsql_test selectD-4.1 { + CREATE TABLE t41(a INTEGER PRIMARY KEY, b INTEGER); + CREATE TABLE t42(d INTEGER PRIMARY KEY, e INTEGER); + CREATE TABLE t43(f INTEGER PRIMARY KEY, g INTEGER); + EXPLAIN QUERY PLAN + SELECT * + FROM t41 + LEFT JOIN (SELECT count(*) AS cnt, x1.d + FROM (t42 INNER JOIN t43 ON d=g) AS x1 + WHERE x1.d>5 + GROUP BY x1.d) AS x2 + ON t41.b=x2.d; +} {/.*SEARCH SUBQUERY 1 AS x2 USING AUTOMATIC.*/} + +finish_test diff --git a/test/selectE.test b/test/selectE.test new file mode 100644 index 0000000..d7592bb --- /dev/null +++ b/test/selectE.test @@ -0,0 +1,95 @@ +# 2013-05-07 +# +# 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 implements regression tests for compound SELECT statements +# that have ORDER BY clauses with collating sequences that differ +# from the collating sequence used for comparison in the compound. +# +# Ticket 6709574d2a8d8b9be3a9cb1afbf4ff2de48ea4e7: +# drh added on 2013-05-06 15:21:16: +# +# In the code shown below (which is intended to be run from the +# sqlite3.exe command-line tool) the three SELECT statements should all +# generate the same answer. But the third one does not. It is as if the +# COLLATE clause on the ORDER BY somehow got pulled into the EXCEPT +# operator. Note that the ".print" commands are instructions to the +# sqlite3.exe shell program to output delimiter lines so that you can more +# easily tell where the output of one query ends and the next query +# begins. +# +# CREATE TABLE t1(a); +# INSERT INTO t1 VALUES('abc'),('def'); +# CREATE TABLE t2(a); +# INSERT INTO t2 VALUES('DEF'); +# +# SELECT a FROM t1 EXCEPT SELECT a FROM t2 ORDER BY a; +# .print ----- +# SELECT a FROM (SELECT a FROM t1 EXCEPT SELECT a FROM t2) +# ORDER BY a COLLATE nocase; +# .print ----- +# SELECT a FROM t1 EXCEPT SELECT a FROM t2 ORDER BY a COLLATE nocase; +# +# Bisecting shows that this problem was introduced in SQLite version 3.6.0 +# by check-in [8bbfa97837a74ef] on 2008-06-15. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test selectE-1.0 { + db eval { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES('abc'),('def'),('ghi'); + CREATE TABLE t2(a); + INSERT INTO t2 VALUES('DEF'),('abc'); + CREATE TABLE t3(a); + INSERT INTO t3 VALUES('def'),('jkl'); + + SELECT a FROM t1 EXCEPT SELECT a FROM t2 + ORDER BY a COLLATE nocase; + } +} {def ghi} +do_test selectE-1.1 { + db eval { + SELECT a FROM t2 EXCEPT SELECT a FROM t3 + ORDER BY a COLLATE nocase; + } +} {abc DEF} +do_test selectE-1.2 { + db eval { + SELECT a FROM t2 EXCEPT SELECT a FROM t3 + ORDER BY a COLLATE binary; + } +} {DEF abc} +do_test selectE-1.3 { + db eval { + SELECT a FROM t2 EXCEPT SELECT a FROM t3 + ORDER BY a; + } +} {DEF abc} + +do_test selectE-2.1 { + db eval { + DELETE FROM t2; + DELETE FROM t3; + INSERT INTO t2 VALUES('ABC'),('def'),('GHI'),('jkl'); + INSERT INTO t3 SELECT lower(a) FROM t2; + SELECT a COLLATE nocase FROM t2 EXCEPT SELECT a FROM t3 + ORDER BY 1 + } +} {} +do_test selectE-2.2 { + db eval { + SELECT a COLLATE nocase FROM t2 EXCEPT SELECT a FROM t3 + ORDER BY 1 COLLATE binary + } +} {} + +finish_test diff --git a/test/shared9.test b/test/shared9.test new file mode 100644 index 0000000..1982a59 --- /dev/null +++ b/test/shared9.test @@ -0,0 +1,230 @@ +# 2012 October 5 +# +# 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. +# +#*********************************************************************** +# +# The tests in this file are intended to show if two connections attach +# to the same shared cache using different database names, views and +# virtual tables may still be accessed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set testprefix shared9 + +ifcapable !view||!trigger { + finish_test + return +} + +db close +set enable_shared_cache [sqlite3_enable_shared_cache 1] + +sqlite3 db1 test.db +sqlite3 db2 test.db +forcedelete test.db2 + +do_test 1.1 { + db1 eval { + ATTACH 'test.db2' AS 'fred'; + CREATE TABLE fred.t1(a, b, c); + CREATE VIEW fred.v1 AS SELECT * FROM t1; + + CREATE TABLE fred.t2(a, b); + CREATE TABLE fred.t3(a, b); + CREATE TRIGGER fred.trig AFTER INSERT ON t2 BEGIN + DELETE FROM t3; + INSERT INTO t3 SELECT * FROM t2; + END; + INSERT INTO t2 VALUES(1, 2); + SELECT * FROM t3; + } +} {1 2} + +do_test 1.2 { db2 eval "ATTACH 'test.db2' AS 'jones'" } {} +do_test 1.3 { db2 eval "SELECT * FROM v1" } {} +do_test 1.4 { db2 eval "INSERT INTO t2 VALUES(3, 4)" } {} + +ifcapable fts3 { + do_test 1.5 { + db1 eval { + CREATE VIRTUAL TABLE fred.t4 USING fts4; + INSERT INTO t4 VALUES('hello world'); + } + } {} + + do_test 1.6 { + db2 eval { + INSERT INTO t4 VALUES('shared cache'); + SELECT * FROM t4 WHERE t4 MATCH 'hello'; + } + } {{hello world}} + + do_test 1.7 { + db1 eval { + SELECT * FROM t4 WHERE t4 MATCH 'c*'; + } + } {{shared cache}} +} + +db1 close +db2 close + +#------------------------------------------------------------------------- +# The following tests attempt to find a similar problem with collation +# sequence names - pointers to database handle specific allocations leaking +# into schema objects and being used after the original handle has been +# closed. +# +forcedelete test.db test.db2 +sqlite3 db1 test.db +sqlite3 db2 test.db +foreach x {collate1 collate2 collate3} { + proc $x {a b} { string compare $a $b } + db1 collate $x $x + db2 collate $x $x +} +do_test 2.1 { + db1 eval { + CREATE TABLE t1(a, b, c COLLATE collate1); + CREATE INDEX i1 ON t1(a COLLATE collate2, c, b); + } +} {} +do_test 2.2 { + db1 close + db2 eval "INSERT INTO t1 VALUES('abc', 'def', 'ghi')" +} {} +db2 close + +#------------------------------------------------------------------------- +# At one point, the following would cause a collation sequence belonging +# to connection [db1] to be invoked by a call to [db2 eval]. Which is a +# problem if [db1] has already been closed. +# +forcedelete test.db test.db2 +sqlite3 db1 test.db +sqlite3 db2 test.db + +proc mycollate_db1 {a b} {set ::invoked_mycollate_db1 1 ; string compare $a $b} +proc mycollate_db2 {a b} {string compare $a $b} + +db1 collate mycollate mycollate_db1 +db2 collate mycollate mycollate_db2 + +do_test 2.3 { + set ::invoked_mycollate_db1 0 + db1 eval { + CREATE TABLE t1(a COLLATE mycollate, CHECK (a IN ('one', 'two', 'three'))); + INSERT INTO t1 VALUES('one'); + } + db1 close + set ::invoked_mycollate_db1 +} {1} +do_test 2.4 { + set ::invoked_mycollate_db1 0 + db2 eval { + INSERT INTO t1 VALUES('two'); + } + db2 close + set ::invoked_mycollate_db1 +} {0} + +forcedelete test.db test.db2 +sqlite3 db1 test.db +sqlite3 db2 test.db +db1 collate mycollate mycollate_db1 +db2 collate mycollate mycollate_db2 + +do_test 2.13 { + set ::invoked_mycollate_db1 0 + db1 eval { + CREATE TABLE t1(a, CHECK (a COLLATE mycollate IN ('one', 'two', 'three'))); + INSERT INTO t1 VALUES('one'); + } + db1 close + set ::invoked_mycollate_db1 +} {1} +do_test 2.14 { + set ::invoked_mycollate_db1 0 + db2 eval { + INSERT INTO t1 VALUES('two'); + } + db2 close + set ::invoked_mycollate_db1 +} {0} + +#------------------------------------------------------------------------- +# This test verifies that a bug causing a busy-handler belonging to one +# shared-cache connection to be executed as a result of an sqlite3_step() +# on another has been fixed. +# +forcedelete test.db test.db2 +sqlite3 db1 test.db +sqlite3 db2 test.db + +proc busyhandler {handle args} { + set ::busyhandler_invoked_for $handle + return 1 +} +db1 busy [list busyhandler db1] +db2 busy [list busyhandler db2] + +do_test 3.1 { + db1 eval { + BEGIN; + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t2 VALUES(1, 2); + } + # Keep this next COMMIT as a separate statement. This ensures that COMMIT + # has already been compiled and loaded into the tcl interface statement + # cache when it is attempted below. + db1 eval COMMIT + db1 eval { + BEGIN; + INSERT INTO t1 VALUES(3, 4); + } +} {} + +do_test 3.2 { + set ::tf [launch_testfixture] + testfixture $::tf { + sqlite3 db test.db + db eval { + BEGIN; + SELECT * FROM t1; + } + } +} {1 2} + +do_test 3.3 { + db2 eval { SELECT * FROM t2 } +} {1 2} + +do_test 3.4 { + list [catch { db1 eval COMMIT } msg] $msg +} {1 {database is locked}} + +# At one point the following would fail, showing that the busy-handler +# belonging to [db2] was invoked instead. +do_test 3.5 { + set ::busyhandler_invoked_for +} {db1} +do_test 3.6 { + close $::tf + db1 eval COMMIT +} {} + +db1 close +db2 close + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test diff --git a/test/sharedA.test b/test/sharedA.test new file mode 100644 index 0000000..146fb26 --- /dev/null +++ b/test/sharedA.test @@ -0,0 +1,188 @@ +# 2013 May 14 +# +# 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. +# +#*********************************************************************** +# +# Test some specific circumstances to do with shared cache mode. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +if {[run_thread_tests]==0} { finish_test ; return } +db close +set ::testprefix sharedA + +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + +#------------------------------------------------------------------------- +# +do_test 0.1 { + sqlite3 db1 test.db + sqlite3 db2 test.db + + db1 eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(100)); + INSERT INTO t1 SELECT randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100) FROM t1; + INSERT INTO t1 SELECT randomblob(100) FROM t1; + CREATE INDEX i1 ON t1(x); + } + + db1 eval { + BEGIN; + DROP INDEX i1; + } + + db2 close + + db1 eval { + INSERT INTO t1 SELECT randomblob(100) FROM t1; + ROLLBACK; + PRAGMA integrity_check; + } +} {ok} + +db1 close +forcedelete test.db + + +#------------------------------------------------------------------------- +# +do_test 1.1 { + sqlite3 db1 test.db + sqlite3 db2 test.db + db2 eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(123); + } + db1 eval { + SELECT * FROM t1; + CREATE INDEX i1 ON t1(x); + } +} {123} + +do_test 1.2 { + db2 eval { SELECT * FROM t1 ORDER BY x; } + + db1 eval { + BEGIN; DROP INDEX i1; + } + db1 close + + db2 eval { SELECT * FROM t1 ORDER BY x; } +} {123} + +do_test 1.3 { + db2 close +} {} + +#------------------------------------------------------------------------- +# +# sqlite3RollbackAll() loops through all attached b-trees and rolls +# back each one separately. Then if the SQLITE_InternChanges flag is +# set, it resets the schema. Both of the above steps must be done +# while holding a mutex, otherwise another thread might slip in and +# try to use the new schema with the old data. +# +# The following sequence of tests attempt to verify that the actions +# taken by sqlite3RollbackAll() are thread-atomic (that they cannot be +# interrupted by a separate thread.) +# +# Note that a TCL interpreter can only be used within the thread in which +# it was originally created (because it uses thread-local-storage). +# The tvfs callbacks must therefore only run on the main thread. +# There is some trickery in the read_callback procedure to ensure that +# this is the case. +# +testvfs tvfs + +# Set up two databases and two database connections. +# +# db1: main(test.db), two(test2.db) +# db2: main(test.db) +# +# The cache for test.db is shared between db1 and db2. +# +do_test 2.1 { + forcedelete test.db test.db2 + sqlite3 db1 test.db -vfs tvfs + db1 eval { ATTACH 'test.db2' AS two } + + db1 eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t1 VALUES(3); + CREATE TABLE two.t2(x); + INSERT INTO t2 SELECT * FROM t1; + } + + sqlite3 db2 test.db -vfs tvfs + db2 eval { SELECT * FROM t1 } +} {1 2 3} + +# Create a prepared statement on db2 that will attempt a schema change +# in test.db. Meanwhile, start a transaction on db1 that changes +# the schema of test.db and that creates a rollback journal on test2.db +# +do_test 2.2 { + set ::STMT [sqlite3_prepare db2 "CREATE INDEX i1 ON t1(x)" -1 tail] + db1 eval { + BEGIN; + CREATE INDEX i1 ON t1(x); + INSERT INTO t2 VALUES('value!'); + } +} {} + +# Set up a callback that will cause db2 to try to execute its +# schema change when db1 accesses the journal file of test2.db. +# +# This callback will be invoked after the content of test.db has +# be rolled back but before the schema has been reset. If the +# sqlite3RollbackAll() operation is not thread-atomic, then the +# db2 statement in the callback will see old content with the newer +# schema, which is wrong. +# +tvfs filter xRead +tvfs script read_callback +unset -nocomplain ::some_time_laster +unset -nocomplain ::thread_result +proc read_callback {call file args} { + if {[string match *test.db2-journal $file]} { + tvfs filter {} ;# Ensure that tvfs callbacks to do run on the + # child thread + sqlthread spawn ::thread_result [subst -nocommands { + sqlite3_step $::STMT + set rc [sqlite3_finalize $::STMT] + }] + after 1000 { set ::some_time_later 1 } + vwait ::some_time_later + } +} +do_test 2.3 { db1 eval ROLLBACK } {} + +# Verify that the db2 statement invoked by the callback detected the +# schema change. +# +if {[info exists ::thread_result]==0} { vwait ::thread_result } +do_test 2.4 { + list $::thread_result [sqlite3_errmsg db2] +} {SQLITE_SCHEMA {database schema has changed}} + +db1 close +db2 close +tvfs delete + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test diff --git a/test/shared_err.test b/test/shared_err.test index f501fc7..17add94 100644 --- a/test/shared_err.test +++ b/test/shared_err.test @@ -401,6 +401,8 @@ do_malloc_test shared_err-8 -tclprep { execsql {INSERT INTO t1 VALUES($a, $b)} db2 } execsql {COMMIT} db2 + execsql BEGIN + execsql ROLLBACK set ::DB2 [sqlite3_connection_pointer db2] set ::STMT [sqlite3_prepare $::DB2 "SELECT a FROM t1 ORDER BY a" -1 DUMMY] sqlite3_step $::STMT ;# Cursor points at 0000000000 @@ -409,8 +411,7 @@ do_malloc_test shared_err-8 -tclprep { execsql { BEGIN; INSERT INTO t1 VALUES(6, NULL); - ROLLBACK; - } + ROLLBACK} } -cleanup { # UPDATE: As of [5668], if the rollback fails SQLITE_CORRUPT is returned. # So these tests have been updated to expect SQLITE_CORRUPT and its diff --git a/test/shell1.test b/test/shell1.test index 47f9e41..c60f3af 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -149,7 +149,7 @@ do_test shell1-1.14.3 { set res [catchcmd "-separator" ""] set rc [lindex $res 0] list $rc \ - [regexp {Error: missing argument for option: -separator} $res] + [regexp {Error: missing argument to -separator} $res] } {1 1} # -stats print memory stats before each finalize @@ -168,7 +168,7 @@ do_test shell1-1.15.3 { set res [catchcmd "-nullvalue" ""] set rc [lindex $res 0] list $rc \ - [regexp {Error: missing argument for option: -nullvalue} $res] + [regexp {Error: missing argument to -nullvalue} $res] } {1 1} # -version show SQLite version @@ -220,7 +220,7 @@ do_test shell1-2.3.2 { } {0 {}} do_test shell1-2.3.3 { catchcmd "test.db" ".explain \"1 2 3\"" -} {0 {}} +} {1 {ERROR: Not a boolean value: "1 2 3". Assuming "no".}} do_test shell1-2.3.4 { catchcmd "test.db" ".explain \"OFF\"" } {0 {}} @@ -253,7 +253,7 @@ do_test shell1-2.4.2 { # .backup ?DB? FILE Backup DB (default "main") to FILE do_test shell1-3.1.1 { catchcmd "test.db" ".backup" -} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}} +} {1 {missing FILENAME argument on .backup}} do_test shell1-3.1.2 { catchcmd "test.db" ".backup FOO" } {0 {}} @@ -263,7 +263,7 @@ do_test shell1-3.1.3 { do_test shell1-3.1.4 { # too many arguments catchcmd "test.db" ".backup FOO BAR BAD" -} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}} +} {1 {too many arguments to .backup}} # .bail ON|OFF Stop after hitting an error. Default OFF do_test shell1-3.2.1 { @@ -326,10 +326,6 @@ do_test shell1-3.5.4 { do_test shell1-3.6.1 { catchcmd "test.db" ".exit" } {0 {}} -do_test shell1-3.6.2 { - # too many arguments - catchcmd "test.db" ".exit BAD" -} {1 {Error: unknown command or invalid arguments: "exit". Enter ".help" for help}} # .explain ON|OFF Turn output mode suitable for EXPLAIN on or off. do_test shell1-3.7.1 { @@ -680,6 +676,15 @@ do_test shell1-3.26.4 { catchcmd "test.db" ".width 1 1" # this should be treated the same as a '1' width for col 1 and 2 } {0 {}} +do_test shell1-3.26.5 { + catchcmd "test.db" ".mode column\n.width 10 -10\nSELECT 'abcdefg', 123456;" + # this should be treated the same as a '1' width for col 1 and 2 +} {0 {abcdefg 123456}} +do_test shell1-3.26.6 { + catchcmd "test.db" ".mode column\n.width -10 10\nSELECT 'abcdefg', 123456;" + # this should be treated the same as a '1' width for col 1 and 2 +} {0 { abcdefg 123456 }} + # .timer ON|OFF Turn the CPU timer measurement on or off do_test shell1-3.27.1 { @@ -701,18 +706,23 @@ do_test shell1-3-28.1 { ".log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');" } "0 {(123) hello\n456}" +do_test shell1-3-29.1 { + catchcmd "test.db" ".print this is a test" +} {0 {this is a test}} + # Test the output of the ".dump" command # do_test shell1-4.1 { db eval { CREATE TABLE t1(x); - INSERT INTO t1 VALUES(null), (1), (2.25), ('hello'), (x'807f'); + INSERT INTO t1 VALUES(null), (''), (1), (2.25), ('hello'), (x'807f'); } catchcmd test.db {.dump} } {0 {PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE t1(x); INSERT INTO "t1" VALUES(NULL); +INSERT INTO "t1" VALUES(''); INSERT INTO "t1" VALUES(1); INSERT INTO "t1" VALUES(2.25); INSERT INTO "t1" VALUES('hello'); @@ -724,10 +734,58 @@ COMMIT;}} do_test shell1-4.2 { catchcmd test.db ".mode insert t1\nselect * from t1;" } {0 {INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES(''); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2.25); INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES(X'807f');}} +# Test the output of ".mode tcl" +# +do_test shell1-4.3 { + catchcmd test.db ".mode tcl\nselect * from t1;" +} {0 {"" +"" +"1" +"2.25" +"hello" +"\200\177"}} + +# Test the output of ".mode tcl" with multiple columns +# +do_test shell1-4.4 { + db eval { + CREATE TABLE t2(x,y); + INSERT INTO t2 VALUES(null, ''), (1, 2.25), ('hello', x'807f'); + } + catchcmd test.db ".mode tcl\nselect * from t2;" +} {0 {"" "" +"1" "2.25" +"hello" "\200\177"}} + +# Test the output of ".mode tcl" with ".nullvalue" +# +do_test shell1-4.5 { + catchcmd test.db ".mode tcl\n.nullvalue NULL\nselect * from t2;" +} {0 {"NULL" "" +"1" "2.25" +"hello" "\200\177"}} + +# Test the output of ".mode tcl" with Tcl reserved characters +# +do_test shell1-4.6 { + db eval { + CREATE TABLE tcl1(x); + INSERT INTO tcl1 VALUES('"'), ('['), (']'), ('\{'), ('\}'), (';'), ('$'); + } + foreach {x y} [catchcmd test.db ".mode tcl\nselect * from tcl1;"] break + list $x $y [llength $y] +} {0 {"\"" +"[" +"]" +"\\{" +"\\}" +";" +"$"} 7} finish_test diff --git a/test/speed1p.test b/test/speed1p.test index 915f165..6bf7b10 100644 --- a/test/speed1p.test +++ b/test/speed1p.test @@ -24,6 +24,8 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl speed_trial_init speed1 +sqlite3_memdebug_vfs_oom_test 0 + # Set a uniform random seed expr srand(0) @@ -78,7 +80,6 @@ do_test speed1p-1.0 { } } {i2a i2b t1 t2} - # 50000 INSERTs on an unindexed table # set list {} diff --git a/test/spellfix.test b/test/spellfix.test index afef981..dfa487a 100644 --- a/test/spellfix.test +++ b/test/spellfix.test @@ -16,7 +16,7 @@ set testprefix spellfix ifcapable !vtab { finish_test ; return } -register_spellfix_module db +load_static_extension db spellfix nextchar set vocab { rabbi rabbit rabbits rabble rabid rabies raccoon raccoons race raced racer @@ -84,6 +84,26 @@ foreach {tn word res} { } $res } +# Tests of the next_char function. +# +do_test 1.10 { + db eval { + CREATE TABLE vocab(w TEXT PRIMARY KEY); + INSERT INTO vocab SELECT word FROM t1; + } +} {} +do_execsql_test 1.11 { + SELECT next_char('re','vocab','w'); +} {a} +do_execsql_test 1.12 { + SELECT next_char('r','vocab','w'); +} {ae} +do_execsql_test 1.13 { + SELECT next_char('','vocab','w'); +} {r} +do_test 1.14 { + catchsql {SELECT next_char('','xyzzy','a')} +} {1 {no such table: xyzzy}} do_execsql_test 2.1 { CREATE VIRTUAL TABLE t2 USING spellfix1; @@ -136,16 +156,52 @@ do_test 3.2 { } } {} -breakpoint foreach {tn word res} { 1 kos* {kosher 3 kiosk 4 kudo 2 kiss 3 kissed 3} 2 kellj* {killjoy 5 kill 4 killed 4 killer 4 killers 4} 3 kellj {kill 4 kills 5 killjoy 7 keel 4 killed 6} } { - do_execsql_test 1.2.$tn { + do_execsql_test 3.2.$tn { SELECT word, matchlen FROM t3 WHERE word MATCH $word ORDER BY score, word LIMIT 5 } $res -} +} + +do_execsql_test 4.0 { + INSERT INTO t3(command) VALUES('edit_cost_table=NULL'); +} +foreach {tn word res} { + 1 kosher {kosher 0 kisser 51 kissers 76 kissed 126 kisses 126} + 2 kellj {keels 60 killjoy 68 kills 80 keel 120 kill 125} + 3 kashar {kosher 80 kisser 91 kissers 116 kissed 166 kisses 166} +} { + do_execsql_test 4.1.$tn { + SELECT word, distance FROM t3 WHERE word MATCH $word + ORDER BY score, word LIMIT 5 + } $res +} +do_execsql_test 5.0 { + CREATE TABLE costs2(iLang, cFrom, cTo, iCost); + INSERT INTO costs2 VALUES(0, 'a', 'o', 1); + INSERT INTO costs2 VALUES(0, 'e', 'o', 4); + INSERT INTO costs2 VALUES(0, 'i', 'o', 8); + INSERT INTO costs2 VALUES(0, 'u', 'o', 16); + INSERT INTO t3(command) VALUES('edit_cost_table="costs2"'); +} + +foreach {tn word res} { + 1 kasher {kosher 1} + 2 kesher {kosher 4} + 3 kisher {kosher 8} + 4 kosher {kosher 0} + 5 kusher {kosher 16} +} { + do_execsql_test 5.1.$tn { + SELECT word, distance FROM t3 WHERE word MATCH $word + ORDER BY score, word LIMIT 1 + } $res +} + + finish_test diff --git a/test/stat.test b/test/stat.test index 926d9b7..ac88f7a 100644 --- a/test/stat.test +++ b/test/stat.test @@ -76,11 +76,16 @@ do_test stat-1.4 { do_execsql_test stat-2.1 { CREATE TABLE t3(a PRIMARY KEY, b); INSERT INTO t3(rowid, a, b) VALUES(2, a_string(111), a_string(222)); - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload FROM stat WHERE name != 'sqlite_master'; } [list \ diff --git a/test/subquery.test b/test/subquery.test index d9d2952..f601d3f 100644 --- a/test/subquery.test +++ b/test/subquery.test @@ -421,7 +421,7 @@ do_test subquery-3.5.7 { # and expose bugs to do with re-using statements that have been # passed to sqlite3_reset(). # -# One problem was that VDBE memory cells were not being initialised +# One problem was that VDBE memory cells were not being initialized # to NULL on the second and subsequent executions. # do_test subquery-4.1.1 { diff --git a/test/subquery2.test b/test/subquery2.test index 0420004..4406efc 100644 --- a/test/subquery2.test +++ b/test/subquery2.test @@ -15,6 +15,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix subquery2 ifcapable !subquery { finish_test @@ -82,5 +83,25 @@ do_test subquery2-1.22 { } } {1 3 5 7} +#------------------------------------------------------------------------- +# Test that ticket d6b36be38a has been fixed. +do_execsql_test 2.1 { + CREATE TABLE t4(a, b); + CREATE TABLE t5(a, b); + INSERT INTO t5 VALUES(3, 5); + + INSERT INTO t4 VALUES(1, 1); + INSERT INTO t4 VALUES(2, 3); + INSERT INTO t4 VALUES(3, 6); + INSERT INTO t4 VALUES(4, 10); + INSERT INTO t4 VALUES(5, 15); +} + +do_execsql_test 2.2 { + SELECT * + FROM (SELECT * FROM t4 ORDER BY a LIMIT -1 OFFSET 1) + LIMIT (SELECT a FROM t5) +} {2 3 3 6 4 10} + finish_test diff --git a/test/syscall.test b/test/syscall.test index d841a9a..5bf1225 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -60,7 +60,7 @@ foreach s { open close access getcwd stat fstat ftruncate fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 unlink openDirectory mkdir rmdir - statvfs fchown umask + statvfs fchown umask mmap munmap mremap } { if {[test_syscall exists $s]} {lappend syscall_list $s} } diff --git a/test/sysfault.test b/test/sysfault.test index 07d525c..92fb534 100644 --- a/test/sysfault.test +++ b/test/sysfault.test @@ -243,5 +243,35 @@ do_faultsim_test 3 -faults vfsfault-* -prep { faultsim_test_result {0 20000} } -finish_test +#------------------------------------------------------------------------- +# Test errors in mmap(). +# +proc vfsfault_install {} { + test_syscall reset + test_syscall install {mmap} +} + +faultsim_delete_and_reopen +execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); +} +faultsim_save_and_close + +do_faultsim_test 4 -faults vfsfault-* -prep { + faultsim_restore_and_reopen + file_control_chunksize_test db main 8192 + execsql { + PRAGMA mmap_size = 1000000; + } +} -body { + test_syscall errno mmap EACCES + + execsql { + SELECT * FROM t1; + } +} -test { + faultsim_test_result {0 {1 2}} {1 {disk I/O error}} +} +finish_test diff --git a/test/tclsqlite.test b/test/tclsqlite.test index c954c71..3d9cd46 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -143,11 +143,11 @@ do_test tcl-1.21 { set v [catch {db total_changes xyz} msg] lappend v $msg } {1 {wrong # args: should be "db total_changes "}} -do_test tcl-1.20 { +do_test tcl-1.22 { set v [catch {db copy} msg] lappend v $msg } {1 {wrong # args: should be "db copy CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"}} -do_test tcl-1.21 { +do_test tcl-1.23 { set v [catch {sqlite3 db2 test.db -vfs nosuchvfs} msg] lappend v $msg } {1 {no such vfs: nosuchvfs}} diff --git a/test/temptable.test b/test/temptable.test index 5eeb0f5..6a1e2b9 100644 --- a/test/temptable.test +++ b/test/temptable.test @@ -150,7 +150,7 @@ do_test temptable-3.4 { # Check for correct name collision processing. A name collision can # occur when process A creates a temporary table T then process B # creates a permanent table also named T. The temp table in process A -# hides the existance of the permanent table. +# hides the existence of the permanent table. # do_test temptable-4.1 { execsql { diff --git a/test/tester.tcl b/test/tester.tcl index 68b2c8d..761a36e 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -31,6 +31,7 @@ # Test the capability of the SQLite version built into the interpreter to # determine if a specific test can be run: # +# capable EXPR # ifcapable EXPR # # Calulate checksums based on database contents: @@ -53,6 +54,7 @@ # do_ioerr_test TESTNAME ARGS... # crashsql ARGS... # integrity_check TESTNAME ?DB? +# verify_ex_errcode TESTNAME EXPECTED ?DB? # do_test TESTNAME SCRIPT EXPECTED # do_execsql_test TESTNAME SQL EXPECTED # do_catchsql_test TESTNAME SQL EXPECTED @@ -121,7 +123,7 @@ if {[info command sqlite_orig]==""} { set res } else { # This command is not opening a new database connection. Pass the - # arguments through to the C implemenation as the are. + # arguments through to the C implementation as the are. # uplevel 1 sqlite_orig $args } @@ -134,7 +136,7 @@ proc getFileRetries {} { # NOTE: Return the default number of retries for [file] operations. A # value of zero or less here means "disabled". # - return [expr {$::tcl_platform(platform) eq "windows" ? 10 : 0}] + return [expr {$::tcl_platform(platform) eq "windows" ? 50 : 0}] } return $::G(file-retries) } @@ -460,6 +462,7 @@ if {0==[info exists ::SLAVE]} { set TC(count) 0 set TC(fail_list) [list] set TC(omit_list) [list] + set TC(warn_list) [list] proc set_test_counter {counter args} { if {[llength $args]} { @@ -494,6 +497,18 @@ proc fail_test {name} { } } +# Remember a warning message to be displayed at the conclusion of all testing +# +proc warning {msg {append 1}} { + puts "Warning: $msg" + set warnList [set_test_counter warn_list] + if {$append} { + lappend warnList $msg + } + set_test_counter warn_list $warnList +} + + # Increment the number of tests run # proc incr_ntest {} { @@ -537,16 +552,19 @@ proc do_test {name cmd expected} { } else { if {[regexp {^~?/.*/$} $expected]} { if {[string index $expected 0]=="~"} { - set re [string range $expected 2 end-1] + set re [string map {# {[-0-9.]+}} [string range $expected 2 end-1]] set ok [expr {![regexp $re $result]}] } else { - set re [string range $expected 1 end-1] + set re [string map {# {[-0-9.]+}} [string range $expected 1 end-1]] set ok [regexp $re $result] } } else { set ok [expr {[string compare $result $expected]==0}] } if {!$ok} { + # if {![info exists ::testprefix] || $::testprefix eq ""} { + # error "no test prefix" + # } puts "\nExpected: \[$expected\]\n Got: \[$result\]" fail_test $name } else { @@ -779,6 +797,9 @@ proc finalize_testing {} { if {$nErr>0} { puts "Failures on these tests: [set_test_counter fail_list]" } + foreach warning [set_test_counter warn_list] { + puts "Warning: $warning" + } run_thread_tests 1 if {[llength $omitList]>0} { puts "Omitted test cases:" @@ -964,6 +985,12 @@ proc integrity_check {name {db db}} { } } +# Check the extended error code +# +proc verify_ex_errcode {name expected {db db}} { + do_test $name [list sqlite3_extended_errcode $db] $expected +} + # Return true if the SQL statement passed as the second argument uses a # statement transaction. @@ -994,6 +1021,12 @@ proc fix_ifcapable_expr {expr} { return $ret } +# Returns non-zero if the capabilities are present; zero otherwise. +# +proc capable {expr} { + set e [fix_ifcapable_expr $expr]; return [expr ($e)] +} + # Evaluate a boolean expression of capabilities. If true, execute the # code. Omit the code if false. # @@ -1020,7 +1053,7 @@ proc ifcapable {expr code {else ""} {elsecode ""}} { # boolean, indicating whether or not the process actually crashed or # reported some other error. The second element in the returned list is the # error message. This is "child process exited abnormally" if the crash -# occured. +# occurred. # # crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql # @@ -1101,6 +1134,25 @@ proc crashsql {args} { lappend r $msg } +proc run_ioerr_prep {} { + set ::sqlite_io_error_pending 0 + catch {db close} + catch {db2 close} + catch {forcedelete test.db} + catch {forcedelete test.db-journal} + catch {forcedelete test2.db} + catch {forcedelete test2.db-journal} + set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] + sqlite3_extended_result_codes $::DB $::ioerropts(-erc) + if {[info exists ::ioerropts(-tclprep)]} { + eval $::ioerropts(-tclprep) + } + if {[info exists ::ioerropts(-sqlprep)]} { + execsql $::ioerropts(-sqlprep) + } + expr 0 +} + # Usage: do_ioerr_test # # This proc is used to implement test cases that check that IO errors @@ -1133,10 +1185,26 @@ proc do_ioerr_test {testname args} { # TEMPORARY: For 3.5.9, disable testing of extended result codes. There are # a couple of obscure IO errors that do not return them. set ::ioerropts(-erc) 0 + + # Create a single TCL script from the TCL and SQL specified + # as the body of the test. + set ::ioerrorbody {} + if {[info exists ::ioerropts(-tclbody)]} { + append ::ioerrorbody "$::ioerropts(-tclbody)\n" + } + if {[info exists ::ioerropts(-sqlbody)]} { + append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}" + } + + save_prng_state + if {$::ioerropts(-cksum)} { + run_ioerr_prep + eval $::ioerrorbody + set ::goodcksum [cksum] + } set ::go 1 #reset_prng_state - save_prng_state for {set n $::ioerropts(-start)} {$::go} {incr n} { set ::TN $n incr ::ioerropts(-count) -1 @@ -1153,27 +1221,12 @@ proc do_ioerr_test {testname args} { # Delete the files test.db and test2.db, then execute the TCL and # SQL (in that order) to prepare for the test case. do_test $testname.$n.1 { - set ::sqlite_io_error_pending 0 - catch {db close} - catch {db2 close} - catch {forcedelete test.db} - catch {forcedelete test.db-journal} - catch {forcedelete test2.db} - catch {forcedelete test2.db-journal} - set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] - sqlite3_extended_result_codes $::DB $::ioerropts(-erc) - if {[info exists ::ioerropts(-tclprep)]} { - eval $::ioerropts(-tclprep) - } - if {[info exists ::ioerropts(-sqlprep)]} { - execsql $::ioerropts(-sqlprep) - } - expr 0 + run_ioerr_prep } {0} # Read the 'checksum' of the database. if {$::ioerropts(-cksum)} { - set checksum [cksum] + set ::checksum [cksum] } # Set the Nth IO error to fail. @@ -1181,20 +1234,10 @@ proc do_ioerr_test {testname args} { set ::sqlite_io_error_persist $::ioerropts(-persist) set ::sqlite_io_error_pending $n }] $n - - # Create a single TCL script from the TCL and SQL specified - # as the body of the test. - set ::ioerrorbody {} - if {[info exists ::ioerropts(-tclbody)]} { - append ::ioerrorbody "$::ioerropts(-tclbody)\n" - } - if {[info exists ::ioerropts(-sqlbody)]} { - append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}" - } - # Execute the TCL Script created in the above block. If - # there are at least N IO operations performed by SQLite as - # a result of the script, the Nth will fail. + # Execute the TCL script created for the body of this test. If + # at least N IO operations performed by SQLite as a result of + # the script, the Nth will fail. do_test $testname.$n.3 { set ::sqlite_io_error_hit 0 set ::sqlite_io_error_hardhit 0 @@ -1290,7 +1333,7 @@ proc do_ioerr_test {testname args} { } } - # If an IO error occured, then the checksum of the database should + # If an IO error occurred, then the checksum of the database should # be the same as before the script that caused the IO error was run. # if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} { @@ -1298,8 +1341,15 @@ proc do_ioerr_test {testname args} { catch {db close} catch {db2 close} set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] - cksum - } $checksum + set nowcksum [cksum] + set res [expr {$nowcksum==$::checksum || $nowcksum==$::goodcksum}] + if {$res==0} { + puts "now=$nowcksum" + puts "the=$::checksum" + puts "fwd=$::goodcksum" + } + set res + } 1 } set ::sqlite_io_error_hardhit 0 diff --git a/test/thread001.test b/test/thread001.test index 7e0893f..a796c57 100644 --- a/test/thread001.test +++ b/test/thread001.test @@ -87,7 +87,7 @@ foreach {tn same_db shared_cache} [list \ do_test t1 { execsql { SELECT - (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) == + (SELECT md5sum(a, b) FROM ab WHERE +a < (SELECT max(a) FROM ab)) == (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab)) } } {1} @@ -131,7 +131,7 @@ foreach {tn same_db shared_cache} [list \ do_test thread001.$tn.6 { execsql { SELECT - (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) == + (SELECT md5sum(a, b) FROM ab WHERE +a < (SELECT max(a) FROM ab)) == (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab)) } } {1} diff --git a/test/tkt-2d1a5c67d.test b/test/tkt-2d1a5c67d.test index bf9595f..3fef187 100644 --- a/test/tkt-2d1a5c67d.test +++ b/test/tkt-2d1a5c67d.test @@ -46,7 +46,7 @@ for {set ii 1} {$ii<=10} {incr ii} { db close forcedelete test.db test.db-wal sqlite3 db test.db -register_wholenumber_module db +load_static_extension db wholenumber db eval { PRAGMA journal_mode=WAL; CREATE TABLE t1(a,b); diff --git a/test/tkt-31338dca7e.test b/test/tkt-31338dca7e.test index 9423c68..41dd9d3 100644 --- a/test/tkt-31338dca7e.test +++ b/test/tkt-31338dca7e.test @@ -91,7 +91,7 @@ do_test tkt-31338-3.1 { INSERT INTO t3 VALUES(4); CREATE TABLE t4(h); INSERT INTO t4 VALUES(5); - + SELECT * FROM t3 LEFT JOIN t1 ON d=g LEFT JOIN t4 ON c=h WHERE (a=1 AND h=4) OR (b IN ( diff --git a/test/tkt-385a5b56b9.test b/test/tkt-385a5b56b9.test index 614e82d..8184864 100644 --- a/test/tkt-385a5b56b9.test +++ b/test/tkt-385a5b56b9.test @@ -39,7 +39,7 @@ do_eqp_test 2.1 { SELECT DISTINCT x FROM t2 } { } do_eqp_test 2.2 { SELECT DISTINCT y FROM t2 } { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2y (~1000000 rows)} } do_eqp_test 2.3 { SELECT DISTINCT x, y FROM t2 WHERE y=10 } { @@ -51,4 +51,3 @@ do_eqp_test 2.4 { SELECT DISTINCT x, y FROM t2 WHERE x=10 } { } finish_test - diff --git a/test/tkt-4dd95f6943.test b/test/tkt-4dd95f6943.test new file mode 100644 index 0000000..353031e --- /dev/null +++ b/test/tkt-4dd95f6943.test @@ -0,0 +1,151 @@ +# 2013 March 13 +# +# 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 implements regression tests for SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix tkt-4dd95f6943 + +do_execsql_test 1.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES (3), (4), (2), (1), (5), (6); +} + +foreach {tn1 idx} { + 1 { CREATE INDEX i1 ON t1(x ASC) } + 2 { CREATE INDEX i1 ON t1(x DESC) } +} { + do_execsql_test 1.$tn1.1 { DROP INDEX IF EXISTS i1; } + do_execsql_test 1.$tn1.2 $idx + + do_execsql_test 1.$tn1.3 { + SELECT x FROM t1 WHERE x IN(2, 4, 5) ORDER BY x ASC; + } {2 4 5} + + do_execsql_test 1.$tn1.4 { + SELECT x FROM t1 WHERE x IN(2, 4, 5) ORDER BY x DESC; + } {5 4 2} +} + + +do_execsql_test 2.0 { + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES (5, 3), (5, 4), (5, 2), (5, 1), (5, 5), (5, 6); + INSERT INTO t2 VALUES (1, 3), (1, 4), (1, 2), (1, 1), (1, 5), (1, 6); + INSERT INTO t2 VALUES (3, 3), (3, 4), (3, 2), (3, 1), (3, 5), (3, 6); + INSERT INTO t2 VALUES (2, 3), (2, 4), (2, 2), (2, 1), (2, 5), (2, 6); + INSERT INTO t2 VALUES (4, 3), (4, 4), (4, 2), (4, 1), (4, 5), (4, 6); + INSERT INTO t2 VALUES (6, 3), (6, 4), (6, 2), (6, 1), (6, 5), (6, 6); + + CREATE TABLE t3(a, b); + INSERT INTO t3 VALUES (2, 2), (4, 4), (5, 5); + CREATE UNIQUE INDEX t3i1 ON t3(a ASC); + CREATE UNIQUE INDEX t3i2 ON t3(b DESC); +} + +foreach {tn1 idx} { + 1 { CREATE INDEX i1 ON t2(x ASC, y ASC) } + 2 { CREATE INDEX i1 ON t2(x ASC, y DESC) } + 3 { CREATE INDEX i1 ON t2(x DESC, y ASC) } + 4 { CREATE INDEX i1 ON t2(x DESC, y DESC) } + + 5 { CREATE INDEX i1 ON t2(y ASC, x ASC) } + 6 { CREATE INDEX i1 ON t2(y ASC, x DESC) } + 7 { CREATE INDEX i1 ON t2(y DESC, x ASC) } + 8 { CREATE INDEX i1 ON t2(y DESC, x DESC) } +} { + do_execsql_test 2.$tn1.1 { DROP INDEX IF EXISTS i1; } + do_execsql_test 2.$tn1.2 $idx + + foreach {tn2 inexpr} { + 3 "(2, 4, 5)" + 4 "(SELECT a FROM t3)" + 5 "(SELECT b FROM t3)" + } { + do_execsql_test 2.$tn1.$tn2.1 " + SELECT x, y FROM t2 WHERE x = 1 AND y IN $inexpr ORDER BY x ASC, y ASC; + " {1 2 1 4 1 5} + + do_execsql_test 2.$tn1.$tn2.2 " + SELECT x, y FROM t2 WHERE x = 2 AND y IN $inexpr ORDER BY x ASC, y DESC; + " {2 5 2 4 2 2} + + do_execsql_test 2.$tn1.$tn2.3 " + SELECT x, y FROM t2 WHERE x = 3 AND y IN $inexpr ORDER BY x DESC, y ASC; + " {3 2 3 4 3 5} + + do_execsql_test 2.$tn1.$tn2.4 " + SELECT x, y FROM t2 WHERE x = 4 AND y IN $inexpr ORDER BY x DESC, y DESC; + " {4 5 4 4 4 2} + + do_execsql_test 2.$tn1.$tn2.5 " + SELECT a, x, y FROM t2, t3 WHERE a = 4 AND x = 1 AND y IN $inexpr + ORDER BY a, x ASC, y ASC; + " {4 1 2 4 1 4 4 1 5} + do_execsql_test 2.$tn1.$tn2.6 " + SELECT a, x, y FROM t2, t3 WHERE a = 2 AND x = 1 AND y IN $inexpr + ORDER BY x ASC, y ASC; + " {2 1 2 2 1 4 2 1 5} + + do_execsql_test 2.$tn1.$tn2.7 " + SELECT a, x, y FROM t2, t3 WHERE a = 4 AND x = 1 AND y IN $inexpr + ORDER BY a, x ASC, y DESC; + " {4 1 5 4 1 4 4 1 2} + do_execsql_test 2.$tn1.8 " + SELECT a, x, y FROM t2, t3 WHERE a = 2 AND x = 1 AND y IN $inexpr + ORDER BY x ASC, y DESC; + " {2 1 5 2 1 4 2 1 2} + + do_execsql_test 2.$tn1.$tn2.9 " + SELECT a, x, y FROM t2, t3 WHERE a = 4 AND x = 1 AND y IN $inexpr + ORDER BY a, x DESC, y ASC; + " {4 1 2 4 1 4 4 1 5} + do_execsql_test 2.$tn1.10 " + SELECT a, x, y FROM t2, t3 WHERE a = 2 AND x = 1 AND y IN $inexpr + ORDER BY x DESC, y ASC; + " {2 1 2 2 1 4 2 1 5} + + do_execsql_test 2.$tn1.$tn2.11 " + SELECT a, x, y FROM t2, t3 WHERE a = 4 AND x = 1 AND y IN $inexpr + ORDER BY a, x DESC, y DESC; + " {4 1 5 4 1 4 4 1 2} + do_execsql_test 2.$tn1.$tn2.12 " + SELECT a, x, y FROM t2, t3 WHERE a = 2 AND x = 1 AND y IN $inexpr + ORDER BY x DESC, y DESC; + " {2 1 5 2 1 4 2 1 2} + } +} + +do_execsql_test 3.0 { + CREATE TABLE t7(x); + INSERT INTO t7 VALUES (1), (2), (3); + CREATE INDEX i7 ON t7(x); + + CREATE TABLE t8(y); + INSERT INTO t8 VALUES (1), (2), (3); +} + +foreach {tn idxdir sortdir sortdata} { + 1 ASC ASC {1 2 3} + 2 ASC DESC {3 2 1} + 3 DESC ASC {1 2 3} + 4 ASC DESC {3 2 1} +} { + + do_execsql_test 3.$tn " + DROP INDEX IF EXISTS i8; + CREATE UNIQUE INDEX i8 ON t8(y $idxdir); + SELECT x FROM t7 WHERE x IN (SELECT y FROM t8) ORDER BY x $sortdir; + " $sortdata +} + +finish_test diff --git a/test/tkt-5d863f876e.test b/test/tkt-5d863f876e.test index 0a9017d..86024e3 100644 --- a/test/tkt-5d863f876e.test +++ b/test/tkt-5d863f876e.test @@ -17,6 +17,8 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl +set ::testprefix tkt-5d863f876e +ifcapable !wal {finish_test ; return } do_multiclient_test tn { do_test $tn.1 { diff --git a/test/tkt-6bfb98dfc0.test b/test/tkt-6bfb98dfc0.test new file mode 100644 index 0000000..675a3fc --- /dev/null +++ b/test/tkt-6bfb98dfc0.test @@ -0,0 +1,61 @@ +# 2013 March 27 +# +# 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 implements regression tests for SQLite library. Specifically, +# it tests that ticket [6bfb98dfc0] +# +# The final INSERT in the script below reports that the database is +# corrupt (SQLITE_CORRUPT) and aborts even though the database is not +# corrupt. +# +# PRAGMA page_size=512; +# CREATE TABLE t1(x INTEGER PRIMARY KEY, y); +# INSERT INTO t1 VALUES(1,randomblob(400)); +# INSERT INTO t1 VALUES(2,randomblob(400)); +# INSERT INTO t1 SELECT x+2, randomblob(400) FROM t1; +# INSERT INTO t1 SELECT x+4, randomblob(400) FROM t1; +# INSERT INTO t1 SELECT x+8, randomblob(400) FROM t1; +# INSERT INTO t1 SELECT x+16, randomblob(400) FROM t1; +# INSERT INTO t1 SELECT x+32, randomblob(400) FROM t1; +# INSERT INTO t1 SELECT x+64, randomblob(400) FROM t1 WHERE x<10; +# CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.x=74 BEGIN +# DELETE FROM t1; +# INSERT INTO t1 VALUES(75, randomblob(400)); +# INSERT INTO t1 VALUES(76, randomblob(400)); +# END; +# INSERT INTO t1 VALUES(74, randomblob(400)); +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-6bfb98dfc0.100 { + db eval { + PRAGMA page_size=512; + CREATE TABLE t1(x INTEGER PRIMARY KEY, y); + INSERT INTO t1 VALUES(1,randomblob(400)); + INSERT INTO t1 VALUES(2,randomblob(400)); + INSERT INTO t1 SELECT x+2, randomblob(400) FROM t1; + INSERT INTO t1 SELECT x+4, randomblob(400) FROM t1; + INSERT INTO t1 SELECT x+8, randomblob(400) FROM t1; + INSERT INTO t1 SELECT x+16, randomblob(400) FROM t1; + INSERT INTO t1 SELECT x+32, randomblob(400) FROM t1; + INSERT INTO t1 SELECT x+64, randomblob(400) FROM t1 WHERE x<10; + CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.x=74 BEGIN + DELETE FROM t1; + INSERT INTO t1 VALUES(75, randomblob(400)); + INSERT INTO t1 VALUES(76, randomblob(400)); + END; + INSERT INTO t1 VALUES(74, randomblob(400)); + SELECT x, length(y) FROM t1 ORDER BY x; + } +} {75 400 76 400} + +finish_test diff --git a/test/tkt-78e04e52ea.test b/test/tkt-78e04e52ea.test index 9524d84..a664ceb 100644 --- a/test/tkt-78e04e52ea.test +++ b/test/tkt-78e04e52ea.test @@ -44,7 +44,7 @@ do_test tkt-78e04-1.4 { execsql { EXPLAIN QUERY PLAN SELECT * FROM "" WHERE "" LIKE 'abc%'; } -} {0 0 0 {SCAN TABLE (~500000 rows)}} +} {0 0 0 {SCAN TABLE USING COVERING INDEX i1 (~500000 rows)}} do_test tkt-78e04-1.5 { execsql { DROP TABLE ""; diff --git a/test/tkt-7a31705a7e6.test b/test/tkt-7a31705a7e6.test new file mode 100644 index 0000000..6470122 --- /dev/null +++ b/test/tkt-7a31705a7e6.test @@ -0,0 +1,26 @@ +# 2013 February 26 +# +# 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 implements regression tests for SQLite library. Specifically, +# it tests that ticket [7a31705a7e6c95d514e6f20a6900f436bbc9fed8] in the +# name resolver has been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test tkt-7a31705a7e6-1.1 { + CREATE TABLE t1 (a INTEGER PRIMARY KEY); + CREATE TABLE t2 (a INTEGER PRIMARY KEY, b INTEGER); + CREATE TABLE t2x (b INTEGER PRIMARY KEY); + SELECT t1.a FROM ((t1 JOIN t2 ON t1.a=t2.a) AS x JOIN t2x ON x.b=t2x.b) as y; +} {} + diff --git a/test/tkt-80ba201079.test b/test/tkt-80ba201079.test index 0122e95..ea0799b 100644 --- a/test/tkt-80ba201079.test +++ b/test/tkt-80ba201079.test @@ -17,7 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -set ::testprefix tkt-80ba2 +set ::testprefix tkt-80ba201079 do_test tkt-80ba2-100 { db eval { diff --git a/test/tkt-a7b7803e.test b/test/tkt-a7b7803e.test new file mode 100644 index 0000000..b617cf6 --- /dev/null +++ b/test/tkt-a7b7803e.test @@ -0,0 +1,84 @@ +# 2012 December 19 +# +# 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 implements regression tests for SQLite library. Specifically, +# it tests that ticket [a7b7803e8d1e8699cd8a460a38133b98892d2e17] has +# been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +do_test tkt-a7b7803e.1 { + db eval { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(0,'first'),(99,'fuzzy'); + SELECT (t1.a==0) AS x, b + FROM t1 + WHERE a=0 OR x; + } +} {1 first} +do_test tkt-a7b7803e.2 { + db eval { + SELECT a, (t1.b='fuzzy') AS x + FROM t1 + WHERE x + } +} {99 1} +do_test tkt-a7b7803e.3 { + db eval { + SELECT (a=99) AS x, (t1.b='fuzzy') AS y, * + FROM t1 + WHERE x AND y + } +} {1 1 99 fuzzy} +do_test tkt-a7b7803e.4 { + db eval { + SELECT (a=99) AS x, (t1.b='first') AS y, * + FROM t1 + WHERE x OR y + ORDER BY a + } +} {0 1 0 first 1 0 99 fuzzy} +do_test tkt-a7b7803e.5 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M, t1 N + WHERE x OR y + ORDER BY M.a, N.a + } +} {0 first 1 first 1 fuzzy 1 first 1 fuzzy 0 fuzzy} +do_test tkt-a7b7803e.6 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M, t1 N + WHERE x AND y + ORDER BY M.a, N.a + } +} {1 fuzzy 1 first} +do_test tkt-a7b7803e.7 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M JOIN t1 N ON x AND y + ORDER BY M.a, N.a + } +} {1 fuzzy 1 first} +do_test tkt-a7b7803e.8 { + db eval { + SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b + FROM t1 M JOIN t1 N ON x + ORDER BY M.a, N.a + } +} {1 fuzzy 1 first 1 fuzzy 0 fuzzy} + + +finish_test diff --git a/test/tkt-cbd054fa6b.test b/test/tkt-cbd054fa6b.test index 180acf5..51e0199 100644 --- a/test/tkt-cbd054fa6b.test +++ b/test/tkt-cbd054fa6b.test @@ -50,7 +50,7 @@ do_test tkt-cbd05-1.3 { WHERE idx = 't1_x' GROUP BY tbl,idx } -} {t1 t1_x { A B C D E F G H I}} +} {/t1 t1_x .[ ABCDEFGHI]{10}./} do_test tkt-cbd05-2.1 { db eval { @@ -82,6 +82,6 @@ do_test tkt-cbd05-2.3 { WHERE idx = 't1_x' GROUP BY tbl,idx } -} {t1 t1_x { A B C D E F G H I}} +} {/t1 t1_x .[ ABCDEFGHI]{10}./} finish_test diff --git a/test/tkt-fc7bd6358f.test b/test/tkt-fc7bd6358f.test new file mode 100644 index 0000000..a1b13c4 --- /dev/null +++ b/test/tkt-fc7bd6358f.test @@ -0,0 +1,78 @@ +# 2013 March 05 +# +# 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 implements regression tests for SQLite library. Specifically, +# it tests that ticket [fc7bd6358f]: +# +# The following SQL yields an incorrect result (zero rows) in all +# versions of SQLite between 3.6.14 and 3.7.15.2: +# +# CREATE TABLE t(textid TEXT); +# INSERT INTO t VALUES('12'); +# INSERT INTO t VALUES('34'); +# CREATE TABLE i(intid INTEGER PRIMARY KEY); +# INSERT INTO i VALUES(12); +# INSERT INTO i VALUES(34); +# +# SELECT t1.textid AS a, i.intid AS b, t2.textid AS c +# FROM t t1, i, t t2 +# WHERE t1.textid = i.intid +# AND t1.textid = t2.textid; +# +# The correct result should be two rows, one with 12|12|12 and the other +# with 34|34|34. With this bug, no rows are returned. Bisecting shows that +# this bug was introduced with check-in [dd4d67a67454] on 2009-04-23. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-fc7bd6358f.100 { + db eval { + CREATE TABLE t(textid TEXT); + INSERT INTO t VALUES('12'); + INSERT INTO t VALUES('34'); + CREATE TABLE i(intid INTEGER PRIMARY KEY); + INSERT INTO i VALUES(12); + INSERT INTO i VALUES(34); + } +} {} +unset -nocomplain from +unset -nocomplain where +unset -nocomplain a +unset -nocomplain b +foreach {a from} { + 1 {FROM t t1, i, t t2} + 2 {FROM i, t t1, t t2} + 3 {FROM t t1, t t2, i} +} { + foreach {b where} { + 1 {WHERE t1.textid=i.intid AND t1.textid=t2.textid} + 2 {WHERE i.intid=t1.textid AND t1.textid=t2.textid} + 3 {WHERE t1.textid=i.intid AND i.intid=t2.textid} + 4 {WHERE t1.textid=i.intid AND t2.textid=i.intid} + 5 {WHERE i.intid=t1.textid AND i.intid=t2.textid} + 6 {WHERE i.intid=t1.textid AND t2.textid=i.intid} + 7 {WHERE t1.textid=t2.textid AND i.intid=t2.textid} + 8 {WHERE t1.textid=t2.textid AND t2.textid=i.intid} + } { + do_test tkt-fc7bd6358f.110.$a.$b.1 { + db eval {PRAGMA automatic_index=ON} + db eval "SELECT t1.textid, i.intid, t2.textid $from $where" + } {12 12 12 34 34 34} + do_test tkt-fc7bd6358f.110.$a.$b.2 { + db eval {PRAGMA automatic_index=OFF} + db eval "SELECT t1.textid, i.intid, t2.textid $from $where" + } {12 12 12 34 34 34} + } +} + + +finish_test diff --git a/test/tkt2409.test b/test/tkt2409.test index 9ac6542..936f57c 100644 --- a/test/tkt2409.test +++ b/test/tkt2409.test @@ -107,7 +107,7 @@ do_test tkt2409-1.2 { integrity_check tkt2409-1.3 # Check that the transaction was rolled back. Because the INSERT -# statement in which the "I/O error" occured did not open a statement +# statement in which the "I/O error" occurred did not open a statement # transaction, SQLite had no choice but to roll back the transaction. # do_test tkt2409-1.4 { @@ -175,7 +175,7 @@ do_test tkt2409-3.2 { integrity_check tkt2409-3.3 # Check that the transaction was rolled back. Because the INSERT -# statement in which the "I/O error" occured did not open a statement +# statement in which the "I/O error" occurred did not open a statement # transaction, SQLite had no choice but to roll back the transaction. # do_test tkt2409-3.4 { diff --git a/test/tkt2822.test b/test/tkt2822.test index 281e5dc..d3512d3 100644 --- a/test/tkt2822.test +++ b/test/tkt2822.test @@ -208,12 +208,15 @@ do_test tkt2822-5.4 { # In "ORDER BY +b" the term is now an expression rather than # a label. It therefore matches by rule (3) instead of rule (2). +# +# 2013-04-13: This is busted. Changed to conform to PostgreSQL and +# MySQL and Oracle behavior. # do_test tkt2822-5.5 { execsql { SELECT a AS b FROM t3 ORDER BY +b; } -} {9 1} +} {1 9} # Tests for rule 2 in compound queries # @@ -273,11 +276,21 @@ do_test tkt2822-7.1 { SELECT * FROM t7 ORDER BY 0; } } {1 {1st ORDER BY term out of range - should be between 1 and 25}} -do_test tkt2822-7.2 { +do_test tkt2822-7.2.1 { catchsql { SELECT * FROM t7 ORDER BY 1, 0; } } {1 {2nd ORDER BY term out of range - should be between 1 and 25}} +do_test tkt2822-7.2.2 { + catchsql { + SELECT * FROM t7 ORDER BY 1, 26; + } +} {1 {2nd ORDER BY term out of range - should be between 1 and 25}} +do_test tkt2822-7.2.3 { + catchsql { + SELECT * FROM t7 ORDER BY 1, 65536; + } +} {1 {2nd ORDER BY term out of range - should be between 1 and 25}} do_test tkt2822-7.3 { catchsql { SELECT * FROM t7 ORDER BY 1, 2, 0; diff --git a/test/tkt3457.test b/test/tkt3457.test index 7b9a1b3..0475741 100644 --- a/test/tkt3457.test +++ b/test/tkt3457.test @@ -62,6 +62,14 @@ do_test tkt3457-1.1 { execsql COMMIT } {} +# Disable fchmod to make sure SQLite itself does not try to change the +# permission bits on us +# +catch { + test_syscall install fchmod + test_syscall fault 1 1 +} + do_test tkt3457-1.2 { forcecopy bak.db-journal test.db-journal file attributes test.db-journal -permissions --------- @@ -84,4 +92,10 @@ do_test tkt3457-1.5 { catchsql { SELECT * FROM t1 } } {0 {1 2 3 4 5 6}} +# Reenable fchmod +catch { + test_syscall uninstall + test_syscall fault 0 0 +} + finish_test diff --git a/test/tkt3527.test b/test/tkt3527.test index d9b1dad..da3d05a 100644 --- a/test/tkt3527.test +++ b/test/tkt3527.test @@ -52,8 +52,8 @@ do_test tkt3527-1.1 { INSERT INTO Element VALUES(5,'Elem5'); INSERT INTO ElemOr Values(3,4); INSERT INTO ElemOr Values(3,5); - INSERT INTO ElemAnd VALUES(1,3,1,1,1); - INSERT INTO ElemAnd VALUES(1,2,1,1,1); + INSERT INTO ElemAnd VALUES(1,3,'a','b','c'); + INSERT INTO ElemAnd VALUES(1,2,'x','y','z'); CREATE VIEW ElemView1 AS SELECT @@ -112,12 +112,12 @@ do_test tkt3527-1.1 { SELECT * FROM ElemView1; } -} {1 1 Elem1 2 1 1 1 0 0 1 1 Elem1 3 1 1 1 0 0 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} +} {1 1 Elem1 2 x y z 0 0 1 1 Elem1 3 a b c 0 0 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} do_test tkt3527-1.2 { db eval { SELECT * FROM ElemView2; } -} {1 1 Elem1 2 1 1 1 0 0 1 1 Elem1 3 1 1 1 0 0 1.3 3 Elem3 4 {} {} {} 1 1 1.3 3 Elem3 5 {} {} {} 1 1 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} +} {1 1 Elem1 2 x y z 0 0 1 1 Elem1 3 a b c 0 0 1.3 3 Elem3 4 {} {} {} 1 1 1.3 3 Elem3 5 {} {} {} 1 1 3 3 Elem3 4 {} {} {} 0 1 3 3 Elem3 5 {} {} {} 0 1} finish_test diff --git a/test/tkt3762.test b/test/tkt3762.test index c24041c..424b1e6 100644 --- a/test/tkt3762.test +++ b/test/tkt3762.test @@ -10,8 +10,8 @@ #*********************************************************************** # # Ticket #3762: Make sure that an incremental vacuum that reduces the -# size of the database file such that a pointer-map page is elemented -# can be correctly rolled back. +# size of the database file such that if a pointer-map page is eliminated +# it can be correctly rolled back. # # That ticket #3762 has been fixed has already been verified by the # savepoint6.test test script. But this script is simplier and a diff --git a/test/transitive1.test b/test/transitive1.test new file mode 100644 index 0000000..ff81af6 --- /dev/null +++ b/test/transitive1.test @@ -0,0 +1,50 @@ +# 2013 April 17 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is testing of transitive WHERE clause constraints +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test transitive1-100 { + CREATE TABLE t1(a TEXT, b TEXT, c TEXT COLLATE NOCASE); + INSERT INTO t1 VALUES('abc','abc','Abc'); + INSERT INTO t1 VALUES('def','def','def'); + INSERT INTO t1 VALUES('ghi','ghi','GHI'); + CREATE INDEX t1a1 ON t1(a); + CREATE INDEX t1a2 ON t1(a COLLATE nocase); + + SELECT * FROM t1 WHERE a=b AND c=b AND c='DEF'; +} {def def def} +do_execsql_test transitive1-110 { + SELECT * FROM t1 WHERE a=b AND c=b AND c>='DEF' ORDER BY +a; +} {def def def ghi ghi GHI} +do_execsql_test transitive1-120 { + SELECT * FROM t1 WHERE a=b AND c=b AND c<='DEF' ORDER BY +a; +} {abc abc Abc def def def} + +do_execsql_test transitive1-200 { + CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT); + INSERT INTO t2 VALUES(100,100,100); + INSERT INTO t2 VALUES(20,20,20); + INSERT INTO t2 VALUES(3,3,3); + + SELECT * FROM t2 WHERE a=b AND c=b AND c=20; +} {20 20 20} +do_execsql_test transitive1-210 { + SELECT * FROM t2 WHERE a=b AND c=b AND c>=20 ORDER BY +a; +} {3 3 3 20 20 20} +do_execsql_test transitive1-220 { + SELECT * FROM t2 WHERE a=b AND c=b AND c<=20 ORDER BY +a; +} {20 20 20 100 100 100} + +finish_test diff --git a/test/trigger1.test b/test/trigger1.test index 9d917bd..1ebe12c 100644 --- a/test/trigger1.test +++ b/test/trigger1.test @@ -11,20 +11,20 @@ # with the database COMMIT/ROLLBACK logic. # # 1. CREATE and DROP TRIGGER tests -# trig-1.1: Error if table does not exist -# trig-1.2: Error if trigger already exists -# trig-1.3: Created triggers are deleted if the transaction is rolled back -# trig-1.4: DROP TRIGGER removes trigger -# trig-1.5: Dropped triggers are restored if the transaction is rolled back -# trig-1.6: Error if dropped trigger doesn't exist -# trig-1.7: Dropping the table automatically drops all triggers -# trig-1.8: A trigger created on a TEMP table is not inserted into sqlite_master -# trig-1.9: Ensure that we cannot create a trigger on sqlite_master -# trig-1.10: -# trig-1.11: -# trig-1.12: Ensure that INSTEAD OF triggers cannot be created on tables -# trig-1.13: Ensure that AFTER triggers cannot be created on views -# trig-1.14: Ensure that BEFORE triggers cannot be created on views +# trigger1-1.1: Error if table does not exist +# trigger1-1.2: Error if trigger already exists +# trigger1-1.3: Created triggers are deleted if the transaction is rolled back +# trigger1-1.4: DROP TRIGGER removes trigger +# trigger1-1.5: Dropped triggers are restored if the transaction is rolled back +# trigger1-1.6: Error if dropped trigger doesn't exist +# trigger1-1.7: Dropping the table automatically drops all triggers +# trigger1-1.8: A trigger created on a TEMP table is not inserted into sqlite_master +# trigger1-1.9: Ensure that we cannot create a trigger on sqlite_master +# trigger1-1.10: +# trigger1-1.11: +# trigger1-1.12: Ensure that INSTEAD OF triggers cannot be created on tables +# trigger1-1.13: Ensure that AFTER triggers cannot be created on views +# trigger1-1.14: Ensure that BEFORE triggers cannot be created on views # set testdir [file dirname $argv0] @@ -210,7 +210,7 @@ do_test trigger1-1.12 { delete from t1 WHERE a=old.a+2; end; } -} {1 {cannot create INSTEAD OF trigger on table: main.t1}} +} {1 {cannot create INSTEAD OF trigger on table: t1}} ifcapable view { # Ensure that we cannot create BEFORE triggers on views @@ -221,7 +221,7 @@ do_test trigger1-1.13 { delete from t1 WHERE a=old.a+2; end; } -} {1 {cannot create BEFORE trigger on view: main.v1}} +} {1 {cannot create BEFORE trigger on view: v1}} # Ensure that we cannot create AFTER triggers on views do_test trigger1-1.14 { catchsql { @@ -231,7 +231,7 @@ do_test trigger1-1.14 { delete from t1 WHERE a=old.a+2; end; } -} {1 {cannot create AFTER trigger on view: main.v1}} +} {1 {cannot create AFTER trigger on view: v1}} } ;# ifcapable view # Check for memory leaks in the trigger parser @@ -265,32 +265,32 @@ ifcapable tempdb { END; } } {0 {}} - do_test trigger-3.2 { + do_test trigger1-3.2 { catchsql { INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {1 {no such table: main.t2}} - do_test trigger-3.3 { + do_test trigger1-3.3 { db close set rc [catch {sqlite3 db test.db} err] if {$rc} {lappend rc $err} set rc } {0} - do_test trigger-3.4 { + do_test trigger1-3.4 { catchsql { INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {1 {no such table: main.t2}} - do_test trigger-3.5 { + do_test trigger1-3.5 { catchsql { CREATE TEMP TABLE t2(x,y); INSERT INTO t1 VALUES(1,2); SELECT * FROM t2; } } {1 {no such table: main.t2}} - do_test trigger-3.6.1 { + do_test trigger1-3.6.1 { catchsql { DROP TRIGGER r1; CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN @@ -300,7 +300,7 @@ ifcapable tempdb { SELECT * FROM t2; } } {0 {1 2 200 100}} - do_test trigger-3.6.2 { + do_test trigger1-3.6.2 { catchsql { DROP TRIGGER r1; DELETE FROM t1; @@ -312,7 +312,7 @@ ifcapable tempdb { SELECT * FROM t2; } } {0 {1 2}} - do_test trigger-3.7 { + do_test trigger1-3.7 { execsql { DROP TABLE t2; CREATE TABLE t2(x,y); @@ -320,7 +320,7 @@ ifcapable tempdb { } } {} - # There are two versions of trigger-3.8 and trigger-3.9. One that uses + # There are two versions of trigger1-3.8 and trigger1-3.9. One that uses # compound SELECT statements, and another that does not. ifcapable compound { do_test trigger1-3.8 { @@ -423,6 +423,7 @@ do_test trigger1-6.2 { do_test trigger1-6.3 { catchsql {DELETE FROM t2} } {1 {deletes are not permitted}} +verify_ex_errcode trigger1-6.3b SQLITE_CONSTRAINT_TRIGGER do_test trigger1-6.4 { execsql {SELECT * FROM t2} } {3 4 7 8} @@ -446,7 +447,7 @@ do_test trigger1-6.8 { execsql {SELECT * FROM t2} } {3 4 7 8} -integrity_check trigger-7.1 +integrity_check trigger1-7.1 # Check to make sure the name of a trigger can be quoted so that keywords # can be used as trigger names. Ticket #468 @@ -491,7 +492,7 @@ do_test trigger1-8.6 { ifcapable conflict { # Make sure REPLACE works inside of triggers. # - # There are two versions of trigger-9.1 and trigger-9.2. One that uses + # There are two versions of trigger1-9.1 and trigger1-9.2. One that uses # compound SELECT statements, and another that does not. ifcapable compound { do_test trigger1-9.1 { @@ -612,7 +613,7 @@ ifcapable tempdb&&attach { SELECT * FROM insert_log; } } {main 11 12 13 temp 14 15 16 aux 17 18 19} - do_test trigger1-10.8 { + do_test trigger1-10.9 { # Drop and re-create the insert_log table in a different database. Note # that we can change the column names because the trigger programs don't # use them explicitly. diff --git a/test/trigger3.test b/test/trigger3.test index 34d1970..fe91b9d 100644 --- a/test/trigger3.test +++ b/test/trigger3.test @@ -45,6 +45,7 @@ do_test trigger3-1.1 { INSERT INTO tbl VALUES (1, 5, 6); } } {1 {Trigger abort}} +verify_ex_errcode trigger3-1.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-1.2 { execsql { SELECT * FROM tbl; @@ -63,6 +64,7 @@ do_test trigger3-2.1 { INSERT INTO tbl VALUES (2, 5, 6); } } {1 {Trigger fail}} +verify_ex_errcode trigger3-2.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-2.2 { execsql { SELECT * FROM tbl; @@ -77,6 +79,7 @@ do_test trigger3-3.1 { INSERT INTO tbl VALUES (3, 5, 6); } } {1 {Trigger rollback}} +verify_ex_errcode trigger3-3.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-3.2 { execsql { SELECT * FROM tbl; @@ -92,6 +95,7 @@ do_test trigger3-3.3 { INSERT INTO tbl VALUES (3, 9, 10); } } {1 {Trigger rollback}} +verify_ex_errcode trigger3-3.3b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-3.4 { execsql {SELECT * FROM tbl} } {} @@ -172,6 +176,7 @@ do_test trigger3-7.1 { INSERT INTO tbl_view VALUES(1, 2, 3); } } {1 {View rollback}} +verify_ex_errcode trigger3-7.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-7.2 { catchsql { INSERT INTO tbl_view VALUES(2, 2, 3); @@ -182,6 +187,7 @@ do_test trigger3-7.3 { INSERT INTO tbl_view VALUES(3, 2, 3); } } {1 {View abort}} +verify_ex_errcode trigger3-7.3b SQLITE_CONSTRAINT_TRIGGER } ;# ifcapable view diff --git a/test/triggerA.test b/test/triggerA.test index 0bc017f..821e2d9 100644 --- a/test/triggerA.test +++ b/test/triggerA.test @@ -192,6 +192,13 @@ do_test triggerA-2.10 { SELECT * FROM result4 ORDER BY a; } } {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504} +do_test triggerA-2.11 { + db eval { + DELETE FROM result4; + UPDATE v5 SET b = main.v5.b+9900000 WHERE main.v5.x BETWEEN 3 AND 5; + SELECT * FROM result4 ORDER BY a; + } +} {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504} # Only run the reamining tests if memory debugging is turned on. # diff --git a/test/triggerC.test b/test/triggerC.test index 12a5e4a..8d98487 100644 --- a/test/triggerC.test +++ b/test/triggerC.test @@ -222,7 +222,7 @@ foreach {n tdefn rc} { execsql $tdefn catchsql { INSERT INTO t2 VALUES(10); - SELECT * FROM t2; + SELECT * FROM t2 ORDER BY rowid; } } $rc } @@ -547,7 +547,7 @@ foreach {n insert log} { eval concat [execsql " DELETE FROM log; $insert ; - SELECT * FROM log; + SELECT * FROM log ORDER BY rowid; "] } [join $log " "] } @@ -584,8 +584,8 @@ foreach {n dml t5g t5} { execsql " BEGIN; $dml ; - SELECT * FROM t5g; - SELECT * FROM t5; + SELECT * FROM t5g ORDER BY rowid; + SELECT * FROM t5 ORDER BY rowid; ROLLBACK; " } [concat $t5g $t5] @@ -611,8 +611,8 @@ foreach {n dml t5g t5} { execsql " BEGIN; $dml ; - SELECT * FROM t5g; - SELECT * FROM t5; + SELECT * FROM t5g ORDER BY rowid; + SELECT * FROM t5 ORDER BY rowid; ROLLBACK; " } [concat $t5g $t5] @@ -633,8 +633,8 @@ foreach {n dml t5g t5} { execsql " BEGIN; $dml ; - SELECT * FROM t5g; - SELECT * FROM t5; + SELECT * FROM t5g ORDER BY rowid; + SELECT * FROM t5 ORDER BY rowid; ROLLBACK; " } [concat $t5g $t5] @@ -949,6 +949,48 @@ do_catchsql_test triggerC-13.2 { UPDATE t12 SET a=a+1, b=b+1; } {1 {too many levels of trigger recursion}} +#------------------------------------------------------------------------- +# The following tests seek to verify that constant values (i.e. literals) +# are not factored out of loops within trigger programs. SQLite does +# not factor constants out of loops within trigger programs as it may only +# do so in code generated before the first table or index is opened. And +# by the time a trigger program is coded, at least one table or index has +# always been opened. +# +# At one point, due to a bug allowing constant factoring within triggers, +# the following SQL would produce the wrong result. +# +set SQL { + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(a, c); + CREATE INDEX i2 ON t1(b, c); + INSERT INTO t1 VALUES(1, 2, 3); + + CREATE TABLE t2(e, f); + CREATE INDEX i3 ON t2(e); + INSERT INTO t2 VALUES(1234567, 3); + + CREATE TABLE empty(x); + CREATE TABLE not_empty(x); + INSERT INTO not_empty VALUES(2); + + CREATE TABLE t4(x); + CREATE TABLE t5(g, h, i); + + CREATE TRIGGER trig BEFORE INSERT ON t4 BEGIN + INSERT INTO t5 SELECT * FROM t1 WHERE + (a IN (SELECT x FROM empty) OR b IN (SELECT x FROM not_empty)) + AND c IN (SELECT f FROM t2 WHERE e=1234567); + END; + + INSERT INTO t4 VALUES(0); + SELECT * FROM t5; +} +reset_db +do_execsql_test triggerC-14.1 $SQL {1 2 3} +reset_db +optimization_control db factor-constants 0 +do_execsql_test triggerC-14.2 $SQL {1 2 3} finish_test diff --git a/test/unique.test b/test/unique.test index 56c49ee..eac19b5 100644 --- a/test/unique.test +++ b/test/unique.test @@ -48,6 +48,7 @@ do_test unique-1.3 { INSERT INTO t1(a,b,c) VALUES(1,3,4) } } {1 {column a is not unique}} +verify_ex_errcode unique-1.3b SQLITE_CONSTRAINT_UNIQUE do_test unique-1.4 { execsql { SELECT * FROM t1 ORDER BY a; @@ -58,6 +59,7 @@ do_test unique-1.5 { INSERT INTO t1(a,b,c) VALUES(3,2,4) } } {1 {column b is not unique}} +verify_ex_errcode unique-1.5b SQLITE_CONSTRAINT_UNIQUE do_test unique-1.6 { execsql { SELECT * FROM t1 ORDER BY a; @@ -99,6 +101,7 @@ do_test unique-2.3 { INSERT INTO t2 VALUES(1,5); } } {1 {column a is not unique}} +verify_ex_errcode unique-2.3b SQLITE_CONSTRAINT_UNIQUE do_test unique-2.4 { catchsql { SELECT * FROM t2 ORDER BY a @@ -125,6 +128,7 @@ do_test unique-2.8 { CREATE UNIQUE INDEX i2 ON t2(a); } } {1 {indexed columns are not unique}} +verify_ex_errcode unique-2.8b SQLITE_CONSTRAINT_UNIQUE do_test unique-2.9 { catchsql { CREATE INDEX i2 ON t2(a); @@ -163,6 +167,7 @@ do_test unique-3.4 { SELECT * FROM t3 ORDER BY a,b,c,d; } } {1 {columns a, c, d are not unique}} +verify_ex_errcode unique-3.4b SQLITE_CONSTRAINT_UNIQUE integrity_check unique-3.5 # Make sure NULLs are distinct as far as the UNIQUE tests are @@ -217,6 +222,7 @@ do_test unique-4.9 { do_test unique-4.10 { catchsql {CREATE UNIQUE INDEX i4c ON t4(b)} } {1 {indexed columns are not unique}} +verify_ex_errcode unique-4.10b SQLITE_CONSTRAINT_UNIQUE integrity_check unique-4.99 # Test the error message generation logic. In particular, make sure we @@ -249,5 +255,7 @@ do_test unique-5.2 { INSERT INTO t5 VALUES(1,2,3,4,5,6); } } {1 {columns first_column_with_long_name, second_column_with_long_name, third_column_with_long_name, fourth_column_with_long_name, fifth_column_with_long_name, sixth_column_with_long_name are not unique}} +verify_ex_errcode unique-5.2b SQLITE_CONSTRAINT_UNIQUE + finish_test diff --git a/test/unordered.test b/test/unordered.test index 6c7c2bb..4aa8310 100644 --- a/test/unordered.test +++ b/test/unordered.test @@ -51,7 +51,7 @@ foreach idxmode {ordered unordered} { 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} 4 "SELECT max(a) FROM t1" {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} - {0 0 0 {SEARCH TABLE t1 (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} 5 "SELECT group_concat(b) FROM t1 GROUP BY a" {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}} diff --git a/test/view.test b/test/view.test index b444090..779f77b 100644 --- a/test/view.test +++ b/test/view.test @@ -576,4 +576,39 @@ do_test view-20.1 { } } {} +# Ticket [d58ccbb3f1b]: Prevent Table.nRef overflow. +db close +sqlite3 db :memory: +do_test view-21.1 { + catchsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(5); + CREATE VIEW v1 AS SELECT x*2 FROM t1; + CREATE VIEW v2 AS SELECT * FROM v1 UNION SELECT * FROM v1; + CREATE VIEW v4 AS SELECT * FROM v2 UNION SELECT * FROM v2; + CREATE VIEW v8 AS SELECT * FROM v4 UNION SELECT * FROM v4; + CREATE VIEW v16 AS SELECT * FROM v8 UNION SELECT * FROM v8; + CREATE VIEW v32 AS SELECT * FROM v16 UNION SELECT * FROM v16; + CREATE VIEW v64 AS SELECT * FROM v32 UNION SELECT * FROM v32; + CREATE VIEW v128 AS SELECT * FROM v64 UNION SELECT * FROM v64; + CREATE VIEW v256 AS SELECT * FROM v128 UNION SELECT * FROM v128; + CREATE VIEW v512 AS SELECT * FROM v256 UNION SELECT * FROM v256; + CREATE VIEW v1024 AS SELECT * FROM v512 UNION SELECT * FROM v512; + CREATE VIEW v2048 AS SELECT * FROM v1024 UNION SELECT * FROM v1024; + CREATE VIEW v4096 AS SELECT * FROM v2048 UNION SELECT * FROM v2048; + CREATE VIEW v8192 AS SELECT * FROM v4096 UNION SELECT * FROM v4096; + CREATE VIEW v16384 AS SELECT * FROM v8192 UNION SELECT * FROM v8192; + CREATE VIEW v32768 AS SELECT * FROM v16384 UNION SELECT * FROM v16384; + CREATE VIEW vx AS SELECT * FROM v32768 UNION SELECT * FROM v32768; + } +} {1 {too many references to "v1": max 65535}} +ifcapable progress { + do_test view-21.2 { + db progress 1000 {expr 1} + catchsql { + SELECT * FROM v32768; + } + } {1 interrupted} +} + finish_test diff --git a/test/vtab1.test b/test/vtab1.test index 3409943..1f17e53 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -1091,12 +1091,54 @@ do_test vtab1.13-3 { } {15 {} 16} +do_test vtab1-14.001 { + execsql {SELECT rowid, * FROM echo_c WHERE +rowid IN (1,2,3)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.002 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid IN (1,2,3)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.003 { + execsql {SELECT rowid, * FROM echo_c WHERE +rowid IN (0,1,5,2,'a',3,NULL)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.004 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid IN (0,1,5,'a',2,3,NULL)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.005 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,1,5,'a',2,3)} +} {} +do_test vtab1-14.006 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,5,'a',2,3)} +} {1 3 G H} +do_test vtab1-14.007 { + execsql {SELECT rowid, * FROM echo_c WHERE +rowid NOT IN (0,5,'a',2,3,NULL)} +} {} +do_test vtab1-14.008 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,5,'a',2,3,NULL)} +} {} +do_test vtab1-14.011 { + execsql {SELECT * FROM echo_c WHERE +a IN (1,3,8,'x',NULL,15,24)} +} {3 G H 15 {} 16} +do_test vtab1-14.012 { + execsql {SELECT * FROM echo_c WHERE a IN (1,3,8,'x',NULL,15,24)} +} {3 G H 15 {} 16} +do_test vtab1-14.013 { + execsql {SELECT * FROM echo_c WHERE a NOT IN (1,8,'x',15,24)} +} {3 G H} +do_test vtab1-14.014 { + execsql {SELECT * FROM echo_c WHERE a NOT IN (1,8,'x',NULL,15,24)} +} {} +do_test vtab1-14.015 { + execsql {SELECT * FROM echo_c WHERE +a NOT IN (1,8,'x',NULL,15,24)} +} {} + + + do_test vtab1-14.1 { execsql { DELETE FROM c } set echo_module "" execsql { SELECT * FROM echo_c WHERE rowid IN (1, 2, 3) } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}] +} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c' WHERE rowid = .} 1/} do_test vtab1-14.2 { set echo_module "" @@ -1114,7 +1156,7 @@ do_test vtab1-14.4 { set echo_module "" execsql { SELECT * FROM echo_c WHERE a IN (1, 2) } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}] +} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE a = .} xFilter {SELECT rowid, . FROM 'c' WHERE a = .} 1/} do_test vtab1-15.1 { execsql { @@ -1293,4 +1335,44 @@ do_test 19.3 { db2 close } {} +#------------------------------------------------------------------------- +# Test that the bug fixed by [b0c1ba655d69] really is fixed. +# +do_execsql_test 20.1 { + CREATE TABLE t7 (a, b); + CREATE TABLE t8 (c, d); + CREATE INDEX i2 ON t7(a); + CREATE INDEX i3 ON t7(b); + CREATE INDEX i4 ON t8(c); + CREATE INDEX i5 ON t8(d); + + CREATE VIRTUAL TABLE t7v USING echo(t7); + CREATE VIRTUAL TABLE t8v USING echo(t8); +} + +do_test 20.2 { + for {set i 0} {$i < 1000} {incr i} { + db eval {INSERT INTO t7 VALUES($i, $i)} + db eval {INSERT INTO t8 VALUES($i, $i)} + } +} {} + +do_execsql_test 20.3 { + SELECT a, b FROM ( + SELECT a, b FROM t7 WHERE a=11 OR b=12 + UNION ALL + SELECT c, d FROM t8 WHERE c=5 OR d=6 + ) + ORDER BY 1, 2; +} {5 5 6 6 11 11 12 12} + +do_execsql_test 20.4 { + SELECT a, b FROM ( + SELECT a, b FROM t7v WHERE a=11 OR b=12 + UNION ALL + SELECT c, d FROM t8v WHERE c=5 OR d=6 + ) + ORDER BY 1, 2; +} {5 5 6 6 11 11 12 12} + finish_test diff --git a/test/wal.test b/test/wal.test index 24ce5f8..c8078a1 100644 --- a/test/wal.test +++ b/test/wal.test @@ -727,6 +727,9 @@ do_test wal-11.9 { list [expr [file size test.db]/1024] [log_deleted test.db-wal] } {37 1} sqlite3_wal db test.db +set nWal 39 +if {[permutation]!="mmap"} {set nWal 37} +ifcapable !mmap {set nWal 37} do_test wal-11.10 { execsql { PRAGMA cache_size = 10; @@ -735,7 +738,7 @@ do_test wal-11.10 { SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [wal_file_size 37 1024]] +} [list 37 [wal_file_size $nWal 1024]] do_test wal-11.11 { execsql { SELECT count(*) FROM t1; @@ -745,7 +748,7 @@ do_test wal-11.11 { } {32 16} do_test wal-11.12 { list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [wal_file_size 37 1024]] +} [list 37 [wal_file_size $nWal 1024]] do_test wal-11.13 { execsql { INSERT INTO t1 VALUES( blob(900) ); @@ -755,7 +758,7 @@ do_test wal-11.13 { } {17 ok} do_test wal-11.14 { list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [wal_file_size 37 1024]] +} [list 37 [wal_file_size $nWal 1024]] #------------------------------------------------------------------------- @@ -1509,10 +1512,10 @@ do_test wal-23.3 { faultsim_restore_and_reopen execsql { SELECT * FROM t1 } } {1 2 3 4} -set nPage [expr 2+$AUTOVACUUM] do_test wal-23.4 { set ::log -} [list SQLITE_OK "Recovered $nPage frames from WAL file $walfile"] +} [list SQLITE_NOTICE_RECOVER_WAL \ + "recovered 2 frames from WAL file $walfile"] ifcapable autovacuum { diff --git a/test/wal5.test b/test/wal5.test index 6eceed5..68750f1 100644 --- a/test/wal5.test +++ b/test/wal5.test @@ -235,7 +235,16 @@ foreach {testprefix do_wal_checkpoint} { do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {} do_test 2.3.$tn.6 { file_page_counts } {1 4 1 4} do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 4 3} - do_test 2.3.$tn.8 { file_page_counts } {1 4 2 4} + + # The checkpoint above only writes page 1 of the db file. The other + # page (page 2) is locked by the read-transaction opened by the + # [sql2] commmand above. So normally, the db is 1 page in size here. + # However, in mmap() mode, the db is pre-allocated to 2 pages at the + # start of the checkpoint, even though page 2 cannot be written. + set nDb 2 + if {[permutation]!="mmap"} {set nDb 1} + ifcapable !mmap {set nDb 1} + do_test 2.3.$tn.8 { file_page_counts } [list $nDb 4 2 4] } # Check that checkpoints block on the correct locks. And respond correctly @@ -343,4 +352,3 @@ foreach {testprefix do_wal_checkpoint} { finish_test - diff --git a/test/wal8.test b/test/wal8.test index 4b97de7..3399538 100644 --- a/test/wal8.test +++ b/test/wal8.test @@ -26,6 +26,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix wal8 +ifcapable !wal {finish_test ; return } db close forcedelete test.db test.db-wal diff --git a/test/wal9.test b/test/wal9.test new file mode 100644 index 0000000..ae2a52b --- /dev/null +++ b/test/wal9.test @@ -0,0 +1,94 @@ +# 2012 October 15 +# +# 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 test case tests that a problem causing a failing assert() has +# been fixed. The problem occurred if a writer process with a subset +# of the *shm file mapped rolled back a transaction begun after the +# entire WAL file was checkpointed into the db file (i.e. a transaction +# that would have restarted the WAL file from the beginning). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix wal9 + +sqlite3 db2 test.db + +do_execsql_test 1.0 { + PRAGMA page_size = 1024; + PRAGMA journal_mode = WAL; + PRAGMA wal_autocheckpoint = 0; + CREATE TABLE t(x); +} {wal 0} + +do_test 1.1 { + execsql "SELECT * FROM t" db2 +} {} + +do_execsql_test 1.2 { + BEGIN; + INSERT INTO t VALUES(randomblob(100)); + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + + INSERT INTO t SELECT randomblob(100) FROM t; + INSERT INTO t SELECT randomblob(100) FROM t; + COMMIT; +} {} + +# Check file sizes are as expected. The real requirement here is that +# the *shm file is now more than one chunk (>32KiB). +# +# The sizes of various files are slightly different in normal and +# auto-vacuum mode. +do_test 1.3 { file size test.db } {1024} +do_test 1.4 { expr {[file size test.db-wal]>(1500*1024)} } {1} +do_test 1.5 { expr {[file size test.db-shm]>32768} } {1} +do_test 1.6 { + foreach {a b c} [db eval {PRAGMA wal_checkpoint}] break + list [expr {$a==0}] [expr {$b>14500}] [expr {$c>14500}] [expr {$b==$c}] +} {1 1 1 1} + +# At this point connection [db2] has mapped the first 32KB of the *shm file +# only. Because the entire WAL file has been checkpointed, it is not +# necessary to map any more of the *-shm file to read or write the database +# (since all data will be read directly from the db file). +# +# However, at one point if a transaction that had not yet written to the +# WAL file was rolled back an assert() attempting to verify that the entire +# *-shm file was mapped would fail. If NDEBUG was defined (and the assert() +# disabled) this bug caused SQLite to ignore the return code of a mmap() +# call. +# +do_test 1.7 { + execsql { + BEGIN; + INSERT INTO t VALUES('hello'); + ROLLBACK; + } db2 +} {} +db2 close + +finish_test diff --git a/test/walfault.test b/test/walfault.test index 6f9aedd..4a9d98a 100644 --- a/test/walfault.test +++ b/test/walfault.test @@ -548,6 +548,44 @@ do_faultsim_test walfault-14 -prep { set nRow [db eval {SELECT count(*) FROM abc}] if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" } } -finish_test + +#------------------------------------------------------------------------- +# Test fault-handling when switching out of exclusive-locking mode. +# +do_test walfault-14-pre { + faultsim_delete_and_reopen + execsql { + PRAGMA auto_vacuum = 0; + PRAGMA journal_mode = WAL; + BEGIN; + CREATE TABLE abc(a PRIMARY KEY); + INSERT INTO abc VALUES(randomblob(1500)); + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } + faultsim_save_and_close +} {} +do_faultsim_test walfault-14 -prep { + faultsim_restore_and_reopen + breakpoint + execsql { + SELECT count(*) FROM abc; + PRAGMA locking_mode = exclusive; + BEGIN; + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } +} -body { + db eval { + PRAGMA locking_mode = normal; + BEGIN; + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } +} -test { + faultsim_integrity_check + set nRow [db eval {SELECT count(*) FROM abc}] + if {$nRow!=3 && $nRow!=4} { error "Bad db content" } +} finish_test diff --git a/test/where.test b/test/where.test index 3826a5f..2dbc283 100644 --- a/test/where.test +++ b/test/where.test @@ -379,11 +379,26 @@ ifcapable subquery { SELECT * FROM t1 WHERE rowid+0 IN (1,2,3,1234) order by 1; } } {1 0 4 2 1 9 3 1 16 102} - do_test where-5.3 { + do_test where-5.3a { count { SELECT * FROM t1 WHERE w IN (-1,1,2,3) order by 1; } - } {1 0 4 2 1 9 3 1 16 14} + } {1 0 4 2 1 9 3 1 16 13} + do_test where-5.3b { + count { + SELECT * FROM t1 WHERE w IN (3,-1,1,2) order by 1; + } + } {1 0 4 2 1 9 3 1 16 13} + do_test where-5.3c { + count { + SELECT * FROM t1 WHERE w IN (3,2,-1,1,2) order by 1; + } + } {1 0 4 2 1 9 3 1 16 13} + do_test where-5.3d { + count { + SELECT * FROM t1 WHERE w IN (-1,1,2,3) order by 1 DESC; + } + } {3 1 16 2 1 9 1 0 4 12} do_test where-5.4 { count { SELECT * FROM t1 WHERE w+0 IN (-1,1,2,3) order by 1; @@ -452,6 +467,30 @@ ifcapable subquery { SELECT * FROM t1 WHERE x IN (1,7) AND y IN (9,16) ORDER BY 1; } } {2 1 9 3 1 16 11} + do_test where-5.100 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x, y + } + } {2 1 9 54 5 3025 62 5 3969} + do_test where-5.101 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x DESC, y DESC + } + } {62 5 3969 54 5 3025 2 1 9} + do_test where-5.102 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x DESC, y + } + } {54 5 3025 62 5 3969 2 1 9} + do_test where-5.103 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x, y DESC + } + } {2 1 9 62 5 3969 54 5 3025} } # This procedure executes the SQL. Then it checks to see if the OP_Sort @@ -511,11 +550,16 @@ do_test where-6.7 { } } {1 100 4 2 99 9 3 98 16 nosort} ifcapable subquery { - do_test where-6.8 { + do_test where-6.8a { cksort { SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a LIMIT 3 } - } {1 100 4 2 99 9 3 98 16 sort} + } {1 100 4 2 99 9 3 98 16 nosort} + do_test where-6.8b { + cksort { + SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a DESC LIMIT 3 + } + } {9 92 100 7 94 64 5 96 36 nosort} } do_test where-6.9.1 { cksort { @@ -1079,6 +1123,7 @@ do_test where-13.12 { # When optimizing out ORDER BY clauses, make sure that trailing terms # of the ORDER BY clause do not reference other tables in a join. # +if {[permutation] != "no_optimization"} { do_test where-14.1 { execsql { CREATE TABLE t8(a INTEGER PRIMARY KEY, b TEXT UNIQUE); @@ -1088,34 +1133,34 @@ do_test where-14.1 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b } -} {1/4 1/1 4/4 4/1 sort} +} {1/4 1/1 4/4 4/1 nosort} do_test where-14.2 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b DESC } -} {1/1 1/4 4/1 4/4 sort} +} {1/1 1/4 4/1 4/4 nosort} do_test where-14.3 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b } -} {1/1 1/4 4/1 4/4 nosort} +} {1/4 1/1 4/4 4/1 nosort} do_test where-14.4 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b DESC } -} {1/1 1/4 4/1 4/4 nosort} +} {1/4 1/1 4/4 4/1 nosort} do_test where-14.5 { # This test case changed from "nosort" to "sort". See ticket 2a5629202f. cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b } -} {4/1 4/4 1/1 1/4 sort} +} {/4/[14] 4/[14] 1/[14] 1/[14] sort/} do_test where-14.6 { # This test case changed from "nosort" to "sort". See ticket 2a5629202f. cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b DESC } -} {4/1 4/4 1/1 1/4 sort} +} {/4/[14] 4/[14] 1/[14] 1/[14] sort/} do_test where-14.7 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b @@ -1130,7 +1175,7 @@ do_test where-14.7.2 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a, x.a||x.b } -} {4/1 4/4 1/1 1/4 nosort} +} {4/4 4/1 1/4 1/1 nosort} do_test where-14.8 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b DESC @@ -1156,6 +1201,7 @@ do_test where-14.12 { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||x.b DESC } } {4/4 4/1 1/4 1/1 sort} +} ;# {permutation != "no_optimization"} # Ticket #2445. # diff --git a/test/where2.test b/test/where2.test index d61c089..e8c2f36 100644 --- a/test/where2.test +++ b/test/where2.test @@ -167,24 +167,54 @@ ifcapable subquery { } } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} } - do_test where2-4.6 { + do_test where2-4.6a { queryplan { SELECT * FROM t1 WHERE x IN (1,2,3,4,5,6,7,8) AND y IN (10000,10001,10002,10003,10004,10005) - ORDER BY 2 + ORDER BY x + } + } {99 6 10000 10006 nosort t1 i1xy} + do_test where2-4.6b { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY x DESC + } + } {99 6 10000 10006 nosort t1 i1xy} + do_test where2-4.6c { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY x, y + } + } {99 6 10000 10006 nosort t1 i1xy} + do_test where2-4.6d { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY x, y DESC } } {99 6 10000 10006 sort t1 i1xy} # Duplicate entires on the RHS of an IN operator do not cause duplicate # output rows. # - do_test where2-4.6 { + do_test where2-4.6x { queryplan { SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207) ORDER BY w } } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} + do_test where2-4.6y { + queryplan { + SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207) + ORDER BY w DESC + } + } {100 6 10201 10207 99 6 10000 10006 sort t1 i1zyx} ifcapable compound { do_test where2-4.7 { queryplan { @@ -207,11 +237,16 @@ do_test where2-5.1 { } {99 6 10000 10006 nosort t1 i1w} ifcapable subquery { - do_test where2-5.2 { + do_test where2-5.2a { queryplan { SELECT * FROM t1 WHERE w IN (99) ORDER BY w } - } {99 6 10000 10006 sort t1 i1w} + } {99 6 10000 10006 nosort t1 i1w} + do_test where2-5.2b { + queryplan { + SELECT * FROM t1 WHERE w IN (99) ORDER BY w DESC + } + } {99 6 10000 10006 nosort t1 i1w} } # Verify that OR clauses get translated into IN operators. diff --git a/test/where8.test b/test/where8.test index a7d5edb..9b6014e 100644 --- a/test/where8.test +++ b/test/where8.test @@ -290,6 +290,38 @@ do_test where8-3.15 { } } {I I I I I I I I I I II II II II II II II II II II III III III III III 9 1} + +do_test where8-3.21 { + execsql_status { + SELECT a, d FROM t1, (t2) WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +do_test where8-3.21.1 { + execsql_status { + SELECT a, d FROM t1, ((t2)) AS t3 WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +if {[permutation] != "no_optimization"} { +do_test where8-3.21.2 { + execsql_status { + SELECT a, d FROM t1, ((SELECT * FROM t2)) AS t3 WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +} +do_test where8-3.22 { + execsql_status { + SELECT a, d FROM ((((((t1))), (((t2)))))) + WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +if {[permutation] != "no_optimization"} { +do_test where8-3.23 { + execsql_status { + SELECT * FROM ((SELECT * FROM t2)) AS t3; + } +} {1 {} I 2 four IV 3 {} IX 4 sixteen XVI 5 {} XXV 6 thirtysix XXXVI 7 fortynine XLIX 8 sixtyeight LXIV 9 eightyone LXXXIX 10 {} C 9 0} +} + #----------------------------------------------------------------------- # The following tests - where8-4.* - verify that adding or removing # indexes does not change the results returned by various queries. diff --git a/test/where9.test b/test/where9.test index 23260a6..1e94fdf 100644 --- a/test/where9.test +++ b/test/where9.test @@ -232,7 +232,7 @@ do_test where9-1.3.3 { } {90 91 92 97 scan 98 sort 0} do_test where9-1.3.4 { count_steps { - SELECT a FROM t4 + SELECT a FROM (t4) WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) OR (b NOT NULL AND c IS NULL AND d NOT NULL) @@ -692,7 +692,7 @@ do_test where9-6.5.3 { do_test where9-6.5.4 { db eval { SELECT count(*) FROM t1 UNION ALL - SELECT a FROM t1 WHERE a%100 IN (5,31,57,82,83,84,85,86,87); + SELECT a FROM t1 WHERE a%100 IN (5,31,57,82,83,84,85,86,87) ORDER BY rowid; ROLLBACK; } } {99 105 131 157 182 183 184 185 186 187} @@ -876,5 +876,42 @@ do_test where9-8.1 { ORDER BY +a; } } {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} +do_test where9-8.2 { + db eval { + SELECT * + FROM t81 LEFT JOIN (t82) ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} +do_test where9-8.3 { + db eval { + SELECT * + FROM (t81) LEFT JOIN (main.t82) ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} + +# Fix for ticket [f2369304e47167e3e644e2f1fe9736063391d7b7] +# Incorrect results when OR is used in the ON clause of a LEFT JOIN +# +do_test where9-9.1 { + db eval { + CREATE TABLE t91(x); INSERT INTO t91 VALUES(1); + CREATE TABLE t92(y INTEGER PRIMARY KEY,a,b); + INSERT INTO t92 VALUES(1,2,3); + SELECT 1 FROM t91 LEFT JOIN t92 ON a=2 OR b=3; + SELECT 2 FROM t91 LEFT JOIN t92 ON a=2 AND b=3; + SELECT 3 FROM t91 LEFT JOIN t92 ON (a=2 OR b=3) AND y IS NULL; + SELECT 4 FROM t91 LEFT JOIN t92 ON (a=2 AND b=3) AND y IS NULL; + CREATE TEMP TABLE x9 AS SELECT * FROM t91 LEFT JOIN t92 ON a=2 OR b=3; + SELECT 5 FROM x9 WHERE y IS NULL; + SELECT 6 FROM t91 LEFT JOIN t92 ON a=2 OR b=3 WHERE y IS NULL; + SELECT 7 FROM t91 LEFT JOIN t92 ON a=2 AND b=3 WHERE y IS NULL; + SELECT 8 FROM t91 LEFT JOIN t92 ON a=22 OR b=33 WHERE y IS NULL; + SELECT 9 FROM t91 LEFT JOIN t92 ON a=22 AND b=33 WHERE y IS NULL; + } +} {1 2 3 4 8 9} finish_test diff --git a/test/whereD.test b/test/whereD.test index 58fe934..9ac5a68 100644 --- a/test/whereD.test +++ b/test/whereD.test @@ -180,7 +180,7 @@ do_test 4.2 { SELECT * FROM t41 AS x LEFT JOIN t42 AS y ON (y.d=x.c) OR (y.e=x.b); } } {1 2 3 3 6 9 4 5 6 {} {} {}} -do_test 4.2 { +do_test 4.3 { db eval { SELECT * FROM t41 AS x LEFT JOIN t42 AS y ON (y.d=x.c) OR (y.d=x.b); } diff --git a/test/whereE.test b/test/whereE.test new file mode 100644 index 0000000..e686a46 --- /dev/null +++ b/test/whereE.test @@ -0,0 +1,62 @@ +# 2012 November 9 +# +# 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 implements regression tests for SQLite library. The +# focus of this file is testing the query planner to make sure it +# is making good planning decisions. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix whereE + +do_execsql_test 1.1 { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,10), (2,20), (3,30), (2,22), (3, 33); + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + ALTER TABLE t1 ADD COLUMN c; + UPDATE t1 SET c=a*rowid+10000; + CREATE INDEX t1ab ON t1(a,b); + + CREATE TABLE t2(x,y); + INSERT INTO t2 VALUES(4,44),(5,55),(6,66),(7,77); + INSERT INTO t2 SELECT x+4, (x+4)*11 FROM t2; + INSERT INTO t2 SELECT x+8, (x+8)*11 FROM t2; + INSERT INTO t2 SELECT x+16, (x+16)*11 FROM t2; + INSERT INTO t2 SELECT x+32, (x+32)*11 FROM t2; + INSERT INTO t2 SELECT x+64, (x+32)*11 FROM t2; + ALTER TABLE t2 ADD COLUMN z; + UPDATE t2 SET z=2; + CREATE UNIQUE INDEX t2zx ON t2(z,x); + + EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +do_execsql_test 1.2 { + EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +do_execsql_test 1.3 { + ANALYZE; + EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +do_execsql_test 1.4 { + EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} + +finish_test diff --git a/test/whereF.test b/test/whereF.test new file mode 100644 index 0000000..57bdbee --- /dev/null +++ b/test/whereF.test @@ -0,0 +1,115 @@ +# 2012 November 9 +# +# 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. +# +#*********************************************************************** +# +# Test cases for query planning decisions. + + +# +# The tests in this file demonstrate the behaviour of the query planner +# in determining the order in which joined tables are scanned. +# +# Assume there are two tables being joined - t1 and t2. Each has a cost +# if it is the outer loop, and a cost if it is the inner loop. As follows: +# +# t1(outer) - cost of scanning t1 as the outer loop. +# t1(inner) - cost of scanning t1 as the inner loop. +# t2(outer) - cost of scanning t2 as the outer loop. +# t2(inner) - cost of scanning t2 as the inner loop. +# +# Depending on the order in which the planner nests the scans, the total +# cost of the join query is one of: +# +# t1(outer) * t2(inner) +# t2(outer) * t1(inner) +# +# The tests in this file attempt to verify that the planner nests joins in +# the correct order when the following are true: +# +# + (t1(outer) * t2(inner)) > (t1(inner) * t2(outer) +# + t1(outer) < t2(outer) +# +# In other words, when the best overall query plan has t2 as the outer loop, +# but when the outer loop is considered independent of the inner, t1 is the +# most efficient choice. +# +# In order to make them more predictable, automatic indexes are turned off for +# the tests in this file. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix x + +do_execsql_test 1.0 { + PRAGMA automatic_index = 0; + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(d, e, f); + CREATE UNIQUE INDEX i1 ON t1(a); + CREATE UNIQUE INDEX i2 ON t2(d); +} {} + +foreach {tn sql} { + 1 "SELECT * FROM t1, t2 WHERE t1.a=t2.e AND t2.d? AND t2.d>t1.c AND t1.b=t2.e" + 2 "SELECT * FROM t2, t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" + 3 "SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" +} { + do_test 2.$tn { + db eval "EXPLAIN QUERY PLAN $sql" + } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} +} + +do_execsql_test 3.0 { + DROP TABLE t1; + DROP TABLE t2; + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(d, e, f); + + CREATE UNIQUE INDEX i1 ON t1(a, b); + CREATE INDEX i2 ON t2(d); +} {} + +foreach {tn sql} { + 1 {SELECT t1.a, t1.b, t2.d, t2.e FROM t1, t2 + WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} + + 2 {SELECT t1.a, t1.b, t2.d, t2.e FROM t2, t1 + WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} + + 3 {SELECT t1.a, t1.b, t2.d, t2.e FROM t2 CROSS JOIN t1 + WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} +} { + do_test 3.$tn { + db eval "EXPLAIN QUERY PLAN $sql" + } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} +} + +finish_test diff --git a/test/win32lock.test b/test/win32lock.test index d014be4..7241720 100644 --- a/test/win32lock.test +++ b/test/win32lock.test @@ -27,6 +27,7 @@ proc xLog {error_code msg} { lappend ::log $msg } sqlite3 db test.db +db eval {PRAGMA mmap_size=0} do_test win32lock-1.1 { db eval { diff --git a/test/zerodamage.test b/test/zerodamage.test index 3d18c8d..de5088b 100644 --- a/test/zerodamage.test +++ b/test/zerodamage.test @@ -18,7 +18,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -set testprefix wal5 +set testprefix zerodamage ifcapable !vtab { finish_test @@ -59,7 +59,7 @@ do_test zerodamage-2.0 { } tv filter xDelete tv script xDeleteCallback - register_wholenumber_module db + load_static_extension db wholenumber db eval { PRAGMA page_size=1024; PRAGMA journal_mode=DELETE; @@ -89,31 +89,33 @@ do_test zerodamage-2.1 { concat [file_control_powersafe_overwrite db -1] [set ::max_journal_size] } {0 0 24704} -# Run a WAL-mode transaction with POWERSAFE_OVERWRITE on to verify that the -# WAL file does not get too big. -# -do_test zerodamage-3.0 { - db eval { - PRAGMA journal_mode=WAL; - } - db close - sqlite3 db file:test.db?psow=TRUE -uri 1 - db eval { - UPDATE t1 SET y=randomblob(50) WHERE x=124; - } - file size test.db-wal -} {1080} +ifcapable wal { + # Run a WAL-mode transaction with POWERSAFE_OVERWRITE on to verify that the + # WAL file does not get too big. + # + do_test zerodamage-3.0 { + db eval { + PRAGMA journal_mode=WAL; + } + db close + sqlite3 db file:test.db?psow=TRUE -uri 1 + db eval { + UPDATE t1 SET y=randomblob(50) WHERE x=124; + } + file size test.db-wal + } {1080} -# Repeat the previous with POWERSAFE_OVERWRITE off. Verify that the WAL file -# is padded. -# -do_test zerodamage-3.1 { - db close - sqlite3 db file:test.db?psow=FALSE -uri 1 - db eval { - UPDATE t1 SET y=randomblob(50) WHERE x=124; - } - file size test.db-wal -} {8416} + # Repeat the previous with POWERSAFE_OVERWRITE off. Verify that the WAL file + # is padded. + # + do_test zerodamage-3.1 { + db close + sqlite3 db file:test.db?psow=FALSE -uri 1 + db eval { + UPDATE t1 SET y=randomblob(50) WHERE x=124; + } + file size test.db-wal + } {8416} +} finish_test -- cgit v1.2.3