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