xref: /sqlite-3.40.0/test/tester.tcl (revision b8eaf9a1)
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#      is_relative_file
23#      test_pwd
24#      get_pwd
25#      copy_file              FROM TO
26#      delete_file            FILENAME
27#      drop_all_tables        ?DB?
28#      drop_all_indexes       ?DB?
29#      forcecopy              FROM TO
30#      forcedelete            FILENAME
31#
32# Test the capability of the SQLite version built into the interpreter to
33# determine if a specific test can be run:
34#
35#      capable                EXPR
36#      ifcapable              EXPR
37#
38# Calulate checksums based on database contents:
39#
40#      dbcksum                DB DBNAME
41#      allcksum               ?DB?
42#      cksum                  ?DB?
43#
44# Commands to execute/explain SQL statements:
45#
46#      memdbsql               SQL
47#      stepsql                DB SQL
48#      execsql2               SQL
49#      explain_no_trace       SQL
50#      explain                SQL ?DB?
51#      catchsql               SQL ?DB?
52#      execsql                SQL ?DB?
53#
54# Commands to run test cases:
55#
56#      do_ioerr_test          TESTNAME ARGS...
57#      crashsql               ARGS...
58#      integrity_check        TESTNAME ?DB?
59#      verify_ex_errcode      TESTNAME EXPECTED ?DB?
60#      do_test                TESTNAME SCRIPT EXPECTED
61#      do_execsql_test        TESTNAME SQL EXPECTED
62#      do_catchsql_test       TESTNAME SQL EXPECTED
63#      do_timed_execsql_test  TESTNAME SQL EXPECTED
64#
65# Commands providing a lower level interface to the global test counters:
66#
67#      set_test_counter       COUNTER ?VALUE?
68#      omit_test              TESTNAME REASON ?APPEND?
69#      fail_test              TESTNAME
70#      incr_ntest
71#
72# Command run at the end of each test file:
73#
74#      finish_test
75#
76# Commands to help create test files that run with the "WAL" and other
77# permutations (see file permutations.test):
78#
79#      wal_is_wal_mode
80#      wal_set_journal_mode   ?DB?
81#      wal_check_journal_mode TESTNAME?DB?
82#      permutation
83#      presql
84#
85# Command to test whether or not --verbose=1 was specified on the command
86# line (returns 0 for not-verbose, 1 for verbose and 2 for "verbose in the
87# output file only").
88#
89#      verbose
90#
91
92# Only run this script once.  If sourced a second time, make it a no-op
93if {[info exists ::tester_tcl_has_run]} return
94
95# Set the precision of FP arithmatic used by the interpreter. And
96# configure SQLite to take database file locks on the page that begins
97# 64KB into the database file instead of the one 1GB in. This means
98# the code that handles that special case can be tested without creating
99# very large database files.
100#
101set tcl_precision 15
102sqlite3_test_control_pending_byte 0x0010000
103
104
105# If the pager codec is available, create a wrapper for the [sqlite3]
106# command that appends "-key {xyzzy}" to the command line. i.e. this:
107#
108#     sqlite3 db test.db
109#
110# becomes
111#
112#     sqlite3 db test.db -key {xyzzy}
113#
114if {[info command sqlite_orig]==""} {
115  rename sqlite3 sqlite_orig
116  proc sqlite3 {args} {
117    if {[llength $args]>=2 && [string index [lindex $args 0] 0]!="-"} {
118      # This command is opening a new database connection.
119      #
120      if {[info exists ::G(perm:sqlite3_args)]} {
121        set args [concat $args $::G(perm:sqlite3_args)]
122      }
123      if {[sqlite_orig -has-codec] && ![info exists ::do_not_use_codec]} {
124        lappend args -key {xyzzy}
125      }
126
127      set res [uplevel 1 sqlite_orig $args]
128      if {[info exists ::G(perm:presql)]} {
129        [lindex $args 0] eval $::G(perm:presql)
130      }
131      if {[info exists ::G(perm:dbconfig)]} {
132        set ::dbhandle [lindex $args 0]
133        uplevel #0 $::G(perm:dbconfig)
134      }
135      [lindex $args 0] cache size 3
136      set res
137    } else {
138      # This command is not opening a new database connection. Pass the
139      # arguments through to the C implementation as the are.
140      #
141      uplevel 1 sqlite_orig $args
142    }
143  }
144}
145
146proc getFileRetries {} {
147  if {![info exists ::G(file-retries)]} {
148    #
149    # NOTE: Return the default number of retries for [file] operations.  A
150    #       value of zero or less here means "disabled".
151    #
152    return [expr {$::tcl_platform(platform) eq "windows" ? 50 : 0}]
153  }
154  return $::G(file-retries)
155}
156
157proc getFileRetryDelay {} {
158  if {![info exists ::G(file-retry-delay)]} {
159    #
160    # NOTE: Return the default number of milliseconds to wait when retrying
161    #       failed [file] operations.  A value of zero or less means "do not
162    #       wait".
163    #
164    return 100; # TODO: Good default?
165  }
166  return $::G(file-retry-delay)
167}
168
169# Return the string representing the name of the current directory.  On
170# Windows, the result is "normalized" to whatever our parent command shell
171# is using to prevent case-mismatch issues.
172#
173proc get_pwd {} {
174  if {$::tcl_platform(platform) eq "windows"} {
175    #
176    # NOTE: Cannot use [file normalize] here because it would alter the
177    #       case of the result to what Tcl considers canonical, which would
178    #       defeat the purpose of this procedure.
179    #
180    if {[info exists ::env(ComSpec)]} {
181      set comSpec $::env(ComSpec)
182    } else {
183      # NOTE: Hard-code the typical default value.
184      set comSpec {C:\Windows\system32\cmd.exe}
185    }
186    return [string map [list \\ /] \
187        [string trim [exec -- $comSpec /c CD]]]
188  } else {
189    return [pwd]
190  }
191}
192
193# Copy file $from into $to. This is used because some versions of
194# TCL for windows (notably the 8.4.1 binary package shipped with the
195# current mingw release) have a broken "file copy" command.
196#
197proc copy_file {from to} {
198  do_copy_file false $from $to
199}
200
201proc forcecopy {from to} {
202  do_copy_file true $from $to
203}
204
205proc do_copy_file {force from to} {
206  set nRetry [getFileRetries]     ;# Maximum number of retries.
207  set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
208
209  # On windows, sometimes even a [file copy -force] can fail. The cause is
210  # usually "tag-alongs" - programs like anti-virus software, automatic backup
211  # tools and various explorer extensions that keep a file open a little longer
212  # than we expect, causing the delete to fail.
213  #
214  # The solution is to wait a short amount of time before retrying the copy.
215  #
216  if {$nRetry > 0} {
217    for {set i 0} {$i<$nRetry} {incr i} {
218      set rc [catch {
219        if {$force} {
220          file copy -force $from $to
221        } else {
222          file copy $from $to
223        }
224      } msg]
225      if {$rc==0} break
226      if {$nDelay > 0} { after $nDelay }
227    }
228    if {$rc} { error $msg }
229  } else {
230    if {$force} {
231      file copy -force $from $to
232    } else {
233      file copy $from $to
234    }
235  }
236}
237
238# Check if a file name is relative
239#
240proc is_relative_file { file } {
241  return [expr {[file pathtype $file] != "absolute"}]
242}
243
244# If the VFS supports using the current directory, returns [pwd];
245# otherwise, it returns only the provided suffix string (which is
246# empty by default).
247#
248proc test_pwd { args } {
249  if {[llength $args] > 0} {
250    set suffix1 [lindex $args 0]
251    if {[llength $args] > 1} {
252      set suffix2 [lindex $args 1]
253    } else {
254      set suffix2 $suffix1
255    }
256  } else {
257    set suffix1 ""; set suffix2 ""
258  }
259  ifcapable curdir {
260    return "[get_pwd]$suffix1"
261  } else {
262    return $suffix2
263  }
264}
265
266# Delete a file or directory
267#
268proc delete_file {args} {
269  do_delete_file false {*}$args
270}
271
272proc forcedelete {args} {
273  do_delete_file true {*}$args
274}
275
276proc do_delete_file {force args} {
277  set nRetry [getFileRetries]     ;# Maximum number of retries.
278  set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
279
280  foreach filename $args {
281    # On windows, sometimes even a [file delete -force] can fail just after
282    # a file is closed. The cause is usually "tag-alongs" - programs like
283    # anti-virus software, automatic backup tools and various explorer
284    # extensions that keep a file open a little longer than we expect, causing
285    # the delete to fail.
286    #
287    # The solution is to wait a short amount of time before retrying the
288    # delete.
289    #
290    if {$nRetry > 0} {
291      for {set i 0} {$i<$nRetry} {incr i} {
292        set rc [catch {
293          if {$force} {
294            file delete -force $filename
295          } else {
296            file delete $filename
297          }
298        } msg]
299        if {$rc==0} break
300        if {$nDelay > 0} { after $nDelay }
301      }
302      if {$rc} { error $msg }
303    } else {
304      if {$force} {
305        file delete -force $filename
306      } else {
307        file delete $filename
308      }
309    }
310  }
311}
312
313if {$::tcl_platform(platform) eq "windows"} {
314  proc do_remove_win32_dir {args} {
315    set nRetry [getFileRetries]     ;# Maximum number of retries.
316    set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
317
318    foreach dirName $args {
319      # On windows, sometimes even a [remove_win32_dir] can fail just after
320      # a directory is emptied. The cause is usually "tag-alongs" - programs
321      # like anti-virus software, automatic backup tools and various explorer
322      # extensions that keep a file open a little longer than we expect,
323      # causing the delete to fail.
324      #
325      # The solution is to wait a short amount of time before retrying the
326      # removal.
327      #
328      if {$nRetry > 0} {
329        for {set i 0} {$i < $nRetry} {incr i} {
330          set rc [catch {
331            remove_win32_dir $dirName
332          } msg]
333          if {$rc == 0} break
334          if {$nDelay > 0} { after $nDelay }
335        }
336        if {$rc} { error $msg }
337      } else {
338        remove_win32_dir $dirName
339      }
340    }
341  }
342
343  proc do_delete_win32_file {args} {
344    set nRetry [getFileRetries]     ;# Maximum number of retries.
345    set nDelay [getFileRetryDelay]  ;# Delay in ms before retrying.
346
347    foreach fileName $args {
348      # On windows, sometimes even a [delete_win32_file] can fail just after
349      # a file is closed. The cause is usually "tag-alongs" - programs like
350      # anti-virus software, automatic backup tools and various explorer
351      # extensions that keep a file open a little longer than we expect,
352      # causing the delete to fail.
353      #
354      # The solution is to wait a short amount of time before retrying the
355      # delete.
356      #
357      if {$nRetry > 0} {
358        for {set i 0} {$i < $nRetry} {incr i} {
359          set rc [catch {
360            delete_win32_file $fileName
361          } msg]
362          if {$rc == 0} break
363          if {$nDelay > 0} { after $nDelay }
364        }
365        if {$rc} { error $msg }
366      } else {
367        delete_win32_file $fileName
368      }
369    }
370  }
371}
372
373proc execpresql {handle args} {
374  trace remove execution $handle enter [list execpresql $handle]
375  if {[info exists ::G(perm:presql)]} {
376    $handle eval $::G(perm:presql)
377  }
378}
379
380# This command should be called after loading tester.tcl from within
381# all test scripts that are incompatible with encryption codecs.
382#
383proc do_not_use_codec {} {
384  set ::do_not_use_codec 1
385  reset_db
386}
387unset -nocomplain do_not_use_codec
388
389# Return true if the "reserved_bytes" integer on database files is non-zero.
390#
391proc nonzero_reserved_bytes {} {
392  return [sqlite3 -has-codec]
393}
394
395# Print a HELP message and exit
396#
397proc print_help_and_quit {} {
398  puts {Options:
399  --pause                  Wait for user input before continuing
400  --soft-heap-limit=N      Set the soft-heap-limit to N
401  --hard-heap-limit=N      Set the hard-heap-limit to N
402  --maxerror=N             Quit after N errors
403  --verbose=(0|1)          Control the amount of output.  Default '1'
404  --output=FILE            set --verbose=2 and output to FILE.  Implies -q
405  -q                       Shorthand for --verbose=0
406  --help                   This message
407}
408  exit 1
409}
410
411# The following block only runs the first time this file is sourced. It
412# does not run in slave interpreters (since the ::cmdlinearg array is
413# populated before the test script is run in slave interpreters).
414#
415if {[info exists cmdlinearg]==0} {
416
417  # Parse any options specified in the $argv array. This script accepts the
418  # following options:
419  #
420  #   --pause
421  #   --soft-heap-limit=NN
422  #   --hard-heap-limit=NN
423  #   --maxerror=NN
424  #   --malloctrace=N
425  #   --backtrace=N
426  #   --binarylog=N
427  #   --soak=N
428  #   --file-retries=N
429  #   --file-retry-delay=N
430  #   --start=[$permutation:]$testfile
431  #   --match=$pattern
432  #   --verbose=$val
433  #   --output=$filename
434  #   -q                                      Reduce output
435  #   --testdir=$dir                          Run tests in subdirectory $dir
436  #   --help
437  #
438  set cmdlinearg(soft-heap-limit)    0
439  set cmdlinearg(hard-heap-limit)    0
440  set cmdlinearg(maxerror)        1000
441  set cmdlinearg(malloctrace)        0
442  set cmdlinearg(backtrace)         10
443  set cmdlinearg(binarylog)          0
444  set cmdlinearg(soak)               0
445  set cmdlinearg(file-retries)       0
446  set cmdlinearg(file-retry-delay)   0
447  set cmdlinearg(start)             ""
448  set cmdlinearg(match)             ""
449  set cmdlinearg(verbose)           ""
450  set cmdlinearg(output)            ""
451  set cmdlinearg(testdir)           "testdir"
452
453  set leftover [list]
454  foreach a $argv {
455    switch -regexp -- $a {
456      {^-+pause$} {
457        # Wait for user input before continuing. This is to give the user an
458        # opportunity to connect profiling tools to the process.
459        puts -nonewline "Press RETURN to begin..."
460        flush stdout
461        gets stdin
462      }
463      {^-+soft-heap-limit=.+$} {
464        foreach {dummy cmdlinearg(soft-heap-limit)} [split $a =] break
465      }
466      {^-+hard-heap-limit=.+$} {
467        foreach {dummy cmdlinearg(hard-heap-limit)} [split $a =] break
468      }
469      {^-+maxerror=.+$} {
470        foreach {dummy cmdlinearg(maxerror)} [split $a =] break
471      }
472      {^-+malloctrace=.+$} {
473        foreach {dummy cmdlinearg(malloctrace)} [split $a =] break
474        if {$cmdlinearg(malloctrace)} {
475          if {0==$::sqlite_options(memdebug)} {
476            set err "Error: --malloctrace=1 requires an SQLITE_MEMDEBUG build"
477            puts stderr $err
478            exit 1
479          }
480          sqlite3_memdebug_log start
481        }
482      }
483      {^-+backtrace=.+$} {
484        foreach {dummy cmdlinearg(backtrace)} [split $a =] break
485        sqlite3_memdebug_backtrace $cmdlinearg(backtrace)
486      }
487      {^-+binarylog=.+$} {
488        foreach {dummy cmdlinearg(binarylog)} [split $a =] break
489        set cmdlinearg(binarylog) [file normalize $cmdlinearg(binarylog)]
490      }
491      {^-+soak=.+$} {
492        foreach {dummy cmdlinearg(soak)} [split $a =] break
493        set ::G(issoak) $cmdlinearg(soak)
494      }
495      {^-+file-retries=.+$} {
496        foreach {dummy cmdlinearg(file-retries)} [split $a =] break
497        set ::G(file-retries) $cmdlinearg(file-retries)
498      }
499      {^-+file-retry-delay=.+$} {
500        foreach {dummy cmdlinearg(file-retry-delay)} [split $a =] break
501        set ::G(file-retry-delay) $cmdlinearg(file-retry-delay)
502      }
503      {^-+start=.+$} {
504        foreach {dummy cmdlinearg(start)} [split $a =] break
505
506        set ::G(start:file) $cmdlinearg(start)
507        if {[regexp {(.*):(.*)} $cmdlinearg(start) -> s.perm s.file]} {
508          set ::G(start:permutation) ${s.perm}
509          set ::G(start:file)        ${s.file}
510        }
511        if {$::G(start:file) == ""} {unset ::G(start:file)}
512      }
513      {^-+match=.+$} {
514        foreach {dummy cmdlinearg(match)} [split $a =] break
515
516        set ::G(match) $cmdlinearg(match)
517        if {$::G(match) == ""} {unset ::G(match)}
518      }
519
520      {^-+output=.+$} {
521        foreach {dummy cmdlinearg(output)} [split $a =] break
522        set cmdlinearg(output) [file normalize $cmdlinearg(output)]
523        if {$cmdlinearg(verbose)==""} {
524          set cmdlinearg(verbose) 2
525        }
526      }
527      {^-+verbose=.+$} {
528        foreach {dummy cmdlinearg(verbose)} [split $a =] break
529        if {$cmdlinearg(verbose)=="file"} {
530          set cmdlinearg(verbose) 2
531        } elseif {[string is boolean -strict $cmdlinearg(verbose)]==0} {
532          error "option --verbose= must be set to a boolean or to \"file\""
533        }
534      }
535      {^-+testdir=.*$} {
536        foreach {dummy cmdlinearg(testdir)} [split $a =] break
537      }
538      {.*help.*} {
539         print_help_and_quit
540      }
541      {^-q$} {
542        set cmdlinearg(output) test-out.txt
543        set cmdlinearg(verbose) 2
544      }
545
546      default {
547        if {[file tail $a]==$a} {
548          lappend leftover $a
549        } else {
550          lappend leftover [file normalize $a]
551        }
552      }
553    }
554  }
555  set testdir [file normalize $testdir]
556  set cmdlinearg(TESTFIXTURE_HOME) [pwd]
557  set cmdlinearg(INFO_SCRIPT) [file normalize [info script]]
558  set argv0 [file normalize $argv0]
559  if {$cmdlinearg(testdir)!=""} {
560    file mkdir $cmdlinearg(testdir)
561    cd $cmdlinearg(testdir)
562  }
563  set argv $leftover
564
565  # Install the malloc layer used to inject OOM errors. And the 'automatic'
566  # extensions. This only needs to be done once for the process.
567  #
568  sqlite3_shutdown
569  install_malloc_faultsim 1
570  sqlite3_initialize
571  autoinstall_test_functions
572
573  # If the --binarylog option was specified, create the logging VFS. This
574  # call installs the new VFS as the default for all SQLite connections.
575  #
576  if {$cmdlinearg(binarylog)} {
577    vfslog new binarylog {} vfslog.bin
578  }
579
580  # Set the backtrace depth, if malloc tracing is enabled.
581  #
582  if {$cmdlinearg(malloctrace)} {
583    sqlite3_memdebug_backtrace $cmdlinearg(backtrace)
584  }
585
586  if {$cmdlinearg(output)!=""} {
587    puts "Copying output to file $cmdlinearg(output)"
588    set ::G(output_fd) [open $cmdlinearg(output) w]
589    fconfigure $::G(output_fd) -buffering line
590  }
591
592  if {$cmdlinearg(verbose)==""} {
593    set cmdlinearg(verbose) 1
594  }
595
596  if {[info commands vdbe_coverage]!=""} {
597    vdbe_coverage start
598  }
599}
600
601# Update the soft-heap-limit each time this script is run. In that
602# way if an individual test file changes the soft-heap-limit, it
603# will be reset at the start of the next test file.
604#
605sqlite3_soft_heap_limit64 $cmdlinearg(soft-heap-limit)
606sqlite3_hard_heap_limit64 $cmdlinearg(hard-heap-limit)
607
608# Create a test database
609#
610proc reset_db {} {
611  catch {db close}
612  forcedelete test.db
613  forcedelete test.db-journal
614  forcedelete test.db-wal
615  sqlite3 db ./test.db
616  set ::DB [sqlite3_connection_pointer db]
617  if {[info exists ::SETUP_SQL]} {
618    db eval $::SETUP_SQL
619  }
620}
621reset_db
622
623# Abort early if this script has been run before.
624#
625if {[info exists TC(count)]} return
626
627# Make sure memory statistics are enabled.
628#
629sqlite3_config_memstatus 1
630
631# Initialize the test counters and set up commands to access them.
632# Or, if this is a slave interpreter, set up aliases to write the
633# counters in the parent interpreter.
634#
635if {0==[info exists ::SLAVE]} {
636  set TC(errors)    0
637  set TC(count)     0
638  set TC(fail_list) [list]
639  set TC(omit_list) [list]
640  set TC(warn_list) [list]
641
642  proc set_test_counter {counter args} {
643    if {[llength $args]} {
644      set ::TC($counter) [lindex $args 0]
645    }
646    set ::TC($counter)
647  }
648}
649
650# Record the fact that a sequence of tests were omitted.
651#
652proc omit_test {name reason {append 1}} {
653  set omitList [set_test_counter omit_list]
654  if {$append} {
655    lappend omitList [list $name $reason]
656  }
657  set_test_counter omit_list $omitList
658}
659
660# Record the fact that a test failed.
661#
662proc fail_test {name} {
663  set f [set_test_counter fail_list]
664  lappend f $name
665  set_test_counter fail_list $f
666  set_test_counter errors [expr [set_test_counter errors] + 1]
667
668  set nFail [set_test_counter errors]
669  if {$nFail>=$::cmdlinearg(maxerror)} {
670    output2 "*** Giving up..."
671    finalize_testing
672  }
673}
674
675# Remember a warning message to be displayed at the conclusion of all testing
676#
677proc warning {msg {append 1}} {
678  output2 "Warning: $msg"
679  set warnList [set_test_counter warn_list]
680  if {$append} {
681    lappend warnList $msg
682  }
683  set_test_counter warn_list $warnList
684}
685
686
687# Increment the number of tests run
688#
689proc incr_ntest {} {
690  set_test_counter count [expr [set_test_counter count] + 1]
691}
692
693# Return true if --verbose=1 was specified on the command line. Otherwise,
694# return false.
695#
696proc verbose {} {
697  return $::cmdlinearg(verbose)
698}
699
700# Use the following commands instead of [puts] for test output within
701# this file. Test scripts can still use regular [puts], which is directed
702# to stdout and, if one is open, the --output file.
703#
704# output1: output that should be printed if --verbose=1 was specified.
705# output2: output that should be printed unconditionally.
706# output2_if_no_verbose: output that should be printed only if --verbose=0.
707#
708proc output1 {args} {
709  set v [verbose]
710  if {$v==1} {
711    uplevel output2 $args
712  } elseif {$v==2} {
713    uplevel puts [lrange $args 0 end-1] $::G(output_fd) [lrange $args end end]
714  }
715}
716proc output2 {args} {
717  set nArg [llength $args]
718  uplevel puts $args
719}
720proc output2_if_no_verbose {args} {
721  set v [verbose]
722  if {$v==0} {
723    uplevel output2 $args
724  } elseif {$v==2} {
725    uplevel puts [lrange $args 0 end-1] stdout [lrange $args end end]
726  }
727}
728
729# Override the [puts] command so that if no channel is explicitly
730# specified the string is written to both stdout and to the file
731# specified by "--output=", if any.
732#
733proc puts_override {args} {
734  set nArg [llength $args]
735  if {$nArg==1 || ($nArg==2 && [string first [lindex $args 0] -nonewline]==0)} {
736    uplevel puts_original $args
737    if {[info exists ::G(output_fd)]} {
738      uplevel puts [lrange $args 0 end-1] $::G(output_fd) [lrange $args end end]
739    }
740  } else {
741    # A channel was explicitly specified.
742    uplevel puts_original $args
743  }
744}
745rename puts puts_original
746proc puts {args} { uplevel puts_override $args }
747
748
749# Invoke the do_test procedure to run a single test
750#
751# The $expected parameter is the expected result.  The result is the return
752# value from the last TCL command in $cmd.
753#
754# Normally, $expected must match exactly.  But if $expected is of the form
755# "/regexp/" then regular expression matching is used.  If $expected is
756# "~/regexp/" then the regular expression must NOT match.  If $expected is
757# of the form "#/value-list/" then each term in value-list must be numeric
758# and must approximately match the corresponding numeric term in $result.
759# Values must match within 10%.  Or if the $expected term is A..B then the
760# $result term must be in between A and B.
761#
762proc do_test {name cmd expected} {
763  global argv cmdlinearg
764
765  fix_testname name
766
767  sqlite3_memdebug_settitle $name
768
769#  if {[llength $argv]==0} {
770#    set go 1
771#  } else {
772#    set go 0
773#    foreach pattern $argv {
774#      if {[string match $pattern $name]} {
775#        set go 1
776#        break
777#      }
778#    }
779#  }
780
781  if {[info exists ::G(perm:prefix)]} {
782    set name "$::G(perm:prefix)$name"
783  }
784
785  incr_ntest
786  output1 -nonewline $name...
787  flush stdout
788
789  if {![info exists ::G(match)] || [string match $::G(match) $name]} {
790    if {[catch {uplevel #0 "$cmd;\n"} result]} {
791      output2_if_no_verbose -nonewline $name...
792      output2 "\nError: $result"
793      fail_test $name
794    } else {
795      if {[permutation]=="maindbname"} {
796        set result [string map [list [string tolower ICECUBE] main] $result]
797      }
798      if {[regexp {^[~#]?/.*/$} $expected]} {
799        # "expected" is of the form "/PATTERN/" then the result if correct if
800        # regular expression PATTERN matches the result.  "~/PATTERN/" means
801        # the regular expression must not match.
802        if {[string index $expected 0]=="~"} {
803          set re [string range $expected 2 end-1]
804          if {[string index $re 0]=="*"} {
805            # If the regular expression begins with * then treat it as a glob instead
806            set ok [string match $re $result]
807          } else {
808            set re [string map {# {[-0-9.]+}} $re]
809            set ok [regexp $re $result]
810          }
811          set ok [expr {!$ok}]
812        } elseif {[string index $expected 0]=="#"} {
813          # Numeric range value comparison.  Each term of the $result is matched
814          # against one term of $expect.  Both $result and $expected terms must be
815          # numeric.  The values must match within 10%.  Or if $expected is of the
816          # form A..B then the $result term must be between A and B.
817          set e2 [string range $expected 2 end-1]
818          foreach i $result j $e2 {
819            if {[regexp {^(-?\d+)\.\.(-?\d)$} $j all A B]} {
820              set ok [expr {$i+0>=$A && $i+0<=$B}]
821            } else {
822              set ok [expr {$i+0>=0.9*$j && $i+0<=1.1*$j}]
823            }
824            if {!$ok} break
825          }
826          if {$ok && [llength $result]!=[llength $e2]} {set ok 0}
827        } else {
828          set re [string range $expected 1 end-1]
829          if {[string index $re 0]=="*"} {
830            # If the regular expression begins with * then treat it as a glob instead
831            set ok [string match $re $result]
832          } else {
833            set re [string map {# {[-0-9.]+}} $re]
834            set ok [regexp $re $result]
835          }
836        }
837      } elseif {[regexp {^~?\*.*\*$} $expected]} {
838        # "expected" is of the form "*GLOB*" then the result if correct if
839        # glob pattern GLOB matches the result.  "~/GLOB/" means
840        # the glob must not match.
841        if {[string index $expected 0]=="~"} {
842          set e [string range $expected 1 end]
843          set ok [expr {![string match $e $result]}]
844        } else {
845          set ok [string match $expected $result]
846        }
847      } else {
848        set ok [expr {[string compare $result $expected]==0}]
849      }
850      if {!$ok} {
851        # if {![info exists ::testprefix] || $::testprefix eq ""} {
852        #   error "no test prefix"
853        # }
854        output1 ""
855        output2 "! $name expected: \[$expected\]\n! $name got:      \[$result\]"
856        fail_test $name
857      } else {
858        output1 " Ok"
859      }
860    }
861  } else {
862    output1 " Omitted"
863    omit_test $name "pattern mismatch" 0
864  }
865  flush stdout
866}
867
868proc dumpbytes {s} {
869  set r ""
870  for {set i 0} {$i < [string length $s]} {incr i} {
871    if {$i > 0} {append r " "}
872    append r [format %02X [scan [string index $s $i] %c]]
873  }
874  return $r
875}
876
877proc catchcmd {db {cmd ""}} {
878  global CLI
879  set out [open cmds.txt w]
880  puts $out $cmd
881  close $out
882  set line "exec $CLI $db < cmds.txt"
883  set rc [catch { eval $line } msg]
884  list $rc $msg
885}
886
887proc catchcmdex {db {cmd ""}} {
888  global CLI
889  set out [open cmds.txt w]
890  fconfigure $out -encoding binary -translation binary
891  puts -nonewline $out $cmd
892  close $out
893  set line "exec -keepnewline -- $CLI $db < cmds.txt"
894  set chans [list stdin stdout stderr]
895  foreach chan $chans {
896    catch {
897      set modes($chan) [fconfigure $chan]
898      fconfigure $chan -encoding binary -translation binary -buffering none
899    }
900  }
901  set rc [catch { eval $line } msg]
902  foreach chan $chans {
903    catch {
904      eval fconfigure [list $chan] $modes($chan)
905    }
906  }
907  # puts [dumpbytes $msg]
908  list $rc $msg
909}
910
911proc filepath_normalize {p} {
912  # test cases should be written to assume "unix"-like file paths
913  if {$::tcl_platform(platform)!="unix"} {
914    string map [list \\ / \{/ / .db\} .db] \
915        [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]
916  } {
917    set p
918  }
919}
920proc do_filepath_test {name cmd expected} {
921  uplevel [list do_test $name [
922    subst -nocommands { filepath_normalize [ $cmd ] }
923  ] [filepath_normalize $expected]]
924}
925
926proc realnum_normalize {r} {
927  # different TCL versions display floating point values differently.
928  string map {1.#INF inf Inf inf .0e e} [regsub -all {(e[+-])0+} $r {\1}]
929}
930proc do_realnum_test {name cmd expected} {
931  uplevel [list do_test $name [
932    subst -nocommands { realnum_normalize [ $cmd ] }
933  ] [realnum_normalize $expected]]
934}
935
936proc fix_testname {varname} {
937  upvar $varname testname
938  if {[info exists ::testprefix]
939   && [string is digit [string range $testname 0 0]]
940  } {
941    set testname "${::testprefix}-$testname"
942  }
943}
944
945proc normalize_list {L} {
946  set L2 [list]
947  foreach l $L {lappend L2 $l}
948  set L2
949}
950
951# Either:
952#
953#   do_execsql_test TESTNAME SQL ?RES?
954#   do_execsql_test -db DB TESTNAME SQL ?RES?
955#
956proc do_execsql_test {args} {
957  set db db
958  if {[lindex $args 0]=="-db"} {
959    set db [lindex $args 1]
960    set args [lrange $args 2 end]
961  }
962
963  if {[llength $args]==2} {
964    foreach {testname sql} $args {}
965    set result ""
966  } elseif {[llength $args]==3} {
967    foreach {testname sql result} $args {}
968
969    # With some versions of Tcl on windows, if $result is all whitespace but
970    # contains some CR/LF characters, the [list {*}$result] below returns a
971    # copy of $result instead of a zero length string. Not clear exactly why
972    # this is. The following is a workaround.
973    if {[llength $result]==0} { set result "" }
974  } else {
975    error [string trim {
976      wrong # args: should be "do_execsql_test ?-db DB? testname sql ?result?"
977    }]
978  }
979
980  fix_testname testname
981
982  uplevel do_test                 \
983      [list $testname]            \
984      [list "execsql {$sql} $db"] \
985      [list [list {*}$result]]
986}
987
988proc do_catchsql_test {testname sql result} {
989  fix_testname testname
990  uplevel do_test [list $testname] [list "catchsql {$sql}"] [list $result]
991}
992proc do_timed_execsql_test {testname sql {result {}}} {
993  fix_testname testname
994  uplevel do_test [list $testname] [list "execsql_timed {$sql}"]\
995                                   [list [list {*}$result]]
996}
997
998# Run an EXPLAIN QUERY PLAN $sql in database "db".  Then rewrite the output
999# as an ASCII-art graph and return a string that is that graph.
1000#
1001# Hexadecimal literals in the output text are converted into "xxxxxx" since those
1002# literals are pointer values that might very from one run of the test to the
1003# next, yet we want the output to be consistent.
1004#
1005proc query_plan_graph {sql} {
1006  db eval "EXPLAIN QUERY PLAN $sql" {
1007    set dx($id) $detail
1008    lappend cx($parent) $id
1009  }
1010  set a "\n  QUERY PLAN\n"
1011  append a [append_graph "  " dx cx 0]
1012  regsub -all { 0x[A-F0-9]+\y} $a { xxxxxx} a
1013  regsub -all {(MATERIALIZE|CO-ROUTINE|SUBQUERY) \d+\y} $a {\1 xxxxxx} a
1014  regsub -all {\((join|subquery)-\d+\)} $a {(\1-xxxxxx)} a
1015  return $a
1016}
1017
1018# Helper routine for [query_plan_graph SQL]:
1019#
1020# Output rows of the graph that are children of $level.
1021#
1022#   prefix:  Prepend to every output line
1023#
1024#   dxname:  Name of an array variable that stores text describe
1025#            The description for $id is $dx($id)
1026#
1027#   cxname:  Name of an array variable holding children of item.
1028#            Children of $id are $cx($id)
1029#
1030#   level:   Render all lines that are children of $level
1031#
1032proc append_graph {prefix dxname cxname level} {
1033  upvar $dxname dx $cxname cx
1034  set a ""
1035  set x $cx($level)
1036  set n [llength $x]
1037  for {set i 0} {$i<$n} {incr i} {
1038    set id [lindex $x $i]
1039    if {$i==$n-1} {
1040      set p1 "`--"
1041      set p2 "   "
1042    } else {
1043      set p1 "|--"
1044      set p2 "|  "
1045    }
1046    append a $prefix$p1$dx($id)\n
1047    if {[info exists cx($id)]} {
1048      append a [append_graph "$prefix$p2" dx cx $id]
1049    }
1050  }
1051  return $a
1052}
1053
1054# Do an EXPLAIN QUERY PLAN test on input $sql with expected results $res
1055#
1056# If $res begins with a "\s+QUERY PLAN\n" then it is assumed to be the
1057# complete graph which must match the output of [query_plan_graph $sql]
1058# exactly.
1059#
1060# If $res does not begin with "\s+QUERY PLAN\n" then take it is a string
1061# that must be found somewhere in the query plan output.
1062#
1063proc do_eqp_test {name sql res} {
1064  if {[regexp {^\s+QUERY PLAN\n} $res]} {
1065    uplevel do_test $name [list [list query_plan_graph $sql]] [list $res]
1066  } else {
1067    if {[string index $res 0]!="/"} {
1068      set res "/*$res*/"
1069    }
1070    uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
1071  }
1072}
1073
1074
1075#-------------------------------------------------------------------------
1076#   Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST
1077#
1078# Where switches are:
1079#
1080#   -errorformat FMTSTRING
1081#   -count
1082#   -query SQL
1083#   -tclquery TCL
1084#   -repair TCL
1085#
1086proc do_select_tests {prefix args} {
1087
1088  set testlist [lindex $args end]
1089  set switches [lrange $args 0 end-1]
1090
1091  set errfmt ""
1092  set countonly 0
1093  set tclquery ""
1094  set repair ""
1095
1096  for {set i 0} {$i < [llength $switches]} {incr i} {
1097    set s [lindex $switches $i]
1098    set n [string length $s]
1099    if {$n>=2 && [string equal -length $n $s "-query"]} {
1100      set tclquery [list execsql [lindex $switches [incr i]]]
1101    } elseif {$n>=2 && [string equal -length $n $s "-tclquery"]} {
1102      set tclquery [lindex $switches [incr i]]
1103    } elseif {$n>=2 && [string equal -length $n $s "-errorformat"]} {
1104      set errfmt [lindex $switches [incr i]]
1105    } elseif {$n>=2 && [string equal -length $n $s "-repair"]} {
1106      set repair [lindex $switches [incr i]]
1107    } elseif {$n>=2 && [string equal -length $n $s "-count"]} {
1108      set countonly 1
1109    } else {
1110      error "unknown switch: $s"
1111    }
1112  }
1113
1114  if {$countonly && $errfmt!=""} {
1115    error "Cannot use -count and -errorformat together"
1116  }
1117  set nTestlist [llength $testlist]
1118  if {$nTestlist%3 || $nTestlist==0 } {
1119    error "SELECT test list contains [llength $testlist] elements"
1120  }
1121
1122  eval $repair
1123  foreach {tn sql res} $testlist {
1124    if {$tclquery != ""} {
1125      execsql $sql
1126      uplevel do_test ${prefix}.$tn [list $tclquery] [list [list {*}$res]]
1127    } elseif {$countonly} {
1128      set nRow 0
1129      db eval $sql {incr nRow}
1130      uplevel do_test ${prefix}.$tn [list [list set {} $nRow]] [list $res]
1131    } elseif {$errfmt==""} {
1132      uplevel do_execsql_test ${prefix}.${tn} [list $sql] [list [list {*}$res]]
1133    } else {
1134      set res [list 1 [string trim [format $errfmt {*}$res]]]
1135      uplevel do_catchsql_test ${prefix}.${tn} [list $sql] [list $res]
1136    }
1137    eval $repair
1138  }
1139
1140}
1141
1142proc delete_all_data {} {
1143  db eval {SELECT tbl_name AS t FROM sqlite_master WHERE type = 'table'} {
1144    db eval "DELETE FROM '[string map {' ''} $t]'"
1145  }
1146}
1147
1148# Run an SQL script.
1149# Return the number of microseconds per statement.
1150#
1151proc speed_trial {name numstmt units sql} {
1152  output2 -nonewline [format {%-21.21s } $name...]
1153  flush stdout
1154  set speed [time {sqlite3_exec_nr db $sql}]
1155  set tm [lindex $speed 0]
1156  if {$tm == 0} {
1157    set rate [format %20s "many"]
1158  } else {
1159    set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]]
1160  }
1161  set u2 $units/s
1162  output2 [format {%12d uS %s %s} $tm $rate $u2]
1163  global total_time
1164  set total_time [expr {$total_time+$tm}]
1165  lappend ::speed_trial_times $name $tm
1166}
1167proc speed_trial_tcl {name numstmt units script} {
1168  output2 -nonewline [format {%-21.21s } $name...]
1169  flush stdout
1170  set speed [time {eval $script}]
1171  set tm [lindex $speed 0]
1172  if {$tm == 0} {
1173    set rate [format %20s "many"]
1174  } else {
1175    set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]]
1176  }
1177  set u2 $units/s
1178  output2 [format {%12d uS %s %s} $tm $rate $u2]
1179  global total_time
1180  set total_time [expr {$total_time+$tm}]
1181  lappend ::speed_trial_times $name $tm
1182}
1183proc speed_trial_init {name} {
1184  global total_time
1185  set total_time 0
1186  set ::speed_trial_times [list]
1187  sqlite3 versdb :memory:
1188  set vers [versdb one {SELECT sqlite_source_id()}]
1189  versdb close
1190  output2 "SQLite $vers"
1191}
1192proc speed_trial_summary {name} {
1193  global total_time
1194  output2 [format {%-21.21s %12d uS TOTAL} $name $total_time]
1195
1196  if { 0 } {
1197    sqlite3 versdb :memory:
1198    set vers [lindex [versdb one {SELECT sqlite_source_id()}] 0]
1199    versdb close
1200    output2 "CREATE TABLE IF NOT EXISTS time(version, script, test, us);"
1201    foreach {test us} $::speed_trial_times {
1202      output2 "INSERT INTO time VALUES('$vers', '$name', '$test', $us);"
1203    }
1204  }
1205}
1206
1207# Clear out left-over configuration setup from the end of a test
1208#
1209proc finish_test_precleanup {} {
1210  catch {db1 close}
1211  catch {db2 close}
1212  catch {db3 close}
1213  catch {unregister_devsim}
1214  catch {unregister_jt_vfs}
1215  catch {unregister_demovfs}
1216}
1217
1218# Run this routine last
1219#
1220proc finish_test {} {
1221  global argv
1222  finish_test_precleanup
1223  if {[llength $argv]>0} {
1224    # If additional test scripts are specified on the command-line,
1225    # run them also, before quitting.
1226    proc finish_test {} {
1227      finish_test_precleanup
1228      return
1229    }
1230    foreach extra $argv {
1231      puts "Running \"$extra\""
1232      db_delete_and_reopen
1233      uplevel #0 source $extra
1234    }
1235  }
1236  catch {db close}
1237  if {0==[info exists ::SLAVE]} { finalize_testing }
1238}
1239proc finalize_testing {} {
1240  global sqlite_open_file_count
1241
1242  set omitList [set_test_counter omit_list]
1243
1244  catch {db close}
1245  catch {db2 close}
1246  catch {db3 close}
1247
1248  vfs_unlink_test
1249  sqlite3 db {}
1250  # sqlite3_clear_tsd_memdebug
1251  db close
1252  sqlite3_reset_auto_extension
1253
1254  sqlite3_soft_heap_limit64 0
1255  sqlite3_hard_heap_limit64 0
1256  set nTest [incr_ntest]
1257  set nErr [set_test_counter errors]
1258
1259  set nKnown 0
1260  if {[file readable known-problems.txt]} {
1261    set fd [open known-problems.txt]
1262    set content [read $fd]
1263    close $fd
1264    foreach x $content {set known_error($x) 1}
1265    foreach x [set_test_counter fail_list] {
1266      if {[info exists known_error($x)]} {incr nKnown}
1267    }
1268  }
1269  if {$nKnown>0} {
1270    output2 "[expr {$nErr-$nKnown}] new errors and $nKnown known errors\
1271         out of $nTest tests"
1272  } else {
1273    set cpuinfo {}
1274    if {[catch {exec hostname} hname]==0} {set cpuinfo [string trim $hname]}
1275    append cpuinfo " $::tcl_platform(os)"
1276    append cpuinfo " [expr {$::tcl_platform(pointerSize)*8}]-bit"
1277    append cpuinfo " [string map {E -e} $::tcl_platform(byteOrder)]"
1278    output2 "SQLite [sqlite3 -sourceid]"
1279    output2 "$nErr errors out of $nTest tests on $cpuinfo"
1280  }
1281  if {$nErr>$nKnown} {
1282    output2 -nonewline "!Failures on these tests:"
1283    foreach x [set_test_counter fail_list] {
1284      if {![info exists known_error($x)]} {output2 -nonewline " $x"}
1285    }
1286    output2 ""
1287  }
1288  foreach warning [set_test_counter warn_list] {
1289    output2 "Warning: $warning"
1290  }
1291  run_thread_tests 1
1292  if {[llength $omitList]>0} {
1293    output2 "Omitted test cases:"
1294    set prec {}
1295    foreach {rec} [lsort $omitList] {
1296      if {$rec==$prec} continue
1297      set prec $rec
1298      output2 [format {.  %-12s %s} [lindex $rec 0] [lindex $rec 1]]
1299    }
1300  }
1301  if {$nErr>0 && ![working_64bit_int]} {
1302    output2 "******************************************************************"
1303    output2 "N.B.:  The version of TCL that you used to build this test harness"
1304    output2 "is defective in that it does not support 64-bit integers.  Some or"
1305    output2 "all of the test failures above might be a result from this defect"
1306    output2 "in your TCL build."
1307    output2 "******************************************************************"
1308  }
1309  if {$::cmdlinearg(binarylog)} {
1310    vfslog finalize binarylog
1311  }
1312  if {$sqlite_open_file_count} {
1313    output2 "$sqlite_open_file_count files were left open"
1314    incr nErr
1315  }
1316  if {[lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1]>0 ||
1317              [sqlite3_memory_used]>0} {
1318    output2 "Unfreed memory: [sqlite3_memory_used] bytes in\
1319         [lindex [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0] 1] allocations"
1320    incr nErr
1321    ifcapable mem5||(mem3&&debug) {
1322      output2 "Writing unfreed memory log to \"./memleak.txt\""
1323      sqlite3_memdebug_dump ./memleak.txt
1324    }
1325  } else {
1326    output2 "All memory allocations freed - no leaks"
1327    ifcapable mem5 {
1328      sqlite3_memdebug_dump ./memusage.txt
1329    }
1330  }
1331  show_memstats
1332  output2 "Maximum memory usage: [sqlite3_memory_highwater 1] bytes"
1333  output2 "Current memory usage: [sqlite3_memory_highwater] bytes"
1334  if {[info commands sqlite3_memdebug_malloc_count] ne ""} {
1335    output2 "Number of malloc()  : [sqlite3_memdebug_malloc_count] calls"
1336  }
1337  if {$::cmdlinearg(malloctrace)} {
1338    output2 "Writing mallocs.tcl..."
1339    memdebug_log_sql mallocs.tcl
1340    sqlite3_memdebug_log stop
1341    sqlite3_memdebug_log clear
1342    if {[sqlite3_memory_used]>0} {
1343      output2 "Writing leaks.tcl..."
1344      sqlite3_memdebug_log sync
1345      memdebug_log_sql leaks.tcl
1346    }
1347  }
1348  if {[info commands vdbe_coverage]!=""} {
1349    vdbe_coverage_report
1350  }
1351  foreach f [glob -nocomplain test.db-*-journal] {
1352    forcedelete $f
1353  }
1354  foreach f [glob -nocomplain test.db-mj*] {
1355    forcedelete $f
1356  }
1357  exit [expr {$nErr>0}]
1358}
1359
1360proc vdbe_coverage_report {} {
1361  puts "Writing vdbe coverage report to vdbe_coverage.txt"
1362  set lSrc [list]
1363  set iLine 0
1364  if {[file exists ../sqlite3.c]} {
1365    set fd [open ../sqlite3.c]
1366    set iLine
1367    while { ![eof $fd] } {
1368      set line [gets $fd]
1369      incr iLine
1370      if {[regexp {^/\** Begin file (.*\.c) \**/} $line -> file]} {
1371        lappend lSrc [list $iLine $file]
1372      }
1373    }
1374    close $fd
1375  }
1376  set fd [open vdbe_coverage.txt w]
1377  foreach miss [vdbe_coverage report] {
1378    foreach {line branch never} $miss {}
1379    set nextfile ""
1380    while {[llength $lSrc]>0 && [lindex $lSrc 0 0] < $line} {
1381      set nextfile [lindex $lSrc 0 1]
1382      set lSrc [lrange $lSrc 1 end]
1383    }
1384    if {$nextfile != ""} {
1385      puts $fd ""
1386      puts $fd "### $nextfile ###"
1387    }
1388    puts $fd "Vdbe branch $line: never $never (path $branch)"
1389  }
1390  close $fd
1391}
1392
1393# Display memory statistics for analysis and debugging purposes.
1394#
1395proc show_memstats {} {
1396  set x [sqlite3_status SQLITE_STATUS_MEMORY_USED 0]
1397  set y [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0]
1398  set val [format {now %10d  max %10d  max-size %10d} \
1399              [lindex $x 1] [lindex $x 2] [lindex $y 2]]
1400  output1 "Memory used:          $val"
1401  set x [sqlite3_status SQLITE_STATUS_MALLOC_COUNT 0]
1402  set val [format {now %10d  max %10d} [lindex $x 1] [lindex $x 2]]
1403  output1 "Allocation count:     $val"
1404  set x [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0]
1405  set y [sqlite3_status SQLITE_STATUS_PAGECACHE_SIZE 0]
1406  set val [format {now %10d  max %10d  max-size %10d} \
1407              [lindex $x 1] [lindex $x 2] [lindex $y 2]]
1408  output1 "Page-cache used:      $val"
1409  set x [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0]
1410  set val [format {now %10d  max %10d} [lindex $x 1] [lindex $x 2]]
1411  output1 "Page-cache overflow:  $val"
1412  ifcapable yytrackmaxstackdepth {
1413    set x [sqlite3_status SQLITE_STATUS_PARSER_STACK 0]
1414    set val [format {               max %10d} [lindex $x 2]]
1415    output2 "Parser stack depth:    $val"
1416  }
1417}
1418
1419# A procedure to execute SQL
1420#
1421proc execsql {sql {db db}} {
1422  # puts "SQL = $sql"
1423  uplevel [list $db eval $sql]
1424}
1425proc execsql_timed {sql {db db}} {
1426  set tm [time {
1427    set x [uplevel [list $db eval $sql]]
1428  } 1]
1429  set tm [lindex $tm 0]
1430  output1 -nonewline " ([expr {$tm*0.001}]ms) "
1431  set x
1432}
1433
1434# Execute SQL and catch exceptions.
1435#
1436proc catchsql {sql {db db}} {
1437  # puts "SQL = $sql"
1438  set r [catch [list uplevel [list $db eval $sql]] msg]
1439  lappend r $msg
1440  return $r
1441}
1442
1443# Do an VDBE code dump on the SQL given
1444#
1445proc explain {sql {db db}} {
1446  output2 ""
1447  output2 "addr  opcode        p1      p2      p3      p4               p5  #"
1448  output2 "----  ------------  ------  ------  ------  ---------------  --  -"
1449  $db eval "explain $sql" {} {
1450    output2 [format {%-4d  %-12.12s  %-6d  %-6d  %-6d  % -17s %s  %s} \
1451      $addr $opcode $p1 $p2 $p3 $p4 $p5 $comment
1452    ]
1453  }
1454}
1455
1456proc explain_i {sql {db db}} {
1457  output2 ""
1458  output2 "addr  opcode        p1      p2      p3      p4                p5  #"
1459  output2 "----  ------------  ------  ------  ------  ----------------  --  -"
1460
1461
1462  # Set up colors for the different opcodes. Scheme is as follows:
1463  #
1464  #   Red:   Opcodes that write to a b-tree.
1465  #   Blue:  Opcodes that reposition or seek a cursor.
1466  #   Green: The ResultRow opcode.
1467  #
1468  if { [catch {fconfigure stdout -mode}]==0 } {
1469    set R "\033\[31;1m"        ;# Red fg
1470    set G "\033\[32;1m"        ;# Green fg
1471    set B "\033\[34;1m"        ;# Red fg
1472    set D "\033\[39;0m"        ;# Default fg
1473  } else {
1474    set R ""
1475    set G ""
1476    set B ""
1477    set D ""
1478  }
1479  foreach opcode {
1480      Seek SeekGE SeekGT SeekLE SeekLT NotFound Last Rewind
1481      NoConflict Next Prev VNext VPrev VFilter
1482      SorterSort SorterNext NextIfOpen
1483  } {
1484    set color($opcode) $B
1485  }
1486  foreach opcode {ResultRow} {
1487    set color($opcode) $G
1488  }
1489  foreach opcode {IdxInsert Insert Delete IdxDelete} {
1490    set color($opcode) $R
1491  }
1492
1493  set bSeenGoto 0
1494  $db eval "explain $sql" {} {
1495    set x($addr) 0
1496    set op($addr) $opcode
1497
1498    if {$opcode == "Goto" && ($bSeenGoto==0 || ($p2 > $addr+10))} {
1499      set linebreak($p2) 1
1500      set bSeenGoto 1
1501    }
1502
1503    if {$opcode=="Once"} {
1504      for {set i $addr} {$i<$p2} {incr i} {
1505        set star($i) $addr
1506      }
1507    }
1508
1509    if {$opcode=="Next"  || $opcode=="Prev"
1510     || $opcode=="VNext" || $opcode=="VPrev"
1511     || $opcode=="SorterNext" || $opcode=="NextIfOpen"
1512    } {
1513      for {set i $p2} {$i<$addr} {incr i} {
1514        incr x($i) 2
1515      }
1516    }
1517
1518    if {$opcode == "Goto" && $p2<$addr && $op($p2)=="Yield"} {
1519      for {set i [expr $p2+1]} {$i<$addr} {incr i} {
1520        incr x($i) 2
1521      }
1522    }
1523
1524    if {$opcode == "Halt" && $comment == "End of coroutine"} {
1525      set linebreak([expr $addr+1]) 1
1526    }
1527  }
1528
1529  $db eval "explain $sql" {} {
1530    if {[info exists linebreak($addr)]} {
1531      output2 ""
1532    }
1533    set I [string repeat " " $x($addr)]
1534
1535    if {[info exists star($addr)]} {
1536      set ii [expr $x($star($addr))]
1537      append I "  "
1538      set I [string replace $I $ii $ii *]
1539    }
1540
1541    set col ""
1542    catch { set col $color($opcode) }
1543
1544    output2 [format {%-4d  %s%s%-12.12s%s  %-6d  %-6d  %-6d  % -17s %s  %s} \
1545      $addr $I $col $opcode $D $p1 $p2 $p3 $p4 $p5 $comment
1546    ]
1547  }
1548  output2 "----  ------------  ------  ------  ------  ----------------  --  -"
1549}
1550
1551proc execsql_pp {sql {db db}} {
1552  set nCol 0
1553  $db eval $sql A {
1554    if {$nCol==0} {
1555      set nCol [llength $A(*)]
1556      foreach c $A(*) {
1557        set aWidth($c) [string length $c]
1558        lappend data $c
1559      }
1560    }
1561    foreach c $A(*) {
1562      set n [string length $A($c)]
1563      if {$n > $aWidth($c)} {
1564        set aWidth($c) $n
1565      }
1566      lappend data $A($c)
1567    }
1568  }
1569  if {$nCol>0} {
1570    set nTotal 0
1571    foreach e [array names aWidth] { incr nTotal $aWidth($e) }
1572    incr nTotal [expr ($nCol-1) * 3]
1573    incr nTotal 4
1574
1575    set fmt ""
1576    foreach c $A(*) {
1577      lappend fmt "% -$aWidth($c)s"
1578    }
1579    set fmt "| [join $fmt { | }] |"
1580
1581    puts [string repeat - $nTotal]
1582    for {set i 0} {$i < [llength $data]} {incr i $nCol} {
1583      set vals [lrange $data $i [expr $i+$nCol-1]]
1584      puts [format $fmt {*}$vals]
1585      if {$i==0} { puts [string repeat - $nTotal] }
1586    }
1587    puts [string repeat - $nTotal]
1588  }
1589}
1590
1591
1592# Show the VDBE program for an SQL statement but omit the Trace
1593# opcode at the beginning.  This procedure can be used to prove
1594# that different SQL statements generate exactly the same VDBE code.
1595#
1596proc explain_no_trace {sql} {
1597  set tr [db eval "EXPLAIN $sql"]
1598  return [lrange $tr 7 end]
1599}
1600
1601# Another procedure to execute SQL.  This one includes the field
1602# names in the returned list.
1603#
1604proc execsql2 {sql} {
1605  set result {}
1606  db eval $sql data {
1607    foreach f $data(*) {
1608      lappend result $f $data($f)
1609    }
1610  }
1611  return $result
1612}
1613
1614# Use a temporary in-memory database to execute SQL statements
1615#
1616proc memdbsql {sql} {
1617  sqlite3 memdb :memory:
1618  set result [memdb eval $sql]
1619  memdb close
1620  return $result
1621}
1622
1623# Use the non-callback API to execute multiple SQL statements
1624#
1625proc stepsql {dbptr sql} {
1626  set sql [string trim $sql]
1627  set r 0
1628  while {[string length $sql]>0} {
1629    if {[catch {sqlite3_prepare $dbptr $sql -1 sqltail} vm]} {
1630      return [list 1 $vm]
1631    }
1632    set sql [string trim $sqltail]
1633#    while {[sqlite_step $vm N VAL COL]=="SQLITE_ROW"} {
1634#      foreach v $VAL {lappend r $v}
1635#    }
1636    while {[sqlite3_step $vm]=="SQLITE_ROW"} {
1637      for {set i 0} {$i<[sqlite3_data_count $vm]} {incr i} {
1638        lappend r [sqlite3_column_text $vm $i]
1639      }
1640    }
1641    if {[catch {sqlite3_finalize $vm} errmsg]} {
1642      return [list 1 $errmsg]
1643    }
1644  }
1645  return $r
1646}
1647
1648# Do an integrity check of the entire database
1649#
1650proc integrity_check {name {db db}} {
1651  ifcapable integrityck {
1652    do_test $name [list execsql {PRAGMA integrity_check} $db] {ok}
1653  }
1654}
1655
1656# Check the extended error code
1657#
1658proc verify_ex_errcode {name expected {db db}} {
1659  do_test $name [list sqlite3_extended_errcode $db] $expected
1660}
1661
1662
1663# Return true if the SQL statement passed as the second argument uses a
1664# statement transaction.
1665#
1666proc sql_uses_stmt {db sql} {
1667  set stmt [sqlite3_prepare $db $sql -1 dummy]
1668  set uses [uses_stmt_journal $stmt]
1669  sqlite3_finalize $stmt
1670  return $uses
1671}
1672
1673proc fix_ifcapable_expr {expr} {
1674  set ret ""
1675  set state 0
1676  for {set i 0} {$i < [string length $expr]} {incr i} {
1677    set char [string range $expr $i $i]
1678    set newstate [expr {[string is alnum $char] || $char eq "_"}]
1679    if {$newstate && !$state} {
1680      append ret {$::sqlite_options(}
1681    }
1682    if {!$newstate && $state} {
1683      append ret )
1684    }
1685    append ret $char
1686    set state $newstate
1687  }
1688  if {$state} {append ret )}
1689  return $ret
1690}
1691
1692# Returns non-zero if the capabilities are present; zero otherwise.
1693#
1694proc capable {expr} {
1695  set e [fix_ifcapable_expr $expr]; return [expr ($e)]
1696}
1697
1698# Evaluate a boolean expression of capabilities.  If true, execute the
1699# code.  Omit the code if false.
1700#
1701proc ifcapable {expr code {else ""} {elsecode ""}} {
1702  #regsub -all {[a-z_0-9]+} $expr {$::sqlite_options(&)} e2
1703  set e2 [fix_ifcapable_expr $expr]
1704  if ($e2) {
1705    set c [catch {uplevel 1 $code} r]
1706  } else {
1707    set c [catch {uplevel 1 $elsecode} r]
1708  }
1709  return -code $c $r
1710}
1711
1712# This proc execs a seperate process that crashes midway through executing
1713# the SQL script $sql on database test.db.
1714#
1715# The crash occurs during a sync() of file $crashfile. When the crash
1716# occurs a random subset of all unsynced writes made by the process are
1717# written into the files on disk. Argument $crashdelay indicates the
1718# number of file syncs to wait before crashing.
1719#
1720# The return value is a list of two elements. The first element is a
1721# boolean, indicating whether or not the process actually crashed or
1722# reported some other error. The second element in the returned list is the
1723# error message. This is "child process exited abnormally" if the crash
1724# occurred.
1725#
1726#   crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql
1727#
1728proc crashsql {args} {
1729
1730  set blocksize ""
1731  set crashdelay 1
1732  set prngseed 0
1733  set opendb { sqlite3 db test.db -vfs crash }
1734  set tclbody {}
1735  set crashfile ""
1736  set dc ""
1737  set dfltvfs 0
1738  set sql [lindex $args end]
1739
1740  for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} {
1741    set z [lindex $args $ii]
1742    set n [string length $z]
1743    set z2 [lindex $args [expr $ii+1]]
1744
1745    if     {$n>1 && [string first $z -delay]==0}     {set crashdelay $z2} \
1746    elseif {$n>1 && [string first $z -opendb]==0}    {set opendb $z2} \
1747    elseif {$n>1 && [string first $z -seed]==0}      {set prngseed $z2} \
1748    elseif {$n>1 && [string first $z -file]==0}      {set crashfile $z2}  \
1749    elseif {$n>1 && [string first $z -tclbody]==0}   {set tclbody $z2}  \
1750    elseif {$n>1 && [string first $z -blocksize]==0} {set blocksize "-s $z2" } \
1751    elseif {$n>1 && [string first $z -characteristics]==0} {set dc "-c {$z2}" }\
1752    elseif {$n>1 && [string first $z -dfltvfs]==0} {set dfltvfs $z2 }\
1753    else   { error "Unrecognized option: $z" }
1754  }
1755
1756  if {$crashfile eq ""} {
1757    error "Compulsory option -file missing"
1758  }
1759
1760  # $crashfile gets compared to the native filename in
1761  # cfSync(), which can be different then what TCL uses by
1762  # default, so here we force it to the "nativename" format.
1763  set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]]
1764
1765  set f [open crash.tcl w]
1766  puts $f "sqlite3_initialize ; sqlite3_shutdown"
1767  puts $f "catch { install_malloc_faultsim 1 }"
1768  puts $f "sqlite3_crash_enable 1 $dfltvfs"
1769  puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
1770  puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
1771  puts $f "autoinstall_test_functions"
1772
1773  # This block sets the cache size of the main database to 10
1774  # pages. This is done in case the build is configured to omit
1775  # "PRAGMA cache_size".
1776  if {$opendb!=""} {
1777    puts $f $opendb
1778    puts $f {db eval {SELECT * FROM sqlite_master;}}
1779    puts $f {set bt [btree_from_db db]}
1780    puts $f {btree_set_cache_size $bt 10}
1781  }
1782
1783  if {$prngseed} {
1784    set seed [expr {$prngseed%10007+1}]
1785    # puts seed=$seed
1786    puts $f "db eval {SELECT randomblob($seed)}"
1787  }
1788
1789  if {[string length $tclbody]>0} {
1790    puts $f $tclbody
1791  }
1792  if {[string length $sql]>0} {
1793    puts $f "db eval {"
1794    puts $f   "$sql"
1795    puts $f "}"
1796  }
1797  close $f
1798  set r [catch {
1799    exec [info nameofexec] crash.tcl >@stdout 2>@stdout
1800  } msg]
1801
1802  # Windows/ActiveState TCL returns a slightly different
1803  # error message.  We map that to the expected message
1804  # so that we don't have to change all of the test
1805  # cases.
1806  if {$::tcl_platform(platform)=="windows"} {
1807    if {$msg=="child killed: unknown signal"} {
1808      set msg "child process exited abnormally"
1809    }
1810  }
1811  if {$r && [string match {*ERROR: LeakSanitizer*} $msg]} {
1812    set msg "child process exited abnormally"
1813  }
1814
1815  lappend r $msg
1816}
1817
1818#   crash_on_write ?-devchar DEVCHAR? CRASHDELAY SQL
1819#
1820proc crash_on_write {args} {
1821
1822  set nArg [llength $args]
1823  if {$nArg<2 || $nArg%2} {
1824    error "bad args: $args"
1825  }
1826  set zSql [lindex $args end]
1827  set nDelay [lindex $args end-1]
1828
1829  set devchar {}
1830  for {set ii 0} {$ii < $nArg-2} {incr ii 2} {
1831    set opt [lindex $args $ii]
1832    switch -- [lindex $args $ii] {
1833      -devchar {
1834        set devchar [lindex $args [expr $ii+1]]
1835      }
1836
1837      default { error "unrecognized option: $opt" }
1838    }
1839  }
1840
1841  set f [open crash.tcl w]
1842  puts $f "sqlite3_crash_on_write $nDelay"
1843  puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
1844  puts $f "sqlite3 db test.db -vfs writecrash"
1845  puts $f "db eval {$zSql}"
1846  puts $f "set {} {}"
1847
1848  close $f
1849  set r [catch {
1850    exec [info nameofexec] crash.tcl >@stdout
1851  } msg]
1852
1853  # Windows/ActiveState TCL returns a slightly different
1854  # error message.  We map that to the expected message
1855  # so that we don't have to change all of the test
1856  # cases.
1857  if {$::tcl_platform(platform)=="windows"} {
1858    if {$msg=="child killed: unknown signal"} {
1859      set msg "child process exited abnormally"
1860    }
1861  }
1862
1863  lappend r $msg
1864}
1865
1866proc run_ioerr_prep {} {
1867  set ::sqlite_io_error_pending 0
1868  catch {db close}
1869  catch {db2 close}
1870  catch {forcedelete test.db}
1871  catch {forcedelete test.db-journal}
1872  catch {forcedelete test2.db}
1873  catch {forcedelete test2.db-journal}
1874  set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
1875  sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
1876  if {[info exists ::ioerropts(-tclprep)]} {
1877    eval $::ioerropts(-tclprep)
1878  }
1879  if {[info exists ::ioerropts(-sqlprep)]} {
1880    execsql $::ioerropts(-sqlprep)
1881  }
1882  expr 0
1883}
1884
1885# Usage: do_ioerr_test <test number> <options...>
1886#
1887# This proc is used to implement test cases that check that IO errors
1888# are correctly handled. The first argument, <test number>, is an integer
1889# used to name the tests executed by this proc. Options are as follows:
1890#
1891#     -tclprep          TCL script to run to prepare test.
1892#     -sqlprep          SQL script to run to prepare test.
1893#     -tclbody          TCL script to run with IO error simulation.
1894#     -sqlbody          TCL script to run with IO error simulation.
1895#     -exclude          List of 'N' values not to test.
1896#     -erc              Use extended result codes
1897#     -persist          Make simulated I/O errors persistent
1898#     -start            Value of 'N' to begin with (default 1)
1899#
1900#     -cksum            Boolean. If true, test that the database does
1901#                       not change during the execution of the test case.
1902#
1903proc do_ioerr_test {testname args} {
1904
1905  set ::ioerropts(-start) 1
1906  set ::ioerropts(-cksum) 0
1907  set ::ioerropts(-erc) 0
1908  set ::ioerropts(-count) 100000000
1909  set ::ioerropts(-persist) 1
1910  set ::ioerropts(-ckrefcount) 0
1911  set ::ioerropts(-restoreprng) 1
1912  array set ::ioerropts $args
1913
1914  # TEMPORARY: For 3.5.9, disable testing of extended result codes. There are
1915  # a couple of obscure IO errors that do not return them.
1916  set ::ioerropts(-erc) 0
1917
1918  # Create a single TCL script from the TCL and SQL specified
1919  # as the body of the test.
1920  set ::ioerrorbody {}
1921  if {[info exists ::ioerropts(-tclbody)]} {
1922    append ::ioerrorbody "$::ioerropts(-tclbody)\n"
1923  }
1924  if {[info exists ::ioerropts(-sqlbody)]} {
1925    append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
1926  }
1927
1928  save_prng_state
1929  if {$::ioerropts(-cksum)} {
1930    run_ioerr_prep
1931    eval $::ioerrorbody
1932    set ::goodcksum [cksum]
1933  }
1934
1935  set ::go 1
1936  #reset_prng_state
1937  for {set n $::ioerropts(-start)} {$::go} {incr n} {
1938    set ::TN $n
1939    incr ::ioerropts(-count) -1
1940    if {$::ioerropts(-count)<0} break
1941
1942    # Skip this IO error if it was specified with the "-exclude" option.
1943    if {[info exists ::ioerropts(-exclude)]} {
1944      if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
1945    }
1946    if {$::ioerropts(-restoreprng)} {
1947      restore_prng_state
1948    }
1949
1950    # Delete the files test.db and test2.db, then execute the TCL and
1951    # SQL (in that order) to prepare for the test case.
1952    do_test $testname.$n.1 {
1953      run_ioerr_prep
1954    } {0}
1955
1956    # Read the 'checksum' of the database.
1957    if {$::ioerropts(-cksum)} {
1958      set ::checksum [cksum]
1959    }
1960
1961    # Set the Nth IO error to fail.
1962    do_test $testname.$n.2 [subst {
1963      set ::sqlite_io_error_persist $::ioerropts(-persist)
1964      set ::sqlite_io_error_pending $n
1965    }] $n
1966
1967    # Execute the TCL script created for the body of this test. If
1968    # at least N IO operations performed by SQLite as a result of
1969    # the script, the Nth will fail.
1970    do_test $testname.$n.3 {
1971      set ::sqlite_io_error_hit 0
1972      set ::sqlite_io_error_hardhit 0
1973      set r [catch $::ioerrorbody msg]
1974      set ::errseen $r
1975      if {[info commands db]!=""} {
1976        set rc [sqlite3_errcode db]
1977        if {$::ioerropts(-erc)} {
1978          # If we are in extended result code mode, make sure all of the
1979          # IOERRs we get back really do have their extended code values.
1980          # If an extended result code is returned, the sqlite3_errcode
1981          # TCLcommand will return a string of the form:  SQLITE_IOERR+nnnn
1982          # where nnnn is a number
1983          if {[regexp {^SQLITE_IOERR} $rc] && ![regexp {IOERR\+\d} $rc]} {
1984            return $rc
1985          }
1986        } else {
1987          # If we are not in extended result code mode, make sure no
1988          # extended error codes are returned.
1989          if {[regexp {\+\d} $rc]} {
1990            return $rc
1991          }
1992        }
1993      }
1994      # The test repeats as long as $::go is non-zero.  $::go starts out
1995      # as 1.  When a test runs to completion without hitting an I/O
1996      # error, that means there is no point in continuing with this test
1997      # case so set $::go to zero.
1998      #
1999      if {$::sqlite_io_error_pending>0} {
2000        set ::go 0
2001        set q 0
2002        set ::sqlite_io_error_pending 0
2003      } else {
2004        set q 1
2005      }
2006
2007      set s [expr $::sqlite_io_error_hit==0]
2008      if {$::sqlite_io_error_hit>$::sqlite_io_error_hardhit && $r==0} {
2009        set r 1
2010      }
2011      set ::sqlite_io_error_hit 0
2012
2013      # One of two things must have happened. either
2014      #   1.  We never hit the IO error and the SQL returned OK
2015      #   2.  An IO error was hit and the SQL failed
2016      #
2017      #puts "s=$s r=$r q=$q"
2018      expr { ($s && !$r && !$q) || (!$s && $r && $q) }
2019    } {1}
2020
2021    set ::sqlite_io_error_hit 0
2022    set ::sqlite_io_error_pending 0
2023
2024    # Check that no page references were leaked. There should be
2025    # a single reference if there is still an active transaction,
2026    # or zero otherwise.
2027    #
2028    # UPDATE: If the IO error occurs after a 'BEGIN' but before any
2029    # locks are established on database files (i.e. if the error
2030    # occurs while attempting to detect a hot-journal file), then
2031    # there may 0 page references and an active transaction according
2032    # to [sqlite3_get_autocommit].
2033    #
2034    if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-ckrefcount)} {
2035      do_test $testname.$n.4 {
2036        set bt [btree_from_db db]
2037        db_enter db
2038        array set stats [btree_pager_stats $bt]
2039        db_leave db
2040        set nRef $stats(ref)
2041        expr {$nRef == 0 || ([sqlite3_get_autocommit db]==0 && $nRef == 1)}
2042      } {1}
2043    }
2044
2045    # If there is an open database handle and no open transaction,
2046    # and the pager is not running in exclusive-locking mode,
2047    # check that the pager is in "unlocked" state. Theoretically,
2048    # if a call to xUnlock() failed due to an IO error the underlying
2049    # file may still be locked.
2050    #
2051    ifcapable pragma {
2052      if { [info commands db] ne ""
2053        && $::ioerropts(-ckrefcount)
2054        && [db one {pragma locking_mode}] eq "normal"
2055        && [sqlite3_get_autocommit db]
2056      } {
2057        do_test $testname.$n.5 {
2058          set bt [btree_from_db db]
2059          db_enter db
2060          array set stats [btree_pager_stats $bt]
2061          db_leave db
2062          set stats(state)
2063        } 0
2064      }
2065    }
2066
2067    # If an IO error occurred, then the checksum of the database should
2068    # be the same as before the script that caused the IO error was run.
2069    #
2070    if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} {
2071      do_test $testname.$n.6 {
2072        catch {db close}
2073        catch {db2 close}
2074        set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
2075        set nowcksum [cksum]
2076        set res [expr {$nowcksum==$::checksum || $nowcksum==$::goodcksum}]
2077        if {$res==0} {
2078          output2 "now=$nowcksum"
2079          output2 "the=$::checksum"
2080          output2 "fwd=$::goodcksum"
2081        }
2082        set res
2083      } 1
2084    }
2085
2086    set ::sqlite_io_error_hardhit 0
2087    set ::sqlite_io_error_pending 0
2088    if {[info exists ::ioerropts(-cleanup)]} {
2089      catch $::ioerropts(-cleanup)
2090    }
2091  }
2092  set ::sqlite_io_error_pending 0
2093  set ::sqlite_io_error_persist 0
2094  unset ::ioerropts
2095}
2096
2097# Return a checksum based on the contents of the main database associated
2098# with connection $db
2099#
2100proc cksum {{db db}} {
2101  set txt [$db eval {
2102      SELECT name, type, sql FROM sqlite_master order by name
2103  }]\n
2104  foreach tbl [$db eval {
2105      SELECT name FROM sqlite_master WHERE type='table' order by name
2106  }] {
2107    append txt [$db eval "SELECT * FROM $tbl"]\n
2108  }
2109  foreach prag {default_synchronous default_cache_size} {
2110    append txt $prag-[$db eval "PRAGMA $prag"]\n
2111  }
2112  set cksum [string length $txt]-[md5 $txt]
2113  # puts $cksum-[file size test.db]
2114  return $cksum
2115}
2116
2117# Generate a checksum based on the contents of the main and temp tables
2118# database $db. If the checksum of two databases is the same, and the
2119# integrity-check passes for both, the two databases are identical.
2120#
2121proc allcksum {{db db}} {
2122  set ret [list]
2123  ifcapable tempdb {
2124    set sql {
2125      SELECT name FROM sqlite_master WHERE type = 'table' UNION
2126      SELECT name FROM sqlite_temp_master WHERE type = 'table' UNION
2127      SELECT 'sqlite_master' UNION
2128      SELECT 'sqlite_temp_master' ORDER BY 1
2129    }
2130  } else {
2131    set sql {
2132      SELECT name FROM sqlite_master WHERE type = 'table' UNION
2133      SELECT 'sqlite_master' ORDER BY 1
2134    }
2135  }
2136  set tbllist [$db eval $sql]
2137  set txt {}
2138  foreach tbl $tbllist {
2139    append txt [$db eval "SELECT * FROM $tbl"]
2140  }
2141  foreach prag {default_cache_size} {
2142    append txt $prag-[$db eval "PRAGMA $prag"]\n
2143  }
2144  # puts txt=$txt
2145  return [md5 $txt]
2146}
2147
2148# Generate a checksum based on the contents of a single database with
2149# a database connection.  The name of the database is $dbname.
2150# Examples of $dbname are "temp" or "main".
2151#
2152proc dbcksum {db dbname} {
2153  if {$dbname=="temp"} {
2154    set master sqlite_temp_master
2155  } else {
2156    set master $dbname.sqlite_master
2157  }
2158  set alltab [$db eval "SELECT name FROM $master WHERE type='table'"]
2159  set txt [$db eval "SELECT * FROM $master"]\n
2160  foreach tab $alltab {
2161    append txt [$db eval "SELECT * FROM $dbname.$tab"]\n
2162  }
2163  return [md5 $txt]
2164}
2165
2166proc memdebug_log_sql {filename} {
2167
2168  set data [sqlite3_memdebug_log dump]
2169  set nFrame [expr [llength [lindex $data 0]]-2]
2170  if {$nFrame < 0} { return "" }
2171
2172  set database temp
2173
2174  set tbl "CREATE TABLE ${database}.malloc(zTest, nCall, nByte, lStack);"
2175
2176  set sql ""
2177  foreach e $data {
2178    set nCall [lindex $e 0]
2179    set nByte [lindex $e 1]
2180    set lStack [lrange $e 2 end]
2181    append sql "INSERT INTO ${database}.malloc VALUES"
2182    append sql "('test', $nCall, $nByte, '$lStack');\n"
2183    foreach f $lStack {
2184      set frames($f) 1
2185    }
2186  }
2187
2188  set tbl2 "CREATE TABLE ${database}.frame(frame INTEGER PRIMARY KEY, line);\n"
2189  set tbl3 "CREATE TABLE ${database}.file(name PRIMARY KEY, content);\n"
2190
2191  set pid [pid]
2192
2193  foreach f [array names frames] {
2194    set addr [format %x $f]
2195    set cmd "eu-addr2line --pid=$pid $addr"
2196    set line [eval exec $cmd]
2197    append sql "INSERT INTO ${database}.frame VALUES($f, '$line');\n"
2198
2199    set file [lindex [split $line :] 0]
2200    set files($file) 1
2201  }
2202
2203  foreach f [array names files] {
2204    set contents ""
2205    catch {
2206      set fd [open $f]
2207      set contents [read $fd]
2208      close $fd
2209    }
2210    set contents [string map {' ''} $contents]
2211    append sql "INSERT INTO ${database}.file VALUES('$f', '$contents');\n"
2212  }
2213
2214  set escaped "BEGIN; ${tbl}${tbl2}${tbl3}${sql} ; COMMIT;"
2215  set escaped [string map [list "{" "\\{" "}" "\\}" "\\" "\\\\"] $escaped]
2216
2217  set fd [open $filename w]
2218  puts $fd "set BUILTIN {"
2219  puts $fd $escaped
2220  puts $fd "}"
2221  puts $fd {set BUILTIN [string map [list "\\{" "{" "\\}" "}" "\\\\" "\\"] $BUILTIN]}
2222  set mtv [open $::testdir/malloctraceviewer.tcl]
2223  set txt [read $mtv]
2224  close $mtv
2225  puts $fd $txt
2226  close $fd
2227}
2228
2229# Drop all tables in database [db]
2230proc drop_all_tables {{db db}} {
2231  ifcapable trigger&&foreignkey {
2232    set pk [$db one "PRAGMA foreign_keys"]
2233    $db eval "PRAGMA foreign_keys = OFF"
2234  }
2235  foreach {idx name file} [db eval {PRAGMA database_list}] {
2236    if {$idx==1} {
2237      set master sqlite_temp_master
2238    } else {
2239      set master $name.sqlite_master
2240    }
2241    foreach {t type} [$db eval "
2242      SELECT name, type FROM $master
2243      WHERE type IN('table', 'view') AND name NOT LIKE 'sqliteX_%' ESCAPE 'X'
2244    "] {
2245      $db eval "DROP $type \"$t\""
2246    }
2247  }
2248  ifcapable trigger&&foreignkey {
2249    $db eval "PRAGMA foreign_keys = $pk"
2250  }
2251}
2252
2253# Drop all auxiliary indexes from the main database opened by handle [db].
2254#
2255proc drop_all_indexes {{db db}} {
2256  set L [$db eval {
2257    SELECT name FROM sqlite_master WHERE type='index' AND sql LIKE 'create%'
2258  }]
2259  foreach idx $L { $db eval "DROP INDEX $idx" }
2260}
2261
2262
2263#-------------------------------------------------------------------------
2264# If a test script is executed with global variable $::G(perm:name) set to
2265# "wal", then the tests are run in WAL mode. Otherwise, they should be run
2266# in rollback mode. The following Tcl procs are used to make this less
2267# intrusive:
2268#
2269#   wal_set_journal_mode ?DB?
2270#
2271#     If running a WAL test, execute "PRAGMA journal_mode = wal" using
2272#     connection handle DB. Otherwise, this command is a no-op.
2273#
2274#   wal_check_journal_mode TESTNAME ?DB?
2275#
2276#     If running a WAL test, execute a tests case that fails if the main
2277#     database for connection handle DB is not currently a WAL database.
2278#     Otherwise (if not running a WAL permutation) this is a no-op.
2279#
2280#   wal_is_wal_mode
2281#
2282#     Returns true if this test should be run in WAL mode. False otherwise.
2283#
2284proc wal_is_wal_mode {} {
2285  expr {[permutation] eq "wal"}
2286}
2287proc wal_set_journal_mode {{db db}} {
2288  if { [wal_is_wal_mode] } {
2289    $db eval "PRAGMA journal_mode = WAL"
2290  }
2291}
2292proc wal_check_journal_mode {testname {db db}} {
2293  if { [wal_is_wal_mode] } {
2294    $db eval { SELECT * FROM sqlite_master }
2295    do_test $testname [list $db eval "PRAGMA main.journal_mode"] {wal}
2296  }
2297}
2298
2299proc wal_is_capable {} {
2300  ifcapable !wal { return 0 }
2301  if {[permutation]=="journaltest"} { return 0 }
2302  return 1
2303}
2304
2305proc permutation {} {
2306  set perm ""
2307  catch {set perm $::G(perm:name)}
2308  set perm
2309}
2310proc presql {} {
2311  set presql ""
2312  catch {set presql $::G(perm:presql)}
2313  set presql
2314}
2315
2316proc isquick {} {
2317  set ret 0
2318  catch {set ret $::G(isquick)}
2319  set ret
2320}
2321
2322#-------------------------------------------------------------------------
2323#
2324proc slave_test_script {script} {
2325
2326  # Create the interpreter used to run the test script.
2327  interp create tinterp
2328
2329  # Populate some global variables that tester.tcl expects to see.
2330  foreach {var value} [list              \
2331    ::argv0 $::argv0                     \
2332    ::argv  {}                           \
2333    ::SLAVE 1                            \
2334  ] {
2335    interp eval tinterp [list set $var $value]
2336  }
2337
2338  # If output is being copied into a file, share the file-descriptor with
2339  # the interpreter.
2340  if {[info exists ::G(output_fd)]} {
2341    interp share {} $::G(output_fd) tinterp
2342  }
2343
2344  # The alias used to access the global test counters.
2345  tinterp alias set_test_counter set_test_counter
2346
2347  # Set up the ::cmdlinearg array in the slave.
2348  interp eval tinterp [list array set ::cmdlinearg [array get ::cmdlinearg]]
2349
2350  # Set up the ::G array in the slave.
2351  interp eval tinterp [list array set ::G [array get ::G]]
2352
2353  # Load the various test interfaces implemented in C.
2354  load_testfixture_extensions tinterp
2355
2356  # Run the test script.
2357  interp eval tinterp $script
2358
2359  # Check if the interpreter call [run_thread_tests]
2360  if { [interp eval tinterp {info exists ::run_thread_tests_called}] } {
2361    set ::run_thread_tests_called 1
2362  }
2363
2364  # Delete the interpreter used to run the test script.
2365  interp delete tinterp
2366}
2367
2368proc slave_test_file {zFile} {
2369  set tail [file tail $zFile]
2370
2371  if {[info exists ::G(start:permutation)]} {
2372    if {[permutation] != $::G(start:permutation)} return
2373    unset ::G(start:permutation)
2374  }
2375  if {[info exists ::G(start:file)]} {
2376    if {$tail != $::G(start:file) && $tail!="$::G(start:file).test"} return
2377    unset ::G(start:file)
2378  }
2379
2380  # Remember the value of the shared-cache setting. So that it is possible
2381  # to check afterwards that it was not modified by the test script.
2382  #
2383  ifcapable shared_cache { set scs [sqlite3_enable_shared_cache] }
2384
2385  # Run the test script in a slave interpreter.
2386  #
2387  unset -nocomplain ::run_thread_tests_called
2388  reset_prng_state
2389  set ::sqlite_open_file_count 0
2390  set time [time { slave_test_script [list source $zFile] }]
2391  set ms [expr [lindex $time 0] / 1000]
2392
2393  # Test that all files opened by the test script were closed. Omit this
2394  # if the test script has "thread" in its name. The open file counter
2395  # is not thread-safe.
2396  #
2397  if {[info exists ::run_thread_tests_called]==0} {
2398    do_test ${tail}-closeallfiles { expr {$::sqlite_open_file_count>0} } {0}
2399  }
2400  set ::sqlite_open_file_count 0
2401
2402  # Test that the global "shared-cache" setting was not altered by
2403  # the test script.
2404  #
2405  ifcapable shared_cache {
2406    set res [expr {[sqlite3_enable_shared_cache] == $scs}]
2407    do_test ${tail}-sharedcachesetting [list set {} $res] 1
2408  }
2409
2410  # Add some info to the output.
2411  #
2412  output2 "Time: $tail $ms ms"
2413  show_memstats
2414}
2415
2416# Open a new connection on database test.db and execute the SQL script
2417# supplied as an argument. Before returning, close the new conection and
2418# restore the 4 byte fields starting at header offsets 28, 92 and 96
2419# to the values they held before the SQL was executed. This simulates
2420# a write by a pre-3.7.0 client.
2421#
2422proc sql36231 {sql} {
2423  set B [hexio_read test.db 92 8]
2424  set A [hexio_read test.db 28 4]
2425  sqlite3 db36231 test.db
2426  catch { db36231 func a_string a_string }
2427  execsql $sql db36231
2428  db36231 close
2429  hexio_write test.db 28 $A
2430  hexio_write test.db 92 $B
2431  return ""
2432}
2433
2434proc db_save {} {
2435  foreach f [glob -nocomplain sv_test.db*] { forcedelete $f }
2436  foreach f [glob -nocomplain test.db*] {
2437    set f2 "sv_$f"
2438    forcecopy $f $f2
2439  }
2440}
2441proc db_save_and_close {} {
2442  db_save
2443  catch { db close }
2444  return ""
2445}
2446proc db_restore {} {
2447  foreach f [glob -nocomplain test.db*] { forcedelete $f }
2448  foreach f2 [glob -nocomplain sv_test.db*] {
2449    set f [string range $f2 3 end]
2450    forcecopy $f2 $f
2451  }
2452}
2453proc db_restore_and_reopen {{dbfile test.db}} {
2454  catch { db close }
2455  db_restore
2456  sqlite3 db $dbfile
2457}
2458proc db_delete_and_reopen {{file test.db}} {
2459  catch { db close }
2460  foreach f [glob -nocomplain test.db*] { forcedelete $f }
2461  sqlite3 db $file
2462}
2463
2464# Close any connections named [db], [db2] or [db3]. Then use sqlite3_config
2465# to configure the size of the PAGECACHE allocation using the parameters
2466# provided to this command. Save the old PAGECACHE parameters in a global
2467# variable so that [test_restore_config_pagecache] can restore the previous
2468# configuration.
2469#
2470# Before returning, reopen connection [db] on file test.db.
2471#
2472proc test_set_config_pagecache {sz nPg} {
2473  catch {db close}
2474  catch {db2 close}
2475  catch {db3 close}
2476
2477  sqlite3_shutdown
2478  set ::old_pagecache_config [sqlite3_config_pagecache $sz $nPg]
2479  sqlite3_initialize
2480  autoinstall_test_functions
2481  reset_db
2482}
2483
2484# Close any connections named [db], [db2] or [db3]. Then use sqlite3_config
2485# to configure the size of the PAGECACHE allocation to the size saved in
2486# the global variable by an earlier call to [test_set_config_pagecache].
2487#
2488# Before returning, reopen connection [db] on file test.db.
2489#
2490proc test_restore_config_pagecache {} {
2491  catch {db close}
2492  catch {db2 close}
2493  catch {db3 close}
2494
2495  sqlite3_shutdown
2496  eval sqlite3_config_pagecache $::old_pagecache_config
2497  unset ::old_pagecache_config
2498  sqlite3_initialize
2499  autoinstall_test_functions
2500  sqlite3 db test.db
2501}
2502
2503proc test_binary_name {nm} {
2504  if {$::tcl_platform(platform)=="windows"} {
2505    set ret "$nm.exe"
2506  } else {
2507    set ret $nm
2508  }
2509  file normalize [file join $::cmdlinearg(TESTFIXTURE_HOME) $ret]
2510}
2511
2512proc test_find_binary {nm} {
2513  set ret [test_binary_name $nm]
2514  if {![file executable $ret]} {
2515    finish_test
2516    return ""
2517  }
2518  return $ret
2519}
2520
2521# Find the name of the 'shell' executable (e.g. "sqlite3.exe") to use for
2522# the tests in shell*.test. If no such executable can be found, invoke
2523# [finish_test ; return] in the callers context.
2524#
2525proc test_find_cli {} {
2526  set prog [test_find_binary sqlite3]
2527  if {$prog==""} { return -code return }
2528  return $prog
2529}
2530
2531# Find invocation of the 'shell' executable (e.g. "sqlite3.exe") to use
2532# for the tests in shell*.test with optional valgrind prefix when the
2533# environment variable SQLITE_CLI_VALGRIND_OPT is set. The set value
2534# operates as follows:
2535#   empty or 0 => no valgrind prefix;
2536#   1 => valgrind options for memory leak check;
2537#   other => use value as valgrind options.
2538# If shell not found, invoke [finish_test ; return] in callers context.
2539#
2540proc test_cli_invocation {} {
2541  set prog [test_find_binary sqlite3]
2542  if {$prog==""} { return -code return }
2543  set vgrun [expr {[permutation]=="valgrind"}]
2544  if {$vgrun || [info exists ::env(SQLITE_CLI_VALGRIND_OPT)]} {
2545    if {$vgrun} {
2546      set vgo "--quiet"
2547    } else {
2548      set vgo $::env(SQLITE_CLI_VALGRIND_OPT)
2549    }
2550    if {$vgo == 0 || $vgo eq ""} {
2551      return $prog
2552    } elseif {$vgo == 1} {
2553      return "valgrind --quiet --leak-check=yes $prog"
2554    } else {
2555      return "valgrind $vgo $prog"
2556    }
2557  } else {
2558    return $prog
2559  }
2560}
2561
2562# Find the name of the 'sqldiff' executable (e.g. "sqlite3.exe") to use for
2563# the tests in sqldiff tests. If no such executable can be found, invoke
2564# [finish_test ; return] in the callers context.
2565#
2566proc test_find_sqldiff {} {
2567  set prog [test_find_binary sqldiff]
2568  if {$prog==""} { return -code return }
2569  return $prog
2570}
2571
2572# Call sqlite3_expanded_sql() on all statements associated with database
2573# connection $db. This sometimes finds use-after-free bugs if run with
2574# valgrind or address-sanitizer.
2575proc expand_all_sql {db} {
2576  set stmt ""
2577  while {[set stmt [sqlite3_next_stmt $db $stmt]]!=""} {
2578    sqlite3_expanded_sql $stmt
2579  }
2580}
2581
2582
2583# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
2584# to non-zero, then set the global variable $AUTOVACUUM to 1.
2585set AUTOVACUUM $sqlite_options(default_autovacuum)
2586
2587# Make sure the FTS enhanced query syntax is disabled.
2588set sqlite_fts3_enable_parentheses 0
2589
2590# During testing, assume that all database files are well-formed.  The
2591# few test cases that deliberately corrupt database files should rescind
2592# this setting by invoking "database_can_be_corrupt"
2593#
2594database_never_corrupt
2595extra_schema_checks 1
2596
2597source $testdir/thread_common.tcl
2598source $testdir/malloc_common.tcl
2599
2600set tester_tcl_has_run 1
2601