2017-10-31 15:49:19 +00:00
# This TCL script is the main driver script for the sqlite3_checker utility
# program.
#
2017-11-01 00:10:34 +00:00
# Special case:
#
# sqlite3_checker --test FILENAME ARGS
#
# uses FILENAME in place of this script.
#
2017-11-01 18:05:32 +00:00
if { [ lindex $argv 0 ] == " - - t e s t " && [ llength $argv ] > 1 } {
set : : argv0 [ lindex $argv 1 ]
2017-11-01 00:10:34 +00:00
set argv [ lrange $argv 2 end]
2017-11-01 18:05:32 +00:00
source $argv0
2017-11-01 00:10:34 +00:00
exit 0
}
# Emulate a TCL shell
#
2017-10-31 15:49:19 +00:00
proc tclsh { } {
set line { }
while { ! [ eof stdin] } {
if { $line != " " } {
puts - nonewline " > "
} else {
puts - nonewline " % "
}
flush stdout
append line [ gets stdin]
if { [ info complete $line ] } {
if { [ catch { uplevel # 0 $line } result] } {
puts stderr " E r r o r : $ r e s u l t "
} elseif { $result != " " } {
puts $result
}
set line { }
} else {
append line \ n
}
}
}
2017-11-01 01:01:20 +00:00
# Do an incremental integrity check of a single index
#
2017-11-07 16:54:20 +00:00
proc check_index { idxname batchsize bTrace} {
2017-11-01 01:01:20 +00:00
set i 0
set more 1
set nerr 0
2017-11-01 13:09:02 +00:00
set pct 00 .0
set max [ db one { SELECT nEntry FROM sqlite_btreeinfo( ' main ' )
WHERE name= $idxname } ]
puts - nonewline " $ i d x n a m e : $ i o f $ m a x r o w s ( $ p c t % ) \r "
flush stdout
2017-11-07 16:54:20 +00:00
if { $bTrace } {
set sql { SELECT errmsg, current_key AS key,
CASE WHEN rowid= 1 THEN scanner_sql END AS traceOut
FROM incremental_index_check( $idxname )
WHERE after_key= $key
LIMIT $batchsize }
} else {
set sql { SELECT errmsg, current_key AS key, NULL AS traceOut
2017-11-01 01:01:20 +00:00
FROM incremental_index_check( $idxname )
WHERE after_key= $key
2017-11-07 16:54:20 +00:00
LIMIT $batchsize }
}
while { $more } {
set more 0
db eval $sql {
2017-11-01 01:01:20 +00:00
set more 1
if { $errmsg != " " } {
incr nerr
2017-11-01 13:09:02 +00:00
puts " $ i d x n a m e : k e y ( $ k e y ) : $ e r r m s g "
2017-11-07 16:54:20 +00:00
} elseif { $traceOut != " " } {
puts " $ i d x n a m e : $ t r a c e O u t "
2017-11-01 01:01:20 +00:00
}
incr i
2017-11-07 16:54:20 +00:00
2017-11-01 01:01:20 +00:00
}
2017-11-01 13:09:02 +00:00
set x [ format { % .1f } [ expr { ( $i * 100.0 ) / $max } ] ]
if { $x != $pct } {
puts - nonewline " $ i d x n a m e : $ i o f $ m a x r o w s ( $ p c t % ) \r "
flush stdout
set pct $x
}
2017-11-01 01:01:20 +00:00
}
2017-11-01 13:09:02 +00:00
puts " $ i d x n a m e : $ n e r r e r r o r s o u t o f $ i e n t r i e s "
2017-11-01 01:01:20 +00:00
}
2017-11-01 00:10:34 +00:00
# Print a usage message on standard error, then quit.
#
2017-10-31 15:49:19 +00:00
proc usage { } {
set argv0 [ file rootname [ file tail [ info nameofexecutable] ] ]
puts stderr " U s a g e : $ a r g v 0 O P T I O N S d a t a b a s e - f i l e n a m e "
puts stderr {
Do sanity checking on a live SQLite3 database file specified by the
" d a t a b a s e - f i l e n a m e " argument.
Options :
2017-11-01 01:01:20 +00:00
--batchsize N Number of rows to check per transaction
--freelist Perform a freelist check
--index NAME Run a check of the index NAME
--summary Print summary information about the database
2017-11-01 00:10:34 +00:00
2017-11-01 01:01:20 +00:00
--table NAME Run a check of all indexes for table NAME
2017-10-31 15:49:19 +00:00
2017-11-01 01:01:20 +00:00
--tclsh Run the built-in TCL interpreter ( for debugging)
2017-11-07 16:54:20 +00:00
--trace ( Debugging only:) Output trace information on the scan
2017-11-01 01:01:20 +00:00
--version Show the version number of SQLite
2017-10-31 15:49:19 +00:00
}
exit 1
}
set file_to_analyze { }
append argv { }
2017-11-01 00:10:34 +00:00
set bFreelistCheck 0
2017-11-01 01:01:20 +00:00
set bSummary 0
set zIndex { }
set zTable { }
2017-11-01 13:09:02 +00:00
set batchsize 1000
2017-11-01 01:01:20 +00:00
set bAll 1
2017-11-07 16:54:20 +00:00
set bTrace 0
2017-11-01 01:01:20 +00:00
set argc [ llength $argv ]
for { set i 0 } { $i < $argc } { incr i} {
set arg [ lindex $argv $i ]
2017-10-31 15:49:19 +00:00
if { [ regexp { ^ - + tclsh$ } $arg ] } {
tclsh
exit 0
}
if { [ regexp { ^ - + version$ } $arg ] } {
sqlite3 mem : memory:
puts [ mem one { SELECT sqlite_version( ) || ' ' || sqlite_source_id( ) } ]
mem close
exit 0
}
2017-11-01 00:10:34 +00:00
if { [ regexp { ^ - + freelist$ } $arg ] } {
set bFreelistCheck 1
2017-11-01 01:01:20 +00:00
set bAll 0
continue
}
if { [ regexp { ^ - + summary$ } $arg ] } {
set bSummary 1
set bAll 0
continue
}
2017-11-07 16:54:20 +00:00
if { [ regexp { ^ - + trace$ } $arg ] } {
set bTrace 1
continue
}
2017-11-01 01:01:20 +00:00
if { [ regexp { ^ - + batchsize$ } $arg ] } {
incr i
if { $i >= $argc } {
puts stderr " m i s s i n g a r g u m e n t o n $ a r g "
exit 1
}
set batchsize [ lindex $argv $i ]
continue
}
if { [ regexp { ^ - + index$ } $arg ] } {
incr i
if { $i >= $argc } {
puts stderr " m i s s i n g a r g u m e n t o n $ a r g "
exit 1
}
set zIndex [ lindex $argv $i ]
set bAll 0
continue
}
if { [ regexp { ^ - + table$ } $arg ] } {
incr i
if { $i >= $argc } {
puts stderr " m i s s i n g a r g u m e n t o n $ a r g "
exit 1
}
set zTable [ lindex $argv $i ]
set bAll 0
2017-11-01 00:10:34 +00:00
continue
}
2017-10-31 15:49:19 +00:00
if { [ regexp { ^ - } $arg ] } {
puts stderr " U n k n o w n o p t i o n : $ a r g "
usage
}
if { $file_to_analyze != " " } {
usage
} else {
set file_to_analyze $arg
}
}
if { $file_to_analyze == " " } usage
# If a TCL script is specified on the command-line, then run that
# script.
#
if { [ file extension $file_to_analyze ] == " . t c l " } {
source $file_to_analyze
exit 0
}
set root_filename $file_to_analyze
regexp { ^ file : ( / / ) ? ( [ ^ ? ] * ) } $file_to_analyze all x1 root_filename
if { ! [ file exists $root_filename ] } {
puts stderr " N o s u c h f i l e : $ r o o t _ f i l e n a m e "
exit 1
}
if { ! [ file readable $root_filename ] } {
puts stderr " F i l e i s n o t r e a d a b l e : $ r o o t _ f i l e n a m e "
exit 1
}
2017-11-01 00:10:34 +00:00
if { [ catch { sqlite3 db $file_to_analyze } res] } {
puts stderr " C a n n o t o p e n d a t a b a b a s e $ r o o t _ f i l e n a m e : $ r e s "
exit 1
}
2017-11-01 01:01:20 +00:00
if { $bFreelistCheck || $bAll } {
puts - nonewline " f r e e l i s t - c h e c k : "
2017-11-01 00:10:34 +00:00
flush stdout
2018-03-02 20:00:42 +00:00
db eval BEGIN
2017-11-01 00:10:34 +00:00
puts [ db one { SELECT checkfreelist( ' main ' ) } ]
2018-03-02 20:00:42 +00:00
db eval END
2017-11-01 00:10:34 +00:00
}
if { $bSummary } {
set scale 0
set pgsz [ db one { PRAGMA page_size} ]
db eval { SELECT nPage* $pgsz AS sz, name, tbl_name
FROM sqlite_btreeinfo
WHERE type= ' index'
ORDER BY 1 DESC, name} {
if { $scale == 0 } {
if { $sz > 10000000 } {
set scale 1000000.0
set unit MB
} else {
set scale 1000.0
set unit KB
}
}
puts [ format { % 7.1f % s index % s of table % s} \
[ expr { $sz / $scale } ] $unit $name $tbl_name ]
}
}
2017-11-01 01:01:20 +00:00
if { $zIndex != " " } {
2017-11-07 16:54:20 +00:00
check_index $zIndex $batchsize $bTrace
2017-11-01 01:01:20 +00:00
}
if { $zTable != " " } {
foreach idx [ db eval { SELECT name FROM sqlite_master
WHERE type= ' index' AND rootpage> 0
AND tbl_name= $zTable } ] {
2017-11-07 16:54:20 +00:00
check_index $idx $batchsize $bTrace
2017-11-01 01:01:20 +00:00
}
}
if { $bAll } {
set allidx [ db eval { SELECT name FROM sqlite_btreeinfo( ' main ' )
WHERE type= ' index' AND rootpage> 0
ORDER BY nEntry} ]
foreach idx $allidx {
2017-11-07 16:54:20 +00:00
check_index $idx $batchsize $bTrace
2017-11-01 01:01:20 +00:00
}
}