| # 2009 October 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 contains tests to verify that malloc() errors that occur |
| # within the FTS3 module code are handled correctly. |
| # |
| |
| set testdir [file dirname $argv0] |
| source $testdir/tester.tcl |
| ifcapable !fts3 { finish_test ; return } |
| source $testdir/malloc_common.tcl |
| source $testdir/fts3_common.tcl |
| |
| # Ensure the lookaside buffer is disabled for these tests. |
| # |
| sqlite3 db test.db |
| sqlite3_db_config_lookaside db 0 0 0 |
| |
| set sqlite_fts3_enable_parentheses 1 |
| set DO_MALLOC_TEST 1 |
| |
| # Test organization: |
| # |
| # fts3_malloc-1.*: Test OOM during CREATE and DROP table statements. |
| # fts3_malloc-2.*: Test OOM during SELECT operations. |
| # fts3_malloc-3.*: Test OOM during SELECT operations with a larger database. |
| # fts3_malloc-4.*: Test OOM during database write operations. |
| # |
| # |
| |
| #------------------------------------------------------------------------- |
| # This proc is used to test a single SELECT statement. Parameter $name is |
| # passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter |
| # $sql is passed the text of the SELECT statement. Parameter $result is |
| # set to the expected output if the SELECT statement is successfully |
| # executed using [db eval]. |
| # |
| # Example: |
| # |
| # do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2} |
| # |
| # If global variable DO_MALLOC_TEST is set to a non-zero value, or if |
| # it is not defined at all, then OOM testing is performed on the SELECT |
| # statement. Each OOM test case is said to pass if either (a) executing |
| # the SELECT statement succeeds and the results match those specified |
| # by parameter $result, or (b) TCL throws an "out of memory" error. |
| # |
| # If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement |
| # is executed just once. In this case the test case passes if the results |
| # match the expected results passed via parameter $result. |
| # |
| proc do_select_test {name sql result} { |
| doPassiveTest $name $sql [list 0 $result] |
| } |
| |
| proc do_error_test {name sql error} { |
| doPassiveTest $name $sql [list 1 $error] |
| } |
| |
| proc doPassiveTest {name sql catchres} { |
| if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } |
| |
| if {$::DO_MALLOC_TEST} { |
| set answers [list {1 {out of memory}} $catchres] |
| set modes [list 100000 transient 1 persistent] |
| } else { |
| set answers [list $catchres] |
| set modes [list 0 nofail] |
| } |
| set str [join $answers " OR "] |
| |
| foreach {nRepeat zName} $modes { |
| for {set iFail 1} 1 {incr iFail} { |
| if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat} |
| |
| set res [catchsql $sql] |
| if {[lsearch $answers $res]>=0} { |
| set res $str |
| } |
| do_test $name.$zName.$iFail [list set {} $res] $str |
| set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign] |
| if {$nFail==0} break |
| } |
| } |
| } |
| |
| |
| #------------------------------------------------------------------------- |
| # Test a single write to the database. In this case a "write" is a |
| # DELETE, UPDATE or INSERT statement. |
| # |
| # If OOM testing is performed, there are several acceptable outcomes: |
| # |
| # 1) The write succeeds. No error is returned. |
| # |
| # 2) An "out of memory" exception is thrown and: |
| # |
| # a) The statement has no effect, OR |
| # b) The current transaction is rolled back, OR |
| # c) The statement succeeds. This can only happen if the connection |
| # is in auto-commit mode (after the statement is executed, so this |
| # includes COMMIT statements). |
| # |
| # If the write operation eventually succeeds, zero is returned. If a |
| # transaction is rolled back, non-zero is returned. |
| # |
| # Parameter $name is the name to use for the test case (or test cases). |
| # The second parameter, $tbl, should be the name of the database table |
| # being modified. Parameter $sql contains the SQL statement to test. |
| # |
| proc do_write_test {name tbl sql} { |
| if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } |
| |
| # Figure out an statement to get a checksum for table $tbl. |
| db eval "SELECT * FROM $tbl" V break |
| set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl" |
| |
| # Calculate the initial table checksum. |
| set cksum1 [db one $cksumsql] |
| |
| |
| if {$::DO_MALLOC_TEST } { |
| set answers [list {1 {out of memory}} {0 {}}] |
| set modes [list 100000 transient 1 persistent] |
| } else { |
| set answers [list {0 {}}] |
| set modes [list 0 nofail] |
| } |
| set str [join $answers " OR "] |
| |
| foreach {nRepeat zName} $modes { |
| for {set iFail 1} 1 {incr iFail} { |
| if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat} |
| |
| set res [catchsql $sql] |
| set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign] |
| if {$nFail==0} { |
| do_test $name.$zName.$iFail [list set {} $res] {0 {}} |
| return |
| } else { |
| if {[lsearch $answers $res]>=0} { |
| set res $str |
| } |
| do_test $name.$zName.$iFail [list set {} $res] $str |
| set cksum2 [db one $cksumsql] |
| if {$cksum1 != $cksum2} return |
| } |
| } |
| } |
| } |
| |
| proc normal_list {l} { |
| set ret [list] |
| foreach elem $l {lappend ret $elem} |
| set ret |
| } |
| |
| |
| do_write_test fts3_malloc-1.1 sqlite_master { |
| CREATE VIRTUAL TABLE ft1 USING fts3(a, b) |
| } |
| do_write_test fts3_malloc-1.2 sqlite_master { |
| CREATE VIRTUAL TABLE ft2 USING fts3([a], [b]); |
| } |
| do_write_test fts3_malloc-1.3 sqlite_master { |
| CREATE VIRTUAL TABLE ft3 USING fts3('a', "b"); |
| } |
| do_write_test fts3_malloc-1.4 sqlite_master { |
| CREATE VIRTUAL TABLE ft4 USING fts3(`a`, 'fred''s column'); |
| } |
| do_error_test fts3_malloc-1.5 { |
| CREATE VIRTUAL TABLE ft5 USING fts3(a, b, tokenize unknown) |
| } {unknown tokenizer: unknown} |
| do_write_test fts3_malloc-1.6 sqlite_master { |
| CREATE VIRTUAL TABLE ft6 USING fts3(a, b, tokenize porter) |
| } |
| |
| # Test the xConnect/xDisconnect methods: |
| #db eval { ATTACH 'test2.db' AS aux } |
| #do_write_test fts3_malloc-1.6 aux.sqlite_master { |
| # CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c); |
| #} |
| #do_write_test fts3_malloc-1.6 aux.sqlite_master { |
| # CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c); |
| #} |
| |
| |
| |
| do_test fts3_malloc-2.0 { |
| execsql { |
| DROP TABLE ft1; |
| DROP TABLE ft2; |
| DROP TABLE ft3; |
| DROP TABLE ft4; |
| DROP TABLE ft6; |
| } |
| execsql { CREATE VIRTUAL TABLE ft USING fts3(a, b) } |
| for {set ii 1} {$ii < 32} {incr ii} { |
| set a [list] |
| set b [list] |
| if {$ii & 0x01} {lappend a one ; lappend b neung} |
| if {$ii & 0x02} {lappend a two ; lappend b song } |
| if {$ii & 0x04} {lappend a three ; lappend b sahm } |
| if {$ii & 0x08} {lappend a four ; lappend b see } |
| if {$ii & 0x10} {lappend a five ; lappend b hah } |
| execsql { INSERT INTO ft VALUES($a, $b) } |
| } |
| } {} |
| |
| foreach {tn sql result} { |
| 1 "SELECT count(*) FROM sqlite_master" {5} |
| 2 "SELECT * FROM ft WHERE docid = 1" {one neung} |
| 3 "SELECT * FROM ft WHERE docid = 2" {two song} |
| 4 "SELECT * FROM ft WHERE docid = 3" {{one two} {neung song}} |
| |
| 5 "SELECT a FROM ft" { |
| {one} {two} {one two} |
| {three} {one three} {two three} |
| {one two three} {four} {one four} |
| {two four} {one two four} {three four} |
| {one three four} {two three four} {one two three four} |
| {five} {one five} {two five} |
| {one two five} {three five} {one three five} |
| {two three five} {one two three five} {four five} |
| {one four five} {two four five} {one two four five} |
| {three four five} {one three four five} {two three four five} |
| {one two three four five} |
| } |
| |
| 6 "SELECT a FROM ft WHERE a MATCH 'one'" { |
| {one} {one two} {one three} {one two three} |
| {one four} {one two four} {one three four} {one two three four} |
| {one five} {one two five} {one three five} {one two three five} |
| {one four five} {one two four five} |
| {one three four five} {one two three four five} |
| } |
| |
| 7 "SELECT a FROM ft WHERE a MATCH 'o*'" { |
| {one} {one two} {one three} {one two three} |
| {one four} {one two four} {one three four} {one two three four} |
| {one five} {one two five} {one three five} {one two three five} |
| {one four five} {one two four five} |
| {one three four five} {one two three four five} |
| } |
| |
| 8 "SELECT a FROM ft WHERE a MATCH 'o* t*'" { |
| {one two} {one three} {one two three} |
| {one two four} {one three four} {one two three four} |
| {one two five} {one three five} {one two three five} |
| {one two four five} {one three four five} {one two three four five} |
| } |
| |
| 9 "SELECT a FROM ft WHERE a MATCH '\"o* t*\"'" { |
| {one two} {one three} {one two three} |
| {one two four} {one three four} {one two three four} |
| {one two five} {one three five} {one two three five} |
| {one two four five} {one three four five} {one two three four five} |
| } |
| |
| 10 {SELECT a FROM ft WHERE a MATCH '"o* f*"'} { |
| {one four} {one five} {one four five} |
| } |
| |
| 11 {SELECT a FROM ft WHERE a MATCH '"one two three"'} { |
| {one two three} |
| {one two three four} |
| {one two three five} |
| {one two three four five} |
| } |
| |
| 12 {SELECT a FROM ft WHERE a MATCH '"two three four"'} { |
| {two three four} |
| {one two three four} |
| {two three four five} |
| {one two three four five} |
| } |
| |
| 12 {SELECT a FROM ft WHERE a MATCH '"two three" five'} { |
| {two three five} {one two three five} |
| {two three four five} {one two three four five} |
| } |
| |
| 13 {SELECT a FROM ft WHERE ft MATCH '"song sahm" hah'} { |
| {two three five} {one two three five} |
| {two three four five} {one two three four five} |
| } |
| |
| 14 {SELECT a FROM ft WHERE b MATCH 'neung'} { |
| {one} {one two} |
| {one three} {one two three} |
| {one four} {one two four} |
| {one three four} {one two three four} |
| {one five} {one two five} |
| {one three five} {one two three five} |
| {one four five} {one two four five} |
| {one three four five} {one two three four five} |
| } |
| |
| 15 {SELECT a FROM ft WHERE b MATCH '"neung song sahm"'} { |
| {one two three} {one two three four} |
| {one two three five} {one two three four five} |
| } |
| |
| 16 {SELECT a FROM ft WHERE b MATCH 'hah "song sahm"'} { |
| {two three five} {one two three five} |
| {two three four five} {one two three four five} |
| } |
| |
| 17 {SELECT a FROM ft WHERE b MATCH 'song OR sahm'} { |
| {two} {one two} {three} |
| {one three} {two three} {one two three} |
| {two four} {one two four} {three four} |
| {one three four} {two three four} {one two three four} |
| {two five} {one two five} {three five} |
| {one three five} {two three five} {one two three five} |
| {two four five} {one two four five} {three four five} |
| {one three four five} {two three four five} {one two three four five} |
| } |
| |
| 18 {SELECT a FROM ft WHERE a MATCH 'three NOT two'} { |
| {three} {one three} {three four} |
| {one three four} {three five} {one three five} |
| {three four five} {one three four five} |
| } |
| |
| 19 {SELECT a FROM ft WHERE b MATCH 'sahm NOT song'} { |
| {three} {one three} {three four} |
| {one three four} {three five} {one three five} |
| {three four five} {one three four five} |
| } |
| |
| 20 {SELECT a FROM ft WHERE ft MATCH 'sahm NOT song'} { |
| {three} {one three} {three four} |
| {one three four} {three five} {one three five} |
| {three four five} {one three four five} |
| } |
| |
| 21 {SELECT a FROM ft WHERE b MATCH 'neung NEAR song NEAR sahm'} { |
| {one two three} {one two three four} |
| {one two three five} {one two three four five} |
| } |
| |
| } { |
| set result [normal_list $result] |
| do_select_test fts3_malloc-2.$tn $sql $result |
| } |
| |
| do_test fts3_malloc-3.0 { |
| execsql BEGIN |
| for {set ii 32} {$ii < 1024} {incr ii} { |
| set a [list] |
| set b [list] |
| if {$ii & 0x0001} {lappend a one ; lappend b neung } |
| if {$ii & 0x0002} {lappend a two ; lappend b song } |
| if {$ii & 0x0004} {lappend a three ; lappend b sahm } |
| if {$ii & 0x0008} {lappend a four ; lappend b see } |
| if {$ii & 0x0010} {lappend a five ; lappend b hah } |
| if {$ii & 0x0020} {lappend a six ; lappend b hok } |
| if {$ii & 0x0040} {lappend a seven ; lappend b jet } |
| if {$ii & 0x0080} {lappend a eight ; lappend b bairt } |
| if {$ii & 0x0100} {lappend a nine ; lappend b gow } |
| if {$ii & 0x0200} {lappend a ten ; lappend b sip } |
| execsql { INSERT INTO ft VALUES($a, $b) } |
| } |
| execsql COMMIT |
| } {} |
| foreach {tn sql result} { |
| 1 "SELECT count(*) FROM ft" {1023} |
| |
| 2 "SELECT a FROM ft WHERE a MATCH 'one two three four five six seven eight'" { |
| {one two three four five six seven eight} |
| {one two three four five six seven eight nine} |
| {one two three four five six seven eight ten} |
| {one two three four five six seven eight nine ten} |
| } |
| |
| 3 {SELECT count(*), sum(docid) FROM ft WHERE a MATCH 'o*'} { |
| 512 262144 |
| } |
| |
| 4 {SELECT count(*), sum(docid) FROM ft WHERE a MATCH '"two three four"'} { |
| 128 66368 |
| } |
| } { |
| set result [normal_list $result] |
| do_select_test fts3_malloc-3.$tn $sql $result |
| } |
| |
| do_test fts3_malloc-4.0 { |
| execsql { DELETE FROM ft WHERE docid>=32 } |
| } {} |
| foreach {tn sql} { |
| 1 "DELETE FROM ft WHERE ft MATCH 'one'" |
| 2 "DELETE FROM ft WHERE ft MATCH 'three'" |
| 3 "DELETE FROM ft WHERE ft MATCH 'five'" |
| } { |
| do_write_test fts3_malloc-4.1.$tn ft_content $sql |
| } |
| do_test fts3_malloc-4.2 { |
| execsql { SELECT a FROM ft } |
| } {two four {two four}} |
| |
| |
| finish_test |
| |