Annotation of embedaddon/sqlite3/test/tester.tcl, revision 1.1.1.1

1.1       misho       1: # 2001 September 15
                      2: #
                      3: # The author disclaims copyright to this source code.  In place of
                      4: # a legal notice, here is a blessing:
                      5: #
                      6: #    May you do good and not evil.
                      7: #    May you find forgiveness for yourself and forgive others.
                      8: #    May you share freely, never taking more than you give.
                      9: #
                     10: #***********************************************************************
                     11: # This file implements some common TCL routines used for regression
                     12: # testing the SQLite library
                     13: #
                     14: # $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $
                     15: 
                     16: #-------------------------------------------------------------------------
                     17: # The commands provided by the code in this file to help with creating 
                     18: # test cases are as follows:
                     19: #
                     20: # Commands to manipulate the db and the file-system at a high level:
                     21: #
                     22: #      copy_file              FROM TO
                     23: #      delete_file            FILENAME
                     24: #      drop_all_tables        ?DB?
                     25: #      forcecopy              FROM TO
                     26: #      forcedelete            FILENAME
                     27: #
                     28: # Test the capability of the SQLite version built into the interpreter to
                     29: # determine if a specific test can be run:
                     30: #
                     31: #      ifcapable              EXPR
                     32: #
                     33: # Calulate checksums based on database contents:
                     34: #
                     35: #      dbcksum                DB DBNAME
                     36: #      allcksum               ?DB?
                     37: #      cksum                  ?DB?
                     38: #
                     39: # Commands to execute/explain SQL statements:
                     40: #
                     41: #      stepsql                DB SQL
                     42: #      execsql2               SQL
                     43: #      explain_no_trace       SQL
                     44: #      explain                SQL ?DB?
                     45: #      catchsql               SQL ?DB?
                     46: #      execsql                SQL ?DB?
                     47: #
                     48: # Commands to run test cases:
                     49: #
                     50: #      do_ioerr_test          TESTNAME ARGS...
                     51: #      crashsql               ARGS...
                     52: #      integrity_check        TESTNAME ?DB?
                     53: #      do_test                TESTNAME SCRIPT EXPECTED
                     54: #      do_execsql_test        TESTNAME SQL EXPECTED
                     55: #      do_catchsql_test       TESTNAME SQL EXPECTED
                     56: #
                     57: # Commands providing a lower level interface to the global test counters:
                     58: #
                     59: #      set_test_counter       COUNTER ?VALUE?
                     60: #      omit_test              TESTNAME REASON ?APPEND?
                     61: #      fail_test              TESTNAME
                     62: #      incr_ntest
                     63: #
                     64: # Command run at the end of each test file:
                     65: #
                     66: #      finish_test
                     67: #
                     68: # Commands to help create test files that run with the "WAL" and other
                     69: # permutations (see file permutations.test):
                     70: #
                     71: #      wal_is_wal_mode
                     72: #      wal_set_journal_mode   ?DB?
                     73: #      wal_check_journal_mode TESTNAME?DB?
                     74: #      permutation
                     75: #      presql
                     76: #
                     77: 
                     78: # Set the precision of FP arithmatic used by the interpreter. And 
                     79: # configure SQLite to take database file locks on the page that begins
                     80: # 64KB into the database file instead of the one 1GB in. This means
                     81: # the code that handles that special case can be tested without creating
                     82: # very large database files.
                     83: #
                     84: set tcl_precision 15
                     85: sqlite3_test_control_pending_byte 0x0010000
                     86: 
                     87: 
                     88: # If the pager codec is available, create a wrapper for the [sqlite3] 
                     89: # command that appends "-key {xyzzy}" to the command line. i.e. this:
                     90: #
                     91: #     sqlite3 db test.db
                     92: #
                     93: # becomes
                     94: #
                     95: #     sqlite3 db test.db -key {xyzzy}
                     96: #
                     97: if {[info command sqlite_orig]==""} {
                     98:   rename sqlite3 sqlite_orig
                     99:   proc sqlite3 {args} {
                    100:     if {[llength $args]>=2 && [string index [lindex $args 0] 0]!="-"} {
                    101:       # This command is opening a new database connection.
                    102:       #
                    103:       if {[info exists ::G(perm:sqlite3_args)]} {
                    104:         set args [concat $args $::G(perm:sqlite3_args)]
                    105:       }
                    106:       if {[sqlite_orig -has-codec] && ![info exists ::do_not_use_codec]} {
                    107:         lappend args -key {xyzzy}
                    108:       }
                    109: 
                    110:       set res [uplevel 1 sqlite_orig $args]
                    111:       if {[info exists ::G(perm:presql)]} {
                    112:         [lindex $args 0] eval $::G(perm:presql)
                    113:       }
                    114:       if {[info exists ::G(perm:dbconfig)]} {
                    115:         set ::dbhandle [lindex $args 0]
                    116:         uplevel #0 $::G(perm:dbconfig)
                    117:       }
                    118:       set res
                    119:     } else {
                    120:       # This command is not opening a new database connection. Pass the 
                    121:       # arguments through to the C implemenation as the are.
                    122:       #
                    123:       uplevel 1 sqlite_orig $args
                    124:     }
                    125:   }
                    126: }
                    127: 
                    128: proc getFileRetries {} {
                    129:   if {![info exists ::G(file-retries)]} {
                    130:     #
                    131:     # NOTE: Return the default number of retries for [file] operations.  A
                    132:     #       value of zero or less here means "disabled".
                    133:     #
                    134:     return [expr {$::tcl_platform(platform) eq "windows" ? 10 : 0}]
                    135:   }
                    136:   return $::G(file-retries)
                    137: }
                    138: 
                    139: proc getFileRetryDelay {} {
                    140:   if {![info exists ::G(file-retry-delay)]} {
                    141:     #
                    142:     # NOTE: Return the default number of milliseconds to wait when retrying
                    143:     #       failed [file] operations.  A value of zero or less means "do not
                    144:     #       wait".
                    145:     #
                    146:     return 100; # TODO: Good default?
                    147:   }
                    148:   return $::G(file-retry-delay)
                    149: }
                    150: 
                    151: # Copy file $from into $to. This is used because some versions of
                    152: # TCL for windows (notably the 8.4.1 binary package shipped with the
                    153: # current mingw release) have a broken "file copy" command.
                    154: #
                    155: proc copy_file {from to} {
                    156:   do_copy_file false $from $to
                    157: }
                    158: 
                    159: proc forcecopy {from to} {
                    160:   do_copy_file true $from $to
                    161: }
                    162: 
                    163: proc do_copy_file {force from to} {
                    164:   set nRetry [getFileRetries]     ;# Maximum number of retries.
                    165:   set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
                    166: 
                    167:   # On windows, sometimes even a [file copy -force] can fail. The cause is
                    168:   # usually "tag-alongs" - programs like anti-virus software, automatic backup
                    169:   # tools and various explorer extensions that keep a file open a little longer
                    170:   # than we expect, causing the delete to fail.
                    171:   #
                    172:   # The solution is to wait a short amount of time before retrying the copy.
                    173:   #
                    174:   if {$nRetry > 0} {
                    175:     for {set i 0} {$i<$nRetry} {incr i} {
                    176:       set rc [catch {
                    177:         if {$force} {
                    178:           file copy -force $from $to
                    179:         } else {
                    180:           file copy $from $to
                    181:         }
                    182:       } msg]
                    183:       if {$rc==0} break
                    184:       if {$nDelay > 0} { after $nDelay }
                    185:     }
                    186:     if {$rc} { error $msg }
                    187:   } else {
                    188:     if {$force} {
                    189:       file copy -force $from $to
                    190:     } else {
                    191:       file copy $from $to
                    192:     }
                    193:   }
                    194: }
                    195: 
                    196: # Delete a file or directory
                    197: #
                    198: proc delete_file {args} {
                    199:   do_delete_file false {*}$args
                    200: }
                    201: 
                    202: proc forcedelete {args} {
                    203:   do_delete_file true {*}$args
                    204: }
                    205: 
                    206: proc do_delete_file {force args} {
                    207:   set nRetry [getFileRetries]     ;# Maximum number of retries.
                    208:   set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
                    209: 
                    210:   foreach filename $args {
                    211:     # On windows, sometimes even a [file delete -force] can fail just after
                    212:     # a file is closed. The cause is usually "tag-alongs" - programs like
                    213:     # anti-virus software, automatic backup tools and various explorer
                    214:     # extensions that keep a file open a little longer than we expect, causing
                    215:     # the delete to fail.
                    216:     #
                    217:     # The solution is to wait a short amount of time before retrying the
                    218:     # delete.
                    219:     #
                    220:     if {$nRetry > 0} {
                    221:       for {set i 0} {$i<$nRetry} {incr i} {
                    222:         set rc [catch {
                    223:           if {$force} {
                    224:             file delete -force $filename
                    225:           } else {
                    226:             file delete $filename
                    227:           }
                    228:         } msg]
                    229:         if {$rc==0} break
                    230:         if {$nDelay > 0} { after $nDelay }
                    231:       }
                    232:       if {$rc} { error $msg }
                    233:     } else {
                    234:       if {$force} {
                    235:         file delete -force $filename
                    236:       } else {
                    237:         file delete $filename
                    238:       }
                    239:     }
                    240:   }
                    241: }
                    242: 
                    243: proc execpresql {handle args} {
                    244:   trace remove execution $handle enter [list execpresql $handle]
                    245:   if {[info exists ::G(perm:presql)]} {
                    246:     $handle eval $::G(perm:presql)
                    247:   }
                    248: }
                    249: 
                    250: # This command should be called after loading tester.tcl from within
                    251: # all test scripts that are incompatible with encryption codecs.
                    252: #
                    253: proc do_not_use_codec {} {
                    254:   set ::do_not_use_codec 1
                    255:   reset_db
                    256: }
                    257: 
                    258: # The following block only runs the first time this file is sourced. It
                    259: # does not run in slave interpreters (since the ::cmdlinearg array is
                    260: # populated before the test script is run in slave interpreters).
                    261: #
                    262: if {[info exists cmdlinearg]==0} {
                    263: 
                    264:   # Parse any options specified in the $argv array. This script accepts the 
                    265:   # following options: 
                    266:   #
                    267:   #   --pause
                    268:   #   --soft-heap-limit=NN
                    269:   #   --maxerror=NN
                    270:   #   --malloctrace=N
                    271:   #   --backtrace=N
                    272:   #   --binarylog=N
                    273:   #   --soak=N
                    274:   #   --file-retries=N
                    275:   #   --file-retry-delay=N
                    276:   #   --start=[$permutation:]$testfile
                    277:   #   --match=$pattern
                    278:   #
                    279:   set cmdlinearg(soft-heap-limit)    0
                    280:   set cmdlinearg(maxerror)        1000
                    281:   set cmdlinearg(malloctrace)        0
                    282:   set cmdlinearg(backtrace)         10
                    283:   set cmdlinearg(binarylog)          0
                    284:   set cmdlinearg(soak)               0
                    285:   set cmdlinearg(file-retries)       0
                    286:   set cmdlinearg(file-retry-delay)   0
                    287:   set cmdlinearg(start)             ""
                    288:   set cmdlinearg(match)             ""
                    289: 
                    290:   set leftover [list]
                    291:   foreach a $argv {
                    292:     switch -regexp -- $a {
                    293:       {^-+pause$} {
                    294:         # Wait for user input before continuing. This is to give the user an 
                    295:         # opportunity to connect profiling tools to the process.
                    296:         puts -nonewline "Press RETURN to begin..."
                    297:         flush stdout
                    298:         gets stdin
                    299:       }
                    300:       {^-+soft-heap-limit=.+$} {
                    301:         foreach {dummy cmdlinearg(soft-heap-limit)} [split $a =] break
                    302:       }
                    303:       {^-+maxerror=.+$} {
                    304:         foreach {dummy cmdlinearg(maxerror)} [split $a =] break
                    305:       }
                    306:       {^-+malloctrace=.+$} {
                    307:         foreach {dummy cmdlinearg(malloctrace)} [split $a =] break
                    308:         if {$cmdlinearg(malloctrace)} {
                    309:           sqlite3_memdebug_log start
                    310:         }
                    311:       }
                    312:       {^-+backtrace=.+$} {
                    313:         foreach {dummy cmdlinearg(backtrace)} [split $a =] break
                    314:         sqlite3_memdebug_backtrace $value
                    315:       }
                    316:       {^-+binarylog=.+$} {
                    317:         foreach {dummy cmdlinearg(binarylog)} [split $a =] break
                    318:       }
                    319:       {^-+soak=.+$} {
                    320:         foreach {dummy cmdlinearg(soak)} [split $a =] break
                    321:         set ::G(issoak) $cmdlinearg(soak)
                    322:       }
                    323:       {^-+file-retries=.+$} {
                    324:         foreach {dummy cmdlinearg(file-retries)} [split $a =] break
                    325:         set ::G(file-retries) $cmdlinearg(file-retries)
                    326:       }
                    327:       {^-+file-retry-delay=.+$} {
                    328:         foreach {dummy cmdlinearg(file-retry-delay)} [split $a =] break
                    329:         set ::G(file-retry-delay) $cmdlinearg(file-retry-delay)
                    330:       }
                    331:       {^-+start=.+$} {
                    332:         foreach {dummy cmdlinearg(start)} [split $a =] break
                    333: 
                    334:         set ::G(start:file) $cmdlinearg(start)
                    335:         if {[regexp {(.*):(.*)} $cmdlinearg(start) -> s.perm s.file]} {
                    336:           set ::G(start:permutation) ${s.perm}
                    337:           set ::G(start:file)        ${s.file}
                    338:         }
                    339:         if {$::G(start:file) == ""} {unset ::G(start:file)}
                    340:       }
                    341:       {^-+match=.+$} {
                    342:         foreach {dummy cmdlinearg(match)} [split $a =] break
                    343: 
                    344:         set ::G(match) $cmdlinearg(match)
                    345:         if {$::G(match) == ""} {unset ::G(match)}
                    346:       }
                    347:       default {
                    348:         lappend leftover $a
                    349:       }
                    350:     }
                    351:   }
                    352:   set argv $leftover
                    353: 
                    354:   # Install the malloc layer used to inject OOM errors. And the 'automatic'
                    355:   # extensions. This only needs to be done once for the process.
                    356:   #
                    357:   sqlite3_shutdown 
                    358:   install_malloc_faultsim 1 
                    359:   sqlite3_initialize
                    360:   autoinstall_test_functions
                    361: 
                    362:   # If the --binarylog option was specified, create the logging VFS. This
                    363:   # call installs the new VFS as the default for all SQLite connections.
                    364:   #
                    365:   if {$cmdlinearg(binarylog)} {
                    366:     vfslog new binarylog {} vfslog.bin
                    367:   }
                    368: 
                    369:   # Set the backtrace depth, if malloc tracing is enabled.
                    370:   #
                    371:   if {$cmdlinearg(malloctrace)} {
                    372:     sqlite3_memdebug_backtrace $cmdlinearg(backtrace)
                    373:   }
                    374: }
                    375: 
                    376: # Update the soft-heap-limit each time this script is run. In that
                    377: # way if an individual test file changes the soft-heap-limit, it
                    378: # will be reset at the start of the next test file.
                    379: #
                    380: sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
                    381: 
                    382: # Create a test database
                    383: #
                    384: proc reset_db {} {
                    385:   catch {db close}
                    386:   forcedelete test.db
                    387:   forcedelete test.db-journal
                    388:   forcedelete test.db-wal
                    389:   sqlite3 db ./test.db
                    390:   set ::DB [sqlite3_connection_pointer db]
                    391:   if {[info exists ::SETUP_SQL]} {
                    392:     db eval $::SETUP_SQL
                    393:   }
                    394: }
                    395: reset_db
                    396: 
                    397: # Abort early if this script has been run before.
                    398: #
                    399: if {[info exists TC(count)]} return
                    400: 
                    401: # Make sure memory statistics are enabled.
                    402: #
                    403: sqlite3_config_memstatus 1
                    404: 
                    405: # Initialize the test counters and set up commands to access them.
                    406: # Or, if this is a slave interpreter, set up aliases to write the
                    407: # counters in the parent interpreter.
                    408: #
                    409: if {0==[info exists ::SLAVE]} {
                    410:   set TC(errors)    0
                    411:   set TC(count)     0
                    412:   set TC(fail_list) [list]
                    413:   set TC(omit_list) [list]
                    414: 
                    415:   proc set_test_counter {counter args} {
                    416:     if {[llength $args]} {
                    417:       set ::TC($counter) [lindex $args 0]
                    418:     }
                    419:     set ::TC($counter)
                    420:   }
                    421: }
                    422: 
                    423: # Record the fact that a sequence of tests were omitted.
                    424: #
                    425: proc omit_test {name reason {append 1}} {
                    426:   set omitList [set_test_counter omit_list]
                    427:   if {$append} {
                    428:     lappend omitList [list $name $reason]
                    429:   }
                    430:   set_test_counter omit_list $omitList
                    431: }
                    432: 
                    433: # Record the fact that a test failed.
                    434: #
                    435: proc fail_test {name} {
                    436:   set f [set_test_counter fail_list]
                    437:   lappend f $name
                    438:   set_test_counter fail_list $f
                    439:   set_test_counter errors [expr [set_test_counter errors] + 1]
                    440: 
                    441:   set nFail [set_test_counter errors]
                    442:   if {$nFail>=$::cmdlinearg(maxerror)} {
                    443:     puts "*** Giving up..."
                    444:     finalize_testing
                    445:   }
                    446: }
                    447: 
                    448: # Increment the number of tests run
                    449: #
                    450: proc incr_ntest {} {
                    451:   set_test_counter count [expr [set_test_counter count] + 1]
                    452: }
                    453: 
                    454: 
                    455: # Invoke the do_test procedure to run a single test 
                    456: #
                    457: proc do_test {name cmd expected} {
                    458: 
                    459:   global argv cmdlinearg
                    460: 
                    461:   fix_testname name
                    462: 
                    463:   sqlite3_memdebug_settitle $name
                    464: 
                    465: #  if {[llength $argv]==0} { 
                    466: #    set go 1
                    467: #  } else {
                    468: #    set go 0
                    469: #    foreach pattern $argv {
                    470: #      if {[string match $pattern $name]} {
                    471: #        set go 1
                    472: #        break
                    473: #      }
                    474: #    }
                    475: #  }
                    476: 
                    477:   if {[info exists ::G(perm:prefix)]} {
                    478:     set name "$::G(perm:prefix)$name"
                    479:   }
                    480: 
                    481:   incr_ntest
                    482:   puts -nonewline $name...
                    483:   flush stdout
                    484: 
                    485:   if {![info exists ::G(match)] || [string match $::G(match) $name]} {
                    486:     if {[catch {uplevel #0 "$cmd;\n"} result]} {
                    487:       puts "\nError: $result"
                    488:       fail_test $name
                    489:     } elseif {[string compare $result $expected]} {
                    490:       puts "\nExpected: \[$expected\]\n     Got: \[$result\]"
                    491:       fail_test $name
                    492:     } else {
                    493:       puts " Ok"
                    494:     }
                    495:   } else {
                    496:     puts " Omitted"
                    497:     omit_test $name "pattern mismatch" 0
                    498:   }
                    499:   flush stdout
                    500: }
                    501: 
                    502: proc filepath_normalize {p} {
                    503:   # test cases should be written to assume "unix"-like file paths
                    504:   if {$::tcl_platform(platform)!="unix"} {
                    505:     # lreverse*2 as a hack to remove any unneeded {} after the string map
                    506:     lreverse [lreverse [string map {\\ /} [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]]]
                    507:   } {
                    508:     set p
                    509:   }
                    510: }
                    511: proc do_filepath_test {name cmd expected} {
                    512:   uplevel [list do_test $name [
                    513:     subst -nocommands { filepath_normalize [ $cmd ] }
                    514:   ] [filepath_normalize $expected]]
                    515: }
                    516: 
                    517: proc realnum_normalize {r} {
                    518:   # different TCL versions display floating point values differently.
                    519:   string map {1.#INF inf Inf inf .0e e} [regsub -all {(e[+-])0+} $r {\1}]
                    520: }
                    521: proc do_realnum_test {name cmd expected} {
                    522:   uplevel [list do_test $name [
                    523:     subst -nocommands { realnum_normalize [ $cmd ] }
                    524:   ] [realnum_normalize $expected]]
                    525: }
                    526: 
                    527: proc fix_testname {varname} {
                    528:   upvar $varname testname
                    529:   if {[info exists ::testprefix] 
                    530:    && [string is digit [string range $testname 0 0]]
                    531:   } {
                    532:     set testname "${::testprefix}-$testname"
                    533:   }
                    534: }
                    535:     
                    536: proc do_execsql_test {testname sql {result {}}} {
                    537:   fix_testname testname
                    538:   uplevel do_test [list $testname] [list "execsql {$sql}"] [list [list {*}$result]]
                    539: }
                    540: proc do_catchsql_test {testname sql result} {
                    541:   fix_testname testname
                    542:   uplevel do_test [list $testname] [list "catchsql {$sql}"] [list $result]
                    543: }
                    544: proc do_eqp_test {name sql res} {
                    545:   uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
                    546: }
                    547: 
                    548: #-------------------------------------------------------------------------
                    549: #   Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST
                    550: #
                    551: # Where switches are:
                    552: #
                    553: #   -errorformat FMTSTRING
                    554: #   -count
                    555: #   -query SQL
                    556: #   -tclquery TCL
                    557: #   -repair TCL
                    558: #
                    559: proc do_select_tests {prefix args} {
                    560: 
                    561:   set testlist [lindex $args end]
                    562:   set switches [lrange $args 0 end-1]
                    563: 
                    564:   set errfmt ""
                    565:   set countonly 0
                    566:   set tclquery ""
                    567:   set repair ""
                    568: 
                    569:   for {set i 0} {$i < [llength $switches]} {incr i} {
                    570:     set s [lindex $switches $i]
                    571:     set n [string length $s]
                    572:     if {$n>=2 && [string equal -length $n $s "-query"]} {
                    573:       set tclquery [list execsql [lindex $switches [incr i]]]
                    574:     } elseif {$n>=2 && [string equal -length $n $s "-tclquery"]} {
                    575:       set tclquery [lindex $switches [incr i]]
                    576:     } elseif {$n>=2 && [string equal -length $n $s "-errorformat"]} {
                    577:       set errfmt [lindex $switches [incr i]]
                    578:     } elseif {$n>=2 && [string equal -length $n $s "-repair"]} {
                    579:       set repair [lindex $switches [incr i]]
                    580:     } elseif {$n>=2 && [string equal -length $n $s "-count"]} {
                    581:       set countonly 1
                    582:     } else {
                    583:       error "unknown switch: $s"
                    584:     }
                    585:   }
                    586: 
                    587:   if {$countonly && $errfmt!=""} {
                    588:     error "Cannot use -count and -errorformat together"
                    589:   }
                    590:   set nTestlist [llength $testlist]
                    591:   if {$nTestlist%3 || $nTestlist==0 } {
                    592:     error "SELECT test list contains [llength $testlist] elements"
                    593:   }
                    594: 
                    595:   eval $repair
                    596:   foreach {tn sql res} $testlist {
                    597:     if {$tclquery != ""} {
                    598:       execsql $sql
                    599:       uplevel do_test ${prefix}.$tn [list $tclquery] [list [list {*}$res]]
                    600:     } elseif {$countonly} {
                    601:       set nRow 0
                    602:       db eval $sql {incr nRow}
                    603:       uplevel do_test ${prefix}.$tn [list [list set {} $nRow]] [list $res]
                    604:     } elseif {$errfmt==""} {
                    605:       uplevel do_execsql_test ${prefix}.${tn} [list $sql] [list [list {*}$res]]
                    606:     } else {
                    607:       set res [list 1 [string trim [format $errfmt {*}$res]]]
                    608:       uplevel do_catchsql_test ${prefix}.${tn} [list $sql] [list $res]
                    609:     }
                    610:     eval $repair
                    611:   }
                    612: 
                    613: }
                    614: 
                    615: proc delete_all_data {} {
                    616:   db eval {SELECT tbl_name AS t FROM sqlite_master WHERE type = 'table'} {
                    617:     db eval "DELETE FROM '[string map {' ''} $t]'"
                    618:   }
                    619: }
                    620: 
                    621: # Run an SQL script.  
                    622: # Return the number of microseconds per statement.
                    623: #
                    624: proc speed_trial {name numstmt units sql} {
                    625:   puts -nonewline [format {%-21.21s } $name...]
                    626:   flush stdout
                    627:   set speed [time {sqlite3_exec_nr db $sql}]
                    628:   set tm [lindex $speed 0]
                    629:   if {$tm == 0} {
                    630:     set rate [format %20s "many"]
                    631:   } else {
                    632:     set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]]
                    633:   }
                    634:   set u2 $units/s
                    635:   puts [format {%12d uS %s %s} $tm $rate $u2]
                    636:   global total_time
                    637:   set total_time [expr {$total_time+$tm}]
                    638:   lappend ::speed_trial_times $name $tm
                    639: }
                    640: proc speed_trial_tcl {name numstmt units script} {
                    641:   puts -nonewline [format {%-21.21s } $name...]
                    642:   flush stdout
                    643:   set speed [time {eval $script}]
                    644:   set tm [lindex $speed 0]
                    645:   if {$tm == 0} {
                    646:     set rate [format %20s "many"]
                    647:   } else {
                    648:     set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]]
                    649:   }
                    650:   set u2 $units/s
                    651:   puts [format {%12d uS %s %s} $tm $rate $u2]
                    652:   global total_time
                    653:   set total_time [expr {$total_time+$tm}]
                    654:   lappend ::speed_trial_times $name $tm
                    655: }
                    656: proc speed_trial_init {name} {
                    657:   global total_time
                    658:   set total_time 0
                    659:   set ::speed_trial_times [list]
                    660:   sqlite3 versdb :memory:
                    661:   set vers [versdb one {SELECT sqlite_source_id()}]
                    662:   versdb close
                    663:   puts "SQLite $vers"
                    664: }
                    665: proc speed_trial_summary {name} {
                    666:   global total_time
                    667:   puts [format {%-21.21s %12d uS TOTAL} $name $total_time]
                    668: 
                    669:   if { 0 } {
                    670:     sqlite3 versdb :memory:
                    671:     set vers [lindex [versdb one {SELECT sqlite_source_id()}] 0]
                    672:     versdb close
                    673:     puts "CREATE TABLE IF NOT EXISTS time(version, script, test, us);"
                    674:     foreach {test us} $::speed_trial_times {
                    675:       puts "INSERT INTO time VALUES('$vers', '$name', '$test', $us);"
                    676:     }
                    677:   }
                    678: }
                    679: 
                    680: # Run this routine last
                    681: #
                    682: proc finish_test {} {
                    683:   catch {db close}
                    684:   catch {db2 close}
                    685:   catch {db3 close}
                    686:   if {0==[info exists ::SLAVE]} { finalize_testing }
                    687: }
                    688: proc finalize_testing {} {
                    689:   global sqlite_open_file_count
                    690: 
                    691:   set omitList [set_test_counter omit_list]
                    692: 
                    693:   catch {db close}
                    694:   catch {db2 close}
                    695:   catch {db3 close}
                    696: 
                    697:   vfs_unlink_test
                    698:   sqlite3 db {}
                    699:   # sqlite3_clear_tsd_memdebug
                    700:   db close
                    701:   sqlite3_reset_auto_extension
                    702: 
                    703:   sqlite3_soft_heap_limit 0
                    704:   set nTest [incr_ntest]
                    705:   set nErr [set_test_counter errors]
                    706: 
                    707:   puts "$nErr errors out of $nTest tests"
                    708:   if {$nErr>0} {
                    709:     puts "Failures on these tests: [set_test_counter fail_list]"
                    710:   }
                    711:   run_thread_tests 1
                    712:   if {[llength $omitList]>0} {
                    713:     puts "Omitted test cases:"
                    714:     set prec {}
                    715:     foreach {rec} [lsort $omitList] {
                    716:       if {$rec==$prec} continue
                    717:       set prec $rec
                    718:       puts [format {  %-12s %s} [lindex $rec 0] [lindex $rec 1]]
                    719:     }
                    720:   }
                    721:   if {$nErr>0 && ![working_64bit_int]} {
                    722:     puts "******************************************************************"
                    723:     puts "N.B.:  The version of TCL that you used to build this test harness"
                    724:     puts "is defective in that it does not support 64-bit integers.  Some or"
                    725:     puts "all of the test failures above might be a result from this defect"
                    726:     puts "in your TCL build."
                    727:     puts "******************************************************************"
                    728:   }
                    729:   if {$::cmdlinearg(binarylog)} {
                    730:     vfslog finalize binarylog
                    731:   }
                    732:   if {$sqlite_open_file_count} {
                    733:     puts "$sqlite_open_file_count files were left open"
                    734:     incr nErr
                    735:   }
                    736:   if {[lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1]>0 ||
                    737:               [sqlite3_memory_used]>0} {
                    738:     puts "Unfreed memory: [sqlite3_memory_used] bytes in\
                    739:          [lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1] allocations"
                    740:     incr nErr
                    741:     ifcapable memdebug||mem5||(mem3&&debug) {
                    742:       puts "Writing unfreed memory log to \"./memleak.txt\""
                    743:       sqlite3_memdebug_dump ./memleak.txt
                    744:     }
                    745:   } else {
                    746:     puts "All memory allocations freed - no leaks"
                    747:     ifcapable memdebug||mem5 {
                    748:       sqlite3_memdebug_dump ./memusage.txt
                    749:     }
                    750:   }
                    751:   show_memstats
                    752:   puts "Maximum memory usage: [sqlite3_memory_highwater 1] bytes"
                    753:   puts "Current memory usage: [sqlite3_memory_highwater] bytes"
                    754:   if {[info commands sqlite3_memdebug_malloc_count] ne ""} {
                    755:     puts "Number of malloc()  : [sqlite3_memdebug_malloc_count] calls"
                    756:   }
                    757:   if {$::cmdlinearg(malloctrace)} {
                    758:     puts "Writing mallocs.sql..."
                    759:     memdebug_log_sql
                    760:     sqlite3_memdebug_log stop
                    761:     sqlite3_memdebug_log clear
                    762: 
                    763:     if {[sqlite3_memory_used]>0} {
                    764:       puts "Writing leaks.sql..."
                    765:       sqlite3_memdebug_log sync
                    766:       memdebug_log_sql leaks.sql
                    767:     }
                    768:   }
                    769:   foreach f [glob -nocomplain test.db-*-journal] {
                    770:     forcedelete $f
                    771:   }
                    772:   foreach f [glob -nocomplain test.db-mj*] {
                    773:     forcedelete $f
                    774:   }
                    775:   exit [expr {$nErr>0}]
                    776: }
                    777: 
                    778: # Display memory statistics for analysis and debugging purposes.
                    779: #
                    780: proc show_memstats {} {
                    781:   set x [sqlite3_status SQLITE_STATUS_MEMORY_USED 0]
                    782:   set y [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0]
                    783:   set val [format {now %10d  max %10d  max-size %10d} \
                    784:               [lindex $x 1] [lindex $x 2] [lindex $y 2]]
                    785:   puts "Memory used:          $val"
                    786:   set x [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0]
                    787:   set val [format {now %10d  max %10d} [lindex $x 1] [lindex $x 2]]
                    788:   puts "Allocation count:     $val"
                    789:   set x [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0]
                    790:   set y [sqlite3_status SQLITE_STATUS_PAGECACHE_SIZE 0]
                    791:   set val [format {now %10d  max %10d  max-size %10d} \
                    792:               [lindex $x 1] [lindex $x 2] [lindex $y 2]]
                    793:   puts "Page-cache used:      $val"
                    794:   set x [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0]
                    795:   set val [format {now %10d  max %10d} [lindex $x 1] [lindex $x 2]]
                    796:   puts "Page-cache overflow:  $val"
                    797:   set x [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0]
                    798:   set val [format {now %10d  max %10d} [lindex $x 1] [lindex $x 2]]
                    799:   puts "Scratch memory used:  $val"
                    800:   set x [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0]
                    801:   set y [sqlite3_status SQLITE_STATUS_SCRATCH_SIZE 0]
                    802:   set val [format {now %10d  max %10d  max-size %10d} \
                    803:                [lindex $x 1] [lindex $x 2] [lindex $y 2]]
                    804:   puts "Scratch overflow:     $val"
                    805:   ifcapable yytrackmaxstackdepth {
                    806:     set x [sqlite3_status SQLITE_STATUS_PARSER_STACK 0]
                    807:     set val [format {               max %10d} [lindex $x 2]]
                    808:     puts "Parser stack depth:    $val"
                    809:   }
                    810: }
                    811: 
                    812: # A procedure to execute SQL
                    813: #
                    814: proc execsql {sql {db db}} {
                    815:   # puts "SQL = $sql"
                    816:   uplevel [list $db eval $sql]
                    817: }
                    818: 
                    819: # Execute SQL and catch exceptions.
                    820: #
                    821: proc catchsql {sql {db db}} {
                    822:   # puts "SQL = $sql"
                    823:   set r [catch [list uplevel [list $db eval $sql]] msg]
                    824:   lappend r $msg
                    825:   return $r
                    826: }
                    827: 
                    828: # Do an VDBE code dump on the SQL given
                    829: #
                    830: proc explain {sql {db db}} {
                    831:   puts ""
                    832:   puts "addr  opcode        p1      p2      p3      p4               p5  #"
                    833:   puts "----  ------------  ------  ------  ------  ---------------  --  -"
                    834:   $db eval "explain $sql" {} {
                    835:     puts [format {%-4d  %-12.12s  %-6d  %-6d  %-6d  % -17s %s  %s} \
                    836:       $addr $opcode $p1 $p2 $p3 $p4 $p5 $comment
                    837:     ]
                    838:   }
                    839: }
                    840: 
                    841: # Show the VDBE program for an SQL statement but omit the Trace
                    842: # opcode at the beginning.  This procedure can be used to prove
                    843: # that different SQL statements generate exactly the same VDBE code.
                    844: #
                    845: proc explain_no_trace {sql} {
                    846:   set tr [db eval "EXPLAIN $sql"]
                    847:   return [lrange $tr 7 end]
                    848: }
                    849: 
                    850: # Another procedure to execute SQL.  This one includes the field
                    851: # names in the returned list.
                    852: #
                    853: proc execsql2 {sql} {
                    854:   set result {}
                    855:   db eval $sql data {
                    856:     foreach f $data(*) {
                    857:       lappend result $f $data($f)
                    858:     }
                    859:   }
                    860:   return $result
                    861: }
                    862: 
                    863: # Use the non-callback API to execute multiple SQL statements
                    864: #
                    865: proc stepsql {dbptr sql} {
                    866:   set sql [string trim $sql]
                    867:   set r 0
                    868:   while {[string length $sql]>0} {
                    869:     if {[catch {sqlite3_prepare $dbptr $sql -1 sqltail} vm]} {
                    870:       return [list 1 $vm]
                    871:     }
                    872:     set sql [string trim $sqltail]
                    873: #    while {[sqlite_step $vm N VAL COL]=="SQLITE_ROW"} {
                    874: #      foreach v $VAL {lappend r $v}
                    875: #    }
                    876:     while {[sqlite3_step $vm]=="SQLITE_ROW"} {
                    877:       for {set i 0} {$i<[sqlite3_data_count $vm]} {incr i} {
                    878:         lappend r [sqlite3_column_text $vm $i]
                    879:       }
                    880:     }
                    881:     if {[catch {sqlite3_finalize $vm} errmsg]} {
                    882:       return [list 1 $errmsg]
                    883:     }
                    884:   }
                    885:   return $r
                    886: }
                    887: 
                    888: # Do an integrity check of the entire database
                    889: #
                    890: proc integrity_check {name {db db}} {
                    891:   ifcapable integrityck {
                    892:     do_test $name [list execsql {PRAGMA integrity_check} $db] {ok}
                    893:   }
                    894: }
                    895: 
                    896: 
                    897: # Return true if the SQL statement passed as the second argument uses a
                    898: # statement transaction.
                    899: #
                    900: proc sql_uses_stmt {db sql} {
                    901:   set stmt [sqlite3_prepare $db $sql -1 dummy]
                    902:   set uses [uses_stmt_journal $stmt]
                    903:   sqlite3_finalize $stmt
                    904:   return $uses
                    905: }
                    906: 
                    907: proc fix_ifcapable_expr {expr} {
                    908:   set ret ""
                    909:   set state 0
                    910:   for {set i 0} {$i < [string length $expr]} {incr i} {
                    911:     set char [string range $expr $i $i]
                    912:     set newstate [expr {[string is alnum $char] || $char eq "_"}]
                    913:     if {$newstate && !$state} {
                    914:       append ret {$::sqlite_options(}
                    915:     }
                    916:     if {!$newstate && $state} {
                    917:       append ret )
                    918:     }
                    919:     append ret $char
                    920:     set state $newstate
                    921:   }
                    922:   if {$state} {append ret )}
                    923:   return $ret
                    924: }
                    925: 
                    926: # Evaluate a boolean expression of capabilities.  If true, execute the
                    927: # code.  Omit the code if false.
                    928: #
                    929: proc ifcapable {expr code {else ""} {elsecode ""}} {
                    930:   #regsub -all {[a-z_0-9]+} $expr {$::sqlite_options(&)} e2
                    931:   set e2 [fix_ifcapable_expr $expr]
                    932:   if ($e2) {
                    933:     set c [catch {uplevel 1 $code} r]
                    934:   } else {
                    935:     set c [catch {uplevel 1 $elsecode} r]
                    936:   }
                    937:   return -code $c $r
                    938: }
                    939: 
                    940: # This proc execs a seperate process that crashes midway through executing
                    941: # the SQL script $sql on database test.db.
                    942: #
                    943: # The crash occurs during a sync() of file $crashfile. When the crash
                    944: # occurs a random subset of all unsynced writes made by the process are
                    945: # written into the files on disk. Argument $crashdelay indicates the
                    946: # number of file syncs to wait before crashing.
                    947: #
                    948: # The return value is a list of two elements. The first element is a
                    949: # boolean, indicating whether or not the process actually crashed or
                    950: # reported some other error. The second element in the returned list is the
                    951: # error message. This is "child process exited abnormally" if the crash
                    952: # occured.
                    953: #
                    954: #   crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql
                    955: #
                    956: proc crashsql {args} {
                    957: 
                    958:   set blocksize ""
                    959:   set crashdelay 1
                    960:   set prngseed 0
                    961:   set tclbody {}
                    962:   set crashfile ""
                    963:   set dc ""
                    964:   set sql [lindex $args end]
                    965:   
                    966:   for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} {
                    967:     set z [lindex $args $ii]
                    968:     set n [string length $z]
                    969:     set z2 [lindex $args [expr $ii+1]]
                    970: 
                    971:     if     {$n>1 && [string first $z -delay]==0}     {set crashdelay $z2} \
                    972:     elseif {$n>1 && [string first $z -seed]==0}      {set prngseed $z2} \
                    973:     elseif {$n>1 && [string first $z -file]==0}      {set crashfile $z2}  \
                    974:     elseif {$n>1 && [string first $z -tclbody]==0}   {set tclbody $z2}  \
                    975:     elseif {$n>1 && [string first $z -blocksize]==0} {set blocksize "-s $z2" } \
                    976:     elseif {$n>1 && [string first $z -characteristics]==0} {set dc "-c {$z2}" } \
                    977:     else   { error "Unrecognized option: $z" }
                    978:   }
                    979: 
                    980:   if {$crashfile eq ""} {
                    981:     error "Compulsory option -file missing"
                    982:   }
                    983: 
                    984:   # $crashfile gets compared to the native filename in 
                    985:   # cfSync(), which can be different then what TCL uses by
                    986:   # default, so here we force it to the "nativename" format.
                    987:   set cfile [string map {\\ \\\\} [file nativename [file join [pwd] $crashfile]]]
                    988: 
                    989:   set f [open crash.tcl w]
                    990:   puts $f "sqlite3_crash_enable 1"
                    991:   puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
                    992:   puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
                    993:   puts $f "sqlite3 db test.db -vfs crash"
                    994: 
                    995:   # This block sets the cache size of the main database to 10
                    996:   # pages. This is done in case the build is configured to omit
                    997:   # "PRAGMA cache_size".
                    998:   puts $f {db eval {SELECT * FROM sqlite_master;}}
                    999:   puts $f {set bt [btree_from_db db]}
                   1000:   puts $f {btree_set_cache_size $bt 10}
                   1001:   if {$prngseed} {
                   1002:     set seed [expr {$prngseed%10007+1}]
                   1003:     # puts seed=$seed
                   1004:     puts $f "db eval {SELECT randomblob($seed)}"
                   1005:   }
                   1006: 
                   1007:   if {[string length $tclbody]>0} {
                   1008:     puts $f $tclbody
                   1009:   }
                   1010:   if {[string length $sql]>0} {
                   1011:     puts $f "db eval {"
                   1012:     puts $f   "$sql"
                   1013:     puts $f "}"
                   1014:   }
                   1015:   close $f
                   1016:   set r [catch {
                   1017:     exec [info nameofexec] crash.tcl >@stdout
                   1018:   } msg]
                   1019:   
                   1020:   # Windows/ActiveState TCL returns a slightly different
                   1021:   # error message.  We map that to the expected message
                   1022:   # so that we don't have to change all of the test
                   1023:   # cases.
                   1024:   if {$::tcl_platform(platform)=="windows"} {
                   1025:     if {$msg=="child killed: unknown signal"} {
                   1026:       set msg "child process exited abnormally"
                   1027:     }
                   1028:   }
                   1029:   
                   1030:   lappend r $msg
                   1031: }
                   1032: 
                   1033: # Usage: do_ioerr_test <test number> <options...>
                   1034: #
                   1035: # This proc is used to implement test cases that check that IO errors
                   1036: # are correctly handled. The first argument, <test number>, is an integer 
                   1037: # used to name the tests executed by this proc. Options are as follows:
                   1038: #
                   1039: #     -tclprep          TCL script to run to prepare test.
                   1040: #     -sqlprep          SQL script to run to prepare test.
                   1041: #     -tclbody          TCL script to run with IO error simulation.
                   1042: #     -sqlbody          TCL script to run with IO error simulation.
                   1043: #     -exclude          List of 'N' values not to test.
                   1044: #     -erc              Use extended result codes
                   1045: #     -persist          Make simulated I/O errors persistent
                   1046: #     -start            Value of 'N' to begin with (default 1)
                   1047: #
                   1048: #     -cksum            Boolean. If true, test that the database does
                   1049: #                       not change during the execution of the test case.
                   1050: #
                   1051: proc do_ioerr_test {testname args} {
                   1052: 
                   1053:   set ::ioerropts(-start) 1
                   1054:   set ::ioerropts(-cksum) 0
                   1055:   set ::ioerropts(-erc) 0
                   1056:   set ::ioerropts(-count) 100000000
                   1057:   set ::ioerropts(-persist) 1
                   1058:   set ::ioerropts(-ckrefcount) 0
                   1059:   set ::ioerropts(-restoreprng) 1
                   1060:   array set ::ioerropts $args
                   1061: 
                   1062:   # TEMPORARY: For 3.5.9, disable testing of extended result codes. There are
                   1063:   # a couple of obscure IO errors that do not return them.
                   1064:   set ::ioerropts(-erc) 0
                   1065: 
                   1066:   set ::go 1
                   1067:   #reset_prng_state
                   1068:   save_prng_state
                   1069:   for {set n $::ioerropts(-start)} {$::go} {incr n} {
                   1070:     set ::TN $n
                   1071:     incr ::ioerropts(-count) -1
                   1072:     if {$::ioerropts(-count)<0} break
                   1073:  
                   1074:     # Skip this IO error if it was specified with the "-exclude" option.
                   1075:     if {[info exists ::ioerropts(-exclude)]} {
                   1076:       if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
                   1077:     }
                   1078:     if {$::ioerropts(-restoreprng)} {
                   1079:       restore_prng_state
                   1080:     }
                   1081: 
                   1082:     # Delete the files test.db and test2.db, then execute the TCL and 
                   1083:     # SQL (in that order) to prepare for the test case.
                   1084:     do_test $testname.$n.1 {
                   1085:       set ::sqlite_io_error_pending 0
                   1086:       catch {db close}
                   1087:       catch {db2 close}
                   1088:       catch {forcedelete test.db}
                   1089:       catch {forcedelete test.db-journal}
                   1090:       catch {forcedelete test2.db}
                   1091:       catch {forcedelete test2.db-journal}
                   1092:       set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
                   1093:       sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
                   1094:       if {[info exists ::ioerropts(-tclprep)]} {
                   1095:         eval $::ioerropts(-tclprep)
                   1096:       }
                   1097:       if {[info exists ::ioerropts(-sqlprep)]} {
                   1098:         execsql $::ioerropts(-sqlprep)
                   1099:       }
                   1100:       expr 0
                   1101:     } {0}
                   1102: 
                   1103:     # Read the 'checksum' of the database.
                   1104:     if {$::ioerropts(-cksum)} {
                   1105:       set checksum [cksum]
                   1106:     }
                   1107: 
                   1108:     # Set the Nth IO error to fail.
                   1109:     do_test $testname.$n.2 [subst {
                   1110:       set ::sqlite_io_error_persist $::ioerropts(-persist)
                   1111:       set ::sqlite_io_error_pending $n
                   1112:     }] $n
                   1113:   
                   1114:     # Create a single TCL script from the TCL and SQL specified
                   1115:     # as the body of the test.
                   1116:     set ::ioerrorbody {}
                   1117:     if {[info exists ::ioerropts(-tclbody)]} {
                   1118:       append ::ioerrorbody "$::ioerropts(-tclbody)\n"
                   1119:     }
                   1120:     if {[info exists ::ioerropts(-sqlbody)]} {
                   1121:       append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
                   1122:     }
                   1123: 
                   1124:     # Execute the TCL Script created in the above block. If
                   1125:     # there are at least N IO operations performed by SQLite as
                   1126:     # a result of the script, the Nth will fail.
                   1127:     do_test $testname.$n.3 {
                   1128:       set ::sqlite_io_error_hit 0
                   1129:       set ::sqlite_io_error_hardhit 0
                   1130:       set r [catch $::ioerrorbody msg]
                   1131:       set ::errseen $r
                   1132:       set rc [sqlite3_errcode $::DB]
                   1133:       if {$::ioerropts(-erc)} {
                   1134:         # If we are in extended result code mode, make sure all of the
                   1135:         # IOERRs we get back really do have their extended code values.
                   1136:         # If an extended result code is returned, the sqlite3_errcode
                   1137:         # TCLcommand will return a string of the form:  SQLITE_IOERR+nnnn
                   1138:         # where nnnn is a number
                   1139:         if {[regexp {^SQLITE_IOERR} $rc] && ![regexp {IOERR\+\d} $rc]} {
                   1140:           return $rc
                   1141:         }
                   1142:       } else {
                   1143:         # If we are not in extended result code mode, make sure no
                   1144:         # extended error codes are returned.
                   1145:         if {[regexp {\+\d} $rc]} {
                   1146:           return $rc
                   1147:         }
                   1148:       }
                   1149:       # The test repeats as long as $::go is non-zero.  $::go starts out
                   1150:       # as 1.  When a test runs to completion without hitting an I/O
                   1151:       # error, that means there is no point in continuing with this test
                   1152:       # case so set $::go to zero.
                   1153:       #
                   1154:       if {$::sqlite_io_error_pending>0} {
                   1155:         set ::go 0
                   1156:         set q 0
                   1157:         set ::sqlite_io_error_pending 0
                   1158:       } else {
                   1159:         set q 1
                   1160:       }
                   1161: 
                   1162:       set s [expr $::sqlite_io_error_hit==0]
                   1163:       if {$::sqlite_io_error_hit>$::sqlite_io_error_hardhit && $r==0} {
                   1164:         set r 1
                   1165:       }
                   1166:       set ::sqlite_io_error_hit 0
                   1167: 
                   1168:       # One of two things must have happened. either
                   1169:       #   1.  We never hit the IO error and the SQL returned OK
                   1170:       #   2.  An IO error was hit and the SQL failed
                   1171:       #
                   1172:       #puts "s=$s r=$r q=$q"
                   1173:       expr { ($s && !$r && !$q) || (!$s && $r && $q) }
                   1174:     } {1}
                   1175: 
                   1176:     set ::sqlite_io_error_hit 0
                   1177:     set ::sqlite_io_error_pending 0
                   1178: 
                   1179:     # Check that no page references were leaked. There should be 
                   1180:     # a single reference if there is still an active transaction, 
                   1181:     # or zero otherwise.
                   1182:     #
                   1183:     # UPDATE: If the IO error occurs after a 'BEGIN' but before any
                   1184:     # locks are established on database files (i.e. if the error 
                   1185:     # occurs while attempting to detect a hot-journal file), then
                   1186:     # there may 0 page references and an active transaction according
                   1187:     # to [sqlite3_get_autocommit].
                   1188:     #
                   1189:     if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-ckrefcount)} {
                   1190:       do_test $testname.$n.4 {
                   1191:         set bt [btree_from_db db]
                   1192:         db_enter db
                   1193:         array set stats [btree_pager_stats $bt]
                   1194:         db_leave db
                   1195:         set nRef $stats(ref)
                   1196:         expr {$nRef == 0 || ([sqlite3_get_autocommit db]==0 && $nRef == 1)}
                   1197:       } {1}
                   1198:     }
                   1199: 
                   1200:     # If there is an open database handle and no open transaction, 
                   1201:     # and the pager is not running in exclusive-locking mode,
                   1202:     # check that the pager is in "unlocked" state. Theoretically,
                   1203:     # if a call to xUnlock() failed due to an IO error the underlying
                   1204:     # file may still be locked.
                   1205:     #
                   1206:     ifcapable pragma {
                   1207:       if { [info commands db] ne ""
                   1208:         && $::ioerropts(-ckrefcount)
                   1209:         && [db one {pragma locking_mode}] eq "normal"
                   1210:         && [sqlite3_get_autocommit db]
                   1211:       } {
                   1212:         do_test $testname.$n.5 {
                   1213:           set bt [btree_from_db db]
                   1214:           db_enter db
                   1215:           array set stats [btree_pager_stats $bt]
                   1216:           db_leave db
                   1217:           set stats(state)
                   1218:         } 0
                   1219:       }
                   1220:     }
                   1221: 
                   1222:     # If an IO error occured, then the checksum of the database should
                   1223:     # be the same as before the script that caused the IO error was run.
                   1224:     #
                   1225:     if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} {
                   1226:       do_test $testname.$n.6 {
                   1227:         catch {db close}
                   1228:         catch {db2 close}
                   1229:         set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
                   1230:         cksum
                   1231:       } $checksum
                   1232:     }
                   1233: 
                   1234:     set ::sqlite_io_error_hardhit 0
                   1235:     set ::sqlite_io_error_pending 0
                   1236:     if {[info exists ::ioerropts(-cleanup)]} {
                   1237:       catch $::ioerropts(-cleanup)
                   1238:     }
                   1239:   }
                   1240:   set ::sqlite_io_error_pending 0
                   1241:   set ::sqlite_io_error_persist 0
                   1242:   unset ::ioerropts
                   1243: }
                   1244: 
                   1245: # Return a checksum based on the contents of the main database associated
                   1246: # with connection $db
                   1247: #
                   1248: proc cksum {{db db}} {
                   1249:   set txt [$db eval {
                   1250:       SELECT name, type, sql FROM sqlite_master order by name
                   1251:   }]\n
                   1252:   foreach tbl [$db eval {
                   1253:       SELECT name FROM sqlite_master WHERE type='table' order by name
                   1254:   }] {
                   1255:     append txt [$db eval "SELECT * FROM $tbl"]\n
                   1256:   }
                   1257:   foreach prag {default_synchronous default_cache_size} {
                   1258:     append txt $prag-[$db eval "PRAGMA $prag"]\n
                   1259:   }
                   1260:   set cksum [string length $txt]-[md5 $txt]
                   1261:   # puts $cksum-[file size test.db]
                   1262:   return $cksum
                   1263: }
                   1264: 
                   1265: # Generate a checksum based on the contents of the main and temp tables
                   1266: # database $db. If the checksum of two databases is the same, and the
                   1267: # integrity-check passes for both, the two databases are identical.
                   1268: #
                   1269: proc allcksum {{db db}} {
                   1270:   set ret [list]
                   1271:   ifcapable tempdb {
                   1272:     set sql {
                   1273:       SELECT name FROM sqlite_master WHERE type = 'table' UNION
                   1274:       SELECT name FROM sqlite_temp_master WHERE type = 'table' UNION
                   1275:       SELECT 'sqlite_master' UNION
                   1276:       SELECT 'sqlite_temp_master' ORDER BY 1
                   1277:     }
                   1278:   } else {
                   1279:     set sql {
                   1280:       SELECT name FROM sqlite_master WHERE type = 'table' UNION
                   1281:       SELECT 'sqlite_master' ORDER BY 1
                   1282:     }
                   1283:   }
                   1284:   set tbllist [$db eval $sql]
                   1285:   set txt {}
                   1286:   foreach tbl $tbllist {
                   1287:     append txt [$db eval "SELECT * FROM $tbl"]
                   1288:   }
                   1289:   foreach prag {default_cache_size} {
                   1290:     append txt $prag-[$db eval "PRAGMA $prag"]\n
                   1291:   }
                   1292:   # puts txt=$txt
                   1293:   return [md5 $txt]
                   1294: }
                   1295: 
                   1296: # Generate a checksum based on the contents of a single database with
                   1297: # a database connection.  The name of the database is $dbname.  
                   1298: # Examples of $dbname are "temp" or "main".
                   1299: #
                   1300: proc dbcksum {db dbname} {
                   1301:   if {$dbname=="temp"} {
                   1302:     set master sqlite_temp_master
                   1303:   } else {
                   1304:     set master $dbname.sqlite_master
                   1305:   }
                   1306:   set alltab [$db eval "SELECT name FROM $master WHERE type='table'"]
                   1307:   set txt [$db eval "SELECT * FROM $master"]\n
                   1308:   foreach tab $alltab {
                   1309:     append txt [$db eval "SELECT * FROM $dbname.$tab"]\n
                   1310:   }
                   1311:   return [md5 $txt]
                   1312: }
                   1313: 
                   1314: proc memdebug_log_sql {{filename mallocs.sql}} {
                   1315: 
                   1316:   set data [sqlite3_memdebug_log dump]
                   1317:   set nFrame [expr [llength [lindex $data 0]]-2]
                   1318:   if {$nFrame < 0} { return "" }
                   1319: 
                   1320:   set database temp
                   1321: 
                   1322:   set tbl "CREATE TABLE ${database}.malloc(zTest, nCall, nByte, lStack);"
                   1323: 
                   1324:   set sql ""
                   1325:   foreach e $data {
                   1326:     set nCall [lindex $e 0]
                   1327:     set nByte [lindex $e 1]
                   1328:     set lStack [lrange $e 2 end]
                   1329:     append sql "INSERT INTO ${database}.malloc VALUES"
                   1330:     append sql "('test', $nCall, $nByte, '$lStack');\n"
                   1331:     foreach f $lStack {
                   1332:       set frames($f) 1
                   1333:     }
                   1334:   }
                   1335: 
                   1336:   set tbl2 "CREATE TABLE ${database}.frame(frame INTEGER PRIMARY KEY, line);\n"
                   1337:   set tbl3 "CREATE TABLE ${database}.file(name PRIMARY KEY, content);\n"
                   1338: 
                   1339:   foreach f [array names frames] {
                   1340:     set addr [format %x $f]
                   1341:     set cmd "addr2line -e [info nameofexec] $addr"
                   1342:     set line [eval exec $cmd]
                   1343:     append sql "INSERT INTO ${database}.frame VALUES($f, '$line');\n"
                   1344: 
                   1345:     set file [lindex [split $line :] 0]
                   1346:     set files($file) 1
                   1347:   }
                   1348: 
                   1349:   foreach f [array names files] {
                   1350:     set contents ""
                   1351:     catch {
                   1352:       set fd [open $f]
                   1353:       set contents [read $fd]
                   1354:       close $fd
                   1355:     }
                   1356:     set contents [string map {' ''} $contents]
                   1357:     append sql "INSERT INTO ${database}.file VALUES('$f', '$contents');\n"
                   1358:   }
                   1359: 
                   1360:   set fd [open $filename w]
                   1361:   puts $fd "BEGIN; ${tbl}${tbl2}${tbl3}${sql} ; COMMIT;"
                   1362:   close $fd
                   1363: }
                   1364: 
                   1365: # Drop all tables in database [db]
                   1366: proc drop_all_tables {{db db}} {
                   1367:   ifcapable trigger&&foreignkey {
                   1368:     set pk [$db one "PRAGMA foreign_keys"]
                   1369:     $db eval "PRAGMA foreign_keys = OFF"
                   1370:   }
                   1371:   foreach {idx name file} [db eval {PRAGMA database_list}] {
                   1372:     if {$idx==1} {
                   1373:       set master sqlite_temp_master
                   1374:     } else {
                   1375:       set master $name.sqlite_master
                   1376:     }
                   1377:     foreach {t type} [$db eval "
                   1378:       SELECT name, type FROM $master
                   1379:       WHERE type IN('table', 'view') AND name NOT LIKE 'sqliteX_%' ESCAPE 'X'
                   1380:     "] {
                   1381:       $db eval "DROP $type \"$t\""
                   1382:     }
                   1383:   }
                   1384:   ifcapable trigger&&foreignkey {
                   1385:     $db eval "PRAGMA foreign_keys = $pk"
                   1386:   }
                   1387: }
                   1388: 
                   1389: #-------------------------------------------------------------------------
                   1390: # If a test script is executed with global variable $::G(perm:name) set to
                   1391: # "wal", then the tests are run in WAL mode. Otherwise, they should be run 
                   1392: # in rollback mode. The following Tcl procs are used to make this less 
                   1393: # intrusive:
                   1394: #
                   1395: #   wal_set_journal_mode ?DB?
                   1396: #
                   1397: #     If running a WAL test, execute "PRAGMA journal_mode = wal" using
                   1398: #     connection handle DB. Otherwise, this command is a no-op.
                   1399: #
                   1400: #   wal_check_journal_mode TESTNAME ?DB?
                   1401: #
                   1402: #     If running a WAL test, execute a tests case that fails if the main
                   1403: #     database for connection handle DB is not currently a WAL database.
                   1404: #     Otherwise (if not running a WAL permutation) this is a no-op.
                   1405: #
                   1406: #   wal_is_wal_mode
                   1407: #   
                   1408: #     Returns true if this test should be run in WAL mode. False otherwise.
                   1409: # 
                   1410: proc wal_is_wal_mode {} {
                   1411:   expr {[permutation] eq "wal"}
                   1412: }
                   1413: proc wal_set_journal_mode {{db db}} {
                   1414:   if { [wal_is_wal_mode] } {
                   1415:     $db eval "PRAGMA journal_mode = WAL"
                   1416:   }
                   1417: }
                   1418: proc wal_check_journal_mode {testname {db db}} {
                   1419:   if { [wal_is_wal_mode] } {
                   1420:     $db eval { SELECT * FROM sqlite_master }
                   1421:     do_test $testname [list $db eval "PRAGMA main.journal_mode"] {wal}
                   1422:   }
                   1423: }
                   1424: 
                   1425: proc permutation {} {
                   1426:   set perm ""
                   1427:   catch {set perm $::G(perm:name)}
                   1428:   set perm
                   1429: }
                   1430: proc presql {} {
                   1431:   set presql ""
                   1432:   catch {set presql $::G(perm:presql)}
                   1433:   set presql
                   1434: }
                   1435: 
                   1436: #-------------------------------------------------------------------------
                   1437: #
                   1438: proc slave_test_script {script} {
                   1439: 
                   1440:   # Create the interpreter used to run the test script.
                   1441:   interp create tinterp
                   1442: 
                   1443:   # Populate some global variables that tester.tcl expects to see.
                   1444:   foreach {var value} [list              \
                   1445:     ::argv0 $::argv0                     \
                   1446:     ::argv  {}                           \
                   1447:     ::SLAVE 1                            \
                   1448:   ] {
                   1449:     interp eval tinterp [list set $var $value]
                   1450:   }
                   1451: 
                   1452:   # The alias used to access the global test counters.
                   1453:   tinterp alias set_test_counter set_test_counter
                   1454: 
                   1455:   # Set up the ::cmdlinearg array in the slave.
                   1456:   interp eval tinterp [list array set ::cmdlinearg [array get ::cmdlinearg]]
                   1457: 
                   1458:   # Set up the ::G array in the slave.
                   1459:   interp eval tinterp [list array set ::G [array get ::G]]
                   1460: 
                   1461:   # Load the various test interfaces implemented in C.
                   1462:   load_testfixture_extensions tinterp
                   1463: 
                   1464:   # Run the test script.
                   1465:   interp eval tinterp $script
                   1466: 
                   1467:   # Check if the interpreter call [run_thread_tests]
                   1468:   if { [interp eval tinterp {info exists ::run_thread_tests_called}] } {
                   1469:     set ::run_thread_tests_called 1
                   1470:   }
                   1471: 
                   1472:   # Delete the interpreter used to run the test script.
                   1473:   interp delete tinterp
                   1474: }
                   1475: 
                   1476: proc slave_test_file {zFile} {
                   1477:   set tail [file tail $zFile]
                   1478: 
                   1479:   if {[info exists ::G(start:permutation)]} {
                   1480:     if {[permutation] != $::G(start:permutation)} return
                   1481:     unset ::G(start:permutation)
                   1482:   }
                   1483:   if {[info exists ::G(start:file)]} {
                   1484:     if {$tail != $::G(start:file) && $tail!="$::G(start:file).test"} return
                   1485:     unset ::G(start:file)
                   1486:   }
                   1487: 
                   1488:   # Remember the value of the shared-cache setting. So that it is possible
                   1489:   # to check afterwards that it was not modified by the test script.
                   1490:   #
                   1491:   ifcapable shared_cache { set scs [sqlite3_enable_shared_cache] }
                   1492: 
                   1493:   # Run the test script in a slave interpreter.
                   1494:   #
                   1495:   unset -nocomplain ::run_thread_tests_called
                   1496:   reset_prng_state
                   1497:   set ::sqlite_open_file_count 0
                   1498:   set time [time { slave_test_script [list source $zFile] }]
                   1499:   set ms [expr [lindex $time 0] / 1000]
                   1500: 
                   1501:   # Test that all files opened by the test script were closed. Omit this
                   1502:   # if the test script has "thread" in its name. The open file counter
                   1503:   # is not thread-safe.
                   1504:   #
                   1505:   if {[info exists ::run_thread_tests_called]==0} {
                   1506:     do_test ${tail}-closeallfiles { expr {$::sqlite_open_file_count>0} } {0}
                   1507:   }
                   1508:   set ::sqlite_open_file_count 0
                   1509: 
                   1510:   # Test that the global "shared-cache" setting was not altered by 
                   1511:   # the test script.
                   1512:   #
                   1513:   ifcapable shared_cache { 
                   1514:     set res [expr {[sqlite3_enable_shared_cache] == $scs}]
                   1515:     do_test ${tail}-sharedcachesetting [list set {} $res] 1
                   1516:   }
                   1517: 
                   1518:   # Add some info to the output.
                   1519:   #
                   1520:   puts "Time: $tail $ms ms"
                   1521:   show_memstats
                   1522: }
                   1523: 
                   1524: # Open a new connection on database test.db and execute the SQL script
                   1525: # supplied as an argument. Before returning, close the new conection and
                   1526: # restore the 4 byte fields starting at header offsets 28, 92 and 96
                   1527: # to the values they held before the SQL was executed. This simulates
                   1528: # a write by a pre-3.7.0 client.
                   1529: #
                   1530: proc sql36231 {sql} {
                   1531:   set B [hexio_read test.db 92 8]
                   1532:   set A [hexio_read test.db 28 4]
                   1533:   sqlite3 db36231 test.db
                   1534:   catch { db36231 func a_string a_string }
                   1535:   execsql $sql db36231
                   1536:   db36231 close
                   1537:   hexio_write test.db 28 $A
                   1538:   hexio_write test.db 92 $B
                   1539:   return ""
                   1540: }
                   1541: 
                   1542: proc db_save {} {
                   1543:   foreach f [glob -nocomplain sv_test.db*] { forcedelete $f }
                   1544:   foreach f [glob -nocomplain test.db*] {
                   1545:     set f2 "sv_$f"
                   1546:     forcecopy $f $f2
                   1547:   }
                   1548: }
                   1549: proc db_save_and_close {} {
                   1550:   db_save
                   1551:   catch { db close }
                   1552:   return ""
                   1553: }
                   1554: proc db_restore {} {
                   1555:   foreach f [glob -nocomplain test.db*] { forcedelete $f }
                   1556:   foreach f2 [glob -nocomplain sv_test.db*] {
                   1557:     set f [string range $f2 3 end]
                   1558:     forcecopy $f2 $f
                   1559:   }
                   1560: }
                   1561: proc db_restore_and_reopen {{dbfile test.db}} {
                   1562:   catch { db close }
                   1563:   db_restore
                   1564:   sqlite3 db $dbfile
                   1565: }
                   1566: proc db_delete_and_reopen {{file test.db}} {
                   1567:   catch { db close }
                   1568:   foreach f [glob -nocomplain test.db*] { forcedelete $f }
                   1569:   sqlite3 db $file
                   1570: }
                   1571: 
                   1572: # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
                   1573: # to non-zero, then set the global variable $AUTOVACUUM to 1.
                   1574: set AUTOVACUUM $sqlite_options(default_autovacuum)
                   1575: 
                   1576: source $testdir/thread_common.tcl
                   1577: source $testdir/malloc_common.tcl

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>