track 3.7.6.2

This commit is contained in:
Stephen Lombardo
2011-05-05 22:37:34 -04:00
parent fc5fc812f6
commit 7cffc8807d
177 changed files with 21128 additions and 2529 deletions
+9 -1
View File
@@ -165,7 +165,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
callback.lo complete.lo ctime.lo date.lo delete.lo \
expr.lo fault.lo fkey.lo \
fts3.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \
fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \
fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo fts3_write.lo \
func.lo global.lo hash.lo \
icu.lo insert.lo journal.lo legacy.lo loadext.lo \
@@ -306,6 +306,7 @@ SRC += \
$(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3.h \
$(TOP)/ext/fts3/fts3Int.h \
$(TOP)/ext/fts3/fts3_aux.c \
$(TOP)/ext/fts3/fts3_expr.c \
$(TOP)/ext/fts3/fts3_hash.c \
$(TOP)/ext/fts3/fts3_hash.h \
@@ -355,6 +356,7 @@ TESTSRC = \
$(TOP)/src/test_demovfs.c \
$(TOP)/src/test_devsym.c \
$(TOP)/src/test_func.c \
$(TOP)/src/test_fuzzer.c \
$(TOP)/src/test_hexio.c \
$(TOP)/src/test_init.c \
$(TOP)/src/test_intarray.c \
@@ -370,10 +372,12 @@ TESTSRC = \
$(TOP)/src/test_schema.c \
$(TOP)/src/test_server.c \
$(TOP)/src/test_superlock.c \
$(TOP)/src/test_syscall.c \
$(TOP)/src/test_stat.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_wholenumber.c \
$(TOP)/src/test_wsd.c
# Source code to the library files needed by the test fixture
@@ -414,6 +418,7 @@ TESTSRC2 = \
$(TOP)/src/where.c \
parse.c \
$(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3_aux.c \
$(TOP)/ext/fts3/fts3_expr.c \
$(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \
@@ -811,6 +816,9 @@ fts2_tokenizer1.lo: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR)
fts3.lo: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c
fts3_aux.lo: $(TOP)/ext/fts3/fts3_aux.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_aux.c
fts3_expr.lo: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c
+1 -1
View File
@@ -1 +1 @@
3.7.5
3.7.6.2
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

+5333
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Vendored
+9 -9
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.62 for sqlite 3.7.5.
# Generated by GNU Autoconf 2.62 for sqlite 3.7.6.2.
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.7.5'
PACKAGE_STRING='sqlite 3.7.5'
PACKAGE_VERSION='3.7.6.2'
PACKAGE_STRING='sqlite 3.7.6.2'
PACKAGE_BUGREPORT=''
# Factoring default headers for most tests.
@@ -1485,7 +1485,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.7.5 to adapt to many kinds of systems.
\`configure' configures sqlite 3.7.6.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1550,7 +1550,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.7.5:";;
short | recursive ) echo "Configuration of sqlite 3.7.6.2:";;
esac
cat <<\_ACEOF
@@ -1666,7 +1666,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.7.5
sqlite configure 3.7.6.2
generated by GNU Autoconf 2.62
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1680,7 +1680,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.7.5, which was
It was created by sqlite $as_me 3.7.6.2, which was
generated by GNU Autoconf 2.62. Invocation command line was
$ $0 $@
@@ -13942,7 +13942,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.7.5, which was
This file was extended by sqlite $as_me 3.7.6.2, which was
generated by GNU Autoconf 2.62. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -13995,7 +13995,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_version="\\
sqlite config.status 3.7.5
sqlite config.status 3.7.6.2
configured by $0, generated by GNU Autoconf 2.62,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+679
View File
@@ -0,0 +1,679 @@
# A Tk console widget for SQLite. Invoke sqlitecon::create with a window name,
# a prompt string, a title to set a new top-level window, and the SQLite
# database handle. For example:
#
# sqlitecon::create .sqlcon {sql:- } {SQL Console} db
#
# A toplevel window is created that allows you to type in SQL commands to
# be processed on the spot.
#
# A limited set of dot-commands are supported:
#
# .table
# .schema ?TABLE?
# .mode list|column|multicolumn|line
# .exit
#
# In addition, a new SQL function named "edit()" is created. This function
# takes a single text argument and returns a text result. Whenever the
# the function is called, it pops up a new toplevel window containing a
# text editor screen initialized to the argument. When the "OK" button
# is pressed, whatever revised text is in the text editor is returned as
# the result of the edit() function. This allows text fields of SQL tables
# to be edited quickly and easily as follows:
#
# UPDATE table1 SET dscr = edit(dscr) WHERE rowid=15;
#
# Create a namespace to work in
#
namespace eval ::sqlitecon {
# do nothing
}
# Create a console widget named $w. The prompt string is $prompt.
# The title at the top of the window is $title. The database connection
# object is $db
#
proc sqlitecon::create {w prompt title db} {
upvar #0 $w.t v
if {[winfo exists $w]} {destroy $w}
if {[info exists v]} {unset v}
toplevel $w
wm title $w $title
wm iconname $w $title
frame $w.mb -bd 2 -relief raised
pack $w.mb -side top -fill x
menubutton $w.mb.file -text File -menu $w.mb.file.m
menubutton $w.mb.edit -text Edit -menu $w.mb.edit.m
pack $w.mb.file $w.mb.edit -side left -padx 8 -pady 1
set m [menu $w.mb.file.m -tearoff 0]
$m add command -label {Close} -command "destroy $w"
sqlitecon::create_child $w $prompt $w.mb.edit.m
set v(db) $db
$db function edit ::sqlitecon::_edit
}
# This routine creates a console as a child window within a larger
# window. It also creates an edit menu named "$editmenu" if $editmenu!="".
# The calling function is responsible for posting the edit menu.
#
proc sqlitecon::create_child {w prompt editmenu} {
upvar #0 $w.t v
if {$editmenu!=""} {
set m [menu $editmenu -tearoff 0]
$m add command -label Cut -command "sqlitecon::Cut $w.t"
$m add command -label Copy -command "sqlitecon::Copy $w.t"
$m add command -label Paste -command "sqlitecon::Paste $w.t"
$m add command -label {Clear Screen} -command "sqlitecon::Clear $w.t"
$m add separator
$m add command -label {Save As...} -command "sqlitecon::SaveFile $w.t"
catch {$editmenu config -postcommand "sqlitecon::EnableEditMenu $w"}
}
scrollbar $w.sb -orient vertical -command "$w.t yview"
pack $w.sb -side right -fill y
text $w.t -font fixed -yscrollcommand "$w.sb set"
pack $w.t -side right -fill both -expand 1
bindtags $w.t Sqlitecon
set v(editmenu) $editmenu
set v(history) 0
set v(historycnt) 0
set v(current) -1
set v(prompt) $prompt
set v(prior) {}
set v(plength) [string length $v(prompt)]
set v(x) 0
set v(y) 0
set v(mode) column
set v(header) on
$w.t mark set insert end
$w.t tag config ok -foreground blue
$w.t tag config err -foreground red
$w.t insert end $v(prompt)
$w.t mark set out 1.0
after idle "focus $w.t"
}
bind Sqlitecon <1> {sqlitecon::Button1 %W %x %y}
bind Sqlitecon <B1-Motion> {sqlitecon::B1Motion %W %x %y}
bind Sqlitecon <B1-Leave> {sqlitecon::B1Leave %W %x %y}
bind Sqlitecon <B1-Enter> {sqlitecon::cancelMotor %W}
bind Sqlitecon <ButtonRelease-1> {sqlitecon::cancelMotor %W}
bind Sqlitecon <KeyPress> {sqlitecon::Insert %W %A}
bind Sqlitecon <Left> {sqlitecon::Left %W}
bind Sqlitecon <Control-b> {sqlitecon::Left %W}
bind Sqlitecon <Right> {sqlitecon::Right %W}
bind Sqlitecon <Control-f> {sqlitecon::Right %W}
bind Sqlitecon <BackSpace> {sqlitecon::Backspace %W}
bind Sqlitecon <Control-h> {sqlitecon::Backspace %W}
bind Sqlitecon <Delete> {sqlitecon::Delete %W}
bind Sqlitecon <Control-d> {sqlitecon::Delete %W}
bind Sqlitecon <Home> {sqlitecon::Home %W}
bind Sqlitecon <Control-a> {sqlitecon::Home %W}
bind Sqlitecon <End> {sqlitecon::End %W}
bind Sqlitecon <Control-e> {sqlitecon::End %W}
bind Sqlitecon <Return> {sqlitecon::Enter %W}
bind Sqlitecon <KP_Enter> {sqlitecon::Enter %W}
bind Sqlitecon <Up> {sqlitecon::Prior %W}
bind Sqlitecon <Control-p> {sqlitecon::Prior %W}
bind Sqlitecon <Down> {sqlitecon::Next %W}
bind Sqlitecon <Control-n> {sqlitecon::Next %W}
bind Sqlitecon <Control-k> {sqlitecon::EraseEOL %W}
bind Sqlitecon <<Cut>> {sqlitecon::Cut %W}
bind Sqlitecon <<Copy>> {sqlitecon::Copy %W}
bind Sqlitecon <<Paste>> {sqlitecon::Paste %W}
bind Sqlitecon <<Clear>> {sqlitecon::Clear %W}
# Insert a single character at the insertion cursor
#
proc sqlitecon::Insert {w a} {
$w insert insert $a
$w yview insert
}
# Move the cursor one character to the left
#
proc sqlitecon::Left {w} {
upvar #0 $w v
scan [$w index insert] %d.%d row col
if {$col>$v(plength)} {
$w mark set insert "insert -1c"
}
}
# Erase the character to the left of the cursor
#
proc sqlitecon::Backspace {w} {
upvar #0 $w v
scan [$w index insert] %d.%d row col
if {$col>$v(plength)} {
$w delete {insert -1c}
}
}
# Erase to the end of the line
#
proc sqlitecon::EraseEOL {w} {
upvar #0 $w v
scan [$w index insert] %d.%d row col
if {$col>=$v(plength)} {
$w delete insert {insert lineend}
}
}
# Move the cursor one character to the right
#
proc sqlitecon::Right {w} {
$w mark set insert "insert +1c"
}
# Erase the character to the right of the cursor
#
proc sqlitecon::Delete w {
$w delete insert
}
# Move the cursor to the beginning of the current line
#
proc sqlitecon::Home w {
upvar #0 $w v
scan [$w index insert] %d.%d row col
$w mark set insert $row.$v(plength)
}
# Move the cursor to the end of the current line
#
proc sqlitecon::End w {
$w mark set insert {insert lineend}
}
# Add a line to the history
#
proc sqlitecon::addHistory {w line} {
upvar #0 $w v
if {$v(historycnt)>0} {
set last [lindex $v(history) [expr $v(historycnt)-1]]
if {[string compare $last $line]} {
lappend v(history) $line
incr v(historycnt)
}
} else {
set v(history) [list $line]
set v(historycnt) 1
}
set v(current) $v(historycnt)
}
# Called when "Enter" is pressed. Do something with the line
# of text that was entered.
#
proc sqlitecon::Enter w {
upvar #0 $w v
scan [$w index insert] %d.%d row col
set start $row.$v(plength)
set line [$w get $start "$start lineend"]
$w insert end \n
$w mark set out end
if {$v(prior)==""} {
set cmd $line
} else {
set cmd $v(prior)\n$line
}
if {[string index $cmd 0]=="." || [$v(db) complete $cmd]} {
regsub -all {\n} [string trim $cmd] { } cmd2
addHistory $w $cmd2
set rc [catch {DoCommand $w $cmd} res]
if {![winfo exists $w]} return
if {$rc} {
$w insert end $res\n err
} elseif {[string length $res]>0} {
$w insert end $res\n ok
}
set v(prior) {}
$w insert end $v(prompt)
} else {
set v(prior) $cmd
regsub -all {[^ ]} $v(prompt) . x
$w insert end $x
}
$w mark set insert end
$w mark set out {insert linestart}
$w yview insert
}
# Execute a single SQL command. Pay special attention to control
# directives that begin with "."
#
# The return value is the text output from the command, properly
# formatted.
#
proc sqlitecon::DoCommand {w cmd} {
upvar #0 $w v
set mode $v(mode)
set header $v(header)
if {[regexp {^(\.[a-z]+)} $cmd all word]} {
if {$word==".mode"} {
regexp {^.[a-z]+ +([a-z]+)} $cmd all v(mode)
return {}
} elseif {$word==".exit"} {
destroy [winfo toplevel $w]
return {}
} elseif {$word==".header"} {
regexp {^.[a-z]+ +([a-z]+)} $cmd all v(header)
return {}
} elseif {$word==".tables"} {
set mode multicolumn
set cmd {SELECT name FROM sqlite_master WHERE type='table'
UNION ALL
SELECT name FROM sqlite_temp_master WHERE type='table'}
$v(db) eval {PRAGMA database_list} {
if {$name!="temp" && $name!="main"} {
append cmd "UNION ALL SELECT name FROM $name.sqlite_master\
WHERE type='table'"
}
}
append cmd { ORDER BY 1}
} elseif {$word==".fullschema"} {
set pattern %
regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern
set mode list
set header 0
set cmd "SELECT sql FROM sqlite_master WHERE tbl_name LIKE '$pattern'
AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master
WHERE tbl_name LIKE '$pattern' AND sql NOT NULL"
$v(db) eval {PRAGMA database_list} {
if {$name!="temp" && $name!="main"} {
append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\
WHERE tbl_name LIKE '$pattern' AND sql NOT NULL"
}
}
} elseif {$word==".schema"} {
set pattern %
regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern
set mode list
set header 0
set cmd "SELECT sql FROM sqlite_master WHERE name LIKE '$pattern'
AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master
WHERE name LIKE '$pattern' AND sql NOT NULL"
$v(db) eval {PRAGMA database_list} {
if {$name!="temp" && $name!="main"} {
append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\
WHERE name LIKE '$pattern' AND sql NOT NULL"
}
}
} else {
return \
".exit\n.mode line|list|column\n.schema ?TABLENAME?\n.tables"
}
}
set res {}
if {$mode=="list"} {
$v(db) eval $cmd x {
set sep {}
foreach col $x(*) {
append res $sep$x($col)
set sep |
}
append res \n
}
if {[info exists x(*)] && $header} {
set sep {}
set hdr {}
foreach col $x(*) {
append hdr $sep$col
set sep |
}
set res $hdr\n$res
}
} elseif {[string range $mode 0 2]=="col"} {
set y {}
$v(db) eval $cmd x {
foreach col $x(*) {
if {![info exists cw($col)] || $cw($col)<[string length $x($col)]} {
set cw($col) [string length $x($col)]
}
lappend y $x($col)
}
}
if {[info exists x(*)] && $header} {
set hdr {}
set ln {}
set dash ---------------------------------------------------------------
append dash ------------------------------------------------------------
foreach col $x(*) {
if {![info exists cw($col)] || $cw($col)<[string length $col]} {
set cw($col) [string length $col]
}
lappend hdr $col
lappend ln [string range $dash 1 $cw($col)]
}
set y [concat $hdr $ln $y]
}
if {[info exists x(*)]} {
set format {}
set arglist {}
set arglist2 {}
set i 0
foreach col $x(*) {
lappend arglist x$i
append arglist2 " \$x$i"
incr i
append format " %-$cw($col)s"
}
set format [string trimleft $format]\n
if {[llength $arglist]>0} {
foreach $arglist $y "append res \[format [list $format] $arglist2\]"
}
}
} elseif {$mode=="multicolumn"} {
set y [$v(db) eval $cmd]
set max 0
foreach e $y {
if {$max<[string length $e]} {set max [string length $e]}
}
set ncol [expr {int(80/($max+2))}]
if {$ncol<1} {set ncol 1}
set nelem [llength $y]
set nrow [expr {($nelem+$ncol-1)/$ncol}]
set format "%-${max}s"
for {set i 0} {$i<$nrow} {incr i} {
set j $i
while 1 {
append res [format $format [lindex $y $j]]
incr j $nrow
if {$j>=$nelem} break
append res { }
}
append res \n
}
} else {
$v(db) eval $cmd x {
foreach col $x(*) {append res "$col = $x($col)\n"}
append res \n
}
}
return [string trimright $res]
}
# Change the line to the previous line
#
proc sqlitecon::Prior w {
upvar #0 $w v
if {$v(current)<=0} return
incr v(current) -1
set line [lindex $v(history) $v(current)]
sqlitecon::SetLine $w $line
}
# Change the line to the next line
#
proc sqlitecon::Next w {
upvar #0 $w v
if {$v(current)>=$v(historycnt)} return
incr v(current) 1
set line [lindex $v(history) $v(current)]
sqlitecon::SetLine $w $line
}
# Change the contents of the entry line
#
proc sqlitecon::SetLine {w line} {
upvar #0 $w v
scan [$w index insert] %d.%d row col
set start $row.$v(plength)
$w delete $start end
$w insert end $line
$w mark set insert end
$w yview insert
}
# Called when the mouse button is pressed at position $x,$y on
# the console widget.
#
proc sqlitecon::Button1 {w x y} {
global tkPriv
upvar #0 $w v
set v(mouseMoved) 0
set v(pressX) $x
set p [sqlitecon::nearestBoundry $w $x $y]
scan [$w index insert] %d.%d ix iy
scan $p %d.%d px py
if {$px==$ix} {
$w mark set insert $p
}
$w mark set anchor $p
focus $w
}
# Find the boundry between characters that is nearest
# to $x,$y
#
proc sqlitecon::nearestBoundry {w x y} {
set p [$w index @$x,$y]
set bb [$w bbox $p]
if {![string compare $bb ""]} {return $p}
if {($x-[lindex $bb 0])<([lindex $bb 2]/2)} {return $p}
$w index "$p + 1 char"
}
# This routine extends the selection to the point specified by $x,$y
#
proc sqlitecon::SelectTo {w x y} {
upvar #0 $w v
set cur [sqlitecon::nearestBoundry $w $x $y]
if {[catch {$w index anchor}]} {
$w mark set anchor $cur
}
set anchor [$w index anchor]
if {[$w compare $cur != $anchor] || (abs($v(pressX) - $x) >= 3)} {
if {$v(mouseMoved)==0} {
$w tag remove sel 0.0 end
}
set v(mouseMoved) 1
}
if {[$w compare $cur < anchor]} {
set first $cur
set last anchor
} else {
set first anchor
set last $cur
}
if {$v(mouseMoved)} {
$w tag remove sel 0.0 $first
$w tag add sel $first $last
$w tag remove sel $last end
update idletasks
}
}
# Called whenever the mouse moves while button-1 is held down.
#
proc sqlitecon::B1Motion {w x y} {
upvar #0 $w v
set v(y) $y
set v(x) $x
sqlitecon::SelectTo $w $x $y
}
# Called whenever the mouse leaves the boundries of the widget
# while button 1 is held down.
#
proc sqlitecon::B1Leave {w x y} {
upvar #0 $w v
set v(y) $y
set v(x) $x
sqlitecon::motor $w
}
# This routine is called to automatically scroll the window when
# the mouse drags offscreen.
#
proc sqlitecon::motor w {
upvar #0 $w v
if {![winfo exists $w]} return
if {$v(y)>=[winfo height $w]} {
$w yview scroll 1 units
} elseif {$v(y)<0} {
$w yview scroll -1 units
} else {
return
}
sqlitecon::SelectTo $w $v(x) $v(y)
set v(timer) [after 50 sqlitecon::motor $w]
}
# This routine cancels the scrolling motor if it is active
#
proc sqlitecon::cancelMotor w {
upvar #0 $w v
catch {after cancel $v(timer)}
catch {unset v(timer)}
}
# Do a Copy operation on the stuff currently selected.
#
proc sqlitecon::Copy w {
if {![catch {set text [$w get sel.first sel.last]}]} {
clipboard clear -displayof $w
clipboard append -displayof $w $text
}
}
# Return 1 if the selection exists and is contained
# entirely on the input line. Return 2 if the selection
# exists but is not entirely on the input line. Return 0
# if the selection does not exist.
#
proc sqlitecon::canCut w {
set r [catch {
scan [$w index sel.first] %d.%d s1x s1y
scan [$w index sel.last] %d.%d s2x s2y
scan [$w index insert] %d.%d ix iy
}]
if {$r==1} {return 0}
if {$s1x==$ix && $s2x==$ix} {return 1}
return 2
}
# Do a Cut operation if possible. Cuts are only allowed
# if the current selection is entirely contained on the
# current input line.
#
proc sqlitecon::Cut w {
if {[sqlitecon::canCut $w]==1} {
sqlitecon::Copy $w
$w delete sel.first sel.last
}
}
# Do a paste opeation.
#
proc sqlitecon::Paste w {
if {[sqlitecon::canCut $w]==1} {
$w delete sel.first sel.last
}
if {[catch {selection get -displayof $w -selection CLIPBOARD} topaste]
&& [catch {selection get -displayof $w -selection PRIMARY} topaste]} {
return
}
if {[info exists ::$w]} {
set prior 0
foreach line [split $topaste \n] {
if {$prior} {
sqlitecon::Enter $w
update
}
set prior 1
$w insert insert $line
}
} else {
$w insert insert $topaste
}
}
# Enable or disable entries in the Edit menu
#
proc sqlitecon::EnableEditMenu w {
upvar #0 $w.t v
set m $v(editmenu)
if {$m=="" || ![winfo exists $m]} return
switch [sqlitecon::canCut $w.t] {
0 {
$m entryconf Copy -state disabled
$m entryconf Cut -state disabled
}
1 {
$m entryconf Copy -state normal
$m entryconf Cut -state normal
}
2 {
$m entryconf Copy -state normal
$m entryconf Cut -state disabled
}
}
}
# Prompt the user for the name of a writable file. Then write the
# entire contents of the console screen to that file.
#
proc sqlitecon::SaveFile w {
set types {
{{Text Files} {.txt}}
{{All Files} *}
}
set f [tk_getSaveFile -filetypes $types -title "Write Screen To..."]
if {$f!=""} {
if {[catch {open $f w} fd]} {
tk_messageBox -type ok -icon error -message $fd
} else {
puts $fd [string trimright [$w get 1.0 end] \n]
close $fd
}
}
}
# Erase everything from the console above the insertion line.
#
proc sqlitecon::Clear w {
$w delete 1.0 {insert linestart}
}
# An in-line editor for SQL
#
proc sqlitecon::_edit {origtxt {title {}}} {
for {set i 0} {[winfo exists .ed$i]} {incr i} continue
set w .ed$i
toplevel $w
wm protocol $w WM_DELETE_WINDOW "$w.b.can invoke"
wm title $w {Inline SQL Editor}
frame $w.b
pack $w.b -side bottom -fill x
button $w.b.can -text Cancel -width 6 -command [list set ::$w 0]
button $w.b.ok -text OK -width 6 -command [list set ::$w 1]
button $w.b.cut -text Cut -width 6 -command [list ::sqlitecon::Cut $w.t]
button $w.b.copy -text Copy -width 6 -command [list ::sqlitecon::Copy $w.t]
button $w.b.paste -text Paste -width 6 -command [list ::sqlitecon::Paste $w.t]
set ::$w {}
pack $w.b.cut $w.b.copy $w.b.paste $w.b.can $w.b.ok\
-side left -padx 5 -pady 5 -expand 1
if {$title!=""} {
label $w.title -text $title
pack $w.title -side top -padx 5 -pady 5
}
text $w.t -bg white -fg black -yscrollcommand [list $w.sb set]
pack $w.t -side left -fill both -expand 1
scrollbar $w.sb -orient vertical -command [list $w.t yview]
pack $w.sb -side left -fill y
$w.t insert end $origtxt
vwait ::$w
if {[set ::$w]} {
set txt [string trimright [$w.t get 1.0 end]]
} else {
set txt $origtxt
}
destroy $w
return $txt
}
+892
View File
@@ -0,0 +1,892 @@
<html>
<head>
<title>The Lemon Parser Generator</title>
</head>
<body bgcolor=white>
<h1 align=center>The Lemon Parser Generator</h1>
<p>Lemon is an LALR(1) parser generator for C or C++.
It does the same job as ``bison'' and ``yacc''.
But lemon is not another bison or yacc clone. It
uses a different grammar syntax which is designed to
reduce the number of coding errors. Lemon also uses a more
sophisticated parsing engine that is faster than yacc and
bison and which is both reentrant and thread-safe.
Furthermore, Lemon implements features that can be used
to eliminate resource leaks, making is suitable for use
in long-running programs such as graphical user interfaces
or embedded controllers.</p>
<p>This document is an introduction to the Lemon
parser generator.</p>
<h2>Theory of Operation</h2>
<p>The main goal of Lemon is to translate a context free grammar (CFG)
for a particular language into C code that implements a parser for
that language.
The program has two inputs:
<ul>
<li>The grammar specification.
<li>A parser template file.
</ul>
Typically, only the grammar specification is supplied by the programmer.
Lemon comes with a default parser template which works fine for most
applications. But the user is free to substitute a different parser
template if desired.</p>
<p>Depending on command-line options, Lemon will generate between
one and three files of outputs.
<ul>
<li>C code to implement the parser.
<li>A header file defining an integer ID for each terminal symbol.
<li>An information file that describes the states of the generated parser
automaton.
</ul>
By default, all three of these output files are generated.
The header file is suppressed if the ``-m'' command-line option is
used and the report file is omitted when ``-q'' is selected.</p>
<p>The grammar specification file uses a ``.y'' suffix, by convention.
In the examples used in this document, we'll assume the name of the
grammar file is ``gram.y''. A typical use of Lemon would be the
following command:
<pre>
lemon gram.y
</pre>
This command will generate three output files named ``gram.c'',
``gram.h'' and ``gram.out''.
The first is C code to implement the parser. The second
is the header file that defines numerical values for all
terminal symbols, and the last is the report that explains
the states used by the parser automaton.</p>
<h3>Command Line Options</h3>
<p>The behavior of Lemon can be modified using command-line options.
You can obtain a list of the available command-line options together
with a brief explanation of what each does by typing
<pre>
lemon -?
</pre>
As of this writing, the following command-line options are supported:
<ul>
<li><tt>-b</tt>
<li><tt>-c</tt>
<li><tt>-g</tt>
<li><tt>-m</tt>
<li><tt>-q</tt>
<li><tt>-s</tt>
<li><tt>-x</tt>
</ul>
The ``-b'' option reduces the amount of text in the report file by
printing only the basis of each parser state, rather than the full
configuration.
The ``-c'' option suppresses action table compression. Using -c
will make the parser a little larger and slower but it will detect
syntax errors sooner.
The ``-g'' option causes no output files to be generated at all.
Instead, the input grammar file is printed on standard output but
with all comments, actions and other extraneous text deleted. This
is a useful way to get a quick summary of a grammar.
The ``-m'' option causes the output C source file to be compatible
with the ``makeheaders'' program.
Makeheaders is a program that automatically generates header files
from C source code. When the ``-m'' option is used, the header
file is not output since the makeheaders program will take care
of generated all header files automatically.
The ``-q'' option suppresses the report file.
Using ``-s'' causes a brief summary of parser statistics to be
printed. Like this:
<pre>
Parser statistics: 74 terminals, 70 nonterminals, 179 rules
340 states, 2026 parser table entries, 0 conflicts
</pre>
Finally, the ``-x'' option causes Lemon to print its version number
and then stops without attempting to read the grammar or generate a parser.</p>
<h3>The Parser Interface</h3>
<p>Lemon doesn't generate a complete, working program. It only generates
a few subroutines that implement a parser. This section describes
the interface to those subroutines. It is up to the programmer to
call these subroutines in an appropriate way in order to produce a
complete system.</p>
<p>Before a program begins using a Lemon-generated parser, the program
must first create the parser.
A new parser is created as follows:
<pre>
void *pParser = ParseAlloc( malloc );
</pre>
The ParseAlloc() routine allocates and initializes a new parser and
returns a pointer to it.
The actual data structure used to represent a parser is opaque --
its internal structure is not visible or usable by the calling routine.
For this reason, the ParseAlloc() routine returns a pointer to void
rather than a pointer to some particular structure.
The sole argument to the ParseAlloc() routine is a pointer to the
subroutine used to allocate memory. Typically this means ``malloc()''.</p>
<p>After a program is finished using a parser, it can reclaim all
memory allocated by that parser by calling
<pre>
ParseFree(pParser, free);
</pre>
The first argument is the same pointer returned by ParseAlloc(). The
second argument is a pointer to the function used to release bulk
memory back to the system.</p>
<p>After a parser has been allocated using ParseAlloc(), the programmer
must supply the parser with a sequence of tokens (terminal symbols) to
be parsed. This is accomplished by calling the following function
once for each token:
<pre>
Parse(pParser, hTokenID, sTokenData, pArg);
</pre>
The first argument to the Parse() routine is the pointer returned by
ParseAlloc().
The second argument is a small positive integer that tells the parse the
type of the next token in the data stream.
There is one token type for each terminal symbol in the grammar.
The gram.h file generated by Lemon contains #define statements that
map symbolic terminal symbol names into appropriate integer values.
(A value of 0 for the second argument is a special flag to the
parser to indicate that the end of input has been reached.)
The third argument is the value of the given token. By default,
the type of the third argument is integer, but the grammar will
usually redefine this type to be some kind of structure.
Typically the second argument will be a broad category of tokens
such as ``identifier'' or ``number'' and the third argument will
be the name of the identifier or the value of the number.</p>
<p>The Parse() function may have either three or four arguments,
depending on the grammar. If the grammar specification file request
it, the Parse() function will have a fourth parameter that can be
of any type chosen by the programmer. The parser doesn't do anything
with this argument except to pass it through to action routines.
This is a convenient mechanism for passing state information down
to the action routines without having to use global variables.</p>
<p>A typical use of a Lemon parser might look something like the
following:
<pre>
01 ParseTree *ParseFile(const char *zFilename){
02 Tokenizer *pTokenizer;
03 void *pParser;
04 Token sToken;
05 int hTokenId;
06 ParserState sState;
07
08 pTokenizer = TokenizerCreate(zFilename);
09 pParser = ParseAlloc( malloc );
10 InitParserState(&sState);
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
12 Parse(pParser, hTokenId, sToken, &sState);
13 }
14 Parse(pParser, 0, sToken, &sState);
15 ParseFree(pParser, free );
16 TokenizerFree(pTokenizer);
17 return sState.treeRoot;
18 }
</pre>
This example shows a user-written routine that parses a file of
text and returns a pointer to the parse tree.
(We've omitted all error-handling from this example to keep it
simple.)
We assume the existence of some kind of tokenizer which is created
using TokenizerCreate() on line 8 and deleted by TokenizerFree()
on line 16. The GetNextToken() function on line 11 retrieves the
next token from the input file and puts its type in the
integer variable hTokenId. The sToken variable is assumed to be
some kind of structure that contains details about each token,
such as its complete text, what line it occurs on, etc. </p>
<p>This example also assumes the existence of structure of type
ParserState that holds state information about a particular parse.
An instance of such a structure is created on line 6 and initialized
on line 10. A pointer to this structure is passed into the Parse()
routine as the optional 4th argument.
The action routine specified by the grammar for the parser can use
the ParserState structure to hold whatever information is useful and
appropriate. In the example, we note that the treeRoot field of
the ParserState structure is left pointing to the root of the parse
tree.</p>
<p>The core of this example as it relates to Lemon is as follows:
<pre>
ParseFile(){
pParser = ParseAlloc( malloc );
while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
Parse(pParser, hTokenId, sToken);
}
Parse(pParser, 0, sToken);
ParseFree(pParser, free );
}
</pre>
Basically, what a program has to do to use a Lemon-generated parser
is first create the parser, then send it lots of tokens obtained by
tokenizing an input source. When the end of input is reached, the
Parse() routine should be called one last time with a token type
of 0. This step is necessary to inform the parser that the end of
input has been reached. Finally, we reclaim memory used by the
parser by calling ParseFree().</p>
<p>There is one other interface routine that should be mentioned
before we move on.
The ParseTrace() function can be used to generate debugging output
from the parser. A prototype for this routine is as follows:
<pre>
ParseTrace(FILE *stream, char *zPrefix);
</pre>
After this routine is called, a short (one-line) message is written
to the designated output stream every time the parser changes states
or calls an action routine. Each such message is prefaced using
the text given by zPrefix. This debugging output can be turned off
by calling ParseTrace() again with a first argument of NULL (0).</p>
<h3>Differences With YACC and BISON</h3>
<p>Programmers who have previously used the yacc or bison parser
generator will notice several important differences between yacc and/or
bison and Lemon.
<ul>
<li>In yacc and bison, the parser calls the tokenizer. In Lemon,
the tokenizer calls the parser.
<li>Lemon uses no global variables. Yacc and bison use global variables
to pass information between the tokenizer and parser.
<li>Lemon allows multiple parsers to be running simultaneously. Yacc
and bison do not.
</ul>
These differences may cause some initial confusion for programmers
with prior yacc and bison experience.
But after years of experience using Lemon, I firmly
believe that the Lemon way of doing things is better.</p>
<h2>Input File Syntax</h2>
<p>The main purpose of the grammar specification file for Lemon is
to define the grammar for the parser. But the input file also
specifies additional information Lemon requires to do its job.
Most of the work in using Lemon is in writing an appropriate
grammar file.</p>
<p>The grammar file for lemon is, for the most part, free format.
It does not have sections or divisions like yacc or bison. Any
declaration can occur at any point in the file.
Lemon ignores whitespace (except where it is needed to separate
tokens) and it honors the same commenting conventions as C and C++.</p>
<h3>Terminals and Nonterminals</h3>
<p>A terminal symbol (token) is any string of alphanumeric
and underscore characters
that begins with an upper case letter.
A terminal can contain lower class letters after the first character,
but the usual convention is to make terminals all upper case.
A nonterminal, on the other hand, is any string of alphanumeric
and underscore characters than begins with a lower case letter.
Again, the usual convention is to make nonterminals use all lower
case letters.</p>
<p>In Lemon, terminal and nonterminal symbols do not need to
be declared or identified in a separate section of the grammar file.
Lemon is able to generate a list of all terminals and nonterminals
by examining the grammar rules, and it can always distinguish a
terminal from a nonterminal by checking the case of the first
character of the name.</p>
<p>Yacc and bison allow terminal symbols to have either alphanumeric
names or to be individual characters included in single quotes, like
this: ')' or '$'. Lemon does not allow this alternative form for
terminal symbols. With Lemon, all symbols, terminals and nonterminals,
must have alphanumeric names.</p>
<h3>Grammar Rules</h3>
<p>The main component of a Lemon grammar file is a sequence of grammar
rules.
Each grammar rule consists of a nonterminal symbol followed by
the special symbol ``::='' and then a list of terminals and/or nonterminals.
The rule is terminated by a period.
The list of terminals and nonterminals on the right-hand side of the
rule can be empty.
Rules can occur in any order, except that the left-hand side of the
first rule is assumed to be the start symbol for the grammar (unless
specified otherwise using the <tt>%start</tt> directive described below.)
A typical sequence of grammar rules might look something like this:
<pre>
expr ::= expr PLUS expr.
expr ::= expr TIMES expr.
expr ::= LPAREN expr RPAREN.
expr ::= VALUE.
</pre>
</p>
<p>There is one non-terminal in this example, ``expr'', and five
terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
``RPAREN'' and ``VALUE''.</p>
<p>Like yacc and bison, Lemon allows the grammar to specify a block
of C code that will be executed whenever a grammar rule is reduced
by the parser.
In Lemon, this action is specified by putting the C code (contained
within curly braces <tt>{...}</tt>) immediately after the
period that closes the rule.
For example:
<pre>
expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
</pre>
</p>
<p>In order to be useful, grammar actions must normally be linked to
their associated grammar rules.
In yacc and bison, this is accomplished by embedding a ``$$'' in the
action to stand for the value of the left-hand side of the rule and
symbols ``$1'', ``$2'', and so forth to stand for the value of
the terminal or nonterminal at position 1, 2 and so forth on the
right-hand side of the rule.
This idea is very powerful, but it is also very error-prone. The
single most common source of errors in a yacc or bison grammar is
to miscount the number of symbols on the right-hand side of a grammar
rule and say ``$7'' when you really mean ``$8''.</p>
<p>Lemon avoids the need to count grammar symbols by assigning symbolic
names to each symbol in a grammar rule and then using those symbolic
names in the action.
In yacc or bison, one would write this:
<pre>
expr -> expr PLUS expr { $$ = $1 + $3; };
</pre>
But in Lemon, the same rule becomes the following:
<pre>
expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
</pre>
In the Lemon rule, any symbol in parentheses after a grammar rule
symbol becomes a place holder for that symbol in the grammar rule.
This place holder can then be used in the associated C action to
stand for the value of that symbol.<p>
<p>The Lemon notation for linking a grammar rule with its reduce
action is superior to yacc/bison on several counts.
First, as mentioned above, the Lemon method avoids the need to
count grammar symbols.
Secondly, if a terminal or nonterminal in a Lemon grammar rule
includes a linking symbol in parentheses but that linking symbol
is not actually used in the reduce action, then an error message
is generated.
For example, the rule
<pre>
expr(A) ::= expr(B) PLUS expr(C). { A = B; }
</pre>
will generate an error because the linking symbol ``C'' is used
in the grammar rule but not in the reduce action.</p>
<p>The Lemon notation for linking grammar rules to reduce actions
also facilitates the use of destructors for reclaiming memory
allocated by the values of terminals and nonterminals on the
right-hand side of a rule.</p>
<h3>Precedence Rules</h3>
<p>Lemon resolves parsing ambiguities in exactly the same way as
yacc and bison. A shift-reduce conflict is resolved in favor
of the shift, and a reduce-reduce conflict is resolved by reducing
whichever rule comes first in the grammar file.</p>
<p>Just like in
yacc and bison, Lemon allows a measure of control
over the resolution of paring conflicts using precedence rules.
A precedence value can be assigned to any terminal symbol
using the %left, %right or %nonassoc directives. Terminal symbols
mentioned in earlier directives have a lower precedence that
terminal symbols mentioned in later directives. For example:</p>
<p><pre>
%left AND.
%left OR.
%nonassoc EQ NE GT GE LT LE.
%left PLUS MINUS.
%left TIMES DIVIDE MOD.
%right EXP NOT.
</pre></p>
<p>In the preceding sequence of directives, the AND operator is
defined to have the lowest precedence. The OR operator is one
precedence level higher. And so forth. Hence, the grammar would
attempt to group the ambiguous expression
<pre>
a AND b OR c
</pre>
like this
<pre>
a AND (b OR c).
</pre>
The associativity (left, right or nonassoc) is used to determine
the grouping when the precedence is the same. AND is left-associative
in our example, so
<pre>
a AND b AND c
</pre>
is parsed like this
<pre>
(a AND b) AND c.
</pre>
The EXP operator is right-associative, though, so
<pre>
a EXP b EXP c
</pre>
is parsed like this
<pre>
a EXP (b EXP c).
</pre>
The nonassoc precedence is used for non-associative operators.
So
<pre>
a EQ b EQ c
</pre>
is an error.</p>
<p>The precedence of non-terminals is transferred to rules as follows:
The precedence of a grammar rule is equal to the precedence of the
left-most terminal symbol in the rule for which a precedence is
defined. This is normally what you want, but in those cases where
you want to precedence of a grammar rule to be something different,
you can specify an alternative precedence symbol by putting the
symbol in square braces after the period at the end of the rule and
before any C-code. For example:</p>
<p><pre>
expr = MINUS expr. [NOT]
</pre></p>
<p>This rule has a precedence equal to that of the NOT symbol, not the
MINUS symbol as would have been the case by default.</p>
<p>With the knowledge of how precedence is assigned to terminal
symbols and individual
grammar rules, we can now explain precisely how parsing conflicts
are resolved in Lemon. Shift-reduce conflicts are resolved
as follows:
<ul>
<li> If either the token to be shifted or the rule to be reduced
lacks precedence information, then resolve in favor of the
shift, but report a parsing conflict.
<li> If the precedence of the token to be shifted is greater than
the precedence of the rule to reduce, then resolve in favor
of the shift. No parsing conflict is reported.
<li> If the precedence of the token it be shifted is less than the
precedence of the rule to reduce, then resolve in favor of the
reduce action. No parsing conflict is reported.
<li> If the precedences are the same and the shift token is
right-associative, then resolve in favor of the shift.
No parsing conflict is reported.
<li> If the precedences are the same the the shift token is
left-associative, then resolve in favor of the reduce.
No parsing conflict is reported.
<li> Otherwise, resolve the conflict by doing the shift and
report the parsing conflict.
</ul>
Reduce-reduce conflicts are resolved this way:
<ul>
<li> If either reduce rule
lacks precedence information, then resolve in favor of the
rule that appears first in the grammar and report a parsing
conflict.
<li> If both rules have precedence and the precedence is different
then resolve the dispute in favor of the rule with the highest
precedence and do not report a conflict.
<li> Otherwise, resolve the conflict by reducing by the rule that
appears first in the grammar and report a parsing conflict.
</ul>
<h3>Special Directives</h3>
<p>The input grammar to Lemon consists of grammar rules and special
directives. We've described all the grammar rules, so now we'll
talk about the special directives.</p>
<p>Directives in lemon can occur in any order. You can put them before
the grammar rules, or after the grammar rules, or in the mist of the
grammar rules. It doesn't matter. The relative order of
directives used to assign precedence to terminals is important, but
other than that, the order of directives in Lemon is arbitrary.</p>
<p>Lemon supports the following special directives:
<ul>
<li><tt>%code</tt>
<li><tt>%default_destructor</tt>
<li><tt>%default_type</tt>
<li><tt>%destructor</tt>
<li><tt>%extra_argument</tt>
<li><tt>%include</tt>
<li><tt>%left</tt>
<li><tt>%name</tt>
<li><tt>%nonassoc</tt>
<li><tt>%parse_accept</tt>
<li><tt>%parse_failure </tt>
<li><tt>%right</tt>
<li><tt>%stack_overflow</tt>
<li><tt>%stack_size</tt>
<li><tt>%start_symbol</tt>
<li><tt>%syntax_error</tt>
<li><tt>%token_destructor</tt>
<li><tt>%token_prefix</tt>
<li><tt>%token_type</tt>
<li><tt>%type</tt>
</ul>
Each of these directives will be described separately in the
following sections:</p>
<h4>The <tt>%code</tt> directive</h4>
<p>The %code directive is used to specify addition C/C++ code that
is added to the end of the main output file. This is similar to
the %include directive except that %include is inserted at the
beginning of the main output file.</p>
<p>%code is typically used to include some action routines or perhaps
a tokenizer as part of the output file.</p>
<h4>The <tt>%default_destructor</tt> directive</h4>
<p>The %default_destructor directive specifies a destructor to
use for non-terminals that do not have their own destructor
specified by a separate %destructor directive. See the documentation
on the %destructor directive below for additional information.</p>
<p>In some grammers, many different non-terminal symbols have the
same datatype and hence the same destructor. This directive is
a convenience way to specify the same destructor for all those
non-terminals using a single statement.</p>
<h4>The <tt>%default_type</tt> directive</h4>
<p>The %default_type directive specifies the datatype of non-terminal
symbols that do no have their own datatype defined using a separate
%type directive. See the documentation on %type below for addition
information.</p>
<h4>The <tt>%destructor</tt> directive</h4>
<p>The %destructor directive is used to specify a destructor for
a non-terminal symbol.
(See also the %token_destructor directive which is used to
specify a destructor for terminal symbols.)</p>
<p>A non-terminal's destructor is called to dispose of the
non-terminal's value whenever the non-terminal is popped from
the stack. This includes all of the following circumstances:
<ul>
<li> When a rule reduces and the value of a non-terminal on
the right-hand side is not linked to C code.
<li> When the stack is popped during error processing.
<li> When the ParseFree() function runs.
</ul>
The destructor can do whatever it wants with the value of
the non-terminal, but its design is to deallocate memory
or other resources held by that non-terminal.</p>
<p>Consider an example:
<pre>
%type nt {void*}
%destructor nt { free($$); }
nt(A) ::= ID NUM. { A = malloc( 100 ); }
</pre>
This example is a bit contrived but it serves to illustrate how
destructors work. The example shows a non-terminal named
``nt'' that holds values of type ``void*''. When the rule for
an ``nt'' reduces, it sets the value of the non-terminal to
space obtained from malloc(). Later, when the nt non-terminal
is popped from the stack, the destructor will fire and call
free() on this malloced space, thus avoiding a memory leak.
(Note that the symbol ``$$'' in the destructor code is replaced
by the value of the non-terminal.)</p>
<p>It is important to note that the value of a non-terminal is passed
to the destructor whenever the non-terminal is removed from the
stack, unless the non-terminal is used in a C-code action. If
the non-terminal is used by C-code, then it is assumed that the
C-code will take care of destroying it if it should really
be destroyed. More commonly, the value is used to build some
larger structure and we don't want to destroy it, which is why
the destructor is not called in this circumstance.</p>
<p>By appropriate use of destructors, it is possible to
build a parser using Lemon that can be used within a long-running
program, such as a GUI, that will not leak memory or other resources.
To do the same using yacc or bison is much more difficult.</p>
<h4>The <tt>%extra_argument</tt> directive</h4>
The %extra_argument directive instructs Lemon to add a 4th parameter
to the parameter list of the Parse() function it generates. Lemon
doesn't do anything itself with this extra argument, but it does
make the argument available to C-code action routines, destructors,
and so forth. For example, if the grammar file contains:</p>
<p><pre>
%extra_argument { MyStruct *pAbc }
</pre></p>
<p>Then the Parse() function generated will have an 4th parameter
of type ``MyStruct*'' and all action routines will have access to
a variable named ``pAbc'' that is the value of the 4th parameter
in the most recent call to Parse().</p>
<h4>The <tt>%include</tt> directive</h4>
<p>The %include directive specifies C code that is included at the
top of the generated parser. You can include any text you want --
the Lemon parser generator copies it blindly. If you have multiple
%include directives in your grammar file the value of the last
%include directive overwrites all the others.</p.
<p>The %include directive is very handy for getting some extra #include
preprocessor statements at the beginning of the generated parser.
For example:</p>
<p><pre>
%include {#include &lt;unistd.h&gt;}
</pre></p>
<p>This might be needed, for example, if some of the C actions in the
grammar call functions that are prototyed in unistd.h.</p>
<h4>The <tt>%left</tt> directive</h4>
The %left directive is used (along with the %right and
%nonassoc directives) to declare precedences of terminal
symbols. Every terminal symbol whose name appears after
a %left directive but before the next period (``.'') is
given the same left-associative precedence value. Subsequent
%left directives have higher precedence. For example:</p>
<p><pre>
%left AND.
%left OR.
%nonassoc EQ NE GT GE LT LE.
%left PLUS MINUS.
%left TIMES DIVIDE MOD.
%right EXP NOT.
</pre></p>
<p>Note the period that terminates each %left, %right or %nonassoc
directive.</p>
<p>LALR(1) grammars can get into a situation where they require
a large amount of stack space if you make heavy use or right-associative
operators. For this reason, it is recommended that you use %left
rather than %right whenever possible.</p>
<h4>The <tt>%name</tt> directive</h4>
<p>By default, the functions generated by Lemon all begin with the
five-character string ``Parse''. You can change this string to something
different using the %name directive. For instance:</p>
<p><pre>
%name Abcde
</pre></p>
<p>Putting this directive in the grammar file will cause Lemon to generate
functions named
<ul>
<li> AbcdeAlloc(),
<li> AbcdeFree(),
<li> AbcdeTrace(), and
<li> Abcde().
</ul>
The %name directive allows you to generator two or more different
parsers and link them all into the same executable.
</p>
<h4>The <tt>%nonassoc</tt> directive</h4>
<p>This directive is used to assign non-associative precedence to
one or more terminal symbols. See the section on precedence rules
or on the %left directive for additional information.</p>
<h4>The <tt>%parse_accept</tt> directive</h4>
<p>The %parse_accept directive specifies a block of C code that is
executed whenever the parser accepts its input string. To ``accept''
an input string means that the parser was able to process all tokens
without error.</p>
<p>For example:</p>
<p><pre>
%parse_accept {
printf("parsing complete!\n");
}
</pre></p>
<h4>The <tt>%parse_failure</tt> directive</h4>
<p>The %parse_failure directive specifies a block of C code that
is executed whenever the parser fails complete. This code is not
executed until the parser has tried and failed to resolve an input
error using is usual error recovery strategy. The routine is
only invoked when parsing is unable to continue.</p>
<p><pre>
%parse_failure {
fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
}
</pre></p>
<h4>The <tt>%right</tt> directive</h4>
<p>This directive is used to assign right-associative precedence to
one or more terminal symbols. See the section on precedence rules
or on the %left directive for additional information.</p>
<h4>The <tt>%stack_overflow</tt> directive</h4>
<p>The %stack_overflow directive specifies a block of C code that
is executed if the parser's internal stack ever overflows. Typically
this just prints an error message. After a stack overflow, the parser
will be unable to continue and must be reset.</p>
<p><pre>
%stack_overflow {
fprintf(stderr,"Giving up. Parser stack overflow\n");
}
</pre></p>
<p>You can help prevent parser stack overflows by avoiding the use
of right recursion and right-precedence operators in your grammar.
Use left recursion and and left-precedence operators instead, to
encourage rules to reduce sooner and keep the stack size down.
For example, do rules like this:
<pre>
list ::= list element. // left-recursion. Good!
list ::= .
</pre>
Not like this:
<pre>
list ::= element list. // right-recursion. Bad!
list ::= .
</pre>
<h4>The <tt>%stack_size</tt> directive</h4>
<p>If stack overflow is a problem and you can't resolve the trouble
by using left-recursion, then you might want to increase the size
of the parser's stack using this directive. Put an positive integer
after the %stack_size directive and Lemon will generate a parse
with a stack of the requested size. The default value is 100.</p>
<p><pre>
%stack_size 2000
</pre></p>
<h4>The <tt>%start_symbol</tt> directive</h4>
<p>By default, the start-symbol for the grammar that Lemon generates
is the first non-terminal that appears in the grammar file. But you
can choose a different start-symbol using the %start_symbol directive.</p>
<p><pre>
%start_symbol prog
</pre></p>
<h4>The <tt>%token_destructor</tt> directive</h4>
<p>The %destructor directive assigns a destructor to a non-terminal
symbol. (See the description of the %destructor directive above.)
This directive does the same thing for all terminal symbols.</p>
<p>Unlike non-terminal symbols which may each have a different data type
for their values, terminals all use the same data type (defined by
the %token_type directive) and so they use a common destructor. Other
than that, the token destructor works just like the non-terminal
destructors.</p>
<h4>The <tt>%token_prefix</tt> directive</h4>
<p>Lemon generates #defines that assign small integer constants
to each terminal symbol in the grammar. If desired, Lemon will
add a prefix specified by this directive
to each of the #defines it generates.
So if the default output of Lemon looked like this:
<pre>
#define AND 1
#define MINUS 2
#define OR 3
#define PLUS 4
</pre>
You can insert a statement into the grammar like this:
<pre>
%token_prefix TOKEN_
</pre>
to cause Lemon to produce these symbols instead:
<pre>
#define TOKEN_AND 1
#define TOKEN_MINUS 2
#define TOKEN_OR 3
#define TOKEN_PLUS 4
</pre>
<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
<p>These directives are used to specify the data types for values
on the parser's stack associated with terminal and non-terminal
symbols. The values of all terminal symbols must be of the same
type. This turns out to be the same data type as the 3rd parameter
to the Parse() function generated by Lemon. Typically, you will
make the value of a terminal symbol by a pointer to some kind of
token structure. Like this:</p>
<p><pre>
%token_type {Token*}
</pre></p>
<p>If the data type of terminals is not specified, the default value
is ``int''.</p>
<p>Non-terminal symbols can each have their own data types. Typically
the data type of a non-terminal is a pointer to the root of a parse-tree
structure that contains all information about that non-terminal.
For example:</p>
<p><pre>
%type expr {Expr*}
</pre></p>
<p>Each entry on the parser's stack is actually a union containing
instances of all data types for every non-terminal and terminal symbol.
Lemon will automatically use the correct element of this union depending
on what the corresponding non-terminal or terminal symbol is. But
the grammar designer should keep in mind that the size of the union
will be the size of its largest element. So if you have a single
non-terminal whose data type requires 1K of storage, then your 100
entry parser stack will require 100K of heap space. If you are willing
and able to pay that price, fine. You just need to know.</p>
<h3>Error Processing</h3>
<p>After extensive experimentation over several years, it has been
discovered that the error recovery strategy used by yacc is about
as good as it gets. And so that is what Lemon uses.</p>
<p>When a Lemon-generated parser encounters a syntax error, it
first invokes the code specified by the %syntax_error directive, if
any. It then enters its error recovery strategy. The error recovery
strategy is to begin popping the parsers stack until it enters a
state where it is permitted to shift a special non-terminal symbol
named ``error''. It then shifts this non-terminal and continues
parsing. But the %syntax_error routine will not be called again
until at least three new tokens have been successfully shifted.</p>
<p>If the parser pops its stack until the stack is empty, and it still
is unable to shift the error symbol, then the %parse_failed routine
is invoked and the parser resets itself to its start state, ready
to begin parsing a new file. This is what will happen at the very
first syntax error, of course, if there are no instances of the
``error'' non-terminal in your grammar.</p>
</body>
</html>
+76
View File
@@ -0,0 +1,76 @@
*** Throughout this document, a page is deemed to have been synced
automatically as soon as it is written when PRAGMA synchronous=OFF.
Otherwise, the page is not synced until the xSync method of the VFS
is called successfully on the file containing the page.
*** Definition: A page of the database file is said to be "overwriteable" if
one or more of the following are true about the page:
(a) The original content of the page as it was at the beginning of
the transaction has been written into the rollback journal and
synced.
(b) The page was a freelist leaf page at the start of the transaction.
(c) The page number is greater than the largest page that existed in
the database file at the start of the transaction.
(1) A page of the database file is never overwritten unless one of the
following are true:
(a) The page and all other pages on the same sector are overwriteable.
(b) The atomic page write optimization is enabled, and the entire
transaction other than the update of the transaction sequence
number consists of a single page change.
(2) The content of a page written into the rollback journal exactly matches
both the content in the database when the rollback journal was written
and the content in the database at the beginning of the current
transaction.
(3) Writes to the database file are an integer multiple of the page size
in length and are aligned to a page boundary.
(4) Reads from the database file are either aligned on a page boundary and
an integer multiple of the page size in length or are taken from the
first 100 bytes of the database file.
(5) All writes to the database file are synced prior to the rollback journal
being deleted, truncated, or zeroed.
(6) If a master journal file is used, then all writes to the database file
are synced prior to the master journal being deleted.
*** Definition: Two databases (or the same database at two points it time)
are said to be "logically equivalent" if they give the same answer to
all queries. Note in particular the the content of freelist leaf
pages can be changed arbitarily without effecting the logical equivalence
of the database.
(7) At any time, if any subset, including the empty set and the total set,
of the unsynced changes to a rollback journal are removed and the
journal is rolled back, the resulting database file will be logical
equivalent to the database file at the beginning of the transaction.
(8) When a transaction is rolled back, the xTruncate method of the VFS
is called to restore the database file to the same size it was at
the beginning of the transaction. (In some VFSes, the xTruncate
method is a no-op, but that does not change the fact the SQLite will
invoke it.)
(9) Whenever the database file is modified, at least one bit in the range
of bytes from 24 through 39 inclusive will be changed prior to releasing
the EXCLUSIVE lock.
(10) The pattern of bits in bytes 24 through 39 shall not repeat in less
than one billion transactions.
(11) A database file is well-formed at the beginning and at the conclusion
of every transaction.
(12) An EXCLUSIVE lock must be held on the database file before making
any changes to the database file.
(13) A SHARED lock must be held on the database file before reading any
content out of the database file.
+130
View File
@@ -0,0 +1,130 @@
The 5 states of an historical rollback lock as implemented by the
xLock, xUnlock, and xCheckReservedLock methods of the sqlite3_io_methods
objec are:
UNLOCKED
SHARED
RESERVED
PENDING
EXCLUSIVE
The wal-index file has a similar locking hierarchy implemented using
the xShmLock method of the sqlite3_vfs object, but with 7
states. Each connection to a wal-index file must be in one of
the following 7 states:
UNLOCKED
READ
READ_FULL
WRITE
PENDING
CHECKPOINT
RECOVER
These roughly correspond to the 5 states of a rollback lock except
that SHARED is split out into 2 states: READ and READ_FULL and
there is an extra RECOVER state used for wal-index reconstruction.
The meanings of the various wal-index locking states is as follows:
UNLOCKED - The wal-index is not in use.
READ - Some prefix of the wal-index is being read. Additional
wal-index information can be appended at any time. The
newly appended content will be ignored by the holder of
the READ lock.
READ_FULL - The entire wal-index is being read. No new information
can be added to the wal-index. The holder of a READ_FULL
lock promises never to read pages from the database file
that are available anywhere in the wal-index.
WRITE - It is OK to append to the wal-index file and to adjust
the header to indicate the new "last valid frame".
PENDING - Waiting on all READ locks to clear so that a
CHECKPOINT lock can be acquired.
CHECKPOINT - It is OK to write any WAL data into the database file
and zero the last valid frame field of the wal-index
header. The wal-index file itself may not be changed
other than to zero the last valid frame field in the
header.
RECOVER - Held during wal-index recovery. Used to prevent a
race if multiple clients try to recover a wal-index at
the same time.
A particular lock manager implementation may coalesce one or more of
the wal-index locking states, though with a reduction in concurrency.
For example, an implemention might implement only exclusive locking,
in which case all states would be equivalent to CHECKPOINT, meaning that
only one reader or one writer or one checkpointer could be active at a
time. Or, an implementation might combine READ and READ_FULL into
a single state equivalent to READ, meaning that a writer could
coexist with a reader, but no reader or writers could coexist with a
checkpointer.
The lock manager must obey the following rules:
(1) A READ cannot coexist with CHECKPOINT.
(2) A READ_FULL cannot coexist with WRITE.
(3) None of WRITE, PENDING, CHECKPOINT, or RECOVER can coexist.
The SQLite core will obey the next set of rules. These rules are
assertions on the behavior of the SQLite core which might be verified
during testing using an instrumented lock manager.
(5) No part of the wal-index will be read without holding either some
kind of SHM lock or an EXCLUSIVE lock on the original database.
The original database is the file named in the 2nd parameter to
the xShmOpen method.
(6) A holder of a READ_FULL will never read any page of the database
file that is contained anywhere in the wal-index.
(7) No part of the wal-index other than the header will be written nor
will the size of the wal-index grow without holding a WRITE or
an EXCLUSIVE on the original database file.
(8) The wal-index header will not be written without holding one of
WRITE, CHECKPOINT, or RECOVER on the wal-index or an EXCLUSIVE on
the original database files.
(9) A CHECKPOINT or RECOVER must be held on the wal-index, or an
EXCLUSIVE on the original database file, in order to reset the
last valid frame counter in the header of the wal-index back to zero.
(10) A WRITE can only increase the last valid frame pointer in the header.
The SQLite core will only ever send requests for UNLOCK, READ, WRITE,
CHECKPOINT, or RECOVER to the lock manager. The SQLite core will never
request a READ_FULL or PENDING lock though the lock manager may deliver
those locking states in response to READ and CHECKPOINT requests,
respectively, if and only if the requested READ or CHECKPOINT cannot
be delivered.
The following are the allowed lock transitions:
Original-State Request New-State
-------------- ---------- ----------
(11a) UNLOCK READ READ
(11b) UNLOCK READ READ_FULL
(11c) UNLOCK CHECKPOINT PENDING
(11d) UNLOCK CHECKPOINT CHECKPOINT
(11e) READ UNLOCK UNLOCK
(11f) READ WRITE WRITE
(11g) READ RECOVER RECOVER
(11h) READ_FULL UNLOCK UNLOCK
(11i) READ_FULL WRITE WRITE
(11j) READ_FULL RECOVER RECOVER
(11k) WRITE READ READ
(11l) PENDING UNLOCK UNLOCK
(11m) PENDING CHECKPOINT CHECKPOINT
(11n) CHECKPOINT UNLOCK UNLOCK
(11o) RECOVER READ READ
These 15 transitions are all that needs to be supported. The lock
manager implementation can assert that fact. The other 27 possible
transitions among the 7 locking states will never occur.
+304 -138
View File
@@ -448,6 +448,8 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){
sqlite3_finalize(p->aStmt[i]);
}
sqlite3_free(p->zSegmentsTbl);
sqlite3_free(p->zReadExprlist);
sqlite3_free(p->zWriteExprlist);
/* Invoke the tokenizer destructor to free the tokenizer. */
p->pTokenizer->pModule->xDestroy(p->pTokenizer);
@@ -665,6 +667,141 @@ static int fts3IsSpecialColumn(
return 1;
}
/*
** Append the output of a printf() style formatting to an existing string.
*/
static void fts3Appendf(
int *pRc, /* IN/OUT: Error code */
char **pz, /* IN/OUT: Pointer to string buffer */
const char *zFormat, /* Printf format string to append */
... /* Arguments for printf format string */
){
if( *pRc==SQLITE_OK ){
va_list ap;
char *z;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
if( z && *pz ){
char *z2 = sqlite3_mprintf("%s%s", *pz, z);
sqlite3_free(z);
z = z2;
}
if( z==0 ) *pRc = SQLITE_NOMEM;
sqlite3_free(*pz);
*pz = z;
}
}
/*
** Return a copy of input string zInput enclosed in double-quotes (") and
** with all double quote characters escaped. For example:
**
** fts3QuoteId("un \"zip\"") -> "un \"\"zip\"\""
**
** The pointer returned points to memory obtained from sqlite3_malloc(). It
** is the callers responsibility to call sqlite3_free() to release this
** memory.
*/
static char *fts3QuoteId(char const *zInput){
int nRet;
char *zRet;
nRet = 2 + strlen(zInput)*2 + 1;
zRet = sqlite3_malloc(nRet);
if( zRet ){
int i;
char *z = zRet;
*(z++) = '"';
for(i=0; zInput[i]; i++){
if( zInput[i]=='"' ) *(z++) = '"';
*(z++) = zInput[i];
}
*(z++) = '"';
*(z++) = '\0';
}
return zRet;
}
/*
** Return a list of comma separated SQL expressions that could be used
** in a SELECT statement such as the following:
**
** SELECT <list of expressions> FROM %_content AS x ...
**
** to return the docid, followed by each column of text data in order
** from left to write. If parameter zFunc is not NULL, then instead of
** being returned directly each column of text data is passed to an SQL
** function named zFunc first. For example, if zFunc is "unzip" and the
** table has the three user-defined columns "a", "b", and "c", the following
** string is returned:
**
** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')"
**
** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
** is the responsibility of the caller to eventually free it.
**
** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and
** a NULL pointer is returned). Otherwise, if an OOM error is encountered
** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If
** no error occurs, *pRc is left unmodified.
*/
static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
char *zRet = 0;
char *zFree = 0;
char *zFunction;
int i;
if( !zFunc ){
zFunction = "";
}else{
zFree = zFunction = fts3QuoteId(zFunc);
}
fts3Appendf(pRc, &zRet, "docid");
for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
}
sqlite3_free(zFree);
return zRet;
}
/*
** Return a list of N comma separated question marks, where N is the number
** of columns in the %_content table (one for the docid plus one for each
** user-defined text column).
**
** If argument zFunc is not NULL, then all but the first question mark
** is preceded by zFunc and an open bracket, and followed by a closed
** bracket. For example, if zFunc is "zip" and the FTS3 table has three
** user-defined text columns, the following string is returned:
**
** "?, zip(?), zip(?), zip(?)"
**
** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
** is the responsibility of the caller to eventually free it.
**
** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and
** a NULL pointer is returned). Otherwise, if an OOM error is encountered
** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If
** no error occurs, *pRc is left unmodified.
*/
static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
char *zRet = 0;
char *zFree = 0;
char *zFunction;
int i;
if( !zFunc ){
zFunction = "";
}else{
zFree = zFunction = fts3QuoteId(zFunc);
}
fts3Appendf(pRc, &zRet, "?");
for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(?)", zFunction);
}
sqlite3_free(zFree);
return zRet;
}
/*
** This function is the implementation of both the xConnect and xCreate
** methods of the FTS3 virtual table.
@@ -701,6 +838,9 @@ static int fts3InitVtab(
const char **aCol; /* Array of column names */
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */
char *zCompress = 0;
char *zUncompress = 0;
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
|| (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4)
@@ -751,6 +891,12 @@ static int fts3InitVtab(
*pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal);
rc = SQLITE_ERROR;
}
}else if( nKey==8 && 0==sqlite3_strnicmp(z, "compress", 8) ){
zCompress = zVal;
zVal = 0;
}else if( nKey==10 && 0==sqlite3_strnicmp(z, "uncompress", 10) ){
zUncompress = zVal;
zVal = 0;
}else{
*pzErr = sqlite3_mprintf("unrecognized parameter: %s", z);
rc = SQLITE_ERROR;
@@ -825,6 +971,15 @@ static int fts3InitVtab(
assert( zCsr <= &((char *)p)[nByte] );
}
if( (zCompress==0)!=(zUncompress==0) ){
char const *zMiss = (zCompress==0 ? "compress" : "uncompress");
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss);
}
p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc);
p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc);
if( rc!=SQLITE_OK ) goto fts3_init_out;
/* If this is an xCreate call, create the underlying tables in the
** database. TODO: For xConnect(), it could verify that said tables exist.
*/
@@ -842,7 +997,8 @@ static int fts3InitVtab(
fts3DeclareVtab(&rc, p);
fts3_init_out:
sqlite3_free(zCompress);
sqlite3_free(zUncompress);
sqlite3_free((void *)aCol);
if( rc!=SQLITE_OK ){
if( p ){
@@ -1935,132 +2091,135 @@ static int fts3DeferredTermSelect(
return SQLITE_OK;
}
/*
** An Fts3SegReaderArray is used to store an array of Fts3SegReader objects.
** Elements are added to the array using fts3SegReaderArrayAdd().
*/
struct Fts3SegReaderArray {
int nSegment; /* Number of valid entries in apSegment[] */
int nAlloc; /* Allocated size of apSegment[] */
int nCost; /* The cost of executing SegReaderIterate() */
Fts3SegReader *apSegment[1]; /* Array of seg-reader objects */
};
/*
** Free an Fts3SegReaderArray object. Also free all seg-readers in the
** array (using sqlite3Fts3SegReaderFree()).
*/
static void fts3SegReaderArrayFree(Fts3SegReaderArray *pArray){
if( pArray ){
int i;
for(i=0; i<pArray->nSegment; i++){
sqlite3Fts3SegReaderFree(pArray->apSegment[i]);
}
sqlite3_free(pArray);
}
}
static int fts3SegReaderArrayAdd(
Fts3SegReaderArray **ppArray,
Fts3SegReader *pNew
int sqlite3Fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */
int iLevel, /* Level of segments to scan */
const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
int isScan, /* True to scan from zTerm to EOF */
Fts3SegReaderCursor *pCsr /* Cursor object to populate */
){
Fts3SegReaderArray *pArray = *ppArray;
int rc = SQLITE_OK;
int rc2;
int iAge = 0;
sqlite3_stmt *pStmt = 0;
Fts3SegReader *pPending = 0;
if( !pArray || pArray->nAlloc==pArray->nSegment ){
int nNew = (pArray ? pArray->nAlloc+16 : 16);
pArray = (Fts3SegReaderArray *)sqlite3_realloc(pArray,
sizeof(Fts3SegReaderArray) + (nNew-1) * sizeof(Fts3SegReader*)
);
if( !pArray ){
sqlite3Fts3SegReaderFree(pNew);
return SQLITE_NOMEM;
assert( iLevel==FTS3_SEGCURSOR_ALL
|| iLevel==FTS3_SEGCURSOR_PENDING
|| iLevel>=0
);
assert( FTS3_SEGCURSOR_PENDING<0 );
assert( FTS3_SEGCURSOR_ALL<0 );
assert( iLevel==FTS3_SEGCURSOR_ALL || (zTerm==0 && isPrefix==1) );
assert( isPrefix==0 || isScan==0 );
memset(pCsr, 0, sizeof(Fts3SegReaderCursor));
/* If iLevel is less than 0, include a seg-reader for the pending-terms. */
assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 );
if( iLevel<0 && isScan==0 ){
rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pPending);
if( rc==SQLITE_OK && pPending ){
int nByte = (sizeof(Fts3SegReader *) * 16);
pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte);
if( pCsr->apSegment==0 ){
rc = SQLITE_NOMEM;
}else{
pCsr->apSegment[0] = pPending;
pCsr->nSegment = 1;
pPending = 0;
}
}
if( nNew==16 ){
pArray->nSegment = 0;
pArray->nCost = 0;
}
pArray->nAlloc = nNew;
*ppArray = pArray;
}
pArray->apSegment[pArray->nSegment++] = pNew;
return SQLITE_OK;
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
if( rc==SQLITE_OK ){
rc = sqlite3Fts3AllSegdirs(p, iLevel, &pStmt);
}
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
/* Read the values returned by the SELECT into local variables. */
sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1);
sqlite3_int64 iLeavesEndBlock = sqlite3_column_int64(pStmt, 2);
sqlite3_int64 iEndBlock = sqlite3_column_int64(pStmt, 3);
int nRoot = sqlite3_column_bytes(pStmt, 4);
char const *zRoot = sqlite3_column_blob(pStmt, 4);
/* If nSegment is a multiple of 16 the array needs to be extended. */
if( (pCsr->nSegment%16)==0 ){
Fts3SegReader **apNew;
int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*);
apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte);
if( !apNew ){
rc = SQLITE_NOMEM;
goto finished;
}
pCsr->apSegment = apNew;
}
/* If zTerm is not NULL, and this segment is not stored entirely on its
** root node, the range of leaves scanned can be reduced. Do this. */
if( iStartBlock && zTerm ){
sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0);
rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi);
if( rc!=SQLITE_OK ) goto finished;
if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock;
}
rc = sqlite3Fts3SegReaderNew(iAge, iStartBlock, iLeavesEndBlock,
iEndBlock, zRoot, nRoot, &pCsr->apSegment[pCsr->nSegment]
);
if( rc!=SQLITE_OK ) goto finished;
pCsr->nSegment++;
iAge++;
}
}
finished:
rc2 = sqlite3_reset(pStmt);
if( rc==SQLITE_DONE ) rc = rc2;
sqlite3Fts3SegReaderFree(pPending);
return rc;
}
static int fts3TermSegReaderArray(
static int fts3TermSegReaderCursor(
Fts3Cursor *pCsr, /* Virtual table cursor handle */
const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
Fts3SegReaderArray **ppArray /* OUT: Allocated seg-reader array */
Fts3SegReaderCursor **ppSegcsr /* OUT: Allocated seg-reader cursor */
){
Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
int rc; /* Return code */
Fts3SegReaderArray *pArray = 0; /* Array object to build */
Fts3SegReader *pReader = 0; /* Seg-reader to add to pArray */
sqlite3_stmt *pStmt = 0; /* SQL statement to scan %_segdir table */
int iAge = 0; /* Used to assign ages to segments */
Fts3SegReaderCursor *pSegcsr; /* Object to allocate and return */
int rc = SQLITE_NOMEM; /* Return code */
/* Allocate a seg-reader to scan the pending terms, if any. */
rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pReader);
if( rc==SQLITE_OK && pReader ) {
rc = fts3SegReaderArrayAdd(&pArray, pReader);
}
/* Loop through the entire %_segdir table. For each segment, create a
** Fts3SegReader to iterate through the subset of the segment leaves
** that may contain a term that matches zTerm/nTerm. For non-prefix
** searches, this is always a single leaf. For prefix searches, this
** may be a contiguous block of leaves.
*/
if( rc==SQLITE_OK ){
rc = sqlite3Fts3AllSegdirs(p, &pStmt);
}
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
Fts3SegReader *pNew = 0;
int nRoot = sqlite3_column_bytes(pStmt, 4);
char const *zRoot = sqlite3_column_blob(pStmt, 4);
if( sqlite3_column_int64(pStmt, 1)==0 ){
/* The entire segment is stored on the root node (which must be a
** leaf). Do not bother inspecting any data in this case, just
** create a Fts3SegReader to scan the single leaf.
*/
rc = sqlite3Fts3SegReaderNew(iAge, 0, 0, 0, zRoot, nRoot, &pNew);
}else{
sqlite3_int64 i1; /* First leaf that may contain zTerm */
sqlite3_int64 i2; /* Final leaf that may contain zTerm */
rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1, (isPrefix?&i2:0));
if( isPrefix==0 ) i2 = i1;
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderNew(iAge, i1, i2, 0, 0, 0, &pNew);
}
pSegcsr = sqlite3_malloc(sizeof(Fts3SegReaderCursor));
if( pSegcsr ){
Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
int i;
int nCost = 0;
rc = sqlite3Fts3SegReaderCursor(
p, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr);
for(i=0; rc==SQLITE_OK && i<pSegcsr->nSegment; i++){
rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost);
}
assert( (pNew==0)==(rc!=SQLITE_OK) );
/* If a new Fts3SegReader was allocated, add it to the array. */
if( rc==SQLITE_OK ){
rc = fts3SegReaderArrayAdd(&pArray, pNew);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderCost(pCsr, pNew, &pArray->nCost);
}
iAge++;
pSegcsr->nCost = nCost;
}
if( rc==SQLITE_DONE ){
rc = sqlite3_reset(pStmt);
}else{
sqlite3_reset(pStmt);
}
if( rc!=SQLITE_OK ){
fts3SegReaderArrayFree(pArray);
pArray = 0;
}
*ppArray = pArray;
*ppSegcsr = pSegcsr;
return rc;
}
static void fts3SegReaderCursorFree(Fts3SegReaderCursor *pSegcsr){
sqlite3Fts3SegReaderFinish(pSegcsr);
sqlite3_free(pSegcsr);
}
/*
** This function retreives the doclist for the specified term (or term
** prefix) from the database.
@@ -2081,11 +2240,11 @@ static int fts3TermSelect(
char **ppOut /* OUT: Malloced result buffer */
){
int rc; /* Return code */
Fts3SegReaderArray *pArray; /* Seg-reader array for this term */
TermSelect tsc; /* Context object for fts3TermSelectCb() */
Fts3SegFilter filter; /* Segment term filter configuration */
Fts3SegReaderCursor *pSegcsr; /* Seg-reader cursor for this term */
TermSelect tsc; /* Context object for fts3TermSelectCb() */
Fts3SegFilter filter; /* Segment term filter configuration */
pArray = pTok->pArray;
pSegcsr = pTok->pSegcsr;
memset(&tsc, 0, sizeof(TermSelect));
tsc.isReqPos = isReqPos;
@@ -2097,13 +2256,18 @@ static int fts3TermSelect(
filter.zTerm = pTok->z;
filter.nTerm = pTok->n;
rc = sqlite3Fts3SegReaderIterate(p, pArray->apSegment, pArray->nSegment,
&filter, fts3TermSelectCb, (void *)&tsc
);
rc = sqlite3Fts3SegReaderStart(p, pSegcsr, &filter);
while( SQLITE_OK==rc
&& SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr))
){
rc = fts3TermSelectCb(p, (void *)&tsc,
pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist
);
}
if( rc==SQLITE_OK ){
rc = fts3TermSelectMerge(&tsc);
}
if( rc==SQLITE_OK ){
*ppOut = tsc.aaOutput[0];
*pnOut = tsc.anOutput[0];
@@ -2114,8 +2278,8 @@ static int fts3TermSelect(
}
}
fts3SegReaderArrayFree(pArray);
pTok->pArray = 0;
fts3SegReaderCursorFree(pSegcsr);
pTok->pSegcsr = 0;
return rc;
}
@@ -2238,13 +2402,13 @@ static int fts3PhraseSelect(
*/
for(ii=0; ii<pPhrase->nToken; ii++){
Fts3PhraseToken *pTok = &pPhrase->aToken[ii];
if( pTok->pArray==0 ){
if( pTok->pSegcsr==0 ){
if( (pCsr->eEvalmode==FTS3_EVAL_FILTER)
|| (pCsr->eEvalmode==FTS3_EVAL_NEXT && pCsr->pDeferred==0)
|| (pCsr->eEvalmode==FTS3_EVAL_MATCHINFO && pTok->bFulltext)
){
rc = fts3TermSegReaderArray(
pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray
rc = fts3TermSegReaderCursor(
pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr
);
if( rc!=SQLITE_OK ) return rc;
}
@@ -2275,10 +2439,10 @@ static int fts3PhraseSelect(
/* Find the remaining token with the lowest cost. */
for(jj=0; jj<pPhrase->nToken; jj++){
Fts3SegReaderArray *pArray = pPhrase->aToken[jj].pArray;
if( pArray && pArray->nCost<nMinCost ){
Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[jj].pSegcsr;
if( pSegcsr && pSegcsr->nCost<nMinCost ){
iTok = jj;
nMinCost = pArray->nCost;
nMinCost = pSegcsr->nCost;
}
}
pTok = &pPhrase->aToken[iTok];
@@ -2297,12 +2461,12 @@ static int fts3PhraseSelect(
if( pCsr->eEvalmode==FTS3_EVAL_NEXT && pTok->pDeferred ){
rc = fts3DeferredTermSelect(pTok->pDeferred, isTermPos, &nList, &pList);
}else{
if( pTok->pArray ){
if( pTok->pSegcsr ){
rc = fts3TermSelect(p, pTok, iCol, isTermPos, &nList, &pList);
}
pTok->bFulltext = 1;
}
assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pArray==0 );
assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pSegcsr==0 );
if( rc!=SQLITE_OK ) break;
if( isFirst ){
@@ -2480,9 +2644,9 @@ static int fts3ExprAllocateSegReaders(
for(ii=0; rc==SQLITE_OK && ii<pPhrase->nToken; ii++){
Fts3PhraseToken *pTok = &pPhrase->aToken[ii];
if( pTok->pArray==0 ){
rc = fts3TermSegReaderArray(
pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray
if( pTok->pSegcsr==0 ){
rc = fts3TermSegReaderCursor(
pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr
);
}
}
@@ -2506,8 +2670,8 @@ static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){
if( pPhrase ){
int kk;
for(kk=0; kk<pPhrase->nToken; kk++){
fts3SegReaderArrayFree(pPhrase->aToken[kk].pArray);
pPhrase->aToken[kk].pArray = 0;
fts3SegReaderCursorFree(pPhrase->aToken[kk].pSegcsr);
pPhrase->aToken[kk].pSegcsr = 0;
}
}
fts3ExprFreeSegReaders(pExpr->pLeft);
@@ -2527,10 +2691,8 @@ static int fts3ExprCost(Fts3Expr *pExpr){
int ii;
nCost = 0;
for(ii=0; ii<pPhrase->nToken; ii++){
Fts3SegReaderArray *pArray = pPhrase->aToken[ii].pArray;
if( pArray ){
nCost += pPhrase->aToken[ii].pArray->nCost;
}
Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[ii].pSegcsr;
if( pSegcsr ) nCost += pSegcsr->nCost;
}
}else{
nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight);
@@ -2872,8 +3034,8 @@ static int fts3FilterMethod(
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
const char *azSql[] = {
"SELECT * FROM %Q.'%q_content' WHERE docid = ?", /* non-full-table-scan */
"SELECT * FROM %Q.'%q_content'", /* full-table-scan */
"SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?", /* non-full-scan */
"SELECT %s FROM %Q.'%q_content' AS x ", /* full-scan */
};
int rc; /* Return code */
char *zSql; /* SQL statement used to access %_content */
@@ -2928,7 +3090,8 @@ static int fts3FilterMethod(
** full-text query or docid lookup, the statement retrieves a single
** row by docid.
*/
zSql = sqlite3_mprintf(azSql[idxNum==FTS3_FULLSCAN_SEARCH], p->zDb, p->zName);
zSql = (char *)azSql[idxNum==FTS3_FULLSCAN_SEARCH];
zSql = sqlite3_mprintf(zSql, p->zReadExprlist, p->zDb, p->zName);
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
@@ -3446,6 +3609,9 @@ int sqlite3Fts3Init(sqlite3 *db){
sqlite3Fts3IcuTokenizerModule(&pIcu);
#endif
rc = sqlite3Fts3InitAux(db);
if( rc!=SQLITE_OK ) return rc;
sqlite3Fts3SimpleTokenizerModule(&pSimple);
sqlite3Fts3PorterTokenizerModule(&pPorter);
+38 -8
View File
@@ -107,7 +107,7 @@ typedef struct Fts3PhraseToken Fts3PhraseToken;
typedef struct Fts3SegFilter Fts3SegFilter;
typedef struct Fts3DeferredToken Fts3DeferredToken;
typedef struct Fts3SegReader Fts3SegReader;
typedef struct Fts3SegReaderArray Fts3SegReaderArray;
typedef struct Fts3SegReaderCursor Fts3SegReaderCursor;
/*
** A connection to a fulltext index is an instance of the following
@@ -130,6 +130,9 @@ struct Fts3Table {
*/
sqlite3_stmt *aStmt[24];
char *zReadExprlist;
char *zWriteExprlist;
int nNodeSize; /* Soft limit for node size */
u8 bHasStat; /* True if %_stat table exists */
u8 bHasDocsize; /* True if %_docsize table exists */
@@ -217,7 +220,7 @@ struct Fts3PhraseToken {
int n; /* Number of bytes in buffer z */
int isPrefix; /* True if token ends with a "*" character */
int bFulltext; /* True if full-text index was used */
Fts3SegReaderArray *pArray; /* Segment-reader for this token */
Fts3SegReaderCursor *pSegcsr; /* Segment-reader for this token */
Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
};
@@ -285,12 +288,8 @@ int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**);
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
int sqlite3Fts3SegReaderIterate(
Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *,
int (*)(Fts3Table *, void *, char *, int, char *, int), void *
);
int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *);
int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **);
int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **);
int sqlite3Fts3ReadLock(Fts3Table *);
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*);
@@ -302,14 +301,23 @@ int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *);
void sqlite3Fts3SegmentsClose(Fts3Table *);
#define FTS3_SEGCURSOR_PENDING -1
#define FTS3_SEGCURSOR_ALL -2
int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*);
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *);
void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *);
int sqlite3Fts3SegReaderCursor(
Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
#define FTS3_SEGMENT_PREFIX 0x00000008
#define FTS3_SEGMENT_SCAN 0x00000010
/* Type passed as 4th argument to SegmentReaderIterate() */
struct Fts3SegFilter {
@@ -319,6 +327,25 @@ struct Fts3SegFilter {
int flags;
};
struct Fts3SegReaderCursor {
/* Used internally by sqlite3Fts3SegReaderXXX() calls */
Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */
int nSegment; /* Size of apSegment array */
int nAdvance; /* How many seg-readers to advance */
Fts3SegFilter *pFilter; /* Pointer to filter object */
char *aBuffer; /* Buffer to merge doclists in */
int nBuffer; /* Allocated size of aBuffer[] in bytes */
/* Cost of running this iterator. Used by fts3.c only. */
int nCost;
/* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */
char *zTerm; /* Pointer to term buffer */
int nTerm; /* Size of zTerm in bytes */
char *aDoclist; /* Pointer to doclist buffer */
int nDoclist; /* Size of aDoclist[] in bytes */
};
/* fts3.c */
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
@@ -355,4 +382,7 @@ void sqlite3Fts3ExprFree(Fts3Expr *);
int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
#endif
/* fts3_aux.c */
int sqlite3Fts3InitAux(sqlite3 *db);
#endif /* _FTSINT_H */
+470
View File
@@ -0,0 +1,470 @@
/*
** 2011 Jan 27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#include "fts3Int.h"
#include <string.h>
#include <assert.h>
typedef struct Fts3auxTable Fts3auxTable;
typedef struct Fts3auxCursor Fts3auxCursor;
struct Fts3auxTable {
sqlite3_vtab base; /* Base class used by SQLite core */
Fts3Table *pFts3Tab;
};
struct Fts3auxCursor {
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
Fts3SegReaderCursor csr; /* Must be right after "base" */
Fts3SegFilter filter;
char *zStop;
int nStop; /* Byte-length of string zStop */
int isEof; /* True if cursor is at EOF */
sqlite3_int64 iRowid; /* Current rowid */
int iCol; /* Current value of 'col' column */
int nStat; /* Size of aStat[] array */
struct Fts3auxColstats {
sqlite3_int64 nDoc; /* 'documents' values for current csr row */
sqlite3_int64 nOcc; /* 'occurrences' values for current csr row */
} *aStat;
};
/*
** Schema of the terms table.
*/
#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)"
/*
** This function does all the work for both the xConnect and xCreate methods.
** These tables have no persistent representation of their own, so xConnect
** and xCreate are identical operations.
*/
static int fts3auxConnectMethod(
sqlite3 *db, /* Database connection */
void *pUnused, /* Unused */
int argc, /* Number of elements in argv array */
const char * const *argv, /* xCreate/xConnect argument array */
sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
char **pzErr /* OUT: sqlite3_malloc'd error message */
){
char const *zDb; /* Name of database (e.g. "main") */
char const *zFts3; /* Name of fts3 table */
int nDb; /* Result of strlen(zDb) */
int nFts3; /* Result of strlen(zFts3) */
int nByte; /* Bytes of space to allocate here */
int rc; /* value returned by declare_vtab() */
Fts3auxTable *p; /* Virtual table object to return */
UNUSED_PARAMETER(pUnused);
/* The user should specify a single argument - the name of an fts3 table. */
if( argc!=4 ){
*pzErr = sqlite3_mprintf(
"wrong number of arguments to fts4aux constructor"
);
return SQLITE_ERROR;
}
zDb = argv[1];
nDb = strlen(zDb);
zFts3 = argv[3];
nFts3 = strlen(zFts3);
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
if( rc!=SQLITE_OK ) return rc;
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
p = (Fts3auxTable *)sqlite3_malloc(nByte);
if( !p ) return SQLITE_NOMEM;
memset(p, 0, nByte);
p->pFts3Tab = (Fts3Table *)&p[1];
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
p->pFts3Tab->db = db;
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
sqlite3Fts3Dequote((char *)p->pFts3Tab->zName);
*ppVtab = (sqlite3_vtab *)p;
return SQLITE_OK;
}
/*
** This function does the work for both the xDisconnect and xDestroy methods.
** These tables have no persistent representation of their own, so xDisconnect
** and xDestroy are identical operations.
*/
static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){
Fts3auxTable *p = (Fts3auxTable *)pVtab;
Fts3Table *pFts3 = p->pFts3Tab;
int i;
/* Free any prepared statements held */
for(i=0; i<SizeofArray(pFts3->aStmt); i++){
sqlite3_finalize(pFts3->aStmt[i]);
}
sqlite3_free(pFts3->zSegmentsTbl);
sqlite3_free(p);
return SQLITE_OK;
}
#define FTS4AUX_EQ_CONSTRAINT 1
#define FTS4AUX_GE_CONSTRAINT 2
#define FTS4AUX_LE_CONSTRAINT 4
/*
** xBestIndex - Analyze a WHERE and ORDER BY clause.
*/
static int fts3auxBestIndexMethod(
sqlite3_vtab *pVTab,
sqlite3_index_info *pInfo
){
int i;
int iEq = -1;
int iGe = -1;
int iLe = -1;
UNUSED_PARAMETER(pVTab);
/* This vtab delivers always results in "ORDER BY term ASC" order. */
if( pInfo->nOrderBy==1
&& pInfo->aOrderBy[0].iColumn==0
&& pInfo->aOrderBy[0].desc==0
){
pInfo->orderByConsumed = 1;
}
/* Search for equality and range constraints on the "term" column. */
for(i=0; i<pInfo->nConstraint; i++){
if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
int op = pInfo->aConstraint[i].op;
if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
}
}
if( iEq>=0 ){
pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
pInfo->aConstraintUsage[iEq].argvIndex = 1;
pInfo->estimatedCost = 5;
}else{
pInfo->idxNum = 0;
pInfo->estimatedCost = 20000;
if( iGe>=0 ){
pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
pInfo->aConstraintUsage[iGe].argvIndex = 1;
pInfo->estimatedCost /= 2;
}
if( iLe>=0 ){
pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
pInfo->estimatedCost /= 2;
}
}
return SQLITE_OK;
}
/*
** xOpen - Open a cursor.
*/
static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
Fts3auxCursor *pCsr; /* Pointer to cursor object to return */
UNUSED_PARAMETER(pVTab);
pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor));
if( !pCsr ) return SQLITE_NOMEM;
memset(pCsr, 0, sizeof(Fts3auxCursor));
*ppCsr = (sqlite3_vtab_cursor *)pCsr;
return SQLITE_OK;
}
/*
** xClose - Close a cursor.
*/
static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
sqlite3Fts3SegmentsClose(pFts3);
sqlite3Fts3SegReaderFinish(&pCsr->csr);
sqlite3_free((void *)pCsr->filter.zTerm);
sqlite3_free(pCsr->zStop);
sqlite3_free(pCsr->aStat);
sqlite3_free(pCsr);
return SQLITE_OK;
}
static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){
if( nSize>pCsr->nStat ){
struct Fts3auxColstats *aNew;
aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat,
sizeof(struct Fts3auxColstats) * nSize
);
if( aNew==0 ) return SQLITE_NOMEM;
memset(&aNew[pCsr->nStat], 0,
sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat)
);
pCsr->aStat = aNew;
pCsr->nStat = nSize;
}
return SQLITE_OK;
}
/*
** xNext - Advance the cursor to the next row, if any.
*/
static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
int rc;
/* Increment our pretend rowid value. */
pCsr->iRowid++;
for(pCsr->iCol++; pCsr->iCol<pCsr->nStat; pCsr->iCol++){
if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK;
}
rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr);
if( rc==SQLITE_ROW ){
int i = 0;
int nDoclist = pCsr->csr.nDoclist;
char *aDoclist = pCsr->csr.aDoclist;
int iCol;
int eState = 0;
if( pCsr->zStop ){
int n = (pCsr->nStop<pCsr->csr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm;
int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n);
if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){
pCsr->isEof = 1;
return SQLITE_OK;
}
}
if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM;
memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat);
iCol = 0;
while( i<nDoclist ){
sqlite3_int64 v = 0;
i += sqlite3Fts3GetVarint(&aDoclist[i], &v);
switch( eState ){
/* State 0. In this state the integer just read was a docid. */
case 0:
pCsr->aStat[0].nDoc++;
eState = 1;
iCol = 0;
break;
/* State 1. In this state we are expecting either a 1, indicating
** that the following integer will be a column number, or the
** start of a position list for column 0.
**
** The only difference between state 1 and state 2 is that if the
** integer encountered in state 1 is not 0 or 1, then we need to
** increment the column 0 "nDoc" count for this term.
*/
case 1:
assert( iCol==0 );
if( v>1 ){
pCsr->aStat[1].nDoc++;
}
eState = 2;
/* fall through */
case 2:
if( v==0 ){ /* 0x00. Next integer will be a docid. */
eState = 0;
}else if( v==1 ){ /* 0x01. Next integer will be a column number. */
eState = 3;
}else{ /* 2 or greater. A position. */
pCsr->aStat[iCol+1].nOcc++;
pCsr->aStat[0].nOcc++;
}
break;
/* State 3. The integer just read is a column number. */
default: assert( eState==3 );
iCol = (int)v;
if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM;
pCsr->aStat[iCol+1].nDoc++;
eState = 2;
break;
}
}
pCsr->iCol = 0;
rc = SQLITE_OK;
}else{
pCsr->isEof = 1;
}
return rc;
}
/*
** xFilter - Initialize a cursor to point at the start of its data.
*/
static int fts3auxFilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
int rc;
int isScan;
UNUSED_PARAMETER(nVal);
assert( idxStr==0 );
assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0
|| idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
|| idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
);
isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
/* In case this cursor is being reused, close and zero it. */
testcase(pCsr->filter.zTerm);
sqlite3Fts3SegReaderFinish(&pCsr->csr);
sqlite3_free((void *)pCsr->filter.zTerm);
sqlite3_free(pCsr->aStat);
memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
const unsigned char *zStr = sqlite3_value_text(apVal[0]);
if( zStr ){
pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
}
}
if( idxNum&FTS4AUX_LE_CONSTRAINT ){
int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
);
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
}
if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor);
return rc;
}
/*
** xEof - Return true if the cursor is at EOF, or false otherwise.
*/
static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
return pCsr->isEof;
}
/*
** xColumn - Return a column value.
*/
static int fts3auxColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
assert( p->isEof==0 );
if( iCol==0 ){ /* Column "term" */
sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
}else if( iCol==1 ){ /* Column "col" */
if( p->iCol ){
sqlite3_result_int(pContext, p->iCol-1);
}else{
sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC);
}
}else if( iCol==2 ){ /* Column "documents" */
sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc);
}else{ /* Column "occurrences" */
sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc);
}
return SQLITE_OK;
}
/*
** xRowid - Return the current rowid for the cursor.
*/
static int fts3auxRowidMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite_int64 *pRowid /* OUT: Rowid value */
){
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
*pRowid = pCsr->iRowid;
return SQLITE_OK;
}
/*
** Register the fts3aux module with database connection db. Return SQLITE_OK
** if successful or an error code if sqlite3_create_module() fails.
*/
int sqlite3Fts3InitAux(sqlite3 *db){
static const sqlite3_module fts3aux_module = {
0, /* iVersion */
fts3auxConnectMethod, /* xCreate */
fts3auxConnectMethod, /* xConnect */
fts3auxBestIndexMethod, /* xBestIndex */
fts3auxDisconnectMethod, /* xDisconnect */
fts3auxDisconnectMethod, /* xDestroy */
fts3auxOpenMethod, /* xOpen */
fts3auxCloseMethod, /* xClose */
fts3auxFilterMethod, /* xFilter */
fts3auxNextMethod, /* xNext */
fts3auxEofMethod, /* xEof */
fts3auxColumnMethod, /* xColumn */
fts3auxRowidMethod, /* xRowid */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindFunction */
0 /* xRename */
};
int rc; /* Return code */
rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0);
return rc;
}
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
+8 -5
View File
@@ -880,13 +880,13 @@ static int fts3ExprLocalHitsCb(
void *pCtx /* Pointer to MatchInfo structure */
){
MatchInfo *p = (MatchInfo *)pCtx;
int iStart = iPhrase * p->nCol * 3;
int i;
for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0;
if( pExpr->aDoclist ){
char *pCsr;
int iStart = iPhrase * p->nCol * 3;
int i;
for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0;
pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1);
if( pCsr ){
@@ -960,6 +960,7 @@ static int fts3MatchinfoSelectDoctotal(
a = sqlite3_column_blob(pStmt, 0);
a += sqlite3Fts3GetVarint(a, &nDoc);
if( nDoc==0 ) return SQLITE_CORRUPT;
*pnDoc = (u32)nDoc;
if( paLen ) *paLen = a;
@@ -1166,9 +1167,11 @@ static int fts3MatchinfoValues(
if( rc==SQLITE_OK ){
int iCol;
for(iCol=0; iCol<pInfo->nCol; iCol++){
u32 iVal;
sqlite3_int64 nToken;
a += sqlite3Fts3GetVarint(a, &nToken);
pInfo->aMatchinfo[iCol] = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
pInfo->aMatchinfo[iCol] = iVal;
}
}
}
+163 -331
View File
@@ -212,7 +212,7 @@ static int fts3SqlStmt(
/* 4 */ "DELETE FROM %Q.'%q_segdir'",
/* 5 */ "DELETE FROM %Q.'%q_docsize'",
/* 6 */ "DELETE FROM %Q.'%q_stat'",
/* 7 */ "SELECT * FROM %Q.'%q_content' WHERE rowid=?",
/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?",
/* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
/* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
@@ -229,7 +229,7 @@ static int fts3SqlStmt(
/* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?",
/* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?",
/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)",
/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%s)",
/* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?",
/* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)",
/* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?",
@@ -246,20 +246,9 @@ static int fts3SqlStmt(
if( !pStmt ){
char *zSql;
if( eStmt==SQL_CONTENT_INSERT ){
int i; /* Iterator variable */
char *zVarlist; /* The "?, ?, ..." string */
zVarlist = (char *)sqlite3_malloc(2*p->nColumn+2);
if( !zVarlist ){
*pp = 0;
return SQLITE_NOMEM;
}
zVarlist[0] = '?';
zVarlist[p->nColumn*2+1] = '\0';
for(i=1; i<=p->nColumn; i++){
zVarlist[i*2-1] = ',';
zVarlist[i*2] = '?';
}
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, zVarlist);
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
}
@@ -300,7 +289,7 @@ static int fts3SelectDocsize(
sqlite3_bind_int64(pStmt, 1, iDocid);
}
rc = sqlite3_step(pStmt);
if( rc!=SQLITE_ROW ){
if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){
rc = sqlite3_reset(pStmt);
if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT;
pStmt = 0;
@@ -401,8 +390,17 @@ int sqlite3Fts3ReadLock(Fts3Table *p){
** 3: end_block
** 4: root
*/
int sqlite3Fts3AllSegdirs(Fts3Table *p, sqlite3_stmt **ppStmt){
return fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, ppStmt, 0);
int sqlite3Fts3AllSegdirs(Fts3Table *p, int iLevel, sqlite3_stmt **ppStmt){
int rc;
sqlite3_stmt *pStmt = 0;
if( iLevel<0 ){
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, &pStmt, 0);
}else{
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
if( rc==SQLITE_OK ) sqlite3_bind_int(pStmt, 1, iLevel);
}
*ppStmt = pStmt;
return rc;
}
@@ -1104,16 +1102,18 @@ int sqlite3Fts3SegReaderCost(
sqlite3_stmt *pStmt;
sqlite3_int64 nDoc = 0;
sqlite3_int64 nByte = 0;
const char *pEnd;
const char *a;
rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
if( rc ) return rc;
if( rc!=SQLITE_OK ) return rc;
a = sqlite3_column_blob(pStmt, 0);
if( a ){
const char *pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
a += sqlite3Fts3GetVarint(a, &nDoc);
while( a<pEnd ){
a += sqlite3Fts3GetVarint(a, &nByte);
}
assert( a );
pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
a += sqlite3Fts3GetVarint(a, &nDoc);
while( a<pEnd ){
a += sqlite3Fts3GetVarint(a, &nByte);
}
if( nDoc==0 || nByte==0 ){
sqlite3_reset(pStmt);
@@ -1303,42 +1303,6 @@ int sqlite3Fts3SegReaderPending(
return rc;
}
/*
** The second argument to this function is expected to be a statement of
** the form:
**
** SELECT
** idx, -- col 0
** start_block, -- col 1
** leaves_end_block, -- col 2
** end_block, -- col 3
** root -- col 4
** FROM %_segdir ...
**
** This function allocates and initializes a Fts3SegReader structure to
** iterate through the terms stored in the segment identified by the
** current row that pStmt is pointing to.
**
** If successful, the Fts3SegReader is left pointing to the first term
** in the segment and SQLITE_OK is returned. Otherwise, an SQLite error
** code is returned.
*/
static int fts3SegReaderNew(
sqlite3_stmt *pStmt, /* See above */
int iAge, /* Segment "age". */
Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */
){
return sqlite3Fts3SegReaderNew(iAge,
sqlite3_column_int64(pStmt, 1),
sqlite3_column_int64(pStmt, 2),
sqlite3_column_int64(pStmt, 3),
sqlite3_column_blob(pStmt, 4),
sqlite3_column_bytes(pStmt, 4),
ppReader
);
}
/*
** Compare the entries pointed to by two Fts3SegReader structures.
** Comparison is as follows:
@@ -1943,25 +1907,6 @@ static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){
return rc;
}
/*
** Set *pnSegment to the number of segments of level iLevel in the database.
**
** Return SQLITE_OK if successful, or an SQLite error code if not.
*/
static int fts3SegmentCount(Fts3Table *p, int iLevel, int *pnSegment){
sqlite3_stmt *pStmt;
int rc;
assert( iLevel>=0 );
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_COUNT, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_int(pStmt, 1, iLevel);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
*pnSegment = sqlite3_column_int(pStmt, 0);
}
return sqlite3_reset(pStmt);
}
/*
** Set *pnSegment to the total number of segments in the database. Set
** *pnMax to the largest segment level in the database (segment levels
@@ -2020,15 +1965,18 @@ static int fts3DeleteSegdir(
return rc;
}
if( iLevel>=0 ){
if( iLevel==FTS3_SEGCURSOR_ALL ){
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
}else if( iLevel==FTS3_SEGCURSOR_PENDING ){
sqlite3Fts3PendingTermsClear(p);
}else{
assert( iLevel>=0 );
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pDelete, 1, iLevel);
sqlite3_step(pDelete);
rc = sqlite3_reset(pDelete);
}
}else{
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
}
return rc;
@@ -2077,85 +2025,15 @@ static void fts3ColumnFilter(
*pnList = nList;
}
/*
** sqlite3Fts3SegReaderIterate() callback used when merging multiple
** segments to create a single, larger segment.
*/
static int fts3MergeCallback(
Fts3Table *p, /* FTS3 Virtual table handle */
void *pContext, /* Pointer to SegmentWriter* to write with */
char *zTerm, /* Term to write to the db */
int nTerm, /* Number of bytes in zTerm */
char *aDoclist, /* Doclist associated with zTerm */
int nDoclist /* Number of bytes in doclist */
){
SegmentWriter **ppW = (SegmentWriter **)pContext;
return fts3SegWriterAdd(p, ppW, 1, zTerm, nTerm, aDoclist, nDoclist);
}
/*
** sqlite3Fts3SegReaderIterate() callback used when flushing the contents
** of the pending-terms hash table to the database.
*/
static int fts3FlushCallback(
Fts3Table *p, /* FTS3 Virtual table handle */
void *pContext, /* Pointer to SegmentWriter* to write with */
char *zTerm, /* Term to write to the db */
int nTerm, /* Number of bytes in zTerm */
char *aDoclist, /* Doclist associated with zTerm */
int nDoclist /* Number of bytes in doclist */
){
SegmentWriter **ppW = (SegmentWriter **)pContext;
return fts3SegWriterAdd(p, ppW, 0, zTerm, nTerm, aDoclist, nDoclist);
}
/*
** This function is used to iterate through a contiguous set of terms
** stored in the full-text index. It merges data contained in one or
** more segments to support this.
**
** The second argument is passed an array of pointers to SegReader objects
** allocated with sqlite3Fts3SegReaderNew(). This function merges the range
** of terms selected by each SegReader. If a single term is present in
** more than one segment, the associated doclists are merged. For each
** term and (possibly merged) doclist in the merged range, the callback
** function xFunc is invoked with its arguments set as follows.
**
** arg 0: Copy of 'p' parameter passed to this function
** arg 1: Copy of 'pContext' parameter passed to this function
** arg 2: Pointer to buffer containing term
** arg 3: Size of arg 2 buffer in bytes
** arg 4: Pointer to buffer containing doclist
** arg 5: Size of arg 2 buffer in bytes
**
** The 4th argument to this function is a pointer to a structure of type
** Fts3SegFilter, defined in fts3Int.h. The contents of this structure
** further restrict the range of terms that callbacks are made for and
** modify the behaviour of this function. See comments above structure
** definition for details.
*/
int sqlite3Fts3SegReaderIterate(
int sqlite3Fts3SegReaderStart(
Fts3Table *p, /* Virtual table handle */
Fts3SegReader **apSegment, /* Array of Fts3SegReader objects */
int nSegment, /* Size of apSegment array */
Fts3SegFilter *pFilter, /* Restrictions on range of iteration */
int (*xFunc)(Fts3Table *, void *, char *, int, char *, int), /* Callback */
void *pContext /* Callback context (2nd argument) */
Fts3SegReaderCursor *pCsr, /* Cursor object */
Fts3SegFilter *pFilter /* Restrictions on range of iteration */
){
int i; /* Iterator variable */
char *aBuffer = 0; /* Buffer to merge doclists in */
int nAlloc = 0; /* Allocated size of aBuffer buffer */
int rc = SQLITE_OK; /* Return code */
int i;
int isIgnoreEmpty = (pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
int isRequirePos = (pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
int isColFilter = (pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
int isPrefix = (pFilter->flags & FTS3_SEGMENT_PREFIX);
/* If there are zero segments, this function is a no-op. This scenario
** comes about only when reading from an empty database.
*/
if( nSegment==0 ) goto finished;
/* Initialize the cursor object */
pCsr->pFilter = pFilter;
/* If the Fts3SegFilter defines a specific term (or term prefix) to search
** for, then advance each segment iterator until it points to a term of
@@ -2163,21 +2041,59 @@ int sqlite3Fts3SegReaderIterate(
** unnecessary merge/sort operations for the case where single segment
** b-tree leaf nodes contain more than one term.
*/
for(i=0; i<nSegment; i++){
for(i=0; i<pCsr->nSegment; i++){
int nTerm = pFilter->nTerm;
const char *zTerm = pFilter->zTerm;
Fts3SegReader *pSeg = apSegment[i];
Fts3SegReader *pSeg = pCsr->apSegment[i];
do {
rc = fts3SegReaderNext(p, pSeg);
if( rc!=SQLITE_OK ) goto finished;
int rc = fts3SegReaderNext(p, pSeg);
if( rc!=SQLITE_OK ) return rc;
}while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 );
}
fts3SegReaderSort(
pCsr->apSegment, pCsr->nSegment, pCsr->nSegment, fts3SegReaderCmp);
fts3SegReaderSort(apSegment, nSegment, nSegment, fts3SegReaderCmp);
while( apSegment[0]->aNode ){
int nTerm = apSegment[0]->nTerm;
char *zTerm = apSegment[0]->zTerm;
int nMerge = 1;
return SQLITE_OK;
}
int sqlite3Fts3SegReaderStep(
Fts3Table *p, /* Virtual table handle */
Fts3SegReaderCursor *pCsr /* Cursor object */
){
int rc = SQLITE_OK;
int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
Fts3SegReader **apSegment = pCsr->apSegment;
int nSegment = pCsr->nSegment;
Fts3SegFilter *pFilter = pCsr->pFilter;
if( pCsr->nSegment==0 ) return SQLITE_OK;
do {
int nMerge;
int i;
/* Advance the first pCsr->nAdvance entries in the apSegment[] array
** forward. Then sort the list in order of current term again.
*/
for(i=0; i<pCsr->nAdvance; i++){
rc = fts3SegReaderNext(p, apSegment[i]);
if( rc!=SQLITE_OK ) return rc;
}
fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp);
pCsr->nAdvance = 0;
/* If all the seg-readers are at EOF, we're finished. return SQLITE_OK. */
assert( rc==SQLITE_OK );
if( apSegment[0]->aNode==0 ) break;
pCsr->nTerm = apSegment[0]->nTerm;
pCsr->zTerm = apSegment[0]->zTerm;
/* If this is a prefix-search, and if the term that apSegment[0] points
** to does not share a suffix with pFilter->zTerm/nTerm, then all
@@ -2186,35 +2102,36 @@ int sqlite3Fts3SegReaderIterate(
** Similarly, if this is a search for an exact match, and the first term
** of segment apSegment[0] is not a match, exit early.
*/
if( pFilter->zTerm ){
if( nTerm<pFilter->nTerm
|| (!isPrefix && nTerm>pFilter->nTerm)
|| memcmp(zTerm, pFilter->zTerm, pFilter->nTerm)
){
goto finished;
if( pFilter->zTerm && !isScan ){
if( pCsr->nTerm<pFilter->nTerm
|| (!isPrefix && pCsr->nTerm>pFilter->nTerm)
|| memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm)
){
break;
}
}
nMerge = 1;
while( nMerge<nSegment
&& apSegment[nMerge]->aNode
&& apSegment[nMerge]->nTerm==nTerm
&& 0==memcmp(zTerm, apSegment[nMerge]->zTerm, nTerm)
&& apSegment[nMerge]->nTerm==pCsr->nTerm
&& 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm)
){
nMerge++;
}
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
if( nMerge==1 && !isIgnoreEmpty ){
Fts3SegReader *p0 = apSegment[0];
rc = xFunc(p, pContext, zTerm, nTerm, p0->aDoclist, p0->nDoclist);
if( rc!=SQLITE_OK ) goto finished;
pCsr->aDoclist = apSegment[0]->aDoclist;
pCsr->nDoclist = apSegment[0]->nDoclist;
rc = SQLITE_ROW;
}else{
int nDoclist = 0; /* Size of doclist */
sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */
/* The current term of the first nMerge entries in the array
** of Fts3SegReader objects is the same. The doclists must be merged
** and a single term added to the new segment.
** and a single term returned with the merged doclist.
*/
for(i=0; i<nMerge; i++){
fts3SegReaderFirstDocid(apSegment[i]);
@@ -2242,54 +2159,57 @@ int sqlite3Fts3SegReaderIterate(
if( !isIgnoreEmpty || nList>0 ){
nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0);
if( nDoclist+nByte>nAlloc ){
if( nDoclist+nByte>pCsr->nBuffer ){
char *aNew;
nAlloc = (nDoclist+nByte)*2;
aNew = sqlite3_realloc(aBuffer, nAlloc);
pCsr->nBuffer = (nDoclist+nByte)*2;
aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
if( !aNew ){
rc = SQLITE_NOMEM;
goto finished;
return SQLITE_NOMEM;
}
aBuffer = aNew;
pCsr->aBuffer = aNew;
}
nDoclist += sqlite3Fts3PutVarint(&aBuffer[nDoclist], iDocid-iPrev);
nDoclist += sqlite3Fts3PutVarint(
&pCsr->aBuffer[nDoclist], iDocid-iPrev
);
iPrev = iDocid;
if( isRequirePos ){
memcpy(&aBuffer[nDoclist], pList, nList);
memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
nDoclist += nList;
aBuffer[nDoclist++] = '\0';
pCsr->aBuffer[nDoclist++] = '\0';
}
}
fts3SegReaderSort(apSegment, nMerge, j, fts3SegReaderDoclistCmp);
}
if( nDoclist>0 ){
rc = xFunc(p, pContext, zTerm, nTerm, aBuffer, nDoclist);
if( rc!=SQLITE_OK ) goto finished;
pCsr->aDoclist = pCsr->aBuffer;
pCsr->nDoclist = nDoclist;
rc = SQLITE_ROW;
}
}
pCsr->nAdvance = nMerge;
}while( rc==SQLITE_OK );
/* If there is a term specified to filter on, and this is not a prefix
** search, return now. The callback that corresponds to the required
** term (if such a term exists in the index) has already been made.
*/
if( pFilter->zTerm && !isPrefix ){
goto finished;
}
for(i=0; i<nMerge; i++){
rc = fts3SegReaderNext(p, apSegment[i]);
if( rc!=SQLITE_OK ) goto finished;
}
fts3SegReaderSort(apSegment, nSegment, nMerge, fts3SegReaderCmp);
}
finished:
sqlite3_free(aBuffer);
return rc;
}
void sqlite3Fts3SegReaderFinish(
Fts3SegReaderCursor *pCsr /* Cursor object */
){
if( pCsr ){
int i;
for(i=0; i<pCsr->nSegment; i++){
sqlite3Fts3SegReaderFree(pCsr->apSegment[i]);
}
sqlite3_free(pCsr->apSegment);
sqlite3_free(pCsr->aBuffer);
pCsr->nSegment = 0;
pCsr->apSegment = 0;
pCsr->aBuffer = 0;
}
}
/*
** Merge all level iLevel segments in the database into a single
** iLevel+1 segment. Or, if iLevel<0, merge all segments into a
@@ -2302,100 +2222,60 @@ int sqlite3Fts3SegReaderIterate(
** an SQLite error code is returned.
*/
static int fts3SegmentMerge(Fts3Table *p, int iLevel){
int i; /* Iterator variable */
int rc; /* Return code */
int iIdx; /* Index of new segment */
int iIdx = 0; /* Index of new segment */
int iNewLevel = 0; /* Level to create new segment at */
sqlite3_stmt *pStmt = 0;
SegmentWriter *pWriter = 0;
int nSegment = 0; /* Number of segments being merged */
Fts3SegReader **apSegment = 0; /* Array of Segment iterators */
Fts3SegReader *pPending = 0; /* Iterator for pending-terms */
SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */
Fts3SegFilter filter; /* Segment term filter condition */
Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */
if( iLevel<0 ){
rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr);
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
if( iLevel==FTS3_SEGCURSOR_ALL ){
/* This call is to merge all segments in the database to a single
** segment. The level of the new segment is equal to the the numerically
** greatest segment level currently present in the database. The index
** of the new segment is always 0.
*/
iIdx = 0;
rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pPending);
if( rc!=SQLITE_OK ) goto finished;
rc = fts3SegmentCountMax(p, &nSegment, &iNewLevel);
if( rc!=SQLITE_OK ) goto finished;
nSegment += (pPending!=0);
if( nSegment<=1 ){
return SQLITE_DONE;
** of the new segment is always 0. */
int nDummy; /* TODO: Remove this */
if( csr.nSegment==1 ){
rc = SQLITE_DONE;
goto finished;
}
rc = fts3SegmentCountMax(p, &nDummy, &iNewLevel);
}else{
/* This call is to merge all segments at level iLevel. Find the next
** available segment index at level iLevel+1. The call to
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
** a single iLevel+2 segment if necessary.
*/
** a single iLevel+2 segment if necessary. */
iNewLevel = iLevel+1;
rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx);
if( rc!=SQLITE_OK ) goto finished;
rc = fts3SegmentCount(p, iLevel, &nSegment);
if( rc!=SQLITE_OK ) goto finished;
}
assert( nSegment>0 );
if( rc!=SQLITE_OK ) goto finished;
assert( csr.nSegment>0 );
assert( iNewLevel>=0 );
/* Allocate space for an array of pointers to segment iterators. */
apSegment = (Fts3SegReader**)sqlite3_malloc(sizeof(Fts3SegReader *)*nSegment);
if( !apSegment ){
rc = SQLITE_NOMEM;
goto finished;
}
memset(apSegment, 0, sizeof(Fts3SegReader *)*nSegment);
/* Allocate a Fts3SegReader structure for each segment being merged. A
** Fts3SegReader stores the state data required to iterate through all
** entries on all leaves of a single segment.
*/
assert( SQL_SELECT_LEVEL+1==SQL_SELECT_ALL_LEVEL);
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL+(iLevel<0), &pStmt, 0);
if( rc!=SQLITE_OK ) goto finished;
sqlite3_bind_int(pStmt, 1, iLevel);
for(i=0; SQLITE_ROW==(sqlite3_step(pStmt)); i++){
rc = fts3SegReaderNew(pStmt, i, &apSegment[i]);
if( rc!=SQLITE_OK ){
goto finished;
}
}
rc = sqlite3_reset(pStmt);
if( pPending ){
apSegment[i] = pPending;
pPending = 0;
}
pStmt = 0;
if( rc!=SQLITE_OK ) goto finished;
memset(&filter, 0, sizeof(Fts3SegFilter));
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
filter.flags |= (iLevel<0 ? FTS3_SEGMENT_IGNORE_EMPTY : 0);
rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment,
&filter, fts3MergeCallback, (void *)&pWriter
);
if( rc!=SQLITE_OK ) goto finished;
filter.flags |= (iLevel==FTS3_SEGCURSOR_ALL ? FTS3_SEGMENT_IGNORE_EMPTY : 0);
rc = fts3DeleteSegdir(p, iLevel, apSegment, nSegment);
if( rc==SQLITE_OK ){
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
rc = sqlite3Fts3SegReaderStart(p, &csr, &filter);
while( SQLITE_OK==rc ){
rc = sqlite3Fts3SegReaderStep(p, &csr);
if( rc!=SQLITE_ROW ) break;
rc = fts3SegWriterAdd(p, &pWriter, 1,
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
}
if( rc!=SQLITE_OK ) goto finished;
assert( pWriter );
rc = fts3DeleteSegdir(p, iLevel, csr.apSegment, csr.nSegment);
if( rc!=SQLITE_OK ) goto finished;
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
finished:
fts3SegWriterFree(pWriter);
if( apSegment ){
for(i=0; i<nSegment; i++){
sqlite3Fts3SegReaderFree(apSegment[i]);
}
sqlite3_free(apSegment);
}
sqlite3Fts3SegReaderFree(pPending);
sqlite3_reset(pStmt);
sqlite3Fts3SegReaderFinish(&csr);
return rc;
}
@@ -2404,55 +2284,7 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){
** Flush the contents of pendingTerms to a level 0 segment.
*/
int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
int rc; /* Return Code */
int idx; /* Index of new segment created */
SegmentWriter *pWriter = 0; /* Used to write the segment */
Fts3SegReader *pReader = 0; /* Used to iterate through the hash table */
/* Allocate a SegReader object to iterate through the contents of the
** pending-terms table. If an error occurs, or if there are no terms
** in the pending-terms table, return immediately.
*/
rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pReader);
if( rc!=SQLITE_OK || pReader==0 ){
return rc;
}
/* Determine the next index at level 0. If level 0 is already full, this
** call may merge all existing level 0 segments into a single level 1
** segment.
*/
rc = fts3AllocateSegdirIdx(p, 0, &idx);
/* If no errors have occured, iterate through the contents of the
** pending-terms hash table using the Fts3SegReader iterator. The callback
** writes each term (along with its doclist) to the database via the
** SegmentWriter handle pWriter.
*/
if( rc==SQLITE_OK ){
void *c = (void *)&pWriter; /* SegReaderIterate() callback context */
Fts3SegFilter f; /* SegReaderIterate() parameters */
memset(&f, 0, sizeof(Fts3SegFilter));
f.flags = FTS3_SEGMENT_REQUIRE_POS;
rc = sqlite3Fts3SegReaderIterate(p, &pReader, 1, &f, fts3FlushCallback, c);
}
assert( pWriter || rc!=SQLITE_OK );
/* If no errors have occured, flush the SegmentWriter object to the
** database. Then delete the SegmentWriter and Fts3SegReader objects
** allocated by this function.
*/
if( rc==SQLITE_OK ){
rc = fts3SegWriterFlush(p, pWriter, 0, idx);
}
fts3SegWriterFree(pWriter);
sqlite3Fts3SegReaderFree(pReader);
if( rc==SQLITE_OK ){
sqlite3Fts3PendingTermsClear(p);
}
return rc;
return fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING);
}
/*
@@ -2619,7 +2451,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
if( !zVal ){
return SQLITE_NOMEM;
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
rc = fts3SegmentMerge(p, -1);
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL);
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
}else{
@@ -2877,7 +2709,7 @@ int sqlite3Fts3Optimize(Fts3Table *p){
int rc;
rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = fts3SegmentMerge(p, -1);
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
if( rc==SQLITE_OK ){
+3 -1
View File
@@ -247,6 +247,8 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
UBool res;
const UChar *zString = sqlite3_value_text16(apArg[1]);
(void)nArg; /* Unused parameter */
/* If the left hand side of the regexp operator is NULL,
** then the result is also NULL.
*/
@@ -475,7 +477,7 @@ int sqlite3IcuInit(sqlite3 *db){
int rc = SQLITE_OK;
int i;
for(i=0; rc==SQLITE_OK && i<(sizeof(scalars)/sizeof(struct IcuScalar)); i++){
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
struct IcuScalar *p = &scalars[i];
rc = sqlite3_create_function(
db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0
+9 -38
View File
@@ -1268,7 +1268,7 @@ static int rtreeFilter(
rc = SQLITE_NOMEM;
}else{
memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc);
assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 );
assert( (idxStr==0 && argc==0) || (int)strlen(idxStr)==argc*2 );
for(ii=0; ii<argc; ii++){
RtreeConstraint *p = &pCsr->aConstraint[ii];
p->op = idxStr[ii*2];
@@ -1353,7 +1353,7 @@ static int rtreeFilter(
*/
static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int rc = SQLITE_OK;
int ii, cCol;
int ii;
int iIdx = 0;
char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
@@ -1361,7 +1361,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
UNUSED_PARAMETER(tab);
assert( pIdxInfo->idxStr==0 );
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
@@ -1385,9 +1385,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
}
if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){
int j, opmsk;
static const unsigned char compatible[] = { 0, 0, 1, 1, 2, 2 };
u8 op = 0;
u8 op;
switch( p->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break;
@@ -1399,37 +1397,10 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
op = RTREE_MATCH;
break;
}
assert( op!=0 );
/* Make sure this particular constraint has not been used before.
** If it has been used before, ignore it.
**
** A <= or < can be used if there is a prior >= or >.
** A >= or > can be used if there is a prior < or <=.
** A <= or < is disqualified if there is a prior <=, <, or ==.
** A >= or > is disqualified if there is a prior >=, >, or ==.
** A == is disqualifed if there is any prior constraint.
*/
assert( compatible[RTREE_EQ & 7]==0 );
assert( compatible[RTREE_LT & 7]==1 );
assert( compatible[RTREE_LE & 7]==1 );
assert( compatible[RTREE_GT & 7]==2 );
assert( compatible[RTREE_GE & 7]==2 );
cCol = p->iColumn - 1 + 'a';
opmsk = compatible[op & 7];
for(j=0; j<iIdx; j+=2){
if( zIdxStr[j+1]==cCol && (compatible[zIdxStr[j] & 7] & opmsk)!=0 ){
op = 0;
break;
}
}
if( op ){
assert( iIdx<sizeof(zIdxStr)-1 );
zIdxStr[iIdx++] = op;
zIdxStr[iIdx++] = cCol;
pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
pIdxInfo->aConstraintUsage[ii].omit = 1;
}
zIdxStr[iIdx++] = op;
zIdxStr[iIdx++] = p->iColumn - 1 + 'a';
pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
pIdxInfo->aConstraintUsage[ii].omit = 1;
}
}
@@ -3133,7 +3104,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
int jj;
nodeGetCell(&tree, &node, ii, &cell);
sqlite3_snprintf(512-nCell,&zCell[nCell],"%d", cell.iRowid);
sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
nCell = strlen(zCell);
for(jj=0; jj<tree.nDim*2; jj++){
sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f);
+47
View File
@@ -106,4 +106,51 @@ do_eqp_test rtree6.2.5 {
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
}
do_execsql_test rtree6-3.1 {
CREATE VIRTUAL TABLE t3 USING rtree(id, x1, x2, y1, y2);
INSERT INTO t3 VALUES(NULL, 1, 1, 2, 2);
SELECT * FROM t3 WHERE
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5;
} {1 1.0 1.0 2.0 2.0}
do_test rtree6.3.2 {
rtree_strategy {
SELECT * FROM t3 WHERE
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
}
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
do_test rtree6.3.3 {
rtree_strategy {
SELECT * FROM t3 WHERE
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
}
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
do_execsql_test rtree6-3.4 {
SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1
} {}
do_execsql_test rtree6-3.5 {
SELECT * FROM t3 WHERE
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>1.1
} {}
finish_test
+34
View File
@@ -0,0 +1,34 @@
# 2011 March 2
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# Make sure the rtreenode() testing function can handle entries with
# 64-bit rowids.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return }
do_test rtreeB-1.1 {
db eval {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1);
INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0);
INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0);
INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0);
INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0);
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
SELECT rtreenode(2, data) FROM t1_node;
}
} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}}
finish_test
+11 -3
View File
@@ -53,7 +53,7 @@ TCCX += -I$(TOP)/ext/async
LIBOBJ+= alter.o analyze.o attach.o auth.o \
backup.o bitvec.o btmutex.o btree.o build.o \
callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \
fts3.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o fts3_write.o \
func.o global.o hash.o \
icu.o insert.o journal.o legacy.o loadext.o \
@@ -187,6 +187,7 @@ SRC += \
$(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3.h \
$(TOP)/ext/fts3/fts3Int.h \
$(TOP)/ext/fts3/fts3_aux.c \
$(TOP)/ext/fts3/fts3_expr.c \
$(TOP)/ext/fts3/fts3_hash.c \
$(TOP)/ext/fts3/fts3_hash.h \
@@ -236,6 +237,7 @@ TESTSRC = \
$(TOP)/src/test_demovfs.c \
$(TOP)/src/test_devsym.c \
$(TOP)/src/test_func.c \
$(TOP)/src/test_fuzzer.c \
$(TOP)/src/test_hexio.c \
$(TOP)/src/test_init.c \
$(TOP)/src/test_intarray.c \
@@ -252,9 +254,11 @@ TESTSRC = \
$(TOP)/src/test_server.c \
$(TOP)/src/test_stat.c \
$(TOP)/src/test_superlock.c \
$(TOP)/src/test_syscall.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_wholenumber.c \
$(TOP)/src/test_wsd.c
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
@@ -293,6 +297,7 @@ TESTSRC2 = \
$(TOP)/src/where.c \
parse.c \
$(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3_aux.c \
$(TOP)/ext/fts3/fts3_expr.c \
$(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \
@@ -462,6 +467,9 @@ fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR)
fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c
fts3_aux.o: $(TOP)/ext/fts3/fts3_aux.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_aux.c
fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c
@@ -531,8 +539,8 @@ test: testfixture$(EXE) sqlite3$(EXE)
# threadtest runs a few thread-safety tests that are implemented in C. This
# target is invoked by the releasetest.tcl script.
#
threadtest3$(EXE): sqlite3.c $(TOP)/test/threadtest3.c
$(TCCX) -O2 sqlite3.c $(TOP)/test/threadtest3.c \
threadtest3$(EXE): sqlite3.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c
$(TCCX) -O2 sqlite3.o $(TOP)/test/threadtest3.c \
-o threadtest3$(EXE) $(THREADLIB)
threadtest: threadtest3$(EXE)
+165 -144
View File
@@ -1,14 +1,11 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
C SQLite\sversion\s3.7.5\srelease\scandidate\s2
D 2011-01-28T17:03:50.592
C Version\s3.7.6.2
D 2011-04-17T17:25:17.173
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in de6498556d536ae60bb8bb10e8c1ba011448658c
F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION de8d3477dbf0d6cc226ccc6e046273627eb55fc5
F VERSION 7fbd89ecdebc63a53a5cedadb822b74f5ae42a24
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
@@ -25,7 +22,7 @@ F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 0eb10c03a6536d8e5ce52ab70fda0a152d8a3262 x
F configure eb34ce3c85e7658b2eab3439f0e5b5c10b166b13 x
F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
@@ -64,37 +61,39 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 28ada7d1c700e57b072b2c95d70565b05925fa46
F ext/fts3/fts3.c 5653c5654ac9b65bf3646af7e1d695c7e9b991a0
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h a6c69c1c5e2c8c19172ddff42d262c087dcd7337
F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e
F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf
F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93
F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295
F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2
F ext/fts3/fts3_snippet.c 196c5e6cde57bfc1907c2d60e9c29590e4f93fb6
F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
F ext/fts3/fts3_write.c 3eea26b9ca4219e1711b0db74fd5a9d448a6afbb
F ext/fts3/fts3_write.c 813495ed106eb9461044e3c0374f4db69b37eb09
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c 850e9a36567bbcce6bd85a4b68243cad8e3c2de2
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 05b293c85403cf39bb5af0e7c010b0cafeab5e47
F ext/rtree/rtree.c f5fa951eba03c41d292958064604a033021acdee
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
F ext/rtree/rtree5.test ce3d7ccae2cfd9d2e1052b462424964c9bdcda12
F ext/rtree/rtree6.test 309806a2a27ef5897d4dd6aee2e8006bf754cc22
F ext/rtree/rtree6.test 0b380bd9af93f3bc496eef42502a336f58949c1b
F ext/rtree/rtree7.test bcb647b42920b3b5d025846689147778485cc318
F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d
F ext/rtree/rtree9.test df9843d1a9195249c8d3b4ea6aedda2d5c73e9c2
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
F ext/rtree/rtreeB.test b1916a9cecb86b02529c4cc5a546e8d6e7ff10da
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0
@@ -102,7 +101,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk 05d0f3475dd331896bd607cfb45c5e21b94589ad
F main.mk bd4e376deea4704b2bd9c77a4e6f0fa3de25c495
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -115,157 +114,166 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c 6a0c176e64a34929a4436048066a84ef4f1445b3
F src/analyze.c a038162344265ac21dfb24b3fcc06c666ebb9c07
F src/attach.c 252c4f7e36cc219349451ed63e278c60e80b26f3
F src/alter.c 280f5c04b11b492703a342222b3de0a999445280
F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a
F src/attach.c 7f97ca76ef2453440170929531a9c778267c0830
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 6728d6d48d55b449af76a3e51c0808849cb32a2e
F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
F src/btree.c 9004c98fc576306eee4fc0562ffeb362ef53912c
F src/btree.h 10f9296bf4edf034f5adce921b7b4383a56a1c90
F src/btreeInt.h 20f73dc93b1eeb83afd7259fbc6bd7dcf2df7fe4
F src/build.c 00a327120d81ace6267e714ae8010c997d55de5d
F src/callback.c a1d1b1c9c85415dff013af033e2fed9c8382d33b
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c 6a9164af8a2ef4612ee30b253635a9bd8e5e1b1b
F src/btree.h 11753dd46597a20702bca8746cb4caa4486a82b5
F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3
F src/build.c f09c46c66a1e7668c6ee25c9a2518aaa6842044c
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 7deec4534f3b5a0c3b4a4cbadf809d321f64f9c4
F src/date.c 1548fdac51377e4e7833251de878b4058c148e1b
F src/delete.c 7ed8a8c8b5f748ece92df173d7e0f7810c899ebd
F src/expr.c 1810f3056b11de99cc10e24629edf00e5fbd3a75
F src/delete.c 7a24fcc9a31664d145acb97ce56b6d9f249a25e4
F src/expr.c e3cf0957c6b8faaaf7386a3bc69e53c0dc9705be
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 17950a28f28b23e8ad3feaac5fc88c324d2f600a
F src/func.c cb41f614edc43b00bfeb030f9768e80eaff47edd
F src/fkey.c a43ba8a005fb5efd1deeee06853e3a6120d46a91
F src/func.c 3a8cb2fb2de3e3aed7f39106daf4878d9d17fcce
F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
F src/insert.c a4995747c062256582a90b4f87f716e11b067050
F src/insert.c acfb89fe4a73d703e425e167bfcc72985f4299ae
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e
F src/main.c 6653e46db7ecb5a7449d8a12900147192f748b97
F src/malloc.c 92d59a007d7a42857d4e9454aa25b6b703286be1
F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85
F src/main.c a8571665d43ff18f89a49d47a281605ce5ea825e
F src/malloc.c 788f2ed928786dfe305b6783d551d6b1a9080976
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
F src/mem3.c 9b237d911ba9904142a804be727cc6664873f8a3
F src/mem5.c 6fe00f46997bebb690397cb029719f711e7640e3
F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f
F src/memjournal.c 0ebce851677a7ac035ba1512a7e65851b34530c6
F src/mutex.c 6949180803ff05a7d0e2b9334a95b4fb5a00e23f
F src/mutex.h fe2ef5e1c4dae531d5a544f9241f19c56d26803d
F src/mutex_noop.c d5cfbca87168c661a0b118cd8e329a908e453151
F src/mutex_os2.c 6a62583e374ba3ac1a3fcc0da2bfdac7d3942689
F src/mutex_os2.c 882d735098c07c8c6a5472b8dd66e19675fe117f
F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579
F src/mutex_w32.c 3ade5ae71449d1d023f0ebb3184c2ae6aa9307e4
F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 2e452c9f2ca507623ad351c33a8a8b27849b1863
F src/os_unix.c 1be46a35bad4bec5171e4de88aaff817260eb378
F src/os_win.c 9abdcdd925416d854eabb0996c96debd92abfef5
F src/pager.c b0fcbe3038fd08b111e1cf1deddd5f42418004d8
F src/pager.h 0ea59db2a33bc6c2c02cae34de33367e1effdf76
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
F src/os_unix.c 4389231d079f8e323d9e24db6658bfd31f6d27a2
F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845
F src/pager.c 055239dcdfe12b3f5d97f6f01f85da01e2d6d912
F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
F src/pcache1.c d548e31beafa792d1994b663a29a5303569efc4e
F src/pragma.c 8a6cd3c787f882fa44f6490d2411fc26839ce8f3
F src/prepare.c 395b3fab1b93f45b6aa194b23ebc201221c47b99
F src/printf.c df2ff3bb5409e8958136933342c46464fbd017e7
F src/pragma.c 49c90ab27a4339d4b5bc0b03c08cbcf20ed8d454
F src/prepare.c e64261559a3187698a3e7e6c8b001a4f4f98dab4
F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c 8a7ba246b0b4bb45df7fbc52681728a0e3deaaa7
F src/shell.c 83c6f0cc5a79a081c7b9ddfe4f557b47e0bad976
F src/sqlite.h.in 76955fcd1c5371268ecc8afe0ce6c49ea750ae38
F src/select.c 649a6f10f7eb7b52a5a28847773cb9968a828ae8
F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79
F src/sqlite.h.in 4d28db70c37a1b17942820308eb59f211140da43
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
F src/sqliteInt.h 45926deaf59b1ce3f55d21d5f91a8cecb6a7eb4c
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc
F src/sqliteInt.h ac8f3f5846275c634f6649969304a9e97f6f9854
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c 549859dc2c143f3deb6a92636a2d27973652c164
F src/test1.c 771407a49ae199241f0efb7055634e4a1899c026
F src/tclsqlite.c 501c9a200fd998a268be475be5858febc90b725b
F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc
F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
F src/test5.c e1a19845625144caf038031234a12185e40d315c
F src/test6.c c7256cc21d2409486d094277d5b017e8eced44ba
F src/test7.c d67f65d82815eb0b57699b122ca0145dbea55bbb
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
F src/test8.c 6b1d12912a04fe6fca8c45bb9c3ea022f4352228
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
F src/test_config.c 9f025a7f3686c94e82dc6d6bd3cbf0f89cd67487
F src/test_demovfs.c 0aed671636735116fc872c5b03706fd5612488b5
F src/test_config.c d536042f27226b4639f0f87d4795fd37428a9ddf
F src/test_demovfs.c 31050680fa6925b4f677cfd4fa965b5f19195e50
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c 13b582345fb1185a93e46c53310fae8547dcce20
F src/test_hexio.c 1237f000ec7a491009b1233f5c626ea71bce1ea2
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254
F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c 785edd54f963aefb3c1628124170a56697c68c70
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
F src/test_malloc.c fd6188b1501c0010fb4241ddc9f0d5ac402c688d
F src/test_multiplex.c 5990431a852aa21c9a67da748f23d2cf1e21f8fc
F src/test_multiplex.c fdabd793ee7a9642c5a8a470def2347144c46d05
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c
F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32
F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8
F src/test_quota.c b5576f17d701af461effd7ca1e71f0d100071192
F src/test_rtree.c 30c981837445a4e187ee850a49c4760d9642f7c3
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c 162c4ec0137a549c009bb9ecab550527743cfc5d
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0
F src/test_thread.c 361ae0a0f1cbf5a28ad0388a258b104017a370c0
F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86
F src/test_vfstrace.c 2265c9895f350c8d3c39b079998fbe7481505cc1
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
F src/trigger.c b8bedb9c0084ceb51a40f54fcca2ce048c8de852
F src/update.c 227e6cd512108b84f69421fc6c7aa1b83d60d6e0
F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
F src/util.c ab1c92426494f499f42b9e307537b03e923d75c1
F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f
F src/vdbe.c 5d310eaf1a4d8383602126fa82e01291ab7d3cf3
F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2
F src/vdbeInt.h 6e6f28e9bccc6c703dca1372fd661c57b5c15fb0
F src/vdbeapi.c 8e9324fd35eb70d0b5904bd1af40f2598744dc4d
F src/vdbeaux.c 33448d23b857654dd69ed2103611f5c733606f68
F src/vdbeblob.c 18955f0ee6b133cd08e1592010cb9a6b11e9984c
F src/vdbemem.c 411649a35686f54268ccabeda175322c4697f5a6
F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5
F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30
F src/wal.c dbca424f71678f663a286ab2a98f947af1d412a7
F src/wal.h c1aac6593a0b02b15dc625987e619edeab39292e
F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8
F src/update.c 81911be16ece3c3e7716aa18565b4814ec41f8b9
F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60
F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
F src/vdbe.c 05deeec6659f2579674a5e6510b3ada2a442f8d5
F src/vdbe.h 8a675fefdf7119441fe817c800a9a52440c2e797
F src/vdbeInt.h fe8f58d305e629fff02f61f655aca1d299f1f6ae
F src/vdbeapi.c e0e2672e0a96ae3f8575c8ecd02912a3e8a554a1
F src/vdbeaux.c 9ae5074b19bdff2d8806a278533956fb281510d5
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
F src/vtab.c b0abc931f95af94c9ffdf9f747eb191cda953123
F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794
F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c af069e6b53234118014dabfece96a9515b69d76b
F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 51756962d522e474338e9b2ebb26e7364d4aa125
F test/alter.test 15f9224868b290d6bf7a63f31437f31aee070636
F test/alter.test 4e47fb9ea59348b88fce4e8bb49de530128b104c
F test/alter2.test 75f731508f1bf27ba09a6075c66cd02216ba464b
F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07
F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/analyze.test c1eb87067fc16ece7c07e823d6395fd831b270c5
F test/analyze2.test 3bde8f0879d9c1f2df3af21fcf42e706d8ee1e43
F test/analyze3.test 820ddfb7591b49607fbaf77240c7955ac3cabb04
F test/analyze.test c8cb89e8736336f1f0646c8123e6028a14c7b55e
F test/analyze2.test 8f2b1534d43f5547ce9a6b736c021d4192c75be3
F test/analyze3.test d61f55d8b472fc6e713160b1e577f7a68e63f38b
F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
F test/analyze5.test 1de8d66b11aae5a1453aa042d62e834a476bac9c
F test/analyze6.test c125622a813325bba1b4999040ddc213773c2290
F test/analyze7.test 5508e7828164ea0b518ed219bed7320a481863d4
F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3
F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf
F test/attach.test ce9660e51768fab93cf129787be886c5d6c4fd81
F test/attach.test 2bb09073d7d5499127db00f50780766dcea913e1
F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
F test/attach3.test bd9830bc3a0d22ed1310c9bff6896927937017dc
F test/attach4.test 31f9eb0ca7bdbc393cc4657b877903a226a83d4b
F test/attachmalloc.test 1d5b821a676f7bf0b00d87cc106b78966789ba57
F test/auth.test 26cc6f219580191539bf335abe03e55e49310846
F test/auth.test b047105c32da7db70b842fd24056723125ecc2ff
F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
F test/autoinc.test 85ef3180a737e6580086a018c09c6f1a52759b46
@@ -273,12 +281,13 @@ F test/autoindex1.test 860fc83f4fefb0c68ad062afc3ff43faa1534fc4
F test/autovacuum.test bb7c0885e6f8f1d633045de48f2b66082162766d
F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
F test/backcompat.test 541314d69ec9db3e03630b7616696ddc5048efb1
F test/backcompat.test 0f23ff8d516acdf42f3d866a66d85306de2d02bc
F test/backup.test 004d3b78bffd990741ab50133ed4347c25c172b1
F test/backup2.test b7c69f937c912e85ac8a5dbd1e1cf290302b2d49
F test/backup_ioerr.test 1f012e692f42c0442ae652443258f70e9f20fa38
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
@@ -297,11 +306,11 @@ F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
F test/cache.test 754baab2f18089fc9bcba7afaeb4dc907c6c6de2
F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
F test/capi3.test 1945a2ba75e3f4c49d5beb8fc092115b6292d471
F test/capi3.test 5c1ea6c940f2d7c4d5af8ef1ec2f92a267d2e37a
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
F test/capi3c.test bea67403a5e37a4b33230ee4723e315a2ffb31e7
F test/capi3d.test cd36571f014f34bdc4421967f6453cbb597d5d16
F test/capi3e.test 4fda47388ddfbfe807987aa62f46fcbceec9327f
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
F test/cast.test 166951664a0b0a2e0f8fb5997a152490c6363932
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
@@ -320,17 +329,17 @@ F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
F test/conflict.test cabc41f7616675df71b4fddabca3bd5d9221915a
F test/corrupt.test 1a5bef8b2d178859af69814ecedcd37219a89968
F test/corrupt2.test 808a28d0ca3b97e9aa8c91cd2b485ea2700b76d1
F test/corrupt3.test a399dacccb91c732f6b071c913e70d195af8c058
F test/corrupt3.test e3006aaf579d2ed7f1b94bf4cc695d3c784fa5af
F test/corrupt4.test b963f9e01e0f92d15c76fb0747876fd4b96dc30a
F test/corrupt5.test c23da7bfb20917cc7fdbb13ee25c7cc4e9fffeff
F test/corrupt6.test 4e4161aef1f30b9f34582bb4142334b7f47eacae
F test/corrupt7.test a90caf89c7d7cb7893ea4d92529bd0c129317ee4
F test/corrupt8.test 48eb37ffb9a03bceada62219e2bd4c92f4b0cb75
F test/corrupt9.test fad0bc26a5c972580a8d763c62f24094f4e8ef25
F test/corruptA.test 38b4f81c16099f6d8fa8b37e188fde76b8243994
F test/corruptA.test 856ea7a2eb5c1c767abbdf02679ac6cb158e4643
F test/corruptB.test 44133515cf46c4d7bba691e3bcfa478080413af0
F test/corruptC.test 483aa35dadfd96bdf549e38d75ffc2942576477e
F test/corruptD.test 3ae6e2dc6e2226c6935a8a40d4b5ee3eba75f8c0
F test/corruptD.test 63a449a3146b460231535d04a409d14ce71795ad
F test/corruptE.test 7290b61145d954be549340e462ca84826d8a31a3
F test/count.test 454e1ce985c94d13efeac405ce54439f49336163
F test/crash.test 1b6ac8410689ff78028887f445062dc897c9ac89
@@ -356,11 +365,11 @@ F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
F test/e_createtable.test b8f5286879315d5b7f4cc5ead1afda4846f0c0bb
F test/e_createtable.test b40fc61bc4f1ad2a3c84590bd1d711507263d921
F test/e_delete.test 55d868b647acc091c261a10b9b0cb0ab660a6acb
F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a
F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d
F test/e_expr.test 4e004d1f5187d4bbc9ca3d55660a8d164dd59f4e
F test/e_expr.test 9e8b9790803df4de23c2d68d566959934a6179d4
F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a
F test/e_fts3.test 75bb0aee26384ef586165e21018a17f7cd843469
F test/e_insert.test 7390c2da39f16a134dc9a439144768c727757d2c
@@ -369,23 +378,25 @@ F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
F test/e_select.test bf385ae3aa0f014c4933ae66fd3e1302138493eb
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
F test/e_update.test 963d6876064e65f318d1c93aaed36a02b9b389bf
F test/e_vacuum.test 057cc29445746fc1d2542984ff0253d511a234bd
F test/e_vacuum.test 6c09c2af7f2f140518f371c5342100118f779dcf
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
F test/enc4.test 4b575ef09e0eff896e73bd24076f96c2aa6a42de
F test/eqp.test 69670e7919030f21de29fb99bf1d68f97aedcbdb
F test/eqp.test f14fadd76da53405e9885e2431cacf7191d83cdb
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
F test/exclusive.test 53e1841b422e554cecf0160f937c473d6d0e3062
F test/exclusive2.test b65264c3e76e1db6c6eda15c02000a40743f6541
F test/exclusive2.test 343d55130c12c67b8bf10407acec043a6c26c86b
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/expr.test 620a636cf7b7d4e5834a0b9d83a4da372e24a7b7
F test/exists.test 5e2d64b4eb5a9d08876599bdae2e1213d2d12e2a
F test/expr.test 19e8ac40313e2282a47b586d11c4892040990d3a
F test/fallocate.test 43dc34b8c24be6baffadc3b4401ee15710ce83c6
F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e
F test/filefmt.test f77c92141960b7933bc6691631d2ad62257ef40a
F test/filefmt.test f178cfc29501a14565954c961b226e61877dd32c
F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f
F test/fkey3.test 42f88d6048d8dc079e2a8cf7baad1cc1483a7620
F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49
F test/fkey_malloc.test a5ede29bd2f6e56dea78c3d43fb86dd696c068c8
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7
@@ -440,18 +451,21 @@ F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa
F test/fts3aux1.test 719c35cbbcc04dde8e5a54a6f69851a0af9ed1f2
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3corrupt.test d874ba27975aa8e5514bf58bf97b473404de0dbb
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
F test/fts3d.test 95fb3c862cbc4297c93fceb9a635543744e9ef52
F test/fts3defer.test d6cb0db9b5997ecf863d96ff419f83f8f2c87f4f
F test/fts3defer2.test da840efaedebfdd54293d04b36098e2d9872caa6
F test/fts3defer2.test 288bef6de15557319b8c12d476ebdc83688ef96c
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3fault.test f83e556465bb69dc8bc676339eca408dce4ca246
F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d
F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b
F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
@@ -468,6 +482,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c
F test/fuzzer1.test 3105b5a89a6cb0d475f0877debec942fe4143462
F test/hook.test f04c3412463f8ec117c1c704c74ca0f627ce733a
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d
@@ -480,12 +495,12 @@ F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test 453d1e490d8f5ad2c9b3a54282a0690d6ae56462
F test/incrvacuum2.test 9e22a794899c91b7d8c8e12eaacac8df249faafe
F test/incrvacuum2.test ae04573b73ad52179f56e194fff0fbe43b509d23
F test/incrvacuum_ioerr.test 57d2f5777ab13fa03b87b262a4ea1bad5cfc0291
F test/index.test df7c00c6edd9504ab71c83a9514f1c5ca0fa54d8
F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
F test/indexedby.test d7367c5a0e8ed8db642824a68126753e0808c706
F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
@@ -516,7 +531,7 @@ F test/jrnlmode3.test c6522b276ba315fd1416198de6fc1da9e72409fb
F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05
F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/like.test 0f64aeaed50b6e3ebaef3af0b3b8f894aed5acca
F test/like.test a47f52692aac96ba82508efba74819214cdebc17
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e
F test/loadext.test 0393ce12d9616aa87597dd0ec88181de181f6db0
@@ -532,7 +547,7 @@ F test/lock_common.tcl d279887a0ab16cdb6d935c1203e64113c5a000e9
F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2
F test/main.test 9d7bbfcc1b52c88ba7b2ba6554068ecf9939f252
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
F test/malloc.test 927e6c8668a1d48c23aa6189bda02aff5a1b83de
F test/malloc.test e56c9c3358da2c18385aea15a42dc970913986c2
F test/malloc3.test 4128b1e6ffa506103b278ad97af89174f310c7ca
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
F test/malloc5.test 4d16d1bb26d2deddd7c4f480deec341f9b2d0e22
@@ -552,15 +567,16 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
F test/malloc_common.tcl 660b82ab528521cc4a48ff6df05ca3b6a00d47c5
F test/malloc_common.tcl 50d0ed21eed0ae9548b58935bd29ac89a05a54fa
F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
F test/memsubsys1.test 679db68394a5692791737b150852173b3e2fea10
F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc
F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
F test/minmax3.test 66a60eb0f20281b0753249d347c5de0766954cee
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/misc1.test e56baf44656dd68d6475a4b44521045a60241e9b
F test/misc2.test a628db7b03e18973e5d446c67696b03de718c9fd
F test/misc3.test 72c5dc87a78e7865c5ec7a969fc572913dbe96b6
@@ -569,7 +585,7 @@ F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test 29032efcd3d826fbd409e2a7af873e7939f4a4e3
F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33
F test/multiplex.test 92a4839213fd8cba8b59f86d42b7a1da1857db39
F test/multiplex.test a88f3e2c16e567e72be7296195c59fbdd6a8d3d4
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test a44e04df1486fcfb02d32468cbcd3c8e1e433723
@@ -579,17 +595,18 @@ F test/notify3.test d60923e186e0900f4812a845fcdfd8eea096e33a
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
F test/pager1.test 7006a8b5dd3df1fe0d51d7da014333d7dc099778
F test/oserror.test 498d8337e9d15543eb7b004fef8594bf204ff43c
F test/pager1.test d8672fd0af5f4f9b99b06283d00f01547809bebe
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
F test/pagerfault.test 9de4d3e0c59970b4c6cb8dac511fa242f335d8a7
F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401
F test/pagerfault3.test 9b413f48a3e9a9a8c26968118f8db19fd7bfb8c7
F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
F test/permutations.test c0ce0f3b741dd92a6d4c2671dbacba4b92dd81eb
F test/permutations.test 5b2a4cb756ffb2407cb4743163668d1d769febb6
F test/pragma.test fdfc09067ea104a0c247a1a79d8093b56656f850
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
@@ -603,7 +620,7 @@ F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
F test/releasetest.tcl 627ccd04a113a193c375594bd5d6d051d8220658
F test/releasetest.tcl c0c0865f1dff08dde08a964ef49e83217ebedbf8
F test/rollback.test 1a83118ea6db4e7d8c10eaa63871b5e90502ffdc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287
@@ -659,26 +676,28 @@ F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 8468e057d8a5531ff99e504e77fcc585a0291bf2
F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
F test/syscall.test 707c95e4ab7863e13f1293c6b0c76bead30249b3
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
F test/table.test 04ba066432430657712d167ebf28080fe878d305
F test/tableapi.test 7262a8cbaa9965d429f1cbd2747edc185fa56516
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
F test/tester.tcl dafe0d30279f6d380d5d2a535781dda91b8cfc3f
F test/tester.tcl 6fa3d2f581b479a3a088b1b5b0d145e548ebe662
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
F test/thread004.test f51dfc3936184aaf73ee85f315224baad272a87f
F test/thread005.test bf5c374ca65dd89fd56c8fe511ccfb46875bda5e
F test/thread1.test 862dd006d189e8b0946935db17399dcac2f8ef91
F test/thread2.test e08034b83fe9693ade77049732518e5b3d2d700d
F test/thread_common.tcl 2aa6f2fdcd4d6e461169c3e5ca098eebf643b863
F test/thread1.test df115faa10a4ba1d456e9d4d9ec165016903eae4
F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46
F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
F test/threadtest3.c d6d209190c7110f9a7e6a8154bdc3260efdbf8b7
F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef
F test/tkt-02a8e81d44.test 58494de77be2cf249228ada3f313fa399821c6ab
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
@@ -691,6 +710,7 @@ F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
F test/tkt-5d863f876e.test 884072c2de496ddbb90c387c9ebc0d4f44a91b8e
F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
F test/tkt-5ee23731f.test 3581260f2a71e51db94e1506ba6b0f7311d002a9
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
@@ -698,11 +718,13 @@ F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
F test/tkt-94c04eaadb.test be5ea61cb04dfdc047d19b5c5a9e75fa3da67a7f
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-b72787b1.test e6b62b2b2785c04d0d698d6a603507e384165049
F test/tkt-cbd054fa6b.test f14f97ea43662e6f70c9e63287081e8be5d9d589
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6
F test/tkt-f3e5abed55.test 19fb59268da6f20a69a181b9c14154132d1c65e3
F test/tkt-f777251dc7a.test 6f24c053bc5cdb7e1e19be9a72c8887cf41d5e87
F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7
F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4
F test/tkt-fc62af4523.test 72825d3febdedcd5593a27989fc05accdbfc2bb4
F test/tkt1435.test f8c52c41de6e5ca02f1845f3a46e18e25cadac00
@@ -774,7 +796,7 @@ F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb
F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267
F test/tkt3793.test 754b73f0e6a9349c70dc57e522cf3247272ecd5d
F test/tkt3810.test 90fa0635dfa7da9680c8cd3513350a49b3a8ae12
F test/tkt3824.test 3da2f5c81b057e3ff355f5dfc9aa0cf0a92e0206
F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0
F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d
F test/tkt3838.test f956f0719b5f805b12dd1dbf19f19d298bacebc3
F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f
@@ -790,7 +812,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
F test/trace2.test 092bc2c5776272700450d60a36919921095bdc21
F test/trace2.test 0ce11265c83333d8f5beeca19e71ed93a88d386c
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
@@ -807,14 +829,17 @@ F test/triggerA.test eaf11a29db2a11967d2d4b49d37f92bce598194e
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 8a691ff6dd47df2e57395bbec4b62101fac0f363
F test/triggerD.test c6add3817351451e419f6ff9e9a259b02b6e2de7
F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e
F test/unordered.test c479d3027f9c4db05b44b83010735c6708abcc91
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09
F test/vacuum2.test 2165164ed2463816e8c4648d0a779a863ce1a76c
F test/vacuum2.test 91a84c9b08adfc4472097d2e8deb0150214e0e76
F test/vacuum3.test f39ad1428347c5808cd2da7578c470f186a4d0ce
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
@@ -837,18 +862,19 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
F test/wal.test 70227190e713b3e7eb2a7d5ec3510b66db01f327
F test/wal2.test 3de797854de175323e7351b5f2514a30d1ee1410
F test/wal3.test ac51126c36814bce334f66a0a4dbbfa56d429733
F test/wal.test 5617ad308bfdb8a8885220d8a261a6096a8d7e57
F test/wal2.test e561a8c6fdd1c2cd1876f3e39757934e7b7361f8
F test/wal3.test 5c396cc22497244d627306f4c1d360167353f8dd
F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30
F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b
F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8
F test/wal_common.tcl 895d76138043b86bdccf36494054bdabcf65837b
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4
F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0
F test/walcksum.test a37b36375c595e61bdb7e1ec49b5f0979b6fc7ce
F test/walcrash.test e763841551d6b23677ccb419797c1589dcbdbaf5
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
F test/walfault.test 81ed760def1c1573151d416b0d09178cf006f9fd
F test/walfault.test 58fce626359c9376fe35101b5c0f2df8040aa839
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c
F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317
@@ -857,14 +883,14 @@ F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
F test/where3.test 8ebedae552e13fc7f2b4e8df6cbe72a095347400
F test/where3.test 8e1175c7ef710c70502858fc4fb08d784b3620b9
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test aa4cfcd6f66e2a4ef87b6717327325bf4d547502
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
F test/where9.test 7ee38c3fd67e76789a6ec769f62f6433d3d4a5cf
F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
@@ -878,13 +904,14 @@ F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c.tcl e0db70c2c52b0e3d0867ca931229e5b90ffe7837
F tool/mksqlite3c.tcl 623e26cc8c83322e4151d3ad85ac69d41221bae8
F tool/mksqlite3h.tcl d76c226a5e8e1f3b5f6593bcabe5e98b3b1ec9ff
F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87
F tool/omittest.tcl 27d6f6e3b1e95aeb26a1c140e6eb57771c6d794a
F tool/omittest.tcl b1dd290c1596e0f31fd335160a74ec5dfea3df4a
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/shell1.test c31b0814a9c543db51ca0cc63edb5e77ea532303
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/shell1.test 7a389c6aaad05621be39501d6f8db410da464dcd
F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3
F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836
F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce
@@ -900,15 +927,9 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 682fe41efd3578e8c9abc7138b61f361c3adbe95
R 7fccaf9f7c013ddef8926255f6aeb773
P c429edf30accac12d5deef10e59cd31146036f11
R 050fbfdb8675b7eaafb0b1677e5953e7
U drh
Z 4223173e58f25d45d236e232b9c90989
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFNQvb6oxKgR168RlERAtOTAJ9BaewewKXL3RGZUy5ycaHwjeEmJACeOPbw
/JKqOugR+37RH7HnLCo9DBk=
=kgPx
-----END PGP SIGNATURE-----
Z 289c52539a2a59a211a989409cbc5426
+1 -1
View File
@@ -1 +1 @@
ed759d5a9edb3bba5f48f243df47be29e3fe8cd7
154ddbc17120be2915eb03edc52af1225eb7cb5e
+22 -6
View File
@@ -370,6 +370,22 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
#endif
}
/*
** Parameter zName is the name of a table that is about to be altered
** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
** If the table is a system table, this function leaves an error message
** in pParse->zErr (system tables may not be altered) and returns non-zero.
**
** Or, if zName is not a system table, zero is returned.
*/
static int isSystemTable(Parse *pParse, const char *zName){
if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
return 1;
}
return 0;
}
/*
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
** command.
@@ -420,14 +436,11 @@ void sqlite3AlterRenameTable(
/* Make sure it is not a system table being altered, or a reserved name
** that the table is being renamed to.
*/
if( sqlite3Strlen30(pTab->zName)>6
&& 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
){
sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName);
if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){
goto exit_rename_table;
}
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto exit_rename_table;
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto
exit_rename_table;
}
#ifndef SQLITE_OMIT_VIEW
@@ -759,6 +772,9 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
goto exit_begin_add_column;
}
if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){
goto exit_begin_add_column;
}
assert( pTab->addColOffset>0 );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+44 -27
View File
@@ -34,7 +34,8 @@ static void openStatTable(
Parse *pParse, /* Parsing context */
int iDb, /* The database we are looking in */
int iStatCur, /* Open the sqlite_stat1 table on this cursor */
const char *zWhere /* Delete entries associated with this table */
const char *zWhere, /* Delete entries for this table or index */
const char *zWhereType /* Either "tbl" or "idx" */
){
static const struct {
const char *zName;
@@ -79,7 +80,7 @@ static void openStatTable(
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
if( zWhere ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, zTab, zWhere
"DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere
);
}else{
/* The sqlite_stat[12] table already exists. Delete all rows. */
@@ -103,6 +104,7 @@ static void openStatTable(
static void analyzeOneTable(
Parse *pParse, /* Parser context */
Table *pTab, /* Table whose indices are to be analyzed */
Index *pOnlyIdx, /* If not NULL, only analyze this one index */
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
int iMem /* Available memory locations begin here */
){
@@ -113,8 +115,7 @@ static void analyzeOneTable(
int i; /* Loop counter */
int topOfLoop; /* The top of the loop */
int endOfLoop; /* The end of the loop */
int addr = 0; /* The address of an instruction */
int jZeroRows = 0; /* Jump from here if number of rows is zero */
int jZeroRows = -1; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
@@ -125,6 +126,7 @@ static void analyzeOneTable(
int regRowid = iMem++; /* Rowid for the inserted record */
#ifdef SQLITE_ENABLE_STAT2
int addr = 0; /* Instruction address */
int regTemp2 = iMem++; /* Temporary use register */
int regSamplerecno = iMem++; /* Index of next sample to record */
int regRecno = iMem++; /* Current sample index */
@@ -147,6 +149,7 @@ static void analyzeOneTable(
assert( sqlite3BtreeHoldsAllMutexes(db) );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 );
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
db->aDb[iDb].zName ) ){
@@ -160,9 +163,12 @@ static void analyzeOneTable(
iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol = pIdx->nColumn;
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
int nCol;
KeyInfo *pKey;
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
nCol = pIdx->nColumn;
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
if( iMem+1+(nCol*2)>pParse->nMem ){
pParse->nMem = iMem+1+(nCol*2);
}
@@ -319,7 +325,7 @@ static void analyzeOneTable(
** is never possible.
*/
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
if( jZeroRows==0 ){
if( jZeroRows<0 ){
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
}
for(i=0; i<nCol; i++){
@@ -345,10 +351,10 @@ static void analyzeOneTable(
VdbeComment((v, "%s", pTab->zName));
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno);
}else{
assert( jZeroRows>0 );
addr = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, jZeroRows);
jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
}
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
@@ -356,9 +362,7 @@ static void analyzeOneTable(
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
if( pParse->nMem<regRec ) pParse->nMem = regRec;
if( jZeroRows ){
sqlite3VdbeJumpHere(v, addr);
}
sqlite3VdbeJumpHere(v, jZeroRows);
}
/*
@@ -385,20 +389,22 @@ static void analyzeDatabase(Parse *pParse, int iDb){
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
pParse->nTab += 2;
openStatTable(pParse, iDb, iStatCur, 0);
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, iStatCur, iMem);
analyzeOneTable(pParse, pTab, 0, iStatCur, iMem);
}
loadAnalysis(pParse, iDb);
}
/*
** Generate code that will do an analysis of a single table in
** a database.
** a database. If pOnlyIdx is not NULL then it is a single index
** in pTab that should be analyzed.
*/
static void analyzeTable(Parse *pParse, Table *pTab){
static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
int iDb;
int iStatCur;
@@ -408,8 +414,12 @@ static void analyzeTable(Parse *pParse, Table *pTab){
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
pParse->nTab += 2;
openStatTable(pParse, iDb, iStatCur, pTab->zName);
analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1);
if( pOnlyIdx ){
openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx");
}else{
openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl");
}
analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1);
loadAnalysis(pParse, iDb);
}
@@ -431,6 +441,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
int i;
char *z, *zDb;
Table *pTab;
Index *pIdx;
Token *pTableName;
/* Read the database schema. If an error occurs, leave an error message
@@ -455,11 +466,12 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
}else{
z = sqlite3NameFromToken(db, pName1);
if( z ){
pTab = sqlite3LocateTable(pParse, 0, z, 0);
sqlite3DbFree(db, z);
if( pTab ){
analyzeTable(pParse, pTab);
if( (pIdx = sqlite3FindIndex(db, z, 0))!=0 ){
analyzeTable(pParse, pIdx->pTable, pIdx);
}else if( (pTab = sqlite3LocateTable(pParse, 0, z, 0))!=0 ){
analyzeTable(pParse, pTab, 0);
}
sqlite3DbFree(db, z);
}
}
}else{
@@ -469,11 +481,12 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
zDb = db->aDb[iDb].zName;
z = sqlite3NameFromToken(db, pTableName);
if( z ){
pTab = sqlite3LocateTable(pParse, 0, z, zDb);
sqlite3DbFree(db, z);
if( pTab ){
analyzeTable(pParse, pTab);
if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
analyzeTable(pParse, pIdx->pTable, pIdx);
}else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){
analyzeTable(pParse, pTab, 0);
}
sqlite3DbFree(db, z);
}
}
}
@@ -535,6 +548,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
if( pIndex==0 ) break;
pIndex->aiRowEst[i] = v;
if( *z==' ' ) z++;
if( memcmp(z, "unordered", 10)==0 ){
pIndex->bUnordered = 1;
break;
}
}
return 0;
}
@@ -589,9 +606,9 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
/* Clear any prior statistics */
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
+10 -6
View File
@@ -176,7 +176,9 @@ static void attachFunc(
case SQLITE_NULL:
/* No key specified. Use the key from the main database */
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
}
break;
}
}
@@ -200,7 +202,7 @@ static void attachFunc(
db->aDb[iDb].pBt = 0;
db->aDb[iDb].pSchema = 0;
}
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
db->nDb = iDb;
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
db->mallocFailed = 1;
@@ -272,7 +274,7 @@ static void detachFunc(
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
return;
detach_error:
@@ -312,9 +314,11 @@ static void codeAttach(
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pAuthArg ){
char *zAuthArg = pAuthArg->u.zToken;
if( NEVER(zAuthArg==0) ){
goto attach_end;
char *zAuthArg;
if( pAuthArg->op==TK_STRING ){
zAuthArg = pAuthArg->u.zToken;
}else{
zAuthArg = 0;
}
rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
if(rc!=SQLITE_OK ){
+24 -5
View File
@@ -219,6 +219,10 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
#ifdef SQLITE_HAS_CODEC
int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc);
int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
#endif
int rc = SQLITE_OK;
i64 iOff;
@@ -237,11 +241,22 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
#ifdef SQLITE_HAS_CODEC
/* Backup is not possible if the page size of the destination is changing
** a a codec is in use.
** and a codec is in use.
*/
if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){
rc = SQLITE_READONLY;
}
/* Backup is not possible if the number of bytes of reserve space differ
** between source and destination. If there is a difference, try to
** fix the destination to agree with the source. If that is not possible,
** then the backup cannot proceed.
*/
if( nSrcReserve!=nDestReserve ){
u32 newPgsz = nSrcPgsz;
rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve);
if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY;
}
#endif
/* This loop runs once for each destination page spanned by the source
@@ -401,7 +416,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
int nDestTruncate;
if( p->pDestDb ){
sqlite3ResetInternalSchema(p->pDestDb, 0);
sqlite3ResetInternalSchema(p->pDestDb, -1);
}
/* Set nDestTruncate to the final number of pages in the destination
@@ -488,7 +503,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest))
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
){
rc = SQLITE_DONE;
}
@@ -502,7 +517,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( bCloseTrans ){
TESTONLY( int rc2 );
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
assert( rc2==SQLITE_OK );
}
@@ -607,7 +622,11 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
** has been modified by a transaction on the source pager. Copy
** the new data into the backup.
*/
int rc = backupOnePage(p, iPage, aData);
int rc;
assert( p->pDestDb );
sqlite3_mutex_enter(p->pDestDb->mutex);
rc = backupOnePage(p, iPage, aData);
sqlite3_mutex_leave(p->pDestDb->mutex);
assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
if( rc!=SQLITE_OK ){
p->rc = rc;
+43 -114
View File
@@ -39,12 +39,13 @@ static void lockBtreeMutex(Btree *p){
** clear the p->locked boolean.
*/
static void unlockBtreeMutex(Btree *p){
BtShared *pBt = p->pBt;
assert( p->locked==1 );
assert( sqlite3_mutex_held(p->pBt->mutex) );
assert( sqlite3_mutex_held(pBt->mutex) );
assert( sqlite3_mutex_held(p->db->mutex) );
assert( p->db==p->pBt->db );
assert( p->db==pBt->db );
sqlite3_mutex_leave(p->pBt->mutex);
sqlite3_mutex_leave(pBt->mutex);
p->locked = 0;
}
@@ -185,30 +186,11 @@ void sqlite3BtreeLeaveCursor(BtCursor *pCur){
*/
void sqlite3BtreeEnterAll(sqlite3 *db){
int i;
Btree *p, *pLater;
Btree *p;
assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nDb; i++){
p = db->aDb[i].pBt;
assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db );
if( p && p->sharable ){
p->wantToLock++;
if( !p->locked ){
assert( p->wantToLock==1 );
while( p->pPrev ) p = p->pPrev;
/* Reason for ALWAYS: There must be at least on unlocked Btree in
** the chain. Otherwise the !p->locked test above would have failed */
while( p->locked && ALWAYS(p->pNext) ) p = p->pNext;
for(pLater = p->pNext; pLater; pLater=pLater->pNext){
if( pLater->locked ){
unlockBtreeMutex(pLater);
}
}
while( p ){
lockBtreeMutex(p);
p = p->pNext;
}
}
}
if( p ) sqlite3BtreeEnter(p);
}
}
void sqlite3BtreeLeaveAll(sqlite3 *db){
@@ -217,16 +199,18 @@ void sqlite3BtreeLeaveAll(sqlite3 *db){
assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nDb; i++){
p = db->aDb[i].pBt;
if( p && p->sharable ){
assert( p->wantToLock>0 );
p->wantToLock--;
if( p->wantToLock==0 ){
unlockBtreeMutex(p);
}
}
if( p ) sqlite3BtreeLeave(p);
}
}
/*
** Return true if a particular Btree requires a lock. Return FALSE if
** no lock is ever required since it is not sharable.
*/
int sqlite3BtreeSharable(Btree *p){
return p->sharable;
}
#ifndef NDEBUG
/*
** Return true if the current thread holds the database connection
@@ -251,97 +235,42 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
}
#endif /* NDEBUG */
/*
** Add a new Btree pointer to a BtreeMutexArray.
** if the pointer can possibly be shared with
** another database connection.
**
** The pointers are kept in sorted order by pBtree->pBt. That
** way when we go to enter all the mutexes, we can enter them
** in order without every having to backup and retry and without
** worrying about deadlock.
**
** The number of shared btrees will always be small (usually 0 or 1)
** so an insertion sort is an adequate algorithm here.
*/
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
int i, j;
BtShared *pBt;
if( pBtree==0 || pBtree->sharable==0 ) return;
#ifndef NDEBUG
{
for(i=0; i<pArray->nMutex; i++){
assert( pArray->aBtree[i]!=pBtree );
}
}
#endif
assert( pArray->nMutex>=0 );
assert( pArray->nMutex<ArraySize(pArray->aBtree)-1 );
pBt = pBtree->pBt;
for(i=0; i<pArray->nMutex; i++){
assert( pArray->aBtree[i]!=pBtree );
if( pArray->aBtree[i]->pBt>pBt ){
for(j=pArray->nMutex; j>i; j--){
pArray->aBtree[j] = pArray->aBtree[j-1];
}
pArray->aBtree[i] = pBtree;
pArray->nMutex++;
return;
}
}
pArray->aBtree[pArray->nMutex++] = pBtree;
}
/*
** Enter the mutex of every btree in the array. This routine is
** called at the beginning of sqlite3VdbeExec(). The mutexes are
** exited at the end of the same function.
** Return true if the correct mutexes are held for accessing the
** db->aDb[iDb].pSchema structure. The mutexes required for schema
** access are:
**
** (1) The mutex on db
** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt.
**
** If pSchema is not NULL, then iDb is computed from pSchema and
** db using sqlite3SchemaToIndex().
*/
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
int i;
for(i=0; i<pArray->nMutex; i++){
Btree *p = pArray->aBtree[i];
/* Some basic sanity checking */
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
assert( !p->locked || p->wantToLock>0 );
/* We should already hold a lock on the database connection */
assert( sqlite3_mutex_held(p->db->mutex) );
/* The Btree is sharable because only sharable Btrees are entered
** into the array in the first place. */
assert( p->sharable );
p->wantToLock++;
if( !p->locked ){
lockBtreeMutex(p);
}
}
int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){
Btree *p;
assert( db!=0 );
if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema);
assert( iDb>=0 && iDb<db->nDb );
if( !sqlite3_mutex_held(db->mutex) ) return 0;
if( iDb==1 ) return 1;
p = db->aDb[iDb].pBt;
assert( p!=0 );
return p->sharable==0 || p->locked==1;
}
#endif /* NDEBUG */
#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */
/*
** Leave the mutex of every btree in the group.
** The following are special cases for mutex enter routines for use
** in single threaded applications that use shared cache. Except for
** these two routines, all mutex operations are no-ops in that case and
** are null #defines in btree.h.
**
** If shared cache is disabled, then all btree mutex routines, including
** the ones below, are no-ops and are null #defines in btree.h.
*/
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
int i;
for(i=0; i<pArray->nMutex; i++){
Btree *p = pArray->aBtree[i];
/* Some basic sanity checking */
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
assert( p->locked );
assert( p->wantToLock>0 );
/* We should already hold a lock on the database connection */
assert( sqlite3_mutex_held(p->db->mutex) );
p->wantToLock--;
if( p->wantToLock==0 ){
unlockBtreeMutex(p);
}
}
}
#else
void sqlite3BtreeEnter(Btree *p){
p->pBt->db = p->db;
}
+38 -30
View File
@@ -1228,7 +1228,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
*/
top -= nByte;
put2byte(&data[hdr+5], top);
assert( top+nByte <= pPage->pBt->usableSize );
assert( top+nByte <= (int)pPage->pBt->usableSize );
*pIdx = top;
return SQLITE_OK;
}
@@ -1249,7 +1249,7 @@ static int freeSpace(MemPage *pPage, int start, int size){
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( start>=pPage->hdrOffset+6+pPage->childPtrSize );
assert( (start + size)<=pPage->pBt->usableSize );
assert( (start + size) <= (int)pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( size>=0 ); /* Minimum cell size is 4 */
@@ -1292,7 +1292,7 @@ static int freeSpace(MemPage *pPage, int start, int size){
while( (pbegin = get2byte(&data[addr]))>0 ){
int pnext, psize, x;
assert( pbegin>addr );
assert( pbegin<=pPage->pBt->usableSize-4 );
assert( pbegin <= (int)pPage->pBt->usableSize-4 );
pnext = get2byte(&data[pbegin]);
psize = get2byte(&data[pbegin+2]);
if( pbegin + psize + 3 >= pnext && pnext>0 ){
@@ -2127,7 +2127,6 @@ int sqlite3BtreeSyncDisabled(Btree *p){
return rc;
}
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
/*
** Change the default pages size and the number of reserved bytes per page.
** Or, if the page size has already been fixed, return SQLITE_READONLY
@@ -2182,6 +2181,7 @@ int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
/*
** Return the number of bytes of space at the end of every page that
** are intentually left unused. This is the "reserved" space that is
@@ -2381,7 +2381,7 @@ static int lockBtree(BtShared *pBt){
pageSize-usableSize);
return rc;
}
if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPageHeader>nPageFile ){
if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPage>nPageFile ){
rc = SQLITE_CORRUPT_BKPT;
goto page1_init_failed;
}
@@ -3160,10 +3160,21 @@ static void btreeEndTransaction(Btree *p){
** the rollback journal (which causes the transaction to commit) and
** drop locks.
**
** Normally, if an error occurs while the pager layer is attempting to
** finalize the underlying journal file, this function returns an error and
** the upper layer will attempt a rollback. However, if the second argument
** is non-zero then this b-tree transaction is part of a multi-file
** transaction. In this case, the transaction has already been committed
** (by deleting a master journal file) and the caller will ignore this
** functions return code. So, even if an error occurs in the pager layer,
** reset the b-tree objects internal state to indicate that the write
** transaction has been closed. This is quite safe, as the pager will have
** transitioned to the error state.
**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
int sqlite3BtreeCommitPhaseTwo(Btree *p){
int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
if( p->inTrans==TRANS_NONE ) return SQLITE_OK;
sqlite3BtreeEnter(p);
@@ -3178,7 +3189,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
assert( pBt->inTransaction==TRANS_WRITE );
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
if( rc!=SQLITE_OK ){
if( rc!=SQLITE_OK && bCleanup==0 ){
sqlite3BtreeLeave(p);
return rc;
}
@@ -3198,7 +3209,7 @@ int sqlite3BtreeCommit(Btree *p){
sqlite3BtreeEnter(p);
rc = sqlite3BtreeCommitPhaseOne(p, 0);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeCommitPhaseTwo(p);
rc = sqlite3BtreeCommitPhaseTwo(p, 0);
}
sqlite3BtreeLeave(p);
return rc;
@@ -4808,7 +4819,7 @@ static int allocateBtreePage(
goto end_allocate_page;
}
k = get4byte(&pTrunk->aData[4]);
k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */
if( k==0 && !searchList ){
/* The trunk has no leaves and the list is not being searched.
** So extract the trunk page itself and use it as the newly
@@ -4893,19 +4904,13 @@ static int allocateBtreePage(
u32 closest;
Pgno iPage;
unsigned char *aData = pTrunk->aData;
rc = sqlite3PagerWrite(pTrunk->pDbPage);
if( rc ){
goto end_allocate_page;
}
if( nearby>0 ){
u32 i;
int dist;
closest = 0;
dist = get4byte(&aData[8]) - nearby;
if( dist<0 ) dist = -dist;
dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
for(i=1; i<k; i++){
int d2 = get4byte(&aData[8+i*4]) - nearby;
if( d2<0 ) d2 = -d2;
int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
if( d2<dist ){
closest = i;
dist = d2;
@@ -4928,11 +4933,12 @@ static int allocateBtreePage(
TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
": %d more free pages\n",
*pPgno, closest+1, k, pTrunk->pgno, n-1));
rc = sqlite3PagerWrite(pTrunk->pDbPage);
if( rc ) goto end_allocate_page;
if( closest<k-1 ){
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
assert( sqlite3PagerIswriteable(pTrunk->pDbPage) );
noContent = !btreeGetHasContent(pBt, *pPgno);
rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
@@ -5001,6 +5007,7 @@ end_allocate_page:
}else{
*ppPage = 0;
}
assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) );
return rc;
}
@@ -5490,7 +5497,7 @@ static void insertCell(
/* The allocateSpace() routine guarantees the following two properties
** if it returns success */
assert( idx >= end+2 );
assert( idx+sz <= pPage->pBt->usableSize );
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
@@ -5533,7 +5540,8 @@ static void assemblePage(
assert( pPage->nOverflow==0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921);
assert( nCell>=0 && nCell<=(int)MX_CELL(pPage->pBt)
&& (int)MX_CELL(pPage->pBt)<=10921);
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
/* Check that the page has just been zeroed by zeroPage() */
@@ -5747,7 +5755,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
assert( pFrom->isInit );
assert( pFrom->nFree>=iToHdr );
assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize );
assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize );
/* Copy the b-tree node content from page pFrom to page pTo. */
iData = get2byte(&aFrom[iFromHdr+5]);
@@ -6014,7 +6022,7 @@ static int balance_nonroot(
pTemp = &aSpace1[iSpace1];
iSpace1 += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iSpace1<=pBt->pageSize );
assert( iSpace1 <= (int)pBt->pageSize );
memcpy(pTemp, apDiv[i], sz);
apCell[nCell] = pTemp+leafCorrection;
assert( leafCorrection==0 || leafCorrection==4 );
@@ -6179,9 +6187,7 @@ static int balance_nonroot(
}
}
if( minI>i ){
int t;
MemPage *pT;
t = apNew[i]->pgno;
pT = apNew[i];
apNew[i] = apNew[minI];
apNew[minI] = pT;
@@ -6260,7 +6266,7 @@ static int balance_nonroot(
}
iOvflSpace += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iOvflSpace<=pBt->pageSize );
assert( iOvflSpace <= (int)pBt->pageSize );
insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
if( rc!=SQLITE_OK ) goto balance_cleanup;
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
@@ -6705,7 +6711,7 @@ int sqlite3BtreeInsert(
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) );
assert( szNew<=MX_CELL_SIZE(pBt) );
assert( szNew <= MX_CELL_SIZE(pBt) );
idx = pCur->aiIdx[pCur->iPage];
if( loc==0 ){
u16 szOld;
@@ -6845,7 +6851,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
pCell = findCell(pLeaf, pLeaf->nCell-1);
nCell = cellSizePtr(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt)>=nCell );
assert( MX_CELL_SIZE(pBt) >= nCell );
allocateTempSpace(pBt);
pTmp = pBt->pTmpSpace;
@@ -7932,8 +7938,10 @@ int sqlite3BtreeIsInTrans(Btree *p){
**
** Return SQLITE_LOCKED if this or any other connection has an open
** transaction on the shared-cache the argument Btree is connected to.
**
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
*/
int sqlite3BtreeCheckpoint(Btree *p){
int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK;
if( p ){
BtShared *pBt = p->pBt;
@@ -7941,7 +7949,7 @@ int sqlite3BtreeCheckpoint(Btree *p){
if( pBt->inTransaction!=TRANS_NONE ){
rc = SQLITE_LOCKED;
}else{
rc = sqlite3PagerCheckpoint(pBt->pPager);
rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt);
}
sqlite3BtreeLeave(p);
}
@@ -7981,7 +7989,7 @@ int sqlite3BtreeIsInBackup(Btree *p){
**
** Just before the shared-btree is closed, the function passed as the
** xFree argument when the memory allocation was made is invoked on the
** blob of allocated memory. This function should not call sqlite3_free()
** blob of allocated memory. The xFree function should not call sqlite3_free()
** on the memory, the btree layer does that.
*/
void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
+6 -20
View File
@@ -39,18 +39,6 @@
typedef struct Btree Btree;
typedef struct BtCursor BtCursor;
typedef struct BtShared BtShared;
typedef struct BtreeMutexArray BtreeMutexArray;
/*
** This structure records all of the Btrees that need to hold
** a mutex before we enter sqlite3VdbeExec(). The Btrees are
** are placed in aBtree[] in order of aBtree[]->pBt. That way,
** we can always lock and unlock them all quickly.
*/
struct BtreeMutexArray {
int nMutex;
Btree *aBtree[SQLITE_MAX_ATTACHED+1];
};
int sqlite3BtreeOpen(
@@ -87,7 +75,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
int sqlite3BtreeCommitPhaseTwo(Btree*);
int sqlite3BtreeCommitPhaseTwo(Btree*, int);
int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*,int);
@@ -207,7 +195,7 @@ void sqlite3BtreeCursorList(Btree*);
#endif
#ifndef SQLITE_OMIT_WAL
int sqlite3BtreeCheckpoint(Btree*);
int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#endif
/*
@@ -224,30 +212,28 @@ void sqlite3BtreeCursorList(Btree*);
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
int sqlite3BtreeSharable(Btree*);
void sqlite3BtreeLeave(Btree*);
void sqlite3BtreeEnterCursor(BtCursor*);
void sqlite3BtreeLeaveCursor(BtCursor*);
void sqlite3BtreeLeaveAll(sqlite3*);
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*);
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*);
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*);
#ifndef NDEBUG
/* These routines are used inside assert() statements only. */
int sqlite3BtreeHoldsMutex(Btree*);
int sqlite3BtreeHoldsAllMutexes(sqlite3*);
int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
#endif
#else
# define sqlite3BtreeSharable(X) 0
# define sqlite3BtreeLeave(X)
# define sqlite3BtreeEnterCursor(X)
# define sqlite3BtreeLeaveCursor(X)
# define sqlite3BtreeLeaveAll(X)
# define sqlite3BtreeMutexArrayEnter(X)
# define sqlite3BtreeMutexArrayLeave(X)
# define sqlite3BtreeMutexArrayInsert(X,Y)
# define sqlite3BtreeHoldsMutex(X) 1
# define sqlite3BtreeHoldsAllMutexes(X) 1
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
#endif
+3 -3
View File
@@ -218,7 +218,7 @@
/* The following value is the maximum cell size assuming a maximum page
** size give above.
*/
#define MX_CELL_SIZE(pBt) (pBt->pageSize-8)
#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8))
/* The maximum number of cells on a single page of the database. This
** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself
@@ -336,7 +336,7 @@ struct BtLock {
** All fields in this structure are accessed under sqlite3.mutex.
** The pBt pointer itself may not be changed while there exists cursors
** in the referenced BtShared that point back to this Btree since those
** cursors have to do go through this Btree to find their BtShared and
** cursors have to go through this Btree to find their BtShared and
** they often do so without holding sqlite3.mutex.
*/
struct Btree {
@@ -426,7 +426,7 @@ struct BtShared {
u32 nPage; /* Number of pages in the database */
void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */
Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */
#ifndef SQLITE_OMIT_SHARED_CACHE
int nRef; /* Number of references to this structure */
+86 -19
View File
@@ -148,7 +148,7 @@ void sqlite3FinishCoding(Parse *pParse){
** on each used database.
*/
if( pParse->cookieGoto>0 ){
u32 mask;
yDbMask mask;
int iDb;
sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
@@ -156,7 +156,10 @@ void sqlite3FinishCoding(Parse *pParse){
sqlite3VdbeUsesBtree(v, iDb);
sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
if( db->init.busy==0 ){
sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
sqlite3VdbeAddOp3(v, OP_VerifyCookie,
iDb, pParse->cookieValue[iDb],
db->aDb[iDb].pSchema->iGeneration);
}
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -269,9 +272,12 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
int nName;
assert( zName!=0 );
nName = sqlite3Strlen30(zName);
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName);
if( p ) break;
}
@@ -331,11 +337,14 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
Index *p = 0;
int i;
int nName = sqlite3Strlen30(zName);
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
Schema *pSchema = db->aDb[j].pSchema;
assert( pSchema );
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
p = sqlite3HashFind(&pSchema->idxHash, zName, nName);
if( p ) break;
}
@@ -362,11 +371,13 @@ static void freeIndex(sqlite3 *db, Index *p){
void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
Index *pIndex;
int len;
Hash *pHash = &db->aDb[iDb].pSchema->idxHash;
Hash *pHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pHash = &db->aDb[iDb].pSchema->idxHash;
len = sqlite3Strlen30(zIdxName);
pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0);
if( pIndex ){
if( ALWAYS(pIndex) ){
if( pIndex->pTable->pIndex==pIndex ){
pIndex->pTable->pIndex = pIndex->pNext;
}else{
@@ -391,26 +402,42 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
** if there were schema changes during the transaction or if a
** schema-cookie mismatch occurs.
**
** If iDb==0 then reset the internal schema tables for all database
** files. If iDb>=1 then reset the internal schema for only the
** If iDb<0 then reset the internal schema tables for all database
** files. If iDb>=0 then reset the internal schema for only the
** single file indicated.
*/
void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
int i, j;
assert( iDb>=0 && iDb<db->nDb );
assert( iDb<db->nDb );
if( iDb==0 ){
sqlite3BtreeEnterAll(db);
if( iDb>=0 ){
/* Case 1: Reset the single schema identified by iDb */
Db *pDb = &db->aDb[iDb];
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
assert( pDb->pSchema!=0 );
sqlite3SchemaClear(pDb->pSchema);
/* If any database other than TEMP is reset, then also reset TEMP
** since TEMP might be holding triggers that reference tables in the
** other database.
*/
if( iDb!=1 ){
pDb = &db->aDb[1];
assert( pDb->pSchema!=0 );
sqlite3SchemaClear(pDb->pSchema);
}
return;
}
for(i=iDb; i<db->nDb; i++){
/* Case 2 (from here to the end): Reset all schemas for all attached
** databases. */
assert( iDb<0 );
sqlite3BtreeEnterAll(db);
for(i=0; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
if( pDb->pSchema ){
assert(i==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt)));
sqlite3SchemaFree(pDb->pSchema);
sqlite3SchemaClear(pDb->pSchema);
}
if( iDb>0 ) return;
}
assert( iDb==0 );
db->flags &= ~SQLITE_InternChanges;
sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
@@ -496,6 +523,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
TESTONLY ( Index *pOld = ) sqlite3HashInsert(
&pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0
);
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
assert( pOld==pIndex || pOld==0 );
}
freeIndex(db, pIndex);
@@ -530,6 +558,7 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
assert( db!=0 );
assert( iDb>=0 && iDb<db->nDb );
assert( zTabName );
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */
pDb = &db->aDb[iDb];
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName,
@@ -784,6 +813,9 @@ void sqlite3StartTable(
if( pTable ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
}
goto begin_table_error;
}
@@ -814,6 +846,7 @@ void sqlite3StartTable(
*/
#ifndef SQLITE_OMIT_AUTOINCREMENT
if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pTable->pSchema->pSeqTab = pTable;
}
#endif
@@ -1274,6 +1307,7 @@ void sqlite3ChangeCookie(Parse *pParse, int iDb){
int r1 = sqlite3GetTempReg(pParse);
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1);
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1);
sqlite3ReleaseTempReg(pParse, r1);
@@ -1381,7 +1415,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
zSep = zSep2;
identPut(zStmt, &k, pCol->zName);
assert( pCol->affinity-SQLITE_AFF_TEXT >= 0 );
assert( pCol->affinity-SQLITE_AFF_TEXT < sizeof(azType)/sizeof(azType[0]) );
assert( pCol->affinity-SQLITE_AFF_TEXT < ArraySize(azType) );
testcase( pCol->affinity==SQLITE_AFF_TEXT );
testcase( pCol->affinity==SQLITE_AFF_NONE );
testcase( pCol->affinity==SQLITE_AFF_NUMERIC );
@@ -1576,6 +1610,7 @@ void sqlite3EndTable(
*/
if( p->tabFlags & TF_Autoincrement ){
Db *pDb = &db->aDb[iDb];
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->pSeqTab==0 ){
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
@@ -1596,6 +1631,7 @@ void sqlite3EndTable(
if( db->init.busy ){
Table *pOld;
Schema *pSchema = p->pSchema;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName,
sqlite3Strlen30(p->zName),p);
if( pOld ){
@@ -1780,6 +1816,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
pSelTab->nCol = 0;
pSelTab->aCol = 0;
sqlite3DeleteTable(db, pSelTab);
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
pTable->pSchema->flags |= DB_UnresetViews;
}else{
pTable->nCol = 0;
@@ -1800,6 +1837,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
*/
static void sqliteViewResetAll(sqlite3 *db, int idx){
HashElem *i;
assert( sqlite3SchemaMutexHeld(db, idx, 0) );
if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
@@ -1833,10 +1871,13 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
** in order to be certain that we got the right one.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){
void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
HashElem *pElem;
Hash *pHash;
Db *pDb;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pDb = &db->aDb[iDb];
pHash = &pDb->pSchema->tblHash;
for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){
Table *pTab = sqliteHashData(pElem);
@@ -1962,6 +2003,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
if( noErr ) db->suppressErr--;
if( pTab==0 ){
if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
goto exit_drop_table;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -2210,6 +2252,7 @@ void sqlite3CreateForeignKey(
pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */
pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */
assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash,
pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey
);
@@ -2479,6 +2522,9 @@ Index *sqlite3CreateIndex(
if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){
if( !ifNotExist ){
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
}
goto exit_create_index;
}
@@ -2565,6 +2611,7 @@ Index *sqlite3CreateIndex(
pIndex->onError = (u8)onError;
pIndex->autoIndex = (u8)(pName==0);
pIndex->pSchema = db->aDb[iDb].pSchema;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
/* Check to see if we should honor DESC requests on index columns
*/
@@ -2694,6 +2741,7 @@ Index *sqlite3CreateIndex(
*/
if( db->init.busy ){
Index *p;
assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
pIndex->zName, sqlite3Strlen30(pIndex->zName),
pIndex);
@@ -2870,6 +2918,8 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
if( pIndex==0 ){
if( !ifExists ){
sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0);
}else{
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
}
pParse->checkSchema = 1;
goto exit_drop_index;
@@ -3442,12 +3492,13 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
}
if( iDb>=0 ){
sqlite3 *db = pToplevel->db;
int mask;
yDbMask mask;
assert( iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 || iDb==1 );
assert( iDb<SQLITE_MAX_ATTACHED+2 );
mask = 1<<iDb;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
mask = ((yDbMask)1)<<iDb;
if( (pToplevel->cookieMask & mask)==0 ){
pToplevel->cookieMask |= mask;
pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
@@ -3458,6 +3509,21 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
}
}
/*
** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each
** attached database. Otherwise, invoke it for the database named zDb only.
*/
void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
sqlite3 *db = pParse->db;
int i;
for(i=0; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){
sqlite3CodeVerifySchema(pParse, i);
}
}
}
/*
** Generate VDBE code that prepares for doing an operation that
** might change the database.
@@ -3474,7 +3540,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
sqlite3CodeVerifySchema(pParse, iDb);
pToplevel->writeMask |= 1<<iDb;
pToplevel->writeMask |= ((yDbMask)1)<<iDb;
pToplevel->isMultiWrite |= setStatement;
}
@@ -3574,6 +3640,7 @@ static void reindexDatabases(Parse *pParse, char const *zColl){
HashElem *k; /* For looping over tables in pDb */
Table *pTab; /* A table in the database */
assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */
for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
assert( pDb!=0 );
for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
+7 -4
View File
@@ -400,12 +400,12 @@ FuncDef *sqlite3FindFunction(
/*
** Free all resources held by the schema structure. The void* argument points
** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the
** pointer itself, it just cleans up subsiduary resources (i.e. the contents
** pointer itself, it just cleans up subsidiary resources (i.e. the contents
** of the schema hash tables).
**
** The Schema.cache_size variable is not cleared.
*/
void sqlite3SchemaFree(void *p){
void sqlite3SchemaClear(void *p){
Hash temp1;
Hash temp2;
HashElem *pElem;
@@ -427,7 +427,10 @@ void sqlite3SchemaFree(void *p){
sqlite3HashClear(&temp1);
sqlite3HashClear(&pSchema->fkeyHash);
pSchema->pSeqTab = 0;
pSchema->flags &= ~DB_SchemaLoaded;
if( pSchema->flags & DB_SchemaLoaded ){
pSchema->iGeneration++;
pSchema->flags &= ~DB_SchemaLoaded;
}
}
/*
@@ -437,7 +440,7 @@ void sqlite3SchemaFree(void *p){
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
Schema * p;
if( pBt ){
p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaFree);
p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
}else{
p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
}
+14 -5
View File
@@ -15,9 +15,18 @@
#include "sqliteInt.h"
/*
** Look up every table that is named in pSrc. If any table is not found,
** add an error message to pParse->zErrMsg and return NULL. If all tables
** are found, return a pointer to the last table.
** While a SrcList can in general represent multiple tables and subqueries
** (as in the FROM clause of a SELECT statement) in this case it contains
** the name of a single table, as one might find in an INSERT, DELETE,
** or UPDATE statement. Look up that table in the symbol table and
** return a pointer. Set an error message and return NULL if the table
** name is not found or if any other error occurs.
**
** The following fields are initialized appropriate in pSrc:
**
** pSrc->a[0].pTab Pointer to the Table object
** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one
**
*/
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
struct SrcList_item *pItem = pSrc->a;
@@ -536,7 +545,7 @@ void sqlite3GenerateRowDelete(
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
if( count ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}
}
@@ -627,7 +636,7 @@ int sqlite3GenerateIndexKey(
}
if( doMakeRec ){
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
}
sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
return regBase;
+17 -15
View File
@@ -92,7 +92,7 @@ Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr *pExpr, Token *pCollName){
CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
CollSeq *pColl = 0;
Expr *p = pExpr;
while( ALWAYS(p) ){
while( p ){
int op;
pColl = p->pColl;
if( pColl ) break;
@@ -389,6 +389,7 @@ Expr *sqlite3ExprAlloc(
if( op!=TK_INTEGER || pToken->z==0
|| sqlite3GetInt32(pToken->z, &iValue)==0 ){
nExtra = pToken->n+1;
assert( iValue>=0 );
}
}
pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra);
@@ -614,6 +615,8 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
*/
void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p==0 ) return;
/* Sanity check: Assert that the IntValue is non-negative if it exists */
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
sqlite3ExprDelete(db, p->pLeft);
sqlite3ExprDelete(db, p->pRight);
@@ -1198,16 +1201,17 @@ int sqlite3ExprIsConstantOrFunction(Expr *p){
*/
int sqlite3ExprIsInteger(Expr *p, int *pValue){
int rc = 0;
/* If an expression is an integer literal that fits in a signed 32-bit
** integer, then the EP_IntValue flag will have already been set */
assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0
|| sqlite3GetInt32(p->u.zToken, &rc)==0 );
if( p->flags & EP_IntValue ){
*pValue = p->u.iValue;
return 1;
}
switch( p->op ){
case TK_INTEGER: {
rc = sqlite3GetInt32(p->u.zToken, pValue);
assert( rc==0 );
break;
}
case TK_UPLUS: {
rc = sqlite3ExprIsInteger(p->pLeft, pValue);
break;
@@ -1222,13 +1226,6 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){
}
default: break;
}
if( rc ){
assert( ExprHasAnyProperty(p, EP_Reduced|EP_TokenOnly)
|| (p->flags2 & EP2_MallocedToken)==0 );
p->op = TK_INTEGER;
p->flags |= EP_IntValue;
p->u.iValue = *pValue;
}
return rc;
}
@@ -1953,6 +1950,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
Vdbe *v = pParse->pVdbe;
if( pExpr->flags & EP_IntValue ){
int i = pExpr->u.iValue;
assert( i>=0 );
if( negFlag ) i = -i;
sqlite3VdbeAddOp2(v, OP_Integer, i, iMem);
}else{
@@ -1963,7 +1961,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
if( c==0 || (c==2 && negFlag) ){
char *zV;
if( negFlag ){ value = -value; }
if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; }
zV = dup8bytes(v, (char*)&value);
sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
}else{
@@ -2347,7 +2345,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
assert( pExpr->u.zToken[0]!=0 );
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
if( pExpr->u.zToken[1]!=0 ){
sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0);
sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, P4_TRANSIENT);
}
break;
}
@@ -3251,6 +3249,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_IN: {
int destIfFalse = sqlite3VdbeMakeLabel(v);
int destIfNull = jumpIfNull ? dest : destIfFalse;
@@ -3259,6 +3258,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
sqlite3VdbeResolveLabel(v, destIfFalse);
break;
}
#endif
default: {
r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
@@ -3392,6 +3392,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_IN: {
if( jumpIfNull ){
sqlite3ExprCodeIN(pParse, pExpr, dest, dest);
@@ -3402,6 +3403,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
}
break;
}
#endif
default: {
r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
+2 -3
View File
@@ -398,7 +398,7 @@ static void fkLookupParent(
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
sqlite3ReleaseTempReg(pParse, regRec);
@@ -687,7 +687,6 @@ void sqlite3FkCheck(
int regNew /* New row data is stored here */
){
sqlite3 *db = pParse->db; /* Database handle */
Vdbe *v; /* VM to write code to */
FKey *pFKey; /* Used to iterate through FKs */
int iDb; /* Index of database containing pTab */
const char *zDb; /* Name of database containing pTab */
@@ -699,7 +698,6 @@ void sqlite3FkCheck(
/* If foreign-keys are disabled, this function is a no-op. */
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
v = sqlite3GetVdbe(pParse);
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
zDb = db->aDb[iDb].zName;
@@ -1156,6 +1154,7 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){
FKey *pFKey; /* Iterator variable */
FKey *pNext; /* Copy of pFKey->pNextFrom */
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
/* Remove the FK from the fkeyHash hash table. */
+2 -7
View File
@@ -1239,13 +1239,8 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
if( type==SQLITE_INTEGER ){
i64 v = sqlite3_value_int64(argv[0]);
p->rSum += v;
if( (p->approx|p->overflow)==0 ){
i64 iNewSum = p->iSum + v;
int s1 = (int)(p->iSum >> (sizeof(i64)*8-1));
int s2 = (int)(v >> (sizeof(i64)*8-1));
int s3 = (int)(iNewSum >> (sizeof(i64)*8-1));
p->overflow = ((s1&s2&~s3) | (~s1&~s2&s3))?1:0;
p->iSum = iNewSum;
if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){
p->overflow = 1;
}
}else{
p->rSum += sqlite3_value_double(argv[0]);
+6 -6
View File
@@ -123,7 +123,7 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
pTab->zColAff = zColAff;
}
sqlite3VdbeChangeP4(v, -1, pTab->zColAff, 0);
sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT);
}
/*
@@ -237,6 +237,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){
for(p = pParse->pAinc; p; p = p->pNext){
pDb = &db->aDb[p->iDb];
memId = p->regCtr;
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
@@ -287,6 +288,7 @@ void sqlite3AutoincrementEnd(Parse *pParse){
int memId = p->regCtr;
iRec = sqlite3GetTempReg(pParse);
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1);
j2 = sqlite3VdbeAddOp0(v, OP_Rewind);
@@ -465,7 +467,6 @@ void sqlite3Insert(
int regIns; /* Block of regs holding rowid+data being inserted */
int regRowid; /* registers holding insert rowid */
int regData; /* register holding first column to insert */
int regRecord; /* Holds the assemblied row record */
int regEof = 0; /* Register recording end of SELECT data */
int *aRegIdx = 0; /* One register allocated to each index */
@@ -794,7 +795,6 @@ void sqlite3Insert(
/* Allocate registers for holding the rowid of the new row,
** the content of the new row, and the assemblied row record.
*/
regRecord = ++pParse->nMem;
regRowid = regIns = pParse->nMem+1;
pParse->nMem += pTab->nCol + 1;
if( IsVirtual(pTab) ){
@@ -1188,7 +1188,7 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Fail: {
char *zMsg;
j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull,
sqlite3VdbeAddOp3(v, OP_HaltIfNull,
SQLITE_CONSTRAINT, onError, regData+i);
zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL",
pTab->zName, pTab->aCol[i].zName);
@@ -1328,7 +1328,7 @@ void sqlite3GenerateConstraintChecks(
}
sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
/* Find out what action to take in case there is an indexing conflict */
@@ -1468,7 +1468,7 @@ void sqlite3CompleteInsertion(
}
sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
if( !pParse->nested ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}
sqlite3VdbeChangeP5(v, pik_flags);
}
+5
View File
@@ -71,6 +71,11 @@
# define sqlite3_complete16 0
#endif
#ifdef SQLITE_OMIT_DECLTYPE
# define sqlite3_column_decltype16 0
# define sqlite3_column_decltype 0
#endif
#ifdef SQLITE_OMIT_PROGRESS_CALLBACK
# define sqlite3_progress_handler 0
#endif
+90 -18
View File
@@ -375,6 +375,13 @@ int sqlite3_config(int op, ...){
sqlite3GlobalConfig.nHeap = va_arg(ap, int);
sqlite3GlobalConfig.mnReq = va_arg(ap, int);
if( sqlite3GlobalConfig.mnReq<1 ){
sqlite3GlobalConfig.mnReq = 1;
}else if( sqlite3GlobalConfig.mnReq>(1<<12) ){
/* cap min request size at 2^12 */
sqlite3GlobalConfig.mnReq = (1<<12);
}
if( sqlite3GlobalConfig.pHeap==0 ){
/* If the heap pointer is NULL, then restore the malloc implementation
** back to NULL pointers too. This will cause the malloc to go
@@ -508,14 +515,42 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
va_start(ap, op);
switch( op ){
case SQLITE_DBCONFIG_LOOKASIDE: {
void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */
rc = setupLookaside(db, pBuf, sz, cnt);
break;
}
default: {
static const struct {
int op; /* The opcode */
u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
} aFlagOp[] = {
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
for(i=0; i<ArraySize(aFlagOp); i++){
if( aFlagOp[i].op==op ){
int onoff = va_arg(ap, int);
int *pRes = va_arg(ap, int*);
int oldFlags = db->flags;
if( onoff>0 ){
db->flags |= aFlagOp[i].mask;
}else if( onoff==0 ){
db->flags &= ~aFlagOp[i].mask;
}
if( oldFlags!=db->flags ){
sqlite3ExpirePreparedStatements(db);
}
if( pRes ){
*pRes = (db->flags & aFlagOp[i].mask)!=0;
}
rc = SQLITE_OK;
break;
}
}
break;
}
}
@@ -652,7 +687,8 @@ int sqlite3_close(sqlite3 *db){
}
sqlite3_mutex_enter(db->mutex);
sqlite3ResetInternalSchema(db, 0);
/* Force xDestroy calls on all virtual tables */
sqlite3ResetInternalSchema(db, -1);
/* If a transaction is open, the ResetInternalSchema() call above
** will not have called the xDisconnect() method on any virtual
@@ -695,7 +731,7 @@ int sqlite3_close(sqlite3 *db){
}
}
}
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
/* Tell the code in notify.c that the connection no longer holds any
** locks and does not require any further unlock-notify callbacks.
@@ -786,7 +822,7 @@ void sqlite3RollbackAll(sqlite3 *db){
if( db->flags&SQLITE_InternChanges ){
sqlite3ExpirePreparedStatements(db);
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
}
/* Any deferred constraint violations have now been resolved. */
@@ -855,7 +891,7 @@ static int sqliteDefaultBusyCallback(
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
static const u8 totals[] =
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 };
# define NDELAY (sizeof(delays)/sizeof(delays[0]))
# define NDELAY ArraySize(delays)
sqlite3 *db = (sqlite3 *)ptr;
int timeout = db->busyTimeout;
int delay, prior;
@@ -1340,19 +1376,33 @@ void *sqlite3_wal_hook(
#endif
}
/*
** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points
** to contains a zero-length string, all attached databases are
** checkpointed.
** Checkpoint database zDb.
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
int sqlite3_wal_checkpoint_v2(
sqlite3 *db, /* Database handle */
const char *zDb, /* Name of attached database (or NULL) */
int eMode, /* SQLITE_CHECKPOINT_* value */
int *pnLog, /* OUT: Size of WAL log in frames */
int *pnCkpt /* OUT: Total number of frames checkpointed */
){
#ifdef SQLITE_OMIT_WAL
return SQLITE_OK;
#else
int rc; /* Return code */
int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
/* Initialize the output variables to -1 in case an error occurs. */
if( pnLog ) *pnLog = -1;
if( pnCkpt ) *pnCkpt = -1;
assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE );
assert( SQLITE_CHECKPOINT_FULL<SQLITE_CHECKPOINT_RESTART );
assert( SQLITE_CHECKPOINT_PASSIVE+2==SQLITE_CHECKPOINT_RESTART );
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_RESTART ){
return SQLITE_MISUSE;
}
sqlite3_mutex_enter(db->mutex);
if( zDb && zDb[0] ){
iDb = sqlite3FindDbName(db, zDb);
@@ -1361,7 +1411,7 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
rc = SQLITE_ERROR;
sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb);
}else{
rc = sqlite3Checkpoint(db, iDb);
rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt);
sqlite3Error(db, rc, 0);
}
rc = sqlite3ApiExit(db, rc);
@@ -1370,6 +1420,16 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
#endif
}
/*
** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points
** to contains a zero-length string, all attached databases are
** checkpointed.
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0);
}
#ifndef SQLITE_OMIT_WAL
/*
** Run a checkpoint on database iDb. This is a no-op if database iDb is
@@ -1387,20 +1447,31 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are
** checkpointed. If an error is encountered it is returned immediately -
** no attempt is made to checkpoint any remaining databases.
**
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
*/
int sqlite3Checkpoint(sqlite3 *db, int iDb){
int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK; /* Return code */
int i; /* Used to iterate through attached dbs */
int bBusy = 0; /* True if SQLITE_BUSY has been encountered */
assert( sqlite3_mutex_held(db->mutex) );
assert( !pnLog || *pnLog==-1 );
assert( !pnCkpt || *pnCkpt==-1 );
for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt);
rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
pnLog = 0;
pnCkpt = 0;
if( rc==SQLITE_BUSY ){
bBusy = 1;
rc = SQLITE_OK;
}
}
}
return rc;
return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc;
}
#endif /* SQLITE_OMIT_WAL */
@@ -1653,8 +1724,8 @@ static const int aHardLimit[] = {
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
#endif
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>30
# error SQLITE_MAX_ATTACHED must be between 0 and 30
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62
# error SQLITE_MAX_ATTACHED must be between 0 and 62
#endif
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
@@ -1773,7 +1844,8 @@ static int openDatabase(
** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were
** dealt with in the previous code block. Besides these, the only
** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY,
** SQLITE_OPEN_READWRITE, and SQLITE_OPEN_CREATE. Silently mask
** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE,
** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask
** off all other flags.
*/
flags &= ~( SQLITE_OPEN_DELETEONCLOSE |
@@ -1812,7 +1884,7 @@ static int openDatabase(
db->autoCommit = 1;
db->nextAutovac = -1;
db->nextPagesize = 0;
db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex
db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger
#if SQLITE_DEFAULT_FILE_FORMAT<4
| SQLITE_LegacyFileFmt
#endif
+1 -1
View File
@@ -404,7 +404,7 @@ void sqlite3ScratchFree(void *p){
pSlot->pNext = mem0.pScratchFree;
mem0.pScratchFree = pSlot;
mem0.nScratchFree++;
assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch );
assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch );
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
sqlite3_mutex_leave(mem0.mutex);
}else{
+3 -2
View File
@@ -127,7 +127,7 @@ static SQLITE_WSD struct Mem5Global {
*/
u8 *aCtrl;
} mem5 = { 0 };
} mem5;
/*
** Access the static variable through a macro for SQLITE_OMIT_WSD
@@ -442,7 +442,7 @@ static int memsys5Roundup(int n){
*/
static int memsys5Log(int iValue){
int iLog;
for(iLog=0; (1<<iLog)<iValue; iLog++);
for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<<iLog)<iValue; iLog++);
return iLog;
}
@@ -473,6 +473,7 @@ static int memsys5Init(void *NotUsed){
zByte = (u8*)sqlite3GlobalConfig.pHeap;
assert( zByte!=0 ); /* sqlite3_config() does not allow otherwise */
/* boundaries on sqlite3GlobalConfig.mnReq are enforced in sqlite3_config() */
nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq);
mem5.szAtom = (1<<nMinLog);
while( (int)sizeof(Mem5Link)>mem5.szAtom ){
+63 -60
View File
@@ -31,11 +31,16 @@
struct sqlite3_mutex {
HMTX mutex; /* Mutex controlling the lock */
int id; /* Mutex type */
int nRef; /* Number of references */
TID owner; /* Thread holding this mutex */
#ifdef SQLITE_DEBUG
int trace; /* True to trace changes */
#endif
};
#define OS2_MUTEX_INITIALIZER 0,0,0,0
#ifdef SQLITE_DEBUG
#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 }
#else
#define SQLITE3_MUTEX_INITIALIZER { 0, 0 }
#endif
/*
** Initialize and deinitialize the mutex subsystem.
@@ -51,11 +56,14 @@ static int os2MutexEnd(void){ return SQLITE_OK; }
** to sqlite3_mutex_alloc() is one of these integer constants:
**
** <ul>
** <li> SQLITE_MUTEX_FAST 0
** <li> SQLITE_MUTEX_RECURSIVE 1
** <li> SQLITE_MUTEX_STATIC_MASTER 2
** <li> SQLITE_MUTEX_STATIC_MEM 3
** <li> SQLITE_MUTEX_STATIC_PRNG 4
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_LRU2
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -69,7 +77,7 @@ static int os2MutexEnd(void){ return SQLITE_OK; }
** might return such a mutex in response to SQLITE_MUTEX_FAST.
**
** The other allowed parameters to sqlite3_mutex_alloc() each return
** a pointer to a static preexisting mutex. Three static mutexes are
** a pointer to a static preexisting mutex. Six static mutexes are
** used by the current version of SQLite. Future versions of SQLite
** may add additional static mutexes. Static mutexes are for internal
** use by SQLite only. Applications that use SQLite mutexes should
@@ -99,13 +107,13 @@ static sqlite3_mutex *os2MutexAlloc(int iType){
}
default: {
static volatile int isInit = 0;
static sqlite3_mutex staticMutexes[] = {
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
static sqlite3_mutex staticMutexes[6] = {
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
};
if ( !isInit ){
APIRET rc;
@@ -151,9 +159,14 @@ static sqlite3_mutex *os2MutexAlloc(int iType){
** SQLite is careful to deallocate every mutex that it allocates.
*/
static void os2MutexFree(sqlite3_mutex *p){
if( p==0 ) return;
assert( p->nRef==0 );
#ifdef SQLITE_DEBUG
TID tid;
PID pid;
ULONG ulCount;
DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
assert( ulCount==0 );
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
DosCloseMutexSem( p->mutex );
sqlite3_free( p );
}
@@ -168,26 +181,29 @@ static int os2MutexHeld(sqlite3_mutex *p){
PID pid;
ULONG ulCount;
PTIB ptib;
if( p!=0 ) {
DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
} else {
DosGetInfoBlocks(&ptib, NULL);
tid = ptib->tib_ptib2->tib2_ultid;
}
return p==0 || (p->nRef!=0 && p->owner==tid);
DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) )
return 0;
DosGetInfoBlocks(&ptib, NULL);
return tid==ptib->tib_ptib2->tib2_ultid;
}
static int os2MutexNotheld(sqlite3_mutex *p){
TID tid;
PID pid;
ULONG ulCount;
PTIB ptib;
if( p!= 0 ) {
DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
} else {
DosGetInfoBlocks(&ptib, NULL);
tid = ptib->tib_ptib2->tib2_ultid;
}
return p==0 || p->nRef==0 || p->owner!=tid;
DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
if( ulCount==0 )
return 1;
DosGetInfoBlocks(&ptib, NULL);
return tid!=ptib->tib_ptib2->tib2_ultid;
}
static void os2MutexTrace(sqlite3_mutex *p, char *pAction){
TID tid;
PID pid;
ULONG ulCount;
DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount);
}
#endif
@@ -203,32 +219,21 @@ static int os2MutexNotheld(sqlite3_mutex *p){
** more than once, the behavior is undefined.
*/
static void os2MutexEnter(sqlite3_mutex *p){
TID tid;
PID holder1;
ULONG holder2;
if( p==0 ) return;
assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT);
DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2);
p->owner = tid;
p->nRef++;
#ifdef SQLITE_DEBUG
if( p->trace ) os2MutexTrace(p, "enter");
#endif
}
static int os2MutexTry(sqlite3_mutex *p){
int rc;
TID tid;
PID holder1;
ULONG holder2;
if( p==0 ) return SQLITE_OK;
int rc = SQLITE_BUSY;
assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR) {
DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2);
p->owner = tid;
p->nRef++;
if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) {
rc = SQLITE_OK;
} else {
rc = SQLITE_BUSY;
#ifdef SQLITE_DEBUG
if( p->trace ) os2MutexTrace(p, "try");
#endif
}
return rc;
}
@@ -239,16 +244,11 @@ static int os2MutexTry(sqlite3_mutex *p){
** is not currently allocated. SQLite will never do either.
*/
static void os2MutexLeave(sqlite3_mutex *p){
TID tid;
PID holder1;
ULONG holder2;
if( p==0 ) return;
assert( p->nRef>0 );
DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2);
assert( p->owner==tid );
p->nRef--;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
assert( os2MutexHeld(p) );
DosReleaseMutexSem(p->mutex);
#ifdef SQLITE_DEBUG
if( p->trace ) os2MutexTrace(p, "leave");
#endif
}
sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
@@ -263,6 +263,9 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
#ifdef SQLITE_DEBUG
os2MutexHeld,
os2MutexNotheld
#else
0,
0
#endif
};
+1 -1
View File
@@ -280,7 +280,7 @@ static int winMutexTry(sqlite3_mutex *p){
#endif
#ifdef SQLITE_DEBUG
if( rc==SQLITE_OK && p->trace ){
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
}
#endif
return rc;
+1003 -208
View File
File diff suppressed because it is too large Load Diff
+701 -327
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -2746,7 +2746,7 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
*/
int sqlite3_os_init(void){
static sqlite3_vfs winVfs = {
2, /* iVersion */
3, /* iVersion */
sizeof(winFile), /* szOsFile */
MAX_PATH, /* mxPathname */
0, /* pNext */
@@ -2765,6 +2765,9 @@ int sqlite3_os_init(void){
winCurrentTime, /* xCurrentTime */
winGetLastError, /* xGetLastError */
winCurrentTimeInt64, /* xCurrentTimeInt64 */
0, /* xSetSystemCall */
0, /* xGetSystemCall */
0, /* xNextSystemCall */
};
#ifndef SQLITE_OMIT_WAL
+50 -31
View File
@@ -2851,6 +2851,28 @@ static int readDbPage(PgHdr *pPg){
return rc;
}
/*
** Update the value of the change-counter at offsets 24 and 92 in
** the header and the sqlite version number at offset 96.
**
** This is an unconditional update. See also the pager_incr_changecounter()
** routine which only updates the change-counter if the update is actually
** needed, as determined by the pPager->changeCountDone state variable.
*/
static void pager_write_changecounter(PgHdr *pPg){
u32 change_counter;
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
put32bits(((char*)pPg->pData)+24, change_counter);
/* Also store the SQLite version number in bytes 96..99 and in
** bytes 92..95 store the change counter for which the version number
** is valid. */
put32bits(((char*)pPg->pData)+92, change_counter);
put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER);
}
#ifndef SQLITE_OMIT_WAL
/*
** This function is invoked once for each page that has already been
@@ -2921,34 +2943,11 @@ static int pagerRollbackWal(Pager *pPager){
return rc;
}
/*
** Update the value of the change-counter at offsets 24 and 92 in
** the header and the sqlite version number at offset 96.
**
** This is an unconditional update. See also the pager_incr_changecounter()
** routine which only updates the change-counter if the update is actually
** needed, as determined by the pPager->changeCountDone state variable.
*/
static void pager_write_changecounter(PgHdr *pPg){
u32 change_counter;
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
put32bits(((char*)pPg->pData)+24, change_counter);
/* Also store the SQLite version number in bytes 96..99 and in
** bytes 92..95 store the change counter for which the version number
** is valid. */
put32bits(((char*)pPg->pData)+92, change_counter);
put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER);
}
/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
** changed.
** changed.
**
** The list of pages passed into this routine is always sorted by page number.
** Hence, if page 1 appears anywhere on the list, it will be the first page.
@@ -2973,6 +2972,19 @@ static int pagerWalFrames(
}
#endif
if( isCommit ){
/* If a WAL transaction is being committed, there is no point in writing
** any pages with page numbers greater than nTruncate into the WAL file.
** They will never be read by any client. So remove them from the pDirty
** list here. */
PgHdr *p;
PgHdr **ppNext = &pList;
for(p=pList; (*ppNext = p); p=p->pDirty){
if( p->pgno<=nTruncate ) ppNext = &p->pDirty;
}
assert( pList );
}
if( pList->pgno==1 ) pager_write_changecounter(pList);
rc = sqlite3WalFrames(pPager->pWal,
pPager->pageSize, pList, nTruncate, isCommit, syncFlags
@@ -2985,6 +2997,7 @@ static int pagerWalFrames(
}
#ifdef SQLITE_CHECK_PAGES
pList = sqlite3PcacheDirtyList(pPager->pPCache);
for(p=pList; p; p=p->pDirty){
pager_set_pagehash(p);
}
@@ -6600,14 +6613,20 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
#ifndef SQLITE_OMIT_WAL
/*
** This function is called when the user invokes "PRAGMA checkpoint".
** This function is called when the user invokes "PRAGMA wal_checkpoint",
** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint()
** or wal_blocking_checkpoint() API functions.
**
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
*/
int sqlite3PagerCheckpoint(Pager *pPager){
int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK;
if( pPager->pWal ){
u8 *zBuf = (u8 *)pPager->pTmpSpace;
rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags,
pPager->pageSize, zBuf);
rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
pPager->xBusyHandler, pPager->pBusyHandlerArg,
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
pnLog, pnCkpt
);
}
return rc;
}
@@ -6635,8 +6654,8 @@ static int pagerExclusiveLock(Pager *pPager){
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
/* If the attempt to grab the pending lock failed, release the
** exclusive lock that may have been obtained instead. */
/* If the attempt to grab the exclusive lock failed, release the
** pending lock that may have been obtained instead. */
pagerUnlockDb(pPager, SHARED_LOCK);
}
+1 -1
View File
@@ -138,7 +138,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
int sqlite3PagerCheckpoint(Pager *pPager);
int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*);
int sqlite3PagerWalSupported(Pager *pPager);
int sqlite3PagerWalCallback(Pager *pPager);
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
+26 -9
View File
@@ -115,7 +115,7 @@ static int invalidateTempStorage(Parse *pParse){
}
sqlite3BtreeClose(db->aDb[1].pBt);
db->aDb[1].pBt = 0;
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
}
return SQLITE_OK;
}
@@ -384,11 +384,11 @@ void sqlite3Pragma(
sqlite3VdbeChangeP1(v, addr+1, iDb);
sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
}else{
int size = sqlite3Atoi(zRight);
if( size<0 ) size = -size;
int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3VdbeAddOp2(v, OP_Integer, size, 1);
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
@@ -691,11 +691,11 @@ void sqlite3Pragma(
*/
if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( !zRight ){
returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
}else{
int size = sqlite3Atoi(zRight);
if( size<0 ) size = -size;
int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
@@ -1112,6 +1112,7 @@ void sqlite3Pragma(
** Begin by filling registers 2, 3, ... with the root pages numbers
** for all tables and indices in the database.
*/
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pTbls = &db->aDb[i].pSchema->tblHash;
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
@@ -1177,7 +1178,7 @@ void sqlite3Pragma(
addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_STATIC);
sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT);
sqlite3VdbeJumpHere(v, addr+9);
sqlite3VdbeJumpHere(v, jmp2);
}
@@ -1207,7 +1208,7 @@ void sqlite3Pragma(
sqlite3VdbeJumpHere(v, addr+4);
sqlite3VdbeChangeP4(v, addr+6,
"wrong # of entries in index ", P4_STATIC);
sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_STATIC);
sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT);
}
}
}
@@ -1386,13 +1387,29 @@ void sqlite3Pragma(
#ifndef SQLITE_OMIT_WAL
/*
** PRAGMA [database.]wal_checkpoint
** PRAGMA [database.]wal_checkpoint = passive|full|restart
**
** Checkpoint the database.
*/
if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){
int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
int eMode = SQLITE_CHECKPOINT_PASSIVE;
if( zRight ){
if( sqlite3StrICmp(zRight, "full")==0 ){
eMode = SQLITE_CHECKPOINT_FULL;
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
eMode = SQLITE_CHECKPOINT_RESTART;
}
}
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeAddOp3(v, OP_Checkpoint, pId2->z?iDb:SQLITE_MAX_ATTACHED, 0, 0);
sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC);
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC);
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC);
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}else
/*
+6 -8
View File
@@ -34,7 +34,7 @@ static void corruptSchema(
"%s - %s", *pData->pzErrMsg, zExtra);
}
}
pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT;
pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT;
}
/*
@@ -141,7 +141,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
int meta[5];
InitData initData;
char const *zMasterSchema;
char const *zMasterName = SCHEMA_TABLE(iDb);
char const *zMasterName;
int openedTransaction = 0;
/*
@@ -278,9 +278,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
pDb->pSchema->enc = ENC(db);
if( pDb->pSchema->cache_size==0 ){
size = meta[BTREE_DEFAULT_CACHE_SIZE-1];
size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]);
if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; }
if( size<0 ) size = -size;
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
@@ -339,7 +338,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}
if( db->mallocFailed ){
rc = SQLITE_NOMEM;
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
}
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
/* Black magic: If the SQLITE_RecoveryMode flag is set, then consider
@@ -471,7 +470,9 @@ static void schemaIsValid(Parse *pParse){
** value stored as part of the in-memory schema representation,
** set Parse.rc to SQLITE_SCHEMA. */
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
sqlite3ResetInternalSchema(db, iDb);
pParse->rc = SQLITE_SCHEMA;
}
@@ -613,9 +614,6 @@ static int sqlite3Prepare(
if( pParse->checkSchema ){
schemaIsValid(pParse);
}
if( pParse->rc==SQLITE_SCHEMA ){
sqlite3ResetInternalSchema(db, 0);
}
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM;
}
+5 -1
View File
@@ -400,7 +400,11 @@ void sqlite3VXPrintf(
v = va_arg(ap,int);
}
if( v<0 ){
longvalue = -v;
if( v==SMALLEST_INT64 ){
longvalue = ((u64)1)<<63;
}else{
longvalue = -v;
}
prefix = '-';
}else{
longvalue = v;
+49 -12
View File
@@ -806,6 +806,22 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
}
}
/*
** Assign expression b to lvalue a. A second, no-op, version of this macro
** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code
** in sqlite3Select() to assign values to structure member variables that
** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the
** code with #ifndef directives.
*/
# define explainSetInteger(a, b) a = b
#else
/* No-op versions of the explainXXX() functions and macros. */
# define explainTempTable(y,z)
# define explainSetInteger(y,z)
#endif
#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT)
/*
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
@@ -837,21 +853,9 @@ static void explainComposite(
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
}
}
/*
** Assign expression b to lvalue a. A second, no-op, version of this macro
** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code
** in sqlite3Select() to assign values to structure member variables that
** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the
** code with #ifndef directives.
*/
# define explainSetInteger(a, b) a = b
#else
/* No-op versions of the explainXXX() functions and macros. */
# define explainTempTable(y,z)
# define explainComposite(v,w,x,y,z)
# define explainSetInteger(y,z)
#endif
/*
@@ -2652,6 +2656,9 @@ static void substSelect(
** appear as unmodified result columns in the outer query. But
** have other optimizations in mind to deal with that case.
**
** (21) The subquery does not use LIMIT or the outer query is not
** DISTINCT. (See ticket [752e1646fc]).
**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
@@ -2720,6 +2727,9 @@ static int flattenSubquery(
}
if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */
if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */
if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){
return 0; /* Restriction (21) */
}
/* OBSOLETE COMMENT 1:
** Restriction 3: If the subquery is a join, make sure the subquery is
@@ -3612,6 +3622,32 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
sqlite3ExprCacheClear(pParse);
}
/*
** Add a single OP_Explain instruction to the VDBE to explain a simple
** count(*) query ("SELECT count(*) FROM pTab").
*/
#ifndef SQLITE_OMIT_EXPLAIN
static void explainSimpleCount(
Parse *pParse, /* Parse context */
Table *pTab, /* Table being queried */
Index *pIdx /* Index used to optimize scan, or NULL */
){
if( pParse->explain==2 ){
char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)",
pTab->zName,
pIdx ? "USING COVERING INDEX " : "",
pIdx ? pIdx->zName : "",
pTab->nRowEst
);
sqlite3VdbeAddOp4(
pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
);
}
}
#else
# define explainSimpleCount(a,b,c)
#endif
/*
** Generate code for the SELECT statement given in the p argument.
**
@@ -4223,6 +4259,7 @@ int sqlite3Select(
}
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
explainSimpleCount(pParse, pTab, pBest);
}else
#endif /* SQLITE_OMIT_BTREECOUNT */
{
+160 -9
View File
@@ -71,6 +71,9 @@ extern int isatty();
#define isatty(x) 1
#endif
/* True if the timer is enabled */
static int enableTimer = 0;
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
#include <sys/time.h>
#include <sys/resource.h>
@@ -78,9 +81,6 @@ extern int isatty();
/* Saved resource information for the beginning of an operation */
static struct rusage sBegin;
/* True if the timer is enabled */
static int enableTimer = 0;
/*
** Begin timing an operation
*/
@@ -124,9 +124,6 @@ static FILETIME ftUserBegin;
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
static GETPROCTIMES getProcessTimesAddr = NULL;
/* True if the timer is enabled */
static int enableTimer = 0;
/*
** Check to see if we have timer support. Return 1 if necessary
** support found (or found previously).
@@ -419,6 +416,7 @@ struct callback_data {
** .explain ON */
char outfile[FILENAME_MAX]; /* Filename for *out */
const char *zDbFilename; /* name of the database file */
const char *zVfs; /* Name of VFS to use */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
};
@@ -1850,7 +1848,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
#endif
if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=1 ){
if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
const char *zFile = azArg[1];
if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){
fclose(p->pLog);
@@ -2171,12 +2169,136 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_free_table(azResult);
}else
if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){
static const struct {
const char *zCtrlName; /* Name of a test-control option */
int ctrlCode; /* Integer code for that option */
} aCtrl[] = {
{ "prng_save", SQLITE_TESTCTRL_PRNG_SAVE },
{ "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE },
{ "prng_reset", SQLITE_TESTCTRL_PRNG_RESET },
{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST },
{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL },
{ "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS },
{ "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE },
{ "assert", SQLITE_TESTCTRL_ASSERT },
{ "always", SQLITE_TESTCTRL_ALWAYS },
{ "reserve", SQLITE_TESTCTRL_RESERVE },
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS },
{ "iskeyword", SQLITE_TESTCTRL_ISKEYWORD },
{ "pghdrsz", SQLITE_TESTCTRL_PGHDRSZ },
{ "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC },
};
int testctrl = -1;
int rc = 0;
int i, n;
open_db(p);
/* convert testctrl text option to value. allow any unique prefix
** of the option name, or a numerical value. */
n = strlen30(azArg[1]);
for(i=0; i<(int)(sizeof(aCtrl)/sizeof(aCtrl[0])); i++){
if( strncmp(azArg[1], aCtrl[i].zCtrlName, n)==0 ){
if( testctrl<0 ){
testctrl = aCtrl[i].ctrlCode;
}else{
fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[i]);
testctrl = -1;
break;
}
}
}
if( testctrl<0 ) testctrl = atoi(azArg[1]);
if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){
fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]);
}else{
switch(testctrl){
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_OPTIMIZATIONS:
case SQLITE_TESTCTRL_RESERVE:
if( nArg==3 ){
int opt = (int)strtol(azArg[2], 0, 0);
rc = sqlite3_test_control(testctrl, p->db, opt);
printf("%d (0x%08x)\n", rc, rc);
} else {
fprintf(stderr,"Error: testctrl %s takes a single int option\n",
azArg[1]);
}
break;
/* sqlite3_test_control(int) */
case SQLITE_TESTCTRL_PRNG_SAVE:
case SQLITE_TESTCTRL_PRNG_RESTORE:
case SQLITE_TESTCTRL_PRNG_RESET:
case SQLITE_TESTCTRL_PGHDRSZ:
if( nArg==2 ){
rc = sqlite3_test_control(testctrl);
printf("%d (0x%08x)\n", rc, rc);
} else {
fprintf(stderr,"Error: testctrl %s takes no options\n", azArg[1]);
}
break;
/* sqlite3_test_control(int, uint) */
case SQLITE_TESTCTRL_PENDING_BYTE:
if( nArg==3 ){
unsigned int opt = (unsigned int)atoi(azArg[2]);
rc = sqlite3_test_control(testctrl, opt);
printf("%d (0x%08x)\n", rc, rc);
} else {
fprintf(stderr,"Error: testctrl %s takes a single unsigned"
" int option\n", azArg[1]);
}
break;
/* sqlite3_test_control(int, int) */
case SQLITE_TESTCTRL_ASSERT:
case SQLITE_TESTCTRL_ALWAYS:
if( nArg==3 ){
int opt = atoi(azArg[2]);
rc = sqlite3_test_control(testctrl, opt);
printf("%d (0x%08x)\n", rc, rc);
} else {
fprintf(stderr,"Error: testctrl %s takes a single int option\n",
azArg[1]);
}
break;
/* sqlite3_test_control(int, char *) */
#ifdef SQLITE_N_KEYWORD
case SQLITE_TESTCTRL_ISKEYWORD:
if( nArg==3 ){
const char *opt = azArg[2];
rc = sqlite3_test_control(testctrl, opt);
printf("%d (0x%08x)\n", rc, rc);
} else {
fprintf(stderr,"Error: testctrl %s takes a single char * option\n",
azArg[1]);
}
break;
#endif
case SQLITE_TESTCTRL_BITVEC_TEST:
case SQLITE_TESTCTRL_FAULT_INSTALL:
case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS:
case SQLITE_TESTCTRL_SCRATCHMALLOC:
default:
fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n",
azArg[1]);
break;
}
}
}else
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
open_db(p);
sqlite3_busy_timeout(p->db, atoi(azArg[1]));
}else
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg==2 ){
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0
&& nArg==2
){
enableTimer = booleanValue(azArg[1]);
}else
@@ -2363,7 +2485,9 @@ static int process_input(struct callback_data *p, FILE *in){
}
}
if( zSql ){
if( !_all_whitespace(zSql) ) fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
if( !_all_whitespace(zSql) ){
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
}
free(zSql);
}
free(zLine);
@@ -2499,6 +2623,10 @@ static const char zOptions[] =
" -stats print memory stats before each finalize\n"
" -nullvalue 'text' set text string for NULL values\n"
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE
" -vfstrace enable tracing of all VFS calls\n"
#endif
;
static void usage(int showDetail){
fprintf(stderr,
@@ -2583,6 +2711,25 @@ int main(int argc, char **argv){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#endif
#ifdef SQLITE_ENABLE_VFSTRACE
}else if( strcmp(argv[i],"-vfstrace")==0 ){
extern int vfstrace_register(
const char *zTraceName,
const char *zOldVfsName,
int (*xOut)(const char*,void*),
void *pOutArg,
int makeDefault
);
vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
#endif
}else if( strcmp(argv[i],"-vfs")==0 ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]);
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]);
exit(1);
}
}
}
if( i<argc ){
@@ -2691,6 +2838,10 @@ int main(int argc, char **argv){
stdin_is_interactive = 0;
}else if( strcmp(z,"-heap")==0 ){
i++;
}else if( strcmp(z,"-vfs")==0 ){
i++;
}else if( strcmp(z,"-vfstrace")==0 ){
i++;
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
usage(1);
}else{
+168 -33
View File
@@ -478,6 +478,8 @@ int sqlite3_exec(
#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */
/* Reserved: 0x00F00000 */
/*
** CAPI3REF: Device Characteristics
**
@@ -725,7 +727,7 @@ struct sqlite3_io_methods {
** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most
** VFSes do not need this signal and should silently ignore this opcode.
** Applications should not call [sqlite3_file_control()] with this
** opcode as doing so may disrupt the operation of the specilized VFSes
** opcode as doing so may disrupt the operation of the specialized VFSes
** that do require it.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -891,10 +893,23 @@ typedef struct sqlite3_mutex sqlite3_mutex;
** date and time if that method is available (if iVersion is 2 or
** greater and the function pointer is not NULL) and will fall back
** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
**
** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
** are not used by the SQLite core. These optional interfaces are provided
** by some VFSes to facilitate testing of the VFS code. By overriding
** system calls with functions under its control, a test program can
** simulate faults and error conditions that would otherwise be difficult
** or impossible to induce. The set of system calls that can be overridden
** varies from one VFS to another, and from one version of the same VFS to the
** next. Applications that use these interfaces must be prepared for any
** or all of these interfaces to be NULL or for their behavior to change
** from one release to the next. Applications must not attempt to access
** any of these methods if the iVersion of the VFS is less than 3.
*/
typedef struct sqlite3_vfs sqlite3_vfs;
typedef void (*sqlite3_syscall_ptr)(void);
struct sqlite3_vfs {
int iVersion; /* Structure version number (currently 2) */
int iVersion; /* Structure version number (currently 3) */
int szOsFile; /* Size of subclassed sqlite3_file */
int mxPathname; /* Maximum file pathname length */
sqlite3_vfs *pNext; /* Next registered VFS */
@@ -920,6 +935,13 @@ struct sqlite3_vfs {
int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
/*
** The methods above are in versions 1 and 2 of the sqlite_vfs object.
** Those below are for version 3 and greater.
*/
int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);
sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName);
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
** New fields may be appended in figure versions. The iVersion
** value will increment whenever this happens.
*/
@@ -1104,17 +1126,12 @@ int sqlite3_config(int, ...);
** The sqlite3_db_config() interface is used to make configuration
** changes to a [database connection]. The interface is similar to
** [sqlite3_config()] except that the changes apply to a single
** [database connection] (specified in the first argument). The
** sqlite3_db_config() interface should only be used immediately after
** the database connection is created using [sqlite3_open()],
** [sqlite3_open16()], or [sqlite3_open_v2()].
** [database connection] (specified in the first argument).
**
** The second argument to sqlite3_db_config(D,V,...) is the
** configuration verb - an integer code that indicates what
** aspect of the [database connection] is being configured.
** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE].
** New verbs are likely to be added in future releases of SQLite.
** Additional arguments depend on the verb.
** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code
** that indicates what aspect of the [database connection] is being configured.
** Subsequent arguments vary depending on the configuration verb.
**
** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if
** the call is considered successful.
@@ -1292,7 +1309,7 @@ struct sqlite3_mem_methods {
** <dt>SQLITE_CONFIG_SCRATCH</dt>
** <dd> ^This option specifies a static memory buffer that SQLite can use for
** scratch memory. There are three arguments: A pointer an 8-byte
** aligned memory buffer from which the scrach allocations will be
** aligned memory buffer from which the scratch allocations will be
** drawn, the size of each scratch allocation (sz),
** and the maximum number of scratch allocations (N). The sz
** argument must be a multiple of 16.
@@ -1339,7 +1356,9 @@ struct sqlite3_mem_methods {
** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
** allocator is engaged to handle all of SQLites memory allocation needs.
** The first pointer (the memory pointer) must be aligned to an 8-byte
** boundary or subsequent behavior of SQLite will be undefined.</dd>
** boundary or subsequent behavior of SQLite will be undefined.
** The minimum allocation size is capped at 2^12. Reasonable values
** for the minimum allocation size are 2^5 through 2^8.</dd>
**
** <dt>SQLITE_CONFIG_MUTEX</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
@@ -1442,7 +1461,7 @@ struct sqlite3_mem_methods {
** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
** pointer to an memory buffer to use for lookaside memory.
** pointer to a memory buffer to use for lookaside memory.
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
** may be NULL in which case SQLite will allocate the
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
@@ -1460,9 +1479,31 @@ struct sqlite3_mem_methods {
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
**
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. There should be two additional arguments.
** The first argument is an integer which is 0 to disable FK enforcement,
** positive to enable FK enforcement or negative to leave FK enforcement
** unchanged. The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether FK enforcement is off or on
** following this call. The second parameter may be a NULL pointer, in
** which case the FK enforcement setting is not reported back. </dd>
**
** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable triggers,
** positive to enable triggers or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
** </dl>
*/
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
/*
@@ -2064,7 +2105,7 @@ void sqlite3_randomness(int N, void *P);
/*
** CAPI3REF: Compile-Time Authorization Callbacks
**
** ^This routine registers a authorizer callback with a particular
** ^This routine registers an authorizer callback with a particular
** [database connection], supplied in the first argument.
** ^The authorizer callback is invoked as SQL statements are being compiled
** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
@@ -2666,7 +2707,7 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
**
** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
** and only if the [prepared statement] X makes no direct changes to
** the content of the database file.
**
@@ -2710,7 +2751,7 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
** whether or not it requires a protected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
** a mutex is held. A internal mutex is held for a protected
** a mutex is held. An internal mutex is held for a protected
** sqlite3_value object but no mutex is held for an unprotected
** sqlite3_value object. If SQLite is compiled to be single-threaded
** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
@@ -2934,7 +2975,9 @@ int sqlite3_column_count(sqlite3_stmt *pStmt);
** column number. ^The leftmost column is number 0.
**
** ^The returned string pointer is valid until either the [prepared statement]
** is destroyed by [sqlite3_finalize()] or until the next call to
** is destroyed by [sqlite3_finalize()] or until the statement is automatically
** reprepared by the first call to [sqlite3_step()] for a particular run
** or until the next call to
** sqlite3_column_name() or sqlite3_column_name16() on the same column.
**
** ^If sqlite3_malloc() fails during the processing of either routine
@@ -2960,7 +3003,9 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N);
** the database name, the _table_ routines return the table name, and
** the origin_ routines return the column name.
** ^The returned string is valid until the [prepared statement] is destroyed
** using [sqlite3_finalize()] or until the same information is requested
** using [sqlite3_finalize()] or until the statement is automatically
** reprepared by the first call to [sqlite3_step()] for a particular run
** or until the same information is requested
** again in a different encoding.
**
** ^The names returned are the original un-aliased names of the
@@ -3392,7 +3437,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
** are used to add SQL functions or aggregates or to redefine the behavior
** of existing SQL functions or aggregates. The only differences between
** these routines are the text encoding expected for
** the the second parameter (the name of the function being created)
** the second parameter (the name of the function being created)
** and the presence or absence of a destructor callback for
** the application data pointer.
**
@@ -3437,7 +3482,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
** callback only; NULL pointers must be passed as the xStep and xFinal
** parameters. ^An aggregate SQL function requires an implementation of xStep
** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
** SQL function or aggregate, pass NULL poiners for all three function
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL,
@@ -3871,7 +3916,7 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n);
** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin
** on an even byte address.
**
** ^The fourth argument, pArg, is a application data pointer that is passed
** ^The fourth argument, pArg, is an application data pointer that is passed
** through as the first argument to the collating function callback.
**
** ^The fifth argument, xCallback, is a pointer to the collating function.
@@ -3887,7 +3932,7 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n);
** by the eTextRep argument. The collating function must return an
** integer that is negative, zero, or positive
** if the first string is less than, equal to, or greater than the second,
** respectively. A collating function must alway return the same answer
** respectively. A collating function must always return the same answer
** given the same inputs. If two or more collating functions are registered
** to the same collation name (using different eTextRep values) then all
** must give an equivalent answer when invoked with equivalent strings.
@@ -4299,7 +4344,7 @@ int sqlite3_release_memory(int);
** <li> Memory accounting is disabled using a combination of the
** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
** <li> An alternative page cache implementation is specifed using
** <li> An alternative page cache implementation is specified using
** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...).
** <li> The page cache allocates from its own memory pool supplied
** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than
@@ -4520,7 +4565,7 @@ typedef struct sqlite3_module sqlite3_module;
** CAPI3REF: Virtual Table Object
** KEYWORDS: sqlite3_module {virtual table module}
**
** This structure, sometimes called a a "virtual table module",
** This structure, sometimes called a "virtual table module",
** defines the implementation of a [virtual tables].
** This structure consists mostly of methods for the module.
**
@@ -4832,7 +4877,7 @@ typedef struct sqlite3_blob sqlite3_blob;
** This is true if any column of the row is changed, even a column
** other than the one the BLOB handle is open on.)^
** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for
** a expired BLOB handle fail with an return code of [SQLITE_ABORT].
** an expired BLOB handle fail with a return code of [SQLITE_ABORT].
** ^(Changes written into a BLOB prior to the BLOB expiring are not
** rolled back by the expiration of the BLOB. Such changes will eventually
** commit if the transaction continues to completion.)^
@@ -5534,24 +5579,21 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
** <dd>This parameter returns the number malloc attempts that were
** satisfied using lookaside memory. Only the high-water value is meaningful;
** the current value is always zero.
** checked out.</dd>)^
** the current value is always zero.)^
**
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
** <dd>This parameter returns the number malloc attempts that might have
** been satisfied using lookaside memory but failed due to the amount of
** memory requested being larger than the lookaside slot size.
** Only the high-water value is meaningful;
** the current value is always zero.
** checked out.</dd>)^
** the current value is always zero.)^
**
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
** <dd>This parameter returns the number malloc attempts that might have
** been satisfied using lookaside memory but failed due to all lookaside
** memory already being in use.
** Only the high-water value is meaningful;
** the current value is always zero.
** checked out.</dd>)^
** the current value is always zero.)^
**
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
@@ -6250,9 +6292,102 @@ int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] can be used to cause this interface to be
** run whenever the WAL reaches a certain size threshold.
**
** See also: [sqlite3_wal_checkpoint_v2()]
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
/*
** CAPI3REF: Checkpoint a database
**
** Run a checkpoint operation on WAL database zDb attached to database
** handle db. The specific operation is determined by the value of the
** eMode parameter:
**
** <dl>
** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
** Checkpoint as many frames as possible without waiting for any database
** readers or writers to finish. Sync the db file if all frames in the log
** are checkpointed. This mode is the same as calling
** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.
**
** <dt>SQLITE_CHECKPOINT_FULL<dd>
** This mode blocks (calls the busy-handler callback) until there is no
** database writer and all readers are reading from the most recent database
** snapshot. It then checkpoints all frames in the log file and syncs the
** database file. This call blocks database writers while it is running,
** but not database readers.
**
** <dt>SQLITE_CHECKPOINT_RESTART<dd>
** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after
** checkpointing the log file it blocks (calls the busy-handler callback)
** until all readers are reading from the database file only. This ensures
** that the next client to write to the database file restarts the log file
** from the beginning. This call blocks database writers while it is running,
** but not database readers.
** </dl>
**
** If pnLog is not NULL, then *pnLog is set to the total number of frames in
** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to
** the total number of checkpointed frames (including any that were already
** checkpointed when this function is called). *pnLog and *pnCkpt may be
** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK.
** If no values are available because of an error, they are both set to -1
** before returning to communicate this to the caller.
**
** All calls obtain an exclusive "checkpoint" lock on the database file. If
** any other process is running a checkpoint operation at the same time, the
** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a
** busy-handler configured, it will not be invoked in this case.
**
** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive
** "writer" lock on the database file. If the writer lock cannot be obtained
** immediately, and a busy-handler is configured, it is invoked and the writer
** lock retried until either the busy-handler returns 0 or the lock is
** successfully obtained. The busy-handler is also invoked while waiting for
** database readers as described above. If the busy-handler returns 0 before
** the writer lock is obtained or while waiting for database readers, the
** checkpoint operation proceeds from that point in the same way as
** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
** without blocking any further. SQLITE_BUSY is returned in this case.
**
** If parameter zDb is NULL or points to a zero length string, then the
** specified operation is attempted on all WAL databases. In this case the
** values written to output parameters *pnLog and *pnCkpt are undefined. If
** an SQLITE_BUSY error is encountered when processing one or more of the
** attached WAL databases, the operation is still attempted on any remaining
** attached databases and SQLITE_BUSY is returned to the caller. If any other
** error occurs while processing an attached database, processing is abandoned
** and the error code returned to the caller immediately. If no error
** (SQLITE_BUSY or otherwise) is encountered while processing the attached
** databases, SQLITE_OK is returned.
**
** If database zDb is the name of an attached database that is not in WAL
** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If
** zDb is not NULL (or a zero length string) and is not the name of any
** attached database, SQLITE_ERROR is returned to the caller.
*/
int sqlite3_wal_checkpoint_v2(
sqlite3 *db, /* Database handle */
const char *zDb, /* Name of attached database (or NULL) */
int eMode, /* SQLITE_CHECKPOINT_* value */
int *pnLog, /* OUT: Size of WAL log in frames */
int *pnCkpt /* OUT: Total number of frames checkpointed */
);
/*
** CAPI3REF: Checkpoint operation parameters
**
** These constants can be used as the 3rd parameter to
** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()]
** documentation for additional information about the meaning and use of
** each of these values.
*/
#define SQLITE_CHECKPOINT_PASSIVE 0
#define SQLITE_CHECKPOINT_FULL 1
#define SQLITE_CHECKPOINT_RESTART 2
/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
+38 -7
View File
@@ -668,9 +668,24 @@ struct Db {
/*
** An instance of the following structure stores a database schema.
**
** Most Schema objects are associated with a Btree. The exception is
** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing.
** In shared cache mode, a single Schema object can be shared by multiple
** Btrees that refer to the same underlying BtShared object.
**
** Schema objects are automatically deallocated when the last Btree that
** references them is destroyed. The TEMP Schema is manually freed by
** sqlite3_close().
*
** A thread must be holding a mutex on the corresponding Btree in order
** to access Schema content. This implies that the thread must also be
** holding a mutex on the sqlite3 connection pointer that owns the Btree.
** For a TEMP Schema, on the connection mutex is required.
*/
struct Schema {
int schema_cookie; /* Database schema version number for this file */
int iGeneration; /* Generation counter. Incremented with each change */
Hash tblHash; /* All tables indexed by name */
Hash idxHash; /* All (named) indices indexed by name */
Hash trigHash; /* All triggers indexed by name */
@@ -924,6 +939,7 @@ struct sqlite3 {
#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */
#define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */
#define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */
#define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */
/*
** Bits of the sqlite3.flags field that are used by the
@@ -1182,7 +1198,7 @@ struct CollSeq {
** schema is shared, as the implementation often stores the database
** connection handle passed to it via the xConnect() or xCreate() method
** during initialization internally. This database connection handle may
** then used by the virtual table implementation to access real tables
** then be used by the virtual table implementation to access real tables
** within the database. So that they appear as part of the callers
** transaction, these accesses need to be made via the same database
** connection as that used to execute SQL operations on the virtual table.
@@ -1460,6 +1476,7 @@ struct Index {
int tnum; /* Page containing root of this index in database file */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
u8 bUnordered; /* Use this index for == or IN queries only */
char *zColAff; /* String defining the affinity of each column */
Index *pNext; /* The next index associated with the same table */
Schema *pSchema; /* Schema containing this index */
@@ -1623,7 +1640,7 @@ struct Expr {
u16 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
int iValue; /* Integer value if EP_IntValue */
int iValue; /* Non-negative integer value if EP_IntValue */
} u;
/* If the EP_TokenOnly flag is set in the Expr.flags mask, then no
@@ -2123,6 +2140,15 @@ struct TriggerPrg {
TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */
};
/*
** The yDbMask datatype for the bitmask of all attached databases.
*/
#if SQLITE_MAX_ATTACHED>30
typedef sqlite3_uint64 yDbMask;
#else
typedef unsigned int yDbMask;
#endif
/*
** An SQL parser context. A copy of this structure is passed through
** the parser and down into all the parser action routine in order to
@@ -2171,8 +2197,8 @@ struct Parse {
int iReg; /* Reg with value of this column. 0 means none. */
int lru; /* Least recently used entry has the smallest value */
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
u32 writeMask; /* Start a write transaction on these databases */
u32 cookieMask; /* Bitmask of schema verified databases */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
u8 isMultiWrite; /* True if statement may affect/insert multiple rows */
u8 mayAbort; /* True if statement may throw an ABORT exception */
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
@@ -2742,6 +2768,7 @@ void sqlite3PrngRestoreState(void);
void sqlite3PrngResetState(void);
void sqlite3RollbackAll(sqlite3*);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
void sqlite3BeginTransaction(Parse*, int);
void sqlite3CommitTransaction(Parse*);
void sqlite3RollbackTransaction(Parse*);
@@ -2903,6 +2930,10 @@ Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*);
int sqlite3CheckCollSeq(Parse *, CollSeq *);
int sqlite3CheckObjectName(Parse *, const char *);
void sqlite3VdbeSetChanges(sqlite3 *, int);
int sqlite3AddInt64(i64*,i64);
int sqlite3SubInt64(i64*,i64);
int sqlite3MulInt64(i64*,i64);
int sqlite3AbsInt32(int);
const void *sqlite3ValueText(sqlite3_value*, u8);
int sqlite3ValueBytes(sqlite3_value*, u8);
@@ -2927,7 +2958,7 @@ extern SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
extern int sqlite3PendingByte;
#endif
#endif
void sqlite3RootPageMoved(Db*, int, int);
void sqlite3RootPageMoved(sqlite3*, int, int, int);
void sqlite3Reindex(Parse*, Token*, Token*);
void sqlite3AlterFunctions(void);
void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
@@ -2954,7 +2985,7 @@ void sqlite3DefaultRowEst(Index*);
void sqlite3RegisterLikeFunctions(sqlite3*, int);
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
void sqlite3MinimumFileFormat(Parse*, int, int);
void sqlite3SchemaFree(void *);
void sqlite3SchemaClear(void *);
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
@@ -3041,7 +3072,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
int sqlite3TempInMemory(const sqlite3*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
const char *sqlite3JournalModename(int);
int sqlite3Checkpoint(sqlite3*, int);
int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
/* Declarations for functions in fkey.c. All of these are replaced by
+1 -1
View File
@@ -118,7 +118,7 @@
/*
** The maximum number of attached databases. This must be between 0
** and 30. The upper bound on 30 is because a 32-bit integer bitmap
** and 62. The upper bound on 62 is because a 64-bit integer bitmap
** is used internally to track attached databases.
*/
#ifndef SQLITE_MAX_ATTACHED
+2
View File
@@ -163,6 +163,7 @@ int sqlite3_db_status(
int i; /* Used to iterate through schemas */
int nByte = 0; /* Used to accumulate return value */
sqlite3BtreeEnterAll(db);
db->pnBytesFreed = &nByte;
for(i=0; i<db->nDb; i++){
Schema *pSchema = db->aDb[i].pSchema;
@@ -189,6 +190,7 @@ int sqlite3_db_status(
}
}
db->pnBytesFreed = 0;
sqlite3BtreeLeaveAll(db);
*pHighwater = 0;
*pCurrent = nByte;
+8 -2
View File
@@ -2468,7 +2468,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else{
pDb->zProfile = 0;
}
#ifndef SQLITE_OMIT_TRACE
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
if( pDb->zProfile ){
pDb->interp = interp;
sqlite3_profile(pDb->db, DbProfileHandler, pDb);
@@ -2652,7 +2652,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else{
pDb->zTrace = 0;
}
#ifndef SQLITE_OMIT_TRACE
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
if( pDb->zTrace ){
pDb->interp = interp;
sqlite3_trace(pDb->db, DbTraceHandler, pDb);
@@ -3581,6 +3581,9 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitequota_Init(Tcl_Interp*);
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
extern int SqlitetestSyscall_Init(Tcl_Interp*);
extern int Sqlitetestfuzzer_Init(Tcl_Interp*);
extern int Sqlitetestwholenumber_Init(Tcl_Interp*);
#ifdef SQLITE_ENABLE_ZIPVFS
extern int Zipvfs_Init(Tcl_Interp*);
@@ -3618,6 +3621,9 @@ static void init_all(Tcl_Interp *interp){
Sqlitequota_Init(interp);
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
SqlitetestSyscall_Init(interp);
Sqlitetestfuzzer_Init(interp);
Sqlitetestwholenumber_Init(interp);
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
+109
View File
@@ -4888,6 +4888,44 @@ static int file_control_chunksize_test(
return TCL_OK;
}
/*
** tclcmd: file_control_sizehint_test DB DBNAME SIZE
**
** This TCL command runs the sqlite3_file_control interface and
** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and
** SQLITE_SET_LOCKPROXYFILE verbs.
*/
static int file_control_sizehint_test(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3_int64 nSize; /* Hinted size */
char *zDb; /* Db name ("main", "temp" etc.) */
sqlite3 *db; /* Database handle */
int rc; /* file_control() return code */
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
|| Tcl_GetWideIntFromObj(interp, objv[3], &nSize)
){
return TCL_ERROR;
}
zDb = Tcl_GetString(objv[2]);
if( zDb[0]=='\0' ) zDb = NULL;
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize);
if( rc ){
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** tclcmd: file_control_lockproxy_test DB PWD
**
@@ -5186,6 +5224,73 @@ static int test_wal_checkpoint(
return TCL_OK;
}
/*
** tclcmd: sqlite3_wal_checkpoint_v2 db MODE ?NAME?
**
** This command calls the wal_checkpoint_v2() function with the specified
** mode argument (passive, full or restart). If present, the database name
** NAME is passed as the second argument to wal_checkpoint_v2(). If it the
** NAME argument is not present, a NULL pointer is passed instead.
**
** If wal_checkpoint_v2() returns any value other than SQLITE_BUSY or
** SQLITE_OK, then this command returns TCL_ERROR. The Tcl result is set
** to the error message obtained from sqlite3_errmsg().
**
** Otherwise, this command returns a list of three integers. The first integer
** is 1 if SQLITE_BUSY was returned, or 0 otherwise. The following two integers
** are the values returned via the output paramaters by wal_checkpoint_v2() -
** the number of frames in the log and the number of frames in the log
** that have been checkpointed.
*/
static int test_wal_checkpoint_v2(
ClientData clientData, /* Unused */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
char *zDb = 0;
sqlite3 *db;
int rc;
int eMode;
int nLog = -555;
int nCkpt = -555;
Tcl_Obj *pRet;
const char * aMode[] = { "passive", "full", "restart", 0 };
assert( SQLITE_CHECKPOINT_PASSIVE==0 );
assert( SQLITE_CHECKPOINT_FULL==1 );
assert( SQLITE_CHECKPOINT_RESTART==2 );
if( objc!=3 && objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?");
return TCL_ERROR;
}
if( objc==4 ){
zDb = Tcl_GetString(objv[3]);
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
|| Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode)
){
return TCL_ERROR;
}
rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt);
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
return TCL_ERROR;
}
pRet = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0));
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog));
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt));
Tcl_SetObjResult(interp, pRet);
return TCL_OK;
}
/*
** tclcmd: test_sqlite3_log ?SCRIPT?
*/
@@ -5541,6 +5646,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_lasterrno_test", file_control_lasterrno_test, 0 },
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
@@ -5572,8 +5678,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_unlock_notify", test_unlock_notify, 0 },
#endif
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
{ "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 },
{ "test_sqlite3_log", test_sqlite3_log, 0 },
#ifndef SQLITE_OMIT_EXPLAIN
{ "print_explain_query_plan", test_print_eqp, 0 },
#endif
};
static int bitmask_size = sizeof(Bitmask)*8;
int i;
+1 -1
View File
@@ -13,7 +13,7 @@
*/
#include "sqliteInt.h"
#include "tcl.h"
#if defined(SQLITE_OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE
#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
+1 -1
View File
@@ -20,7 +20,7 @@
** the SQLITE_SERVER option.
*/
#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE
SQLITE_OS_UNIX && SQLITE_THREADSAFE
#include <stdlib.h>
#include <string.h>
+6
View File
@@ -91,6 +91,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "mutex", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_MUTEX_NOOP
Tcl_SetVar2(interp, "sqlite_options", "mutex_noop", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "mutex_noop", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_ALTERTABLE
Tcl_SetVar2(interp, "sqlite_options", "altertable", "0", TCL_GLOBAL_ONLY);
#else
+3 -3
View File
@@ -115,7 +115,7 @@
** operations.
*/
#if !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX)
#if !defined(SQLITE_TEST) || SQLITE_OS_UNIX
#include <sqlite3.h>
@@ -637,14 +637,14 @@ sqlite3_vfs *sqlite3_demovfs(void){
return &demovfs;
}
#endif /* !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX) */
#endif /* !defined(SQLITE_TEST) || SQLITE_OS_UNIX */
#ifdef SQLITE_TEST
#include <tcl.h>
#ifdef SQLITE_OS_UNIX
#if SQLITE_OS_UNIX
static int register_demovfs(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+5
View File
@@ -149,8 +149,13 @@ static void test_destructor_count(
** arguments. It returns the text value returned by the sqlite3_errmsg16()
** API function.
*/
#ifndef SQLITE_OMIT_BUILTIN_TEST
void sqlite3BeginBenignMalloc(void);
void sqlite3EndBenignMalloc(void);
#else
#define sqlite3BeginBenignMalloc()
#define sqlite3EndBenignMalloc()
#endif
static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){
}
static void test_agg_errmsg16_final(sqlite3_context *ctx){
+944
View File
@@ -0,0 +1,944 @@
/*
** 2011 March 24
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** Code for demonstartion virtual table that generates variations
** on an input word at increasing edit distances from the original.
**
** A fuzzer virtual table is created like this:
**
** CREATE VIRTUAL TABLE temp.f USING fuzzer;
**
** The name of the new virtual table in the example above is "f".
** Note that all fuzzer virtual tables must be TEMP tables. The
** "temp." prefix in front of the table name is required when the
** table is being created. The "temp." prefix can be omitted when
** using the table as long as the name is unambiguous.
**
** Before being used, the fuzzer needs to be programmed by giving it
** character transformations and a cost associated with each transformation.
** Examples:
**
** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100);
**
** The above statement says that the cost of inserting a letter 'a' is
** 100. (All costs are integers. We recommend that costs be scaled so
** that the average cost is around 100.)
**
** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87);
**
** The above statement says that the cost of deleting a single letter
** 'b' is 87.
**
** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38);
** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40);
**
** This third example says that the cost of transforming the single
** letter "o" into the two-letter sequence "oe" is 38 and that the
** cost of transforming "oe" back into "o" is 40.
**
** After all the transformation costs have been set, the fuzzer table
** can be queried as follows:
**
** SELECT word, distance FROM f
** WHERE word MATCH 'abcdefg'
** AND distance<200;
**
** This first query outputs the string "abcdefg" and all strings that
** can be derived from that string by appling the specified transformations.
** The strings are output together with their total transformation cost
** (called "distance") and appear in order of increasing cost. No string
** is output more than once. If there are multiple ways to transform the
** target string into the output string then the lowest cost transform is
** the one that is returned. In the example, the search is limited to
** strings with a total distance of less than 200.
**
** It is important to put some kind of a limit on the fuzzer output. This
** can be either in the form of a LIMIT clause at the end of the query,
** or better, a "distance<NNN" constraint where NNN is some number. The
** running time and memory requirement is exponential in the value of NNN
** so you want to make sure that NNN is not too big. A value of NNN that
** is about twice the average transformation cost seems to give good results.
**
** The fuzzer table can be useful for tasks such as spelling correction.
** Suppose there is a second table vocabulary(w) where the w column contains
** all correctly spelled words. Let $word be a word you want to look up.
**
** SELECT vocabulary.w FROM f, vocabulary
** WHERE f.word MATCH $word
** AND f.distance<=200
** AND f.word=vocabulary.w
** LIMIT 20
**
** The query above gives the 20 closest words to the $word being tested.
** (Note that for good performance, the vocubulary.w column should be
** indexed.)
**
** A similar query can be used to find all words in the dictionary that
** begin with some prefix $prefix:
**
** SELECT vocabulary.w FROM f, vocabulary
** WHERE f.word MATCH $prefix
** AND f.distance<=200
** AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF')
** LIMIT 50
**
** This last query will show up to 50 words out of the vocabulary that
** match or nearly match the $prefix.
*/
#include "sqlite3.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Forward declaration of objects used by this implementation
*/
typedef struct fuzzer_vtab fuzzer_vtab;
typedef struct fuzzer_cursor fuzzer_cursor;
typedef struct fuzzer_rule fuzzer_rule;
typedef struct fuzzer_seen fuzzer_seen;
typedef struct fuzzer_stem fuzzer_stem;
/*
** Type of the "cost" of an edit operation. Might be changed to
** "float" or "double" or "sqlite3_int64" in the future.
*/
typedef int fuzzer_cost;
/*
** Each transformation rule is stored as an instance of this object.
** All rules are kept on a linked list sorted by rCost.
*/
struct fuzzer_rule {
fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
fuzzer_cost rCost; /* Cost of this transformation */
int nFrom, nTo; /* Length of the zFrom and zTo strings */
char *zFrom; /* Transform from */
char zTo[4]; /* Transform to (extra space appended) */
};
/*
** A stem object is used to generate variants. It is also used to record
** previously generated outputs.
**
** Every stem is added to a hash table as it is output. Generation of
** duplicate stems is suppressed.
**
** Active stems (those that might generate new outputs) are kepts on a linked
** list sorted by increasing cost. The cost is the sum of rBaseCost and
** pRule->rCost.
*/
struct fuzzer_stem {
char *zBasis; /* Word being fuzzed */
int nBasis; /* Length of the zBasis string */
const fuzzer_rule *pRule; /* Current rule to apply */
int n; /* Apply pRule at this character offset */
fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */
fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */
fuzzer_stem *pNext; /* Next stem in rCost order */
fuzzer_stem *pHash; /* Next stem with same hash on zBasis */
};
/*
** A fuzzer virtual-table object
*/
struct fuzzer_vtab {
sqlite3_vtab base; /* Base class - must be first */
char *zClassName; /* Name of this class. Default: "fuzzer" */
fuzzer_rule *pRule; /* All active rules in this fuzzer */
fuzzer_rule *pNewRule; /* New rules to add when last cursor expires */
int nCursor; /* Number of active cursors */
};
#define FUZZER_HASH 4001 /* Hash table size */
#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */
/* A fuzzer cursor object */
struct fuzzer_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_int64 iRowid; /* The rowid of the current word */
fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */
fuzzer_cost rLimit; /* Maximum cost of any term */
fuzzer_stem *pStem; /* Stem with smallest rCostX */
fuzzer_stem *pDone; /* Stems already processed to completion */
fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */
int mxQueue; /* Largest used index in aQueue[] */
char *zBuf; /* Temporary use buffer */
int nBuf; /* Bytes allocated for zBuf */
int nStem; /* Number of stems allocated */
fuzzer_rule nullRule; /* Null rule used first */
fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
};
/* Methods for the fuzzer module */
static int fuzzerConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
fuzzer_vtab *pNew;
int n;
if( strcmp(argv[1],"temp")!=0 ){
*pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]);
return SQLITE_ERROR;
}
n = strlen(argv[0]) + 1;
pNew = sqlite3_malloc( sizeof(*pNew) + n );
if( pNew==0 ) return SQLITE_NOMEM;
pNew->zClassName = (char*)&pNew[1];
memcpy(pNew->zClassName, argv[0], n);
sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)");
memset(pNew, 0, sizeof(*pNew));
*ppVtab = &pNew->base;
return SQLITE_OK;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */
static int fuzzerDisconnect(sqlite3_vtab *pVtab){
fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
assert( p->nCursor==0 );
do{
while( p->pRule ){
fuzzer_rule *pRule = p->pRule;
p->pRule = pRule->pNext;
sqlite3_free(pRule);
}
p->pRule = p->pNewRule;
p->pNewRule = 0;
}while( p->pRule );
sqlite3_free(p);
return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */
/*
** The two input rule lists are both sorted in order of increasing
** cost. Merge them together into a single list, sorted by cost, and
** return a pointer to the head of that list.
*/
static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
fuzzer_rule head;
fuzzer_rule *pTail;
pTail = &head;
while( pA && pB ){
if( pA->rCost<=pB->rCost ){
pTail->pNext = pA;
pTail = pA;
pA = pA->pNext;
}else{
pTail->pNext = pB;
pTail = pB;
pB = pB->pNext;
}
}
if( pA==0 ){
pTail->pNext = pB;
}else{
pTail->pNext = pA;
}
return head.pNext;
}
/*
** Open a new fuzzer cursor.
*/
static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
fuzzer_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
*ppCursor = &pCur->base;
if( p->nCursor==0 && p->pNewRule ){
unsigned int i;
fuzzer_rule *pX;
fuzzer_rule *a[15];
for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
while( (pX = p->pNewRule)!=0 ){
p->pNewRule = pX->pNext;
pX->pNext = 0;
for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
pX = fuzzerMergeRules(a[i], pX);
a[i] = 0;
}
a[i] = fuzzerMergeRules(a[i], pX);
}
for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
pX = fuzzerMergeRules(a[i], pX);
}
p->pRule = fuzzerMergeRules(p->pRule, pX);
}
p->nCursor++;
return SQLITE_OK;
}
/*
** Free all stems in a list.
*/
static void fuzzerClearStemList(fuzzer_stem *pStem){
while( pStem ){
fuzzer_stem *pNext = pStem->pNext;
sqlite3_free(pStem);
pStem = pNext;
}
}
/*
** Free up all the memory allocated by a cursor. Set it rLimit to 0
** to indicate that it is at EOF.
*/
static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){
int i;
fuzzerClearStemList(pCur->pStem);
fuzzerClearStemList(pCur->pDone);
for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]);
pCur->rLimit = (fuzzer_cost)0;
if( clearHash && pCur->nStem ){
pCur->mxQueue = 0;
pCur->pStem = 0;
pCur->pDone = 0;
memset(pCur->aQueue, 0, sizeof(pCur->aQueue));
memset(pCur->apHash, 0, sizeof(pCur->apHash));
}
pCur->nStem = 0;
}
/*
** Close a fuzzer cursor.
*/
static int fuzzerClose(sqlite3_vtab_cursor *cur){
fuzzer_cursor *pCur = (fuzzer_cursor *)cur;
fuzzerClearCursor(pCur, 0);
sqlite3_free(pCur->zBuf);
pCur->pVtab->nCursor--;
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
** Compute the current output term for a fuzzer_stem.
*/
static int fuzzerRender(
fuzzer_stem *pStem, /* The stem to be rendered */
char **pzBuf, /* Write results into this buffer. realloc if needed */
int *pnBuf /* Size of the buffer */
){
const fuzzer_rule *pRule = pStem->pRule;
int n;
char *z;
n = pStem->nBasis + pRule->nTo - pRule->nFrom;
if( (*pnBuf)<n+1 ){
(*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
if( (*pzBuf)==0 ) return SQLITE_NOMEM;
(*pnBuf) = n+100;
}
n = pStem->n;
z = *pzBuf;
if( n<0 ){
memcpy(z, pStem->zBasis, pStem->nBasis+1);
}else{
memcpy(z, pStem->zBasis, n);
memcpy(&z[n], pRule->zTo, pRule->nTo);
memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
pStem->nBasis-n-pRule->nFrom+1);
}
return SQLITE_OK;
}
/*
** Compute a hash on zBasis.
*/
static unsigned int fuzzerHash(const char *z){
unsigned int h = 0;
while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); }
return h % FUZZER_HASH;
}
/*
** Current cost of a stem
*/
static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){
return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost;
}
#if 0
/*
** Print a description of a fuzzer_stem on stderr.
*/
static void fuzzerStemPrint(
const char *zPrefix,
fuzzer_stem *pStem,
const char *zSuffix
){
if( pStem->n<0 ){
fprintf(stderr, "%s[%s](%d)-->self%s",
zPrefix,
pStem->zBasis, pStem->rBaseCost,
zSuffix
);
}else{
char *zBuf = 0;
int nBuf = 0;
if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return;
fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s",
zPrefix,
pStem->zBasis, pStem->rBaseCost, zBuf, pStem->,
zSuffix
);
sqlite3_free(zBuf);
}
}
#endif
/*
** Return 1 if the string to which the cursor is point has already
** been emitted. Return 0 if not. Return -1 on a memory allocation
** failures.
*/
static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
unsigned int h;
fuzzer_stem *pLookup;
if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
return -1;
}
h = fuzzerHash(pCur->zBuf);
pLookup = pCur->apHash[h];
while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
pLookup = pLookup->pHash;
}
return pLookup!=0;
}
/*
** Advance a fuzzer_stem to its next value. Return 0 if there are
** no more values that can be generated by this fuzzer_stem. Return
** -1 on a memory allocation failure.
*/
static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
const fuzzer_rule *pRule;
while( (pRule = pStem->pRule)!=0 ){
while( pStem->n < pStem->nBasis - pRule->nFrom ){
pStem->n++;
if( pRule->nFrom==0
|| memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
){
/* Found a rewrite case. Make sure it is not a duplicate */
int rc = fuzzerSeen(pCur, pStem);
if( rc<0 ) return -1;
if( rc==0 ){
fuzzerCost(pStem);
return 1;
}
}
}
pStem->n = -1;
pStem->pRule = pRule->pNext;
if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
}
return 0;
}
/*
** The two input stem lists are both sorted in order of increasing
** rCostX. Merge them together into a single list, sorted by rCostX, and
** return a pointer to the head of that new list.
*/
static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){
fuzzer_stem head;
fuzzer_stem *pTail;
pTail = &head;
while( pA && pB ){
if( pA->rCostX<=pB->rCostX ){
pTail->pNext = pA;
pTail = pA;
pA = pA->pNext;
}else{
pTail->pNext = pB;
pTail = pB;
pB = pB->pNext;
}
}
if( pA==0 ){
pTail->pNext = pB;
}else{
pTail->pNext = pA;
}
return head.pNext;
}
/*
** Load pCur->pStem with the lowest-cost stem. Return a pointer
** to the lowest-cost stem.
*/
static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){
fuzzer_stem *pBest, *pX;
int iBest;
int i;
if( pCur->pStem==0 ){
iBest = -1;
pBest = 0;
for(i=0; i<=pCur->mxQueue; i++){
pX = pCur->aQueue[i];
if( pX==0 ) continue;
if( pBest==0 || pBest->rCostX>pX->rCostX ){
pBest = pX;
iBest = i;
}
}
if( pBest ){
pCur->aQueue[iBest] = pBest->pNext;
pBest->pNext = 0;
pCur->pStem = pBest;
}
}
return pCur->pStem;
}
/*
** Insert pNew into queue of pending stems. Then find the stem
** with the lowest rCostX and move it into pCur->pStem.
** list. The insert is done such the pNew is in the correct order
** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost.
*/
static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){
fuzzer_stem *pX;
int i;
/* If pCur->pStem exists and is greater than pNew, then make pNew
** the new pCur->pStem and insert the old pCur->pStem instead.
*/
if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){
pNew->pNext = 0;
pCur->pStem = pNew;
pNew = pX;
}
/* Insert the new value */
pNew->pNext = 0;
pX = pNew;
for(i=0; i<=pCur->mxQueue; i++){
if( pCur->aQueue[i] ){
pX = fuzzerMergeStems(pX, pCur->aQueue[i]);
pCur->aQueue[i] = 0;
}else{
pCur->aQueue[i] = pX;
break;
}
}
if( i>pCur->mxQueue ){
if( i<FUZZER_NQUEUE ){
pCur->mxQueue = i;
pCur->aQueue[i] = pX;
}else{
assert( pCur->mxQueue==FUZZER_NQUEUE-1 );
pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]);
pCur->aQueue[FUZZER_NQUEUE-1] = pX;
}
}
return fuzzerLowestCostStem(pCur);
}
/*
** Allocate a new fuzzer_stem. Add it to the hash table but do not
** link it into either the pCur->pStem or pCur->pDone lists.
*/
static fuzzer_stem *fuzzerNewStem(
fuzzer_cursor *pCur,
const char *zWord,
fuzzer_cost rBaseCost
){
fuzzer_stem *pNew;
unsigned int h;
pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 );
if( pNew==0 ) return 0;
memset(pNew, 0, sizeof(*pNew));
pNew->zBasis = (char*)&pNew[1];
pNew->nBasis = strlen(zWord);
memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
pNew->pRule = pCur->pVtab->pRule;
pNew->n = -1;
pNew->rBaseCost = pNew->rCostX = rBaseCost;
h = fuzzerHash(pNew->zBasis);
pNew->pHash = pCur->apHash[h];
pCur->apHash[h] = pNew;
pCur->nStem++;
return pNew;
}
/*
** Advance a cursor to its next row of output
*/
static int fuzzerNext(sqlite3_vtab_cursor *cur){
fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
int rc;
fuzzer_stem *pStem, *pNew;
pCur->iRowid++;
/* Use the element the cursor is currently point to to create
** a new stem and insert the new stem into the priority queue.
*/
pStem = pCur->pStem;
if( pStem->rCostX>0 ){
rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf);
if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX);
if( pNew ){
if( fuzzerAdvance(pCur, pNew)==0 ){
pNew->pNext = pCur->pDone;
pCur->pDone = pNew;
}else{
if( fuzzerInsert(pCur, pNew)==pNew ){
return SQLITE_OK;
}
}
}else{
return SQLITE_NOMEM;
}
}
/* Adjust the priority queue so that the first element of the
** stem list is the next lowest cost word.
*/
while( (pStem = pCur->pStem)!=0 ){
if( fuzzerAdvance(pCur, pStem) ){
pCur->pStem = 0;
pStem = fuzzerInsert(pCur, pStem);
if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
if( rc<0 ) return SQLITE_NOMEM;
continue;
}
return SQLITE_OK; /* New word found */
}
pCur->pStem = 0;
pStem->pNext = pCur->pDone;
pCur->pDone = pStem;
if( fuzzerLowestCostStem(pCur) ){
rc = fuzzerSeen(pCur, pCur->pStem);
if( rc<0 ) return SQLITE_NOMEM;
if( rc==0 ){
return SQLITE_OK;
}
}
}
/* Reach this point only if queue has been exhausted and there is
** nothing left to be output. */
pCur->rLimit = (fuzzer_cost)0;
return SQLITE_OK;
}
/*
** Called to "rewind" a cursor back to the beginning so that
** it starts its output over again. Always called at least once
** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call.
*/
static int fuzzerFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
const char *zWord = 0;
fuzzer_stem *pStem;
fuzzerClearCursor(pCur, 1);
pCur->rLimit = 2147483647;
if( idxNum==1 ){
zWord = (const char*)sqlite3_value_text(argv[0]);
}else if( idxNum==2 ){
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]);
}else if( idxNum==3 ){
zWord = (const char*)sqlite3_value_text(argv[0]);
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]);
}
if( zWord==0 ) zWord = "";
pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
if( pStem==0 ) return SQLITE_NOMEM;
pCur->nullRule.pNext = pCur->pVtab->pRule;
pCur->nullRule.rCost = 0;
pCur->nullRule.nFrom = 0;
pCur->nullRule.nTo = 0;
pCur->nullRule.zFrom = "";
pStem->pRule = &pCur->nullRule;
pStem->n = pStem->nBasis;
pCur->iRowid = 1;
return SQLITE_OK;
}
/*
** Only the word and distance columns have values. All other columns
** return NULL
*/
static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
if( i==0 ){
/* the "word" column */
if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
return SQLITE_NOMEM;
}
sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
}else if( i==1 ){
/* the "distance" column */
sqlite3_result_int(ctx, pCur->pStem->rCostX);
}else{
/* All other columns are NULL */
sqlite3_result_null(ctx);
}
return SQLITE_OK;
}
/*
** The rowid.
*/
static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
** that the cursor has nothing more to output.
*/
static int fuzzerEof(sqlite3_vtab_cursor *cur){
fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
return pCur->rLimit<=(fuzzer_cost)0;
}
/*
** Search for terms of these forms:
**
** word MATCH $str
** distance < $value
** distance <= $value
**
** The distance< and distance<= are both treated as distance<=.
** The query plan number is as follows:
**
** 0: None of the terms above are found
** 1: There is a "word MATCH" term with $str in filter.argv[0].
** 2: There is a "distance<" term with $value in filter.argv[0].
** 3: Both "word MATCH" and "distance<" with $str in argv[0] and
** $value in argv[1].
*/
static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iPlan = 0;
int iDistTerm = -1;
int i;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( (iPlan & 1)==0
&& pConstraint->iColumn==0
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
){
iPlan |= 1;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
}
if( (iPlan & 2)==0
&& pConstraint->iColumn==1
&& (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|| pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
){
iPlan |= 2;
iDistTerm = i;
}
}
if( iPlan==2 ){
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1;
}else if( iPlan==3 ){
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2;
}
pIdxInfo->idxNum = iPlan;
if( pIdxInfo->nOrderBy==1
&& pIdxInfo->aOrderBy[0].iColumn==1
&& pIdxInfo->aOrderBy[0].desc==0
){
pIdxInfo->orderByConsumed = 1;
}
pIdxInfo->estimatedCost = (double)10000;
return SQLITE_OK;
}
/*
** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed.
**
** On an insert, the cFrom, cTo, and cost columns are used to construct
** a new rule. All other columns are ignored. The rule is ignored
** if cFrom and cTo are identical. A NULL value for cFrom or cTo is
** interpreted as an empty string. The cost must be positive.
*/
static int fuzzerUpdate(
sqlite3_vtab *pVTab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
fuzzer_rule *pRule;
const char *zFrom;
int nFrom;
const char *zTo;
int nTo;
fuzzer_cost rCost;
if( argc!=7 ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table",
p->zClassName);
return SQLITE_CONSTRAINT;
}
if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table",
p->zClassName);
return SQLITE_CONSTRAINT;
}
zFrom = (char*)sqlite3_value_text(argv[4]);
if( zFrom==0 ) zFrom = "";
zTo = (char*)sqlite3_value_text(argv[5]);
if( zTo==0 ) zTo = "";
if( strcmp(zFrom,zTo)==0 ){
/* Silently ignore null transformations */
return SQLITE_OK;
}
rCost = sqlite3_value_int(argv[6]);
if( rCost<=0 ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cost must be positive");
return SQLITE_CONSTRAINT;
}
nFrom = strlen(zFrom);
nTo = strlen(zTo);
pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
if( pRule==0 ){
return SQLITE_NOMEM;
}
pRule->zFrom = &pRule->zTo[nTo+1];
pRule->nFrom = nFrom;
memcpy(pRule->zFrom, zFrom, nFrom+1);
memcpy(pRule->zTo, zTo, nTo+1);
pRule->nTo = nTo;
pRule->rCost = rCost;
pRule->pNext = p->pNewRule;
p->pNewRule = pRule;
return SQLITE_OK;
}
/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module fuzzerModule = {
0, /* iVersion */
fuzzerConnect,
fuzzerConnect,
fuzzerBestIndex,
fuzzerDisconnect,
fuzzerDisconnect,
fuzzerOpen, /* xOpen - open a cursor */
fuzzerClose, /* xClose - close a cursor */
fuzzerFilter, /* xFilter - configure scan constraints */
fuzzerNext, /* xNext - advance a cursor */
fuzzerEof, /* xEof - check for end of scan */
fuzzerColumn, /* xColumn - read data */
fuzzerRowid, /* xRowid - read data */
fuzzerUpdate, /* xUpdate - INSERT */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/*
** Register the fuzzer virtual table
*/
int fuzzer_register(sqlite3 *db){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
#endif
return rc;
}
#ifdef SQLITE_TEST
#include <tcl.h>
/*
** Decode a pointer to an sqlite3 object.
*/
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
/*
** Register the echo virtual table module.
*/
static int register_fuzzer_module(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
fuzzer_register(db);
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
*/
int Sqlitetestfuzzer_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
void *clientData;
} aObjCmd[] = {
{ "register_fuzzer_module", register_fuzzer_module, 0 },
};
int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
}
return TCL_OK;
}
#endif /* SQLITE_TEST */
+6 -1
View File
@@ -312,8 +312,13 @@ static int utf8_to_utf8(
sqlite3TestBinToHex(z,nOut);
Tcl_AppendResult(interp, (char*)z, 0);
sqlite3_free(z);
#endif
return TCL_OK;
#else
Tcl_AppendResult(interp,
"[utf8_to_utf8] unavailable - SQLITE_DEBUG not defined", 0
);
return TCL_ERROR;
#endif
}
static int getFts3Varint(const char *p, sqlite_int64 *v){
+393 -187
View File
@@ -22,18 +22,51 @@
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
#include "sqliteInt.h"
#include "test_multiplex.h"
#ifndef SQLITE_CORE
#define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
#endif
#include "sqlite3ext.h"
/*
** These should be defined to be the same as the values in
** sqliteInt.h. They are defined seperately here so that
** the multiplex VFS shim can be built as a loadable
** module.
*/
#define UNUSED_PARAMETER(x) (void)(x)
#define MAX_PAGE_SIZE 0x10000
#define DEFAULT_SECTOR_SIZE 0x1000
/*
** For a build without mutexes, no-op the mutex calls.
*/
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
#define sqlite3_mutex_free(X)
#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X) SQLITE_OK
#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X) ((void)(X),1)
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */
/************************ Shim Definitions ******************************/
#define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
/* This is the limit on the chunk size. It may be changed by calling
** the sqlite3_multiplex_set() interface.
** the xFileControl() interface. It will be rounded up to a
** multiple of MAX_PAGE_SIZE. We default it here to 1GB.
*/
#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
#define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384)
/* Default limit on number of chunks. Care should be taken
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
** format specifier. It may be changed by calling
** the sqlite3_multiplex_set() interface.
** the xFileControl() interface.
*/
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
@@ -64,10 +97,13 @@ typedef struct multiplexConn multiplexConn;
*/
struct multiplexGroup {
sqlite3_file **pReal; /* Handles to each chunk */
char *bOpen; /* 0 if chunk not opened */
char *bOpen; /* array of bools - 0 if chunk not opened */
char *zName; /* Base filename of this group */
int nName; /* Length of base filename */
int flags; /* Flags used for original opening */
int nChunkSize; /* Chunk size used for this group */
int nMaxChunks; /* Max number of chunks for this group */
int bEnabled; /* TRUE to use Multiplex VFS for this file */
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
};
@@ -126,11 +162,6 @@ static struct {
*/
multiplexGroup *pGroups;
/* Chunk params.
*/
int nChunkSize;
int nMaxChunks;
/* Storage for temp file names. Allocated during
** initialization to the max pathname of the underlying VFS.
*/
@@ -146,13 +177,28 @@ static struct {
static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
/*
** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer.
**
** The value returned will never be negative. Nor will it ever be greater
** than the actual length of the string. For very long strings (greater
** than 1GiB) the value returned might be less than the true string length.
*/
int multiplexStrlen30(const char *z){
const char *z2 = z;
if( z==0 ) return 0;
while( *z2 ){ z2++; }
return 0x3fffffff & (int)(z2 - z);
}
/* Translate an sqlite3_file* that is really a multiplexGroup* into
** the sqlite3_file* for the underlying original VFS.
*/
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
multiplexGroup *pGroup = pConn->pGroup;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
if( iChunk<gMultiplex.nMaxChunks ){
if( iChunk<pGroup->nMaxChunks ){
sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */
if( !pGroup->bOpen[iChunk] ){
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
@@ -177,6 +223,62 @@ static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc,
return NULL;
}
/*
** This is the implementation of the multiplex_control() SQL function.
*/
static void multiplexControlFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int rc = SQLITE_OK;
sqlite3 *db = sqlite3_context_db_handle(context);
int op;
int iVal;
if( !db || argc!=2 ){
rc = SQLITE_ERROR;
}else{
/* extract params */
op = sqlite3_value_int(argv[0]);
iVal = sqlite3_value_int(argv[1]);
/* map function op to file_control op */
switch( op ){
case 1:
op = MULTIPLEX_CTRL_ENABLE;
break;
case 2:
op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;
break;
case 3:
op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;
break;
default:
rc = SQLITE_NOTFOUND;
break;
}
}
if( rc==SQLITE_OK ){
rc = sqlite3_file_control(db, 0, op, &iVal);
}
sqlite3_result_error_code(context, rc);
}
/*
** This is the entry point to register the auto-extension for the
** multiplex_control() function.
*/
static int multiplexFuncInit(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,
0, multiplexControlFunc, 0, 0);
return rc;
}
/************************* VFS Method Wrappers *****************************/
/*
@@ -198,7 +300,7 @@ static int multiplexOpen(
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
sqlite3_file *pSubOpen; /* Real file descriptor */
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int nName = sqlite3Strlen30(zName);
int nName = multiplexStrlen30(zName);
int i;
int sz;
@@ -210,11 +312,11 @@ static int multiplexOpen(
multiplexEnter();
pMultiplexOpen = (multiplexConn*)pConn;
/* allocate space for group */
sz = sizeof(multiplexGroup) /* multiplexGroup */
+ (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
+ (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */
+ gMultiplex.nMaxChunks /* bOpen[] */
+ nName + 1; /* zName */
sz = sizeof(multiplexGroup) /* multiplexGroup */
+ (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */
+ (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */
+ SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */
+ nName + 1; /* zName */
#ifndef SQLITE_MULTIPLEX_EXT_OVWR
sz += SQLITE_MULTIPLEX_EXT_SZ;
assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
@@ -230,14 +332,18 @@ static int multiplexOpen(
char *p = (char *)&pGroup[1];
pMultiplexOpen->pGroup = pGroup;
memset(pGroup, 0, sz);
pGroup->bEnabled = -1;
pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
pGroup->pReal = (sqlite3_file **)p;
p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
for(i=0; i<gMultiplex.nMaxChunks; i++){
p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks);
for(i=0; i<pGroup->nMaxChunks; i++){
pGroup->pReal[i] = (sqlite3_file *)p;
p += pOrigVfs->szOsFile;
}
/* bOpen[] vals should all be zero from memset above */
pGroup->bOpen = p;
p += gMultiplex.nMaxChunks;
p += pGroup->nMaxChunks;
pGroup->zName = p;
/* save off base filename, name length, and original open flags */
memcpy(pGroup->zName, zName, nName+1);
@@ -245,6 +351,14 @@ static int multiplexOpen(
pGroup->flags = flags;
pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
if( pSubOpen ){
/* if this file is already larger than chunk size, disable
** the multiplex feature.
*/
sqlite3_int64 sz;
int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){
pGroup->bEnabled = 0;
}
if( pSubOpen->pMethods->iVersion==1 ){
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
}else{
@@ -274,24 +388,29 @@ static int multiplexDelete(
){
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int rc = SQLITE_OK;
int nName = sqlite3Strlen30(zName);
int nName = multiplexStrlen30(zName);
int i;
UNUSED_PARAMETER(pVfs);
multiplexEnter();
memcpy(gMultiplex.zName, zName, nName+1);
for(i=0; i<gMultiplex.nMaxChunks; i++){
for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; i++){
int rc2;
int exists = 0;
if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ,
SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
gMultiplex.zName+nName,
SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
}
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, delete it */
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
@@ -353,7 +472,7 @@ static int multiplexClose(sqlite3_file *pConn){
int i;
multiplexEnter();
/* close any open handles */
for(i=0; i<gMultiplex.nMaxChunks; i++){
for(i=0; i<pGroup->nMaxChunks; i++){
if( pGroup->bOpen[i] ){
sqlite3_file *pSubOpen = pGroup->pReal[i];
int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
@@ -384,23 +503,29 @@ static int multiplexRead(
sqlite3_int64 iOfst
){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
multiplexEnter();
while( iAmt > 0 ){
int i = (int)(iOfst/gMultiplex.nChunkSize);
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
if( extra<0 ) extra = 0;
iAmt -= extra;
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_READ;
break;
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
}else{
while( iAmt > 0 ){
int i = (int)(iOfst / pGroup->nChunkSize);
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
if( extra<0 ) extra = 0;
iAmt -= extra;
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_READ;
break;
}
}
}
multiplexLeave();
@@ -418,23 +543,29 @@ static int multiplexWrite(
sqlite3_int64 iOfst
){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
multiplexEnter();
while( iAmt > 0 ){
int i = (int)(iOfst/gMultiplex.nChunkSize);
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
if( extra<0 ) extra = 0;
iAmt -= extra;
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_WRITE;
break;
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
}else{
while( iAmt > 0 ){
int i = (int)(iOfst / pGroup->nChunkSize);
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
if( extra<0 ) extra = 0;
iAmt -= extra;
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_WRITE;
break;
}
}
}
multiplexLeave();
@@ -449,35 +580,44 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
int rc2;
int i;
sqlite3_file *pSubOpen;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
multiplexEnter();
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
/* delete the chunks above the truncate limit */
for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
/* close any open chunks before deleting them */
if( pGroup->bOpen[i] ){
pSubOpen = pGroup->pReal[i];
rc2 = pSubOpen->pMethods->xClose(pSubOpen);
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
pGroup->bOpen[i] = 0;
}
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
}
pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
if( pSubOpen ){
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
if( rc2!=SQLITE_OK ) rc = rc2;
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size);
}else{
rc = SQLITE_IOERR_TRUNCATE;
int rc2;
int i;
sqlite3_file *pSubOpen;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
/* delete the chunks above the truncate limit */
for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; i++){
/* close any open chunks before deleting them */
if( pGroup->bOpen[i] ){
pSubOpen = pGroup->pReal[i];
rc2 = pSubOpen->pMethods->xClose(pSubOpen);
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
pGroup->bOpen[i] = 0;
}
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ,
SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
gMultiplex.zName+pGroup->nName,
SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
}
pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL);
if( pSubOpen ){
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
if( rc2!=SQLITE_OK ) rc = rc2;
}else{
rc = SQLITE_IOERR_TRUNCATE;
}
}
multiplexLeave();
return rc;
@@ -491,7 +631,7 @@ static int multiplexSync(sqlite3_file *pConn, int flags){
int rc = SQLITE_OK;
int i;
multiplexEnter();
for(i=0; i<gMultiplex.nMaxChunks; i++){
for(i=0; i<pGroup->nMaxChunks; i++){
/* if we don't have it open, we don't need to sync it */
if( pGroup->bOpen[i] ){
sqlite3_file *pSubOpen = pGroup->pReal[i];
@@ -513,46 +653,56 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
int rc2;
int i;
multiplexEnter();
*pSize = 0;
for(i=0; i<gMultiplex.nMaxChunks; i++){
sqlite3_file *pSubOpen = NULL;
/* if not opened already, check to see if the chunk exists */
if( pGroup->bOpen[i] ){
pSubOpen = pGroup->pReal[i];
}else{
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int exists = 0;
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
if( i ){
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
}else{
*pSize = 0;
for(i=0; i<pGroup->nMaxChunks; i++){
sqlite3_file *pSubOpen = NULL;
/* if not opened already, check to see if the chunk exists */
if( pGroup->bOpen[i] ){
pSubOpen = pGroup->pReal[i];
}else{
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int exists = 0;
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ,
SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
gMultiplex.zName+pGroup->nName,
SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
}
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, open it */
pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
}else{
/* stop at first "gap" */
break;
}
}
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, open it */
pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
sqlite3_int64 sz;
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc2!=SQLITE_OK ){
rc = rc2;
}else{
if( sz>pGroup->nChunkSize ){
rc = SQLITE_IOERR_FSTAT;
}
*pSize += sz;
}
}else{
/* stop at first "gap" */
break;
}
}
if( pSubOpen ){
sqlite3_int64 sz;
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc2!=SQLITE_OK ){
rc = rc2;
}else{
if( sz>gMultiplex.nChunkSize ){
rc = SQLITE_IOERR_FSTAT;
}
*pSize += sz;
}
}else{
break;
}
}
multiplexLeave();
return rc;
@@ -594,18 +744,62 @@ static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
return SQLITE_IOERR_CHECKRESERVEDLOCK;
}
/* Pass xFileControl requests through to the original VFS unchanged.
/* Pass xFileControl requests through to the original VFS unchanged,
** except for any MULTIPLEX_CTRL_* requests here.
*/
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_ERROR;
sqlite3_file *pSubOpen;
if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
switch( op ){
case MULTIPLEX_CTRL_ENABLE:
if( pArg ) {
int bEnabled = *(int *)pArg;
pGroup->bEnabled = bEnabled;
rc = SQLITE_OK;
}
break;
case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
if( pArg ) {
int nChunkSize = *(int *)pArg;
if( nChunkSize<1 ){
rc = SQLITE_MISUSE;
}else{
/* Round up to nearest multiple of MAX_PAGE_SIZE. */
nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1));
nChunkSize &= ~(MAX_PAGE_SIZE-1);
pGroup->nChunkSize = nChunkSize;
rc = SQLITE_OK;
}
}
break;
case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
if( pArg ) {
int nMaxChunks = *(int *)pArg;
if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){
rc = SQLITE_MISUSE;
}else{
pGroup->nMaxChunks = nMaxChunks;
rc = SQLITE_OK;
}
}
break;
case SQLITE_FCNTL_SIZE_HINT:
case SQLITE_FCNTL_CHUNK_SIZE:
/* no-op these */
rc = SQLITE_OK;
break;
default:
pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
}
break;
}
return SQLITE_ERROR;
return rc;
}
/* Pass xSectorSize requests through to the original VFS unchanged.
@@ -617,7 +811,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){
if( pSubOpen ){
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
return SQLITE_DEFAULT_SECTOR_SIZE;
return DEFAULT_SECTOR_SIZE;
}
/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
@@ -692,9 +886,10 @@ static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
/************************** Public Interfaces *****************************/
/*
** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName
** as the VFS that does the actual work. Use the default if
** zOrigVfsName==NULL.
** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
**
** Use the VFS named zOrigVfsName as the VFS that does the actual work.
** Use the default if zOrigVfsName==NULL.
**
** The multiplex VFS shim is named "multiplex". It will become the default
** VFS if makeDefault is non-zero.
@@ -717,14 +912,12 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
sqlite3_mutex_free(gMultiplex.pMutex);
return SQLITE_NOMEM;
}
gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
gMultiplex.pGroups = NULL;
gMultiplex.isInitialized = 1;
gMultiplex.pOrigVfs = pOrigVfs;
gMultiplex.sThisVfs = *pOrigVfs;
gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
gMultiplex.sThisVfs.zName = "multiplex";
gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
gMultiplex.sThisVfs.xOpen = multiplexOpen;
gMultiplex.sThisVfs.xDelete = multiplexDelete;
gMultiplex.sThisVfs.xAccess = multiplexAccess;
@@ -759,11 +952,14 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
sqlite3_auto_extension((void*)multiplexFuncInit);
return SQLITE_OK;
}
/*
** Shutdown the multiplex system.
** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
**
** All SQLite database connections must be closed before calling this
** routine.
@@ -782,31 +978,9 @@ int sqlite3_multiplex_shutdown(void){
return SQLITE_OK;
}
/*
** Adjust chunking params. VFS should be initialized first.
** No files should be open. Re-intializing will reset these
** to the default.
*/
int sqlite3_multiplex_set(
int nChunkSize, /* Max chunk size */
int nMaxChunks /* Max number of chunks */
){
if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
if( gMultiplex.pGroups ) return SQLITE_MISUSE;
if( nChunkSize<32 ) return SQLITE_MISUSE;
if( nMaxChunks<1 ) return SQLITE_MISUSE;
if( nMaxChunks>99 ) return SQLITE_MISUSE;
multiplexEnter();
gMultiplex.nChunkSize = nChunkSize;
gMultiplex.nMaxChunks = nMaxChunks;
multiplexLeave();
return SQLITE_OK;
}
/***************************** Test Code ***********************************/
#ifdef SQLITE_TEST
#include <tcl.h>
extern const char *sqlite3TestErrorName(int);
@@ -866,36 +1040,6 @@ static int test_multiplex_shutdown(
return TCL_OK;
}
/*
** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
*/
static int test_multiplex_set(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int nChunkSize; /* Max chunk size */
int nMaxChunks; /* Max number of chunks */
int rc; /* Value returned by sqlite3_multiplex_set() */
UNUSED_PARAMETER(clientData);
/* Process arguments */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
/* Invoke sqlite3_multiplex_set() */
rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_multiplex_dump
*/
@@ -929,16 +1073,16 @@ static int test_multiplex_dump(
Tcl_NewIntObj(pGroup->flags));
/* count number of chunks with open handles */
for(i=0; i<gMultiplex.nMaxChunks; i++){
for(i=0; i<pGroup->nMaxChunks; i++){
if( pGroup->bOpen[i] ) nChunks++;
}
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(nChunks));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(gMultiplex.nChunkSize));
Tcl_NewIntObj(pGroup->nChunkSize));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(gMultiplex.nMaxChunks));
Tcl_NewIntObj(pGroup->nMaxChunks));
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
}
@@ -947,6 +1091,68 @@ static int test_multiplex_dump(
return TCL_OK;
}
/*
** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
*/
static int test_multiplex_control(
ClientData cd,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc; /* Return code from file_control() */
int idx; /* Index in aSub[] */
Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */
sqlite3 *db; /* Underlying db handle for HANDLE */
int iValue = 0;
void *pArg = 0;
struct SubCommand {
const char *zName;
int op;
int argtype;
} aSub[] = {
{ "enable", MULTIPLEX_CTRL_ENABLE, 1 },
{ "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 },
{ "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 },
{ 0, 0, 0 }
};
if( objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
Tcl_AppendResult(interp, "expected database handle, got \"", 0);
Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
return TCL_ERROR;
}else{
db = *(sqlite3 **)cmdInfo.objClientData;
}
rc = Tcl_GetIndexFromObjStruct(
interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
);
if( rc!=TCL_OK ) return rc;
switch( aSub[idx].argtype ){
case 1:
if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
return TCL_ERROR;
}
pArg = (void *)&iValue;
break;
default:
Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
return TCL_ERROR;
}
rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
}
/*
** This routine registers the custom TCL commands defined in this
** module. This should be the only procedure visible from outside
@@ -959,8 +1165,8 @@ int Sqlitemultiplex_Init(Tcl_Interp *interp){
} aCmd[] = {
{ "sqlite3_multiplex_initialize", test_multiplex_initialize },
{ "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
{ "sqlite3_multiplex_set", test_multiplex_set },
{ "sqlite3_multiplex_dump", test_multiplex_dump },
{ "sqlite3_multiplex_control", test_multiplex_control },
};
int i;
+91
View File
@@ -0,0 +1,91 @@
/*
** 2011 March 18
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains a VFS "shim" - a layer that sits in between the
** pager and the real VFS.
**
** This particular shim enforces a multiplex system on DB files.
** This shim shards/partitions a single DB file into smaller
** "chunks" such that the total DB file size may exceed the maximum
** file size of the underlying file system.
**
*/
#ifndef _TEST_MULTIPLEX_H
#define _TEST_MULTIPLEX_H
/*
** CAPI: File-control Operations Supported by Multiplex VFS
**
** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle.
**
** MULTIPLEX_CTRL_ENABLE:
** This file control is used to enable or disable the multiplex
** shim.
**
** MULTIPLEX_CTRL_SET_CHUNK_SIZE:
** This file control is used to set the maximum allowed chunk
** size for a multiplex file set. The chunk size should be
** a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up
** if not.
**
** MULTIPLEX_CTRL_SET_MAX_CHUNKS:
** This file control is used to set the maximum number of chunks
** allowed to be used for a mutliplex file set.
*/
#define MULTIPLEX_CTRL_ENABLE 214014
#define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015
#define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016
/*
** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
**
** Use the VFS named zOrigVfsName as the VFS that does the actual work.
** Use the default if zOrigVfsName==NULL.
**
** The multiplex VFS shim is named "multiplex". It will become the default
** VFS if makeDefault is non-zero.
**
** An auto-extension is registered which will make the function
** multiplex_control() available to database connections. This
** function gives access to the xFileControl interface of the
** multiplex VFS shim.
**
** SELECT multiplex_control(<op>,<val>);
**
** <op>=1 MULTIPLEX_CTRL_ENABLE
** <val>=0 disable
** <val>=1 enable
**
** <op>=2 MULTIPLEX_CTRL_SET_CHUNK_SIZE
** <val> int, chunk size
**
** <op>=3 MULTIPLEX_CTRL_SET_MAX_CHUNKS
** <val> int, max chunks
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
** during start-up.
*/
extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault);
/*
** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
**
** All SQLite database connections must be closed before calling this
** routine.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
** shutting down in order to free all remaining multiplex groups.
*/
extern int sqlite3_multiplex_shutdown(void);
#endif
+2 -2
View File
@@ -213,14 +213,14 @@ static sqlite3_io_methods vfslog_io_methods = {
vfslogShmUnmap /* xShmUnmap */
};
#if defined(SQLITE_OS_UNIX) && !defined(NO_GETTOD)
#if SQLITE_OS_UNIX && !defined(NO_GETTOD)
#include <sys/time.h>
static sqlite3_uint64 vfslog_time(){
struct timeval sTime;
gettimeofday(&sTime, 0);
return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
}
#elif defined(SQLITE_OS_WIN)
#elif SQLITE_OS_WIN
#include <windows.h>
#include <time.h>
static sqlite3_uint64 vfslog_time(){
+2 -3
View File
@@ -206,7 +206,7 @@
** and only if the SQLITE_SERVER macro is defined.
*/
#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
#if defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE
#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
/*
** We require only pthreads and the public interface of SQLite.
@@ -453,7 +453,6 @@ void *sqlite3_server(void *NotUsed){
pthread_mutex_unlock(&pMsg->clientMutex);
pthread_cond_signal(&pMsg->clientWakeup);
}
sqlite3_thread_cleanup();
pthread_mutex_unlock(&g.serverMutex);
return 0;
}
@@ -487,5 +486,5 @@ void sqlite3_server_stop(void){
pthread_mutex_unlock(&g.serverMutex);
}
#endif /* defined(SQLITE_OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE */
#endif /* SQLITE_OS_UNIX && SQLITE_THREADSAFE */
#endif /* defined(SQLITE_SERVER) */
+674
View File
@@ -0,0 +1,674 @@
/*
** 2011 March 28
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** The code in this file implements a Tcl interface used to test error
** handling in the os_unix.c module. Wrapper functions that support fault
** injection are registered as the low-level OS functions using the
** xSetSystemCall() method of the VFS. The Tcl interface is as follows:
**
**
** test_syscall install LIST
** Install wrapper functions for all system calls in argument LIST.
** LIST must be a list consisting of zero or more of the following
** literal values:
**
** open close access getcwd stat fstat
** ftruncate fcntl read pread pread64 write
** pwrite pwrite64 fchmod fallocate
**
** test_syscall uninstall
** Uninstall all wrapper functions.
**
** test_syscall fault ?COUNT PERSIST?
** If [test_syscall fault] is invoked without the two arguments, fault
** injection is disabled. Otherwise, fault injection is configured to
** cause a failure on the COUNT'th next call to a system call with a
** wrapper function installed. A COUNT value of 1 means fail the next
** system call.
**
** Argument PERSIST is interpreted as a boolean. If true, the all
** system calls following the initial failure also fail. Otherwise, only
** the single transient failure is injected.
**
** test_syscall errno CALL ERRNO
** Set the value that the global "errno" is set to following a fault
** in call CALL. Argument CALL must be one of the system call names
** listed above (under [test_syscall install]). ERRNO is a symbolic
** name (i.e. "EACCES"). Not all errno codes are supported. Add extra
** to the aErrno table in function test_syscall_errno() below as
** required.
**
** test_syscall reset ?SYSTEM-CALL?
** With no argument, this is an alias for the [uninstall] command. However,
** this command uses a VFS call of the form:
**
** xSetSystemCall(pVfs, 0, 0);
**
** To restore the default system calls. The [uninstall] command restores
** each system call individually by calling (i.e.):
**
** xSetSystemCall(pVfs, "open", 0);
**
** With an argument, this command attempts to reset the system call named
** by the parameter using the same method as [uninstall].
**
** test_syscall exists SYSTEM-CALL
** Return true if the named system call exists. Or false otherwise.
**
** test_syscall list
** Return a list of all system calls. The list is constructed using
** the xNextSystemCall() VFS method.
*/
#include "sqlite3.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "sqliteInt.h"
#if SQLITE_OS_UNIX
/* From test1.c */
extern const char *sqlite3TestErrorName(int);
#include <sys/types.h>
#include <errno.h>
static struct TestSyscallGlobal {
int bPersist; /* 1 for persistent errors, 0 for transient */
int nCount; /* Fail after this many more calls */
int nFail; /* Number of failures that have occurred */
} gSyscall = { 0, 0 };
static int ts_open(const char *, int, int);
static int ts_close(int fd);
static int ts_access(const char *zPath, int mode);
static char *ts_getcwd(char *zPath, size_t nPath);
static int ts_stat(const char *zPath, struct stat *p);
static int ts_fstat(int fd, struct stat *p);
static int ts_ftruncate(int fd, off_t n);
static int ts_fcntl(int fd, int cmd, ... );
static int ts_read(int fd, void *aBuf, size_t nBuf);
static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off);
static int ts_write(int fd, const void *aBuf, size_t nBuf);
static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off);
static int ts_fchmod(int fd, mode_t mode);
static int ts_fallocate(int fd, off_t off, off_t len);
struct TestSyscallArray {
const char *zName;
sqlite3_syscall_ptr xTest;
sqlite3_syscall_ptr xOrig;
int default_errno; /* Default value for errno following errors */
int custom_errno; /* Current value for errno if error */
} aSyscall[] = {
/* 0 */ { "open", (sqlite3_syscall_ptr)ts_open, 0, EACCES, 0 },
/* 1 */ { "close", (sqlite3_syscall_ptr)ts_close, 0, 0, 0 },
/* 2 */ { "access", (sqlite3_syscall_ptr)ts_access, 0, 0, 0 },
/* 3 */ { "getcwd", (sqlite3_syscall_ptr)ts_getcwd, 0, 0, 0 },
/* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 },
/* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 },
/* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 },
/* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, EACCES, 0 },
/* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 },
/* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 },
/* 10 */ { "pread64", (sqlite3_syscall_ptr)ts_pread64, 0, 0, 0 },
/* 11 */ { "write", (sqlite3_syscall_ptr)ts_write, 0, 0, 0 },
/* 12 */ { "pwrite", (sqlite3_syscall_ptr)ts_pwrite, 0, 0, 0 },
/* 13 */ { "pwrite64", (sqlite3_syscall_ptr)ts_pwrite64, 0, 0, 0 },
/* 14 */ { "fchmod", (sqlite3_syscall_ptr)ts_fchmod, 0, 0, 0 },
/* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 },
{ 0, 0, 0, 0, 0 }
};
#define orig_open ((int(*)(const char *, int, int))aSyscall[0].xOrig)
#define orig_close ((int(*)(int))aSyscall[1].xOrig)
#define orig_access ((int(*)(const char*,int))aSyscall[2].xOrig)
#define orig_getcwd ((char*(*)(char*,size_t))aSyscall[3].xOrig)
#define orig_stat ((int(*)(const char*,struct stat*))aSyscall[4].xOrig)
#define orig_fstat ((int(*)(int,struct stat*))aSyscall[5].xOrig)
#define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig)
#define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig)
#define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig)
#define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig)
#define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig)
#define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig)
#define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\
aSyscall[12].xOrig)
#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
aSyscall[13].xOrig)
#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig)
#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig)
/*
** This function is called exactly once from within each invocation of a
** system call wrapper in this file. It returns 1 if the function should
** fail, or 0 if it should succeed.
*/
static int tsIsFail(void){
gSyscall.nCount--;
if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){
gSyscall.nFail++;
return 1;
}
return 0;
}
/*
** Return the current error-number value for function zFunc. zFunc must be
** the name of a system call in the aSyscall[] table.
**
** Usually, the current error-number is the value that errno should be set
** to if the named system call fails. The exception is "fallocate". See
** comments above the implementation of ts_fallocate() for details.
*/
static int tsErrno(const char *zFunc){
int i;
int nFunc = strlen(zFunc);
for(i=0; aSyscall[i].zName; i++){
if( strlen(aSyscall[i].zName)!=nFunc ) continue;
if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
return aSyscall[i].custom_errno;
}
assert(0);
return 0;
}
/*
** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the
** value of errno before returning.
*/
static int tsIsFailErrno(const char *zFunc){
if( tsIsFail() ){
errno = tsErrno(zFunc);
return 1;
}
return 0;
}
/*
** A wrapper around open().
*/
static int ts_open(const char *zFile, int flags, int mode){
if( tsIsFailErrno("open") ){
return -1;
}
return orig_open(zFile, flags, mode);
}
/*
** A wrapper around close().
*/
static int ts_close(int fd){
if( tsIsFail() ){
/* Even if simulating an error, close the original file-descriptor.
** This is to stop the test process from running out of file-descriptors
** when running a long test. If a call to close() appears to fail, SQLite
** never attempts to use the file-descriptor afterwards (or even to close
** it a second time). */
orig_close(fd);
return -1;
}
return orig_close(fd);
}
/*
** A wrapper around access().
*/
static int ts_access(const char *zPath, int mode){
if( tsIsFail() ){
return -1;
}
return orig_access(zPath, mode);
}
/*
** A wrapper around getcwd().
*/
static char *ts_getcwd(char *zPath, size_t nPath){
if( tsIsFail() ){
return NULL;
}
return orig_getcwd(zPath, nPath);
}
/*
** A wrapper around stat().
*/
static int ts_stat(const char *zPath, struct stat *p){
if( tsIsFail() ){
return -1;
}
return orig_stat(zPath, p);
}
/*
** A wrapper around fstat().
*/
static int ts_fstat(int fd, struct stat *p){
if( tsIsFailErrno("fstat") ){
return -1;
}
return orig_fstat(fd, p);
}
/*
** A wrapper around ftruncate().
*/
static int ts_ftruncate(int fd, off_t n){
if( tsIsFailErrno("ftruncate") ){
return -1;
}
return orig_ftruncate(fd, n);
}
/*
** A wrapper around fcntl().
*/
static int ts_fcntl(int fd, int cmd, ... ){
va_list ap;
void *pArg;
if( tsIsFailErrno("fcntl") ){
return -1;
}
va_start(ap, cmd);
pArg = va_arg(ap, void *);
return orig_fcntl(fd, cmd, pArg);
}
/*
** A wrapper around read().
*/
static int ts_read(int fd, void *aBuf, size_t nBuf){
if( tsIsFailErrno("read") ){
return -1;
}
return orig_read(fd, aBuf, nBuf);
}
/*
** A wrapper around pread().
*/
static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
if( tsIsFailErrno("pread") ){
return -1;
}
return orig_pread(fd, aBuf, nBuf, off);
}
/*
** A wrapper around pread64().
*/
static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){
if( tsIsFailErrno("pread64") ){
return -1;
}
return orig_pread64(fd, aBuf, nBuf, off);
}
/*
** A wrapper around write().
*/
static int ts_write(int fd, const void *aBuf, size_t nBuf){
if( tsIsFailErrno("write") ){
return -1;
}
return orig_write(fd, aBuf, nBuf);
}
/*
** A wrapper around pwrite().
*/
static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
if( tsIsFailErrno("pwrite") ){
return -1;
}
return orig_pwrite(fd, aBuf, nBuf, off);
}
/*
** A wrapper around pwrite64().
*/
static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){
if( tsIsFailErrno("pwrite64") ){
return -1;
}
return orig_pwrite64(fd, aBuf, nBuf, off);
}
/*
** A wrapper around fchmod().
*/
static int ts_fchmod(int fd, mode_t mode){
if( tsIsFail() ){
return -1;
}
return orig_fchmod(fd, mode);
}
/*
** A wrapper around fallocate().
**
** SQLite assumes that the fallocate() function is compatible with
** posix_fallocate(). According to the Linux man page (2009-09-30):
**
** posix_fallocate() returns zero on success, or an error number on
** failure. Note that errno is not set.
*/
static int ts_fallocate(int fd, off_t off, off_t len){
if( tsIsFail() ){
return tsErrno("fallocate");
}
return orig_fallocate(fd, off, len);
}
static int test_syscall_install(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_vfs *pVfs;
int nElem;
int i;
Tcl_Obj **apElem;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST");
return TCL_ERROR;
}
if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
return TCL_ERROR;
}
pVfs = sqlite3_vfs_find(0);
for(i=0; i<nElem; i++){
int iCall;
int rc = Tcl_GetIndexFromObjStruct(interp,
apElem[i], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
);
if( rc ) return rc;
if( aSyscall[iCall].xOrig==0 ){
aSyscall[iCall].xOrig = pVfs->xGetSystemCall(pVfs, aSyscall[iCall].zName);
pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest);
}
aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno;
}
return TCL_OK;
}
static int test_syscall_uninstall(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_vfs *pVfs;
int i;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
pVfs = sqlite3_vfs_find(0);
for(i=0; aSyscall[i].zName; i++){
if( aSyscall[i].xOrig ){
pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0);
aSyscall[i].xOrig = 0;
}
}
return TCL_OK;
}
static int test_syscall_reset(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_vfs *pVfs;
int i;
int rc;
if( objc!=2 && objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
pVfs = sqlite3_vfs_find(0);
if( objc==2 ){
rc = pVfs->xSetSystemCall(pVfs, 0, 0);
for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0;
}else{
int nFunc;
char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc);
rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0);
for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){
if( strlen(aSyscall[i].zName)!=nFunc ) continue;
if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
aSyscall[i].xOrig = 0;
}
}
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1));
return TCL_ERROR;
}
Tcl_ResetResult(interp);
return TCL_OK;
}
static int test_syscall_exists(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_vfs *pVfs;
sqlite3_syscall_ptr x;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
pVfs = sqlite3_vfs_find(0);
x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2]));
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0));
return TCL_OK;
}
static int test_syscall_fault(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int nCount = 0;
int bPersist = 0;
if( objc!=2 && objc!=4 ){
Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?");
return TCL_ERROR;
}
if( objc==4 ){
if( Tcl_GetIntFromObj(interp, objv[2], &nCount)
|| Tcl_GetBooleanFromObj(interp, objv[3], &bPersist)
){
return TCL_ERROR;
}
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail));
gSyscall.nCount = nCount;
gSyscall.bPersist = bPersist;
gSyscall.nFail = 0;
return TCL_OK;
}
static int test_syscall_errno(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int iCall;
int iErrno;
int rc;
struct Errno {
const char *z;
int i;
} aErrno[] = {
{ "EACCES", EACCES },
{ "EINTR", EINTR },
{ "EIO", EIO },
{ "EOVERFLOW", EOVERFLOW },
{ "ENOMEM", ENOMEM },
{ "EAGAIN", EAGAIN },
{ "ETIMEDOUT", ETIMEDOUT },
{ "EBUSY", EBUSY },
{ "EPERM", EPERM },
{ "EDEADLK", EDEADLK },
{ "ENOLCK", ENOLCK },
{ 0, 0 }
};
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(interp,
objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
);
if( rc!=TCL_OK ) return rc;
rc = Tcl_GetIndexFromObjStruct(interp,
objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno
);
if( rc!=TCL_OK ) return rc;
aSyscall[iCall].custom_errno = aErrno[iErrno].i;
return TCL_OK;
}
static int test_syscall_list(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zSys;
sqlite3_vfs *pVfs;
Tcl_Obj *pList;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
pVfs = sqlite3_vfs_find(0);
pList = Tcl_NewObj();
Tcl_IncrRefCount(pList);
for(zSys = pVfs->xNextSystemCall(pVfs, 0);
zSys!=0;
zSys = pVfs->xNextSystemCall(pVfs, zSys)
){
Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1));
}
Tcl_SetObjResult(interp, pList);
Tcl_DecrRefCount(pList);
return TCL_OK;
}
static int test_syscall_defaultvfs(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_vfs *pVfs;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
pVfs = sqlite3_vfs_find(0);
Tcl_SetObjResult(interp, Tcl_NewStringObj(pVfs->zName, -1));
return TCL_OK;
}
static int test_syscall(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
struct SyscallCmd {
const char *zName;
Tcl_ObjCmdProc *xCmd;
} aCmd[] = {
{ "fault", test_syscall_fault },
{ "install", test_syscall_install },
{ "uninstall", test_syscall_uninstall },
{ "reset", test_syscall_reset },
{ "errno", test_syscall_errno },
{ "exists", test_syscall_exists },
{ "list", test_syscall_list },
{ "defaultvfs", test_syscall_defaultvfs },
{ 0, 0 }
};
int iCmd;
int rc;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(interp,
objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd
);
if( rc!=TCL_OK ) return rc;
return aCmd[iCmd].xCmd(clientData, interp, objc, objv);
}
int SqlitetestSyscall_Init(Tcl_Interp *interp){
struct SyscallCmd {
const char *zName;
Tcl_ObjCmdProc *xCmd;
} aCmd[] = {
{ "test_syscall", test_syscall},
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xCmd, 0, 0);
}
return TCL_OK;
}
#else
int SqlitetestSyscall_Init(Tcl_Interp *interp){
return TCL_OK;
}
#endif
+4 -4
View File
@@ -53,7 +53,7 @@ struct EvalEvent {
static Tcl_ObjCmdProc sqlthread_proc;
static Tcl_ObjCmdProc clock_seconds_proc;
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
static Tcl_ObjCmdProc blocking_step_proc;
static Tcl_ObjCmdProc blocking_prepare_v2_proc;
#endif
@@ -116,7 +116,7 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
interp = Tcl_CreateInterp();
Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
Tcl_CreateObjCommand(interp,
"sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
@@ -392,7 +392,7 @@ static int clock_seconds_proc(
** should be considered if these functions are to be extended (i.e. to
** support windows) in the future.
*/
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
/* BEGIN_SQLITE_BLOCKING_STEP */
/* This example uses the pthreads API */
@@ -614,7 +614,7 @@ static int blocking_prepare_v2_proc(
int SqlitetestThread_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
#if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
Tcl_CreateObjCommand(interp,
"sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
+773
View File
@@ -0,0 +1,773 @@
/*
** 2011 March 16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains code implements a VFS shim that writes diagnostic
** output for each VFS call, similar to "strace".
*/
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
/*
** An instance of this structure is attached to the each trace VFS to
** provide auxiliary information.
*/
typedef struct vfstrace_info vfstrace_info;
struct vfstrace_info {
sqlite3_vfs *pRootVfs; /* The underlying real VFS */
int (*xOut)(const char*, void*); /* Send output here */
void *pOutArg; /* First argument to xOut */
const char *zVfsName; /* Name of this trace-VFS */
sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */
};
/*
** The sqlite3_file object for the trace VFS
*/
typedef struct vfstrace_file vfstrace_file;
struct vfstrace_file {
sqlite3_file base; /* Base class. Must be first */
vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */
const char *zFName; /* Base name of the file */
sqlite3_file *pReal; /* The real underlying file */
};
/*
** Method declarations for vfstrace_file.
*/
static int vfstraceClose(sqlite3_file*);
static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size);
static int vfstraceSync(sqlite3_file*, int flags);
static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int vfstraceLock(sqlite3_file*, int);
static int vfstraceUnlock(sqlite3_file*, int);
static int vfstraceCheckReservedLock(sqlite3_file*, int *);
static int vfstraceFileControl(sqlite3_file*, int op, void *pArg);
static int vfstraceSectorSize(sqlite3_file*);
static int vfstraceDeviceCharacteristics(sqlite3_file*);
static int vfstraceShmLock(sqlite3_file*,int,int,int);
static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **);
static void vfstraceShmBarrier(sqlite3_file*);
static int vfstraceShmUnmap(sqlite3_file*,int);
/*
** Method declarations for vfstrace_vfs.
*/
static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *);
static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename);
static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
static void vfstraceDlClose(sqlite3_vfs*, void*);
static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int vfstraceSleep(sqlite3_vfs*, int microseconds);
static int vfstraceCurrentTime(sqlite3_vfs*, double*);
static int vfstraceGetLastError(sqlite3_vfs*, int, char*);
static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr);
static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *);
static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName);
/*
** Return a pointer to the tail of the pathname. Examples:
**
** /home/drh/xyzzy.txt -> xyzzy.txt
** xyzzy.txt -> xyzzy.txt
*/
static const char *fileTail(const char *z){
int i;
if( z==0 ) return 0;
i = strlen(z)-1;
while( i>0 && z[i-1]!='/' ){ i--; }
return &z[i];
}
/*
** Send trace output defined by zFormat and subsequent arguments.
*/
static void vfstrace_printf(
vfstrace_info *pInfo,
const char *zFormat,
...
){
va_list ap;
char *zMsg;
va_start(ap, zFormat);
zMsg = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
pInfo->xOut(zMsg, pInfo->pOutArg);
sqlite3_free(zMsg);
}
/*
** Convert value rc into a string and print it using zFormat. zFormat
** should have exactly one %s
*/
static void vfstrace_print_errcode(
vfstrace_info *pInfo,
const char *zFormat,
int rc
){
char zBuf[50];
char *zVal;
switch( rc ){
case SQLITE_OK: zVal = "SQLITE_OK"; break;
case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break;
case SQLITE_PERM: zVal = "SQLITE_PERM"; break;
case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break;
case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break;
case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break;
case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break;
case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break;
case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break;
case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break;
case SQLITE_FULL: zVal = "SQLITE_FULL"; break;
case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break;
case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break;
case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break;
case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break;
case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break;
case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break;
case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break;
case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break;
case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break;
case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break;
case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break;
case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break;
case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break;
case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break;
case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break;
case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break;
case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break;
case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break;
case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break;
case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break;
case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break;
case SQLITE_IOERR_CHECKRESERVEDLOCK:
zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break;
case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break;
case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break;
case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break;
case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break;
case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break;
case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break;
case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break;
case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
default: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc);
zVal = zBuf;
break;
}
}
vfstrace_printf(pInfo, zFormat, zVal);
}
/*
** Append to a buffer.
*/
static void strappend(char *z, int *pI, const char *zAppend){
int i = *pI;
while( zAppend[0] ){ z[i++] = *(zAppend++); }
z[i] = 0;
*pI = i;
}
/*
** Close an vfstrace-file.
*/
static int vfstraceClose(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xClose(p->pReal);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
if( rc==SQLITE_OK ){
sqlite3_free((void*)p->base.pMethods);
p->base.pMethods = 0;
}
return rc;
}
/*
** Read data from an vfstrace-file.
*/
static int vfstraceRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Write data to an vfstrace-file.
*/
static int vfstraceWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Truncate an vfstrace-file.
*/
static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
size);
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Sync an vfstrace-file.
*/
static int vfstraceSync(sqlite3_file *pFile, int flags){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
int i;
char zBuf[100];
memcpy(zBuf, "|0", 3);
i = 0;
if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL");
else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
}
vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
&zBuf[1]);
rc = p->pReal->pMethods->xSync(p->pReal, flags);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the current file-size of an vfstrace-file.
*/
static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
vfstrace_print_errcode(pInfo, " -> %s,", rc);
vfstrace_printf(pInfo, " size=%lld\n", *pSize);
return rc;
}
/*
** Return the name of a lock.
*/
static const char *lockName(int eLock){
const char *azLockNames[] = {
"NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE"
};
if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){
return "???";
}else{
return azLockNames[eLock];
}
}
/*
** Lock an vfstrace-file.
*/
static int vfstraceLock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Unlock an vfstrace-file.
*/
static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Check if another file-handle holds a RESERVED lock on an vfstrace-file.
*/
static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
/*
** File control method. For custom operations on an vfstrace-file.
*/
static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zBuf[100];
char *zOp;
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
case SQLITE_FCNTL_SIZE_HINT: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld",
*(sqlite3_int64*)pArg);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break;
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
default: {
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
zOp = zBuf;
break;
}
}
vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)",
pInfo->zVfsName, p->zFName, zOp);
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Return the sector-size in bytes for an vfstrace-file.
*/
static int vfstraceSectorSize(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xSectorSize(p->pReal);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the device characteristic flags supported by an vfstrace-file.
*/
static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
return rc;
}
/*
** Shared-memory operations.
*/
static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zLck[100];
int i = 0;
memcpy(zLck, "|0", 3);
if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
if( flags & ~(0xf) ){
sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
}
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d,n=%d,%s)",
pInfo->zVfsName, p->zFName, ofst, n, &zLck[1]);
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static int vfstraceShmMap(
sqlite3_file *pFile,
int iRegion,
int szRegion,
int isWrite,
void volatile **pp
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)",
pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite);
rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static void vfstraceShmBarrier(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName);
p->pReal->pMethods->xShmBarrier(p->pReal);
}
static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
pInfo->zVfsName, p->zFName, delFlag);
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Open an vfstrace file handle.
*/
static int vfstraceOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
int rc;
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
p->pInfo = pInfo;
p->zFName = zName ? fileTail(zName) : "<temp>";
p->pReal = (sqlite3_file *)&p[1];
rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
pNew->xClose = vfstraceClose;
pNew->xRead = vfstraceRead;
pNew->xWrite = vfstraceWrite;
pNew->xTruncate = vfstraceTruncate;
pNew->xSync = vfstraceSync;
pNew->xFileSize = vfstraceFileSize;
pNew->xLock = vfstraceLock;
pNew->xUnlock = vfstraceUnlock;
pNew->xCheckReservedLock = vfstraceCheckReservedLock;
pNew->xFileControl = vfstraceFileControl;
pNew->xSectorSize = vfstraceSectorSize;
pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics;
if( pNew->iVersion>=2 ){
pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0;
pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0;
pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0;
pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0;
}
pFile->pMethods = pNew;
}
vfstrace_print_errcode(pInfo, " -> %s", rc);
if( pOutFlags ){
vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags);
}else{
vfstrace_printf(pInfo, "\n");
}
return rc;
}
/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
pInfo->zVfsName, zPath, dirSync);
rc = pRoot->xDelete(pRoot, zPath, dirSync);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int vfstraceAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
pInfo->zVfsName, zPath, flags);
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
/*
** Populate buffer zOut with the full canonical pathname corresponding
** to the pathname in zPath. zOut is guaranteed to point to a buffer
** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
*/
static int vfstraceFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
pInfo->zVfsName, zPath);
rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
return rc;
}
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
return pRoot->xDlOpen(pRoot, zPath);
}
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
pRoot->xDlError(pRoot, nByte, zErrMsg);
vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
}
/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym);
return pRoot->xDlSym(pRoot, p, zSym);
}
/*
** Close the dynamic library handle pHandle.
*/
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
pRoot->xDlClose(pRoot, pHandle);
}
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
return pRoot->xRandomness(pRoot, nByte, zBufOut);
}
/*
** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xSleep(pRoot, nMicro);
}
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xCurrentTime(pRoot, pTimeOut);
}
static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
}
/*
** Return th3 emost recent error code and message
*/
static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xGetLastError(pRoot, iErr, zErr);
}
/*
** Override system calls.
*/
static int vfstraceSetSystemCall(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_syscall_ptr pFunc
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xSetSystemCall(pRoot, zName, pFunc);
}
static sqlite3_syscall_ptr vfstraceGetSystemCall(
sqlite3_vfs *pVfs,
const char *zName
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xGetSystemCall(pRoot, zName);
}
static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xNextSystemCall(pRoot, zName);
}
/*
** Clients invoke this routine to construct a new trace-vfs shim.
**
** Return SQLITE_OK on success.
**
** SQLITE_NOMEM is returned in the case of a memory allocation error.
** SQLITE_NOTFOUND is returned if zOldVfsName does not exist.
*/
int vfstrace_register(
const char *zTraceName, /* Name of the newly constructed VFS */
const char *zOldVfsName, /* Name of the underlying VFS */
int (*xOut)(const char*,void*), /* Output routine. ex: fputs */
void *pOutArg, /* 2nd argument to xOut. ex: stderr */
int makeDefault /* True to make the new VFS the default */
){
sqlite3_vfs *pNew;
sqlite3_vfs *pRoot;
vfstrace_info *pInfo;
int nName;
int nByte;
pRoot = sqlite3_vfs_find(zOldVfsName);
if( pRoot==0 ) return SQLITE_NOTFOUND;
nName = strlen(zTraceName);
nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1;
pNew = sqlite3_malloc( nByte );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, nByte);
pInfo = (vfstrace_info*)&pNew[1];
pNew->iVersion = pRoot->iVersion;
pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file);
pNew->mxPathname = pRoot->mxPathname;
pNew->zName = (char*)&pInfo[1];
memcpy((char*)&pInfo[1], zTraceName, nName+1);
pNew->pAppData = pInfo;
pNew->xOpen = vfstraceOpen;
pNew->xDelete = vfstraceDelete;
pNew->xAccess = vfstraceAccess;
pNew->xFullPathname = vfstraceFullPathname;
pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen;
pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError;
pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym;
pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose;
pNew->xRandomness = vfstraceRandomness;
pNew->xSleep = vfstraceSleep;
pNew->xCurrentTime = vfstraceCurrentTime;
pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError;
if( pNew->iVersion>=2 ){
pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 :
vfstraceCurrentTimeInt64;
if( pNew->iVersion>=3 ){
pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 :
vfstraceSetSystemCall;
pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 :
vfstraceGetSystemCall;
pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 :
vfstraceNextSystemCall;
}
}
pInfo->pRootVfs = pRoot;
pInfo->xOut = xOut;
pInfo->pOutArg = pOutArg;
pInfo->zVfsName = pNew->zName;
pInfo->pTraceVfs = pNew;
vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
pInfo->zVfsName, pRoot->zName);
return sqlite3_vfs_register(pNew, makeDefault);
}
+311
View File
@@ -0,0 +1,311 @@
/*
** 2011 April 02
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements a virtual table that returns the whole numbers
** between 1 and 4294967295, inclusive.
**
** Example:
**
** CREATE VIRTUAL TABLE nums USING wholenumber;
** SELECT value FROM nums WHERE value<10;
**
** Results in:
**
** 1 2 3 4 5 6 7 8 9
*/
#include "sqlite3.h"
#include <assert.h>
#include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* A wholenumber cursor object */
typedef struct wholenumber_cursor wholenumber_cursor;
struct wholenumber_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
unsigned iValue; /* Current value */
unsigned mxValue; /* Maximum value */
};
/* Methods for the wholenumber module */
static int wholenumberConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3_vtab *pNew;
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
memset(pNew, 0, sizeof(*pNew));
return SQLITE_OK;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */
static int wholenumberDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */
/*
** Open a new wholenumber cursor.
*/
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
wholenumber_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Close a wholenumber cursor.
*/
static int wholenumberClose(sqlite3_vtab_cursor *cur){
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** Advance a cursor to its next row of output
*/
static int wholenumberNext(sqlite3_vtab_cursor *cur){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
pCur->iValue++;
return SQLITE_OK;
}
/*
** Return the value associated with a wholenumber.
*/
static int wholenumberColumn(
sqlite3_vtab_cursor *cur,
sqlite3_context *ctx,
int i
){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
sqlite3_result_int64(ctx, pCur->iValue);
return SQLITE_OK;
}
/*
** The rowid.
*/
static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
*pRowid = pCur->iValue;
return SQLITE_OK;
}
/*
** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal
** that the cursor has nothing more to output.
*/
static int wholenumberEof(sqlite3_vtab_cursor *cur){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
return pCur->iValue>pCur->mxValue || pCur->iValue==0;
}
/*
** Called to "rewind" a cursor back to the beginning so that
** it starts its output over again. Always called at least once
** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call.
**
** idxNum Constraints
** ------ ---------------------
** 0 (none)
** 1 value > $argv0
** 2 value >= $argv0
** 4 value < $argv0
** 8 value <= $argv0
**
** 5 value > $argv0 AND value < $argv1
** 6 value >= $argv0 AND value < $argv1
** 9 value > $argv0 AND value <= $argv1
** 10 value >= $argv0 AND value <= $argv1
*/
static int wholenumberFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor;
sqlite3_int64 v;
int i = 0;
pCur->iValue = 1;
pCur->mxValue = 0xffffffff; /* 4294967295 */
if( idxNum & 3 ){
v = sqlite3_value_int64(argv[0]) + (idxNum&1);
if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
i++;
}
if( idxNum & 12 ){
v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
}
return SQLITE_OK;
}
/*
** Search for terms of these forms:
**
** (1) value > $value
** (2) value >= $value
** (4) value < $value
** (8) value <= $value
**
** idxNum is an ORed combination of 1 or 2 with 4 or 8.
*/
static int wholenumberBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
int idxNum = 0;
int argvIdx = 1;
int ltIdx = -1;
int gtIdx = -1;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){
idxNum |= 1;
ltIdx = i;
}
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){
idxNum |= 2;
ltIdx = i;
}
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
idxNum |= 4;
gtIdx = i;
}
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
idxNum |= 8;
gtIdx = i;
}
}
pIdxInfo->idxNum = idxNum;
if( ltIdx>=0 ){
pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++;
pIdxInfo->aConstraintUsage[ltIdx].omit = 1;
}
if( gtIdx>=0 ){
pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx;
pIdxInfo->aConstraintUsage[gtIdx].omit = 1;
}
if( pIdxInfo->nOrderBy==1
&& pIdxInfo->aOrderBy[0].desc==0
){
pIdxInfo->orderByConsumed = 1;
}
pIdxInfo->estimatedCost = (double)1;
return SQLITE_OK;
}
/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module wholenumberModule = {
0, /* iVersion */
wholenumberConnect,
wholenumberConnect,
wholenumberBestIndex,
wholenumberDisconnect,
wholenumberDisconnect,
wholenumberOpen, /* xOpen - open a cursor */
wholenumberClose, /* xClose - close a cursor */
wholenumberFilter, /* xFilter - configure scan constraints */
wholenumberNext, /* xNext - advance a cursor */
wholenumberEof, /* xEof - check for end of scan */
wholenumberColumn, /* xColumn - read data */
wholenumberRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/*
** Register the wholenumber virtual table
*/
int wholenumber_register(sqlite3 *db){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
#endif
return rc;
}
#ifdef SQLITE_TEST
#include <tcl.h>
/*
** Decode a pointer to an sqlite3 object.
*/
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
/*
** Register the echo virtual table module.
*/
static int register_wholenumber_module(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
wholenumber_register(db);
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
*/
int Sqlitetestwholenumber_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
void *clientData;
} aObjCmd[] = {
{ "register_wholenumber_module", register_wholenumber_module, 0 },
};
int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
}
return TCL_OK;
}
#endif /* SQLITE_TEST */
+20 -4
View File
@@ -54,6 +54,7 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
if( pTmpSchema!=pTab->pSchema ){
HashElem *p;
assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
Trigger *pTrig = (Trigger *)sqliteHashData(p);
if( pTrig->pTabSchema==pTab->pSchema
@@ -165,10 +166,14 @@ void sqlite3BeginTrigger(
if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),
zName, sqlite3Strlen30(zName)) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
}
goto trigger_cleanup;
}
@@ -262,7 +267,6 @@ void sqlite3FinishTrigger(
int iDb; /* Database containing the trigger */
Token nameToken; /* Trigger name for error reporting */
pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0;
if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup;
zName = pTrig->zName;
@@ -305,6 +309,7 @@ void sqlite3FinishTrigger(
if( db->init.busy ){
Trigger *pLink = pTrig;
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig);
if( pTrig ){
db->mallocFailed = 1;
@@ -486,15 +491,19 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
zDb = pName->a[0].zDatabase;
zName = pName->a[0].zName;
nName = sqlite3Strlen30(zName);
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName);
if( pTrigger ) break;
}
if( !pTrigger ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0);
}else{
sqlite3CodeVerifyNamedSchema(pParse, zDb);
}
pParse->checkSchema = 1;
goto drop_trigger_cleanup;
@@ -562,7 +571,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3OpenMasterTable(pParse, iDb);
base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0);
sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT);
sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
@@ -577,8 +586,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
** Remove a trigger from the hash tables of the sqlite* pointer.
*/
void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
Hash *pHash = &(db->aDb[iDb].pSchema->trigHash);
Trigger *pTrigger;
Hash *pHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pHash = &(db->aDb[iDb].pSchema->trigHash);
pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0);
if( ALWAYS(pTrigger) ){
if( pTrigger->pSchema==pTrigger->pTabSchema ){
@@ -624,8 +636,12 @@ Trigger *sqlite3TriggersExist(
int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
){
int mask = 0;
Trigger *pList = sqlite3TriggerList(pParse, pTab);
Trigger *pList = 0;
Trigger *p;
if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
pList = sqlite3TriggerList(pParse, pTab);
}
assert( pList==0 || IsVirtual(pTab)==0 );
for(p=pList; p; p=p->pNext){
if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
+1 -3
View File
@@ -128,7 +128,6 @@ void sqlite3Update(
int regNew;
int regOld = 0;
int regRowSet = 0; /* Rowset of rows to be updated */
int regRec; /* Register used for new table record to insert */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
@@ -286,7 +285,6 @@ void sqlite3Update(
}
regNew = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
regRec = ++pParse->nMem;
/* Start the view context. */
if( isView ){
@@ -396,7 +394,7 @@ void sqlite3Update(
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
);
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){
if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
+4 -4
View File
@@ -167,7 +167,7 @@ int sqlite3Utf8Read(
const unsigned char *zIn, /* First byte of UTF-8 character */
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
){
int c;
unsigned int c;
/* Same as READ_UTF8() above but without the zTerm parameter.
** For this routine, we assume the UTF8 string is always zero-terminated.
@@ -410,15 +410,15 @@ int sqlite3Utf8CharLen(const char *zIn, int nByte){
** This has the effect of making sure that the string is well-formed
** UTF-8. Miscoded characters are removed.
**
** The translation is done in-place (since it is impossible for the
** correct UTF-8 encoding to be longer than a malformed encoding).
** The translation is done in-place and aborted if the output
** overruns the input.
*/
int sqlite3Utf8To8(unsigned char *zIn){
unsigned char *zOut = zIn;
unsigned char *zStart = zIn;
u32 c;
while( zIn[0] ){
while( zIn[0] && zOut<=zIn ){
c = sqlite3Utf8Read(zIn, (const u8**)&zIn);
if( c!=0xfffd ){
WRITE_UTF8(zOut, c);
+114 -26
View File
@@ -26,8 +26,8 @@
*/
#ifdef SQLITE_COVERAGE_TEST
void sqlite3Coverage(int x){
static int dummy = 0;
dummy += x;
static unsigned dummy = 0;
dummy += (unsigned)x;
}
#endif
@@ -441,14 +441,17 @@ static int compare2pow63(const char *zNum, int incr){
/*
** Convert zNum to a 64-bit signed integer and write
** the value of the integer into *pNum.
** If zNum is exactly 9223372036854665808, return 2.
** This is a special case as the context will determine
** if it is too big (used as a negative).
** If zNum is not an integer or is an integer that
** is too large to be expressed with 64 bits,
** then return 1. Otherwise return 0.
** Convert zNum to a 64-bit signed integer.
**
** If the zNum value is representable as a 64-bit twos-complement
** integer, then write that value into *pNum and return 0.
**
** If zNum is exactly 9223372036854665808, return 2. This special
** case is broken out because while 9223372036854665808 cannot be a
** signed 64-bit integer, its negative -9223372036854665808 can be.
**
** If zNum is too big for a 64-bit integer and is not
** 9223372036854665808 then return 1.
**
** length is the number of bytes in the string (bytes, not characters).
** The string is not necessarily zero-terminated. The encoding is
@@ -456,7 +459,7 @@ static int compare2pow63(const char *zNum, int incr){
*/
int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
int incr = (enc==SQLITE_UTF8?1:2);
i64 v = 0;
u64 u = 0;
int neg = 0; /* assume positive */
int i;
int c = 0;
@@ -464,20 +467,26 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
const char *zEnd = zNum + length;
if( enc==SQLITE_UTF16BE ) zNum++;
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
if( zNum>=zEnd ) goto do_atoi_calc;
if( *zNum=='-' ){
neg = 1;
zNum+=incr;
}else if( *zNum=='+' ){
zNum+=incr;
if( zNum<zEnd ){
if( *zNum=='-' ){
neg = 1;
zNum+=incr;
}else if( *zNum=='+' ){
zNum+=incr;
}
}
do_atoi_calc:
zStart = zNum;
while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
v = v*10 + c - '0';
u = u*10 + c - '0';
}
if( u>LARGEST_INT64 ){
*pNum = SMALLEST_INT64;
}else if( neg ){
*pNum = -(i64)u;
}else{
*pNum = (i64)u;
}
*pNum = neg ? -v : v;
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
@@ -487,14 +496,25 @@ do_atoi_calc:
return 1;
}else if( i<19*incr ){
/* Less than 19 digits, so we know that it fits in 64 bits */
assert( u<=LARGEST_INT64 );
return 0;
}else{
/* 19-digit numbers must be no larger than 9223372036854775807 if positive
** or 9223372036854775808 if negative. Note that 9223372036854665808
** is 2^63. Return 1 if to large */
c=compare2pow63(zNum, incr);
if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */
return c<neg ? 0 : 1;
/* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
c = compare2pow63(zNum, incr);
if( c<0 ){
/* zNum is less than 9223372036854775808 so it fits */
assert( u<=LARGEST_INT64 );
return 0;
}else if( c>0 ){
/* zNum is greater than 9223372036854775808 so it overflows */
return 1;
}else{
/* zNum is exactly 9223372036854775808. Fits if negative. The
** special case 2 overflow if positive */
assert( u-1==LARGEST_INT64 );
assert( (*pNum)==SMALLEST_INT64 );
return neg ? 0 : 2;
}
}
}
@@ -1060,3 +1080,71 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
return 1;
}
}
/*
** Attempt to add, substract, or multiply the 64-bit signed value iB against
** the other 64-bit signed integer at *pA and store the result in *pA.
** Return 0 on success. Or if the operation would have resulted in an
** overflow, leave *pA unchanged and return 1.
*/
int sqlite3AddInt64(i64 *pA, i64 iB){
i64 iA = *pA;
testcase( iA==0 ); testcase( iA==1 );
testcase( iB==-1 ); testcase( iB==0 );
if( iB>=0 ){
testcase( iA>0 && LARGEST_INT64 - iA == iB );
testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 );
if( iA>0 && LARGEST_INT64 - iA < iB ) return 1;
*pA += iB;
}else{
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 );
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 );
if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
*pA += iB;
}
return 0;
}
int sqlite3SubInt64(i64 *pA, i64 iB){
testcase( iB==SMALLEST_INT64+1 );
if( iB==SMALLEST_INT64 ){
testcase( (*pA)==(-1) ); testcase( (*pA)==0 );
if( (*pA)>=0 ) return 1;
*pA -= iB;
return 0;
}else{
return sqlite3AddInt64(pA, -iB);
}
}
#define TWOPOWER32 (((i64)1)<<32)
#define TWOPOWER31 (((i64)1)<<31)
int sqlite3MulInt64(i64 *pA, i64 iB){
i64 iA = *pA;
i64 iA1, iA0, iB1, iB0, r;
iA1 = iA/TWOPOWER32;
iA0 = iA % TWOPOWER32;
iB1 = iB/TWOPOWER32;
iB0 = iB % TWOPOWER32;
if( iA1*iB1 != 0 ) return 1;
assert( iA1*iB0==0 || iA0*iB1==0 );
r = iA1*iB0 + iA0*iB1;
testcase( r==(-TWOPOWER31)-1 );
testcase( r==(-TWOPOWER31) );
testcase( r==TWOPOWER31 );
testcase( r==TWOPOWER31-1 );
if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1;
r *= TWOPOWER32;
if( sqlite3AddInt64(&r, iA0*iB0) ) return 1;
*pA = r;
return 0;
}
/*
** Compute the absolute value of a 32-bit signed integer, of possible. Or
** if the integer has a value of -2147483648, return +2147483647
*/
int sqlite3AbsInt32(int x){
if( x>=0 ) return x;
if( x==(int)0x80000000 ) return 0x7fffffff;
return -x;
}
+4 -1
View File
@@ -335,8 +335,11 @@ end_of_vacuum:
pDb->pSchema = 0;
}
sqlite3ResetInternalSchema(db, 0);
/* This both clears the schemas and reduces the size of the db->aDb[]
** array. */
sqlite3ResetInternalSchema(db, -1);
return rc;
}
#endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */
+126 -101
View File
@@ -551,7 +551,7 @@ int sqlite3VdbeExec(
Op *pOp; /* Current operation */
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if true */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int checkProgress; /* True if progress callbacks are enabled */
@@ -571,7 +571,7 @@ int sqlite3VdbeExec(
/*** INSERT STACK UNION HERE ***/
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
sqlite3VdbeMutexArrayEnter(p);
sqlite3VdbeEnter(p);
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
@@ -1246,19 +1246,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
iA = pIn1->u.i;
iB = pIn2->u.i;
switch( pOp->opcode ){
case OP_Add: iB += iA; break;
case OP_Subtract: iB -= iA; break;
case OP_Multiply: iB *= iA; break;
case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break;
case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break;
case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break;
case OP_Divide: {
if( iA==0 ) goto arithmetic_result_is_null;
/* Dividing the largest possible negative 64-bit integer (1<<63) by
** -1 returns an integer too large to store in a 64-bit data-type. On
** some architectures, the value overflows to (1<<63). On others,
** a SIGFPE is issued. The following statement normalizes this
** behavior so that all architectures behave as if integer
** overflow occurred.
*/
if( iA==-1 && iB==SMALLEST_INT64 ) iA = 1;
if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math;
iB /= iA;
break;
}
@@ -1272,6 +1265,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
pOut->u.i = iB;
MemSetTypeFlag(pOut, MEM_Int);
}else{
fp_math:
rA = sqlite3VdbeRealValue(pIn1);
rB = sqlite3VdbeRealValue(pIn2);
switch( pOp->opcode ){
@@ -1431,6 +1425,15 @@ case OP_Function: {
if( sqlite3VdbeMemTooBig(pOut) ){
goto too_big;
}
#if 0
/* The app-defined function has done something that as caused this
** statement to expire. (Perhaps the function called sqlite3_exec()
** with a CREATE TABLE statement.)
*/
if( p->expired ) rc = SQLITE_ABORT;
#endif
REGISTER_TRACE(pOp->p3, pOut);
UPDATE_MAX_BLOBSIZE(pOut);
break;
@@ -1466,8 +1469,10 @@ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */
case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */
case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */
case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
i64 a;
i64 b;
i64 iA;
u64 uA;
i64 iB;
u8 op;
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
@@ -1476,16 +1481,38 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
sqlite3VdbeMemSetNull(pOut);
break;
}
a = sqlite3VdbeIntValue(pIn2);
b = sqlite3VdbeIntValue(pIn1);
switch( pOp->opcode ){
case OP_BitAnd: a &= b; break;
case OP_BitOr: a |= b; break;
case OP_ShiftLeft: a <<= b; break;
default: assert( pOp->opcode==OP_ShiftRight );
a >>= b; break;
iA = sqlite3VdbeIntValue(pIn2);
iB = sqlite3VdbeIntValue(pIn1);
op = pOp->opcode;
if( op==OP_BitAnd ){
iA &= iB;
}else if( op==OP_BitOr ){
iA |= iB;
}else if( iB!=0 ){
assert( op==OP_ShiftRight || op==OP_ShiftLeft );
/* If shifting by a negative amount, shift in the other direction */
if( iB<0 ){
assert( OP_ShiftRight==OP_ShiftLeft+1 );
op = 2*OP_ShiftLeft + 1 - op;
iB = iB>(-64) ? -iB : 64;
}
if( iB>=64 ){
iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1;
}else{
memcpy(&uA, &iA, sizeof(uA));
if( op==OP_ShiftLeft ){
uA <<= iB;
}else{
uA >>= iB;
/* Sign-extend on a right shift of a negative number */
if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB);
}
memcpy(&iA, &uA, sizeof(iA));
}
}
pOut->u.i = a;
pOut->u.i = iA;
MemSetTypeFlag(pOut, MEM_Int);
break;
}
@@ -2411,7 +2438,6 @@ case OP_MakeRecord: {
*/
nData = 0; /* Number of bytes of data space */
nHdr = 0; /* Number of bytes of header space */
nByte = 0; /* Data space required for this record */
nZero = 0; /* Number of zero bytes at the end of the record */
nField = pOp->p1;
zAffinity = pOp->p4.z;
@@ -2632,7 +2658,7 @@ case OP_Savepoint: {
}
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
sqlite3ExpirePreparedStatements(db);
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
db->flags = (db->flags | SQLITE_InternChanges);
}
}
@@ -2772,7 +2798,7 @@ case OP_Transaction: {
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (1<<pOp->p1))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
pBt = db->aDb[pOp->p1].pBt;
if( pBt ){
@@ -2828,7 +2854,7 @@ case OP_ReadCookie: { /* out2-prerelease */
assert( pOp->p3<SQLITE_N_BTREE_META );
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
assert( (p->btreeMask & (1<<iDb))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 );
sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta);
pOut->u.i = iMeta;
@@ -2849,9 +2875,10 @@ case OP_SetCookie: { /* in3 */
Db *pDb;
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (1<<pOp->p1))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
pIn3 = &aMem[pOp->p3];
sqlite3VdbeMemIntegerify(pIn3);
/* See note about index shifting on OP_ReadCookie */
@@ -2873,10 +2900,12 @@ case OP_SetCookie: { /* in3 */
break;
}
/* Opcode: VerifyCookie P1 P2 *
/* Opcode: VerifyCookie P1 P2 P3 * *
**
** Check the value of global database parameter number 0 (the
** schema version) and make sure it is equal to P2.
** schema version) and make sure it is equal to P2 and that the
** generation counter on the local schema parse equals P3.
**
** P1 is the database number which is 0 for the main database file
** and 1 for the file holding temporary tables and some higher number
** for auxiliary databases.
@@ -2891,16 +2920,20 @@ case OP_SetCookie: { /* in3 */
*/
case OP_VerifyCookie: {
int iMeta;
int iGen;
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (1<<pOp->p1))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
pBt = db->aDb[pOp->p1].pBt;
if( pBt ){
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
iGen = db->aDb[pOp->p1].pSchema->iGeneration;
}else{
iMeta = 0;
iGen = iMeta = 0;
}
if( iMeta!=pOp->p2 ){
if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
/* If the schema-cookie from the database file matches the cookie
@@ -2920,7 +2953,7 @@ case OP_VerifyCookie: {
sqlite3ResetInternalSchema(db, pOp->p1);
}
sqlite3ExpirePreparedStatements(db);
p->expired = 1;
rc = SQLITE_SCHEMA;
}
break;
@@ -2996,12 +3029,13 @@ case OP_OpenWrite: {
p2 = pOp->p2;
iDb = pOp->p3;
assert( iDb>=0 && iDb<db->nDb );
assert( (p->btreeMask & (1<<iDb))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 );
pDb = &db->aDb[iDb];
pX = pDb->pBt;
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
wrFlag = 1;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->file_format < p->minWriteFileFormat ){
p->minWriteFileFormat = pDb->pSchema->file_format;
}
@@ -3685,7 +3719,6 @@ case OP_NewRowid: { /* out2-prerelease */
** and try again, up to 100 times.
*/
assert( pC->isTable );
cnt = 0;
#ifdef SQLITE_32BIT_ROWID
# define MAX_ROWID 0x7fffffff
@@ -4492,14 +4525,16 @@ case OP_Destroy: { /* out2-prerelease */
}else{
iDb = pOp->p3;
assert( iCnt==1 );
assert( (p->btreeMask & (1<<iDb))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 );
rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved);
pOut->flags = MEM_Int;
pOut->u.i = iMoved;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( rc==SQLITE_OK && iMoved!=0 ){
sqlite3RootPageMoved(&db->aDb[iDb], iMoved, pOp->p1);
resetSchemaOnFault = 1;
sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1);
/* All OP_Destroy operations occur on the same btree */
assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 );
resetSchemaOnFault = iDb+1;
}
#endif
}
@@ -4528,7 +4563,7 @@ case OP_Clear: {
int nChange;
nChange = 0;
assert( (p->btreeMask & (1<<pOp->p2))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
rc = sqlite3BtreeClearTable(
db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0)
);
@@ -4573,7 +4608,7 @@ case OP_CreateTable: { /* out2-prerelease */
pgno = 0;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (1<<pOp->p1))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
@@ -4587,14 +4622,10 @@ case OP_CreateTable: { /* out2-prerelease */
break;
}
/* Opcode: ParseSchema P1 P2 * P4 *
/* Opcode: ParseSchema P1 * * P4 *
**
** Read and parse all entries from the SQLITE_MASTER table of database P1
** that match the WHERE clause P4. P2 is the "force" flag. Always do
** the parsing if P2 is true. If P2 is false, then this routine is a
** no-op if the schema is not currently loaded. In other words, if P2
** is false, the SQLITE_MASTER table is only parsed if the rest of the
** schema is already loaded into the symbol table.
** that match the WHERE clause P4.
**
** This opcode invokes the parser to create a new virtual machine,
** then runs the new virtual machine. It is thus a re-entrant opcode.
@@ -4605,33 +4636,20 @@ case OP_ParseSchema: {
char *zSql;
InitData initData;
/* Any prepared statement that invokes this opcode will hold mutexes
** on every btree. This is a prerequisite for invoking
** sqlite3InitCallback().
*/
#ifdef SQLITE_DEBUG
for(iDb=0; iDb<db->nDb; iDb++){
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
}
#endif
iDb = pOp->p1;
assert( iDb>=0 && iDb<db->nDb );
/* If pOp->p2 is 0, then this opcode is being executed to read a
** single row, for example the row corresponding to a new index
** created by this VDBE, from the sqlite_master table. It only
** does this if the corresponding in-memory schema is currently
** loaded. Otherwise, the new index definition can be loaded along
** with the rest of the schema when it is required.
**
** Although the mutex on the BtShared object that corresponds to
** database iDb (the database containing the sqlite_master table
** read by this instruction) is currently held, it is necessary to
** obtain the mutexes on all attached databases before checking if
** the schema of iDb is loaded. This is because, at the start of
** the sqlite3_exec() call below, SQLite will invoke
** sqlite3BtreeEnterAll(). If all mutexes are not already held, the
** iDb mutex may be temporarily released to avoid deadlock. If
** this happens, then some other thread may delete the in-memory
** schema of database iDb before the SQL statement runs. The schema
** will not be reloaded becuase the db->init.busy flag is set. This
** can result in a "no such table: sqlite_master" or "malformed
** database schema" error being returned to the user.
*/
assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
sqlite3BtreeEnterAll(db);
if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){
assert( DbHasProperty(db, iDb, DB_SchemaLoaded) );
/* Used to be a conditional */ {
zMaster = SCHEMA_TABLE(iDb);
initData.db = db;
initData.iDb = pOp->p1;
@@ -4652,7 +4670,6 @@ case OP_ParseSchema: {
db->init.busy = 0;
}
}
sqlite3BtreeLeaveAll(db);
if( rc==SQLITE_NOMEM ){
goto no_mem;
}
@@ -4753,7 +4770,7 @@ case OP_IntegrityCk: {
}
aRoot[j] = 0;
assert( pOp->p5<db->nDb );
assert( (p->btreeMask & (1<<pOp->p5))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 );
z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot,
(int)pnErr->u.i, &nErr);
sqlite3DbFree(db, aRoot);
@@ -5181,7 +5198,9 @@ case OP_AggStep: {
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
rc = ctx.isError;
}
sqlite3VdbeMemRelease(&ctx.s);
break;
}
@@ -5215,13 +5234,36 @@ case OP_AggFinal: {
}
#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 * * * *
/* Opcode: Checkpoint P1 P2 P3 * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL
** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns
** SQLITE_BUSY or not, respectively. Write the number of pages in the
** WAL after the checkpoint into mem[P3+1] and the number of pages
** in the WAL that have been checkpointed after the checkpoint
** completes into mem[P3+2]. However on an error, mem[P3+1] and
** mem[P3+2] are initialized to -1.
*/
case OP_Checkpoint: {
rc = sqlite3Checkpoint(db, pOp->p1);
int i; /* Loop counter */
int aRes[3]; /* Results */
Mem *pMem; /* Write results here */
aRes[0] = 0;
aRes[1] = aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
|| pOp->p2==SQLITE_CHECKPOINT_FULL
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
);
rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
if( rc==SQLITE_BUSY ){
rc = SQLITE_OK;
aRes[0] = 1;
}
for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){
sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]);
}
break;
};
#endif
@@ -5256,25 +5298,6 @@ case OP_JournalMode: { /* out2-prerelease */
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
/* This opcode is used in two places: PRAGMA journal_mode and ATTACH.
** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called
** when the statment is prepared and so p->aMutex.nMutex>0. All mutexes
** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree()
** is not called when the statement is prepared because it requires the
** iDb index of the database as a parameter, and the database has not
** yet been attached so that index is unavailable. We have to wait
** until runtime (now) to get the mutex on the newly attached database.
** No other mutexes are required by the ATTACH command so this is safe
** to do.
*/
assert( (p->btreeMask & (1<<pOp->p1))!=0 || p->aMutex.nMutex==0 );
if( p->aMutex.nMutex==0 ){
/* This occurs right after ATTACH. Get a mutex on the newly ATTACHed
** database. */
sqlite3VdbeUsesBtree(p, pOp->p1);
sqlite3VdbeMutexArrayEnter(p);
}
pBt = db->aDb[pOp->p1].pBt;
pPager = sqlite3BtreePager(pBt);
eOld = sqlite3PagerGetJournalMode(pPager);
@@ -5372,7 +5395,7 @@ case OP_IncrVacuum: { /* jump */
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (1<<pOp->p1))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
pBt = db->aDb[pOp->p1].pBt;
rc = sqlite3BtreeIncrVacuum(pBt);
if( rc==SQLITE_DONE ){
@@ -5421,7 +5444,7 @@ case OP_TableLock: {
if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){
int p1 = pOp->p1;
assert( p1>=0 && p1<db->nDb );
assert( (p->btreeMask & (1<<p1))!=0 );
assert( (p->btreeMask & (((yDbMask)1)<<p1))!=0 );
assert( isWriteLock==0 || isWriteLock==1 );
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
if( (rc&0xFF)==SQLITE_LOCKED ){
@@ -5910,13 +5933,15 @@ vdbe_error_halt:
sqlite3VdbeHalt(p);
if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
rc = SQLITE_ERROR;
if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, 0);
if( resetSchemaOnFault>0 ){
sqlite3ResetInternalSchema(db, resetSchemaOnFault-1);
}
/* This is the only way out of this procedure. We have to
** release the mutexes on btrees that were acquired at the
** top. */
vdbe_return:
sqlite3BtreeMutexArrayLeave(&p->aMutex);
sqlite3VdbeLeave(p);
return rc;
/* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
+1 -1
View File
@@ -108,7 +108,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */
#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */
#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */
#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */
#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */
#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */
#define P4_REAL (-12) /* P4 is a 64-bit floating point value */
+10 -8
View File
@@ -302,10 +302,10 @@ struct Vdbe {
u8 readOnly; /* True for read-only statements */
u8 isPrepareV2; /* True if prepared with prepare_v2() */
int nChange; /* Number of db changes made since last reset */
int btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
int iStatement; /* Statement number (or 0 if has not opened stmt) */
int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif
@@ -388,6 +388,14 @@ void sqlite3VdbeFrameDelete(VdbeFrame*);
int sqlite3VdbeFrameRestore(VdbeFrame *);
void sqlite3VdbeMemStoreType(Mem *pMem);
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
void sqlite3VdbeEnter(Vdbe*);
void sqlite3VdbeLeave(Vdbe*);
#else
# define sqlite3VdbeEnter(X)
# define sqlite3VdbeLeave(X)
#endif
#ifdef SQLITE_DEBUG
void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
#endif
@@ -398,12 +406,6 @@ int sqlite3VdbeCheckFk(Vdbe *, int);
# define sqlite3VdbeCheckFk(p,i) 0
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
void sqlite3VdbeMutexArrayEnter(Vdbe *p);
#else
# define sqlite3VdbeMutexArrayEnter(p)
#endif
int sqlite3VdbeMemTranslate(Mem*, u8);
#ifdef SQLITE_DEBUG
void sqlite3VdbePrintSql(Vdbe*);
+5 -3
View File
@@ -682,13 +682,11 @@ int sqlite3_data_count(sqlite3_stmt *pStmt){
*/
static Mem *columnMem(sqlite3_stmt *pStmt, int i){
Vdbe *pVm;
int vals;
Mem *pOut;
pVm = (Vdbe *)pStmt;
if( pVm && pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
sqlite3_mutex_enter(pVm->db->mutex);
vals = sqlite3_data_count(pStmt);
pOut = &pVm->pResultSet[i];
}else{
/* If the value passed as the second argument is out of range, return
@@ -706,7 +704,11 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
#if defined(SQLITE_DEBUG) && defined(__GNUC__)
__attribute__((aligned(8)))
#endif
= {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
= {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0,
#ifdef SQLITE_DEBUG
0, 0, /* pScopyFrom, pFiller */
#endif
0, 0 };
if( pVm && ALWAYS(pVm->db) ){
sqlite3_mutex_enter(pVm->db->mutex);
+105 -56
View File
@@ -157,6 +157,12 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
p->expired = 0;
if( op==OP_ParseSchema ){
/* Any program that uses the OP_ParseSchema opcode needs to lock
** all btrees. */
int j;
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
}
#ifdef SQLITE_DEBUG
pOp->zComment = 0;
if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
@@ -457,7 +463,7 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
assert( aOp && !p->db->mallocFailed );
/* Check that sqlite3VdbeUsesBtree() was not called on this VM */
assert( p->aMutex.nMutex==0 );
assert( p->btreeMask==0 );
resolveP2Values(p, pnMaxArg);
*pnOp = p->nOp;
@@ -559,6 +565,7 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
void sqlite3VdbeJumpHere(Vdbe *p, int addr){
assert( addr>=0 );
sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -944,22 +951,80 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**
** The prepared statement has to know in advance which Btree objects
** will be used so that it can acquire mutexes on them all in sorted
** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired
** in order (and released in reverse order) to avoid deadlocks.
** The prepared statements need to know in advance the complete set of
** attached databases that they will be using. A mask of these databases
** is maintained in p->btreeMask and is used for locking and other purposes.
*/
void sqlite3VdbeUsesBtree(Vdbe *p, int i){
int mask;
assert( i>=0 && i<p->db->nDb && i<sizeof(u32)*8 );
assert( i>=0 && i<p->db->nDb && i<(int)sizeof(yDbMask)*8 );
assert( i<(int)sizeof(p->btreeMask)*8 );
mask = ((u32)1)<<i;
if( (p->btreeMask & mask)==0 ){
p->btreeMask |= mask;
sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt);
p->btreeMask |= ((yDbMask)1)<<i;
if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){
p->lockMask |= ((yDbMask)1)<<i;
}
}
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
/*
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
** this routine obtains the mutex associated with each BtShared structure
** that may be accessed by the VM passed as an argument. In doing so it also
** sets the BtShared.db member of each of the BtShared structures, ensuring
** that the correct busy-handler callback is invoked if required.
**
** If SQLite is not threadsafe but does support shared-cache mode, then
** sqlite3BtreeEnter() is invoked to set the BtShared.db variables
** of all of BtShared structures accessible via the database handle
** associated with the VM.
**
** If SQLite is not threadsafe and does not support shared-cache mode, this
** function is a no-op.
**
** The p->btreeMask field is a bitmask of all btrees that the prepared
** statement p will ever use. Let N be the number of bits in p->btreeMask
** corresponding to btrees that use shared cache. Then the runtime of
** this routine is N*N. But as N is rarely more than 1, this should not
** be a problem.
*/
void sqlite3VdbeEnter(Vdbe *p){
int i;
yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
if( p->lockMask==0 ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
for(i=0, mask=1; i<nDb; i++, mask += mask){
if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeEnter(aDb[i].pBt);
}
}
}
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
/*
** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter().
*/
void sqlite3VdbeLeave(Vdbe *p){
int i;
yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
if( p->lockMask==0 ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
for(i=0, mask=1; i<nDb; i++, mask += mask){
if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeLeave(aDb[i].pBt);
}
}
}
#endif
#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
/*
@@ -1517,7 +1582,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
*/
static void closeAllCursors(Vdbe *p){
if( p->pFrame ){
VdbeFrame *pFrame = p->pFrame;
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
}
@@ -1703,7 +1768,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
rc = sqlite3BtreeCommitPhaseTwo(pBt);
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
}
}
if( rc==SQLITE_OK ){
@@ -1835,7 +1900,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
sqlite3BtreeCommitPhaseTwo(pBt);
sqlite3BtreeCommitPhaseTwo(pBt, 1);
}
}
sqlite3EndBenignMalloc();
@@ -1958,33 +2023,6 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
return rc;
}
/*
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
** this routine obtains the mutex associated with each BtShared structure
** that may be accessed by the VM passed as an argument. In doing so it
** sets the BtShared.db member of each of the BtShared structures, ensuring
** that the correct busy-handler callback is invoked if required.
**
** If SQLite is not threadsafe but does support shared-cache mode, then
** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables
** of all of BtShared structures accessible via the database handle
** associated with the VM. Of course only a subset of these structures
** will be accessed by the VM, and we could use Vdbe.btreeMask to figure
** that subset out, but there is no advantage to doing so.
**
** If SQLite is not threadsafe and does not support shared-cache mode, this
** function is a no-op.
*/
#ifndef SQLITE_OMIT_SHARED_CACHE
void sqlite3VdbeMutexArrayEnter(Vdbe *p){
#if SQLITE_THREADSAFE
sqlite3BtreeMutexArrayEnter(&p->aMutex);
#else
sqlite3BtreeEnterAll(p->db);
#endif
}
#endif
/*
** This function is called when a transaction opened by the database
** handle associated with the VM passed as an argument is about to be
@@ -2057,7 +2095,7 @@ int sqlite3VdbeHalt(Vdbe *p){
int isSpecialError; /* Set to true if a 'special' error */
/* Lock all btrees used by the statement */
sqlite3VdbeMutexArrayEnter(p);
sqlite3VdbeEnter(p);
/* Check for one of the special errors */
mrc = p->rc & 0xff;
@@ -2108,17 +2146,22 @@ int sqlite3VdbeHalt(Vdbe *p){
&& db->writeVdbeCnt==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
if( sqlite3VdbeCheckFk(p, 1) ){
sqlite3BtreeMutexArrayLeave(&p->aMutex);
return SQLITE_ERROR;
rc = sqlite3VdbeCheckFk(p, 1);
if( rc!=SQLITE_OK ){
if( NEVER(p->readOnly) ){
sqlite3VdbeLeave(p);
return SQLITE_ERROR;
}
rc = SQLITE_CONSTRAINT;
}else{
/* The auto-commit flag is true, the vdbe program was successful
** or hit an 'OR FAIL' constraint and there are no deferred foreign
** key constraints to hold up the transaction. This means a commit
** is required. */
rc = vdbeCommit(db, p);
}
/* The auto-commit flag is true, the vdbe program was successful
** or hit an 'OR FAIL' constraint and there are no deferred foreign
** key constraints to hold up the transaction. This means a commit
** is required. */
rc = vdbeCommit(db, p);
if( rc==SQLITE_BUSY ){
sqlite3BtreeMutexArrayLeave(&p->aMutex);
if( rc==SQLITE_BUSY && p->readOnly ){
sqlite3VdbeLeave(p);
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
p->rc = rc;
@@ -2185,12 +2228,12 @@ int sqlite3VdbeHalt(Vdbe *p){
/* Rollback or commit any schema changes that occurred. */
if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
db->flags = (db->flags | SQLITE_InternChanges);
}
/* Release the locks */
sqlite3BtreeMutexArrayLeave(&p->aMutex);
sqlite3VdbeLeave(p);
}
/* We have successfully halted and closed the VM. Record this fact. */
@@ -2216,7 +2259,7 @@ int sqlite3VdbeHalt(Vdbe *p){
}
assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
return SQLITE_OK;
return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}
@@ -2492,7 +2535,13 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
if( file_format>=4 && (i&1)==i ){
return 8+(u32)i;
}
u = i<0 ? -i : i;
if( i<0 ){
if( i<(-MAX_6BYTE) ) return 6;
/* Previous test prevents: u = -(-9223372036854775808) */
u = -i;
}else{
u = i;
}
if( u<=127 ) return 1;
if( u<=32767 ) return 2;
if( u<=8388607 ) return 3;
+1
View File
@@ -266,6 +266,7 @@ int sqlite3_blob_open(
/* Configure the OP_VerifyCookie */
sqlite3VdbeChangeP1(v, 1, iDb);
sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration);
/* Make sure a mutex is held on the table to be accessed */
sqlite3VdbeUsesBtree(v, iDb);
+12 -4
View File
@@ -367,7 +367,7 @@ i64 sqlite3VdbeIntValue(Mem *pMem){
}else if( flags & MEM_Real ){
return doubleToInt64(pMem->r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value;
i64 value = 0;
assert( pMem->z || pMem->n==0 );
testcase( pMem->z==0 );
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
@@ -1077,11 +1077,19 @@ int sqlite3ValueFromExpr(
/* This branch happens for multiple negative signs. Ex: -(-5) */
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
sqlite3VdbeMemNumerify(pVal);
pVal->u.i = -1 * pVal->u.i;
/* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */
pVal->r = (double)-1 * pVal->r;
if( pVal->u.i==SMALLEST_INT64 ){
pVal->flags &= MEM_Int;
pVal->flags |= MEM_Real;
pVal->r = (double)LARGEST_INT64;
}else{
pVal->u.i = -pVal->u.i;
}
pVal->r = -pVal->r;
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}else if( op==TK_NULL ){
pVal = sqlite3ValueNew(db);
if( pVal==0 ) goto no_mem;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
else if( op==TK_BLOB ){
+1 -1
View File
@@ -85,7 +85,7 @@ char *sqlite3VdbeExpandSql(
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
sqlite3StrAccumAppend(&out, "-- ", 3);
sqlite3StrAccumAppend(&out, zStart, zRawSql-zStart);
sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
}
}else{
while( zRawSql[0] ){
+6 -6
View File
@@ -48,7 +48,7 @@ static int createModule(
if( pDel==pMod ){
db->mallocFailed = 1;
}
sqlite3ResetInternalSchema(db, 0);
sqlite3ResetInternalSchema(db, -1);
}else if( xDestroy ){
xDestroy(pAux);
}
@@ -145,10 +145,9 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
** that contains table p is held by the caller. See header comments
** above function sqlite3VtabUnlockList() for an explanation of why
** this makes it safe to access the sqlite3.pDisconnect list of any
** database connection that may have an entry in the p->pVTable list. */
assert( db==0 ||
sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt)
);
** database connection that may have an entry in the p->pVTable list.
*/
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
while( pVTable ){
sqlite3 *db2 = pVTable->db;
@@ -372,7 +371,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC);
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
}
@@ -387,6 +386,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
Schema *pSchema = pTab->pSchema;
const char *zName = pTab->zName;
int nName = sqlite3Strlen30(zName);
assert( sqlite3SchemaMutexHeld(db, 0, pSchema) );
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab);
if( pOld ){
db->mallocFailed = 1;
+156 -40
View File
@@ -1558,6 +1558,34 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
return rc;
}
/*
** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
** n. If the attempt fails and parameter xBusy is not NULL, then it is a
** busy-handler function. Invoke it and retry the lock until either the
** lock is successfully obtained or the busy-handler returns 0.
*/
static int walBusyLock(
Wal *pWal, /* WAL connection */
int (*xBusy)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */
int lockIdx, /* Offset of first byte to lock */
int n /* Number of bytes to lock */
){
int rc;
do {
rc = walLockExclusive(pWal, lockIdx, n);
}while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
return rc;
}
/*
** The cache of the wal-index header must be valid to call this function.
** Return the page-size in bytes used by the database.
*/
static int walPagesize(Wal *pWal){
return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
}
/*
** Copy as much content as we can from the WAL back into the database file
** in response to an sqlite3_wal_checkpoint() request or the equivalent.
@@ -1591,8 +1619,10 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
*/
static int walCheckpoint(
Wal *pWal, /* Wal connection */
int eMode, /* One of PASSIVE, FULL or RESTART */
int (*xBusyCall)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags for OsSync() (or 0) */
int nBuf, /* Size of zBuf in bytes */
u8 *zBuf /* Temporary buffer to use */
){
int rc; /* Return code */
@@ -1604,8 +1634,9 @@ static int walCheckpoint(
u32 mxPage; /* Max database page to write */
int i; /* Loop counter */
volatile WalCkptInfo *pInfo; /* The checkpoint status information */
int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */
szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
szPage = walPagesize(pWal);
testcase( szPage<=32768 );
testcase( szPage>=65536 );
pInfo = walCkptInfo(pWal);
@@ -1618,11 +1649,7 @@ static int walCheckpoint(
}
assert( pIter );
/*** TODO: Move this test out to the caller. Make it an assert() here ***/
if( szPage!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
goto walcheckpoint_out;
}
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall;
/* Compute in mxSafeFrame the index of the last frame of the WAL that is
** safe to write into the database. Frames beyond mxSafeFrame might
@@ -1633,14 +1660,15 @@ static int walCheckpoint(
mxPage = pWal->hdr.nPage;
for(i=1; i<WAL_NREADER; i++){
u32 y = pInfo->aReadMark[i];
if( mxSafeFrame>=y ){
if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame );
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
pInfo->aReadMark[i] = READMARK_NOT_USED;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
xBusy = 0;
}else{
goto walcheckpoint_out;
}
@@ -1648,7 +1676,7 @@ static int walCheckpoint(
}
if( pInfo->nBackfill<mxSafeFrame
&& (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
&& (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
){
i64 nSize; /* Current size of database file */
u32 nBackfill = pInfo->nBackfill;
@@ -1701,13 +1729,32 @@ static int walCheckpoint(
/* Release the reader lock held while backfilling */
walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
}else if( rc==SQLITE_BUSY ){
}
if( rc==SQLITE_BUSY ){
/* Reset the return code so as not to report a checkpoint failure
** just because active readers prevent any backfill.
*/
** just because there are active readers. */
rc = SQLITE_OK;
}
/* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal
** file has been copied into the database file, then block until all
** readers have finished using the wal file. This ensures that the next
** process to write to the database restarts the wal file.
*/
if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
assert( pWal->writeLock );
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
rc = SQLITE_BUSY;
}else if( eMode==SQLITE_CHECKPOINT_RESTART ){
assert( mxSafeFrame==pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
}
}
}
walcheckpoint_out:
walIteratorFree(pIter);
return rc;
@@ -1739,7 +1786,9 @@ int sqlite3WalClose(
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf);
rc = sqlite3WalCheckpoint(
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
);
if( rc==SQLITE_OK ){
isDelete = 1;
}
@@ -1951,10 +2000,31 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
assert( pWal->readLock<0 ); /* Not currently locked */
/* Take steps to avoid spinning forever if there is a protocol error. */
/* Take steps to avoid spinning forever if there is a protocol error.
**
** Circumstances that cause a RETRY should only last for the briefest
** instances of time. No I/O or other system calls are done while the
** locks are held, so the locks should not be held for very long. But
** if we are unlucky, another process that is holding a lock might get
** paged out or take a page-fault that is time-consuming to resolve,
** during the few nanoseconds that it is holding the lock. In that case,
** it might take longer than normal for the lock to free.
**
** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few
** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
** is more of a scheduler yield than an actual delay. But on the 10th
** an subsequent retries, the delays start becoming longer and longer,
** so that on the 100th (and last) RETRY we delay for 21 milliseconds.
** The total delay time before giving up is less than 1 second.
*/
if( cnt>5 ){
if( cnt>100 ) return SQLITE_PROTOCOL;
sqlite3OsSleep(pWal->pVfs, 1);
int nDelay = 1; /* Pause time in microseconds */
if( cnt>100 ){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */
sqlite3OsSleep(pWal->pVfs, nDelay);
}
if( !useWal ){
@@ -2036,22 +2106,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
mxI = i;
}
}
if( mxI==0 ){
/* If we get here, it means that all of the aReadMark[] entries between
** 1 and WAL_NREADER-1 are zero. Try to initialize aReadMark[1] to
** be mxFrame, then retry.
*/
rc = walLockExclusive(pWal, WAL_READ_LOCK(1), 1);
if( rc==SQLITE_OK ){
pInfo->aReadMark[1] = pWal->hdr.mxFrame;
walUnlockExclusive(pWal, WAL_READ_LOCK(1), 1);
rc = WAL_RETRY;
}else if( rc==SQLITE_BUSY ){
rc = WAL_RETRY;
}
return rc;
}else{
if( mxReadMark < pWal->hdr.mxFrame ){
/* There was once an "if" here. The extra "{" is to preserve indentation. */
{
if( mxReadMark < pWal->hdr.mxFrame || mxI==0 ){
for(i=1; i<WAL_NREADER; i++){
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
@@ -2064,6 +2121,10 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
}
}
if( mxI==0 ){
assert( rc==SQLITE_BUSY );
return WAL_RETRY;
}
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
if( rc ){
@@ -2124,6 +2185,10 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
do{
rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
}while( rc==WAL_RETRY );
testcase( (rc&0xff)==SQLITE_BUSY );
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
return rc;
}
@@ -2441,6 +2506,8 @@ static int walRestartLog(Wal *pWal){
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
assert( pInfo->nBackfill==pWal->hdr.mxFrame );
if( pInfo->nBackfill>0 ){
u32 salt1;
sqlite3_randomness(4, &salt1);
rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){
/* If all readers are using WAL_READ_LOCK(0) (in other words if no
@@ -2458,7 +2525,7 @@ static int walRestartLog(Wal *pWal){
pWal->nCkpt++;
pWal->hdr.mxFrame = 0;
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
sqlite3_randomness(4, &aSalt[1]);
aSalt[1] = salt1;
walIndexWriteHdr(pWal);
pInfo->nBackfill = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
@@ -2475,6 +2542,10 @@ static int walRestartLog(Wal *pWal){
int notUsed;
rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
}
return rc;
}
@@ -2654,17 +2725,27 @@ int sqlite3WalFrames(
**
** Obtain a CHECKPOINT lock and then backfill as much information as
** we can from WAL into the database.
**
** If parameter xBusy is not NULL, it is a pointer to a busy-handler
** callback. In this case this function runs a blocking checkpoint.
*/
int sqlite3WalCheckpoint(
Wal *pWal, /* Wal connection */
int eMode, /* PASSIVE, FULL or RESTART */
int (*xBusy)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags to sync db file with (or 0) */
int nBuf, /* Size of temporary buffer */
u8 *zBuf /* Temporary buffer to use */
u8 *zBuf, /* Temporary buffer to use */
int *pnLog, /* OUT: Number of frames in WAL */
int *pnCkpt /* OUT: Number of backfilled frames in WAL */
){
int rc; /* Return code */
int isChanged = 0; /* True if a new wal-index header is loaded */
int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 );
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
@@ -2676,11 +2757,45 @@ int sqlite3WalCheckpoint(
}
pWal->ckptLock = 1;
/* Copy data from the log to the database file. */
rc = walIndexReadHdr(pWal, &isChanged);
if( rc==SQLITE_OK ){
rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf);
/* If this is a blocking-checkpoint, then obtain the write-lock as well
** to prevent any writers from running while the checkpoint is underway.
** This has to be done before the call to walIndexReadHdr() below.
**
** If the writer lock cannot be obtained, then a passive checkpoint is
** run instead. Since the checkpointer is not holding the writer lock,
** there is no point in blocking waiting for any readers. Assuming no
** other error occurs, this function will return SQLITE_BUSY to the caller.
*/
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
if( rc==SQLITE_OK ){
pWal->writeLock = 1;
}else if( rc==SQLITE_BUSY ){
eMode2 = SQLITE_CHECKPOINT_PASSIVE;
rc = SQLITE_OK;
}
}
/* Read the wal-index header. */
if( rc==SQLITE_OK ){
rc = walIndexReadHdr(pWal, &isChanged);
}
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf);
}
/* If no error occurred, set the output variables. */
if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
}
}
if( isChanged ){
/* If a new wal-index header was loaded before the checkpoint was
** performed, then the pager-cache associated with pWal is now
@@ -2692,10 +2807,11 @@ int sqlite3WalCheckpoint(
}
/* Release the locks. */
sqlite3WalEndWriteTransaction(pWal);
walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
pWal->ckptLock = 0;
WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
return rc;
return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
}
/* Return the value to pass to a sqlite3_wal_hook callback, the
+20 -15
View File
@@ -20,22 +20,22 @@
#include "sqliteInt.h"
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalEndReadTransaction(z)
# define sqlite3WalRead(v,w,x,y,z) 0
# define sqlite3WalDbsize(y) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWriteTransaction(x) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalRead(v,w,x,y,z) 0
# define sqlite3WalDbsize(y) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWriteTransaction(x) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalSavepoint(y,z)
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
# define sqlite3WalCheckpoint(u,v,w,x) 0
# define sqlite3WalCallback(z) 0
# define sqlite3WalExclusiveMode(y,z) 0
# define sqlite3WalHeapMemory(z) 0
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
# define sqlite3WalCheckpoint(r,s,t,u,v,w,x,y,z) 0
# define sqlite3WalCallback(z) 0
# define sqlite3WalExclusiveMode(y,z) 0
# define sqlite3WalHeapMemory(z) 0
#else
#define WAL_SAVEPOINT_NDATA 4
@@ -86,9 +86,14 @@ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int);
/* Copy pages from the log to the database file */
int sqlite3WalCheckpoint(
Wal *pWal, /* Write-ahead log connection */
int eMode, /* One of PASSIVE, FULL and RESTART */
int (*xBusy)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags to sync db file with (or 0) */
int nBuf, /* Size of buffer nBuf */
u8 *zBuf /* Temporary buffer to use */
u8 *zBuf, /* Temporary buffer to use */
int *pnLog, /* OUT: Number of frames in WAL */
int *pnCkpt /* OUT: Number of backfilled frames in WAL */
);
/* Return the value to pass to a sqlite3_wal_hook callback, the
+394 -92
View File
@@ -18,6 +18,7 @@
*/
#include "sqliteInt.h"
/*
** Trace output macros
*/
@@ -117,6 +118,11 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
#ifdef SQLITE_ENABLE_STAT2
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
# define TERM_VNULL 0x00 /* Disabled if not using stat2 */
#endif
/*
** An instance of the following structure holds all information about a
@@ -210,6 +216,7 @@ struct WhereCost {
#define WO_ISNULL 0x080
#define WO_OR 0x100 /* Two or more OR-connected terms */
#define WO_AND 0x200 /* Two or more AND-connected terms */
#define WO_NOOP 0x800 /* This term does not restrict search space */
#define WO_ALL 0xfff /* Mask of all possible WO_* values */
#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
@@ -392,7 +399,7 @@ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){
*/
static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){
int i;
assert( pMaskSet->n<=sizeof(Bitmask)*8 );
assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
for(i=0; i<pMaskSet->n; i++){
if( pMaskSet->ix[i]==iCursor ){
return ((Bitmask)1)<<i;
@@ -1060,7 +1067,7 @@ static void exprAnalyzeOrTerm(
}else{
sqlite3ExprListDelete(db, pList);
}
pTerm->eOperator = 0; /* case 1 trumps case 2 */
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
}
}
}
@@ -1324,6 +1331,47 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef SQLITE_ENABLE_STAT2
/* When sqlite_stat2 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
** virtual term of that form.
**
** Note that the virtual term must be tagged with TERM_VNULL. This
** TERM_VNULL tag will suppress the not-null check at the beginning
** of the loop. Without the TERM_VNULL flag, the not-null check at
** the start of the loop will prevent any results from being returned.
*/
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
int idxNew;
WhereTerm *pNewTerm;
pNewExpr = sqlite3PExpr(pParse, TK_GT,
sqlite3ExprDup(db, pLeft, 0),
sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0);
idxNew = whereClauseInsert(pWC, pNewExpr,
TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
if( idxNew ){
pNewTerm = &pWC->a[idxNew];
pNewTerm->prereqRight = 0;
pNewTerm->leftCursor = pLeft->iTable;
pNewTerm->u.leftColumn = pLeft->iColumn;
pNewTerm->eOperator = WO_GT;
pNewTerm->iParent = idxTerm;
pTerm = &pWC->a[idxTerm];
pTerm->nChild = 1;
pTerm->wtFlags |= TERM_COPIED;
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
#endif /* SQLITE_ENABLE_STAT2 */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
*/
@@ -1376,6 +1424,7 @@ static int isSortingIndex(
int base, /* Cursor number for the table to be sorted */
ExprList *pOrderBy, /* The ORDER BY clause */
int nEqCol, /* Number of index columns with == constraints */
int wsFlags, /* Index usages flags */
int *pbRev /* Set to 1 if ORDER BY is DESC */
){
int i, j; /* Loop counters */
@@ -1481,11 +1530,14 @@ static int isSortingIndex(
return 1;
}
if( pIdx->onError!=OE_None && i==pIdx->nColumn
&& (wsFlags & WHERE_COLUMN_NULL)==0
&& !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){
/* All terms of this index match some prefix of the ORDER BY clause
** and the index is UNIQUE and no terms on the tail of the ORDER BY
** clause reference other tables in a join. If this is all true then
** the order by clause is superfluous. */
** the order by clause is superfluous. Not that if the matching
** condition is IS NULL then the result is not necessarily unique
** even on a UNIQUE index, so disallow those cases. */
return 1;
}
return 0;
@@ -1722,7 +1774,7 @@ static void bestAutomaticIndex(
pWCEnd = &pWC->a[pWC->nTerm];
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n",
WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n",
pCost->rCost, costTempIdx));
pCost->rCost = costTempIdx;
pCost->plan.nRow = logN + 1;
@@ -1843,7 +1895,7 @@ static void constructAutomaticIndex(
idxCols |= cMask;
pIdx->aiColumn[n] = pTerm->u.leftColumn;
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
pIdx->azColl[n] = pColl->zName;
pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY";
n++;
}
}
@@ -2201,11 +2253,18 @@ static void bestVirtualIndex(
/*
** Argument pIdx is a pointer to an index structure that has an array of
** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column
** stored in Index.aSample. The domain of values stored in said column
** may be thought of as divided into (SQLITE_INDEX_SAMPLES+1) regions.
** Region 0 contains all values smaller than the first sample value. Region
** 1 contains values larger than or equal to the value of the first sample,
** but smaller than the value of the second. And so on.
** stored in Index.aSample. These samples divide the domain of values stored
** the index into (SQLITE_INDEX_SAMPLES+1) regions.
** Region 0 contains all values less than the first sample value. Region
** 1 contains values between the first and second samples. Region 2 contains
** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES
** contains values larger than the last sample.
**
** If the index contains many duplicates of a single value, then it is
** possible that two or more adjacent samples can hold the same value.
** When that is the case, the smallest possible region code is returned
** when roundUp is false and the largest possible region code is returned
** when roundUp is true.
**
** If successful, this function determines which of the regions value
** pVal lies in, sets *piRegion to the region index (a value between 0
@@ -2218,8 +2277,10 @@ static int whereRangeRegion(
Parse *pParse, /* Database connection */
Index *pIdx, /* Index to consider domain of */
sqlite3_value *pVal, /* Value to consider */
int roundUp, /* Return largest valid region if true */
int *piRegion /* OUT: Region of domain in which value lies */
){
assert( roundUp==0 || roundUp==1 );
if( ALWAYS(pVal) ){
IndexSample *aSample = pIdx->aSample;
int i = 0;
@@ -2229,7 +2290,17 @@ static int whereRangeRegion(
double r = sqlite3_value_double(pVal);
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
if( aSample[i].eType==SQLITE_NULL ) continue;
if( aSample[i].eType>=SQLITE_TEXT || aSample[i].u.r>r ) break;
if( aSample[i].eType>=SQLITE_TEXT ) break;
if( roundUp ){
if( aSample[i].u.r>r ) break;
}else{
if( aSample[i].u.r>=r ) break;
}
}
}else if( eType==SQLITE_NULL ){
i = 0;
if( roundUp ){
while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++;
}
}else{
sqlite3 *db = pParse->db;
@@ -2260,7 +2331,7 @@ static int whereRangeRegion(
n = sqlite3ValueBytes(pVal, pColl->enc);
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
int r;
int c;
int eSampletype = aSample[i].eType;
if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
if( (eSampletype!=eType) ) break;
@@ -2274,14 +2345,14 @@ static int whereRangeRegion(
assert( db->mallocFailed );
return SQLITE_NOMEM;
}
r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
sqlite3DbFree(db, zSample);
}else
#endif
{
r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
}
if( r>0 ) break;
if( c-roundUp>=0 ) break;
}
}
@@ -2364,9 +2435,9 @@ static int valueFromExpr(
** constraints.
**
** In the absence of sqlite_stat2 ANALYZE data, each range inequality
** reduces the search space by 2/3rds. Hence a single constraint (x>?)
** results in a return of 33 and a range constraint (x>? AND x<?) results
** in a return of 11.
** reduces the search space by 3/4ths. Hence a single constraint (x>?)
** results in a return of 25 and a range constraint (x>? AND x<?) results
** in a return of 6.
*/
static int whereRangeScanEst(
Parse *pParse, /* Parsing & code generating context */
@@ -2386,15 +2457,21 @@ static int whereRangeScanEst(
int iEst;
int iLower = 0;
int iUpper = SQLITE_INDEX_SAMPLES;
int roundUpUpper = 0;
int roundUpLower = 0;
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pLower ){
Expr *pExpr = pLower->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal);
assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
roundUpLower = (pLower->eOperator==WO_GT) ?1:0;
}
if( rc==SQLITE_OK && pUpper ){
Expr *pExpr = pUpper->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal);
assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0;
}
if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){
@@ -2402,28 +2479,29 @@ static int whereRangeScanEst(
sqlite3ValueFree(pUpperVal);
goto range_est_fallback;
}else if( pLowerVal==0 ){
rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper);
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
if( pLower ) iLower = iUpper/2;
}else if( pUpperVal==0 ){
rc = whereRangeRegion(pParse, p, pLowerVal, &iLower);
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2;
}else{
rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper);
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
if( rc==SQLITE_OK ){
rc = whereRangeRegion(pParse, p, pLowerVal, &iLower);
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
}
}
WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper));
iEst = iUpper - iLower;
testcase( iEst==SQLITE_INDEX_SAMPLES );
assert( iEst<=SQLITE_INDEX_SAMPLES );
if( iEst<1 ){
iEst = 1;
*piEst = 50/SQLITE_INDEX_SAMPLES;
}else{
*piEst = (iEst*100)/SQLITE_INDEX_SAMPLES;
}
sqlite3ValueFree(pLowerVal);
sqlite3ValueFree(pUpperVal);
*piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES;
return rc;
}
range_est_fallback:
@@ -2433,22 +2511,156 @@ range_est_fallback:
UNUSED_PARAMETER(nEq);
#endif
assert( pLower || pUpper );
if( pLower && pUpper ){
*piEst = 11;
}else{
*piEst = 33;
}
*piEst = 100;
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4;
if( pUpper ) *piEst /= 4;
return rc;
}
#ifdef SQLITE_ENABLE_STAT2
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
** column of an index and sqlite_stat2 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
** Write the estimated row count into *pnRow and return SQLITE_OK.
** If unable to make an estimate, leave *pnRow unchanged and return
** non-zero.
**
** This routine can fail if it is unable to load a collating sequence
** required for string comparison, or if unable to allocate memory
** for a UTF conversion required for comparison. The error is stored
** in the pParse structure.
*/
static int whereEqualScanEst(
Parse *pParse, /* Parsing & code generating context */
Index *p, /* The index whose left-most column is pTerm */
Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */
double *pnRow /* Write the revised row estimate here */
){
sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
int iLower, iUpper; /* Range of histogram regions containing pRhs */
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
double nRowEst; /* New estimate of the number of rows */
assert( p->aSample!=0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pExpr ){
rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
if( rc ) goto whereEqualScanEst_cancel;
}else{
pRhs = sqlite3ValueNew(pParse->db);
}
if( pRhs==0 ) return SQLITE_NOTFOUND;
rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower);
if( rc ) goto whereEqualScanEst_cancel;
rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper);
if( rc ) goto whereEqualScanEst_cancel;
WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper));
if( iLower>=iUpper ){
nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2);
if( nRowEst<*pnRow ) *pnRow = nRowEst;
}else{
nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES;
*pnRow = nRowEst;
}
whereEqualScanEst_cancel:
sqlite3ValueFree(pRhs);
return rc;
}
#endif /* defined(SQLITE_ENABLE_STAT2) */
#ifdef SQLITE_ENABLE_STAT2
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
** is a list of values. Example:
**
** WHERE x IN (1,2,3,4)
**
** Write the estimated row count into *pnRow and return SQLITE_OK.
** If unable to make an estimate, leave *pnRow unchanged and return
** non-zero.
**
** This routine can fail if it is unable to load a collating sequence
** required for string comparison, or if unable to allocate memory
** for a UTF conversion required for comparison. The error is stored
** in the pParse structure.
*/
static int whereInScanEst(
Parse *pParse, /* Parsing & code generating context */
Index *p, /* The index whose left-most column is pTerm */
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
double *pnRow /* Write the revised row estimate here */
){
sqlite3_value *pVal = 0; /* One value from list */
int iLower, iUpper; /* Range of histogram regions containing pRhs */
u8 aff; /* Column affinity */
int rc = SQLITE_OK; /* Subfunction return code */
double nRowEst; /* New estimate of the number of rows */
int nSpan = 0; /* Number of histogram regions spanned */
int nSingle = 0; /* Histogram regions hit by a single value */
int nNotFound = 0; /* Count of values that are not constants */
int i; /* Loop counter */
u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */
u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */
assert( p->aSample!=0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
memset(aSpan, 0, sizeof(aSpan));
memset(aSingle, 0, sizeof(aSingle));
for(i=0; i<pList->nExpr; i++){
sqlite3ValueFree(pVal);
rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal);
if( rc ) break;
if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
nNotFound++;
continue;
}
rc = whereRangeRegion(pParse, p, pVal, 0, &iLower);
if( rc ) break;
rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper);
if( rc ) break;
if( iLower>=iUpper ){
aSingle[iLower] = 1;
}else{
assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES );
while( iLower<iUpper ) aSpan[iLower++] = 1;
}
}
if( rc==SQLITE_OK ){
for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){
if( aSpan[i] ){
nSpan++;
}else if( aSingle[i] ){
nSingle++;
}
}
nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES)
+ nNotFound*p->aiRowEst[1];
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
*pnRow = nRowEst;
WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n",
nSpan, nSingle, nNotFound, nRowEst));
}
sqlite3ValueFree(pVal);
return rc;
}
#endif /* defined(SQLITE_ENABLE_STAT2) */
/*
** Find the query plan for accessing a particular table. Write the
** Find the best query plan for accessing a particular table. Write the
** best query plan and its cost into the WhereCost object supplied as the
** last parameter.
**
** The lowest cost plan wins. The cost is an estimate of the amount of
** CPU and disk I/O need to process the request using the selected plan.
** CPU and disk I/O needed to process the requested result.
** Factors that influence cost include:
**
** * The estimated number of rows that will be retrieved. (The
@@ -2467,7 +2679,7 @@ range_est_fallback:
**
** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table
** in the SELECT statement, then no indexes are considered. However, the
** selected plan may still take advantage of the tables built-in rowid
** selected plan may still take advantage of the built-in rowid primary key
** index.
*/
static void bestBtreeIndex(
@@ -2510,9 +2722,11 @@ static void bestBtreeIndex(
wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
eqTermMask = idxEqTermMask;
}else{
/* There is no INDEXED BY clause. Create a fake Index object to
** represent the primary key */
Index *pFirst; /* Any other index on the table */
/* There is no INDEXED BY clause. Create a fake Index object in local
** variable sPk to represent the rowid primary key index. Make this
** fake index the first in a chain of Index objects with all of the real
** indices to follow */
Index *pFirst; /* First of real indices on the table */
memset(&sPk, 0, sizeof(Index));
sPk.nColumn = 1;
sPk.aiColumn = &aiColumnPk;
@@ -2523,6 +2737,8 @@ static void bestBtreeIndex(
aiRowEstPk[1] = 1;
pFirst = pSrc->pTab->pIndex;
if( pSrc->notIndexed==0 ){
/* The real indices of the table are only considered if the
** NOT INDEXED qualifier is omitted from the FROM clause */
sPk.pNext = pFirst;
}
pProbe = &sPk;
@@ -2539,16 +2755,19 @@ static void bestBtreeIndex(
const unsigned int * const aiRowEst = pProbe->aiRowEst;
double cost; /* Cost of using pProbe */
double nRow; /* Estimated number of rows in result set */
double log10N; /* base-10 logarithm of nRow (inexact) */
int rev; /* True to scan in reverse order */
int wsFlags = 0;
Bitmask used = 0;
/* The following variables are populated based on the properties of
** scan being evaluated. They are then used to determine the expected
** index being evaluated. They are then used to determine the expected
** cost and number of rows returned.
**
** nEq:
** Number of equality terms that can be implemented using the index.
** In other words, the number of initial fields in the index that
** are used in == or IN or NOT NULL constraints of the WHERE clause.
**
** nInMul:
** The "in-multiplier". This is an estimate of how many seek operations
@@ -2572,7 +2791,9 @@ static void bestBtreeIndex(
**
** bInEst:
** Set to true if there was at least one "x IN (SELECT ...)" term used
** in determining the value of nInMul.
** in determining the value of nInMul. Note that the RHS of the
** IN operator must be a SELECT, not a value list, for this variable
** to be true.
**
** estBound:
** An estimate on the amount of the table that must be searched. A
@@ -2580,8 +2801,8 @@ static void bestBtreeIndex(
** might reduce this to a value less than 100 to indicate that only
** a fraction of the table needs searching. In the absence of
** sqlite_stat2 ANALYZE data, a single inequality reduces the search
** space to 1/3rd its original size. So an x>? constraint reduces
** estBound to 33. Two constraints (x>? AND x<?) reduce estBound to 11.
** space to 1/4rd its original size. So an x>? constraint reduces
** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6.
**
** bSort:
** Boolean. True if there is an ORDER BY clause that will require an
@@ -2589,25 +2810,31 @@ static void bestBtreeIndex(
** correctly order records).
**
** bLookup:
** Boolean. True if for each index entry visited a lookup on the
** corresponding table b-tree is required. This is always false
** for the rowid index. For other indexes, it is true unless all the
** columns of the table used by the SELECT statement are present in
** the index (such an index is sometimes described as a covering index).
** Boolean. True if a table lookup is required for each index entry
** visited. In other words, true if this is not a covering index.
** This is always false for the rowid primary key index of a table.
** For other indexes, it is true unless all the columns of the table
** used by the SELECT statement are present in the index (such an
** index is sometimes described as a covering index).
** For example, given the index on (a, b), the second of the following
** two queries requires table b-tree lookups, but the first does not.
** two queries requires table b-tree lookups in order to find the value
** of column c, but the first does not because columns a and b are
** both available in the index.
**
** SELECT a, b FROM tbl WHERE a = 1;
** SELECT a, b, c FROM tbl WHERE a = 1;
*/
int nEq;
int bInEst = 0;
int nInMul = 1;
int estBound = 100;
int nBound = 0; /* Number of range constraints seen */
int bSort = 0;
int bLookup = 0;
WhereTerm *pTerm; /* A single term of the WHERE clause */
int nEq; /* Number of == or IN terms matching index */
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
int nInMul = 1; /* Number of distinct equalities to lookup */
int estBound = 100; /* Estimated reduction in search space */
int nBound = 0; /* Number of range constraints seen */
int bSort = 0; /* True if external sort required */
int bLookup = 0; /* True if not a covering index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
#ifdef SQLITE_ENABLE_STAT2
WhereTerm *pFirstTerm = 0; /* First term matching the index */
#endif
/* Determine the values of nEq and nInMul */
for(nEq=0; nEq<pProbe->nColumn; nEq++){
@@ -2619,19 +2846,24 @@ static void bestBtreeIndex(
Expr *pExpr = pTerm->pExpr;
wsFlags |= WHERE_COLUMN_IN;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
nInMul *= 25;
bInEst = 1;
}else if( ALWAYS(pExpr->x.pList) ){
nInMul *= pExpr->x.pList->nExpr + 1;
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nInMul *= pExpr->x.pList->nExpr;
}
}else if( pTerm->eOperator & WO_ISNULL ){
wsFlags |= WHERE_COLUMN_NULL;
}
#ifdef SQLITE_ENABLE_STAT2
if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
#endif
used |= pTerm->prereqRight;
}
/* Determine the value of estBound. */
if( nEq<pProbe->nColumn ){
if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){
int j = pProbe->aiColumn[nEq];
if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
@@ -2662,8 +2894,10 @@ static void bestBtreeIndex(
** in wsFlags. Otherwise, if there is an ORDER BY clause but the index
** will scan rows in a different order, set the bSort variable. */
if( pOrderBy ){
if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0
&& isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev)
if( (wsFlags & WHERE_COLUMN_IN)==0
&& pProbe->bUnordered==0
&& isSortingIndex(pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy,
nEq, wsFlags, &rev)
){
wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
wsFlags |= (rev ? WHERE_REVERSE : 0);
@@ -2694,8 +2928,8 @@ static void bestBtreeIndex(
}
/*
** Estimate the number of rows of output. For an IN operator,
** do not let the estimate exceed half the rows in the table.
** Estimate the number of rows of output. For an "x IN (SELECT...)"
** constraint, do not let the estimate exceed half the rows in the table.
*/
nRow = (double)(aiRowEst[nEq] * nInMul);
if( bInEst && nRow*2>aiRowEst[0] ){
@@ -2703,31 +2937,90 @@ static void bestBtreeIndex(
nInMul = (int)(nRow / aiRowEst[nEq]);
}
/* Assume constant cost to access a row and logarithmic cost to
** do a binary search. Hence, the initial cost is the number of output
** rows plus log2(table-size) times the number of binary searches.
#ifdef SQLITE_ENABLE_STAT2
/* If the constraint is of the form x=VALUE and histogram
** data is available for column x, then it might be possible
** to get a better estimate on the number of rows based on
** VALUE and how common that value is according to the histogram.
*/
cost = nRow + nInMul*estLog(aiRowEst[0]);
if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator==WO_EQ );
testcase( pFirstTerm->eOperator==WO_ISNULL );
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
}else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
}
}
#endif /* SQLITE_ENABLE_STAT2 */
/* Adjust the number of rows and the cost downward to reflect rows
/* Adjust the number of output rows and downward to reflect rows
** that are excluded by range constraints.
*/
nRow = (nRow * (double)estBound) / (double)100;
cost = (cost * (double)estBound) / (double)100;
if( nRow<1 ) nRow = 1;
/* Add in the estimated cost of sorting the result
/* Experiments run on real SQLite databases show that the time needed
** to do a binary search to locate a row in a table or index is roughly
** log10(N) times the time to move from one row to the next row within
** a table or index. The actual times can vary, with the size of
** records being an important factor. Both moves and searches are
** slower with larger records, presumably because fewer records fit
** on one page and hence more pages have to be fetched.
**
** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do
** not give us data on the relative sizes of table and index records.
** So this computation assumes table records are about twice as big
** as index records
*/
if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
/* The cost of a full table scan is a number of move operations equal
** to the number of rows in the table.
**
** We add an additional 4x penalty to full table scans. This causes
** the cost function to err on the side of choosing an index over
** choosing a full scan. This 4x full-scan penalty is an arguable
** decision and one which we expect to revisit in the future. But
** it seems to be working well enough at the moment.
*/
cost = aiRowEst[0]*4;
}else{
log10N = estLog(aiRowEst[0]);
cost = nRow;
if( pIdx ){
if( bLookup ){
/* For an index lookup followed by a table lookup:
** nInMul index searches to find the start of each index range
** + nRow steps through the index
** + nRow table searches to lookup the table entry using the rowid
*/
cost += (nInMul + nRow)*log10N;
}else{
/* For a covering index:
** nInMul index searches to find the initial entry
** + nRow steps through the index
*/
cost += nInMul*log10N;
}
}else{
/* For a rowid primary key lookup:
** nInMult table searches to find the initial entry for each range
** + nRow steps through the table
*/
cost += nInMul*log10N;
}
}
/* Add in the estimated cost of sorting the result. Actual experimental
** measurements of sorting performance in SQLite show that sorting time
** adds C*N*log10(N) to the cost, where N is the number of rows to be
** sorted and C is a factor between 1.95 and 4.3. We will split the
** difference and select C of 3.0.
*/
if( bSort ){
cost += cost*estLog(cost);
cost += nRow*estLog(nRow)*3;
}
/* If all information can be taken directly from the index, we avoid
** doing table lookups. This reduces the cost by half. (Not really -
** this needs to be fixed.)
*/
if( pIdx && bLookup==0 ){
cost /= (double)2;
}
/**** Cost of using this index has now been computed ****/
/* If there are additional constraints on this table that cannot
@@ -2768,15 +3061,19 @@ static void bestBtreeIndex(
}
}else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
if( nSkipRange ){
/* Ignore the first nBound range constraints since the index
/* Ignore the first nSkipRange range constraints since the index
** has already accounted for these */
nSkipRange--;
}else{
/* Assume each additional range constraint reduces the result
** set size by a factor of 3 */
** set size by a factor of 3. Indexed range constraints reduce
** the search space by a larger factor: 4. We make indexed range
** more selective intentionally because of the subjective
** observation that indexed range constraints really are more
** selective in practice, on average. */
nRow /= 3;
}
}else{
}else if( pTerm->eOperator!=WO_NOOP ){
/* Any other expression lowers the output row count by half */
nRow /= 2;
}
@@ -2787,10 +3084,10 @@ static void bestBtreeIndex(
WHERETRACE((
"%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
" notReady=0x%llx nRow=%.2f cost=%.2f used=0x%llx\n",
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n",
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
nEq, nInMul, estBound, bSort, bLookup, wsFlags,
notReady, nRow, cost, used
notReady, log10N, nRow, cost, used
));
/* If this index is the best we have seen so far, then record this
@@ -3614,7 +3911,9 @@ static Bitmask codeOneLoopStart(
if( pRangeStart ){
Expr *pRight = pRangeStart->pExpr->pRight;
sqlite3ExprCode(pParse, pRight, regBase+nEq);
sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){
sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
}
if( zStartAff ){
if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){
/* Since the comparison is to be performed with no conversions
@@ -3653,7 +3952,9 @@ static Bitmask codeOneLoopStart(
Expr *pRight = pRangeEnd->pExpr->pRight;
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
sqlite3ExprCode(pParse, pRight, regBase+nEq);
sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){
sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
}
if( zEndAff ){
if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){
/* Since the comparison is to be performed with no conversions
@@ -3711,7 +4012,13 @@ static Bitmask codeOneLoopStart(
/* Record the instruction used to terminate the loop. Disable
** WHERE clause terms made redundant by the index range scan.
*/
pLevel->op = bRev ? OP_Prev : OP_Next;
if( pLevel->plan.wsFlags & WHERE_UNIQUE ){
pLevel->op = OP_Noop;
}else if( bRev ){
pLevel->op = OP_Prev;
}else{
pLevel->op = OP_Next;
}
pLevel->p1 = iIdxCur;
}else
@@ -3757,7 +4064,6 @@ static Bitmask codeOneLoopStart(
**
*/
WhereClause *pOrWc; /* The OR-clause broken out into subterms */
WhereTerm *pFinal; /* Final subterm within the OR-clause. */
SrcList *pOrTab; /* Shortened table list or OR-clause generation */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
@@ -3773,7 +4079,6 @@ static Bitmask codeOneLoopStart(
assert( pTerm->eOperator==WO_OR );
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
pOrWc = &pTerm->u.pOrInfo->wc;
pFinal = &pOrWc->a[pOrWc->nTerm-1];
pLevel->op = OP_Return;
pLevel->p1 = regReturn;
@@ -3882,7 +4187,6 @@ static Bitmask codeOneLoopStart(
** the use of indices become tests that are evaluated against each row of
** the relevant input tables.
*/
k = 0;
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
Expr *pE;
testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
@@ -3900,7 +4204,6 @@ static Bitmask codeOneLoopStart(
continue;
}
sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL);
k = 1;
pTerm->wtFlags |= TERM_CODED;
}
@@ -4208,8 +4511,6 @@ WhereInfo *sqlite3WhereBegin(
** clause.
*/
notReady = ~(Bitmask)0;
pTabItem = pTabList->a;
pLevel = pWInfo->a;
andFlags = ~0;
WHERETRACE(("*** Optimizer Start ***\n"));
for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
@@ -4320,8 +4621,8 @@ WhereInfo *sqlite3WhereBegin(
** (1) The table must not depend on other tables that have not
** yet run.
**
** (2) A full-table-scan plan cannot supercede another plan unless
** it is an "optimal" plan as defined above.
** (2) A full-table-scan plan cannot supercede indexed plan unless
** the full-table-scan is an "optimal" plan as defined above.
**
** (3) All tables have an INDEXED BY clause or this table lacks an
** INDEXED BY clause or this table uses the specific
@@ -4337,6 +4638,7 @@ WhereInfo *sqlite3WhereBegin(
*/
if( (sCost.used&notReady)==0 /* (1) */
&& (bestJ<0 || (notIndexed&m)!=0 /* (2) */
|| (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
|| (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
&& (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */
|| NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
+19
View File
@@ -840,4 +840,23 @@ do_test alter-14.2 {
} {1 {Cannot add a PRIMARY KEY column}}
#-------------------------------------------------------------------------
# Test that it is not possible to use ALTER TABLE on any system table.
#
set system_table_list {1 sqlite_master}
catchsql ANALYZE
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
ifcapable stat2 { lappend system_table_list 3 sqlite_stat2 }
foreach {tn tbl} $system_table_list {
do_test alter-15.$tn.1 {
catchsql "ALTER TABLE $tbl RENAME TO xyz"
} [list 1 "table $tbl may not be altered"]
do_test alter-15.$tn.2 {
catchsql "ALTER TABLE $tbl ADD COLUMN xyz"
} [list 1 "table $tbl may not be altered"]
}
finish_test

Some files were not shown because too many files have changed in this diff Show More