Fixes default log output to console for macOS
[sqlcipher.git] / ext / repair / sqlite3_checker.tcl
blob2ae6e15b123cf733042ef5bc524ae95ffdc1d34c
1 # This TCL script is the main driver script for the sqlite3_checker utility
2 # program.
5 # Special case:
7 # sqlite3_checker --test FILENAME ARGS
9 # uses FILENAME in place of this script.
11 if {[lindex $argv 0]=="--test" && [llength $argv]>1} {
12 set ::argv0 [lindex $argv 1]
13 set argv [lrange $argv 2 end]
14 source $argv0
15 exit 0
18 # Emulate a TCL shell
20 proc tclsh {} {
21 set line {}
22 while {![eof stdin]} {
23 if {$line!=""} {
24 puts -nonewline "> "
25 } else {
26 puts -nonewline "% "
28 flush stdout
29 append line [gets stdin]
30 if {[info complete $line]} {
31 if {[catch {uplevel #0 $line} result]} {
32 puts stderr "Error: $result"
33 } elseif {$result!=""} {
34 puts $result
36 set line {}
37 } else {
38 append line \n
43 # Do an incremental integrity check of a single index
45 proc check_index {idxname batchsize bTrace} {
46 set i 0
47 set more 1
48 set nerr 0
49 set pct 00.0
50 set max [db one {SELECT nEntry FROM sqlite_btreeinfo('main')
51 WHERE name=$idxname}]
52 puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
53 flush stdout
54 if {$bTrace} {
55 set sql {SELECT errmsg, current_key AS key,
56 CASE WHEN rowid=1 THEN scanner_sql END AS traceOut
57 FROM incremental_index_check($idxname)
58 WHERE after_key=$key
59 LIMIT $batchsize}
60 } else {
61 set sql {SELECT errmsg, current_key AS key, NULL AS traceOut
62 FROM incremental_index_check($idxname)
63 WHERE after_key=$key
64 LIMIT $batchsize}
66 while {$more} {
67 set more 0
68 db eval $sql {
69 set more 1
70 if {$errmsg!=""} {
71 incr nerr
72 puts "$idxname: key($key): $errmsg"
73 } elseif {$traceOut!=""} {
74 puts "$idxname: $traceOut"
76 incr i
79 set x [format {%.1f} [expr {($i*100.0)/$max}]]
80 if {$x!=$pct} {
81 puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
82 flush stdout
83 set pct $x
86 puts "$idxname: $nerr errors out of $i entries"
89 # Print a usage message on standard error, then quit.
91 proc usage {} {
92 set argv0 [file rootname [file tail [info nameofexecutable]]]
93 puts stderr "Usage: $argv0 OPTIONS database-filename"
94 puts stderr {
95 Do sanity checking on a live SQLite3 database file specified by the
96 "database-filename" argument.
98 Options:
100 --batchsize N Number of rows to check per transaction
102 --freelist Perform a freelist check
104 --index NAME Run a check of the index NAME
106 --summary Print summary information about the database
108 --table NAME Run a check of all indexes for table NAME
110 --tclsh Run the built-in TCL interpreter (for debugging)
112 --trace (Debugging only:) Output trace information on the scan
114 --version Show the version number of SQLite
116 exit 1
119 set file_to_analyze {}
120 append argv {}
121 set bFreelistCheck 0
122 set bSummary 0
123 set zIndex {}
124 set zTable {}
125 set batchsize 1000
126 set bAll 1
127 set bTrace 0
128 set argc [llength $argv]
129 for {set i 0} {$i<$argc} {incr i} {
130 set arg [lindex $argv $i]
131 if {[regexp {^-+tclsh$} $arg]} {
132 tclsh
133 exit 0
135 if {[regexp {^-+version$} $arg]} {
136 sqlite3 mem :memory:
137 puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
138 mem close
139 exit 0
141 if {[regexp {^-+freelist$} $arg]} {
142 set bFreelistCheck 1
143 set bAll 0
144 continue
146 if {[regexp {^-+summary$} $arg]} {
147 set bSummary 1
148 set bAll 0
149 continue
151 if {[regexp {^-+trace$} $arg]} {
152 set bTrace 1
153 continue
155 if {[regexp {^-+batchsize$} $arg]} {
156 incr i
157 if {$i>=$argc} {
158 puts stderr "missing argument on $arg"
159 exit 1
161 set batchsize [lindex $argv $i]
162 continue
164 if {[regexp {^-+index$} $arg]} {
165 incr i
166 if {$i>=$argc} {
167 puts stderr "missing argument on $arg"
168 exit 1
170 set zIndex [lindex $argv $i]
171 set bAll 0
172 continue
174 if {[regexp {^-+table$} $arg]} {
175 incr i
176 if {$i>=$argc} {
177 puts stderr "missing argument on $arg"
178 exit 1
180 set zTable [lindex $argv $i]
181 set bAll 0
182 continue
184 if {[regexp {^-} $arg]} {
185 puts stderr "Unknown option: $arg"
186 usage
188 if {$file_to_analyze!=""} {
189 usage
190 } else {
191 set file_to_analyze $arg
194 if {$file_to_analyze==""} usage
196 # If a TCL script is specified on the command-line, then run that
197 # script.
199 if {[file extension $file_to_analyze]==".tcl"} {
200 source $file_to_analyze
201 exit 0
204 set root_filename $file_to_analyze
205 regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
206 if {![file exists $root_filename]} {
207 puts stderr "No such file: $root_filename"
208 exit 1
210 if {![file readable $root_filename]} {
211 puts stderr "File is not readable: $root_filename"
212 exit 1
215 if {[catch {sqlite3 db $file_to_analyze} res]} {
216 puts stderr "Cannot open datababase $root_filename: $res"
217 exit 1
220 if {$bFreelistCheck || $bAll} {
221 puts -nonewline "freelist-check: "
222 flush stdout
223 db eval BEGIN
224 puts [db one {SELECT checkfreelist('main')}]
225 db eval END
227 if {$bSummary} {
228 set scale 0
229 set pgsz [db one {PRAGMA page_size}]
230 db eval {SELECT nPage*$pgsz AS sz, name, tbl_name
231 FROM sqlite_btreeinfo
232 WHERE type='index'
233 ORDER BY 1 DESC, name} {
234 if {$scale==0} {
235 if {$sz>10000000} {
236 set scale 1000000.0
237 set unit MB
238 } else {
239 set scale 1000.0
240 set unit KB
243 puts [format {%7.1f %s index %s of table %s} \
244 [expr {$sz/$scale}] $unit $name $tbl_name]
247 if {$zIndex!=""} {
248 check_index $zIndex $batchsize $bTrace
250 if {$zTable!=""} {
251 foreach idx [db eval {SELECT name FROM sqlite_master
252 WHERE type='index' AND rootpage>0
253 AND tbl_name=$zTable}] {
254 check_index $idx $batchsize $bTrace
257 if {$bAll} {
258 set allidx [db eval {SELECT name FROM sqlite_btreeinfo('main')
259 WHERE type='index' AND rootpage>0
260 ORDER BY nEntry}]
261 foreach idx $allidx {
262 check_index $idx $batchsize $bTrace