diff options
| author | Hans-Christoph Steiner <hans@eds.org> | 2013-08-13 15:42:54 -0400 | 
|---|---|---|
| committer | Hans-Christoph Steiner <hans@eds.org> | 2013-08-13 15:42:54 -0400 | 
| commit | 08119c361d1181b3e8f1abb429236e488a664753 (patch) | |
| tree | 77e5a8b6d411ca32c360c7e48df5c293b1e0baac /test | |
| parent | 1b5ba8e022836fa8ab93bc90df1b34a29ea6e134 (diff) | |
Imported Upstream version 2.2.1
Diffstat (limited to 'test')
161 files changed, 7843 insertions, 424 deletions
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 <<NULL>> 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 <<NULL>> 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 <test number> <options...>  #  # 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<t1.b AND t1.c!=10" +  2 "SELECT * FROM t2,           t1 WHERE t1.a=t2.e AND t2.d<t1.b AND t1.c!=10" +  3 "SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=t2.e AND t2.d<t1.b AND t1.c!=10" +} { +  do_test 1.$tn { +    db eval "EXPLAIN QUERY PLAN $sql" +   } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} +} + +do_execsql_test 2.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); +  CREATE UNIQUE INDEX i2 ON t1(b); +  CREATE UNIQUE INDEX i3 ON t2(d); +} {} + +foreach {tn sql} { +  1 "SELECT * FROM t1,           t2 WHERE t1.a>? 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  | 
