mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-30 22:11:14 +00:00
track sqlite 3.6.11
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
autom4te.cache
|
||||
Makefile
|
||||
config.h
|
||||
config.log
|
||||
config.status
|
||||
libtool
|
||||
sqlite3.pc
|
||||
|
||||
.deps
|
||||
.libs
|
||||
|
||||
*.lo
|
||||
*.la
|
||||
|
||||
lemon
|
||||
mkkeywordhash
|
||||
sqlite3
|
||||
tsrc
|
||||
|
||||
.target_source
|
||||
keywordhash.h
|
||||
lempar.c
|
||||
opcodes.[ch]
|
||||
parse.[chy]
|
||||
parse.h.temp
|
||||
parse.out
|
||||
sqlite3.[ch]
|
||||
+131
-78
@@ -76,13 +76,17 @@ TLIBS = @LIBS@
|
||||
TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@
|
||||
|
||||
# Enable/disable loadable extensions, and other optional features
|
||||
# based on configuration. (-DSQLITE_OMIT*). The same set of OMIT
|
||||
# flags should be passed to the LEMON parser generator and the
|
||||
# mkkeywordhash tool as well.
|
||||
# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
|
||||
# The same set of OMIT and ENABLE flags should be passed to the
|
||||
# LEMON parser generator and the mkkeywordhash tool as well.
|
||||
OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
|
||||
|
||||
TCC += $(OPT_FEATURE_FLAGS)
|
||||
|
||||
# Add in any optional parameters specified on the make commane line
|
||||
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
|
||||
TCC += $(OPTS)
|
||||
|
||||
# Version numbers and release number for the SQLite being compiled.
|
||||
#
|
||||
VERSION = @VERSION@
|
||||
@@ -106,6 +110,10 @@ HAVE_TCL = @HAVE_TCL@
|
||||
#
|
||||
TCLSH_CMD = @TCLSH_CMD@
|
||||
|
||||
# Where do we want to install the tcl plugin
|
||||
#
|
||||
TCLLIBDIR = @TCLLIBDIR@
|
||||
|
||||
# The suffix used on shared libraries. Ex: ".dll", ".so", ".dylib"
|
||||
#
|
||||
SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
|
||||
@@ -121,7 +129,7 @@ SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
|
||||
#
|
||||
# for more info.
|
||||
#
|
||||
GCOV_CFLAGS1 = -fprofile-arcs -ftest-coverage
|
||||
GCOV_CFLAGS1 = -DSQLITE_COVERAGE_TEST=1 -fprofile-arcs -ftest-coverage
|
||||
GCOV_LDFLAGS1 = -lgcov
|
||||
USE_GCOV = @USE_GCOV@
|
||||
LTCOMPILE_EXTRAS += $(GCOV_CFLAGS$(USE_GCOV))
|
||||
@@ -144,7 +152,7 @@ ALLOWRELEASE = @ALLOWRELEASE@
|
||||
|
||||
# libtool compile/link/install
|
||||
LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(TCC) $(LTCOMPILE_EXTRAS)
|
||||
LTLINK = $(LIBTOOL) --mode=link $(TCC) @LDFLAGS@ $(LTLINK_EXTRAS)
|
||||
LTLINK = $(LIBTOOL) --mode=link $(TCC) $(LTCOMPILE_EXTRAS) @LDFLAGS@ $(LTLINK_EXTRAS)
|
||||
LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL)
|
||||
|
||||
# nawk compatible awk.
|
||||
@@ -158,15 +166,17 @@ NAWK = @AWK@
|
||||
OBJS0 = alter.lo analyze.lo attach.lo auth.lo bitvec.lo btmutex.lo \
|
||||
btree.lo build.lo callback.lo complete.lo date.lo \
|
||||
delete.lo expr.lo fault.lo func.lo global.lo \
|
||||
hash.lo journal.lo insert.lo loadext.lo \
|
||||
main.lo malloc.lo mem1.lo mem2.lo mem3.lo mem4.lo mem5.lo mem6.lo \
|
||||
mutex.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
|
||||
hash.lo journal.lo insert.lo legacy.lo loadext.lo \
|
||||
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
|
||||
memjournal.lo \
|
||||
mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
|
||||
opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \
|
||||
pager.lo parse.lo pragma.lo prepare.lo printf.lo random.lo \
|
||||
select.lo status.lo table.lo tokenize.lo trigger.lo update.lo \
|
||||
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo update.lo \
|
||||
util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbefifo.lo vdbemem.lo \
|
||||
where.lo utf.lo legacy.lo vtab.lo
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo \
|
||||
walker.lo where.lo utf.lo vtab.lo
|
||||
|
||||
# Object files for the amalgamation.
|
||||
#
|
||||
@@ -185,6 +195,7 @@ SRC = \
|
||||
$(TOP)/src/analyze.c \
|
||||
$(TOP)/src/attach.c \
|
||||
$(TOP)/src/auth.c \
|
||||
$(TOP)/src/backup.c \
|
||||
$(TOP)/src/bitvec.c \
|
||||
$(TOP)/src/btmutex.c \
|
||||
$(TOP)/src/btree.c \
|
||||
@@ -208,14 +219,15 @@ SRC = \
|
||||
$(TOP)/src/loadext.c \
|
||||
$(TOP)/src/main.c \
|
||||
$(TOP)/src/malloc.c \
|
||||
$(TOP)/src/mem0.c \
|
||||
$(TOP)/src/mem1.c \
|
||||
$(TOP)/src/mem2.c \
|
||||
$(TOP)/src/mem3.c \
|
||||
$(TOP)/src/mem4.c \
|
||||
$(TOP)/src/mem5.c \
|
||||
$(TOP)/src/mem6.c \
|
||||
$(TOP)/src/memjournal.c \
|
||||
$(TOP)/src/mutex.c \
|
||||
$(TOP)/src/mutex.h \
|
||||
$(TOP)/src/mutex_noop.c \
|
||||
$(TOP)/src/mutex_os2.c \
|
||||
$(TOP)/src/mutex_unix.c \
|
||||
$(TOP)/src/mutex_w32.c \
|
||||
@@ -228,10 +240,15 @@ SRC = \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache.h \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
$(TOP)/src/resolve.c \
|
||||
$(TOP)/src/rowset.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/status.c \
|
||||
$(TOP)/src/shell.c \
|
||||
@@ -252,10 +269,10 @@ SRC = \
|
||||
$(TOP)/src/vdbeapi.c \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbeblob.c \
|
||||
$(TOP)/src/vdbefifo.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.c \
|
||||
$(TOP)/src/walker.c \
|
||||
$(TOP)/src/where.c
|
||||
|
||||
# Generated source code files
|
||||
@@ -292,6 +309,8 @@ SRC += \
|
||||
SRC += \
|
||||
$(TOP)/ext/fts3/fts3.c \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_expr.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.c \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_icu.c \
|
||||
@@ -300,6 +319,7 @@ SRC += \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer1.c
|
||||
SRC += \
|
||||
$(TOP)/ext/icu/sqliteicu.h \
|
||||
$(TOP)/ext/icu/icu.c
|
||||
SRC += \
|
||||
$(TOP)/ext/rtree/rtree.h \
|
||||
@@ -309,6 +329,7 @@ SRC += \
|
||||
#
|
||||
TESTSRC2 = \
|
||||
$(TOP)/src/attach.c \
|
||||
$(TOP)/src/backup.c \
|
||||
$(TOP)/src/bitvec.c \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/build.c \
|
||||
@@ -322,6 +343,8 @@ TESTSRC2 = \
|
||||
$(TOP)/src/os_unix.c \
|
||||
$(TOP)/src/os_win.c \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
@@ -351,16 +374,19 @@ TESTSRC = \
|
||||
$(TOP)/src/test9.c \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_backup.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_func.c \
|
||||
$(TOP)/src/test_hexio.c \
|
||||
$(TOP)/src/test_journal.c \
|
||||
$(TOP)/src/test_malloc.c \
|
||||
$(TOP)/src/test_md5.c \
|
||||
$(TOP)/src/test_mutex.c \
|
||||
$(TOP)/src/test_onefile.c \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_tclvar.c \
|
||||
@@ -398,10 +424,13 @@ HDR += \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer.h
|
||||
HDR += \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_expr.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.h
|
||||
HDR += \
|
||||
$(TOP)/ext/rtree/rtree.h
|
||||
HDR += \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
|
||||
# If using the amalgamation, use sqlite3.c directly to build the test
|
||||
# fixture. Otherwise link against libsqlite3.la. (This distinction is
|
||||
@@ -421,6 +450,9 @@ all: sqlite3.h libsqlite3.la sqlite3$(TEXE) $(HAVE_TCL:1=libtclsqlite3.la)
|
||||
Makefile: $(TOP)/Makefile.in
|
||||
./config.status
|
||||
|
||||
sqlite3.pc: $(TOP)/sqlite3.pc.in
|
||||
./config.status
|
||||
|
||||
# Generate the file "last_change" which contains the date of change
|
||||
# of the most recently modified source code file
|
||||
#
|
||||
@@ -435,8 +467,9 @@ libsqlite3.la: $(LIBOBJ)
|
||||
libtclsqlite3.la: tclsqlite.lo libsqlite3.la
|
||||
$(LTLINK) -o $@ tclsqlite.lo \
|
||||
libsqlite3.la @TCL_STUB_LIB_SPEC@ $(TLIBS) \
|
||||
-rpath "$(libdir)/sqlite" \
|
||||
-version-info "8:6:8"
|
||||
-rpath "$(TCLLIBDIR)" \
|
||||
-version-info "8:6:8" \
|
||||
-avoid-version
|
||||
|
||||
sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
|
||||
$(LTLINK) $(READLINE_FLAGS) \
|
||||
@@ -469,72 +502,72 @@ lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: sqlite3.c
|
||||
$(LTCOMPILE) -c sqlite3.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c sqlite3.c
|
||||
|
||||
# Rules to build individual files
|
||||
#
|
||||
alter.lo: $(TOP)/src/alter.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/alter.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/alter.c
|
||||
|
||||
analyze.lo: $(TOP)/src/analyze.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/analyze.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/analyze.c
|
||||
|
||||
attach.lo: $(TOP)/src/attach.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/attach.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/attach.c
|
||||
|
||||
auth.lo: $(TOP)/src/auth.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/auth.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/auth.c
|
||||
|
||||
bitvec.lo: $(TOP)/src/bitvec.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/bitvec.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/bitvec.c
|
||||
|
||||
btmutex.lo: $(TOP)/src/btmutex.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/btmutex.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/btmutex.c
|
||||
|
||||
btree.lo: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
|
||||
$(LTCOMPILE) -c $(TOP)/src/btree.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/btree.c
|
||||
|
||||
build.lo: $(TOP)/src/build.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/build.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/build.c
|
||||
|
||||
callback.lo: $(TOP)/src/callback.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/callback.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/callback.c
|
||||
|
||||
complete.lo: $(TOP)/src/complete.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/complete.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/complete.c
|
||||
|
||||
date.lo: $(TOP)/src/date.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/date.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/date.c
|
||||
|
||||
delete.lo: $(TOP)/src/delete.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/delete.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/delete.c
|
||||
|
||||
expr.lo: $(TOP)/src/expr.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/expr.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/expr.c
|
||||
|
||||
fault.lo: $(TOP)/src/fault.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/fault.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/fault.c
|
||||
|
||||
func.lo: $(TOP)/src/func.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/func.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/func.c
|
||||
|
||||
global.lo: $(TOP)/src/global.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/global.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/global.c
|
||||
|
||||
hash.lo: $(TOP)/src/hash.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/hash.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/hash.c
|
||||
|
||||
insert.lo: $(TOP)/src/insert.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/insert.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/insert.c
|
||||
|
||||
journal.lo: $(TOP)/src/journal.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/journal.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/journal.c
|
||||
|
||||
legacy.lo: $(TOP)/src/legacy.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/legacy.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/legacy.c
|
||||
|
||||
loadext.lo: $(TOP)/src/loadext.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/loadext.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/loadext.c
|
||||
|
||||
main.lo: $(TOP)/src/main.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/main.c
|
||||
@@ -542,6 +575,9 @@ main.lo: $(TOP)/src/main.c $(HDR)
|
||||
malloc.lo: $(TOP)/src/malloc.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/malloc.c
|
||||
|
||||
mem0.lo: $(TOP)/src/mem0.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem0.c
|
||||
|
||||
mem1.lo: $(TOP)/src/mem1.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem1.c
|
||||
|
||||
@@ -551,18 +587,18 @@ mem2.lo: $(TOP)/src/mem2.c $(HDR)
|
||||
mem3.lo: $(TOP)/src/mem3.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem3.c
|
||||
|
||||
mem4.lo: $(TOP)/src/mem4.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem4.c
|
||||
|
||||
mem5.lo: $(TOP)/src/mem5.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem5.c
|
||||
|
||||
mem6.lo: $(TOP)/src/mem6.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem6.c
|
||||
memjournal.lo: $(TOP)/src/memjournal.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/memjournal.c
|
||||
|
||||
mutex.lo: $(TOP)/src/mutex.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex.c
|
||||
|
||||
mutex_noop.lo: $(TOP)/src/mutex_noop.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_noop.c
|
||||
|
||||
mutex_os2.lo: $(TOP)/src/mutex_os2.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_os2.c
|
||||
|
||||
@@ -573,10 +609,16 @@ mutex_w32.lo: $(TOP)/src/mutex_w32.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_w32.c
|
||||
|
||||
pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
|
||||
$(LTCOMPILE) -c $(TOP)/src/pager.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pager.c
|
||||
|
||||
pcache.lo: $(TOP)/src/pcache.c $(HDR) $(TOP)/src/pcache.h
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pcache.c
|
||||
|
||||
pcache1.lo: $(TOP)/src/pcache1.c $(HDR) $(TOP)/src/pcache.h
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pcache1.c
|
||||
|
||||
opcodes.lo: opcodes.c
|
||||
$(LTCOMPILE) -c opcodes.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c opcodes.c
|
||||
|
||||
opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
|
||||
sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
|
||||
@@ -585,101 +627,107 @@ opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
|
||||
cat parse.h $(TOP)/src/vdbe.c | $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
|
||||
|
||||
os.lo: $(TOP)/src/os.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os.c
|
||||
|
||||
os_unix.lo: $(TOP)/src/os_unix.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os_unix.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_unix.c
|
||||
|
||||
os_win.lo: $(TOP)/src/os_win.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os_win.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_win.c
|
||||
|
||||
os_os2.lo: $(TOP)/src/os_os2.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os_os2.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_os2.c
|
||||
|
||||
parse.lo: parse.c $(HDR)
|
||||
$(LTCOMPILE) -c parse.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c parse.c
|
||||
|
||||
parse.h: parse.c
|
||||
|
||||
parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/addopcodes.awk
|
||||
cp $(TOP)/src/parse.y .
|
||||
./lemon$(BEXE) $(OPTS) $(OPT_FEATURE_FLAGS) parse.y
|
||||
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
mv parse.h parse.h.temp
|
||||
$(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
pragma.lo: $(TOP)/src/pragma.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/pragma.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pragma.c
|
||||
|
||||
prepare.lo: $(TOP)/src/prepare.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/prepare.c
|
||||
|
||||
printf.lo: $(TOP)/src/printf.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/printf.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/printf.c
|
||||
|
||||
random.lo: $(TOP)/src/random.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/random.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/random.c
|
||||
|
||||
resolve.lo: $(TOP)/src/resolve.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/resolve.c
|
||||
|
||||
rowset.lo: $(TOP)/src/rowset.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/rowset.c
|
||||
|
||||
select.lo: $(TOP)/src/select.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/select.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/select.c
|
||||
|
||||
status.lo: $(TOP)/src/status.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/status.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/status.c
|
||||
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in
|
||||
sed -e s/--VERS--/$(RELEASE)/ $(TOP)/src/sqlite.h.in | \
|
||||
sed -e s/--VERSION-NUMBER--/$(VERSION_NUMBER)/ >sqlite3.h
|
||||
|
||||
table.lo: $(TOP)/src/table.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/table.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/table.c
|
||||
|
||||
tclsqlite.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DUSE_TCL_STUBS=1 -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
tokenize.lo: $(TOP)/src/tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/tokenize.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/tokenize.c
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash$(BEXE) $(OPTS) $(OPT_FEATURE_FLAGS) $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash$(BEXE) >keywordhash.h
|
||||
|
||||
trigger.lo: $(TOP)/src/trigger.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/trigger.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/trigger.c
|
||||
|
||||
update.lo: $(TOP)/src/update.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/update.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/update.c
|
||||
|
||||
utf.lo: $(TOP)/src/utf.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/utf.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/utf.c
|
||||
|
||||
util.lo: $(TOP)/src/util.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/util.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/util.c
|
||||
|
||||
vacuum.lo: $(TOP)/src/vacuum.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vacuum.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vacuum.c
|
||||
|
||||
vdbe.lo: $(TOP)/src/vdbe.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbe.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbe.c
|
||||
|
||||
vdbeapi.lo: $(TOP)/src/vdbeapi.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbeapi.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbeapi.c
|
||||
|
||||
vdbeaux.lo: $(TOP)/src/vdbeaux.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbeaux.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbeaux.c
|
||||
|
||||
vdbeblob.lo: $(TOP)/src/vdbeblob.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbeblob.c
|
||||
|
||||
vdbefifo.lo: $(TOP)/src/vdbefifo.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbefifo.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbeblob.c
|
||||
|
||||
vdbemem.lo: $(TOP)/src/vdbemem.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbemem.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbemem.c
|
||||
|
||||
vtab.lo: $(TOP)/src/vtab.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vtab.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c
|
||||
|
||||
walker.lo: $(TOP)/src/walker.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/walker.c
|
||||
|
||||
where.lo: $(TOP)/src/where.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/where.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/where.c
|
||||
|
||||
tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c
|
||||
@@ -717,7 +765,7 @@ sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl
|
||||
$(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL)
|
||||
|
||||
|
||||
install: sqlite3$(BEXE) libsqlite3.la sqlite3.h ${HAVE_TCL:1=tcl_install}
|
||||
install: sqlite3$(BEXE) libsqlite3.la sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install}
|
||||
$(INSTALL) -d $(DESTDIR)$(libdir)
|
||||
$(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir)
|
||||
$(INSTALL) -d $(DESTDIR)$(bindir)
|
||||
@@ -728,8 +776,13 @@ install: sqlite3$(BEXE) libsqlite3.la sqlite3.h ${HAVE_TCL:1=tcl_install}
|
||||
$(INSTALL) -d $(DESTDIR)$(pkgconfigdir)
|
||||
$(INSTALL) -m 0644 sqlite3.pc $(DESTDIR)$(pkgconfigdir)
|
||||
|
||||
tcl_install: libtclsqlite3.la
|
||||
$(TCLSH_CMD) $(TOP)/tclinstaller.tcl $(VERSION)
|
||||
pkgIndex.tcl:
|
||||
echo 'package ifneeded sqlite3 $(RELEASE) [list load $(TCLLIBDIR)/libtclsqlite3.so sqlite3]' > $@
|
||||
tcl_install: libtclsqlite3.la pkgIndex.tcl
|
||||
$(INSTALL) -d $(DESTDIR)$(TCLLIBDIR)
|
||||
$(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)
|
||||
rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a
|
||||
$(INSTALL) -m 0644 pkgIndex.tcl $(DESTDIR)$(TCLLIBDIR)
|
||||
|
||||
clean:
|
||||
rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la
|
||||
|
||||
+635
@@ -0,0 +1,635 @@
|
||||
#!/usr/make
|
||||
#
|
||||
# Makefile for SQLITE on VxWorks
|
||||
|
||||
ifeq ($(FORCPU),)
|
||||
FORCPU = SH32gnule
|
||||
endif
|
||||
|
||||
TOOL_FAMILY = gnu
|
||||
|
||||
include $(WIND_USR)/tool/gnu/make.$(FORCPU)
|
||||
|
||||
#### The toplevel directory of the source tree. This is the directory
|
||||
# that contains this "Makefile.in" and the "configure.in" script.
|
||||
#
|
||||
TOP = ../sqlite-3.6.5
|
||||
|
||||
#### C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
#
|
||||
BCC = gcc -g -O2
|
||||
#BCC = /opt/ancic/bin/c89 -0
|
||||
|
||||
#### If the target operating system supports the "usleep()" system
|
||||
# call, then define the HAVE_USLEEP macro for all C modules.
|
||||
#
|
||||
USLEEP =
|
||||
#USLEEP = -DHAVE_USLEEP=1
|
||||
|
||||
#### If you want the SQLite library to be safe for use within a
|
||||
# multi-threaded program, then define the following macro
|
||||
# appropriately:
|
||||
#
|
||||
THREADSAFE = -DSQLITE_THREADSAFE=1
|
||||
#THREADSAFE = -DSQLITE_THREADSAFE=0
|
||||
|
||||
#### Specify any extra linker options needed to make the library
|
||||
# thread safe
|
||||
#
|
||||
#THREADLIB = -lpthread
|
||||
THREADLIB =
|
||||
|
||||
#### Specify any extra libraries needed to access required functions.
|
||||
#
|
||||
#TLIBS = -lrt # fdatasync on Solaris 8
|
||||
#for x86 vxWorks
|
||||
#TLIBS += TLIBS = $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
#for SH4 shared library
|
||||
TLIBS_SHARED += -L$(WIND_USR)/lib/sh/SH32/commonle/PIC
|
||||
#TLIBS_SHARED += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
#for SH4 static
|
||||
TLIBS += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
|
||||
#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1
|
||||
# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all
|
||||
# malloc()s and free()s in order to track down memory leaks.
|
||||
#
|
||||
# SQLite uses some expensive assert() statements in the inner loop.
|
||||
# You can make the library go almost twice as fast if you compile
|
||||
# with -DNDEBUG=1
|
||||
#
|
||||
#OPTS = -DSQLITE_DEBUG=2
|
||||
#OPTS = -DSQLITE_DEBUG=1
|
||||
#OPTS =
|
||||
OPTS = -DNDEBUG=1 -DSQLITE_OS_UNIX=1 $(THREADSAFE)
|
||||
OPTS += -DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
OPTS += -DSQLITE_ENABLE_LOCKING_STYLE=1
|
||||
OPTS += -DSQLITE_THREAD_OVERRIDE_LOCK=0
|
||||
OPTS += -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
OPTS += -DHAVE_FDATASYNC=1
|
||||
|
||||
#### The suffix to add to executable files. ".exe" for windows.
|
||||
# Nothing for unix.
|
||||
#
|
||||
EXE = .vxe
|
||||
#EXE =
|
||||
|
||||
#### C Compile and options for use in building executables that
|
||||
# will run on the target platform. This is usually the same
|
||||
# as BCC, unless you are cross-compiling.
|
||||
#
|
||||
#TCC = gcc -O6
|
||||
#TCC = gcc -g -O0 -Wall
|
||||
#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
|
||||
#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6
|
||||
TCC = $(CC) $(DEFINE_CC) -O2 -g -mrtp $(CC_ARCH_SPEC) -D_REENTRANT=1 -D_VX_CPU=_VX_$(CPU) -D_VX_TOOL_FAMILY=$(TOOL_FAMILY) -D_VX_TOOL=$(TOOL)
|
||||
TCC += -I$(WIND_USR)/h -I$(WIND_USR)/h/wrn/coreip
|
||||
#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive
|
||||
|
||||
#TCC_SHARED = $(TCC) -fPIC
|
||||
TCC_SHARED = $(TCC)
|
||||
|
||||
#### Tools used to build a static library.
|
||||
#
|
||||
#ARX = ar cr
|
||||
#ARX = /opt/mingw/bin/i386-mingw32-ar cr
|
||||
AR += cr
|
||||
#RANLIB = ranlib
|
||||
#RANLIB = /opt/mingw/bin/i386-mingw32-ranlib
|
||||
|
||||
#MKSHLIB = gcc -shared
|
||||
#SO = so
|
||||
#SHPREFIX = lib
|
||||
MKSHLIB = $(CC) $(DEFINE_CC) -mrtp -shared $(CC_ARCH_SPEC) -D_VX_CPU=_VX_$(CPU) -D_VX_TOOL_FAMILY=$(TOOL_FAMILY) -D_VX_TOOL=$(TOOL)
|
||||
SO = so
|
||||
SHPREFIX = lib
|
||||
|
||||
#### Extra compiler options needed for programs that use the TCL library.
|
||||
#
|
||||
#TCL_FLAGS =
|
||||
#TCL_FLAGS = -DSTATIC_BUILD=1
|
||||
TCL_FLAGS = -I/home/drh/tcltk/8.4linux
|
||||
#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1
|
||||
#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux
|
||||
|
||||
#### Linker options needed to link against the TCL library.
|
||||
#
|
||||
#LIBTCL = -ltcl -lm -ldl
|
||||
LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl
|
||||
#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt
|
||||
#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc
|
||||
|
||||
#### Additional objects for SQLite library when TCL support is enabled.
|
||||
TCLOBJ =
|
||||
#TCLOBJ = tclsqlite.o
|
||||
|
||||
#### Compiler options needed for programs that use the readline() library.
|
||||
#
|
||||
READLINE_FLAGS =
|
||||
#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline
|
||||
|
||||
#### Linker options needed by programs using readline() must link against.
|
||||
#
|
||||
LIBREADLINE =
|
||||
#LIBREADLINE = -static -lreadline -ltermcap
|
||||
|
||||
#### Which "awk" program provides nawk compatibilty
|
||||
#
|
||||
# NAWK = nawk
|
||||
NAWK = awk
|
||||
|
||||
|
||||
#### Pasted and adapted main.mk file
|
||||
###############################################################################
|
||||
# The following macros should be defined before this script is
|
||||
# invoked:
|
||||
#
|
||||
# TOP The toplevel directory of the source tree. This is the
|
||||
# directory that contains this "Makefile.in" and the
|
||||
# "configure.in" script.
|
||||
#
|
||||
# BCC C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
#
|
||||
# THREADLIB Specify any extra linker options needed to make the library
|
||||
# thread safe
|
||||
#
|
||||
# OPTS Extra compiler command-line options.
|
||||
#
|
||||
# EXE The suffix to add to executable files. ".exe" for windows
|
||||
# and "" for Unix.
|
||||
#
|
||||
# TCC C Compiler and options for use in building executables that
|
||||
# will run on the target platform. This is usually the same
|
||||
# as BCC, unless you are cross-compiling.
|
||||
#
|
||||
# AR Tools used to build a static library.
|
||||
# RANLIB
|
||||
#
|
||||
# TCL_FLAGS Extra compiler options needed for programs that use the
|
||||
# TCL library.
|
||||
#
|
||||
# LIBTCL Linker options needed to link against the TCL library.
|
||||
#
|
||||
# READLINE_FLAGS Compiler options needed for programs that use the
|
||||
# readline() library.
|
||||
#
|
||||
# LIBREADLINE Linker options needed by programs using readline() must
|
||||
# link against.
|
||||
#
|
||||
# NAWK Nawk compatible awk program. Older (obsolete?) solaris
|
||||
# systems need this to avoid using the original AT&T AWK.
|
||||
#
|
||||
# Once the macros above are defined, the rest of this make script will
|
||||
# build the SQLite library and testing tools.
|
||||
################################################################################
|
||||
|
||||
# This is how we compile
|
||||
#
|
||||
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
|
||||
TCCX_SHARED = $(TCC_SHARED) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
bitvec.o btmutex.o btree.o build.o \
|
||||
callback.o complete.o date.o delete.o \
|
||||
expr.o fault.o func.o global.o hash.o \
|
||||
icu.o insert.o journal.o legacy.o loadext.o \
|
||||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o memjournal.o \
|
||||
mutex.o mutex_os2.o mutex_unix.o mutex_w32.o mutex_noop.o \
|
||||
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
pager.o parse.o pcache.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \
|
||||
walker.o where.o utf.o vtab.o
|
||||
|
||||
|
||||
# All of the source code files.
|
||||
#
|
||||
SRC = \
|
||||
$(TOP)/src/alter.c \
|
||||
$(TOP)/src/analyze.c \
|
||||
$(TOP)/src/attach.c \
|
||||
$(TOP)/src/auth.c \
|
||||
$(TOP)/src/bitvec.c \
|
||||
$(TOP)/src/btmutex.c \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/btree.h \
|
||||
$(TOP)/src/btreeInt.h \
|
||||
$(TOP)/src/build.c \
|
||||
$(TOP)/src/callback.c \
|
||||
$(TOP)/src/complete.c \
|
||||
$(TOP)/src/date.c \
|
||||
$(TOP)/src/delete.c \
|
||||
$(TOP)/src/expr.c \
|
||||
$(TOP)/src/fault.c \
|
||||
$(TOP)/src/func.c \
|
||||
$(TOP)/src/global.c \
|
||||
$(TOP)/src/hash.c \
|
||||
$(TOP)/src/hash.h \
|
||||
$(TOP)/src/hwtime.h \
|
||||
$(TOP)/src/insert.c \
|
||||
$(TOP)/src/journal.c \
|
||||
$(TOP)/src/legacy.c \
|
||||
$(TOP)/src/loadext.c \
|
||||
$(TOP)/src/main.c \
|
||||
$(TOP)/src/malloc.c \
|
||||
$(TOP)/src/mem0.c \
|
||||
$(TOP)/src/mem1.c \
|
||||
$(TOP)/src/mem2.c \
|
||||
$(TOP)/src/mem3.c \
|
||||
$(TOP)/src/mem5.c \
|
||||
$(TOP)/src/memjournal.c \
|
||||
$(TOP)/src/mutex.c \
|
||||
$(TOP)/src/mutex.h \
|
||||
$(TOP)/src/mutex_os2.c \
|
||||
$(TOP)/src/mutex_unix.c \
|
||||
$(TOP)/src/mutex_w32.c \
|
||||
$(TOP)/src/mutex_noop.c \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_os2.c \
|
||||
$(TOP)/src/os_unix.c \
|
||||
$(TOP)/src/os_win.c \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache.h \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
$(TOP)/src/resolve.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/status.c \
|
||||
$(TOP)/src/shell.c \
|
||||
$(TOP)/src/sqlite.h.in \
|
||||
$(TOP)/src/sqlite3ext.h \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/table.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/trigger.c \
|
||||
$(TOP)/src/utf.c \
|
||||
$(TOP)/src/update.c \
|
||||
$(TOP)/src/util.c \
|
||||
$(TOP)/src/vacuum.c \
|
||||
$(TOP)/src/vdbe.c \
|
||||
$(TOP)/src/vdbe.h \
|
||||
$(TOP)/src/vdbeapi.c \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbeblob.c \
|
||||
$(TOP)/src/vdbefifo.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.c \
|
||||
$(TOP)/src/walker.c \
|
||||
$(TOP)/src/where.c
|
||||
|
||||
# Source code for extensions
|
||||
#
|
||||
SRC += \
|
||||
$(TOP)/ext/fts1/fts1.c \
|
||||
$(TOP)/ext/fts1/fts1.h \
|
||||
$(TOP)/ext/fts1/fts1_hash.c \
|
||||
$(TOP)/ext/fts1/fts1_hash.h \
|
||||
$(TOP)/ext/fts1/fts1_porter.c \
|
||||
$(TOP)/ext/fts1/fts1_tokenizer.h \
|
||||
$(TOP)/ext/fts1/fts1_tokenizer1.c
|
||||
SRC += \
|
||||
$(TOP)/ext/fts2/fts2.c \
|
||||
$(TOP)/ext/fts2/fts2.h \
|
||||
$(TOP)/ext/fts2/fts2_hash.c \
|
||||
$(TOP)/ext/fts2/fts2_hash.h \
|
||||
$(TOP)/ext/fts2/fts2_icu.c \
|
||||
$(TOP)/ext/fts2/fts2_porter.c \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer.h \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer.c \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer1.c
|
||||
SRC += \
|
||||
$(TOP)/ext/fts3/fts3.c \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.c \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_icu.c \
|
||||
$(TOP)/ext/fts3/fts3_porter.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.h \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer1.c
|
||||
SRC += \
|
||||
$(TOP)/ext/icu/icu.c \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
SRC += \
|
||||
$(TOP)/ext/rtree/rtree.h \
|
||||
$(TOP)/ext/rtree/rtree.c
|
||||
|
||||
|
||||
# Generated source code files
|
||||
#
|
||||
SRC += \
|
||||
keywordhash.h \
|
||||
opcodes.c \
|
||||
opcodes.h \
|
||||
parse.c \
|
||||
parse.h \
|
||||
sqlite3.h
|
||||
|
||||
|
||||
# Source code to the test files.
|
||||
#
|
||||
TESTSRC = \
|
||||
$(TOP)/src/test1.c \
|
||||
$(TOP)/src/test2.c \
|
||||
$(TOP)/src/test3.c \
|
||||
$(TOP)/src/test4.c \
|
||||
$(TOP)/src/test5.c \
|
||||
$(TOP)/src/test6.c \
|
||||
$(TOP)/src/test7.c \
|
||||
$(TOP)/src/test8.c \
|
||||
$(TOP)/src/test9.c \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_func.c \
|
||||
$(TOP)/src/test_hexio.c \
|
||||
$(TOP)/src/test_malloc.c \
|
||||
$(TOP)/src/test_md5.c \
|
||||
$(TOP)/src/test_mutex.c \
|
||||
$(TOP)/src/test_onefile.c \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_tclvar.c \
|
||||
$(TOP)/src/test_thread.c \
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
|
||||
TESTSRC2 = \
|
||||
$(TOP)/src/attach.c $(TOP)/src/btree.c $(TOP)/src/build.c $(TOP)/src/date.c \
|
||||
$(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/os.c \
|
||||
$(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
|
||||
$(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \
|
||||
$(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
HDR = \
|
||||
$(TOP)/src/btree.h \
|
||||
$(TOP)/src/btreeInt.h \
|
||||
$(TOP)/src/hash.h \
|
||||
$(TOP)/src/hwtime.h \
|
||||
keywordhash.h \
|
||||
$(TOP)/src/mutex.h \
|
||||
opcodes.h \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/pcache.h \
|
||||
parse.h \
|
||||
sqlite3.h \
|
||||
$(TOP)/src/sqlite3ext.h \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/vdbe.h \
|
||||
$(TOP)/src/vdbeInt.h
|
||||
|
||||
# Header files used by extensions
|
||||
#
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/fts1/fts1.h \
|
||||
$(TOP)/ext/fts1/fts1_hash.h \
|
||||
$(TOP)/ext/fts1/fts1_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/fts2/fts2.h \
|
||||
$(TOP)/ext/fts2/fts2_hash.h \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/rtree/rtree.h
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
#
|
||||
all: sqlite3.h libsqlite3.a sqlite3$(EXE)
|
||||
|
||||
libsqlite3.a: $(LIBOBJ)
|
||||
$(AR) libsqlite3.a $(LIBOBJ)
|
||||
$(RANLIB) libsqlite3.a
|
||||
|
||||
$(SHPREFIX)sqlite3.$(SO): $(LIBOBJ)
|
||||
$(MKSHLIB) -o $(SHPREFIX)sqlite3.$(SO) $(LIBOBJ) $(TLIBS_SHARED)
|
||||
|
||||
sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
|
||||
$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \
|
||||
$(TOP)/src/shell.c \
|
||||
$(LIBREADLINE) $(TLIBS) $(THREADLIB) -L. -lsqlite3
|
||||
|
||||
# This target creates a directory named "tsrc" and fills it with
|
||||
# copies of all of the C source code and header files needed to
|
||||
# build on the target system. Some of the C source code and header
|
||||
# files are automatically generated. This target takes care of
|
||||
# all that automatic generation.
|
||||
#
|
||||
target_source: $(SRC)
|
||||
rm -rf tsrc
|
||||
mkdir tsrc
|
||||
cp -f $(SRC) tsrc
|
||||
rm tsrc/sqlite.h.in tsrc/parse.y
|
||||
touch target_source
|
||||
|
||||
sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
tclsh $(TOP)/tool/mksqlite3c.tcl
|
||||
cp sqlite3.c tclsqlite3.c
|
||||
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
|
||||
|
||||
fts2amal.c: target_source $(TOP)/ext/fts2/mkfts2amal.tcl
|
||||
tclsh $(TOP)/ext/fts2/mkfts2amal.tcl
|
||||
|
||||
fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl
|
||||
tclsh $(TOP)/ext/fts3/mkfts3amal.tcl
|
||||
|
||||
# Rules to build the LEMON compiler generator
|
||||
#
|
||||
lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
||||
$(BCC) -o lemon $(TOP)/tool/lemon.c
|
||||
cp $(TOP)/tool/lempar.c .
|
||||
|
||||
# Rules to build individual *.o files from generated *.c files. This
|
||||
# applies to:
|
||||
#
|
||||
# parse.o
|
||||
# opcodes.o
|
||||
#
|
||||
%.o: %.c $(HDR)
|
||||
$(TCCX_SHARED) -c $<
|
||||
|
||||
# Rules to build individual *.o files from files in the src directory.
|
||||
#
|
||||
%.o: $(TOP)/src/%.c $(HDR)
|
||||
$(TCCX_SHARED) -c $<
|
||||
|
||||
tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(TCCX_SHARED) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
|
||||
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
|
||||
sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
|
||||
cat parse.h $(TOP)/src/vdbe.c | \
|
||||
$(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
|
||||
|
||||
# Rules to build parse.c and parse.h - the outputs of lemon.
|
||||
#
|
||||
parse.h: parse.c
|
||||
|
||||
parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
|
||||
cp $(TOP)/src/parse.y .
|
||||
rm -f parse.h
|
||||
./lemon $(OPTS) parse.y
|
||||
mv parse.h parse.h.temp
|
||||
awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in
|
||||
sed -e s/--VERS--/`cat ${TOP}/VERSION`/ \
|
||||
-e s/--VERSION-NUMBER--/`cat ${TOP}/VERSION | sed 's/[^0-9]/ /g' | $(NAWK) '{printf "%d%03d%03d",$$1,$$2,$$3}'`/ \
|
||||
$(TOP)/src/sqlite.h.in >sqlite3.h
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash >keywordhash.h
|
||||
|
||||
|
||||
|
||||
# Rules to build the extension objects.
|
||||
#
|
||||
icu.o: $(TOP)/ext/icu/icu.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/icu/icu.c
|
||||
|
||||
fts2.o: $(TOP)/ext/fts2/fts2.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2.c
|
||||
|
||||
fts2_hash.o: $(TOP)/ext/fts2/fts2_hash.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_hash.c
|
||||
|
||||
fts2_icu.o: $(TOP)/ext/fts2/fts2_icu.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_icu.c
|
||||
|
||||
fts2_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_porter.c
|
||||
|
||||
fts2_tokenizer.o: $(TOP)/ext/fts2/fts2_tokenizer.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
|
||||
fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c
|
||||
|
||||
fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c
|
||||
|
||||
fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c
|
||||
|
||||
fts3_icu.o: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_icu.c
|
||||
|
||||
fts3_porter.o: $(TOP)/ext/fts3/fts3_porter.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_porter.c
|
||||
|
||||
fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
|
||||
fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c
|
||||
|
||||
rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
|
||||
$(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
|
||||
|
||||
|
||||
# Rules for building test programs and for running tests
|
||||
#
|
||||
tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a
|
||||
$(TCCX_SHARED) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
|
||||
$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
|
||||
|
||||
|
||||
# Rules to build the 'testfixture' application.
|
||||
#
|
||||
TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
|
||||
|
||||
testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
|
||||
$(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \
|
||||
-o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite3.a
|
||||
|
||||
amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
|
||||
$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
|
||||
-o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
|
||||
|
||||
fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
|
||||
-DSQLITE_ENABLE_FTS3=1 \
|
||||
$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c \
|
||||
-o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
|
||||
|
||||
fulltest: testfixture$(EXE) sqlite3$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/all.test
|
||||
|
||||
soaktest: testfixture$(EXE) sqlite3$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/all.test -soak 1
|
||||
|
||||
test: testfixture$(EXE) sqlite3$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/veryquick.test
|
||||
|
||||
sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \
|
||||
$(TOP)/tool/spaceanal.tcl
|
||||
sed \
|
||||
-e '/^#/d' \
|
||||
-e 's,\\,\\\\,g' \
|
||||
-e 's,",\\",g' \
|
||||
-e 's,^,",' \
|
||||
-e 's,$$,\\n",' \
|
||||
$(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h
|
||||
$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
|
||||
-DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_DEBUG=1 -DSQLITE_PRIVATE="" \
|
||||
$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
|
||||
-o sqlite3_analyzer$(EXE) \
|
||||
$(LIBTCL) $(THREADLIB)
|
||||
|
||||
TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO)
|
||||
$(TEST_EXTENSION): $(TOP)/src/test_loadext.c
|
||||
$(MKSHLIB) $(TOP)/src/test_loadext.c -o $(TEST_EXTENSION)
|
||||
|
||||
extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
|
||||
./testfixture$(EXE) $(TOP)/test/loadext.test
|
||||
|
||||
clean:
|
||||
rm -f *.o sqlite3$(EXE) libsqlite3.a sqlite3.h opcodes.*
|
||||
rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc target_source
|
||||
rm -f testloadext.dll libtestloadext.so
|
||||
rm -f sqlite3.c fts?amal.c tclsqlite3.c
|
||||
rm -f $(SHPREFIX)sqlite3.$(SO)
|
||||
Vendored
+105
-57
@@ -1,4 +1,4 @@
|
||||
# generated automatically by aclocal 1.10.1 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.10.2 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
@@ -387,12 +387,12 @@ m4_define([lt_decl_dquote_varnames],
|
||||
# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
|
||||
# ---------------------------------------------------
|
||||
m4_define([lt_decl_varnames_tagged],
|
||||
[_$0(m4_quote(m4_default([$1], [[, ]])),
|
||||
m4_quote(m4_if([$2], [],
|
||||
m4_quote(lt_decl_tag_varnames),
|
||||
m4_quote(m4_shift($@)))),
|
||||
m4_split(m4_normalize(m4_quote(_LT_TAGS))))])
|
||||
m4_define([_lt_decl_varnames_tagged], [lt_combine([$1], [$2], [_], $3)])
|
||||
[m4_assert([$# <= 2])dnl
|
||||
_$0(m4_quote(m4_default([$1], [[, ]])),
|
||||
m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
|
||||
m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
|
||||
m4_define([_lt_decl_varnames_tagged],
|
||||
[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
|
||||
|
||||
|
||||
# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
|
||||
@@ -952,10 +952,10 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
|
||||
_lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
|
||||
darwin1.*)
|
||||
_lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
|
||||
darwin*) # darwin 5.x on
|
||||
darwin*) # darwin 5.x on
|
||||
# if running on 10.5 or later, the deployment target defaults
|
||||
# to the OS version, if on x86, and 10.4, the deployment
|
||||
# target defaults to 10.4. Don't you love it?
|
||||
# target defaults to 10.4. Don't you love it?
|
||||
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
|
||||
10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
|
||||
_lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
|
||||
@@ -997,7 +997,11 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES],
|
||||
_LT_TAGVAR(whole_archive_flag_spec, $1)=''
|
||||
_LT_TAGVAR(link_all_deplibs, $1)=yes
|
||||
_LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
|
||||
if test "$GCC" = "yes"; then
|
||||
case $cc_basename in
|
||||
ifort*) _lt_dar_can_shared=yes ;;
|
||||
*) _lt_dar_can_shared=$GCC ;;
|
||||
esac
|
||||
if test "$_lt_dar_can_shared" = "yes"; then
|
||||
output_verbose_link_cmd=echo
|
||||
_LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
|
||||
_LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
|
||||
@@ -1519,7 +1523,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
|
||||
lt_cv_sys_max_cmd_len=-1;
|
||||
;;
|
||||
|
||||
cygwin* | mingw*)
|
||||
cygwin* | mingw* | cegcc*)
|
||||
# On Win9x/ME, this test blows up -- it succeeds, but takes
|
||||
# about 5 minutes as the teststring grows exponentially.
|
||||
# Worse, since 9x/ME are not pre-emptively multitasking,
|
||||
@@ -1687,10 +1691,6 @@ else
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" void exit (int);
|
||||
#endif
|
||||
|
||||
void fnord() { int i=42;}
|
||||
int main ()
|
||||
{
|
||||
@@ -1706,7 +1706,7 @@ int main ()
|
||||
else
|
||||
puts (dlerror ());
|
||||
|
||||
exit (status);
|
||||
return status;
|
||||
}]
|
||||
_LT_EOF
|
||||
if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
|
||||
@@ -1745,7 +1745,7 @@ else
|
||||
lt_cv_dlopen_self=yes
|
||||
;;
|
||||
|
||||
mingw* | pw32*)
|
||||
mingw* | pw32* | cegcc*)
|
||||
lt_cv_dlopen="LoadLibrary"
|
||||
lt_cv_dlopen_libs=
|
||||
;;
|
||||
@@ -2042,6 +2042,7 @@ m4_defun([_LT_SYS_DYNAMIC_LINKER],
|
||||
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||
m4_require([_LT_DECL_EGREP])dnl
|
||||
m4_require([_LT_FILEUTILS_DEFAULTS])dnl
|
||||
m4_require([_LT_DECL_OBJDUMP])dnl
|
||||
m4_require([_LT_DECL_SED])dnl
|
||||
AC_MSG_CHECKING([dynamic linker characteristics])
|
||||
m4_if([$1],
|
||||
@@ -2206,14 +2207,14 @@ bsdi[[45]]*)
|
||||
# libtool to hard-code these into programs
|
||||
;;
|
||||
|
||||
cygwin* | mingw* | pw32*)
|
||||
cygwin* | mingw* | pw32* | cegcc*)
|
||||
version_type=windows
|
||||
shrext_cmds=".dll"
|
||||
need_version=no
|
||||
need_lib_prefix=no
|
||||
|
||||
case $GCC,$host_os in
|
||||
yes,cygwin* | yes,mingw* | yes,pw32*)
|
||||
yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
|
||||
library_names_spec='$libname.dll.a'
|
||||
# DLL is installed to $(libdir)/../bin by postinstall_cmds
|
||||
postinstall_cmds='base_file=`basename \${file}`~
|
||||
@@ -2236,7 +2237,7 @@ cygwin* | mingw* | pw32*)
|
||||
soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
|
||||
sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
|
||||
;;
|
||||
mingw*)
|
||||
mingw* | cegcc*)
|
||||
# MinGW DLLs use traditional 'lib' prefix
|
||||
soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
|
||||
sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
|
||||
@@ -2675,7 +2676,7 @@ tpf*)
|
||||
version_type=linux
|
||||
need_lib_prefix=no
|
||||
need_version=no
|
||||
library_name_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
|
||||
library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
|
||||
shlibpath_var=LD_LIBRARY_PATH
|
||||
shlibpath_overrides_runpath=no
|
||||
hardcode_into_libs=yes
|
||||
@@ -2699,7 +2700,7 @@ variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
|
||||
if test "$GCC" = yes; then
|
||||
variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
|
||||
fi
|
||||
|
||||
|
||||
if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
|
||||
sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
|
||||
fi
|
||||
@@ -2976,6 +2977,7 @@ _LT_DECL([], [reload_cmds], [2])dnl
|
||||
# -- PORTME fill in with the dynamic library characteristics
|
||||
m4_defun([_LT_CHECK_MAGIC_METHOD],
|
||||
[m4_require([_LT_DECL_EGREP])
|
||||
m4_require([_LT_DECL_OBJDUMP])
|
||||
AC_CACHE_CHECK([how to recognize dependent libraries],
|
||||
lt_cv_deplibs_check_method,
|
||||
[lt_cv_file_magic_cmd='$MAGIC_CMD'
|
||||
@@ -3026,6 +3028,12 @@ mingw* | pw32*)
|
||||
fi
|
||||
;;
|
||||
|
||||
cegcc)
|
||||
# use the weaker test based on 'objdump'. See mingw*.
|
||||
lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
|
||||
lt_cv_file_magic_cmd='$OBJDUMP -f'
|
||||
;;
|
||||
|
||||
darwin* | rhapsody*)
|
||||
lt_cv_deplibs_check_method=pass_all
|
||||
;;
|
||||
@@ -3337,7 +3345,7 @@ case $host_os in
|
||||
aix*)
|
||||
symcode='[[BCDT]]'
|
||||
;;
|
||||
cygwin* | mingw* | pw32*)
|
||||
cygwin* | mingw* | pw32* | cegcc*)
|
||||
symcode='[[ABCDGISTW]]'
|
||||
;;
|
||||
hpux*)
|
||||
@@ -3583,7 +3591,7 @@ m4_if([$1], [CXX], [
|
||||
beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
|
||||
# PIC is the default for these OSes.
|
||||
;;
|
||||
mingw* | cygwin* | os2* | pw32*)
|
||||
mingw* | cygwin* | os2* | pw32* | cegcc*)
|
||||
# This hack is so that the source file can tell whether it is being
|
||||
# built for inclusion in a dll (and should export symbols for example).
|
||||
# Although the cygwin gcc ignores -fPIC, still need this for old-style
|
||||
@@ -3610,10 +3618,11 @@ m4_if([$1], [CXX], [
|
||||
fi
|
||||
;;
|
||||
hpux*)
|
||||
# PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
|
||||
# not for PA HP-UX.
|
||||
# PIC is the default for 64-bit PA HP-UX, but not for 32-bit
|
||||
# PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
|
||||
# sets the default TLS model and affects inlining.
|
||||
case $host_cpu in
|
||||
hppa*64*|ia64*)
|
||||
hppa*64*)
|
||||
;;
|
||||
*)
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
|
||||
@@ -3711,12 +3720,19 @@ m4_if([$1], [CXX], [
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
|
||||
;;
|
||||
icpc* | ecpc* )
|
||||
# Intel C++
|
||||
ecpc* )
|
||||
# old Intel C++ for x86_64 which still supported -KPIC.
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
|
||||
;;
|
||||
icpc* )
|
||||
# Intel C++, used to be incompatible with GCC.
|
||||
# ICC 10 doesn't accept -KPIC any more.
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
|
||||
;;
|
||||
pgCC* | pgcpp*)
|
||||
# Portland Group C++ compiler
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
@@ -3882,7 +3898,7 @@ m4_if([$1], [CXX], [
|
||||
# PIC is the default for these OSes.
|
||||
;;
|
||||
|
||||
mingw* | cygwin* | pw32* | os2*)
|
||||
mingw* | cygwin* | pw32* | os2* | cegcc*)
|
||||
# This hack is so that the source file can tell whether it is being
|
||||
# built for inclusion in a dll (and should export symbols for example).
|
||||
# Although the cygwin gcc ignores -fPIC, still need this for old-style
|
||||
@@ -3898,10 +3914,11 @@ m4_if([$1], [CXX], [
|
||||
;;
|
||||
|
||||
hpux*)
|
||||
# PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
|
||||
# not for PA HP-UX.
|
||||
# PIC is the default for 64-bit PA HP-UX, but not for 32-bit
|
||||
# PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
|
||||
# sets the default TLS model and affects inlining.
|
||||
case $host_cpu in
|
||||
hppa*64*|ia64*)
|
||||
hppa*64*)
|
||||
# +Z the default
|
||||
;;
|
||||
*)
|
||||
@@ -3951,7 +3968,7 @@ m4_if([$1], [CXX], [
|
||||
fi
|
||||
;;
|
||||
|
||||
mingw* | cygwin* | pw32* | os2*)
|
||||
mingw* | cygwin* | pw32* | os2* | cegcc*)
|
||||
# This hack is so that the source file can tell whether it is being
|
||||
# built for inclusion in a dll (and should export symbols for example).
|
||||
m4_if([$1], [GCJ], [],
|
||||
@@ -3982,11 +3999,25 @@ m4_if([$1], [CXX], [
|
||||
|
||||
linux* | k*bsd*-gnu)
|
||||
case $cc_basename in
|
||||
icc* | ecc* | ifort*)
|
||||
# old Intel for x86_64 which still supported -KPIC.
|
||||
ecc*)
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
|
||||
;;
|
||||
# icc used to be incompatible with GCC.
|
||||
# ICC 10 doesn't accept -KPIC any more.
|
||||
icc* | ifort*)
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
|
||||
;;
|
||||
# Lahey Fortran 8.1.
|
||||
lf95*)
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
|
||||
;;
|
||||
pgcc* | pgf77* | pgf90* | pgf95*)
|
||||
# Portland Group compilers (*not* the Pentium gcc compiler,
|
||||
# which looks to be a dead project)
|
||||
@@ -4168,7 +4199,7 @@ m4_if([$1], [CXX], [
|
||||
pw32*)
|
||||
_LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
|
||||
;;
|
||||
cygwin* | mingw*)
|
||||
cygwin* | mingw* | cegcc*)
|
||||
_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
|
||||
;;
|
||||
*)
|
||||
@@ -4220,7 +4251,7 @@ dnl Note also adjust exclude_expsyms for C++ above.
|
||||
extract_expsyms_cmds=
|
||||
|
||||
case $host_os in
|
||||
cygwin* | mingw* | pw32*)
|
||||
cygwin* | mingw* | pw32* | cegcc*)
|
||||
# FIXME: the MSVC++ port hasn't been tested in a loooong time
|
||||
# When not using gcc, we currently assume that we are using
|
||||
# Microsoft Visual C++.
|
||||
@@ -4307,7 +4338,7 @@ _LT_EOF
|
||||
fi
|
||||
;;
|
||||
|
||||
cygwin* | mingw* | pw32*)
|
||||
cygwin* | mingw* | pw32* | cegcc*)
|
||||
# _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
|
||||
# as there is no search path for DLLs.
|
||||
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
|
||||
@@ -4373,6 +4404,9 @@ _LT_EOF
|
||||
tmp_addflag=' -i_dynamic -nofor_main' ;;
|
||||
ifc* | ifort*) # Intel Fortran compiler
|
||||
tmp_addflag=' -nofor_main' ;;
|
||||
lf95*) # Lahey Fortran 8.1
|
||||
_LT_TAGVAR(whole_archive_flag_spec, $1)=
|
||||
tmp_sharedflag='--shared' ;;
|
||||
xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
|
||||
tmp_sharedflag='-qmkshrobj'
|
||||
tmp_addflag= ;;
|
||||
@@ -4604,6 +4638,7 @@ _LT_EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
|
||||
# It seems that -bexpall does not export symbols beginning with
|
||||
# underscore (_), so it is better to generate a list of symbols to export.
|
||||
_LT_TAGVAR(always_export_symbols, $1)=yes
|
||||
@@ -4658,7 +4693,7 @@ _LT_EOF
|
||||
_LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
|
||||
;;
|
||||
|
||||
cygwin* | mingw* | pw32*)
|
||||
cygwin* | mingw* | pw32* | cegcc*)
|
||||
# When not using gcc, we currently assume that we are using
|
||||
# Microsoft Visual C++.
|
||||
# hardcode_libdir_flag_spec is actually meaningless, as there is
|
||||
@@ -4762,7 +4797,7 @@ _LT_EOF
|
||||
_LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
|
||||
;;
|
||||
ia64*)
|
||||
_LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
|
||||
_LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
|
||||
;;
|
||||
*)
|
||||
_LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
|
||||
@@ -5543,6 +5578,7 @@ if test "$_lt_caught_CXX_error" != yes; then
|
||||
fi
|
||||
fi
|
||||
|
||||
_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
|
||||
# It seems that -bexpall does not export symbols beginning with
|
||||
# underscore (_), so it is better to generate a list of symbols to
|
||||
# export.
|
||||
@@ -5601,7 +5637,7 @@ if test "$_lt_caught_CXX_error" != yes; then
|
||||
esac
|
||||
;;
|
||||
|
||||
cygwin* | mingw* | pw32*)
|
||||
cygwin* | mingw* | pw32* | cegcc*)
|
||||
# _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
|
||||
# as there is no search path for DLLs.
|
||||
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
|
||||
@@ -6982,6 +7018,18 @@ AC_SUBST([GREP])
|
||||
])
|
||||
|
||||
|
||||
# _LT_DECL_OBJDUMP
|
||||
# --------------
|
||||
# If we don't have a new enough Autoconf to choose the best objdump
|
||||
# available, choose the one first in the user's PATH.
|
||||
m4_defun([_LT_DECL_OBJDUMP],
|
||||
[AC_CHECK_TOOL(OBJDUMP, objdump, false)
|
||||
test -z "$OBJDUMP" && OBJDUMP=objdump
|
||||
_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
|
||||
AC_SUBST([OBJDUMP])
|
||||
])
|
||||
|
||||
|
||||
# _LT_DECL_SED
|
||||
# ------------
|
||||
# Check for a fully-functional sed program, that truncates
|
||||
@@ -7442,7 +7490,7 @@ LT_OPTION_DEFINE([LT_INIT], [win32-dll],
|
||||
[enable_win32_dll=yes
|
||||
|
||||
case $host in
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32*)
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
|
||||
AC_CHECK_TOOL(AS, as, false)
|
||||
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
|
||||
AC_CHECK_TOOL(OBJDUMP, objdump, false)
|
||||
@@ -7683,14 +7731,14 @@ LT_OPTION_DEFINE([LTDL_INIT], [convenience],
|
||||
|
||||
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 5 ltsugar.m4
|
||||
# serial 6 ltsugar.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
|
||||
@@ -7746,14 +7794,14 @@ m4_define([lt_append],
|
||||
# Produce a SEP delimited list of all paired combinations of elements of
|
||||
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
|
||||
# has the form PREFIXmINFIXSUFFIXn.
|
||||
# Needed until we can rely on m4_combine added in Autoconf 2.62.
|
||||
m4_define([lt_combine],
|
||||
[m4_if([$2], [], [],
|
||||
[m4_if([$4], [], [],
|
||||
[lt_join(m4_quote(m4_default([$1], [[, ]])),
|
||||
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_prefix, [$2],
|
||||
[m4_foreach(_Lt_suffix, lt_car([m4_shiftn(3, $@)]),
|
||||
[_Lt_prefix[]$3[]_Lt_suffix ])])))))])])dnl
|
||||
])
|
||||
[m4_if(m4_eval([$# > 3]), [1],
|
||||
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
|
||||
[[m4_foreach([_Lt_prefix], [$2],
|
||||
[m4_foreach([_Lt_suffix],
|
||||
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
|
||||
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
|
||||
|
||||
|
||||
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
|
||||
@@ -7816,15 +7864,15 @@ m4_define([lt_dict_filter],
|
||||
|
||||
# Generated from ltversion.in.
|
||||
|
||||
# serial 2976 ltversion.m4
|
||||
# serial 3012 ltversion.m4
|
||||
# This file is part of GNU Libtool
|
||||
|
||||
m4_define([LT_PACKAGE_VERSION], [2.2.4])
|
||||
m4_define([LT_PACKAGE_REVISION], [1.2976])
|
||||
m4_define([LT_PACKAGE_VERSION], [2.2.6])
|
||||
m4_define([LT_PACKAGE_REVISION], [1.3012])
|
||||
|
||||
AC_DEFUN([LTVERSION_VERSION],
|
||||
[macro_version='2.2.4'
|
||||
macro_revision='1.2976'
|
||||
[macro_version='2.2.6'
|
||||
macro_revision='1.3012'
|
||||
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
|
||||
_LT_DECL(, macro_revision, 0)
|
||||
])
|
||||
|
||||
+14
-15
@@ -14,19 +14,18 @@ BEGIN {
|
||||
if( max<$3 ) max = $3
|
||||
}
|
||||
END {
|
||||
printf "#define TK_%-29s %4d\n", "TO_TEXT", max+1
|
||||
printf "#define TK_%-29s %4d\n", "TO_BLOB", max+2
|
||||
printf "#define TK_%-29s %4d\n", "TO_NUMERIC", max+3
|
||||
printf "#define TK_%-29s %4d\n", "TO_INT", max+4
|
||||
printf "#define TK_%-29s %4d\n", "TO_REAL", max+5
|
||||
printf "#define TK_%-29s %4d\n", "END_OF_FILE", max+6
|
||||
printf "#define TK_%-29s %4d\n", "ILLEGAL", max+7
|
||||
printf "#define TK_%-29s %4d\n", "SPACE", max+8
|
||||
printf "#define TK_%-29s %4d\n", "UNCLOSED_STRING", max+9
|
||||
printf "#define TK_%-29s %4d\n", "COMMENT", max+10
|
||||
printf "#define TK_%-29s %4d\n", "FUNCTION", max+11
|
||||
printf "#define TK_%-29s %4d\n", "COLUMN", max+12
|
||||
printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", max+13
|
||||
printf "#define TK_%-29s %4d\n", "AGG_COLUMN", max+14
|
||||
printf "#define TK_%-29s %4d\n", "CONST_FUNC", max+15
|
||||
printf "#define TK_%-29s %4d\n", "TO_TEXT", ++max
|
||||
printf "#define TK_%-29s %4d\n", "TO_BLOB", ++max
|
||||
printf "#define TK_%-29s %4d\n", "TO_NUMERIC", ++max
|
||||
printf "#define TK_%-29s %4d\n", "TO_INT", ++max
|
||||
printf "#define TK_%-29s %4d\n", "TO_REAL", ++max
|
||||
printf "#define TK_%-29s %4d\n", "END_OF_FILE", ++max
|
||||
printf "#define TK_%-29s %4d\n", "ILLEGAL", ++max
|
||||
printf "#define TK_%-29s %4d\n", "SPACE", ++max
|
||||
printf "#define TK_%-29s %4d\n", "UNCLOSED_STRING", ++max
|
||||
printf "#define TK_%-29s %4d\n", "FUNCTION", ++max
|
||||
printf "#define TK_%-29s %4d\n", "COLUMN", ++max
|
||||
printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", ++max
|
||||
printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max
|
||||
printf "#define TK_%-29s %4d\n", "CONST_FUNC", ++max
|
||||
}
|
||||
|
||||
+90
-81
@@ -1,95 +1,104 @@
|
||||
/*
|
||||
** 2008 March 6
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Configuration header template to be filled in by 'configure' script
|
||||
**
|
||||
** @(#) $Id: config.h.in,v 1.3 2008/07/22 05:05:02 shane Exp $
|
||||
*/
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/*****************************
|
||||
** Data types
|
||||
*****************************/
|
||||
|
||||
/* Define as 1 if you have the int8_t type */
|
||||
#undef HAVE_INT8_T
|
||||
|
||||
/* Define as 1 if you have the int16_t type */
|
||||
#undef HAVE_INT16_T
|
||||
|
||||
/* Define as 1 if you have the int32_t type */
|
||||
#undef HAVE_INT32_T
|
||||
|
||||
/* Define as 1 if you have the int64_t type */
|
||||
#undef HAVE_INT64_T
|
||||
|
||||
/* Define as 1 if you have the uint8_t type */
|
||||
#undef HAVE_UINT8_T
|
||||
|
||||
/* Define as 1 if you have the uint16_t type */
|
||||
#undef HAVE_UINT16_T
|
||||
|
||||
/* Define as 1 if you have the uint32_t type */
|
||||
#undef HAVE_UINT32_T
|
||||
|
||||
/* Define as 1 if you have the uint64_t type */
|
||||
#undef HAVE_UINT64_T
|
||||
|
||||
|
||||
/*****************************
|
||||
** Header Files
|
||||
*****************************/
|
||||
|
||||
/* Define as 1 if you have the sys/types.h header */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define as 1 if you have the stdlib.h header */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define as 1 if you have the stdint.h header */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define as 1 if you have the inttypes.h header */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
|
||||
/*****************************
|
||||
** Functions
|
||||
*****************************/
|
||||
|
||||
/* Define as 1 if you have the usleep() function */
|
||||
#undef HAVE_USLEEP
|
||||
|
||||
/* Define as 1 if you have the fdatasync() function */
|
||||
/* Define to 1 if you have the `fdatasync' function. */
|
||||
#undef HAVE_FDATASYNC
|
||||
|
||||
/* Define as 1 if you have the gmtime_r() function */
|
||||
/* Define to 1 if you have the `gmtime_r' function. */
|
||||
#undef HAVE_GMTIME_R
|
||||
|
||||
/* Define as 1 if you have the localtime_r() function */
|
||||
/* Define to 1 if the system has the type `int16_t'. */
|
||||
#undef HAVE_INT16_T
|
||||
|
||||
/* Define to 1 if the system has the type `int32_t'. */
|
||||
#undef HAVE_INT32_T
|
||||
|
||||
/* Define to 1 if the system has the type `int64_t'. */
|
||||
#undef HAVE_INT64_T
|
||||
|
||||
/* Define to 1 if the system has the type `int8_t'. */
|
||||
#undef HAVE_INT8_T
|
||||
|
||||
/* Define to 1 if the system has the type `intptr_t'. */
|
||||
#undef HAVE_INTPTR_T
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the `localtime_r' function. */
|
||||
#undef HAVE_LOCALTIME_R
|
||||
|
||||
/* Define as 1 if you have the localtime_s() function */
|
||||
/* Define to 1 if you have the `localtime_s' function. */
|
||||
#undef HAVE_LOCALTIME_S
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/*****************************
|
||||
** Large file support
|
||||
*****************************/
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#undef HAVE_UINT16_T
|
||||
|
||||
/* Define to 1 if the system has the type `uint32_t'. */
|
||||
#undef HAVE_UINT32_T
|
||||
|
||||
/* Define to 1 if the system has the type `uint64_t'. */
|
||||
#undef HAVE_UINT64_T
|
||||
|
||||
/* Define to 1 if the system has the type `uint8_t'. */
|
||||
#undef HAVE_UINT8_T
|
||||
|
||||
/* Define to 1 if the system has the type `uintptr_t'. */
|
||||
#undef HAVE_UINTPTR_T
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to 1 if you have the `usleep' function. */
|
||||
#undef HAVE_USLEEP
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
|
||||
/* End of header */
|
||||
#endif
|
||||
|
||||
+40
-18
@@ -92,7 +92,7 @@ AC_INIT(sqlite, m4_esyscmd([cat VERSION | tr -d '\n']))
|
||||
dnl Put the RCS revision string after AC_INIT so that it will also
|
||||
dnl show in in configure.
|
||||
# The following RCS revision string applies to configure.in
|
||||
# $Revision: 1.48 $
|
||||
# $Revision: 1.54 $
|
||||
|
||||
#########
|
||||
# Programs needed
|
||||
@@ -140,6 +140,16 @@ if test "$TCLSH_CMD" = "none"; then
|
||||
fi
|
||||
AC_SUBST(TCLSH_CMD)
|
||||
|
||||
AC_ARG_VAR([TCLLIBDIR], [Where to install tcl plugin])
|
||||
if test "x${TCLLIBDIR+set}" != "xset" ; then
|
||||
TCLLIBDIR='$(libdir)'
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
|
||||
TCLLIBDIR=$i
|
||||
break
|
||||
done
|
||||
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
|
||||
fi
|
||||
|
||||
|
||||
#########
|
||||
# Set up an appropriate program prefix
|
||||
@@ -150,15 +160,15 @@ fi
|
||||
AC_SUBST(program_prefix)
|
||||
|
||||
VERSION=[`cat $srcdir/VERSION | sed 's/^\([0-9]*\.*[0-9]*\).*/\1/'`]
|
||||
echo "Version set to $VERSION"
|
||||
AC_MSG_NOTICE(Version set to $VERSION)
|
||||
AC_SUBST(VERSION)
|
||||
RELEASE=`cat $srcdir/VERSION`
|
||||
echo "Release set to $RELEASE"
|
||||
AC_MSG_NOTICE(Release set to $RELEASE)
|
||||
AC_SUBST(RELEASE)
|
||||
VERSION_NUMBER=[`cat $srcdir/VERSION \
|
||||
VERSION_NUMBER=[`cat $srcdir/VERSION \
|
||||
| sed 's/[^0-9]/ /g' \
|
||||
| awk '{printf "%d%03d%03d",$1,$2,$3}'`]
|
||||
echo "Version number set to $VERSION_NUMBER"
|
||||
AC_MSG_NOTICE(Version number set to $VERSION_NUMBER)
|
||||
AC_SUBST(VERSION_NUMBER)
|
||||
|
||||
#########
|
||||
@@ -297,17 +307,17 @@ case "$enable_tempstore" in
|
||||
TEMP_STORE=1
|
||||
AC_MSG_RESULT([no])
|
||||
;;
|
||||
always )
|
||||
TEMP_STORE=3
|
||||
AC_MSG_RESULT([always])
|
||||
;;
|
||||
yes )
|
||||
TEMP_STORE=2
|
||||
AC_MSG_RESULT([yes])
|
||||
;;
|
||||
always )
|
||||
TEMP_STORE=3
|
||||
AC_MSG_RESULT([always])
|
||||
;;
|
||||
* )
|
||||
TEMP_STORE=1
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_MSG_RESULT([no])
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -344,10 +354,6 @@ if test "$TARGET_EXEEXT" = ".exe"; then
|
||||
SQLITE_OS_WIN=0
|
||||
SQLITE_OS_OS2=1
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1"
|
||||
if test "$ac_compiler_gnu" = "yes" ; then
|
||||
CFLAGS="$CFLAGS -Zomf -Zexe -Zmap"
|
||||
BUILD_CFLAGS="$BUILD_CFLAGS -Zomf -Zexe"
|
||||
fi
|
||||
else
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=1
|
||||
@@ -389,6 +395,18 @@ if test "${use_tcl}" = "yes" ; then
|
||||
AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start autosearch by asking tclsh
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# then check for a private Tcl installation
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
@@ -615,46 +633,50 @@ else
|
||||
fi
|
||||
|
||||
#########
|
||||
# attempt to duplicate any OMITS into the $(OPT_FEATURE_FLAGS) parameter
|
||||
# attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter
|
||||
for option in $CFLAGS $CPPFLAGS
|
||||
do
|
||||
case $option in
|
||||
-DSQLITE_OMIT*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";;
|
||||
-DSQLITE_ENABLE*) OPT_FEATURE_FLAGS="$OPT_FEATURE_FLAGS $option";;
|
||||
esac
|
||||
done
|
||||
AC_SUBST(OPT_FEATURE_FLAGS)
|
||||
|
||||
|
||||
# attempt to remove any OMITS from the $(CFLAGS) parameter
|
||||
# attempt to remove any OMITS and ENABLES from the $(CFLAGS) parameter
|
||||
ac_temp_CFLAGS=""
|
||||
for option in $CFLAGS
|
||||
do
|
||||
case $option in
|
||||
-DSQLITE_OMIT*) ;;
|
||||
-DSQLITE_ENABLE*) ;;
|
||||
*) ac_temp_CFLAGS="$ac_temp_CFLAGS $option";;
|
||||
esac
|
||||
done
|
||||
CFLAGS=$ac_temp_CFLAGS
|
||||
|
||||
|
||||
# attempt to remove any OMITS from the $(CPPFLAGS) parameter
|
||||
# attempt to remove any OMITS and ENABLES from the $(CPPFLAGS) parameter
|
||||
ac_temp_CPPFLAGS=""
|
||||
for option in $CPPFLAGS
|
||||
do
|
||||
case $option in
|
||||
-DSQLITE_OMIT*) ;;
|
||||
-DSQLITE_ENABLE*) ;;
|
||||
*) ac_temp_CPPFLAGS="$ac_temp_CPPFLAGS $option";;
|
||||
esac
|
||||
done
|
||||
CPPFLAGS=$ac_temp_CPPFLAGS
|
||||
|
||||
|
||||
# attempt to remove any OMITS from the $(BUILD_CFLAGS) parameter
|
||||
# attempt to remove any OMITS and ENABLES from the $(BUILD_CFLAGS) parameter
|
||||
ac_temp_BUILD_CFLAGS=""
|
||||
for option in $BUILD_CFLAGS
|
||||
do
|
||||
case $option in
|
||||
-DSQLITE_OMIT*) ;;
|
||||
-DSQLITE_ENABLE*) ;;
|
||||
*) ac_temp_BUILD_CFLAGS="$ac_temp_BUILD_CFLAGS $option";;
|
||||
esac
|
||||
done
|
||||
|
||||
+4
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file implements a tokenizer for fts2 based on the ICU library.
|
||||
**
|
||||
** $Id: fts2_icu.c,v 1.2 2008/07/22 22:20:50 shess Exp $
|
||||
** $Id: fts2_icu.c,v 1.3 2008/12/18 05:30:26 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
@@ -112,7 +112,9 @@ static int icuOpen(
|
||||
|
||||
*ppCursor = 0;
|
||||
|
||||
if( -1 == nInput ) nInput = strlen(nInput);
|
||||
if( nInput<0 ){
|
||||
nInput = strlen(zInput);
|
||||
}
|
||||
nChar = nInput+1;
|
||||
pCsr = (IcuCursor *)sqlite3_malloc(
|
||||
sizeof(IcuCursor) + /* IcuCursor */
|
||||
|
||||
@@ -239,7 +239,7 @@ int registerTokenizer(
|
||||
}
|
||||
|
||||
static
|
||||
int queryTokenizer(
|
||||
int queryFts2Tokenizer(
|
||||
sqlite3 *db,
|
||||
char *zName,
|
||||
const sqlite3_tokenizer_module **pp
|
||||
@@ -272,7 +272,7 @@ void sqlite3Fts2SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
** build unless SQLITE_TEST is defined.
|
||||
**
|
||||
** The purpose of this is to test that the fts2_tokenizer() function
|
||||
** can be used as designed by the C-code in the queryTokenizer and
|
||||
** can be used as designed by the C-code in the queryFts2Tokenizer and
|
||||
** registerTokenizer() functions above. These two functions are repeated
|
||||
** in the README.tokenizer file as an example, so it is important to
|
||||
** test them.
|
||||
@@ -296,10 +296,10 @@ static void intTestFunc(
|
||||
|
||||
/* Test the query function */
|
||||
sqlite3Fts2SimpleTokenizerModule(&p1);
|
||||
rc = queryTokenizer(db, "simple", &p2);
|
||||
rc = queryFts2Tokenizer(db, "simple", &p2);
|
||||
assert( rc==SQLITE_OK );
|
||||
assert( p1==p2 );
|
||||
rc = queryTokenizer(db, "nosuchtokenizer", &p2);
|
||||
rc = queryFts2Tokenizer(db, "nosuchtokenizer", &p2);
|
||||
assert( rc==SQLITE_ERROR );
|
||||
assert( p2==0 );
|
||||
assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") );
|
||||
@@ -307,7 +307,7 @@ static void intTestFunc(
|
||||
/* Test the storage function */
|
||||
rc = registerTokenizer(db, "nosuchtokenizer", p1);
|
||||
assert( rc==SQLITE_OK );
|
||||
rc = queryTokenizer(db, "nosuchtokenizer", &p2);
|
||||
rc = queryFts2Tokenizer(db, "nosuchtokenizer", &p2);
|
||||
assert( rc==SQLITE_OK );
|
||||
assert( p2==p1 );
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,209 @@
|
||||
|
||||
1. OVERVIEW
|
||||
|
||||
This README file describes the syntax of the arguments that may be passed to
|
||||
the FTS3 MATCH operator used for full-text queries. For example, if table
|
||||
"t1" is an Fts3 virtual table, the following SQL query:
|
||||
|
||||
SELECT * FROM t1 WHERE <col> MATCH <full-text query>
|
||||
|
||||
may be used to retrieve all rows that match a specified for full-text query.
|
||||
The text "<col>" should be replaced by either the name of the fts3 table
|
||||
(in this case "t1"), or by the name of one of the columns of the fts3
|
||||
table. <full-text-query> should be replaced by an SQL expression that
|
||||
computes to a string containing an Fts3 query.
|
||||
|
||||
If the left-hand-side of the MATCH operator is set to the name of the
|
||||
fts3 table, then by default the query may be matched against any column
|
||||
of the table. If it is set to a column name, then by default the query
|
||||
may only match the specified column. In both cases this may be overriden
|
||||
as part of the query text (see sections 2 and 3 below).
|
||||
|
||||
As of SQLite version 3.6.8, Fts3 supports two slightly different query
|
||||
formats; the standard syntax, which is used by default, and the enhanced
|
||||
query syntax which can be selected by compiling with the pre-processor
|
||||
symbol SQLITE_ENABLE_FTS3_PARENTHESIS defined.
|
||||
|
||||
-DSQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
|
||||
2. STANDARD QUERY SYNTAX
|
||||
|
||||
When using the standard Fts3 query syntax, a query usually consists of a
|
||||
list of terms (words) separated by white-space characters. To match a
|
||||
query, a row (or column) of an Fts3 table must contain each of the specified
|
||||
terms. For example, the following query:
|
||||
|
||||
<col> MATCH 'hello world'
|
||||
|
||||
matches rows (or columns, if <col> is the name of a column name) that
|
||||
contain at least one instance of the token "hello", and at least one
|
||||
instance of the token "world". Tokens may be grouped into phrases using
|
||||
quotation marks. In this case, a matching row or column must contain each
|
||||
of the tokens in the phrase in the order specified, with no intervening
|
||||
tokens. For example, the query:
|
||||
|
||||
<col> MATCH '"hello world" joe"
|
||||
|
||||
matches the first of the following two documents, but not the second or
|
||||
third:
|
||||
|
||||
"'Hello world', said Joe."
|
||||
"One should always greet the world with a cheery hello, thought Joe."
|
||||
"How many hello world programs could their be?"
|
||||
|
||||
As well as grouping tokens together by phrase, the binary NEAR operator
|
||||
may be used to search for rows that contain two or more specified tokens
|
||||
or phrases within a specified proximity of each other. The NEAR operator
|
||||
must always be specified in upper case. The word "near" in lower or mixed
|
||||
case is treated as an ordinary token. For example, the following query:
|
||||
|
||||
<col> MATCH 'engineering NEAR consultancy'
|
||||
|
||||
matches rows that contain both the "engineering" and "consultancy" tokens
|
||||
in the same column with not more than 10 other words between them. It does
|
||||
not matter which of the two terms occurs first in the document, only that
|
||||
they be seperated by only 10 tokens or less. The user may also specify
|
||||
a different required proximity by adding "/N" immediately after the NEAR
|
||||
operator, where N is an integer. For example:
|
||||
|
||||
<col> MATCH 'engineering NEAR/5 consultancy'
|
||||
|
||||
searches for a row containing an instance of each specified token seperated
|
||||
by not more than 5 other tokens. More than one NEAR operator can be used
|
||||
in as sequence. For example this query:
|
||||
|
||||
<col> MATCH 'reliable NEAR/2 engineering NEAR/5 consultancy'
|
||||
|
||||
searches for a row that contains an instance of the token "reliable"
|
||||
seperated by not more than two tokens from an instance of "engineering",
|
||||
which is in turn separated by not more than 5 other tokens from an
|
||||
instance of the term "consultancy". Phrases enclosed in quotes may
|
||||
also be used as arguments to the NEAR operator.
|
||||
|
||||
Similar to the NEAR operator, one or more tokens or phrases may be
|
||||
separated by OR operators. In this case, only one of the specified tokens
|
||||
or phrases must appear in the document. For example, the query:
|
||||
|
||||
<col> MATCH 'hello OR world'
|
||||
|
||||
matches rows that contain either the term "hello", or the term "world",
|
||||
or both. Note that unlike in many programming languages, the OR operator
|
||||
has a higher precedence than the AND operators implied between white-space
|
||||
separated tokens. The following query matches documents that contain the
|
||||
term 'sqlite' and at least one of the terms 'fantastic' or 'impressive',
|
||||
not those that contain both 'sqlite' and 'fantastic' or 'impressive':
|
||||
|
||||
<col> MATCH 'sqlite fantastic OR impressive'
|
||||
|
||||
Any token that is part of an Fts3 query expression, whether or not it is
|
||||
part of a phrase enclosed in quotes, may have a '*' character appended to
|
||||
it. In this case, the token matches all terms that begin with the characters
|
||||
of the token, not just those that exactly match it. For example, the
|
||||
following query:
|
||||
|
||||
<col> MATCH 'sql*'
|
||||
|
||||
matches all rows that contain the term "SQLite", as well as those that
|
||||
contain "SQL".
|
||||
|
||||
A token that is not part of a quoted phrase may be preceded by a '-'
|
||||
character, which indicates that matching rows must not contain the
|
||||
specified term. For example, the following:
|
||||
|
||||
<col> MATCH '"database engine" -sqlite'
|
||||
|
||||
matches rows that contain the phrase "database engine" but do not contain
|
||||
the term "sqlite". If the '-' character occurs inside a quoted phrase,
|
||||
it is ignored. It is possible to use both the '-' prefix and the '*' postfix
|
||||
on a single term. At this time, all Fts3 queries must contain at least
|
||||
one term or phrase that is not preceded by the '-' prefix.
|
||||
|
||||
Regardless of whether or not a table name or column name is used on the
|
||||
left hand side of the MATCH operator, a specific column of the fts3 table
|
||||
may be associated with each token in a query by preceding a token with
|
||||
a column name followed by a ':' character. For example, regardless of what
|
||||
is specified for <col>, the following query requires that column "col1"
|
||||
of the table contains the term "hello", and that column "col2" of the
|
||||
table contains the term "world". If the table does not contain columns
|
||||
named "col1" and "col2", then an error is returned and the query is
|
||||
not run.
|
||||
|
||||
<col> MATCH 'col1:hello col2:world'
|
||||
|
||||
It is not possible to associate a specific table column with a quoted
|
||||
phrase or a term preceded by a '-' operator. A '*' character may be
|
||||
appended to a term associated with a specific column for prefix matching.
|
||||
|
||||
3. ENHANCED QUERY SYNTAX
|
||||
|
||||
The enhanced query syntax is quite similar to the standard query syntax,
|
||||
with the following four differences:
|
||||
|
||||
1) Parenthesis are supported. When using the enhanced query syntax,
|
||||
parenthesis may be used to overcome the built-in precedence of the
|
||||
supplied binary operators. For example, the following query:
|
||||
|
||||
<col> MATCH '(hello world) OR (simple example)'
|
||||
|
||||
matches documents that contain both "hello" and "world", and documents
|
||||
that contain both "simple" and "example". It is not possible to forumlate
|
||||
such a query using the standard syntax.
|
||||
|
||||
2) Instead of separating tokens and phrases by whitespace, an AND operator
|
||||
may be explicitly specified. This does not change query processing at
|
||||
all, but may be used to improve readability. For example, the following
|
||||
query is handled identically to the one above:
|
||||
|
||||
<col> MATCH '(hello AND world) OR (simple AND example)'
|
||||
|
||||
As with the OR and NEAR operators, the AND operator must be specified
|
||||
in upper case. The word "and" specified in lower or mixed case is
|
||||
handled as a regular token.
|
||||
|
||||
3) The '-' token prefix is not supported. Instead, a new binary operator,
|
||||
NOT, is included. The NOT operator requires that the query specified
|
||||
as its left-hand operator matches, but that the query specified as the
|
||||
right-hand operator does not. For example, to query for all rows that
|
||||
contain the term "example" but not the term "simple", the following
|
||||
query could be used:
|
||||
|
||||
<col> MATCH 'example NOT simple'
|
||||
|
||||
As for all other operators, the NOT operator must be specified in
|
||||
upper case. Otherwise it will be treated as a regular token.
|
||||
|
||||
4) Unlike in the standard syntax, where the OR operator has a higher
|
||||
precedence than the implicit AND operator, when using the enhanced
|
||||
syntax implicit and explict AND operators have a higher precedence
|
||||
than OR operators. Using the enhanced syntax, the following two
|
||||
queries are equivalent:
|
||||
|
||||
<col> MATCH 'sqlite fantastic OR impressive'
|
||||
<col> MATCH '(sqlite AND fantastic) OR impressive'
|
||||
|
||||
however, when using the standard syntax, the query:
|
||||
|
||||
<col> MATCH 'sqlite fantastic OR impressive'
|
||||
|
||||
is equivalent to the enhanced syntax query:
|
||||
|
||||
<col> MATCH 'sqlite AND (fantastic OR impressive)'
|
||||
|
||||
The precedence of all enhanced syntax operators, in order from highest
|
||||
to lowest, is:
|
||||
|
||||
NEAR (highest precedence, tightest grouping)
|
||||
NOT
|
||||
AND
|
||||
OR (lowest precedence, loosest grouping)
|
||||
|
||||
Using the advanced syntax, it is possible to specify expressions enclosed
|
||||
in parenthesis as operands to the NOT, AND and OR operators. However both
|
||||
the left and right hand side operands of NEAR operators must be either
|
||||
tokens or phrases. Attempting the following query will return an error:
|
||||
|
||||
<col> MATCH 'sqlite NEAR (fantastic OR impressive)'
|
||||
|
||||
Queries of this form must be re-written as:
|
||||
|
||||
<col> MATCH 'sqlite NEAR fantastic OR sqlite NEAR impressive'
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
When creating a new full-text table, FTS3 allows the user to select
|
||||
the text tokenizer implementation to be used when indexing text
|
||||
by specifying a "tokenizer" clause as part of the CREATE VIRTUAL TABLE
|
||||
by specifying a "tokenize" clause as part of the CREATE VIRTUAL TABLE
|
||||
statement:
|
||||
|
||||
CREATE VIRTUAL TABLE <table-name> USING fts3(
|
||||
<columns ...> [, tokenizer <tokenizer-name> [<tokenizer-args>]]
|
||||
<columns ...> [, tokenize <tokenizer-name> [<tokenizer-args>]]
|
||||
);
|
||||
|
||||
The built-in tokenizers (valid values to pass as <tokenizer name>) are
|
||||
|
||||
+336
-528
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,890 @@
|
||||
/*
|
||||
** 2008 Nov 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This module contains code that implements a parser for fts3 query strings
|
||||
** (the right-hand argument to the MATCH operator). Because the supported
|
||||
** syntax is relatively simple, the whole tokenizer/parser system is
|
||||
** hand-coded. The public interface to this module is declared in source
|
||||
** code file "fts3_expr.h".
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
/*
|
||||
** By default, this module parses the legacy syntax that has been
|
||||
** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
** is defined, then it uses the new syntax. The differences between
|
||||
** the new and the old syntaxes are:
|
||||
**
|
||||
** a) The new syntax supports parenthesis. The old does not.
|
||||
**
|
||||
** b) The new syntax supports the AND and NOT operators. The old does not.
|
||||
**
|
||||
** c) The old syntax supports the "-" token qualifier. This is not
|
||||
** supported by the new syntax (it is replaced by the NOT operator).
|
||||
**
|
||||
** d) When using the old syntax, the OR operator has a greater precedence
|
||||
** than an implicit AND. When using the new, both implicity and explicit
|
||||
** AND operators have a higher precedence than OR.
|
||||
**
|
||||
** If compiled with SQLITE_TEST defined, then this module exports the
|
||||
** symbol "int sqlite3_fts3_enable_parentheses". Setting this variable
|
||||
** to zero causes the module to use the old syntax. If it is set to
|
||||
** non-zero the new syntax is activated. This is so both syntaxes can
|
||||
** be tested using a single build of testfixture.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3_fts3_enable_parentheses = 0;
|
||||
#else
|
||||
# ifdef SQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
# define sqlite3_fts3_enable_parentheses 1
|
||||
# else
|
||||
# define sqlite3_fts3_enable_parentheses 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Default span for NEAR operators.
|
||||
*/
|
||||
#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
|
||||
|
||||
#include "fts3_expr.h"
|
||||
#include "sqlite3.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct ParseContext ParseContext;
|
||||
struct ParseContext {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
|
||||
const char **azCol; /* Array of column names for fts3 table */
|
||||
int nCol; /* Number of entries in azCol[] */
|
||||
int iDefaultCol; /* Default column to query */
|
||||
sqlite3_context *pCtx; /* Write error message here */
|
||||
int nNest; /* Number of nested brackets */
|
||||
};
|
||||
|
||||
/*
|
||||
** This function is equivalent to the standard isspace() function.
|
||||
**
|
||||
** The standard isspace() can be awkward to use safely, because although it
|
||||
** is defined to accept an argument of type int, its behaviour when passed
|
||||
** an integer that falls outside of the range of the unsigned char type
|
||||
** is undefined (and sometimes, "undefined" means segfault). This wrapper
|
||||
** is defined to accept an argument of type char, and always returns 0 for
|
||||
** any values that fall outside of the range of the unsigned char type (i.e.
|
||||
** negative values).
|
||||
*/
|
||||
static int fts3isspace(char c){
|
||||
return (c&0x80)==0 ? isspace(c) : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the next token from buffer z (length n) using the tokenizer
|
||||
** and other information (column names etc.) in pParse. Create an Fts3Expr
|
||||
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
|
||||
** single token and set *ppExpr to point to it. If the end of the buffer is
|
||||
** reached before a token is found, set *ppExpr to zero. It is the
|
||||
** responsibility of the caller to eventually deallocate the allocated
|
||||
** Fts3Expr structure (if any) by passing it to sqlite3_free().
|
||||
**
|
||||
** Return SQLITE_OK if successful, or SQLITE_NOMEM if a memory allocation
|
||||
** fails.
|
||||
*/
|
||||
static int getNextToken(
|
||||
ParseContext *pParse, /* fts3 query parse context */
|
||||
int iCol, /* Value for Fts3Phrase.iColumn */
|
||||
const char *z, int n, /* Input string */
|
||||
Fts3Expr **ppExpr, /* OUT: expression */
|
||||
int *pnConsumed /* OUT: Number of bytes consumed */
|
||||
){
|
||||
sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
|
||||
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
|
||||
int rc;
|
||||
sqlite3_tokenizer_cursor *pCursor;
|
||||
Fts3Expr *pRet = 0;
|
||||
int nConsumed = 0;
|
||||
|
||||
rc = pModule->xOpen(pTokenizer, z, n, &pCursor);
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zToken;
|
||||
int nToken, iStart, iEnd, iPosition;
|
||||
int nByte; /* total space to allocate */
|
||||
|
||||
pCursor->pTokenizer = pTokenizer;
|
||||
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
|
||||
pRet = (Fts3Expr *)sqlite3_malloc(nByte);
|
||||
if( !pRet ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pRet, 0, nByte);
|
||||
pRet->eType = FTSQUERY_PHRASE;
|
||||
pRet->pPhrase = (Fts3Phrase *)&pRet[1];
|
||||
pRet->pPhrase->nToken = 1;
|
||||
pRet->pPhrase->iColumn = iCol;
|
||||
pRet->pPhrase->aToken[0].n = nToken;
|
||||
pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1];
|
||||
memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
|
||||
|
||||
if( iEnd<n && z[iEnd]=='*' ){
|
||||
pRet->pPhrase->aToken[0].isPrefix = 1;
|
||||
iEnd++;
|
||||
}
|
||||
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
|
||||
pRet->pPhrase->isNot = 1;
|
||||
}
|
||||
}
|
||||
nConsumed = iEnd;
|
||||
}
|
||||
|
||||
pModule->xClose(pCursor);
|
||||
}
|
||||
|
||||
*pnConsumed = nConsumed;
|
||||
*ppExpr = pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Enlarge a memory allocation. If an out-of-memory allocation occurs,
|
||||
** then free the old allocation.
|
||||
*/
|
||||
void *fts3ReallocOrFree(void *pOrig, int nNew){
|
||||
void *pRet = sqlite3_realloc(pOrig, nNew);
|
||||
if( !pRet ){
|
||||
sqlite3_free(pOrig);
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Buffer zInput, length nInput, contains the contents of a quoted string
|
||||
** that appeared as part of an fts3 query expression. Neither quote character
|
||||
** is included in the buffer. This function attempts to tokenize the entire
|
||||
** input buffer and create an Fts3Expr structure of type FTSQUERY_PHRASE
|
||||
** containing the results.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned and *ppExpr set to point at the
|
||||
** allocated Fts3Expr structure. Otherwise, either SQLITE_NOMEM (out of memory
|
||||
** error) or SQLITE_ERROR (tokenization error) is returned and *ppExpr set
|
||||
** to 0.
|
||||
*/
|
||||
static int getNextString(
|
||||
ParseContext *pParse, /* fts3 query parse context */
|
||||
const char *zInput, int nInput, /* Input string */
|
||||
Fts3Expr **ppExpr /* OUT: expression */
|
||||
){
|
||||
sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
|
||||
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
|
||||
int rc;
|
||||
Fts3Expr *p = 0;
|
||||
sqlite3_tokenizer_cursor *pCursor = 0;
|
||||
char *zTemp = 0;
|
||||
int nTemp = 0;
|
||||
|
||||
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
|
||||
if( rc==SQLITE_OK ){
|
||||
int ii;
|
||||
pCursor->pTokenizer = pTokenizer;
|
||||
for(ii=0; rc==SQLITE_OK; ii++){
|
||||
const char *zToken;
|
||||
int nToken, iBegin, iEnd, iPos;
|
||||
rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
|
||||
if( rc==SQLITE_OK ){
|
||||
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
|
||||
p = fts3ReallocOrFree(p, nByte+ii*sizeof(struct PhraseToken));
|
||||
zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken);
|
||||
if( !p || !zTemp ){
|
||||
goto no_mem;
|
||||
}
|
||||
if( ii==0 ){
|
||||
memset(p, 0, nByte);
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->eType = FTSQUERY_PHRASE;
|
||||
p->pPhrase->iColumn = pParse->iDefaultCol;
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->pPhrase->nToken = ii+1;
|
||||
p->pPhrase->aToken[ii].n = nToken;
|
||||
memcpy(&zTemp[nTemp], zToken, nToken);
|
||||
nTemp += nToken;
|
||||
if( iEnd<nInput && zInput[iEnd]=='*' ){
|
||||
p->pPhrase->aToken[ii].isPrefix = 1;
|
||||
}else{
|
||||
p->pPhrase->aToken[ii].isPrefix = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pModule->xClose(pCursor);
|
||||
pCursor = 0;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_DONE ){
|
||||
int jj;
|
||||
char *zNew;
|
||||
int nNew = 0;
|
||||
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
|
||||
nByte += (p->pPhrase->nToken-1) * sizeof(struct PhraseToken);
|
||||
p = fts3ReallocOrFree(p, nByte + nTemp);
|
||||
if( !p ){
|
||||
goto no_mem;
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
zNew = &(((char *)p)[nByte]);
|
||||
memcpy(zNew, zTemp, nTemp);
|
||||
for(jj=0; jj<p->pPhrase->nToken; jj++){
|
||||
p->pPhrase->aToken[jj].z = &zNew[nNew];
|
||||
nNew += p->pPhrase->aToken[jj].n;
|
||||
}
|
||||
sqlite3_free(zTemp);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
*ppExpr = p;
|
||||
return rc;
|
||||
no_mem:
|
||||
|
||||
if( pCursor ){
|
||||
pModule->xClose(pCursor);
|
||||
}
|
||||
sqlite3_free(zTemp);
|
||||
sqlite3_free(p);
|
||||
*ppExpr = 0;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
** Function getNextNode(), which is called by fts3ExprParse(), may itself
|
||||
** call fts3ExprParse(). So this forward declaration is required.
|
||||
*/
|
||||
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
|
||||
|
||||
/*
|
||||
** The output variable *ppExpr is populated with an allocated Fts3Expr
|
||||
** structure, or set to 0 if the end of the input buffer is reached.
|
||||
**
|
||||
** Returns an SQLite error code. SQLITE_OK if everything works, SQLITE_NOMEM
|
||||
** if a malloc failure occurs, or SQLITE_ERROR if a parse error is encountered.
|
||||
** If SQLITE_ERROR is returned, pContext is populated with an error message.
|
||||
*/
|
||||
static int getNextNode(
|
||||
ParseContext *pParse, /* fts3 query parse context */
|
||||
const char *z, int n, /* Input string */
|
||||
Fts3Expr **ppExpr, /* OUT: expression */
|
||||
int *pnConsumed /* OUT: Number of bytes consumed */
|
||||
){
|
||||
static const struct Fts3Keyword {
|
||||
char z[4]; /* Keyword text */
|
||||
unsigned char n; /* Length of the keyword */
|
||||
unsigned char parenOnly; /* Only valid in paren mode */
|
||||
unsigned char eType; /* Keyword code */
|
||||
} aKeyword[] = {
|
||||
{ "OR" , 2, 0, FTSQUERY_OR },
|
||||
{ "AND", 3, 1, FTSQUERY_AND },
|
||||
{ "NOT", 3, 1, FTSQUERY_NOT },
|
||||
{ "NEAR", 4, 0, FTSQUERY_NEAR }
|
||||
};
|
||||
int ii;
|
||||
int iCol;
|
||||
int iColLen;
|
||||
int rc;
|
||||
Fts3Expr *pRet = 0;
|
||||
|
||||
const char *zInput = z;
|
||||
int nInput = n;
|
||||
|
||||
/* Skip over any whitespace before checking for a keyword, an open or
|
||||
** close bracket, or a quoted string.
|
||||
*/
|
||||
while( nInput>0 && fts3isspace(*zInput) ){
|
||||
nInput--;
|
||||
zInput++;
|
||||
}
|
||||
if( nInput==0 ){
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/* See if we are dealing with a keyword. */
|
||||
for(ii=0; ii<(int)(sizeof(aKeyword)/sizeof(struct Fts3Keyword)); ii++){
|
||||
const struct Fts3Keyword *pKey = &aKeyword[ii];
|
||||
|
||||
if( (pKey->parenOnly & ~sqlite3_fts3_enable_parentheses)!=0 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
if( nInput>=pKey->n && 0==memcmp(zInput, pKey->z, pKey->n) ){
|
||||
int nNear = SQLITE_FTS3_DEFAULT_NEAR_PARAM;
|
||||
int nKey = pKey->n;
|
||||
char cNext;
|
||||
|
||||
/* If this is a "NEAR" keyword, check for an explicit nearness. */
|
||||
if( pKey->eType==FTSQUERY_NEAR ){
|
||||
assert( nKey==4 );
|
||||
if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
|
||||
nNear = 0;
|
||||
for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
|
||||
nNear = nNear * 10 + (zInput[nKey] - '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point this is probably a keyword. But for that to be true,
|
||||
** the next byte must contain either whitespace, an open or close
|
||||
** parenthesis, a quote character, or EOF.
|
||||
*/
|
||||
cNext = zInput[nKey];
|
||||
if( fts3isspace(cNext)
|
||||
|| cNext=='"' || cNext=='(' || cNext==')' || cNext==0
|
||||
){
|
||||
pRet = (Fts3Expr *)sqlite3_malloc(sizeof(Fts3Expr));
|
||||
memset(pRet, 0, sizeof(Fts3Expr));
|
||||
pRet->eType = pKey->eType;
|
||||
pRet->nNear = nNear;
|
||||
*ppExpr = pRet;
|
||||
*pnConsumed = (zInput - z) + nKey;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Turns out that wasn't a keyword after all. This happens if the
|
||||
** user has supplied a token such as "ORacle". Continue.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for an open bracket. */
|
||||
if( sqlite3_fts3_enable_parentheses ){
|
||||
if( *zInput=='(' ){
|
||||
int nConsumed;
|
||||
int rc;
|
||||
pParse->nNest++;
|
||||
rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed);
|
||||
if( rc==SQLITE_OK && !*ppExpr ){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
*pnConsumed = (zInput - z) + 1 + nConsumed;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check for a close bracket. */
|
||||
if( *zInput==')' ){
|
||||
pParse->nNest--;
|
||||
*pnConsumed = (zInput - z) + 1;
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we are dealing with a quoted phrase. If this is the case, then
|
||||
** search for the closing quote and pass the whole string to getNextString()
|
||||
** for processing. This is easy to do, as fts3 has no syntax for escaping
|
||||
** a quote character embedded in a string.
|
||||
*/
|
||||
if( *zInput=='"' ){
|
||||
for(ii=1; ii<nInput && zInput[ii]!='"'; ii++);
|
||||
*pnConsumed = (zInput - z) + ii + 1;
|
||||
if( ii==nInput ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return getNextString(pParse, &zInput[1], ii-1, ppExpr);
|
||||
}
|
||||
|
||||
|
||||
/* If control flows to this point, this must be a regular token, or
|
||||
** the end of the input. Read a regular token using the sqlite3_tokenizer
|
||||
** interface. Before doing so, figure out if there is an explicit
|
||||
** column specifier for the token.
|
||||
**
|
||||
** TODO: Strangely, it is not possible to associate a column specifier
|
||||
** with a quoted phrase, only with a single token. Not sure if this was
|
||||
** an implementation artifact or an intentional decision when fts3 was
|
||||
** first implemented. Whichever it was, this module duplicates the
|
||||
** limitation.
|
||||
*/
|
||||
iCol = pParse->iDefaultCol;
|
||||
iColLen = 0;
|
||||
for(ii=0; ii<pParse->nCol; ii++){
|
||||
const char *zStr = pParse->azCol[ii];
|
||||
int nStr = strlen(zStr);
|
||||
if( nInput>nStr && zInput[nStr]==':' && memcmp(zStr, zInput, nStr)==0 ){
|
||||
iCol = ii;
|
||||
iColLen = ((zInput - z) + nStr + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = getNextToken(pParse, iCol, &z[iColLen], n-iColLen, ppExpr, pnConsumed);
|
||||
*pnConsumed += iColLen;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The argument is an Fts3Expr structure for a binary operator (any type
|
||||
** except an FTSQUERY_PHRASE). Return an integer value representing the
|
||||
** precedence of the operator. Lower values have a higher precedence (i.e.
|
||||
** group more tightly). For example, in the C language, the == operator
|
||||
** groups more tightly than ||, and would therefore have a higher precedence.
|
||||
**
|
||||
** When using the new fts3 query syntax (when SQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
** is defined), the order of the operators in precedence from highest to
|
||||
** lowest is:
|
||||
**
|
||||
** NEAR
|
||||
** NOT
|
||||
** AND (including implicit ANDs)
|
||||
** OR
|
||||
**
|
||||
** Note that when using the old query syntax, the OR operator has a higher
|
||||
** precedence than the AND operator.
|
||||
*/
|
||||
static int opPrecedence(Fts3Expr *p){
|
||||
assert( p->eType!=FTSQUERY_PHRASE );
|
||||
if( sqlite3_fts3_enable_parentheses ){
|
||||
return p->eType;
|
||||
}else if( p->eType==FTSQUERY_NEAR ){
|
||||
return 1;
|
||||
}else if( p->eType==FTSQUERY_OR ){
|
||||
return 2;
|
||||
}
|
||||
assert( p->eType==FTSQUERY_AND );
|
||||
return 3;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument ppHead contains a pointer to the current head of a query
|
||||
** expression tree being parsed. pPrev is the expression node most recently
|
||||
** inserted into the tree. This function adds pNew, which is always a binary
|
||||
** operator node, into the expression tree based on the relative precedence
|
||||
** of pNew and the existing nodes of the tree. This may result in the head
|
||||
** of the tree changing, in which case *ppHead is set to the new root node.
|
||||
*/
|
||||
static void insertBinaryOperator(
|
||||
Fts3Expr **ppHead, /* Pointer to the root node of a tree */
|
||||
Fts3Expr *pPrev, /* Node most recently inserted into the tree */
|
||||
Fts3Expr *pNew /* New binary node to insert into expression tree */
|
||||
){
|
||||
Fts3Expr *pSplit = pPrev;
|
||||
while( pSplit->pParent && opPrecedence(pSplit->pParent)<=opPrecedence(pNew) ){
|
||||
pSplit = pSplit->pParent;
|
||||
}
|
||||
|
||||
if( pSplit->pParent ){
|
||||
assert( pSplit->pParent->pRight==pSplit );
|
||||
pSplit->pParent->pRight = pNew;
|
||||
pNew->pParent = pSplit->pParent;
|
||||
}else{
|
||||
*ppHead = pNew;
|
||||
}
|
||||
pNew->pLeft = pSplit;
|
||||
pSplit->pParent = pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse the fts3 query expression found in buffer z, length n. This function
|
||||
** returns either when the end of the buffer is reached or an unmatched
|
||||
** closing bracket - ')' - is encountered.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned, *ppExpr is set to point to the
|
||||
** parsed form of the expression and *pnConsumed is set to the number of
|
||||
** bytes read from buffer z. Otherwise, *ppExpr is set to 0 and SQLITE_NOMEM
|
||||
** (out of memory error) or SQLITE_ERROR (parse error) is returned.
|
||||
*/
|
||||
static int fts3ExprParse(
|
||||
ParseContext *pParse, /* fts3 query parse context */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
Fts3Expr **ppExpr, /* OUT: Parsed query structure */
|
||||
int *pnConsumed /* OUT: Number of bytes consumed */
|
||||
){
|
||||
Fts3Expr *pRet = 0;
|
||||
Fts3Expr *pPrev = 0;
|
||||
Fts3Expr *pNotBranch = 0; /* Only used in legacy parse mode */
|
||||
int nIn = n;
|
||||
const char *zIn = z;
|
||||
int rc = SQLITE_OK;
|
||||
int isRequirePhrase = 1;
|
||||
|
||||
while( rc==SQLITE_OK ){
|
||||
Fts3Expr *p = 0;
|
||||
int nByte = 0;
|
||||
rc = getNextNode(pParse, zIn, nIn, &p, &nByte);
|
||||
if( rc==SQLITE_OK ){
|
||||
int isPhrase;
|
||||
|
||||
if( !sqlite3_fts3_enable_parentheses
|
||||
&& p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot
|
||||
){
|
||||
/* Create an implicit NOT operator. */
|
||||
Fts3Expr *pNot = sqlite3_malloc(sizeof(Fts3Expr));
|
||||
if( !pNot ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_NOMEM;
|
||||
goto exprparse_out;
|
||||
}
|
||||
memset(pNot, 0, sizeof(Fts3Expr));
|
||||
pNot->eType = FTSQUERY_NOT;
|
||||
pNot->pRight = p;
|
||||
if( pNotBranch ){
|
||||
pNotBranch->pLeft = p;
|
||||
pNot->pRight = pNotBranch;
|
||||
}
|
||||
pNotBranch = pNot;
|
||||
}else{
|
||||
int eType = p->eType;
|
||||
assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot );
|
||||
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
|
||||
|
||||
/* The isRequirePhrase variable is set to true if a phrase or
|
||||
** an expression contained in parenthesis is required. If a
|
||||
** binary operator (AND, OR, NOT or NEAR) is encounted when
|
||||
** isRequirePhrase is set, this is a syntax error.
|
||||
*/
|
||||
if( !isPhrase && isRequirePhrase ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_ERROR;
|
||||
goto exprparse_out;
|
||||
}
|
||||
|
||||
if( isPhrase && !isRequirePhrase ){
|
||||
/* Insert an implicit AND operator. */
|
||||
Fts3Expr *pAnd;
|
||||
assert( pRet && pPrev );
|
||||
pAnd = sqlite3_malloc(sizeof(Fts3Expr));
|
||||
if( !pAnd ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_NOMEM;
|
||||
goto exprparse_out;
|
||||
}
|
||||
memset(pAnd, 0, sizeof(Fts3Expr));
|
||||
pAnd->eType = FTSQUERY_AND;
|
||||
insertBinaryOperator(&pRet, pPrev, pAnd);
|
||||
pPrev = pAnd;
|
||||
}
|
||||
|
||||
/* This test catches attempts to make either operand of a NEAR
|
||||
** operator something other than a phrase. For example, either of
|
||||
** the following:
|
||||
**
|
||||
** (bracketed expression) NEAR phrase
|
||||
** phrase NEAR (bracketed expression)
|
||||
**
|
||||
** Return an error in either case.
|
||||
*/
|
||||
if( pPrev && (
|
||||
(eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE)
|
||||
|| (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR)
|
||||
)){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_ERROR;
|
||||
goto exprparse_out;
|
||||
}
|
||||
|
||||
if( isPhrase ){
|
||||
if( pRet ){
|
||||
assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
|
||||
pPrev->pRight = p;
|
||||
p->pParent = pPrev;
|
||||
}else{
|
||||
pRet = p;
|
||||
}
|
||||
}else{
|
||||
insertBinaryOperator(&pRet, pPrev, p);
|
||||
}
|
||||
isRequirePhrase = !isPhrase;
|
||||
}
|
||||
assert( nByte>0 );
|
||||
}
|
||||
assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) );
|
||||
nIn -= nByte;
|
||||
zIn += nByte;
|
||||
pPrev = p;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_DONE && pRet && isRequirePhrase ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
if( !sqlite3_fts3_enable_parentheses && pNotBranch ){
|
||||
if( !pRet ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNotBranch->pLeft = pRet;
|
||||
pRet = pNotBranch;
|
||||
}
|
||||
}
|
||||
}
|
||||
*pnConsumed = n - nIn;
|
||||
|
||||
exprparse_out:
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3Fts3ExprFree(pRet);
|
||||
sqlite3Fts3ExprFree(pNotBranch);
|
||||
pRet = 0;
|
||||
}
|
||||
*ppExpr = pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameters z and n contain a pointer to and length of a buffer containing
|
||||
** an fts3 query expression, respectively. This function attempts to parse the
|
||||
** query expression and create a tree of Fts3Expr structures representing the
|
||||
** parsed expression. If successful, *ppExpr is set to point to the head
|
||||
** of the parsed expression tree and SQLITE_OK is returned. If an error
|
||||
** occurs, either SQLITE_NOMEM (out-of-memory error) or SQLITE_ERROR (parse
|
||||
** error) is returned and *ppExpr is set to 0.
|
||||
**
|
||||
** If parameter n is a negative number, then z is assumed to point to a
|
||||
** nul-terminated string and the length is determined using strlen().
|
||||
**
|
||||
** The first parameter, pTokenizer, is passed the fts3 tokenizer module to
|
||||
** use to normalize query tokens while parsing the expression. The azCol[]
|
||||
** array, which is assumed to contain nCol entries, should contain the names
|
||||
** of each column in the target fts3 table, in order from left to right.
|
||||
** Column names must be nul-terminated strings.
|
||||
**
|
||||
** The iDefaultCol parameter should be passed the index of the table column
|
||||
** that appears on the left-hand-side of the MATCH operator (the default
|
||||
** column to match against for tokens for which a column name is not explicitly
|
||||
** specified as part of the query string), or -1 if tokens may by default
|
||||
** match any table column.
|
||||
*/
|
||||
int sqlite3Fts3ExprParse(
|
||||
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
|
||||
char **azCol, /* Array of column names for fts3 table */
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
int iDefaultCol, /* Default column to query */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
Fts3Expr **ppExpr /* OUT: Parsed query structure */
|
||||
){
|
||||
int nParsed;
|
||||
int rc;
|
||||
ParseContext sParse;
|
||||
sParse.pTokenizer = pTokenizer;
|
||||
sParse.azCol = (const char **)azCol;
|
||||
sParse.nCol = nCol;
|
||||
sParse.iDefaultCol = iDefaultCol;
|
||||
sParse.nNest = 0;
|
||||
if( z==0 ){
|
||||
*ppExpr = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( n<0 ){
|
||||
n = strlen(z);
|
||||
}
|
||||
rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
|
||||
|
||||
/* Check for mismatched parenthesis */
|
||||
if( rc==SQLITE_OK && sParse.nNest ){
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3Fts3ExprFree(*ppExpr);
|
||||
*ppExpr = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().
|
||||
*/
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *p){
|
||||
if( p ){
|
||||
sqlite3Fts3ExprFree(p->pLeft);
|
||||
sqlite3Fts3ExprFree(p->pRight);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*****************************************************************************
|
||||
** Everything after this point is just test code.
|
||||
*/
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
** Function to query the hash-table of tokenizers (see README.tokenizers).
|
||||
*/
|
||||
static int queryTestTokenizer(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
const sqlite3_tokenizer_module **pp
|
||||
){
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt;
|
||||
const char zSql[] = "SELECT fts3_tokenizer(?)";
|
||||
|
||||
*pp = 0;
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
|
||||
memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
|
||||
}
|
||||
}
|
||||
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is part of the test interface for the query parser. It
|
||||
** writes a text representation of the query expression pExpr into the
|
||||
** buffer pointed to by argument zBuf. It is assumed that zBuf is large
|
||||
** enough to store the required text representation.
|
||||
*/
|
||||
static void exprToString(Fts3Expr *pExpr, char *zBuf){
|
||||
switch( pExpr->eType ){
|
||||
case FTSQUERY_PHRASE: {
|
||||
Fts3Phrase *pPhrase = pExpr->pPhrase;
|
||||
int i;
|
||||
zBuf += sprintf(zBuf, "PHRASE %d %d", pPhrase->iColumn, pPhrase->isNot);
|
||||
for(i=0; i<pPhrase->nToken; i++){
|
||||
zBuf += sprintf(zBuf," %.*s",pPhrase->aToken[i].n,pPhrase->aToken[i].z);
|
||||
zBuf += sprintf(zBuf,"%s", (pPhrase->aToken[i].isPrefix?"+":""));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case FTSQUERY_NEAR:
|
||||
zBuf += sprintf(zBuf, "NEAR/%d ", pExpr->nNear);
|
||||
break;
|
||||
case FTSQUERY_NOT:
|
||||
zBuf += sprintf(zBuf, "NOT ");
|
||||
break;
|
||||
case FTSQUERY_AND:
|
||||
zBuf += sprintf(zBuf, "AND ");
|
||||
break;
|
||||
case FTSQUERY_OR:
|
||||
zBuf += sprintf(zBuf, "OR ");
|
||||
break;
|
||||
}
|
||||
|
||||
zBuf += sprintf(zBuf, "{");
|
||||
exprToString(pExpr->pLeft, zBuf);
|
||||
zBuf += strlen(zBuf);
|
||||
zBuf += sprintf(zBuf, "} ");
|
||||
|
||||
zBuf += sprintf(zBuf, "{");
|
||||
exprToString(pExpr->pRight, zBuf);
|
||||
zBuf += strlen(zBuf);
|
||||
zBuf += sprintf(zBuf, "}");
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the implementation of a scalar SQL function used to test the
|
||||
** expression parser. It should be called as follows:
|
||||
**
|
||||
** fts3_exprtest(<tokenizer>, <expr>, <column 1>, ...);
|
||||
**
|
||||
** The first argument, <tokenizer>, is the name of the fts3 tokenizer used
|
||||
** to parse the query expression (see README.tokenizers). The second argument
|
||||
** is the query expression to parse. Each subsequent argument is the name
|
||||
** of a column of the fts3 table that the query expression may refer to.
|
||||
** For example:
|
||||
**
|
||||
** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2');
|
||||
*/
|
||||
static void fts3ExprTest(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_tokenizer_module const *pModule = 0;
|
||||
sqlite3_tokenizer *pTokenizer = 0;
|
||||
int rc;
|
||||
char **azCol = 0;
|
||||
const char *zExpr;
|
||||
int nExpr;
|
||||
int nCol;
|
||||
int ii;
|
||||
Fts3Expr *pExpr;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
if( argc<3 ){
|
||||
sqlite3_result_error(context,
|
||||
"Usage: fts3_exprtest(tokenizer, expr, col1, ...", -1
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = queryTestTokenizer(db,
|
||||
(const char *)sqlite3_value_text(argv[0]), &pModule);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
goto exprtest_out;
|
||||
}else if( !pModule ){
|
||||
sqlite3_result_error(context, "No such tokenizer module", -1);
|
||||
goto exprtest_out;
|
||||
}
|
||||
|
||||
rc = pModule->xCreate(0, 0, &pTokenizer);
|
||||
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
goto exprtest_out;
|
||||
}
|
||||
pTokenizer->pModule = pModule;
|
||||
|
||||
zExpr = (const char *)sqlite3_value_text(argv[1]);
|
||||
nExpr = sqlite3_value_bytes(argv[1]);
|
||||
nCol = argc-2;
|
||||
azCol = (char **)sqlite3_malloc(nCol*sizeof(char *));
|
||||
if( !azCol ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
goto exprtest_out;
|
||||
}
|
||||
for(ii=0; ii<nCol; ii++){
|
||||
azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3ExprParse(
|
||||
pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr
|
||||
);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
goto exprtest_out;
|
||||
}else if( rc==SQLITE_OK ){
|
||||
char zBuf[4096];
|
||||
exprToString(pExpr, zBuf);
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
sqlite3Fts3ExprFree(pExpr);
|
||||
}else{
|
||||
sqlite3_result_error(context, "Error parsing expression", -1);
|
||||
}
|
||||
|
||||
exprtest_out:
|
||||
if( pModule && pTokenizer ){
|
||||
rc = pModule->xDestroy(pTokenizer);
|
||||
}
|
||||
sqlite3_free(azCol);
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the query expression parser test function fts3_exprtest()
|
||||
** with database connection db.
|
||||
*/
|
||||
void sqlite3Fts3ExprInitTestInterface(sqlite3* db){
|
||||
sqlite3_create_function(
|
||||
db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
** 2008 Nov 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
*/
|
||||
|
||||
#include "fts3_tokenizer.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following describes the syntax supported by the fts3 MATCH
|
||||
** operator in a similar format to that used by the lemon parser
|
||||
** generator. This module does not use actually lemon, it uses a
|
||||
** custom parser.
|
||||
**
|
||||
** query ::= andexpr (OR andexpr)*.
|
||||
**
|
||||
** andexpr ::= notexpr (AND? notexpr)*.
|
||||
**
|
||||
** notexpr ::= nearexpr (NOT nearexpr|-TOKEN)*.
|
||||
** notexpr ::= LP query RP.
|
||||
**
|
||||
** nearexpr ::= phrase (NEAR distance_opt nearexpr)*.
|
||||
**
|
||||
** distance_opt ::= .
|
||||
** distance_opt ::= / INTEGER.
|
||||
**
|
||||
** phrase ::= TOKEN.
|
||||
** phrase ::= COLUMN:TOKEN.
|
||||
** phrase ::= "TOKEN TOKEN TOKEN...".
|
||||
*/
|
||||
|
||||
typedef struct Fts3Expr Fts3Expr;
|
||||
typedef struct Fts3Phrase Fts3Phrase;
|
||||
|
||||
/*
|
||||
** A "phrase" is a sequence of one or more tokens that must match in
|
||||
** sequence. A single token is the base case and the most common case.
|
||||
** For a sequence of tokens contained in "...", nToken will be the number
|
||||
** of tokens in the string.
|
||||
*/
|
||||
struct Fts3Phrase {
|
||||
int nToken; /* Number of tokens in the phrase */
|
||||
int iColumn; /* Index of column this phrase must match */
|
||||
int isNot; /* Phrase prefixed by unary not (-) operator */
|
||||
struct PhraseToken {
|
||||
char *z; /* Text of the token */
|
||||
int n; /* Number of bytes in buffer pointed to by z */
|
||||
int isPrefix; /* True if token ends in with a "*" character */
|
||||
} aToken[1]; /* One entry for each token in the phrase */
|
||||
};
|
||||
|
||||
/*
|
||||
** A tree of these objects forms the RHS of a MATCH operator.
|
||||
*/
|
||||
struct Fts3Expr {
|
||||
int eType; /* One of the FTSQUERY_XXX values defined below */
|
||||
int nNear; /* Valid if eType==FTSQUERY_NEAR */
|
||||
Fts3Expr *pParent; /* pParent->pLeft==this or pParent->pRight==this */
|
||||
Fts3Expr *pLeft; /* Left operand */
|
||||
Fts3Expr *pRight; /* Right operand */
|
||||
Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */
|
||||
};
|
||||
|
||||
int sqlite3Fts3ExprParse(sqlite3_tokenizer *, char **, int, int,
|
||||
const char *, int, Fts3Expr **);
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *);
|
||||
|
||||
/*
|
||||
** Candidate values for Fts3Query.eType. Note that the order of the first
|
||||
** four values is in order of precedence when parsing expressions. For
|
||||
** example, the following:
|
||||
**
|
||||
** "a OR b AND c NOT d NEAR e"
|
||||
**
|
||||
** is equivalent to:
|
||||
**
|
||||
** "a OR (b AND (c NOT (d NEAR e)))"
|
||||
*/
|
||||
#define FTSQUERY_NEAR 1
|
||||
#define FTSQUERY_NOT 2
|
||||
#define FTSQUERY_AND 3
|
||||
#define FTSQUERY_OR 4
|
||||
#define FTSQUERY_PHRASE 5
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
void sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
|
||||
#endif
|
||||
@@ -338,6 +338,13 @@ void *sqlite3Fts3HashInsert(
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
if( pH->htsize==0 ){
|
||||
fts3Rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
new_elem = (fts3HashElem*)fts3HashMalloc( sizeof(fts3HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
@@ -352,14 +359,6 @@ void *sqlite3Fts3HashInsert(
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
fts3Rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
fts3HashFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
fts3Rehash(pH,pH->htsize*2);
|
||||
}
|
||||
|
||||
+4
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file implements a tokenizer for fts3 based on the ICU library.
|
||||
**
|
||||
** $Id: fts3_icu.c,v 1.2 2007/10/24 21:52:37 shess Exp $
|
||||
** $Id: fts3_icu.c,v 1.3 2008/09/01 18:34:20 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
@@ -112,7 +112,9 @@ static int icuOpen(
|
||||
|
||||
*ppCursor = 0;
|
||||
|
||||
if( -1 == nInput ) nInput = strlen(nInput);
|
||||
if( nInput<0 ){
|
||||
nInput = strlen(zInput);
|
||||
}
|
||||
nChar = nInput+1;
|
||||
pCsr = (IcuCursor *)sqlite3_malloc(
|
||||
sizeof(IcuCursor) + /* IcuCursor */
|
||||
|
||||
@@ -113,7 +113,10 @@ struct sqlite3_tokenizer_module {
|
||||
** stemming has been performed). *pnBytes should be set to the length
|
||||
** of this buffer in bytes. The input text that generated the token is
|
||||
** identified by the byte offsets returned in *piStartOffset and
|
||||
** *piEndOffset.
|
||||
** *piEndOffset. *piStartOffset should be set to the index of the first
|
||||
** byte of the token in the input buffer. *piEndOffset should be set
|
||||
** to the index of the first byte just past the end of the token in
|
||||
** the input buffer.
|
||||
**
|
||||
** The buffer *ppToken is set to point at is managed by the tokenizer
|
||||
** implementation. It is only required to be valid until the next call
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
** 2008 May 26
|
||||
**
|
||||
** 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 header file is used by programs that want to link against the
|
||||
** ICU extension. All it does is declare the sqlite3IcuInit() interface.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sqlite3IcuInit(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
+44
-11
@@ -12,7 +12,7 @@
|
||||
** This file contains code for implementations of the r-tree and r*-tree
|
||||
** algorithms packaged as an SQLite virtual table module.
|
||||
**
|
||||
** $Id: rtree.c,v 1.7 2008/07/16 14:43:35 drh Exp $
|
||||
** $Id: rtree.c,v 1.12 2008/12/22 15:04:32 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE)
|
||||
@@ -226,8 +226,12 @@ struct RtreeCell {
|
||||
RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2];
|
||||
};
|
||||
|
||||
#define MAX(x,y) ((x) < (y) ? (y) : (x))
|
||||
#define MIN(x,y) ((x) > (y) ? (y) : (x))
|
||||
#ifndef MAX
|
||||
# define MAX(x,y) ((x) < (y) ? (y) : (x))
|
||||
#endif
|
||||
#ifndef MIN
|
||||
# define MIN(x,y) ((x) > (y) ? (y) : (x))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Functions to deserialize a 16 bit integer, 32 bit real number and
|
||||
@@ -396,7 +400,8 @@ nodeAcquire(
|
||||
*/
|
||||
if( (pNode = nodeHashLookup(pRtree, iNode)) ){
|
||||
assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
|
||||
if( pParent ){
|
||||
if( pParent && !pNode->pParent ){
|
||||
nodeReference(pParent);
|
||||
pNode->pParent = pParent;
|
||||
}
|
||||
pNode->nRef++;
|
||||
@@ -600,7 +605,7 @@ static void nodeGetCell(
|
||||
** the virtual table module xCreate() and xConnect() methods.
|
||||
*/
|
||||
static int rtreeInit(
|
||||
sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int, int
|
||||
sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -613,7 +618,7 @@ static int rtreeCreate(
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1, (int)pAux);
|
||||
return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -626,7 +631,7 @@ static int rtreeConnect(
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0, (int)pAux);
|
||||
return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1116,6 +1121,13 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->aConstraintUsage[ii].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[jj].omit = 1;
|
||||
|
||||
/* This strategy involves a two rowid lookups on an B-Tree structures
|
||||
** and then a linear search of an R-Tree node. This should be
|
||||
** considered almost as quick as a direct rowid lookup (for which
|
||||
** sqlite uses an internal cost of 0.0).
|
||||
*/
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -1169,6 +1181,8 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( iIdx>=0 );
|
||||
pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1));
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1215,6 +1229,25 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the area covered by p2 is a subset of the area covered
|
||||
** by p1. False otherwise.
|
||||
*/
|
||||
static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
||||
int ii;
|
||||
int isInt = (pRtree->eCoordType==RTREE_COORD_INT32);
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
RtreeCoord *a1 = &p1->aCoord[ii];
|
||||
RtreeCoord *a2 = &p2->aCoord[ii];
|
||||
if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f))
|
||||
|| ( isInt && (a2[0].i<a1[0].i || a2[1].i>a1[1].i))
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the amount cell p would grow by if it were unioned with pCell.
|
||||
*/
|
||||
@@ -1381,7 +1414,7 @@ static void AdjustTree(
|
||||
int iCell = nodeParentIndex(pRtree, p);
|
||||
|
||||
nodeGetCell(pRtree, pParent, iCell, &cell);
|
||||
if( cellGrowth(pRtree, &cell, pCell)>0.0 ){
|
||||
if( !cellContains(pRtree, &cell, pCell) ){
|
||||
cellUnion(pRtree, &cell, pCell);
|
||||
nodeOverwriteCell(pRtree, pParent, &cell, iCell);
|
||||
}
|
||||
@@ -2623,18 +2656,18 @@ static int getPageSize(sqlite3 *db, const char *zDb, int *piPageSize){
|
||||
*/
|
||||
static int rtreeInit(
|
||||
sqlite3 *db, /* Database connection */
|
||||
void *pAux, /* Pointer to head of rtree list */
|
||||
void *pAux, /* One of the RTREE_COORD_* constants */
|
||||
int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */
|
||||
sqlite3_vtab **ppVtab, /* OUT: New virtual table */
|
||||
char **pzErr, /* OUT: Error message, if any */
|
||||
int isCreate, /* True for xCreate, false for xConnect */
|
||||
int eCoordType /* One of the RTREE_COORD_* constants */
|
||||
int isCreate /* True for xCreate, false for xConnect */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int iPageSize = 0;
|
||||
Rtree *pRtree;
|
||||
int nDb; /* Length of string argv[1] */
|
||||
int nName; /* Length of string argv[2] */
|
||||
int eCoordType = (int)pAux;
|
||||
|
||||
const char *aErrMsg[] = {
|
||||
0, /* 0 */
|
||||
|
||||
+33
-1
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# The focus of this file is testing the r-tree extension.
|
||||
#
|
||||
# $Id: rtree1.test,v 1.5 2008/07/14 15:37:01 danielk1977 Exp $
|
||||
# $Id: rtree1.test,v 1.6 2008/12/22 15:04:32 danielk1977 Exp $
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
@@ -360,5 +360,37 @@ do_test rtree-8.1.6 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
|
||||
do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
|
||||
do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test cases rtree-9.*
|
||||
#
|
||||
# Test that ticket #3549 is fixed.
|
||||
do_test rtree-9.1 {
|
||||
execsql {
|
||||
CREATE TABLE foo (id INTEGER PRIMARY KEY);
|
||||
CREATE VIRTUAL TABLE bar USING rtree (id, minX, maxX, minY, maxY);
|
||||
INSERT INTO foo VALUES (null);
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
INSERT INTO foo SELECT null FROM foo;
|
||||
DELETE FROM foo WHERE id > 40;
|
||||
INSERT INTO bar SELECT NULL, 0, 0, 0, 0 FROM foo;
|
||||
}
|
||||
} {}
|
||||
|
||||
# This used to crash.
|
||||
do_test rtree-9.2 {
|
||||
execsql {
|
||||
SELECT count(*) FROM bar b1, bar b2, foo s1 WHERE s1.id = b1.id;
|
||||
}
|
||||
} {1600}
|
||||
do_test rtree-9.3 {
|
||||
execsql {
|
||||
SELECT count(*) FROM bar b1, bar b2, foo s1
|
||||
WHERE b1.minX <= b2.maxX AND s1.id = b1.id;
|
||||
}
|
||||
} {1600}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# 2008 Sep 1
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: rtree6.test,v 1.1 2008/09/01 12:47:00 danielk1977 Exp $
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname $argv0] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Operator Byte Value
|
||||
# ----------------------
|
||||
# = 0x41 ('A')
|
||||
# <= 0x42 ('B')
|
||||
# < 0x43 ('C')
|
||||
# >= 0x44 ('D')
|
||||
# > 0x45 ('E')
|
||||
# ----------------------
|
||||
|
||||
proc rtree_strategy {sql} {
|
||||
set ret [list]
|
||||
db eval "explain $sql" a {
|
||||
if {$a(opcode) eq "VFilter"} {
|
||||
lappend ret $a(p4)
|
||||
}
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
proc query_plan {sql} {
|
||||
set ret [list]
|
||||
db eval "explain query plan $sql" a {
|
||||
lappend ret $a(detail)
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
do_test rtree6-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(k INTEGER PRIMARY KEY, v);
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test rtree6-1.2 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1>10}
|
||||
} {Ea}
|
||||
|
||||
do_test rtree6-1.3 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1<10}
|
||||
} {Ca}
|
||||
|
||||
do_test rtree6-1.4 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
|
||||
} {Ca}
|
||||
|
||||
do_test rtree6-1.5 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
|
||||
} {Ca}
|
||||
|
||||
do_test rtree6.2.1 {
|
||||
query_plan {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
|
||||
} [list \
|
||||
{TABLE t1 VIRTUAL TABLE INDEX 2:Ca} \
|
||||
{TABLE t2 USING PRIMARY KEY} \
|
||||
]
|
||||
|
||||
do_test rtree6.2.2 {
|
||||
query_plan {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
|
||||
} [list \
|
||||
{TABLE t1 VIRTUAL TABLE INDEX 2:Ca} \
|
||||
{TABLE t2 USING PRIMARY KEY} \
|
||||
]
|
||||
|
||||
do_test rtree6.2.3 {
|
||||
query_plan {SELECT * FROM t1,t2 WHERE k=ii}
|
||||
} [list \
|
||||
{TABLE t2} \
|
||||
{TABLE t1 VIRTUAL TABLE INDEX 1:} \
|
||||
]
|
||||
|
||||
do_test rtree6.2.4 {
|
||||
query_plan {SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10}
|
||||
} [list \
|
||||
{TABLE t2} \
|
||||
{TABLE t1 VIRTUAL TABLE INDEX 2:CaEb} \
|
||||
]
|
||||
|
||||
do_test rtree6.2.5 {
|
||||
query_plan {SELECT * FROM t1,t2 WHERE k=ii AND x1<v}
|
||||
} [list \
|
||||
{TABLE t2} \
|
||||
{TABLE t1 VIRTUAL TABLE INDEX 1:} \
|
||||
]
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 2008 Sep 08
|
||||
#
|
||||
# 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 focus of this file is testing that ticket #3363 is fixed.
|
||||
#
|
||||
# $Id: tkt3363.test,v 1.1 2008/09/08 11:07:03 danielk1977 Exp $
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname $argv0] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test tkt3363.1.1 {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2, y1, y2) }
|
||||
} {}
|
||||
|
||||
do_test tkt3363.1.2 {
|
||||
for {set ii 1} {$ii < 50} {incr ii} {
|
||||
set x 1000000
|
||||
set y [expr 4000000 + $ii*10]
|
||||
execsql { INSERT INTO t1 VALUES($ii, $x, $x, $y, $y) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test tkt3363.1.3 {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1 WHERE +y2>4000425.0;
|
||||
}
|
||||
} {7}
|
||||
|
||||
do_test tkt3363.1.4 {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1 WHERE y2>4000425.0;
|
||||
}
|
||||
} {7}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -44,40 +44,30 @@
|
||||
|
||||
# This is how we compile
|
||||
#
|
||||
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
|
||||
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP)
|
||||
TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ+= alter.o analyze.o attach.o auth.o bitvec.o btmutex.o btree.o build.o \
|
||||
callback.o complete.o date.o delete.o \
|
||||
expr.o fault.o func.o global.o hash.o insert.o journal.o loadext.o \
|
||||
main.o malloc.o mem1.o mem2.o mem3.o mem4.o mem5.o mem6.o \
|
||||
mutex.o mutex_os2.o mutex_unix.o mutex_w32.o \
|
||||
LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
backup.o bitvec.o btmutex.o btree.o build.o \
|
||||
callback.o complete.o date.o delete.o expr.o fault.o \
|
||||
fts3.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
|
||||
fts3_tokenizer.o fts3_tokenizer1.o \
|
||||
func.o global.o hash.o \
|
||||
icu.o insert.o journal.o legacy.o loadext.o \
|
||||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
|
||||
memjournal.o \
|
||||
mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \
|
||||
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
pager.o parse.o pragma.o prepare.o printf.o random.o \
|
||||
select.o status.o table.o $(TCLOBJ) tokenize.o trigger.o \
|
||||
pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \
|
||||
where.o utf.o legacy.o vtab.o rtree.o icu.o
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o \
|
||||
walker.o where.o utf.o vtab.o
|
||||
|
||||
|
||||
EXTOBJ = icu.o
|
||||
EXTOBJ += fts1.o \
|
||||
fts1_hash.o \
|
||||
fts1_tokenizer1.o \
|
||||
fts1_porter.o
|
||||
EXTOBJ += fts2.o \
|
||||
fts2_hash.o \
|
||||
fts2_icu.o \
|
||||
fts2_porter.o \
|
||||
fts2_tokenizer.o \
|
||||
fts2_tokenizer1.o
|
||||
EXTOBJ += fts3.o \
|
||||
fts3_hash.o \
|
||||
fts3_icu.o \
|
||||
fts3_porter.o \
|
||||
fts3_tokenizer.o \
|
||||
fts3_tokenizer1.o
|
||||
EXTOBJ += rtree.o
|
||||
|
||||
# All of the source code files.
|
||||
#
|
||||
@@ -86,6 +76,7 @@ SRC = \
|
||||
$(TOP)/src/analyze.c \
|
||||
$(TOP)/src/attach.c \
|
||||
$(TOP)/src/auth.c \
|
||||
$(TOP)/src/backup.c \
|
||||
$(TOP)/src/bitvec.c \
|
||||
$(TOP)/src/btmutex.c \
|
||||
$(TOP)/src/btree.c \
|
||||
@@ -109,14 +100,15 @@ SRC = \
|
||||
$(TOP)/src/loadext.c \
|
||||
$(TOP)/src/main.c \
|
||||
$(TOP)/src/malloc.c \
|
||||
$(TOP)/src/mem0.c \
|
||||
$(TOP)/src/mem1.c \
|
||||
$(TOP)/src/mem2.c \
|
||||
$(TOP)/src/mem3.c \
|
||||
$(TOP)/src/mem4.c \
|
||||
$(TOP)/src/mem5.c \
|
||||
$(TOP)/src/mem6.c \
|
||||
$(TOP)/src/memjournal.c \
|
||||
$(TOP)/src/mutex.c \
|
||||
$(TOP)/src/mutex.h \
|
||||
$(TOP)/src/mutex_noop.c \
|
||||
$(TOP)/src/mutex_os2.c \
|
||||
$(TOP)/src/mutex_unix.c \
|
||||
$(TOP)/src/mutex_w32.c \
|
||||
@@ -129,10 +121,15 @@ SRC = \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache.h \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
$(TOP)/src/resolve.c \
|
||||
$(TOP)/src/rowset.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/status.c \
|
||||
$(TOP)/src/shell.c \
|
||||
@@ -153,10 +150,10 @@ SRC = \
|
||||
$(TOP)/src/vdbeapi.c \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbeblob.c \
|
||||
$(TOP)/src/vdbefifo.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.c \
|
||||
$(TOP)/src/walker.c \
|
||||
$(TOP)/src/where.c
|
||||
|
||||
# Source code for extensions
|
||||
@@ -182,6 +179,8 @@ SRC += \
|
||||
SRC += \
|
||||
$(TOP)/ext/fts3/fts3.c \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_expr.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.c \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_icu.c \
|
||||
@@ -190,7 +189,8 @@ SRC += \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer1.c
|
||||
SRC += \
|
||||
$(TOP)/ext/icu/icu.c
|
||||
$(TOP)/ext/icu/sqliteicu.h \
|
||||
$(TOP)/ext/icu/icu.c
|
||||
SRC += \
|
||||
$(TOP)/ext/rtree/rtree.h \
|
||||
$(TOP)/ext/rtree/rtree.c
|
||||
@@ -221,33 +221,40 @@ TESTSRC = \
|
||||
$(TOP)/src/test9.c \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_backup.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_func.c \
|
||||
$(TOP)/src/test_hexio.c \
|
||||
$(TOP)/src/test_journal.c \
|
||||
$(TOP)/src/test_malloc.c \
|
||||
$(TOP)/src/test_md5.c \
|
||||
$(TOP)/src/test_mutex.c \
|
||||
$(TOP)/src/test_onefile.c \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_tclvar.c \
|
||||
$(TOP)/src/test_thread.c \
|
||||
$(TOP)/src/test_wsd.c \
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
|
||||
TESTSRC2 = \
|
||||
$(TOP)/src/attach.c $(TOP)/src/btree.c $(TOP)/src/build.c $(TOP)/src/date.c \
|
||||
$(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \
|
||||
$(TOP)/src/build.c $(TOP)/src/date.c \
|
||||
$(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/os.c \
|
||||
$(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
|
||||
$(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c $(TOP)/src/random.c \
|
||||
$(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache1.c $(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c
|
||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c \
|
||||
$(TOP)/ext/fts3/fts3.c $(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
@@ -262,6 +269,7 @@ HDR = \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/pcache.h \
|
||||
parse.h \
|
||||
sqlite3.h \
|
||||
$(TOP)/src/sqlite3ext.h \
|
||||
@@ -282,10 +290,13 @@ EXTHDR += \
|
||||
$(TOP)/ext/fts2/fts2_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_expr.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/rtree/rtree.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@@ -358,8 +369,8 @@ opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
|
||||
sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
|
||||
cat parse.h $(TOP)/src/vdbe.c |$(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
|
||||
|
||||
cat parse.h $(TOP)/src/vdbe.c | \
|
||||
$(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
|
||||
|
||||
# Rules to build parse.c and parse.h - the outputs of lemon.
|
||||
#
|
||||
@@ -409,6 +420,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_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c
|
||||
|
||||
fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c
|
||||
|
||||
@@ -502,4 +516,4 @@ clean:
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc target_source
|
||||
rm -f testloadext.dll libtestloadext.so
|
||||
rm -f sqlite3.c fts?amal.c
|
||||
rm -f sqlite3.c fts?amal.c tclsqlite3.c
|
||||
|
||||
@@ -13,6 +13,7 @@ TCLSTUBLIB=$TCLDIR/libtcl84stub.a
|
||||
OPTS='-DUSE_TCL_STUBS=1 -DBUILD_sqlite=1 -DSQLITE_OS_WIN=1'
|
||||
OPTS="$OPTS -DSQLITE_THREADSAFE=1"
|
||||
OPTS="$OPTS -DSQLITE_ENABLE_FTS3=1"
|
||||
OPTS="$OPTS -DSQLITE_ENABLE_RTREE=1"
|
||||
OPTS="$OPTS -DSQLITE_ENABLE_COLUMN_METADATA=1"
|
||||
CC="i386-mingw32msvc-gcc -Os $OPTS -Itsrc -I$TCLDIR"
|
||||
NM="i386-mingw32msvc-nm"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ includedir=@includedir@
|
||||
|
||||
Name: SQLite
|
||||
Description: SQL database engine
|
||||
Version: @VERSION@
|
||||
Version: @RELEASE@
|
||||
Libs: -L${libdir} -lsqlite
|
||||
Libs.private: @LIBS@
|
||||
Cflags: -I${includedir}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ includedir=@includedir@
|
||||
|
||||
Name: SQLite
|
||||
Description: SQL database engine
|
||||
Version: @VERSION@
|
||||
Version: @RELEASE@
|
||||
Libs: -L${libdir} -lsqlite3
|
||||
Libs.private: @LIBS@
|
||||
Cflags: -I${includedir}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+21
-13
@@ -12,10 +12,9 @@
|
||||
** This file contains C code routines that used to generate VDBE code
|
||||
** that implements the ALTER TABLE command.
|
||||
**
|
||||
** $Id: alter.c,v 1.47 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** The code in this file only exists if we are not omitting the
|
||||
@@ -39,7 +38,7 @@
|
||||
*/
|
||||
static void renameTableFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
||||
@@ -53,6 +52,8 @@ static void renameTableFunc(
|
||||
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TABLE
|
||||
** statement is that the table name is the first non-space token that
|
||||
** is immediately followed by a TK_LP or TK_USING token.
|
||||
@@ -74,7 +75,7 @@ static void renameTableFunc(
|
||||
do {
|
||||
zCsr += len;
|
||||
len = sqlite3GetToken(zCsr, &token);
|
||||
} while( token==TK_SPACE || token==TK_COMMENT );
|
||||
} while( token==TK_SPACE );
|
||||
assert( len>0 );
|
||||
} while( token!=TK_LP && token!=TK_USING );
|
||||
|
||||
@@ -94,7 +95,7 @@ static void renameTableFunc(
|
||||
*/
|
||||
static void renameTriggerFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
||||
@@ -106,9 +107,10 @@ static void renameTriggerFunc(
|
||||
unsigned char const *zCsr = zSql;
|
||||
int len = 0;
|
||||
char *zRet;
|
||||
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TRIGGER
|
||||
** statement is that the table name is the first token that is immediatedly
|
||||
** preceded by either TK_ON or TK_DOT and immediatedly followed by one
|
||||
@@ -302,7 +304,9 @@ void sqlite3AlterRenameTable(
|
||||
/* Make sure it is not a system table being altered, or a reserved name
|
||||
** that the table is being renamed to.
|
||||
*/
|
||||
if( strlen(pTab->zName)>6 && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ){
|
||||
if( sqlite3Strlen30(pTab->zName)>6
|
||||
&& 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName);
|
||||
goto exit_rename_table;
|
||||
}
|
||||
@@ -441,15 +445,15 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
Expr *pDflt; /* Default value for the new column */
|
||||
sqlite3 *db; /* The database connection; */
|
||||
|
||||
if( pParse->nErr ) return;
|
||||
db = pParse->db;
|
||||
if( pParse->nErr || db->mallocFailed ) return;
|
||||
pNew = pParse->pNewTable;
|
||||
assert( pNew );
|
||||
|
||||
db = pParse->db;
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zTab = pNew->zName;
|
||||
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
|
||||
pCol = &pNew->aCol[pNew->nCol-1];
|
||||
pDflt = pCol->pDflt;
|
||||
pTab = sqlite3FindTable(db, zTab, zDb);
|
||||
@@ -508,7 +512,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
|
||||
if( zCol ){
|
||||
char *zEnd = &zCol[pColDef->n-1];
|
||||
while( (zEnd>zCol && *zEnd==';') || isspace(*(unsigned char *)zEnd) ){
|
||||
while( (zEnd>zCol && *zEnd==';') || sqlite3Isspace(*zEnd) ){
|
||||
*zEnd-- = '\0';
|
||||
}
|
||||
sqlite3NestedParse(pParse,
|
||||
@@ -579,7 +583,11 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
||||
/* Put a copy of the Table struct in Parse.pNewTable for the
|
||||
** sqlite3AddColumn() function and friends to modify.
|
||||
** sqlite3AddColumn() function and friends to modify. But modify
|
||||
** the name by adding an "sqlite_altertab_" prefix. By adding this
|
||||
** prefix, we insure that the name will not collide with an existing
|
||||
** table because user table are not allowed to have the "sqlite_"
|
||||
** prefix on their name.
|
||||
*/
|
||||
pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
|
||||
if( !pNew ) goto exit_begin_add_column;
|
||||
@@ -591,7 +599,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
nAlloc = (((pNew->nCol-1)/8)*8)+8;
|
||||
assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
|
||||
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
|
||||
pNew->zName = sqlite3DbStrDup(db, pTab->zName);
|
||||
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
|
||||
if( !pNew->aCol || !pNew->zName ){
|
||||
db->mallocFailed = 1;
|
||||
goto exit_begin_add_column;
|
||||
|
||||
+16
-9
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code associated with the ANALYZE command.
|
||||
**
|
||||
** @(#) $Id: analyze.c,v 1.43 2008/07/28 19:34:53 drh Exp $
|
||||
** @(#) $Id: analyze.c,v 1.48 2009/02/13 16:59:53 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
#include "sqliteInt.h"
|
||||
@@ -33,7 +33,7 @@ static void openStatTable(
|
||||
sqlite3 *db = pParse->db;
|
||||
Db *pDb;
|
||||
int iRootPage;
|
||||
int createStat1 = 0;
|
||||
u8 createStat1 = 0;
|
||||
Table *pStat;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
|
||||
@@ -86,11 +86,11 @@ static void openStatTable(
|
||||
static void analyzeOneTable(
|
||||
Parse *pParse, /* Parser context */
|
||||
Table *pTab, /* Table whose indices are to be analyzed */
|
||||
int iStatCur, /* Cursor that writes to the sqlite_stat1 table */
|
||||
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
|
||||
int iMem /* Available memory locations begin here */
|
||||
){
|
||||
Index *pIdx; /* An index to being analyzed */
|
||||
int iIdxCur; /* Cursor number for index being analyzed */
|
||||
int iIdxCur; /* Index of VdbeCursor for index being analyzed */
|
||||
int nCol; /* Number of columns in the index */
|
||||
Vdbe *v; /* The virtual machine being built up */
|
||||
int i; /* Loop counter */
|
||||
@@ -356,7 +356,7 @@ struct analysisInfo {
|
||||
** argv[0] = name of the index
|
||||
** argv[1] = results of analysis - on integer for each column
|
||||
*/
|
||||
static int analysisLoader(void *pData, int argc, char **argv, char **azNotUsed){
|
||||
static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
analysisInfo *pInfo = (analysisInfo*)pData;
|
||||
Index *pIndex;
|
||||
int i, c;
|
||||
@@ -364,6 +364,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **azNotUsed){
|
||||
const char *z;
|
||||
|
||||
assert( argc==2 );
|
||||
UNUSED_PARAMETER2(NotUsed, argc);
|
||||
|
||||
if( argv==0 || argv[0]==0 || argv[1]==0 ){
|
||||
return 0;
|
||||
}
|
||||
@@ -414,10 +416,15 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1",
|
||||
sInfo.zDatabase);
|
||||
(void)sqlite3SafetyOff(db);
|
||||
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
|
||||
(void)sqlite3SafetyOn(db);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
(void)sqlite3SafetyOff(db);
|
||||
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
|
||||
(void)sqlite3SafetyOn(db);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
+39
-24
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||
**
|
||||
** $Id: attach.c,v 1.77 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: attach.c,v 1.82 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -39,7 +39,7 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
|
||||
int rc = SQLITE_OK;
|
||||
if( pExpr ){
|
||||
if( pExpr->op!=TK_ID ){
|
||||
rc = sqlite3ExprResolveNames(pName, pExpr);
|
||||
rc = sqlite3ResolveExprNames(pName, pExpr);
|
||||
if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){
|
||||
sqlite3ErrorMsg(pName->pParse, "invalid name: \"%T\"", &pExpr->span);
|
||||
return SQLITE_ERROR;
|
||||
@@ -64,7 +64,7 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
|
||||
*/
|
||||
static void attachFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int i;
|
||||
@@ -76,6 +76,8 @@ static void attachFunc(
|
||||
char *zErrDyn = 0;
|
||||
char zErr[128];
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
zFile = (const char *)sqlite3_value_text(argv[0]);
|
||||
zName = (const char *)sqlite3_value_text(argv[1]);
|
||||
if( zFile==0 ) zFile = "";
|
||||
@@ -232,7 +234,7 @@ attach_error:
|
||||
*/
|
||||
static void detachFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zName = (const char *)sqlite3_value_text(argv[0]);
|
||||
@@ -241,6 +243,8 @@ static void detachFunc(
|
||||
Db *pDb = 0;
|
||||
char zErr[128];
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
if( zName==0 ) zName = "";
|
||||
for(i=0; i<db->nDb; i++){
|
||||
pDb = &db->aDb[i];
|
||||
@@ -261,7 +265,7 @@ static void detachFunc(
|
||||
"cannot DETACH database within transaction");
|
||||
goto detach_error;
|
||||
}
|
||||
if( sqlite3BtreeIsInReadTrans(pDb->pBt) ){
|
||||
if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
@@ -283,8 +287,7 @@ detach_error:
|
||||
static void codeAttach(
|
||||
Parse *pParse, /* The parser context */
|
||||
int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */
|
||||
const char *zFunc, /* Either "sqlite_attach" or "sqlite_detach */
|
||||
int nFunc, /* Number of args to pass to zFunc */
|
||||
FuncDef *pFunc, /* FuncDef wrapper for detachFunc() or attachFunc() */
|
||||
Expr *pAuthArg, /* Expression to pass to authorization callback */
|
||||
Expr *pFilename, /* Name of database file */
|
||||
Expr *pDbname, /* Name of the database to use internally */
|
||||
@@ -293,7 +296,6 @@ static void codeAttach(
|
||||
int rc;
|
||||
NameContext sName;
|
||||
Vdbe *v;
|
||||
FuncDef *pFunc;
|
||||
sqlite3* db = pParse->db;
|
||||
int regArgs;
|
||||
|
||||
@@ -332,9 +334,9 @@ static void codeAttach(
|
||||
|
||||
assert( v || db->mallocFailed );
|
||||
if( v ){
|
||||
sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-nFunc, regArgs+3);
|
||||
sqlite3VdbeChangeP5(v, nFunc);
|
||||
pFunc = sqlite3FindFunction(db, zFunc, strlen(zFunc), nFunc, SQLITE_UTF8,0);
|
||||
sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-pFunc->nArg, regArgs+3);
|
||||
assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg );
|
||||
sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg));
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF);
|
||||
|
||||
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
|
||||
@@ -356,7 +358,19 @@ attach_end:
|
||||
** DETACH pDbname
|
||||
*/
|
||||
void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
codeAttach(pParse, SQLITE_DETACH, "sqlite_detach", 1, pDbname, 0, 0, pDbname);
|
||||
static FuncDef detach_func = {
|
||||
1, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
detachFunc, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"sqlite_detach", /* zName */
|
||||
0 /* pHash */
|
||||
};
|
||||
codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -365,21 +379,22 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
** ATTACH p AS pDbname KEY pKey
|
||||
*/
|
||||
void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
codeAttach(pParse, SQLITE_ATTACH, "sqlite_attach", 3, p, p, pDbname, pKey);
|
||||
static FuncDef attach_func = {
|
||||
3, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
attachFunc, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"sqlite_attach", /* zName */
|
||||
0 /* pHash */
|
||||
};
|
||||
codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_ATTACH */
|
||||
|
||||
/*
|
||||
** Register the functions sqlite_attach and sqlite_detach.
|
||||
*/
|
||||
void sqlite3AttachFunctions(sqlite3 *db){
|
||||
#ifndef SQLITE_OMIT_ATTACH
|
||||
static const int enc = SQLITE_UTF8;
|
||||
sqlite3CreateFunc(db, "sqlite_attach", 3, enc, 0, attachFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "sqlite_detach", 1, enc, 0, detachFunc, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
|
||||
+611
@@ -0,0 +1,611 @@
|
||||
/*
|
||||
** 2009 January 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains the implementation of the sqlite3_backup_XXX()
|
||||
** API functions and the related features.
|
||||
**
|
||||
** $Id: backup.c,v 1.12 2009/02/16 17:55:47 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
|
||||
/* Macro to find the minimum of two numeric values.
|
||||
*/
|
||||
#ifndef MIN
|
||||
# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Structure allocated for each backup operation.
|
||||
*/
|
||||
struct sqlite3_backup {
|
||||
sqlite3* pDestDb; /* Destination database handle */
|
||||
Btree *pDest; /* Destination b-tree file */
|
||||
u32 iDestSchema; /* Original schema cookie in destination */
|
||||
int bDestLocked; /* True once a write-transaction is open on pDest */
|
||||
|
||||
Pgno iNext; /* Page number of the next source page to copy */
|
||||
sqlite3* pSrcDb; /* Source database handle */
|
||||
Btree *pSrc; /* Source b-tree file */
|
||||
|
||||
int rc; /* Backup process error code */
|
||||
|
||||
/* These two variables are set by every call to backup_step(). They are
|
||||
** read by calls to backup_remaining() and backup_pagecount().
|
||||
*/
|
||||
Pgno nRemaining; /* Number of pages left to copy */
|
||||
Pgno nPagecount; /* Total number of pages to copy */
|
||||
|
||||
sqlite3_backup *pNext; /* Next backup associated with source pager */
|
||||
};
|
||||
|
||||
/*
|
||||
** THREAD SAFETY NOTES:
|
||||
**
|
||||
** Once it has been created using backup_init(), a single sqlite3_backup
|
||||
** structure may be accessed via two groups of thread-safe entry points:
|
||||
**
|
||||
** * Via the sqlite3_backup_XXX() API function backup_step() and
|
||||
** backup_finish(). Both these functions obtain the source database
|
||||
** handle mutex and the mutex associated with the source BtShared
|
||||
** structure, in that order.
|
||||
**
|
||||
** * Via the BackupUpdate() and BackupRestart() functions, which are
|
||||
** invoked by the pager layer to report various state changes in
|
||||
** the page cache associated with the source database. The mutex
|
||||
** associated with the source database BtShared structure will always
|
||||
** be held when either of these functions are invoked.
|
||||
**
|
||||
** The other sqlite3_backup_XXX() API functions, backup_remaining() and
|
||||
** backup_pagecount() are not thread-safe functions. If they are called
|
||||
** while some other thread is calling backup_step() or backup_finish(),
|
||||
** the values returned may be invalid. There is no way for a call to
|
||||
** BackupUpdate() or BackupRestart() to interfere with backup_remaining()
|
||||
** or backup_pagecount().
|
||||
**
|
||||
** Depending on the SQLite configuration, the database handles and/or
|
||||
** the Btree objects may have their own mutexes that require locking.
|
||||
** Non-sharable Btrees (in-memory databases for example), do not have
|
||||
** associated mutexes.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Return a pointer corresponding to database zDb (i.e. "main", "temp")
|
||||
** in connection handle pDb. If such a database cannot be found, return
|
||||
** a NULL pointer and write an error message to pErrorDb.
|
||||
**
|
||||
** If the "temp" database is requested, it may need to be opened by this
|
||||
** function. If an error occurs while doing so, return 0 and write an
|
||||
** error message to pErrorDb.
|
||||
*/
|
||||
static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
int i = sqlite3FindDbName(pDb, zDb);
|
||||
|
||||
if( i==1 ){
|
||||
Parse sParse;
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = pDb;
|
||||
if( sqlite3OpenTempDatabase(&sParse) ){
|
||||
sqlite3ErrorClear(&sParse);
|
||||
sqlite3Error(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
|
||||
return 0;
|
||||
}
|
||||
assert( sParse.zErrMsg==0 );
|
||||
}
|
||||
|
||||
if( i<0 ){
|
||||
sqlite3Error(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pDb->aDb[i].pBt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create an sqlite3_backup process to copy the contents of zSrcDb from
|
||||
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
|
||||
** a pointer to the new sqlite3_backup object.
|
||||
**
|
||||
** If an error occurs, NULL is returned and an error code and error message
|
||||
** stored in database handle pDestDb.
|
||||
*/
|
||||
sqlite3_backup *sqlite3_backup_init(
|
||||
sqlite3* pDestDb, /* Database to write to */
|
||||
const char *zDestDb, /* Name of database within pDestDb */
|
||||
sqlite3* pSrcDb, /* Database connection to read from */
|
||||
const char *zSrcDb /* Name of database within pSrcDb */
|
||||
){
|
||||
sqlite3_backup *p; /* Value to return */
|
||||
|
||||
/* Lock the source database handle. The destination database
|
||||
** handle is not locked in this routine, but it is locked in
|
||||
** sqlite3_backup_step(). The user is required to ensure that no
|
||||
** other thread accesses the destination handle for the duration
|
||||
** of the backup operation. Any attempt to use the destination
|
||||
** database connection while a backup is in progress may cause
|
||||
** a malfunction or a deadlock.
|
||||
*/
|
||||
sqlite3_mutex_enter(pSrcDb->mutex);
|
||||
sqlite3_mutex_enter(pDestDb->mutex);
|
||||
|
||||
if( pSrcDb==pDestDb ){
|
||||
sqlite3Error(
|
||||
pDestDb, SQLITE_ERROR, "source and destination must be distinct"
|
||||
);
|
||||
p = 0;
|
||||
}else {
|
||||
/* Allocate space for a new sqlite3_backup object */
|
||||
p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup));
|
||||
if( !p ){
|
||||
sqlite3Error(pDestDb, SQLITE_NOMEM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the allocation succeeded, populate the new object. */
|
||||
if( p ){
|
||||
memset(p, 0, sizeof(sqlite3_backup));
|
||||
p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
|
||||
p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
|
||||
p->pDestDb = pDestDb;
|
||||
p->pSrcDb = pSrcDb;
|
||||
p->iNext = 1;
|
||||
|
||||
if( 0==p->pSrc || 0==p->pDest ){
|
||||
/* One (or both) of the named databases did not exist. An error has
|
||||
** already been written into the pDestDb handle. All that is left
|
||||
** to do here is free the sqlite3_backup structure.
|
||||
*/
|
||||
sqlite3_free(p);
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If everything has gone as planned, attach the backup object to the
|
||||
** source pager. The source pager calls BackupUpdate() and BackupRestart()
|
||||
** to notify this module if the source file is modified mid-backup.
|
||||
*/
|
||||
if( p ){
|
||||
sqlite3_backup **pp; /* Pointer to head of pagers backup list */
|
||||
sqlite3BtreeEnter(p->pSrc);
|
||||
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
|
||||
p->pNext = *pp;
|
||||
*pp = p;
|
||||
sqlite3BtreeLeave(p->pSrc);
|
||||
p->pSrc->nBackup++;
|
||||
}
|
||||
|
||||
sqlite3_mutex_leave(pDestDb->mutex);
|
||||
sqlite3_mutex_leave(pSrcDb->mutex);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument rc is an SQLite error code. Return true if this error is
|
||||
** considered fatal if encountered during a backup operation. All errors
|
||||
** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED.
|
||||
*/
|
||||
static int isFatalError(int rc){
|
||||
return (rc!=SQLITE_OK && rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED);
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zSrcData points to a buffer containing the data for
|
||||
** page iSrcPg from the source database. Copy this data into the
|
||||
** destination database.
|
||||
*/
|
||||
static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
|
||||
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
|
||||
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
|
||||
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
|
||||
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
|
||||
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
i64 iOff;
|
||||
|
||||
assert( p->bDestLocked );
|
||||
assert( !isFatalError(p->rc) );
|
||||
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
|
||||
assert( zSrcData );
|
||||
|
||||
/* Catch the case where the destination is an in-memory database and the
|
||||
** page sizes of the source and destination differ.
|
||||
*/
|
||||
if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(sqlite3BtreePager(p->pDest)) ){
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/* This loop runs once for each destination page spanned by the source
|
||||
** page. For each iteration, variable iOff is set to the byte offset
|
||||
** of the destination page.
|
||||
*/
|
||||
for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){
|
||||
DbPage *pDestPg = 0;
|
||||
Pgno iDest = (Pgno)(iOff/nDestPgsz)+1;
|
||||
if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue;
|
||||
if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg))
|
||||
&& SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg))
|
||||
){
|
||||
const u8 *zIn = &zSrcData[iOff%nSrcPgsz];
|
||||
u8 *zDestData = sqlite3PagerGetData(pDestPg);
|
||||
u8 *zOut = &zDestData[iOff%nDestPgsz];
|
||||
|
||||
/* Copy the data from the source page into the destination page.
|
||||
** Then clear the Btree layer MemPage.isInit flag. Both this module
|
||||
** and the pager code use this trick (clearing the first byte
|
||||
** of the page 'extra' space to invalidate the Btree layers
|
||||
** cached parse of the page). MemPage.isInit is marked
|
||||
** "MUST BE FIRST" for this purpose.
|
||||
*/
|
||||
memcpy(zOut, zIn, nCopy);
|
||||
((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
|
||||
}
|
||||
sqlite3PagerUnref(pDestPg);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If pFile is currently larger than iSize bytes, then truncate it to
|
||||
** exactly iSize bytes. If pFile is not larger than iSize bytes, then
|
||||
** this function is a no-op.
|
||||
**
|
||||
** Return SQLITE_OK if everything is successful, or an SQLite error
|
||||
** code if an error occurs.
|
||||
*/
|
||||
static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){
|
||||
i64 iCurrent;
|
||||
int rc = sqlite3OsFileSize(pFile, &iCurrent);
|
||||
if( rc==SQLITE_OK && iCurrent>iSize ){
|
||||
rc = sqlite3OsTruncate(pFile, iSize);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy nPage pages from the source b-tree to the destination.
|
||||
*/
|
||||
int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
int rc;
|
||||
|
||||
sqlite3_mutex_enter(p->pSrcDb->mutex);
|
||||
sqlite3BtreeEnter(p->pSrc);
|
||||
if( p->pDestDb ){
|
||||
sqlite3_mutex_enter(p->pDestDb->mutex);
|
||||
}
|
||||
|
||||
rc = p->rc;
|
||||
if( !isFatalError(rc) ){
|
||||
Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */
|
||||
Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */
|
||||
int ii; /* Iterator variable */
|
||||
int nSrcPage = -1; /* Size of source db in pages */
|
||||
int bCloseTrans = 0; /* True if src db requires unlocking */
|
||||
|
||||
/* If the source pager is currently in a write-transaction, return
|
||||
** SQLITE_LOCKED immediately.
|
||||
*/
|
||||
if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
|
||||
rc = SQLITE_LOCKED;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
rc = sqlite3BtreeGetMeta(p->pDest, 1, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* If there is no open read-transaction on the source database, open
|
||||
** one now. If a transaction is opened here, then it will be closed
|
||||
** before this function exits.
|
||||
*/
|
||||
if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){
|
||||
rc = sqlite3BtreeBeginTrans(p->pSrc, 0);
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
|
||||
/* Now that there is a read-lock on the source database, query the
|
||||
** source pager for the number of pages in the database.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerPagecount(pSrcPager, &nSrcPage);
|
||||
}
|
||||
for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){
|
||||
const Pgno iSrcPg = p->iNext; /* Source page number */
|
||||
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
|
||||
DbPage *pSrcPg; /* Source page object */
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
}
|
||||
}
|
||||
p->iNext++;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
p->nPagecount = nSrcPage;
|
||||
p->nRemaining = nSrcPage+1-p->iNext;
|
||||
if( p->iNext>(Pgno)nSrcPage ){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_DONE ){
|
||||
const int nSrcPagesize = sqlite3BtreeGetPageSize(p->pSrc);
|
||||
const int nDestPagesize = sqlite3BtreeGetPageSize(p->pDest);
|
||||
int nDestTruncate;
|
||||
|
||||
/* Update the schema version field in the destination database. This
|
||||
** is to make sure that the schema-version really does change in
|
||||
** the case where the source and destination databases have the
|
||||
** same schema version.
|
||||
*/
|
||||
sqlite3BtreeUpdateMeta(p->pDest, 1, p->iDestSchema+1);
|
||||
if( p->pDestDb ){
|
||||
sqlite3ResetInternalSchema(p->pDestDb, 0);
|
||||
}
|
||||
|
||||
/* Set nDestTruncate to the final number of pages in the destination
|
||||
** database. The complication here is that the destination page
|
||||
** size may be different to the source page size.
|
||||
**
|
||||
** If the source page size is smaller than the destination page size,
|
||||
** round up. In this case the call to sqlite3OsTruncate() below will
|
||||
** fix the size of the file. However it is important to call
|
||||
** sqlite3PagerTruncateImage() here so that any pages in the
|
||||
** destination file that lie beyond the nDestTruncate page mark are
|
||||
** journalled by PagerCommitPhaseOne() before they are destroyed
|
||||
** by the file truncation.
|
||||
*/
|
||||
if( nSrcPagesize<nDestPagesize ){
|
||||
int ratio = nDestPagesize/nSrcPagesize;
|
||||
nDestTruncate = (nSrcPage+ratio-1)/ratio;
|
||||
if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
|
||||
nDestTruncate--;
|
||||
}
|
||||
}else{
|
||||
nDestTruncate = nSrcPage * (nSrcPagesize/nDestPagesize);
|
||||
}
|
||||
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
|
||||
|
||||
if( nSrcPagesize<nDestPagesize ){
|
||||
/* If the source page-size is smaller than the destination page-size,
|
||||
** two extra things may need to happen:
|
||||
**
|
||||
** * The destination may need to be truncated, and
|
||||
**
|
||||
** * Data stored on the pages immediately following the
|
||||
** pending-byte page in the source database may need to be
|
||||
** copied into the destination database.
|
||||
*/
|
||||
const i64 iSize = (i64)nSrcPagesize * (i64)nSrcPage;
|
||||
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
|
||||
|
||||
assert( pFile );
|
||||
assert( (i64)nDestTruncate*(i64)nDestPagesize >= iSize || (
|
||||
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
|
||||
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+nDestPagesize
|
||||
));
|
||||
if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1))
|
||||
&& SQLITE_OK==(rc = backupTruncateFile(pFile, iSize))
|
||||
&& SQLITE_OK==(rc = sqlite3PagerSync(pDestPager))
|
||||
){
|
||||
i64 iOff;
|
||||
i64 iEnd = MIN(PENDING_BYTE + nDestPagesize, iSize);
|
||||
for(
|
||||
iOff=PENDING_BYTE+nSrcPagesize;
|
||||
rc==SQLITE_OK && iOff<iEnd;
|
||||
iOff+=nSrcPagesize
|
||||
){
|
||||
PgHdr *pSrcPg = 0;
|
||||
const Pgno iSrcPg = (Pgno)((iOff/nSrcPagesize)+1);
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *zData = sqlite3PagerGetData(pSrcPg);
|
||||
rc = sqlite3OsWrite(pFile, zData, nSrcPagesize, iOff);
|
||||
}
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
|
||||
}
|
||||
|
||||
/* Finish committing the transaction to the destination database. */
|
||||
if( SQLITE_OK==rc
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest))
|
||||
){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If bCloseTrans is true, then this function opened a read transaction
|
||||
** on the source database. Close the read transaction here. There is
|
||||
** no need to check the return values of the btree methods here, as
|
||||
** "committing" a read-only transaction cannot fail.
|
||||
*/
|
||||
if( bCloseTrans ){
|
||||
TESTONLY( int rc2 );
|
||||
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
|
||||
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
|
||||
assert( rc2==SQLITE_OK );
|
||||
}
|
||||
|
||||
p->rc = rc;
|
||||
}
|
||||
if( p->pDestDb ){
|
||||
sqlite3_mutex_leave(p->pDestDb->mutex);
|
||||
}
|
||||
sqlite3BtreeLeave(p->pSrc);
|
||||
sqlite3_mutex_leave(p->pSrcDb->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release all resources associated with an sqlite3_backup* handle.
|
||||
*/
|
||||
int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
|
||||
sqlite3_mutex *mutex; /* Mutex to protect source database */
|
||||
int rc; /* Value to return */
|
||||
|
||||
/* Enter the mutexes */
|
||||
sqlite3_mutex_enter(p->pSrcDb->mutex);
|
||||
sqlite3BtreeEnter(p->pSrc);
|
||||
mutex = p->pSrcDb->mutex;
|
||||
if( p->pDestDb ){
|
||||
sqlite3_mutex_enter(p->pDestDb->mutex);
|
||||
}
|
||||
|
||||
/* Detach this backup from the source pager. */
|
||||
if( p->pDestDb ){
|
||||
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
|
||||
while( *pp!=p ){
|
||||
pp = &(*pp)->pNext;
|
||||
}
|
||||
*pp = p->pNext;
|
||||
p->pSrc->nBackup--;
|
||||
}
|
||||
|
||||
/* If a transaction is still open on the Btree, roll it back. */
|
||||
sqlite3BtreeRollback(p->pDest);
|
||||
|
||||
/* Set the error code of the destination database handle. */
|
||||
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
|
||||
sqlite3Error(p->pDestDb, rc, 0);
|
||||
|
||||
/* Exit the mutexes and free the backup context structure. */
|
||||
if( p->pDestDb ){
|
||||
sqlite3_mutex_leave(p->pDestDb->mutex);
|
||||
}
|
||||
sqlite3BtreeLeave(p->pSrc);
|
||||
if( p->pDestDb ){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of pages still to be backed up as of the most recent
|
||||
** call to sqlite3_backup_step().
|
||||
*/
|
||||
int sqlite3_backup_remaining(sqlite3_backup *p){
|
||||
return p->nRemaining;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of pages in the source database as of the most
|
||||
** recent call to sqlite3_backup_step().
|
||||
*/
|
||||
int sqlite3_backup_pagecount(sqlite3_backup *p){
|
||||
return p->nPagecount;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called after the contents of page iPage of the
|
||||
** source database have been modified. If page iPage has already been
|
||||
** copied into the destination database, then the data written to the
|
||||
** destination is now invalidated. The destination copy of iPage needs
|
||||
** to be updated with the new data before the backup operation is
|
||||
** complete.
|
||||
**
|
||||
** It is assumed that the mutex associated with the BtShared object
|
||||
** corresponding to the source database is held when this function is
|
||||
** called.
|
||||
*/
|
||||
void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
|
||||
sqlite3_backup *p; /* Iterator variable */
|
||||
for(p=pBackup; p; p=p->pNext){
|
||||
assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
|
||||
if( !isFatalError(p->rc) && iPage<p->iNext ){
|
||||
/* The backup process p has already copied page iPage. But now it
|
||||
** has been modified by a transaction on the source pager. Copy
|
||||
** the new data into the backup.
|
||||
*/
|
||||
int rc = backupOnePage(p, iPage, aData);
|
||||
assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
|
||||
if( rc!=SQLITE_OK ){
|
||||
p->rc = rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Restart the backup process. This is called when the pager layer
|
||||
** detects that the database has been modified by an external database
|
||||
** connection. In this case there is no way of knowing which of the
|
||||
** pages that have been copied into the destination database are still
|
||||
** valid and which are not, so the entire process needs to be restarted.
|
||||
**
|
||||
** It is assumed that the mutex associated with the BtShared object
|
||||
** corresponding to the source database is held when this function is
|
||||
** called.
|
||||
*/
|
||||
void sqlite3BackupRestart(sqlite3_backup *pBackup){
|
||||
sqlite3_backup *p; /* Iterator variable */
|
||||
for(p=pBackup; p; p=p->pNext){
|
||||
assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
|
||||
p->iNext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Copy the complete content of pBtFrom into pBtTo. A transaction
|
||||
** must be active for both files.
|
||||
**
|
||||
** The size of file pTo may be reduced by this operation. If anything
|
||||
** goes wrong, the transaction on pTo is rolled back. If successful, the
|
||||
** transaction is committed before returning.
|
||||
*/
|
||||
int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
int rc;
|
||||
sqlite3_backup b;
|
||||
sqlite3BtreeEnter(pTo);
|
||||
sqlite3BtreeEnter(pFrom);
|
||||
|
||||
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
|
||||
** to 0. This is used by the implementations of sqlite3_backup_step()
|
||||
** and sqlite3_backup_finish() to detect that they are being called
|
||||
** from this function, not directly by the user.
|
||||
*/
|
||||
memset(&b, 0, sizeof(b));
|
||||
b.pSrcDb = pFrom->db;
|
||||
b.pSrc = pFrom;
|
||||
b.pDest = pTo;
|
||||
b.iNext = 1;
|
||||
|
||||
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
|
||||
** file. By passing this as the number of pages to copy to
|
||||
** sqlite3_backup_step(), we can guarantee that the copy finishes
|
||||
** within a single call (unless an error occurs). The assert() statement
|
||||
** checks this assumption - (p->rc) should be set to either SQLITE_DONE
|
||||
** or an error code.
|
||||
*/
|
||||
sqlite3_backup_step(&b, 0x7FFFFFFF);
|
||||
assert( b.rc!=SQLITE_OK );
|
||||
rc = sqlite3_backup_finish(&b);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTo->pBt->pageSizeFixed = 0;
|
||||
}
|
||||
|
||||
sqlite3BtreeLeave(pFrom);
|
||||
sqlite3BtreeLeave(pTo);
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VACUUM */
|
||||
+126
-57
@@ -12,12 +12,14 @@
|
||||
** This file implements an object that represents a fixed-length
|
||||
** bitmap. Bits are numbered starting with 1.
|
||||
**
|
||||
** A bitmap is used to record what pages a database file have been
|
||||
** journalled during a transaction. Usually only a few pages are
|
||||
** journalled. So the bitmap is usually sparse and has low cardinality.
|
||||
** A bitmap is used to record which pages of a database file have been
|
||||
** journalled during a transaction, or which pages have the "dont-write"
|
||||
** property. Usually only a few pages are meet either condition.
|
||||
** So the bitmap is usually sparse and has low cardinality.
|
||||
** But sometimes (for example when during a DROP of a large table) most
|
||||
** or all of the pages get journalled. In those cases, the bitmap becomes
|
||||
** dense. The algorithm needs to handle both cases well.
|
||||
** or all of the pages in a database can get journalled. In those cases,
|
||||
** the bitmap becomes dense with high cardinality. The algorithm needs
|
||||
** to handle both cases well.
|
||||
**
|
||||
** The size of the bitmap is fixed when the object is created.
|
||||
**
|
||||
@@ -32,21 +34,42 @@
|
||||
** start of a transaction, and is thus usually less than a few thousand,
|
||||
** but can be as large as 2 billion for a really big database.
|
||||
**
|
||||
** @(#) $Id: bitvec.c,v 1.6 2008/06/20 14:59:51 danielk1977 Exp $
|
||||
** @(#) $Id: bitvec.c,v 1.13 2009/01/20 17:06:27 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* Size of the Bitvec structure in bytes. */
|
||||
#define BITVEC_SZ 512
|
||||
|
||||
/* Round the union size down to the nearest pointer boundary, since that's how
|
||||
** it will be aligned within the Bitvec struct. */
|
||||
#define BITVEC_USIZE (((BITVEC_SZ-12)/sizeof(Bitvec*))*sizeof(Bitvec*))
|
||||
#define BITVEC_NCHAR BITVEC_USIZE
|
||||
#define BITVEC_NBIT (BITVEC_NCHAR*8)
|
||||
#define BITVEC_NINT (BITVEC_USIZE/4)
|
||||
#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
|
||||
|
||||
/* Type of the array "element" for the bitmap representation.
|
||||
** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
|
||||
** Setting this to the "natural word" size of your CPU may improve
|
||||
** performance. */
|
||||
#define BITVEC_TELEM u8
|
||||
/* Size, in bits, of the bitmap element. */
|
||||
#define BITVEC_SZELEM 8
|
||||
/* Number of elements in a bitmap array. */
|
||||
#define BITVEC_NELEM (BITVEC_USIZE/sizeof(BITVEC_TELEM))
|
||||
/* Number of bits in the bitmap array. */
|
||||
#define BITVEC_NBIT (BITVEC_NELEM*BITVEC_SZELEM)
|
||||
|
||||
/* Number of u32 values in hash table. */
|
||||
#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32))
|
||||
/* Maximum number of entries in hash table before
|
||||
** sub-dividing and re-hashing. */
|
||||
#define BITVEC_MXHASH (BITVEC_NINT/2)
|
||||
/* Hashing function for the aHash representation.
|
||||
** Empirical testing showed that the *37 multiplier
|
||||
** (an arbitrary prime)in the hash function provided
|
||||
** no fewer collisions than the no-op *1. */
|
||||
#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
|
||||
|
||||
#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
|
||||
|
||||
#define BITVEC_HASH(X) (((X)*37)%BITVEC_NINT)
|
||||
|
||||
/*
|
||||
** A bitmap is an instance of the following structure.
|
||||
@@ -70,11 +93,15 @@
|
||||
** to hold deal with values between 1 and iDivisor.
|
||||
*/
|
||||
struct Bitvec {
|
||||
u32 iSize; /* Maximum bit index */
|
||||
u32 nSet; /* Number of bits that are set */
|
||||
u32 iDivisor; /* Number of bits handled by each apSub[] entry */
|
||||
u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */
|
||||
u32 nSet; /* Number of bits that are set - only valid for aHash element */
|
||||
/* Max nSet is BITVEC_NINT. For BITVEC_SZ of 512, this would be 125. */
|
||||
u32 iDivisor; /* Number of bits handled by each apSub[] entry. */
|
||||
/* Should >=0 for apSub element. */
|
||||
/* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */
|
||||
/* For a BITVEC_SZ of 512, this would be 34,359,739. */
|
||||
union {
|
||||
u8 aBitmap[BITVEC_NCHAR]; /* Bitmap representation */
|
||||
BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */
|
||||
u32 aHash[BITVEC_NINT]; /* Hash table representation */
|
||||
Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */
|
||||
} u;
|
||||
@@ -103,16 +130,19 @@ Bitvec *sqlite3BitvecCreate(u32 iSize){
|
||||
int sqlite3BitvecTest(Bitvec *p, u32 i){
|
||||
if( p==0 ) return 0;
|
||||
if( i>p->iSize || i==0 ) return 0;
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
i--;
|
||||
return (p->u.aBitmap[i/8] & (1<<(i&7)))!=0;
|
||||
i--;
|
||||
while( p->iDivisor ){
|
||||
u32 bin = i/p->iDivisor;
|
||||
i = i%p->iDivisor;
|
||||
p = p->u.apSub[bin];
|
||||
if (!p) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if( p->iDivisor>0 ){
|
||||
u32 bin = (i-1)/p->iDivisor;
|
||||
i = (i-1)%p->iDivisor + 1;
|
||||
return sqlite3BitvecTest(p->u.apSub[bin], i);
|
||||
}else{
|
||||
u32 h = BITVEC_HASH(i);
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0;
|
||||
} else{
|
||||
u32 h = BITVEC_HASH(i++);
|
||||
while( p->u.aHash[h] ){
|
||||
if( p->u.aHash[h]==i ) return 1;
|
||||
h++;
|
||||
@@ -125,40 +155,62 @@ int sqlite3BitvecTest(Bitvec *p, u32 i){
|
||||
/*
|
||||
** Set the i-th bit. Return 0 on success and an error code if
|
||||
** anything goes wrong.
|
||||
**
|
||||
** This routine might cause sub-bitmaps to be allocated. Failing
|
||||
** to get the memory needed to hold the sub-bitmap is the only
|
||||
** that can go wrong with an insert, assuming p and i are valid.
|
||||
**
|
||||
** The calling function must ensure that p is a valid Bitvec object
|
||||
** and that the value for "i" is within range of the Bitvec object.
|
||||
** Otherwise the behavior is undefined.
|
||||
*/
|
||||
int sqlite3BitvecSet(Bitvec *p, u32 i){
|
||||
u32 h;
|
||||
assert( p!=0 );
|
||||
assert( i>0 );
|
||||
assert( i<=p->iSize );
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
i--;
|
||||
p->u.aBitmap[i/8] |= 1 << (i&7);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( p->iDivisor ){
|
||||
u32 bin = (i-1)/p->iDivisor;
|
||||
i = (i-1)%p->iDivisor + 1;
|
||||
i--;
|
||||
while((p->iSize > BITVEC_NBIT) && p->iDivisor) {
|
||||
u32 bin = i/p->iDivisor;
|
||||
i = i%p->iDivisor;
|
||||
if( p->u.apSub[bin]==0 ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
|
||||
sqlite3EndBenignMalloc();
|
||||
if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
return sqlite3BitvecSet(p->u.apSub[bin], i);
|
||||
p = p->u.apSub[bin];
|
||||
}
|
||||
h = BITVEC_HASH(i);
|
||||
while( p->u.aHash[h] ){
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
h = BITVEC_HASH(i++);
|
||||
/* if there wasn't a hash collision, and this doesn't */
|
||||
/* completely fill the hash, then just add it without */
|
||||
/* worring about sub-dividing and re-hashing. */
|
||||
if( !p->u.aHash[h] ){
|
||||
if (p->nSet<(BITVEC_NINT-1)) {
|
||||
goto bitvec_set_end;
|
||||
} else {
|
||||
goto bitvec_set_rehash;
|
||||
}
|
||||
}
|
||||
/* there was a collision, check to see if it's already */
|
||||
/* in hash, if not, try to find a spot for it */
|
||||
do {
|
||||
if( p->u.aHash[h]==i ) return SQLITE_OK;
|
||||
h++;
|
||||
if( h==BITVEC_NINT ) h = 0;
|
||||
}
|
||||
p->nSet++;
|
||||
if( h>=BITVEC_NINT ) h = 0;
|
||||
} while( p->u.aHash[h] );
|
||||
/* we didn't find it in the hash. h points to the first */
|
||||
/* available free spot. check to see if this is going to */
|
||||
/* make our hash too "full". */
|
||||
bitvec_set_rehash:
|
||||
if( p->nSet>=BITVEC_MXHASH ){
|
||||
int j, rc;
|
||||
unsigned int j;
|
||||
int rc;
|
||||
u32 aiValues[BITVEC_NINT];
|
||||
memcpy(aiValues, p->u.aHash, sizeof(aiValues));
|
||||
memset(p->u.apSub, 0, sizeof(p->u.apSub[0])*BITVEC_NPTR);
|
||||
memset(p->u.apSub, 0, sizeof(aiValues));
|
||||
p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR;
|
||||
rc = sqlite3BitvecSet(p, i);
|
||||
for(j=0; j<BITVEC_NINT; j++){
|
||||
@@ -166,35 +218,44 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
bitvec_set_end:
|
||||
p->nSet++;
|
||||
p->u.aHash[h] = i;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the i-th bit. Return 0 on success and an error code if
|
||||
** anything goes wrong.
|
||||
** Clear the i-th bit.
|
||||
*/
|
||||
void sqlite3BitvecClear(Bitvec *p, u32 i){
|
||||
assert( p!=0 );
|
||||
assert( i>0 );
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
i--;
|
||||
p->u.aBitmap[i/8] &= ~(1 << (i&7));
|
||||
}else if( p->iDivisor ){
|
||||
u32 bin = (i-1)/p->iDivisor;
|
||||
i = (i-1)%p->iDivisor + 1;
|
||||
if( p->u.apSub[bin] ){
|
||||
sqlite3BitvecClear(p->u.apSub[bin], i);
|
||||
i--;
|
||||
while( p->iDivisor ){
|
||||
u32 bin = i/p->iDivisor;
|
||||
i = i%p->iDivisor;
|
||||
p = p->u.apSub[bin];
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1)));
|
||||
}else{
|
||||
int j;
|
||||
unsigned int j;
|
||||
u32 aiValues[BITVEC_NINT];
|
||||
memcpy(aiValues, p->u.aHash, sizeof(aiValues));
|
||||
memset(p->u.aHash, 0, sizeof(p->u.aHash[0])*BITVEC_NINT);
|
||||
memset(p->u.aHash, 0, sizeof(aiValues));
|
||||
p->nSet = 0;
|
||||
for(j=0; j<BITVEC_NINT; j++){
|
||||
if( aiValues[j] && aiValues[j]!=i ){
|
||||
sqlite3BitvecSet(p, aiValues[j]);
|
||||
if( aiValues[j] && aiValues[j]!=(i+1) ){
|
||||
u32 h = BITVEC_HASH(aiValues[j]-1);
|
||||
p->nSet++;
|
||||
while( p->u.aHash[h] ){
|
||||
h++;
|
||||
if( h>=BITVEC_NINT ) h = 0;
|
||||
}
|
||||
p->u.aHash[h] = aiValues[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +267,7 @@ void sqlite3BitvecClear(Bitvec *p, u32 i){
|
||||
void sqlite3BitvecDestroy(Bitvec *p){
|
||||
if( p==0 ) return;
|
||||
if( p->iDivisor ){
|
||||
int i;
|
||||
unsigned int i;
|
||||
for(i=0; i<BITVEC_NPTR; i++){
|
||||
sqlite3BitvecDestroy(p->u.apSub[i]);
|
||||
}
|
||||
@@ -214,6 +275,14 @@ void sqlite3BitvecDestroy(Bitvec *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value of the iSize parameter specified when Bitvec *p
|
||||
** was created.
|
||||
*/
|
||||
u32 sqlite3BitvecSize(Bitvec *p){
|
||||
return p->iSize;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
/*
|
||||
** Let V[] be an array of unsigned characters sufficient to hold
|
||||
|
||||
+2
-4
@@ -10,7 +10,7 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: btmutex.c,v 1.10 2008/07/14 19:39:17 drh Exp $
|
||||
** $Id: btmutex.c,v 1.12 2008/11/17 19:18:55 danielk1977 Exp $
|
||||
**
|
||||
** This file contains code used to implement mutexes on Btree objects.
|
||||
** This code really belongs in btree.c. But btree.c is getting too
|
||||
@@ -61,7 +61,6 @@ void sqlite3BtreeEnter(Btree *p){
|
||||
p->wantToLock++;
|
||||
if( p->locked ) return;
|
||||
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
/* In most cases, we should be able to acquire the lock we
|
||||
** want without having to go throught the ascending lock
|
||||
** procedure that follows. Just be sure not to block.
|
||||
@@ -93,7 +92,6 @@ void sqlite3BtreeEnter(Btree *p){
|
||||
pLater->locked = 1;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_MUTEX_NOOP */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -250,7 +248,7 @@ void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
|
||||
}
|
||||
#endif
|
||||
assert( pArray->nMutex>=0 );
|
||||
assert( pArray->nMutex<sizeof(pArray->aBtree)/sizeof(pArray->aBtree[0])-1 );
|
||||
assert( pArray->nMutex<ArraySize(pArray->aBtree)-1 );
|
||||
pBt = pBtree->pBt;
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
assert( pArray->aBtree[i]!=pBtree );
|
||||
|
||||
+1278
-1206
File diff suppressed because it is too large
Load Diff
+12
-6
@@ -13,7 +13,7 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.102 2008/07/11 21:02:54 drh Exp $
|
||||
** @(#) $Id: btree.h,v 1.108 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@@ -98,12 +98,13 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInStmt(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
int sqlite3BtreeSchemaLocked(Btree *);
|
||||
int sqlite3BtreeLockTable(Btree *, int, u8);
|
||||
int sqlite3BtreeSavepoint(Btree *, int, int);
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
const char *sqlite3BtreeGetDirname(Btree *);
|
||||
const char *sqlite3BtreeGetJournalname(Btree *);
|
||||
int sqlite3BtreeCopyFile(Btree *, Btree *);
|
||||
|
||||
@@ -117,13 +118,11 @@ int sqlite3BtreeIncrVacuum(Btree *);
|
||||
#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */
|
||||
|
||||
int sqlite3BtreeDropTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTable(Btree*, int);
|
||||
int sqlite3BtreeClearTable(Btree*, int, int*);
|
||||
int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue);
|
||||
int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
|
||||
void sqlite3BtreeTripAllCursors(Btree*, int);
|
||||
|
||||
struct UnpackedRecord; /* Forward declaration. Definition in vdbeaux.c. */
|
||||
|
||||
int sqlite3BtreeCursor(
|
||||
Btree*, /* BTree containing table to open */
|
||||
int iTable, /* Index of root page */
|
||||
@@ -137,11 +136,17 @@ int sqlite3BtreeCloseCursor(BtCursor*);
|
||||
int sqlite3BtreeMoveto(
|
||||
BtCursor*,
|
||||
const void *pKey,
|
||||
struct UnpackedRecord *pUnKey,
|
||||
i64 nKey,
|
||||
int bias,
|
||||
int *pRes
|
||||
);
|
||||
int sqlite3BtreeMovetoUnpacked(
|
||||
BtCursor*,
|
||||
UnpackedRecord *pUnKey,
|
||||
i64 intKey,
|
||||
int bias,
|
||||
int *pRes
|
||||
);
|
||||
int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
@@ -166,6 +171,7 @@ struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
||||
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
void sqlite3BtreeCacheOverflow(BtCursor *);
|
||||
void sqlite3BtreeClearCursor(BtCursor *);
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
|
||||
|
||||
+30
-31
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btreeInt.h,v 1.30 2008/08/01 20:10:08 drh Exp $
|
||||
** $Id: btreeInt.h,v 1.42 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@@ -204,10 +204,6 @@
|
||||
** * zero or more pages numbers of leaves
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "pager.h"
|
||||
#include "btree.h"
|
||||
#include "os.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* Round up a number to the next larger multiple of 8. This is used
|
||||
** to force 8-byte alignment on 64-bit architectures.
|
||||
@@ -271,7 +267,6 @@ typedef struct BtLock BtLock;
|
||||
*/
|
||||
struct MemPage {
|
||||
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
|
||||
u8 idxShift; /* True if Cell indices have changed */
|
||||
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
|
||||
u8 intKey; /* True if intkey flag is set */
|
||||
u8 leaf; /* True if leaf flag is set */
|
||||
@@ -281,7 +276,6 @@ struct MemPage {
|
||||
u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
|
||||
u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
|
||||
u16 cellOffset; /* Index in aData of first cell pointer */
|
||||
u16 idxParent; /* Index in parent of this node */
|
||||
u16 nFree; /* Number of free bytes on the page */
|
||||
u16 nCell; /* Number of cells on this page, local and ovfl */
|
||||
u16 maskPage; /* Mask for page offset */
|
||||
@@ -293,7 +287,6 @@ struct MemPage {
|
||||
u8 *aData; /* Pointer to disk image of the page data */
|
||||
DbPage *pDbPage; /* Pager page handle */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
MemPage *pParent; /* The parent of this page. NULL for root */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -331,6 +324,7 @@ struct Btree {
|
||||
u8 sharable; /* True if we can share pBt with another db */
|
||||
u8 locked; /* True if db currently has pBt locked */
|
||||
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
|
||||
int nBackup; /* Number of backup operations reading this btree */
|
||||
Btree *pNext; /* List of other sharable Btrees from the same db */
|
||||
Btree *pPrev; /* Back pointer of the same list */
|
||||
};
|
||||
@@ -374,20 +368,19 @@ struct BtShared {
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
u8 autoVacuum; /* True if auto-vacuum is enabled */
|
||||
u8 incrVacuum; /* True if incr-vacuum is enabled */
|
||||
Pgno nTrunc; /* Non-zero if the db will be truncated (incr vacuum) */
|
||||
#endif
|
||||
u16 pageSize; /* Total number of bytes on a page */
|
||||
u16 usableSize; /* Number of usable bytes on each page */
|
||||
int maxLocal; /* Maximum local payload in non-LEAFDATA tables */
|
||||
int minLocal; /* Minimum local payload in non-LEAFDATA tables */
|
||||
int maxLeaf; /* Maximum local payload in a LEAFDATA table */
|
||||
int minLeaf; /* Minimum local payload in a LEAFDATA table */
|
||||
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
|
||||
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
|
||||
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
|
||||
u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
|
||||
u8 inTransaction; /* Transaction state */
|
||||
int nTransaction; /* Number of open transactions (read + write) */
|
||||
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 */
|
||||
BusyHandler busyHdr; /* The busy handler for this btree */
|
||||
Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int nRef; /* Number of references to this structure */
|
||||
BtShared *pNext; /* Next on a list of sharable BtShared structs */
|
||||
@@ -414,6 +407,17 @@ struct CellInfo {
|
||||
u16 nSize; /* Size of the cell content on the main b-tree page */
|
||||
};
|
||||
|
||||
/*
|
||||
** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than
|
||||
** this will be declared corrupt. This value is calculated based on a
|
||||
** maximum database size of 2^31 pages a minimum fanout of 2 for a
|
||||
** root-node and 3 for all other internal nodes.
|
||||
**
|
||||
** If a tree that appears to be taller than this is encountered, it is
|
||||
** assumed that the database is corrupt.
|
||||
*/
|
||||
#define BTCURSOR_MAX_DEPTH 20
|
||||
|
||||
/*
|
||||
** A cursor is a pointer to a particular entry within a particular
|
||||
** b-tree within a database file.
|
||||
@@ -434,8 +438,6 @@ struct BtCursor {
|
||||
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
|
||||
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
MemPage *pPage; /* Page that contains the entry */
|
||||
int idx; /* Index of the entry in pPage->aCell[] */
|
||||
CellInfo info; /* A parse of the cell we are pointing at */
|
||||
u8 wrFlag; /* True if writable */
|
||||
u8 atLast; /* Cursor pointing to the last entry */
|
||||
@@ -448,6 +450,12 @@ struct BtCursor {
|
||||
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
|
||||
Pgno *aOverflow; /* Cache of overflow page locations */
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
u8 pagesShuffled; /* True if Btree pages are rearranged by balance()*/
|
||||
#endif
|
||||
i16 iPage; /* Index of current page in apPage */
|
||||
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
||||
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -480,18 +488,10 @@ struct BtCursor {
|
||||
#define CURSOR_REQUIRESEEK 2
|
||||
#define CURSOR_FAULT 3
|
||||
|
||||
/* The database page the PENDING_BYTE occupies. This page is never used.
|
||||
** TODO: This macro is very similary to PAGER_MJ_PGNO() in pager.c. They
|
||||
** should possibly be consolidated (presumably in pager.h).
|
||||
**
|
||||
** If disk I/O is omitted (meaning that the database is stored purely
|
||||
** in memory) then there is no pending byte.
|
||||
/*
|
||||
** The database page the PENDING_BYTE occupies. This page is never used.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_DISKIO
|
||||
# define PENDING_BYTE_PAGE(pBt) 0x7fffffff
|
||||
#else
|
||||
# define PENDING_BYTE_PAGE(pBt) ((PENDING_BYTE/(pBt)->pageSize)+1)
|
||||
#endif
|
||||
# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
|
||||
|
||||
/*
|
||||
** A linked list of the following structures is stored at BtShared.pLock.
|
||||
@@ -597,7 +597,7 @@ typedef struct IntegrityCk IntegrityCk;
|
||||
struct IntegrityCk {
|
||||
BtShared *pBt; /* The tree being checked out */
|
||||
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
|
||||
int nPage; /* Number of pages in the database */
|
||||
Pgno nPage; /* Number of pages in the database */
|
||||
int *anRef; /* Number of times each page is referenced */
|
||||
int mxErr; /* Stop accumulating errors when this reaches zero */
|
||||
int nErr; /* Number of messages written to zErrMsg so far */
|
||||
@@ -609,7 +609,7 @@ struct IntegrityCk {
|
||||
** Read or write a two- and four-byte big-endian integer values.
|
||||
*/
|
||||
#define get2byte(x) ((x)[0]<<8 | (x)[1])
|
||||
#define put2byte(p,v) ((p)[0] = (v)>>8, (p)[1] = (v))
|
||||
#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
|
||||
#define get4byte sqlite3Get4byte
|
||||
#define put4byte sqlite3Put4byte
|
||||
|
||||
@@ -617,11 +617,10 @@ struct IntegrityCk {
|
||||
** Internal routines that should be accessed by the btree layer only.
|
||||
*/
|
||||
int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int);
|
||||
int sqlite3BtreeInitPage(MemPage *pPage, MemPage *pParent);
|
||||
int sqlite3BtreeInitPage(MemPage *pPage);
|
||||
void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*);
|
||||
void sqlite3BtreeParseCell(MemPage*, int, CellInfo*);
|
||||
int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur);
|
||||
void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur);
|
||||
void sqlite3BtreeReleaseTempCursor(BtCursor *pCur);
|
||||
int sqlite3BtreeIsRootPage(MemPage *pPage);
|
||||
void sqlite3BtreeMoveToParent(BtCursor *pCur);
|
||||
|
||||
+222
-110
@@ -22,17 +22,16 @@
|
||||
** COMMIT
|
||||
** ROLLBACK
|
||||
**
|
||||
** $Id: build.c,v 1.493 2008/08/04 04:39:49 danielk1977 Exp $
|
||||
** $Id: build.c,v 1.518 2009/02/13 03:43:32 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** This routine is called when a new SQL statement is beginning to
|
||||
** be parsed. Initialize the pParse structure as needed.
|
||||
*/
|
||||
void sqlite3BeginParse(Parse *pParse, int explainFlag){
|
||||
pParse->explain = explainFlag;
|
||||
pParse->explain = (u8)explainFlag;
|
||||
pParse->nVar = 0;
|
||||
}
|
||||
|
||||
@@ -187,7 +186,8 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
*/
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(v, 0);
|
||||
if( pOp && pOp->opcode==OP_Trace ){
|
||||
sqlite3VdbeChangeP4(v, 0, pParse->zSql, pParse->zTail-pParse->zSql);
|
||||
sqlite3VdbeChangeP4(v, 0, pParse->zSql,
|
||||
(int)(pParse->zTail - pParse->zSql));
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TRACE */
|
||||
@@ -365,11 +365,12 @@ static void freeIndex(Index *p){
|
||||
** it is not unlinked from the Table that it indexes.
|
||||
** Unlinking from the Table must be done by the calling function.
|
||||
*/
|
||||
static void sqliteDeleteIndex(Index *p){
|
||||
static void sqlite3DeleteIndex(Index *p){
|
||||
Index *pOld;
|
||||
const char *zName = p->zName;
|
||||
|
||||
pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen(zName)+1, 0);
|
||||
pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName,
|
||||
sqlite3Strlen30(zName)+1, 0);
|
||||
assert( pOld==0 || pOld==p );
|
||||
freeIndex(p);
|
||||
}
|
||||
@@ -409,8 +410,8 @@ 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>=2 then reset the internal schema for only the
|
||||
** If iDb==0 then reset the internal schema tables for all database
|
||||
** files. If iDb>=1 then reset the internal schema for only the
|
||||
** single file indicated.
|
||||
*/
|
||||
void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
|
||||
@@ -524,7 +525,7 @@ void sqlite3DeleteTable(Table *pTable){
|
||||
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
|
||||
pNext = pIndex->pNext;
|
||||
assert( pIndex->pSchema==pTable->pSchema );
|
||||
sqliteDeleteIndex(pIndex);
|
||||
sqlite3DeleteIndex(pIndex);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
@@ -534,7 +535,7 @@ void sqlite3DeleteTable(Table *pTable){
|
||||
for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
|
||||
pNextFKey = pFKey->pNextFrom;
|
||||
assert( sqlite3HashFind(&pTable->pSchema->aFKey,
|
||||
pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
|
||||
pFKey->zTo, sqlite3Strlen30(pFKey->zTo)+1)!=pFKey );
|
||||
sqlite3DbFree(db, pFKey);
|
||||
}
|
||||
#endif
|
||||
@@ -565,11 +566,12 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( zTabName && zTabName[0] );
|
||||
pDb = &db->aDb[iDb];
|
||||
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, strlen(zTabName)+1,0);
|
||||
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName,
|
||||
sqlite3Strlen30(zTabName)+1,0);
|
||||
if( p ){
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
|
||||
int nTo = strlen(pF1->zTo) + 1;
|
||||
int nTo = sqlite3Strlen30(pF1->zTo) + 1;
|
||||
pF2 = sqlite3HashFind(&pDb->pSchema->aFKey, pF1->zTo, nTo);
|
||||
if( pF2==pF1 ){
|
||||
sqlite3HashInsert(&pDb->pSchema->aFKey, pF1->zTo, nTo, pF1->pNextTo);
|
||||
@@ -618,6 +620,27 @@ void sqlite3OpenMasterTable(Parse *p, int iDb){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zName points to a nul-terminated buffer containing the name
|
||||
** of a database ("main", "temp" or the name of an attached db). This
|
||||
** function returns the index of the named database in db->aDb[], or
|
||||
** -1 if the named db cannot be found.
|
||||
*/
|
||||
int sqlite3FindDbName(sqlite3 *db, const char *zName){
|
||||
int i = -1; /* Database number */
|
||||
if( zName ){
|
||||
Db *pDb;
|
||||
int n = sqlite3Strlen30(zName);
|
||||
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
|
||||
if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) &&
|
||||
0==sqlite3StrICmp(pDb->zName, zName) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** The token *pName contains the name of a database (either "main" or
|
||||
** "temp" or the name of an attached db). This routine returns the
|
||||
@@ -625,22 +648,11 @@ void sqlite3OpenMasterTable(Parse *p, int iDb){
|
||||
** does not exist.
|
||||
*/
|
||||
int sqlite3FindDb(sqlite3 *db, Token *pName){
|
||||
int i = -1; /* Database number */
|
||||
int n; /* Number of characters in the name */
|
||||
Db *pDb; /* A database whose name space is being searched */
|
||||
char *zName; /* Name we are searching for */
|
||||
|
||||
int i; /* Database number */
|
||||
char *zName; /* Name we are searching for */
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
if( zName ){
|
||||
n = strlen(zName);
|
||||
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
|
||||
if( (!OMIT_TEMPDB || i!=1 ) && n==strlen(pDb->zName) &&
|
||||
0==sqlite3StrICmp(pDb->zName, zName) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(db, zName);
|
||||
}
|
||||
i = sqlite3FindDbName(db, zName);
|
||||
sqlite3DbFree(db, zName);
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -670,7 +682,11 @@ int sqlite3TwoPartName(
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
if( pName2 && pName2->n>0 ){
|
||||
assert( !db->init.busy );
|
||||
if( db->init.busy ) {
|
||||
sqlite3ErrorMsg(pParse, "corrupt database");
|
||||
pParse->nErr++;
|
||||
return -1;
|
||||
}
|
||||
*pUnqual = pName2;
|
||||
iDb = sqlite3FindDb(db, pName1);
|
||||
if( iDb<0 ){
|
||||
@@ -948,7 +964,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
z = sqlite3NameFromToken(pParse->db, pName);
|
||||
z = sqlite3NameFromToken(db, pName);
|
||||
if( z==0 ) return;
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( STRICMP(z, p->aCol[i].zName) ){
|
||||
@@ -959,7 +975,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
|
||||
}
|
||||
if( (p->nCol & 0x7)==0 ){
|
||||
Column *aNew;
|
||||
aNew = sqlite3DbRealloc(pParse->db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
|
||||
aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
|
||||
if( aNew==0 ){
|
||||
sqlite3DbFree(db, z);
|
||||
return;
|
||||
@@ -989,7 +1005,7 @@ void sqlite3AddNotNull(Parse *pParse, int onError){
|
||||
int i;
|
||||
if( (p = pParse->pNewTable)==0 ) return;
|
||||
i = p->nCol-1;
|
||||
if( i>=0 ) p->aCol[i].notNull = onError;
|
||||
if( i>=0 ) p->aCol[i].notNull = (u8)onError;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1140,12 +1156,12 @@ void sqlite3AddPrimaryKey(
|
||||
char *zType = 0;
|
||||
int iCol = -1, i;
|
||||
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
|
||||
if( pTab->hasPrimKey ){
|
||||
if( pTab->tabFlags & TF_HasPrimaryKey ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"table \"%s\" has more than one primary key", pTab->zName);
|
||||
goto primary_key_exit;
|
||||
}
|
||||
pTab->hasPrimKey = 1;
|
||||
pTab->tabFlags |= TF_HasPrimaryKey;
|
||||
if( pList==0 ){
|
||||
iCol = pTab->nCol - 1;
|
||||
pTab->aCol[iCol].isPrimKey = 1;
|
||||
@@ -1168,8 +1184,9 @@ void sqlite3AddPrimaryKey(
|
||||
if( zType && sqlite3StrICmp(zType, "INTEGER")==0
|
||||
&& sortOrder==SQLITE_SO_ASC ){
|
||||
pTab->iPKey = iCol;
|
||||
pTab->keyConf = onError;
|
||||
pTab->autoInc = autoInc;
|
||||
pTab->keyConf = (u8)onError;
|
||||
assert( autoInc==0 || autoInc==1 );
|
||||
pTab->tabFlags |= autoInc*TF_Autoincrement;
|
||||
}else if( autoInc ){
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
|
||||
@@ -1331,9 +1348,9 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
|
||||
int i, j, needQuote;
|
||||
i = *pIdx;
|
||||
for(j=0; zIdent[j]; j++){
|
||||
if( !isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
|
||||
if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
|
||||
}
|
||||
needQuote = zIdent[j]!=0 || isdigit(zIdent[0])
|
||||
needQuote = zIdent[j]!=0 || sqlite3Isdigit(zIdent[0])
|
||||
|| sqlite3KeywordCode(zIdent, j)!=TK_ID;
|
||||
if( needQuote ) z[i++] = '"';
|
||||
for(j=0; zIdent[j]; j++){
|
||||
@@ -1350,7 +1367,7 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
|
||||
** table. Memory to hold the text of the statement is obtained
|
||||
** from sqliteMalloc() and must be freed by the calling function.
|
||||
*/
|
||||
static char *createTableStmt(sqlite3 *db, Table *p, int isTemp){
|
||||
static char *createTableStmt(sqlite3 *db, Table *p){
|
||||
int i, k, n;
|
||||
char *zStmt;
|
||||
char *zSep, *zSep2, *zEnd, *z;
|
||||
@@ -1360,7 +1377,7 @@ static char *createTableStmt(sqlite3 *db, Table *p, int isTemp){
|
||||
n += identLength(pCol->zName);
|
||||
z = pCol->zType;
|
||||
if( z ){
|
||||
n += (strlen(z) + 1);
|
||||
n += (sqlite3Strlen30(z) + 1);
|
||||
}
|
||||
}
|
||||
n += identLength(p->zName);
|
||||
@@ -1379,21 +1396,20 @@ static char *createTableStmt(sqlite3 *db, Table *p, int isTemp){
|
||||
db->mallocFailed = 1;
|
||||
return 0;
|
||||
}
|
||||
sqlite3_snprintf(n, zStmt,
|
||||
!OMIT_TEMPDB&&isTemp ? "CREATE TEMP TABLE ":"CREATE TABLE ");
|
||||
k = strlen(zStmt);
|
||||
sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
|
||||
k = sqlite3Strlen30(zStmt);
|
||||
identPut(zStmt, &k, p->zName);
|
||||
zStmt[k++] = '(';
|
||||
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
|
||||
sqlite3_snprintf(n-k, &zStmt[k], zSep);
|
||||
k += strlen(&zStmt[k]);
|
||||
k += sqlite3Strlen30(&zStmt[k]);
|
||||
zSep = zSep2;
|
||||
identPut(zStmt, &k, pCol->zName);
|
||||
if( (z = pCol->zType)!=0 ){
|
||||
zStmt[k++] = ' ';
|
||||
assert( strlen(z)+k+1<=n );
|
||||
assert( (int)(sqlite3Strlen30(z)+k+1)<=n );
|
||||
sqlite3_snprintf(n-k, &zStmt[k], "%s", z);
|
||||
k += strlen(z);
|
||||
k += sqlite3Strlen30(z);
|
||||
}
|
||||
}
|
||||
sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd);
|
||||
@@ -1456,7 +1472,7 @@ void sqlite3EndTable(
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = &sSrc;
|
||||
sNC.isCheck = 1;
|
||||
if( sqlite3ExprResolveNames(&sNC, p->pCheck) ){
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pCheck) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1529,10 +1545,10 @@ void sqlite3EndTable(
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
pParse->nTab = 2;
|
||||
sqlite3SelectDestInit(&dest, SRT_Table, 1);
|
||||
sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
|
||||
sqlite3Select(pParse, pSelect, &dest);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, 1);
|
||||
if( pParse->nErr==0 ){
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
|
||||
if( pSelTab==0 ) return;
|
||||
assert( p->aCol==0 );
|
||||
p->nCol = pSelTab->nCol;
|
||||
@@ -1545,9 +1561,9 @@ void sqlite3EndTable(
|
||||
|
||||
/* Compute the complete text of the CREATE statement */
|
||||
if( pSelect ){
|
||||
zStmt = createTableStmt(db, p, p->pSchema==db->aDb[1].pSchema);
|
||||
zStmt = createTableStmt(db, p);
|
||||
}else{
|
||||
n = pEnd->z - pParse->sNameToken.z + 1;
|
||||
n = (int)(pEnd->z - pParse->sNameToken.z) + 1;
|
||||
zStmt = sqlite3MPrintf(db,
|
||||
"CREATE %s %.*s", zType2, n, pParse->sNameToken.z
|
||||
);
|
||||
@@ -1578,7 +1594,7 @@ void sqlite3EndTable(
|
||||
/* Check to see if we need to create an sqlite_sequence table for
|
||||
** keeping track of autoincrement keys.
|
||||
*/
|
||||
if( p->autoInc ){
|
||||
if( p->tabFlags & TF_Autoincrement ){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
if( pDb->pSchema->pSeqTab==0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
@@ -1601,7 +1617,8 @@ void sqlite3EndTable(
|
||||
Table *pOld;
|
||||
FKey *pFKey;
|
||||
Schema *pSchema = p->pSchema;
|
||||
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, strlen(p->zName)+1,p);
|
||||
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName,
|
||||
sqlite3Strlen30(p->zName)+1,p);
|
||||
if( pOld ){
|
||||
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
|
||||
db->mallocFailed = 1;
|
||||
@@ -1610,7 +1627,7 @@ void sqlite3EndTable(
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
|
||||
void *data;
|
||||
int nTo = strlen(pFKey->zTo) + 1;
|
||||
int nTo = sqlite3Strlen30(pFKey->zTo) + 1;
|
||||
pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo);
|
||||
data = sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
|
||||
if( data==(void *)pFKey ){
|
||||
@@ -1630,7 +1647,7 @@ void sqlite3EndTable(
|
||||
if( pCons->z==0 ){
|
||||
pCons = pEnd;
|
||||
}
|
||||
nName = (const char *)pCons->z - zName;
|
||||
nName = (int)((const char *)pCons->z - zName);
|
||||
p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName);
|
||||
}
|
||||
#endif
|
||||
@@ -1701,9 +1718,9 @@ void sqlite3CreateView(
|
||||
sEnd.z += sEnd.n;
|
||||
}
|
||||
sEnd.n = 0;
|
||||
n = sEnd.z - pBegin->z;
|
||||
n = (int)(sEnd.z - pBegin->z);
|
||||
z = (const unsigned char*)pBegin->z;
|
||||
while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; }
|
||||
while( n>0 && (z[n-1]==';' || sqlite3Isspace(z[n-1])) ){ n--; }
|
||||
sEnd.z = &z[n-1];
|
||||
sEnd.n = 1;
|
||||
|
||||
@@ -1774,10 +1791,10 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
db->xAuth = xAuth;
|
||||
#else
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
#endif
|
||||
pParse->nTab = n;
|
||||
if( pSelTab ){
|
||||
@@ -2011,7 +2028,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( pTab->readOnly || pTab==db->aDb[iDb].pSchema->pSeqTab ){
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
|
||||
goto exit_drop_table;
|
||||
}
|
||||
@@ -2041,7 +2058,6 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( v ){
|
||||
sqlite3VdbeAddOp0(v, OP_VBegin);
|
||||
}
|
||||
@@ -2066,7 +2082,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
** at the btree level, in case the sqlite_sequence table needs to
|
||||
** move as a result of the drop (can happen in auto-vacuum mode).
|
||||
*/
|
||||
if( pTab->autoInc ){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %s.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
@@ -2136,6 +2152,7 @@ void sqlite3CreateForeignKey(
|
||||
ExprList *pToCol, /* Columns in the other table */
|
||||
int flags /* Conflict resolution algorithms. */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
FKey *pFKey = 0;
|
||||
Table *p = pParse->pNewTable;
|
||||
@@ -2143,10 +2160,8 @@ void sqlite3CreateForeignKey(
|
||||
int i;
|
||||
int nCol;
|
||||
char *z;
|
||||
sqlite3 *db;
|
||||
|
||||
assert( pTo!=0 );
|
||||
db = pParse->db;
|
||||
if( p==0 || pParse->nErr || IN_DECLARE_VTAB ) goto fk_end;
|
||||
if( pFromCol==0 ){
|
||||
int iCol = p->nCol-1;
|
||||
@@ -2169,7 +2184,7 @@ void sqlite3CreateForeignKey(
|
||||
nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1;
|
||||
if( pToCol ){
|
||||
for(i=0; i<pToCol->nExpr; i++){
|
||||
nByte += strlen(pToCol->a[i].zName) + 1;
|
||||
nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
|
||||
}
|
||||
}
|
||||
pFKey = sqlite3DbMallocZero(db, nByte );
|
||||
@@ -2208,7 +2223,7 @@ void sqlite3CreateForeignKey(
|
||||
}
|
||||
if( pToCol ){
|
||||
for(i=0; i<nCol; i++){
|
||||
int n = strlen(pToCol->a[i].zName);
|
||||
int n = sqlite3Strlen30(pToCol->a[i].zName);
|
||||
pFKey->aCol[i].zCol = z;
|
||||
memcpy(z, pToCol->a[i].zName, n);
|
||||
z[n] = 0;
|
||||
@@ -2216,9 +2231,9 @@ void sqlite3CreateForeignKey(
|
||||
}
|
||||
}
|
||||
pFKey->isDeferred = 0;
|
||||
pFKey->deleteConf = flags & 0xff;
|
||||
pFKey->updateConf = (flags >> 8 ) & 0xff;
|
||||
pFKey->insertConf = (flags >> 16 ) & 0xff;
|
||||
pFKey->deleteConf = (u8)(flags & 0xff);
|
||||
pFKey->updateConf = (u8)((flags >> 8 ) & 0xff);
|
||||
pFKey->insertConf = (u8)((flags >> 16 ) & 0xff);
|
||||
|
||||
/* Link the foreign key to the table as the last step.
|
||||
*/
|
||||
@@ -2244,7 +2259,8 @@ void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){
|
||||
Table *pTab;
|
||||
FKey *pFKey;
|
||||
if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return;
|
||||
pFKey->isDeferred = isDeferred;
|
||||
assert( isDeferred==0 || isDeferred==1 );
|
||||
pFKey->isDeferred = (u8)isDeferred;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2401,7 +2417,7 @@ void sqlite3CreateIndex(
|
||||
}
|
||||
pTab = sqlite3LocateTable(pParse, 0, pTblName->a[0].zName,
|
||||
pTblName->a[0].zDatabase);
|
||||
if( !pTab ) goto exit_create_index;
|
||||
if( !pTab || db->mallocFailed ) goto exit_create_index;
|
||||
assert( db->aDb[iDb].pSchema==pTab->pSchema );
|
||||
}else{
|
||||
assert( pName==0 );
|
||||
@@ -2412,7 +2428,8 @@ void sqlite3CreateIndex(
|
||||
pDb = &db->aDb[iDb];
|
||||
|
||||
if( pTab==0 || pParse->nErr ) goto exit_create_index;
|
||||
if( pTab->readOnly ){
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
|
||||
&& memcmp(&pTab->zName[7],"altertab_",9)!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
@@ -2494,26 +2511,27 @@ void sqlite3CreateIndex(
|
||||
*/
|
||||
if( pList==0 ){
|
||||
nullId.z = (u8*)pTab->aCol[pTab->nCol-1].zName;
|
||||
nullId.n = strlen((char*)nullId.z);
|
||||
nullId.n = sqlite3Strlen30((char*)nullId.z);
|
||||
pList = sqlite3ExprListAppend(pParse, 0, 0, &nullId);
|
||||
if( pList==0 ) goto exit_create_index;
|
||||
pList->a[0].sortOrder = sortOrder;
|
||||
pList->a[0].sortOrder = (u8)sortOrder;
|
||||
}
|
||||
|
||||
/* Figure out how many bytes of space are required to store explicitly
|
||||
** specified collation sequence names.
|
||||
*/
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
Expr *pExpr = pList->a[i].pExpr;
|
||||
if( pExpr ){
|
||||
nExtra += (1 + strlen(pExpr->pColl->zName));
|
||||
Expr *pExpr;
|
||||
CollSeq *pColl;
|
||||
if( (pExpr = pList->a[i].pExpr)!=0 && (pColl = pExpr->pColl)!=0 ){
|
||||
nExtra += (1 + sqlite3Strlen30(pColl->zName));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate the index structure.
|
||||
*/
|
||||
nName = strlen(zName);
|
||||
nName = sqlite3Strlen30(zName);
|
||||
nCol = pList->nExpr;
|
||||
pIndex = sqlite3DbMallocZero(db,
|
||||
sizeof(Index) + /* Index structure */
|
||||
@@ -2536,8 +2554,8 @@ void sqlite3CreateIndex(
|
||||
memcpy(pIndex->zName, zName, nName+1);
|
||||
pIndex->pTable = pTab;
|
||||
pIndex->nColumn = pList->nExpr;
|
||||
pIndex->onError = onError;
|
||||
pIndex->autoIndex = pName==0;
|
||||
pIndex->onError = (u8)onError;
|
||||
pIndex->autoIndex = (u8)(pName==0);
|
||||
pIndex->pSchema = db->aDb[iDb].pSchema;
|
||||
|
||||
/* Check to see if we should honor DESC requests on index columns
|
||||
@@ -2573,11 +2591,11 @@ void sqlite3CreateIndex(
|
||||
** break backwards compatibility - it needs to be a warning.
|
||||
*/
|
||||
pIndex->aiColumn[i] = j;
|
||||
if( pListItem->pExpr ){
|
||||
if( pListItem->pExpr && pListItem->pExpr->pColl ){
|
||||
assert( pListItem->pExpr->pColl );
|
||||
zColl = zExtra;
|
||||
sqlite3_snprintf(nExtra, zExtra, "%s", pListItem->pExpr->pColl->zName);
|
||||
zExtra += (strlen(zColl) + 1);
|
||||
zExtra += (sqlite3Strlen30(zColl) + 1);
|
||||
}else{
|
||||
zColl = pTab->aCol[j].zColl;
|
||||
if( !zColl ){
|
||||
@@ -2589,7 +2607,7 @@ void sqlite3CreateIndex(
|
||||
}
|
||||
pIndex->azColl[i] = zColl;
|
||||
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
|
||||
pIndex->aSortOrder[i] = requestedSortOrder;
|
||||
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
|
||||
}
|
||||
sqlite3DefaultRowEst(pIndex);
|
||||
|
||||
@@ -2650,7 +2668,8 @@ void sqlite3CreateIndex(
|
||||
if( db->init.busy ){
|
||||
Index *p;
|
||||
p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
|
||||
pIndex->zName, strlen(pIndex->zName)+1, pIndex);
|
||||
pIndex->zName, sqlite3Strlen30(pIndex->zName)+1,
|
||||
pIndex);
|
||||
if( p ){
|
||||
assert( p==pIndex ); /* Malloc must have failed */
|
||||
db->mallocFailed = 1;
|
||||
@@ -2924,7 +2943,7 @@ void *sqlite3ArrayAllocate(
|
||||
*pIdx = -1;
|
||||
return pArray;
|
||||
}
|
||||
*pnAlloc = newSize;
|
||||
*pnAlloc = sqlite3DbMallocSize(db, pNew)/szEntry;
|
||||
pArray = pNew;
|
||||
}
|
||||
z = (char*)pArray;
|
||||
@@ -2990,11 +3009,83 @@ int sqlite3IdListIndex(IdList *pList, const char *zName){
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Expand the space allocated for the given SrcList object by
|
||||
** creating nExtra new slots beginning at iStart. iStart is zero based.
|
||||
** New slots are zeroed.
|
||||
**
|
||||
** For example, suppose a SrcList initially contains two entries: A,B.
|
||||
** To append 3 new entries onto the end, do this:
|
||||
**
|
||||
** sqlite3SrcListEnlarge(db, pSrclist, 3, 2);
|
||||
**
|
||||
** After the call above it would contain: A, B, nil, nil, nil.
|
||||
** If the iStart argument had been 1 instead of 2, then the result
|
||||
** would have been: A, nil, nil, nil, B. To prepend the new slots,
|
||||
** the iStart value would be 0. The result then would
|
||||
** be: nil, nil, nil, A, B.
|
||||
**
|
||||
** If a memory allocation fails the SrcList is unchanged. The
|
||||
** db->mallocFailed flag will be set to true.
|
||||
*/
|
||||
SrcList *sqlite3SrcListEnlarge(
|
||||
sqlite3 *db, /* Database connection to notify of OOM errors */
|
||||
SrcList *pSrc, /* The SrcList to be enlarged */
|
||||
int nExtra, /* Number of new slots to add to pSrc->a[] */
|
||||
int iStart /* Index in pSrc->a[] of first new slot */
|
||||
){
|
||||
int i;
|
||||
|
||||
/* Sanity checking on calling parameters */
|
||||
assert( iStart>=0 );
|
||||
assert( nExtra>=1 );
|
||||
if( pSrc==0 || iStart>pSrc->nSrc ){
|
||||
assert( db->mallocFailed );
|
||||
return pSrc;
|
||||
}
|
||||
|
||||
/* Allocate additional space if needed */
|
||||
if( pSrc->nSrc+nExtra>pSrc->nAlloc ){
|
||||
SrcList *pNew;
|
||||
int nAlloc = pSrc->nSrc+nExtra;
|
||||
int nGot;
|
||||
pNew = sqlite3DbRealloc(db, pSrc,
|
||||
sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
|
||||
if( pNew==0 ){
|
||||
assert( db->mallocFailed );
|
||||
return pSrc;
|
||||
}
|
||||
pSrc = pNew;
|
||||
nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
|
||||
pSrc->nAlloc = (u16)nGot;
|
||||
}
|
||||
|
||||
/* Move existing slots that come after the newly inserted slots
|
||||
** out of the way */
|
||||
for(i=pSrc->nSrc-1; i>=iStart; i--){
|
||||
pSrc->a[i+nExtra] = pSrc->a[i];
|
||||
}
|
||||
pSrc->nSrc += (i16)nExtra;
|
||||
|
||||
/* Zero the newly allocated slots */
|
||||
memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra);
|
||||
for(i=iStart; i<iStart+nExtra; i++){
|
||||
pSrc->a[i].iCursor = -1;
|
||||
}
|
||||
|
||||
/* Return a pointer to the enlarged SrcList */
|
||||
return pSrc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Append a new table name to the given SrcList. Create a new SrcList if
|
||||
** need be. A new entry is created in the SrcList even if pToken is NULL.
|
||||
**
|
||||
** A new SrcList is returned, or NULL if malloc() fails.
|
||||
** A SrcList is returned, or NULL if there is an OOM error. The returned
|
||||
** SrcList might be the same as the SrcList that was input or it might be
|
||||
** a new one. If an OOM error does occurs, then the prior value of pList
|
||||
** that is input to this routine is automatically freed.
|
||||
**
|
||||
** If pDatabase is not null, it means that the table has an optional
|
||||
** database name prefix. Like this: "database.table". The pDatabase
|
||||
@@ -3027,19 +3118,12 @@ SrcList *sqlite3SrcListAppend(
|
||||
if( pList==0 ) return 0;
|
||||
pList->nAlloc = 1;
|
||||
}
|
||||
if( pList->nSrc>=pList->nAlloc ){
|
||||
SrcList *pNew;
|
||||
pList->nAlloc *= 2;
|
||||
pNew = sqlite3DbRealloc(db, pList,
|
||||
sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) );
|
||||
if( pNew==0 ){
|
||||
sqlite3SrcListDelete(db, pList);
|
||||
return 0;
|
||||
}
|
||||
pList = pNew;
|
||||
pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc);
|
||||
if( db->mallocFailed ){
|
||||
sqlite3SrcListDelete(db, pList);
|
||||
return 0;
|
||||
}
|
||||
pItem = &pList->a[pList->nSrc];
|
||||
memset(pItem, 0, sizeof(pList->a[0]));
|
||||
pItem = &pList->a[pList->nSrc-1];
|
||||
if( pDatabase && pDatabase->z==0 ){
|
||||
pDatabase = 0;
|
||||
}
|
||||
@@ -3050,14 +3134,11 @@ SrcList *sqlite3SrcListAppend(
|
||||
}
|
||||
pItem->zName = sqlite3NameFromToken(db, pTable);
|
||||
pItem->zDatabase = sqlite3NameFromToken(db, pDatabase);
|
||||
pItem->iCursor = -1;
|
||||
pItem->isPopulated = 0;
|
||||
pList->nSrc++;
|
||||
return pList;
|
||||
}
|
||||
|
||||
/*
|
||||
** Assign cursors to all tables in a SrcList
|
||||
** Assign VdbeCursor index numbers to all tables in a SrcList
|
||||
*/
|
||||
void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
|
||||
int i;
|
||||
@@ -3085,6 +3166,7 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
|
||||
sqlite3DbFree(db, pItem->zDatabase);
|
||||
sqlite3DbFree(db, pItem->zName);
|
||||
sqlite3DbFree(db, pItem->zAlias);
|
||||
sqlite3DbFree(db, pItem->zIndex);
|
||||
sqlite3DeleteTable(pItem->pTab);
|
||||
sqlite3SelectDelete(db, pItem->pSelect);
|
||||
sqlite3ExprDelete(db, pItem->pOn);
|
||||
@@ -3138,6 +3220,24 @@ SrcList *sqlite3SrcListAppendFromTerm(
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an INDEXED BY or NOT INDEXED clause to the most recently added
|
||||
** element of the source-list passed as the second argument.
|
||||
*/
|
||||
void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
|
||||
if( pIndexedBy && p && p->nSrc>0 ){
|
||||
struct SrcList_item *pItem = &p->a[p->nSrc-1];
|
||||
assert( pItem->notIndexed==0 && pItem->zIndex==0 );
|
||||
if( pIndexedBy->n==1 && !pIndexedBy->z ){
|
||||
/* A "NOT INDEXED" clause was supplied. See parse.y
|
||||
** construct "indexed_opt" for details. */
|
||||
pItem->notIndexed = 1;
|
||||
}else{
|
||||
pItem->zIndex = sqlite3NameFromToken(pParse->db, pIndexedBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** When building up a FROM clause in the parser, the join operator
|
||||
** is initially attached to the left operand. But the code generator
|
||||
@@ -3220,6 +3320,26 @@ void sqlite3RollbackTransaction(Parse *pParse){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the parser when it parses a command to create,
|
||||
** release or rollback an SQL savepoint.
|
||||
*/
|
||||
void sqlite3Savepoint(Parse *pParse, int op, Token *pName){
|
||||
char *zName = sqlite3NameFromToken(pParse->db, pName);
|
||||
if( zName ){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
static const char *az[] = { "BEGIN", "RELEASE", "ROLLBACK" };
|
||||
assert( !SAVEPOINT_BEGIN && SAVEPOINT_RELEASE==1 && SAVEPOINT_ROLLBACK==2 );
|
||||
#endif
|
||||
if( !v || sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0) ){
|
||||
sqlite3DbFree(pParse->db, zName);
|
||||
return;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure the TEMP database is open and available for use. Return
|
||||
** the number of errors. Leave any error messages in the pParse structure.
|
||||
@@ -3311,11 +3431,6 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
|
||||
** rollback the whole transaction. For operations where all constraints
|
||||
** can be checked before any changes are made to the database, it is never
|
||||
** necessary to undo a write and the checkpoint should not be set.
|
||||
**
|
||||
** Only database iDb and the temp database are made writable by this call.
|
||||
** If iDb==0, then the main and temp databases are made writable. If
|
||||
** iDb==1 then only the temp database is made writable. If iDb>1 then the
|
||||
** specified auxiliary database and the temp database are made writable.
|
||||
*/
|
||||
void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
@@ -3325,9 +3440,6 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
|
||||
if( setStatement && pParse->nested==0 ){
|
||||
sqlite3VdbeAddOp1(v, OP_Statement, iDb);
|
||||
}
|
||||
if( (OMIT_TEMPDB || iDb!=1) && pParse->db->aDb[1].pBt!=0 ){
|
||||
sqlite3BeginWriteOperation(pParse, setStatement, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3485,7 +3597,7 @@ KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
|
||||
pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl, -1);
|
||||
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
||||
}
|
||||
pKey->nField = nCol;
|
||||
pKey->nField = (u16)nCol;
|
||||
}
|
||||
|
||||
if( pParse->nErr ){
|
||||
|
||||
+128
-54
@@ -13,7 +13,7 @@
|
||||
** This file contains functions used to access the internal hash tables
|
||||
** of user defined functions and collation sequences.
|
||||
**
|
||||
** $Id: callback.c,v 1.26 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: callback.c,v 1.35 2009/01/31 22:28:49 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -56,7 +56,7 @@ static void callCollNeeded(sqlite3 *db, const char *zName, int nName){
|
||||
static int synthCollSeq(sqlite3 *db, CollSeq *pColl){
|
||||
CollSeq *pColl2;
|
||||
char *z = pColl->zName;
|
||||
int n = strlen(z);
|
||||
int n = sqlite3Strlen30(z);
|
||||
int i;
|
||||
static const u8 aEnc[] = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 };
|
||||
for(i=0; i<3; i++){
|
||||
@@ -222,6 +222,90 @@ CollSeq *sqlite3FindCollSeq(
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/* During the search for the best function definition, this procedure
|
||||
** is called to test how well the function passed as the first argument
|
||||
** matches the request for a function with nArg arguments in a system
|
||||
** that uses encoding enc. The value returned indicates how well the
|
||||
** request is matched. A higher value indicates a better match.
|
||||
**
|
||||
** The returned value is always between 0 and 6, as follows:
|
||||
**
|
||||
** 0: Not a match, or if nArg<0 and the function is has no implementation.
|
||||
** 1: A variable arguments function that prefers UTF-8 when a UTF-16
|
||||
** encoding is requested, or vice versa.
|
||||
** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
|
||||
** requested, or vice versa.
|
||||
** 3: A variable arguments function using the same text encoding.
|
||||
** 4: A function with the exact number of arguments requested that
|
||||
** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
|
||||
** 5: A function with the exact number of arguments requested that
|
||||
** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
|
||||
** 6: An exact match.
|
||||
**
|
||||
*/
|
||||
static int matchQuality(FuncDef *p, int nArg, u8 enc){
|
||||
int match = 0;
|
||||
if( p->nArg==-1 || p->nArg==nArg
|
||||
|| (nArg==-1 && (p->xFunc!=0 || p->xStep!=0))
|
||||
){
|
||||
match = 1;
|
||||
if( p->nArg==nArg || nArg==-1 ){
|
||||
match = 4;
|
||||
}
|
||||
if( enc==p->iPrefEnc ){
|
||||
match += 2;
|
||||
}
|
||||
else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
|
||||
(enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
|
||||
match += 1;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search a FuncDefHash for a function with the given name. Return
|
||||
** a pointer to the matching FuncDef if found, or 0 if there is no match.
|
||||
*/
|
||||
static FuncDef *functionSearch(
|
||||
FuncDefHash *pHash, /* Hash table to search */
|
||||
int h, /* Hash of the name */
|
||||
const char *zFunc, /* Name of function */
|
||||
int nFunc /* Number of bytes in zFunc */
|
||||
){
|
||||
FuncDef *p;
|
||||
for(p=pHash->a[h]; p; p=p->pHash){
|
||||
if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a new FuncDef into a FuncDefHash hash table.
|
||||
*/
|
||||
void sqlite3FuncDefInsert(
|
||||
FuncDefHash *pHash, /* The hash table into which to insert */
|
||||
FuncDef *pDef /* The function definition to insert */
|
||||
){
|
||||
FuncDef *pOther;
|
||||
int nName = sqlite3Strlen30(pDef->zName);
|
||||
u8 c1 = (u8)pDef->zName[0];
|
||||
int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a);
|
||||
pOther = functionSearch(pHash, h, pDef->zName, nName);
|
||||
if( pOther ){
|
||||
pDef->pNext = pOther->pNext;
|
||||
pOther->pNext = pDef;
|
||||
}else{
|
||||
pDef->pNext = 0;
|
||||
pDef->pHash = pHash->a[h];
|
||||
pHash->a[h] = pDef;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Locate a user function given a name, a number of arguments and a flag
|
||||
** indicating whether the function prefers UTF-16 over UTF-8. Return a
|
||||
@@ -251,70 +335,60 @@ FuncDef *sqlite3FindFunction(
|
||||
int createFlag /* Create new entry if true and does not otherwise exist */
|
||||
){
|
||||
FuncDef *p; /* Iterator variable */
|
||||
FuncDef *pFirst; /* First function with this name */
|
||||
FuncDef *pBest = 0; /* Best match found so far */
|
||||
int bestmatch = 0;
|
||||
int bestScore = 0; /* Score of best match */
|
||||
int h; /* Hash value */
|
||||
|
||||
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
if( nArg<-1 ) nArg = -1;
|
||||
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
|
||||
|
||||
pFirst = (FuncDef*)sqlite3HashFind(&db->aFunc, zName, nName);
|
||||
for(p=pFirst; p; p=p->pNext){
|
||||
/* During the search for the best function definition, bestmatch is set
|
||||
** as follows to indicate the quality of the match with the definition
|
||||
** pointed to by pBest:
|
||||
**
|
||||
** 0: pBest is NULL. No match has been found.
|
||||
** 1: A variable arguments function that prefers UTF-8 when a UTF-16
|
||||
** encoding is requested, or vice versa.
|
||||
** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
|
||||
** requested, or vice versa.
|
||||
** 3: A variable arguments function using the same text encoding.
|
||||
** 4: A function with the exact number of arguments requested that
|
||||
** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
|
||||
** 5: A function with the exact number of arguments requested that
|
||||
** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
|
||||
** 6: An exact match.
|
||||
**
|
||||
** A larger value of 'matchqual' indicates a more desirable match.
|
||||
*/
|
||||
if( p->nArg==-1 || p->nArg==nArg || nArg==-1 ){
|
||||
int match = 1; /* Quality of this match */
|
||||
if( p->nArg==nArg || nArg==-1 ){
|
||||
match = 4;
|
||||
}
|
||||
if( enc==p->iPrefEnc ){
|
||||
match += 2;
|
||||
}
|
||||
else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
|
||||
(enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
|
||||
match += 1;
|
||||
}
|
||||
/* First search for a match amongst the application-defined functions.
|
||||
*/
|
||||
p = functionSearch(&db->aFunc, h, zName, nName);
|
||||
while( p ){
|
||||
int score = matchQuality(p, nArg, enc);
|
||||
if( score>bestScore ){
|
||||
pBest = p;
|
||||
bestScore = score;
|
||||
}
|
||||
p = p->pNext;
|
||||
}
|
||||
|
||||
if( match>bestmatch ){
|
||||
/* If no match is found, search the built-in functions.
|
||||
**
|
||||
** Except, if createFlag is true, that means that we are trying to
|
||||
** install a new function. Whatever FuncDef structure is returned will
|
||||
** have fields overwritten with new information appropriate for the
|
||||
** new function. But the FuncDefs for built-in functions are read-only.
|
||||
** So we must not search for built-ins when creating a new function.
|
||||
*/
|
||||
if( !createFlag && !pBest ){
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
p = functionSearch(pHash, h, zName, nName);
|
||||
while( p ){
|
||||
int score = matchQuality(p, nArg, enc);
|
||||
if( score>bestScore ){
|
||||
pBest = p;
|
||||
bestmatch = match;
|
||||
bestScore = score;
|
||||
}
|
||||
p = p->pNext;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the createFlag parameter is true, and the seach did not reveal an
|
||||
/* If the createFlag parameter is true and the search did not reveal an
|
||||
** exact match for the name, number of arguments and encoding, then add a
|
||||
** new entry to the hash table and return it.
|
||||
*/
|
||||
if( createFlag && bestmatch<6 &&
|
||||
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName))!=0 ){
|
||||
pBest->nArg = nArg;
|
||||
pBest->pNext = pFirst;
|
||||
if( createFlag && (bestScore<6 || pBest->nArg!=nArg) &&
|
||||
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
|
||||
pBest->zName = (char *)&pBest[1];
|
||||
pBest->nArg = (u16)nArg;
|
||||
pBest->iPrefEnc = enc;
|
||||
memcpy(pBest->zName, zName, nName);
|
||||
pBest->zName[nName] = 0;
|
||||
if( pBest==sqlite3HashInsert(&db->aFunc,pBest->zName,nName,(void*)pBest) ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3DbFree(db, pBest);
|
||||
return 0;
|
||||
}
|
||||
sqlite3FuncDefInsert(&db->aFunc, pBest);
|
||||
}
|
||||
|
||||
if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
|
||||
@@ -339,14 +413,14 @@ void sqlite3SchemaFree(void *p){
|
||||
|
||||
temp1 = pSchema->tblHash;
|
||||
temp2 = pSchema->trigHash;
|
||||
sqlite3HashInit(&pSchema->trigHash, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&pSchema->trigHash, 0);
|
||||
sqlite3HashClear(&pSchema->aFKey);
|
||||
sqlite3HashClear(&pSchema->idxHash);
|
||||
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
|
||||
sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem));
|
||||
}
|
||||
sqlite3HashClear(&temp2);
|
||||
sqlite3HashInit(&pSchema->tblHash, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&pSchema->tblHash, 0);
|
||||
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
|
||||
Table *pTab = sqliteHashData(pElem);
|
||||
sqlite3DeleteTable(pTab);
|
||||
@@ -370,10 +444,10 @@ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
|
||||
if( !p ){
|
||||
db->mallocFailed = 1;
|
||||
}else if ( 0==p->file_format ){
|
||||
sqlite3HashInit(&p->tblHash, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&p->idxHash, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&p->trigHash, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&p->aFKey, SQLITE_HASH_STRING, 1);
|
||||
sqlite3HashInit(&p->tblHash, 0);
|
||||
sqlite3HashInit(&p->idxHash, 0);
|
||||
sqlite3HashInit(&p->trigHash, 0);
|
||||
sqlite3HashInit(&p->aFKey, 1);
|
||||
p->enc = SQLITE_UTF8;
|
||||
}
|
||||
return p;
|
||||
|
||||
+107
-93
@@ -16,7 +16,7 @@
|
||||
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: date.c,v 1.87 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: date.c,v 1.103 2009/02/04 03:59:25 shane Exp $
|
||||
**
|
||||
** SQLite processes all times and dates as Julian Day numbers. The
|
||||
** dates and times are stored as the number of days since noon
|
||||
@@ -46,7 +46,6 @@
|
||||
** Richmond, Virginia (USA)
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
@@ -80,10 +79,10 @@ struct DateTime {
|
||||
int h, m; /* Hour and minutes */
|
||||
int tz; /* Timezone offset in minutes */
|
||||
double s; /* Seconds */
|
||||
char validYMD; /* True if Y,M,D are valid */
|
||||
char validHMS; /* True if h,m,s are valid */
|
||||
char validJD; /* True if iJD is valid */
|
||||
char validTZ; /* True if tz is valid */
|
||||
char validYMD; /* True (1) if Y,M,D are valid */
|
||||
char validHMS; /* True (1) if h,m,s are valid */
|
||||
char validJD; /* True (1) if iJD is valid */
|
||||
char validTZ; /* True (1) if tz is valid */
|
||||
};
|
||||
|
||||
|
||||
@@ -118,7 +117,7 @@ static int getDigits(const char *zDate, ...){
|
||||
pVal = va_arg(ap, int*);
|
||||
val = 0;
|
||||
while( N-- ){
|
||||
if( !isdigit(*(u8*)zDate) ){
|
||||
if( !sqlite3Isdigit(*zDate) ){
|
||||
goto end_getDigits;
|
||||
}
|
||||
val = val*10 + *zDate - '0';
|
||||
@@ -162,7 +161,7 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
||||
int sgn = 0;
|
||||
int nHr, nMn;
|
||||
int c;
|
||||
while( isspace(*(u8*)zDate) ){ zDate++; }
|
||||
while( sqlite3Isspace(*zDate) ){ zDate++; }
|
||||
p->tz = 0;
|
||||
c = *zDate;
|
||||
if( c=='-' ){
|
||||
@@ -182,7 +181,7 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
||||
zDate += 5;
|
||||
p->tz = sgn*(nMn + nHr*60);
|
||||
zulu_time:
|
||||
while( isspace(*(u8*)zDate) ){ zDate++; }
|
||||
while( sqlite3Isspace(*zDate) ){ zDate++; }
|
||||
return *zDate!=0;
|
||||
}
|
||||
|
||||
@@ -206,10 +205,10 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
return 1;
|
||||
}
|
||||
zDate += 2;
|
||||
if( *zDate=='.' && isdigit((u8)zDate[1]) ){
|
||||
if( *zDate=='.' && sqlite3Isdigit(zDate[1]) ){
|
||||
double rScale = 1.0;
|
||||
zDate++;
|
||||
while( isdigit(*(u8*)zDate) ){
|
||||
while( sqlite3Isdigit(*zDate) ){
|
||||
ms = ms*10.0 + *zDate - '0';
|
||||
rScale *= 10.0;
|
||||
zDate++;
|
||||
@@ -225,7 +224,7 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
p->m = m;
|
||||
p->s = s + ms;
|
||||
if( parseTimezone(zDate, p) ) return 1;
|
||||
p->validTZ = p->tz!=0;
|
||||
p->validTZ = (p->tz!=0)?1:0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -254,12 +253,12 @@ static void computeJD(DateTime *p){
|
||||
}
|
||||
A = Y/100;
|
||||
B = 2 - A + (A/4);
|
||||
X1 = 365.25*(Y+4716);
|
||||
X2 = 30.6001*(M+1);
|
||||
p->iJD = (X1 + X2 + D + B - 1524.5)*86400000;
|
||||
X1 = 36525*(Y+4716)/100;
|
||||
X2 = 306001*(M+1)/10000;
|
||||
p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000);
|
||||
p->validJD = 1;
|
||||
if( p->validHMS ){
|
||||
p->iJD += p->h*3600000 + p->m*60000 + p->s*1000;
|
||||
p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000);
|
||||
if( p->validTZ ){
|
||||
p->iJD -= p->tz*60000;
|
||||
p->validYMD = 0;
|
||||
@@ -294,7 +293,7 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
return 1;
|
||||
}
|
||||
zDate += 10;
|
||||
while( isspace(*(u8*)zDate) || 'T'==*(u8*)zDate ){ zDate++; }
|
||||
while( sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; }
|
||||
if( parseHhMmSs(zDate, p)==0 ){
|
||||
/* We got the time */
|
||||
}else if( *zDate==0 ){
|
||||
@@ -373,14 +372,14 @@ static void computeYMD(DateTime *p){
|
||||
p->M = 1;
|
||||
p->D = 1;
|
||||
}else{
|
||||
Z = (p->iJD + 43200000)/86400000;
|
||||
A = (Z - 1867216.25)/36524.25;
|
||||
Z = (int)((p->iJD + 43200000)/86400000);
|
||||
A = (int)((Z - 1867216.25)/36524.25);
|
||||
A = Z + 1 + A - (A/4);
|
||||
B = A + 1524;
|
||||
C = (B - 122.1)/365.25;
|
||||
D = 365.25*C;
|
||||
E = (B-D)/30.6001;
|
||||
X1 = 30.6001*E;
|
||||
C = (int)((B - 122.1)/365.25);
|
||||
D = (36525*C)/100;
|
||||
E = (int)((B-D)/30.6001);
|
||||
X1 = (int)(30.6001*E);
|
||||
p->D = B - D - X1;
|
||||
p->M = E<14 ? E-1 : E-13;
|
||||
p->Y = p->M>2 ? C - 4716 : C - 4715;
|
||||
@@ -395,9 +394,9 @@ static void computeHMS(DateTime *p){
|
||||
int s;
|
||||
if( p->validHMS ) return;
|
||||
computeJD(p);
|
||||
s = (p->iJD + 43200000) % 86400000;
|
||||
s = (int)((p->iJD + 43200000) % 86400000);
|
||||
p->s = s/1000.0;
|
||||
s = p->s;
|
||||
s = (int)p->s;
|
||||
p->s -= s;
|
||||
p->h = s/3600;
|
||||
s -= p->h*3600;
|
||||
@@ -429,7 +428,7 @@ static void clearYMD_HMS_TZ(DateTime *p){
|
||||
** between localtime and UTC (a.k.a. GMT)
|
||||
** for the time value p where p is in UTC.
|
||||
*/
|
||||
static int localtimeOffset(DateTime *p){
|
||||
static sqlite3_int64 localtimeOffset(DateTime *p){
|
||||
DateTime x, y;
|
||||
time_t t;
|
||||
x = *p;
|
||||
@@ -442,13 +441,13 @@ static int localtimeOffset(DateTime *p){
|
||||
x.m = 0;
|
||||
x.s = 0.0;
|
||||
} else {
|
||||
int s = x.s + 0.5;
|
||||
int s = (int)(x.s + 0.5);
|
||||
x.s = s;
|
||||
}
|
||||
x.tz = 0;
|
||||
x.validJD = 0;
|
||||
computeJD(&x);
|
||||
t = x.iJD/1000 - 2440587.5*86400.0;
|
||||
t = x.iJD/1000 - 21086676*(i64)10000;
|
||||
#ifdef HAVE_LOCALTIME_R
|
||||
{
|
||||
struct tm sLocal;
|
||||
@@ -521,8 +520,8 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
double r;
|
||||
char *z, zBuf[30];
|
||||
z = zBuf;
|
||||
for(n=0; n<sizeof(zBuf)-1 && zMod[n]; n++){
|
||||
z[n] = tolower(zMod[n]);
|
||||
for(n=0; n<ArraySize(zBuf)-1 && zMod[n]; n++){
|
||||
z[n] = (char)sqlite3UpperToLower[(u8)zMod[n]];
|
||||
}
|
||||
z[n] = 0;
|
||||
switch( z[0] ){
|
||||
@@ -550,13 +549,13 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
** seconds since 1970. Convert to a real julian day number.
|
||||
*/
|
||||
if( strcmp(z, "unixepoch")==0 && p->validJD ){
|
||||
p->iJD = p->iJD/86400.0 + 2440587.5*86400000.0;
|
||||
p->iJD = p->iJD/86400 + 21086676*(i64)10000000;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
else if( strcmp(z, "utc")==0 ){
|
||||
double c1;
|
||||
sqlite3_int64 c1;
|
||||
computeJD(p);
|
||||
c1 = localtimeOffset(p);
|
||||
p->iJD -= c1;
|
||||
@@ -576,7 +575,7 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
** date is already on the appropriate weekday, this is a no-op.
|
||||
*/
|
||||
if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
|
||||
&& (n=r)==r && n>=0 && r<7 ){
|
||||
&& (n=(int)r)==r && n>=0 && r<7 ){
|
||||
sqlite3_int64 Z;
|
||||
computeYMD_HMS(p);
|
||||
p->validTZ = 0;
|
||||
@@ -630,6 +629,7 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
double rRounder;
|
||||
n = getValue(z, &r);
|
||||
assert( n>=1 );
|
||||
if( z[n]==':' ){
|
||||
@@ -641,7 +641,7 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
const char *z2 = z;
|
||||
DateTime tx;
|
||||
sqlite3_int64 day;
|
||||
if( !isdigit(*(u8*)z2) ) z2++;
|
||||
if( !sqlite3Isdigit(*z2) ) z2++;
|
||||
memset(&tx, 0, sizeof(tx));
|
||||
if( parseHhMmSs(z2, &tx) ) break;
|
||||
computeJD(&tx);
|
||||
@@ -656,38 +656,43 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
break;
|
||||
}
|
||||
z += n;
|
||||
while( isspace(*(u8*)z) ) z++;
|
||||
n = strlen(z);
|
||||
while( sqlite3Isspace(*z) ) z++;
|
||||
n = sqlite3Strlen30(z);
|
||||
if( n>10 || n<3 ) break;
|
||||
if( z[n-1]=='s' ){ z[n-1] = 0; n--; }
|
||||
computeJD(p);
|
||||
rc = 0;
|
||||
rRounder = r<0 ? -0.5 : +0.5;
|
||||
if( n==3 && strcmp(z,"day")==0 ){
|
||||
p->iJD += r*86400000.0 + 0.5;
|
||||
p->iJD += (sqlite3_int64)(r*86400000.0 + rRounder);
|
||||
}else if( n==4 && strcmp(z,"hour")==0 ){
|
||||
p->iJD += r*(86400000.0/24.0) + 0.5;
|
||||
p->iJD += (sqlite3_int64)(r*(86400000.0/24.0) + rRounder);
|
||||
}else if( n==6 && strcmp(z,"minute")==0 ){
|
||||
p->iJD += r*(86400000.0/(24.0*60.0)) + 0.5;
|
||||
p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0)) + rRounder);
|
||||
}else if( n==6 && strcmp(z,"second")==0 ){
|
||||
p->iJD += r*(86400000.0/(24.0*60.0*60.0)) + 0.5;
|
||||
p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0*60.0)) + rRounder);
|
||||
}else if( n==5 && strcmp(z,"month")==0 ){
|
||||
int x, y;
|
||||
computeYMD_HMS(p);
|
||||
p->M += r;
|
||||
p->M += (int)r;
|
||||
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
|
||||
p->Y += x;
|
||||
p->M -= x*12;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
y = r;
|
||||
y = (int)r;
|
||||
if( y!=r ){
|
||||
p->iJD += (r - y)*30.0*86400000.0 + 0.5;
|
||||
p->iJD += (sqlite3_int64)((r - y)*30.0*86400000.0 + rRounder);
|
||||
}
|
||||
}else if( n==4 && strcmp(z,"year")==0 ){
|
||||
int y = (int)r;
|
||||
computeYMD_HMS(p);
|
||||
p->Y += r;
|
||||
p->Y += y;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
if( y!=r ){
|
||||
p->iJD += (sqlite3_int64)((r - y)*365.0*86400000.0 + rRounder);
|
||||
}
|
||||
}else{
|
||||
rc = 1;
|
||||
}
|
||||
@@ -724,7 +729,7 @@ static int isDate(
|
||||
setDateTimeToCurrent(context, p);
|
||||
}else if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|
||||
|| eType==SQLITE_INTEGER ){
|
||||
p->iJD = sqlite3_value_double(argv[0])*86400000.0 + 0.5;
|
||||
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
}else{
|
||||
z = sqlite3_value_text(argv[0]);
|
||||
@@ -847,7 +852,7 @@ static void strftimeFunc(
|
||||
){
|
||||
DateTime x;
|
||||
u64 n;
|
||||
int i, j;
|
||||
size_t i,j;
|
||||
char *z;
|
||||
sqlite3 *db;
|
||||
const char *zFmt = (const char*)sqlite3_value_text(argv[0]);
|
||||
@@ -887,13 +892,17 @@ static void strftimeFunc(
|
||||
i++;
|
||||
}
|
||||
}
|
||||
testcase( n==sizeof(zBuf)-1 );
|
||||
testcase( n==sizeof(zBuf) );
|
||||
testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
|
||||
testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
if( n<sizeof(zBuf) ){
|
||||
z = zBuf;
|
||||
}else if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
}else if( n>(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
return;
|
||||
}else{
|
||||
z = sqlite3DbMallocRaw(db, n);
|
||||
z = sqlite3DbMallocRaw(db, (int)n);
|
||||
if( z==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
@@ -912,7 +921,7 @@ static void strftimeFunc(
|
||||
double s = x.s;
|
||||
if( s>59.999 ) s = 59.999;
|
||||
sqlite3_snprintf(7, &z[j],"%06.3f", s);
|
||||
j += strlen(&z[j]);
|
||||
j += sqlite3Strlen30(&z[j]);
|
||||
break;
|
||||
}
|
||||
case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break;
|
||||
@@ -924,10 +933,10 @@ static void strftimeFunc(
|
||||
y.M = 1;
|
||||
y.D = 1;
|
||||
computeJD(&y);
|
||||
nDay = (x.iJD - y.iJD)/86400000.0 + 0.5;
|
||||
nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
|
||||
if( zFmt[i]=='W' ){
|
||||
int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
|
||||
wd = ((x.iJD+43200000)/86400000) % 7;
|
||||
wd = (int)(((x.iJD+43200000)/86400000)%7);
|
||||
sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7);
|
||||
j += 2;
|
||||
}else{
|
||||
@@ -938,7 +947,7 @@ static void strftimeFunc(
|
||||
}
|
||||
case 'J': {
|
||||
sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0);
|
||||
j+=strlen(&z[j]);
|
||||
j+=sqlite3Strlen30(&z[j]);
|
||||
break;
|
||||
}
|
||||
case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break;
|
||||
@@ -946,12 +955,18 @@ static void strftimeFunc(
|
||||
case 's': {
|
||||
sqlite3_snprintf(30,&z[j],"%d",
|
||||
(int)(x.iJD/1000.0 - 210866760000.0));
|
||||
j += strlen(&z[j]);
|
||||
j += sqlite3Strlen30(&z[j]);
|
||||
break;
|
||||
}
|
||||
case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break;
|
||||
case 'w': z[j++] = (((x.iJD+129600000)/86400000) % 7) + '0'; break;
|
||||
case 'Y': sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=strlen(&z[j]);break;
|
||||
case 'w': {
|
||||
z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
|
||||
break;
|
||||
}
|
||||
case 'Y': {
|
||||
sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]);
|
||||
break;
|
||||
}
|
||||
default: z[j++] = '%'; break;
|
||||
}
|
||||
}
|
||||
@@ -968,9 +983,10 @@ static void strftimeFunc(
|
||||
*/
|
||||
static void ctimeFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
timeFunc(context, 0, 0);
|
||||
}
|
||||
|
||||
@@ -981,9 +997,10 @@ static void ctimeFunc(
|
||||
*/
|
||||
static void cdateFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
dateFunc(context, 0, 0);
|
||||
}
|
||||
|
||||
@@ -994,9 +1011,10 @@ static void cdateFunc(
|
||||
*/
|
||||
static void ctimestampFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
datetimeFunc(context, 0, 0);
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */
|
||||
@@ -1024,9 +1042,19 @@ static void currentTimeFunc(
|
||||
double rT;
|
||||
char zBuf[20];
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
|
||||
db = sqlite3_context_db_handle(context);
|
||||
sqlite3OsCurrentTime(db->pVfs, &rT);
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
t = 86400.0*(rT - 2440587.5) + 0.5;
|
||||
#else
|
||||
/* without floating point support, rT will have
|
||||
** already lost fractional day precision.
|
||||
*/
|
||||
t = 86400 * (rT - 2440587) - 43200;
|
||||
#endif
|
||||
#ifdef HAVE_GMTIME_R
|
||||
{
|
||||
struct tm sNow;
|
||||
@@ -1052,42 +1080,28 @@ static void currentTimeFunc(
|
||||
** functions. This should be the only routine in this file with
|
||||
** external linkage.
|
||||
*/
|
||||
void sqlite3RegisterDateTimeFunctions(sqlite3 *db){
|
||||
void sqlite3RegisterDateTimeFunctions(void){
|
||||
static SQLITE_WSD FuncDef aDateTimeFuncs[] = {
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
static const struct {
|
||||
char *zName;
|
||||
int nArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFuncs[] = {
|
||||
{ "julianday", -1, juliandayFunc },
|
||||
{ "date", -1, dateFunc },
|
||||
{ "time", -1, timeFunc },
|
||||
{ "datetime", -1, datetimeFunc },
|
||||
{ "strftime", -1, strftimeFunc },
|
||||
{ "current_time", 0, ctimeFunc },
|
||||
{ "current_timestamp", 0, ctimestampFunc },
|
||||
{ "current_date", 0, cdateFunc },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
|
||||
sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
|
||||
SQLITE_UTF8, 0, aFuncs[i].xFunc, 0, 0);
|
||||
}
|
||||
FUNCTION(julianday, -1, 0, 0, juliandayFunc ),
|
||||
FUNCTION(date, -1, 0, 0, dateFunc ),
|
||||
FUNCTION(time, -1, 0, 0, timeFunc ),
|
||||
FUNCTION(datetime, -1, 0, 0, datetimeFunc ),
|
||||
FUNCTION(strftime, -1, 0, 0, strftimeFunc ),
|
||||
FUNCTION(current_time, 0, 0, 0, ctimeFunc ),
|
||||
FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
|
||||
FUNCTION(current_date, 0, 0, 0, cdateFunc ),
|
||||
#else
|
||||
static const struct {
|
||||
char *zName;
|
||||
char *zFormat;
|
||||
} aFuncs[] = {
|
||||
{ "current_time", "%H:%M:%S" },
|
||||
{ "current_date", "%Y-%m-%d" },
|
||||
{ "current_timestamp", "%Y-%m-%d %H:%M:%S" }
|
||||
STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc),
|
||||
STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d", 0, currentTimeFunc),
|
||||
STR_FUNCTION(current_date, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aDateTimeFuncs);
|
||||
|
||||
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
|
||||
sqlite3CreateFunc(db, aFuncs[i].zName, 0, SQLITE_UTF8,
|
||||
aFuncs[i].zFormat, currentTimeFunc, 0, 0);
|
||||
for(i=0; i<ArraySize(aDateTimeFuncs); i++){
|
||||
sqlite3FuncDefInsert(pHash, &aFunc[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+147
-61
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.171 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: delete.c,v 1.191 2008/12/23 23:56:22 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -22,16 +22,17 @@
|
||||
** are found, return a pointer to the last table.
|
||||
*/
|
||||
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
Table *pTab = 0;
|
||||
int i;
|
||||
struct SrcList_item *pItem;
|
||||
for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
|
||||
pTab = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
|
||||
sqlite3DeleteTable(pItem->pTab);
|
||||
pItem->pTab = pTab;
|
||||
if( pTab ){
|
||||
pTab->nRef++;
|
||||
}
|
||||
struct SrcList_item *pItem = pSrc->a;
|
||||
Table *pTab;
|
||||
assert( pItem && pSrc->nSrc==1 );
|
||||
pTab = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
|
||||
sqlite3DeleteTable(pItem->pTab);
|
||||
pItem->pTab = pTab;
|
||||
if( pTab ){
|
||||
pTab->nRef++;
|
||||
}
|
||||
if( sqlite3IndexedByLookup(pParse, pItem) ){
|
||||
pTab = 0;
|
||||
}
|
||||
return pTab;
|
||||
}
|
||||
@@ -42,7 +43,8 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
** writable return 0;
|
||||
*/
|
||||
int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
|
||||
if( (pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0
|
||||
if( ((pTab->tabFlags & TF_Readonly)!=0
|
||||
&& (pParse->db->flags & SQLITE_WriteSchema)==0
|
||||
&& pParse->nested==0)
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|| (pTab->pMod && pTab->pMod->pModule->xUpdate==0)
|
||||
@@ -74,7 +76,7 @@ void sqlite3OpenTable(
|
||||
if( IsVirtual(pTab) ) return;
|
||||
v = sqlite3GetVdbe(p);
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName);
|
||||
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
@@ -89,7 +91,7 @@ void sqlite3OpenTable(
|
||||
*/
|
||||
void sqlite3MaterializeView(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Select *pView, /* View definition */
|
||||
Table *pView, /* View definition */
|
||||
Expr *pWhere, /* Optional WHERE clause to be added */
|
||||
int iCur /* Cursor number for ephemerial table */
|
||||
){
|
||||
@@ -97,20 +99,114 @@ void sqlite3MaterializeView(
|
||||
Select *pDup;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
pDup = sqlite3SelectDup(db, pView);
|
||||
pDup = sqlite3SelectDup(db, pView->pSelect);
|
||||
if( pWhere ){
|
||||
SrcList *pFrom;
|
||||
Token viewName;
|
||||
|
||||
pWhere = sqlite3ExprDup(db, pWhere);
|
||||
pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, 0, pDup, 0, 0);
|
||||
viewName.z = (u8*)pView->zName;
|
||||
viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z);
|
||||
pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0);
|
||||
pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
|
||||
sqlite3Select(pParse, pDup, &dest, 0, 0, 0);
|
||||
sqlite3Select(pParse, pDup, &dest);
|
||||
sqlite3SelectDelete(db, pDup);
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */
|
||||
|
||||
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
|
||||
/*
|
||||
** Generate an expression tree to implement the WHERE, ORDER BY,
|
||||
** and LIMIT/OFFSET portion of DELETE and UPDATE statements.
|
||||
**
|
||||
** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1;
|
||||
** \__________________________/
|
||||
** pLimitWhere (pInClause)
|
||||
*/
|
||||
Expr *sqlite3LimitWhere(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pSrc, /* the FROM clause -- which tables to scan */
|
||||
Expr *pWhere, /* The WHERE clause. May be null */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
|
||||
Expr *pLimit, /* The LIMIT clause. May be null */
|
||||
Expr *pOffset, /* The OFFSET clause. May be null */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For error messages. */
|
||||
){
|
||||
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
|
||||
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
|
||||
Expr *pSelectRowid = NULL; /* SELECT rowid ... */
|
||||
ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */
|
||||
SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */
|
||||
Select *pSelect = NULL; /* Complete SELECT tree */
|
||||
|
||||
/* Check that there isn't an ORDER BY without a LIMIT clause.
|
||||
*/
|
||||
if( pOrderBy && (pLimit == 0) ) {
|
||||
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
||||
pParse->parseError = 1;
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
/* We only need to generate a select expression if there
|
||||
** is a limit/offset term to enforce.
|
||||
*/
|
||||
if( pLimit == 0 ) {
|
||||
/* if pLimit is null, pOffset will always be null as well. */
|
||||
assert( pOffset == 0 );
|
||||
return pWhere;
|
||||
}
|
||||
|
||||
/* Generate a select expression tree to enforce the limit/offset
|
||||
** term for the DELETE or UPDATE statement. For example:
|
||||
** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
|
||||
** becomes:
|
||||
** DELETE FROM table_a WHERE rowid IN (
|
||||
** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
|
||||
** );
|
||||
*/
|
||||
|
||||
pSelectRowid = sqlite3Expr(pParse->db, TK_ROW, 0, 0, 0);
|
||||
if( pSelectRowid == 0 ) goto limit_where_cleanup_2;
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid, 0);
|
||||
if( pEList == 0 ) goto limit_where_cleanup_2;
|
||||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc);
|
||||
if( pSelectSrc == 0 ) {
|
||||
sqlite3ExprListDelete(pParse->db, pEList);
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
/* generate the SELECT expression tree. */
|
||||
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,pOrderBy,0,pLimit,pOffset);
|
||||
if( pSelect == 0 ) return 0;
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
pWhereRowid = sqlite3Expr(pParse->db, TK_ROW, 0, 0, 0);
|
||||
if( pWhereRowid == 0 ) goto limit_where_cleanup_1;
|
||||
pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
|
||||
if( pInClause == 0 ) goto limit_where_cleanup_1;
|
||||
|
||||
pInClause->pSelect = pSelect;
|
||||
sqlite3ExprSetHeight(pParse, pInClause);
|
||||
return pInClause;
|
||||
|
||||
/* something went wrong. clean up anything allocated. */
|
||||
limit_where_cleanup_1:
|
||||
sqlite3SelectDelete(pParse->db, pSelect);
|
||||
return 0;
|
||||
|
||||
limit_where_cleanup_2:
|
||||
sqlite3ExprDelete(pParse->db, pWhere);
|
||||
sqlite3ExprListDelete(pParse->db, pOrderBy);
|
||||
sqlite3ExprDelete(pParse->db, pLimit);
|
||||
sqlite3ExprDelete(pParse->db, pOffset);
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
|
||||
/*
|
||||
** Generate code for a DELETE FROM statement.
|
||||
@@ -137,16 +233,17 @@ void sqlite3DeleteFrom(
|
||||
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
|
||||
NameContext sNC; /* Name context to resolve expressions in */
|
||||
int iDb; /* Database number */
|
||||
int memCnt = 0; /* Memory cell used for change counting */
|
||||
int memCnt = -1; /* Memory cell used for change counting */
|
||||
int rcauth; /* Value returned by authorization callback */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
int triggers_exist = 0; /* True if any triggers exist */
|
||||
#endif
|
||||
int iBeginAfterTrigger; /* Address of after trigger program */
|
||||
int iEndAfterTrigger; /* Exit of after trigger program */
|
||||
int iBeginBeforeTrigger; /* Address of before trigger program */
|
||||
int iEndBeforeTrigger; /* Exit of before trigger program */
|
||||
int iBeginAfterTrigger = 0; /* Address of after trigger program */
|
||||
int iEndAfterTrigger = 0; /* Exit of after trigger program */
|
||||
int iBeginBeforeTrigger = 0; /* Address of before trigger program */
|
||||
int iEndBeforeTrigger = 0; /* Exit of before trigger program */
|
||||
u32 old_col_mask = 0; /* Mask of OLD.* columns in use */
|
||||
|
||||
sContext.pParse = 0;
|
||||
@@ -168,7 +265,7 @@ void sqlite3DeleteFrom(
|
||||
** deleted from is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0);
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_DELETE, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
@@ -185,9 +282,12 @@ void sqlite3DeleteFrom(
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
zDb = db->aDb[iDb].zName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
|
||||
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
|
||||
if( rcauth==SQLITE_DENY ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
assert(!isView || triggers_exist);
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
@@ -245,16 +345,18 @@ void sqlite3DeleteFrom(
|
||||
/* If we are trying to delete from a view, realize that view into
|
||||
** a ephemeral table.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, iCur);
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Resolve the column names in the WHERE clause.
|
||||
*/
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = pTabList;
|
||||
if( sqlite3ExprResolveNames(&sNC, pWhere) ){
|
||||
if( sqlite3ResolveExprNames(&sNC, pWhere) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
@@ -266,55 +368,39 @@ void sqlite3DeleteFrom(
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
|
||||
/* Special case: A DELETE without a WHERE clause deletes everything.
|
||||
** It is easier just to erase the whole table. Note, however, that
|
||||
** this means that the row change count will be incorrect.
|
||||
*/
|
||||
if( pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
/* If counting rows deleted, just count the total number of
|
||||
** entries in the table. */
|
||||
int addr2;
|
||||
if( !isView ){
|
||||
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
|
||||
addr2 = sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iCur, addr2);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCur);
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){
|
||||
assert( !isView );
|
||||
sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
||||
}
|
||||
if( !isView ){
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, pTab->tnum, iDb);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
|
||||
}
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table and pick which records to delete.
|
||||
*/
|
||||
else{
|
||||
{
|
||||
int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
|
||||
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
|
||||
|
||||
/* Begin the database scan
|
||||
/* Collect rowids of every row to be deleted.
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
|
||||
WHERE_FILL_ROWSET, iRowSet);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Remember the rowid of every item to be deleted.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, iRowid);
|
||||
sqlite3VdbeAddOp1(v, OP_FifoWrite, iRowid);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||
@@ -343,7 +429,7 @@ void sqlite3DeleteFrom(
|
||||
if( triggers_exist ){
|
||||
sqlite3VdbeResolveLabel(v, addr);
|
||||
}
|
||||
addr = sqlite3VdbeAddOp2(v, OP_FifoRead, iRowid, end);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
|
||||
|
||||
if( triggers_exist ){
|
||||
int iData = ++pParse->nMem; /* For storing row data of OLD table */
|
||||
@@ -410,7 +496,7 @@ void sqlite3DeleteFrom(
|
||||
if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
|
||||
}
|
||||
|
||||
delete_from_cleanup:
|
||||
|
||||
+271
-774
File diff suppressed because it is too large
Load Diff
+29
-9
@@ -10,7 +10,7 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: fault.c,v 1.10 2008/06/22 12:37:58 drh Exp $
|
||||
** $Id: fault.c,v 1.11 2008/09/02 00:52:52 drh Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -35,10 +35,27 @@
|
||||
/*
|
||||
** Global variables.
|
||||
*/
|
||||
static struct BenignMallocHooks {
|
||||
typedef struct BenignMallocHooks BenignMallocHooks;
|
||||
static SQLITE_WSD struct BenignMallocHooks {
|
||||
void (*xBenignBegin)(void);
|
||||
void (*xBenignEnd)(void);
|
||||
} hooks;
|
||||
} sqlite3Hooks = { 0, 0 };
|
||||
|
||||
/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks
|
||||
** structure. If writable static data is unsupported on the target,
|
||||
** we have to locate the state vector at run-time. In the more common
|
||||
** case where writable static data is supported, wsdHooks can refer directly
|
||||
** to the "sqlite3Hooks" state vector declared above.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
# define wsdHooksInit \
|
||||
BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlite3Hooks)
|
||||
# define wsdHooks x[0]
|
||||
#else
|
||||
# define wsdHooksInit
|
||||
# define wsdHooks sqlite3Hooks
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register hooks to call when sqlite3BeginBenignMalloc() and
|
||||
@@ -48,8 +65,9 @@ void sqlite3BenignMallocHooks(
|
||||
void (*xBenignBegin)(void),
|
||||
void (*xBenignEnd)(void)
|
||||
){
|
||||
hooks.xBenignBegin = xBenignBegin;
|
||||
hooks.xBenignEnd = xBenignEnd;
|
||||
wsdHooksInit;
|
||||
wsdHooks.xBenignBegin = xBenignBegin;
|
||||
wsdHooks.xBenignEnd = xBenignEnd;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -58,13 +76,15 @@ void sqlite3BenignMallocHooks(
|
||||
** indicates that subsequent malloc failures are non-benign.
|
||||
*/
|
||||
void sqlite3BeginBenignMalloc(void){
|
||||
if( hooks.xBenignBegin ){
|
||||
hooks.xBenignBegin();
|
||||
wsdHooksInit;
|
||||
if( wsdHooks.xBenignBegin ){
|
||||
wsdHooks.xBenignBegin();
|
||||
}
|
||||
}
|
||||
void sqlite3EndBenignMalloc(void){
|
||||
if( hooks.xBenignEnd ){
|
||||
hooks.xBenignEnd();
|
||||
wsdHooksInit;
|
||||
if( wsdHooks.xBenignEnd ){
|
||||
wsdHooks.xBenignEnd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+205
-166
@@ -16,15 +16,13 @@
|
||||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: func.c,v 1.196 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: func.c,v 1.222 2009/02/04 03:59:25 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "vdbeInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** Return the collating function associated with a function.
|
||||
*/
|
||||
@@ -45,7 +43,7 @@ static void minmaxFunc(
|
||||
int iBest;
|
||||
CollSeq *pColl;
|
||||
|
||||
if( argc==0 ) return;
|
||||
assert( argc>1 );
|
||||
mask = sqlite3_user_data(context)==0 ? 0 : -1;
|
||||
pColl = sqlite3GetFuncCollSeq(context);
|
||||
assert( pColl );
|
||||
@@ -55,6 +53,7 @@ static void minmaxFunc(
|
||||
for(i=1; i<argc; i++){
|
||||
if( sqlite3_value_type(argv[i])==SQLITE_NULL ) return;
|
||||
if( (sqlite3MemCompare(argv[iBest], argv[i], pColl)^mask)>=0 ){
|
||||
testcase( mask==0 );
|
||||
iBest = i;
|
||||
}
|
||||
}
|
||||
@@ -66,16 +65,17 @@ static void minmaxFunc(
|
||||
*/
|
||||
static void typeofFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *z = 0;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_NULL: z = "null"; break;
|
||||
case SQLITE_INTEGER: z = "integer"; break;
|
||||
case SQLITE_TEXT: z = "text"; break;
|
||||
case SQLITE_FLOAT: z = "real"; break;
|
||||
case SQLITE_BLOB: z = "blob"; break;
|
||||
default: z = "null"; break;
|
||||
}
|
||||
sqlite3_result_text(context, z, -1, SQLITE_STATIC);
|
||||
}
|
||||
@@ -92,6 +92,7 @@ static void lengthFunc(
|
||||
int len;
|
||||
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_BLOB:
|
||||
case SQLITE_INTEGER:
|
||||
@@ -122,6 +123,7 @@ static void lengthFunc(
|
||||
*/
|
||||
static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_INTEGER: {
|
||||
i64 iVal = sqlite3_value_int64(argv[0]);
|
||||
@@ -168,8 +170,14 @@ static void substrFunc(
|
||||
int len;
|
||||
int p0type;
|
||||
i64 p1, p2;
|
||||
int negP2 = 0;
|
||||
|
||||
assert( argc==3 || argc==2 );
|
||||
if( sqlite3_value_type(argv[1])==SQLITE_NULL
|
||||
|| (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL)
|
||||
){
|
||||
return;
|
||||
}
|
||||
p0type = sqlite3_value_type(argv[0]);
|
||||
if( p0type==SQLITE_BLOB ){
|
||||
len = sqlite3_value_bytes(argv[0]);
|
||||
@@ -187,6 +195,10 @@ static void substrFunc(
|
||||
p1 = sqlite3_value_int(argv[1]);
|
||||
if( argc==3 ){
|
||||
p2 = sqlite3_value_int(argv[2]);
|
||||
if( p2<0 ){
|
||||
p2 = -p2;
|
||||
negP2 = 1;
|
||||
}
|
||||
}else{
|
||||
p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH];
|
||||
}
|
||||
@@ -194,13 +206,25 @@ static void substrFunc(
|
||||
p1 += len;
|
||||
if( p1<0 ){
|
||||
p2 += p1;
|
||||
if( p2<0 ) p2 = 0;
|
||||
p1 = 0;
|
||||
}
|
||||
}else if( p1>0 ){
|
||||
p1--;
|
||||
}else if( p2>0 ){
|
||||
p2--;
|
||||
}
|
||||
if( negP2 ){
|
||||
p1 -= p2;
|
||||
if( p1<0 ){
|
||||
p2 += p1;
|
||||
p1 = 0;
|
||||
}
|
||||
}
|
||||
assert( p1>=0 && p2>=0 );
|
||||
if( p1+p2>len ){
|
||||
p2 = len-p1;
|
||||
if( p2<0 ) p2 = 0;
|
||||
}
|
||||
if( p0type!=SQLITE_BLOB ){
|
||||
while( *z && p1 ){
|
||||
@@ -210,16 +234,16 @@ static void substrFunc(
|
||||
for(z2=z; *z2 && p2; p2--){
|
||||
SQLITE_SKIP_UTF8(z2);
|
||||
}
|
||||
sqlite3_result_text(context, (char*)z, z2-z, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(context, (char*)z, (int)(z2-z), SQLITE_TRANSIENT);
|
||||
}else{
|
||||
if( p2<0 ) p2 = 0;
|
||||
sqlite3_result_blob(context, (char*)&z[p1], p2, SQLITE_TRANSIENT);
|
||||
sqlite3_result_blob(context, (char*)&z[p1], (int)p2, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the round() function
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
int n = 0;
|
||||
double r;
|
||||
@@ -237,6 +261,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
sqlite3AtoF(zBuf, &r);
|
||||
sqlite3_result_double(context, r);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allocate nByte bytes of space using sqlite3_malloc(). If the
|
||||
@@ -249,7 +274,7 @@ static void *contextMalloc(sqlite3_context *context, i64 nByte){
|
||||
sqlite3_result_error_toobig(context);
|
||||
z = 0;
|
||||
}else{
|
||||
z = sqlite3Malloc(nByte);
|
||||
z = sqlite3Malloc((int)nByte);
|
||||
if( !z && nByte>0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}
|
||||
@@ -264,7 +289,7 @@ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
char *z1;
|
||||
const char *z2;
|
||||
int i, n;
|
||||
if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
|
||||
UNUSED_PARAMETER(argc);
|
||||
z2 = (char*)sqlite3_value_text(argv[0]);
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
/* Verify that the call to _bytes() does not invalidate the _text() pointer */
|
||||
@@ -274,17 +299,17 @@ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
if( z1 ){
|
||||
memcpy(z1, z2, n+1);
|
||||
for(i=0; z1[i]; i++){
|
||||
z1[i] = toupper(z1[i]);
|
||||
z1[i] = (char)sqlite3Toupper(z1[i]);
|
||||
}
|
||||
sqlite3_result_text(context, z1, -1, sqlite3_free);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
char *z1;
|
||||
u8 *z1;
|
||||
const char *z2;
|
||||
int i, n;
|
||||
if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
|
||||
UNUSED_PARAMETER(argc);
|
||||
z2 = (char*)sqlite3_value_text(argv[0]);
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
/* Verify that the call to _bytes() does not invalidate the _text() pointer */
|
||||
@@ -294,9 +319,9 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
if( z1 ){
|
||||
memcpy(z1, z2, n+1);
|
||||
for(i=0; z1[i]; i++){
|
||||
z1[i] = tolower(z1[i]);
|
||||
z1[i] = sqlite3Tolower(z1[i]);
|
||||
}
|
||||
sqlite3_result_text(context, z1, -1, sqlite3_free);
|
||||
sqlite3_result_text(context, (char *)z1, -1, sqlite3_free);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,10 +350,11 @@ static void ifnullFunc(
|
||||
*/
|
||||
static void randomFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
sqlite_int64 r;
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
if( (r<<1)==0 ) r = 0; /* Prevent 0x8000.... as the result so that we */
|
||||
/* can always do abs() of the result */
|
||||
@@ -347,6 +373,7 @@ static void randomBlob(
|
||||
int n;
|
||||
unsigned char *p;
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
n = sqlite3_value_int(argv[0]);
|
||||
if( n<1 ){
|
||||
n = 1;
|
||||
@@ -364,10 +391,11 @@ static void randomBlob(
|
||||
*/
|
||||
static void last_insert_rowid(
|
||||
sqlite3_context *context,
|
||||
int arg,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_result_int64(context, sqlite3_last_insert_rowid(db));
|
||||
}
|
||||
|
||||
@@ -377,10 +405,11 @@ static void last_insert_rowid(
|
||||
*/
|
||||
static void changes(
|
||||
sqlite3_context *context,
|
||||
int arg,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_result_int(context, sqlite3_changes(db));
|
||||
}
|
||||
|
||||
@@ -390,10 +419,11 @@ static void changes(
|
||||
*/
|
||||
static void total_changes(
|
||||
sqlite3_context *context,
|
||||
int arg,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_result_int(context, sqlite3_total_changes(db));
|
||||
}
|
||||
|
||||
@@ -638,10 +668,11 @@ static void likeFunc(
|
||||
*/
|
||||
static void nullifFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
CollSeq *pColl = sqlite3GetFuncCollSeq(context);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
if( sqlite3MemCompare(argv[0], argv[1], pColl)!=0 ){
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}
|
||||
@@ -653,9 +684,10 @@ static void nullifFunc(
|
||||
*/
|
||||
static void versionFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
int NotUsed,
|
||||
sqlite3_value **NotUsed2
|
||||
){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC);
|
||||
}
|
||||
|
||||
@@ -678,12 +710,9 @@ static const char hexdigits[] = {
|
||||
** single-quote escapes.
|
||||
*/
|
||||
static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
if( argc<1 ) return;
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_NULL: {
|
||||
sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
@@ -731,6 +760,12 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
z[j] = 0;
|
||||
sqlite3_result_text(context, z, j, sqlite3_free);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||||
sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,6 +783,7 @@ static void hexFunc(
|
||||
const unsigned char *pBlob;
|
||||
char *zHex, *z;
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
pBlob = sqlite3_value_blob(argv[0]);
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
assert( pBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */
|
||||
@@ -773,11 +809,12 @@ static void zeroblobFunc(
|
||||
){
|
||||
i64 n;
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
n = sqlite3_value_int64(argv[0]);
|
||||
if( n>SQLITE_MAX_LENGTH ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else{
|
||||
sqlite3_result_zeroblob(context, n);
|
||||
sqlite3_result_zeroblob(context, (int)n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,12 +841,22 @@ static void replaceFunc(
|
||||
int i, j; /* Loop counters */
|
||||
|
||||
assert( argc==3 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
zStr = sqlite3_value_text(argv[0]);
|
||||
if( zStr==0 ) return;
|
||||
nStr = sqlite3_value_bytes(argv[0]);
|
||||
assert( zStr==sqlite3_value_text(argv[0]) ); /* No encoding change */
|
||||
zPattern = sqlite3_value_text(argv[1]);
|
||||
if( zPattern==0 || zPattern[0]==0 ) return;
|
||||
if( zPattern==0 ){
|
||||
assert( sqlite3_value_type(argv[1])==SQLITE_NULL
|
||||
|| sqlite3_context_db_handle(context)->mallocFailed );
|
||||
return;
|
||||
}
|
||||
if( zPattern[0]==0 ){
|
||||
assert( sqlite3_value_type(argv[1])!=SQLITE_NULL );
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
return;
|
||||
}
|
||||
nPattern = sqlite3_value_bytes(argv[1]);
|
||||
assert( zPattern==sqlite3_value_text(argv[1]) ); /* No encoding change */
|
||||
zRep = sqlite3_value_text(argv[2]);
|
||||
@@ -869,8 +916,8 @@ static void trimFunc(
|
||||
int nIn; /* Number of bytes in input */
|
||||
int flags; /* 1: trimleft 2: trimright 3: trim */
|
||||
int i; /* Loop counter */
|
||||
unsigned char *aLen; /* Length of each character in zCharSet */
|
||||
unsigned char **azChar; /* Individual characters in zCharSet */
|
||||
unsigned char *aLen = 0; /* Length of each character in zCharSet */
|
||||
unsigned char **azChar = 0; /* Individual characters in zCharSet */
|
||||
int nChar; /* Number of characters in zCharSet */
|
||||
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
||||
@@ -882,7 +929,7 @@ static void trimFunc(
|
||||
assert( zIn==sqlite3_value_text(argv[0]) );
|
||||
if( argc==1 ){
|
||||
static const unsigned char lenOne[] = { 1 };
|
||||
static const unsigned char *azOne[] = { (u8*)" " };
|
||||
static unsigned char * const azOne[] = { (u8*)" " };
|
||||
nChar = 1;
|
||||
aLen = (u8*)lenOne;
|
||||
azChar = (unsigned char **)azOne;
|
||||
@@ -903,7 +950,7 @@ static void trimFunc(
|
||||
for(z=zCharSet, nChar=0; *z; nChar++){
|
||||
azChar[nChar] = (unsigned char *)z;
|
||||
SQLITE_SKIP_UTF8(z);
|
||||
aLen[nChar] = z - azChar[nChar];
|
||||
aLen[nChar] = (u8)(z - azChar[nChar]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -911,7 +958,7 @@ static void trimFunc(
|
||||
flags = SQLITE_PTR_TO_INT(sqlite3_user_data(context));
|
||||
if( flags & 1 ){
|
||||
while( nIn>0 ){
|
||||
int len;
|
||||
int len = 0;
|
||||
for(i=0; i<nChar; i++){
|
||||
len = aLen[i];
|
||||
if( memcmp(zIn, azChar[i], len)==0 ) break;
|
||||
@@ -923,7 +970,7 @@ static void trimFunc(
|
||||
}
|
||||
if( flags & 2 ){
|
||||
while( nIn>0 ){
|
||||
int len;
|
||||
int len = 0;
|
||||
for(i=0; i<nChar; i++){
|
||||
len = aLen[i];
|
||||
if( len<=nIn && memcmp(&zIn[nIn-len],azChar[i],len)==0 ) break;
|
||||
@@ -939,6 +986,7 @@ static void trimFunc(
|
||||
sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
/*
|
||||
** Compute the soundex encoding of a word.
|
||||
@@ -964,10 +1012,10 @@ static void soundexFunc(
|
||||
assert( argc==1 );
|
||||
zIn = (u8*)sqlite3_value_text(argv[0]);
|
||||
if( zIn==0 ) zIn = (u8*)"";
|
||||
for(i=0; zIn[i] && !isalpha(zIn[i]); i++){}
|
||||
for(i=0; zIn[i] && !sqlite3Isalpha(zIn[i]); i++){}
|
||||
if( zIn[i] ){
|
||||
u8 prevcode = iCode[zIn[i]&0x7f];
|
||||
zResult[0] = toupper(zIn[i]);
|
||||
zResult[0] = sqlite3Toupper(zIn[i]);
|
||||
for(j=1; j<4 && zIn[i]; i++){
|
||||
int code = iCode[zIn[i]&0x7f];
|
||||
if( code>0 ){
|
||||
@@ -1040,6 +1088,7 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
SumCtx *p;
|
||||
int type;
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
type = sqlite3_value_numeric_type(argv[0]);
|
||||
if( p && type!=SQLITE_NULL ){
|
||||
@@ -1049,10 +1098,10 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
p->rSum += v;
|
||||
if( (p->approx|p->overflow)==0 ){
|
||||
i64 iNewSum = p->iSum + v;
|
||||
int s1 = p->iSum >> (sizeof(i64)*8-1);
|
||||
int s2 = v >> (sizeof(i64)*8-1);
|
||||
int s3 = iNewSum >> (sizeof(i64)*8-1);
|
||||
p->overflow = (s1&s2&~s3) | (~s1&~s2&s3);
|
||||
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;
|
||||
}
|
||||
}else{
|
||||
@@ -1084,7 +1133,8 @@ static void avgFinalize(sqlite3_context *context){
|
||||
static void totalFinalize(sqlite3_context *context){
|
||||
SumCtx *p;
|
||||
p = sqlite3_aggregate_context(context, 0);
|
||||
sqlite3_result_double(context, p ? p->rSum : 0.0);
|
||||
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
sqlite3_result_double(context, p ? p->rSum : (double)0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1115,9 +1165,14 @@ static void countFinalize(sqlite3_context *context){
|
||||
/*
|
||||
** Routines to implement min() and max() aggregate functions.
|
||||
*/
|
||||
static void minmaxStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
static void minmaxStep(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Mem *pArg = (Mem *)argv[0];
|
||||
Mem *pBest;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
|
||||
@@ -1166,8 +1221,9 @@ static void groupConcatStep(
|
||||
const char *zVal;
|
||||
StrAccum *pAccum;
|
||||
const char *zSep;
|
||||
int nVal, nSep, i;
|
||||
if( argc==0 || sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
int nVal, nSep;
|
||||
assert( argc==1 || argc==2 );
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
|
||||
|
||||
if( pAccum ){
|
||||
@@ -1175,22 +1231,18 @@ static void groupConcatStep(
|
||||
pAccum->useMalloc = 1;
|
||||
pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
|
||||
if( pAccum->nChar ){
|
||||
if( argc>1 ){
|
||||
zSep = (char*)sqlite3_value_text(argv[argc-1]);
|
||||
nSep = sqlite3_value_bytes(argv[argc-1]);
|
||||
if( argc==2 ){
|
||||
zSep = (char*)sqlite3_value_text(argv[1]);
|
||||
nSep = sqlite3_value_bytes(argv[1]);
|
||||
}else{
|
||||
zSep = ",";
|
||||
nSep = 1;
|
||||
}
|
||||
sqlite3StrAccumAppend(pAccum, zSep, nSep);
|
||||
}
|
||||
i = 0;
|
||||
do{
|
||||
zVal = (char*)sqlite3_value_text(argv[i]);
|
||||
nVal = sqlite3_value_bytes(argv[i]);
|
||||
sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
i++;
|
||||
}while( i<argc-1 );
|
||||
zVal = (char*)sqlite3_value_text(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
}
|
||||
}
|
||||
static void groupConcatFinalize(sqlite3_context *context){
|
||||
@@ -1214,108 +1266,9 @@ static void groupConcatFinalize(sqlite3_context *context){
|
||||
** external linkage.
|
||||
*/
|
||||
void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
|
||||
static const struct {
|
||||
char *zName;
|
||||
signed char nArg;
|
||||
u8 argType; /* 1: 0, 2: 1, 3: 2,... N: N-1. */
|
||||
u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */
|
||||
u8 needCollSeq;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
|
||||
} aFuncs[] = {
|
||||
{ "min", -1, 0, SQLITE_UTF8, 1, minmaxFunc },
|
||||
{ "min", 0, 0, SQLITE_UTF8, 1, 0 },
|
||||
{ "max", -1, 1, SQLITE_UTF8, 1, minmaxFunc },
|
||||
{ "max", 0, 1, SQLITE_UTF8, 1, 0 },
|
||||
{ "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc },
|
||||
{ "length", 1, 0, SQLITE_UTF8, 0, lengthFunc },
|
||||
{ "substr", 2, 0, SQLITE_UTF8, 0, substrFunc },
|
||||
{ "substr", 3, 0, SQLITE_UTF8, 0, substrFunc },
|
||||
{ "abs", 1, 0, SQLITE_UTF8, 0, absFunc },
|
||||
{ "round", 1, 0, SQLITE_UTF8, 0, roundFunc },
|
||||
{ "round", 2, 0, SQLITE_UTF8, 0, roundFunc },
|
||||
{ "upper", 1, 0, SQLITE_UTF8, 0, upperFunc },
|
||||
{ "lower", 1, 0, SQLITE_UTF8, 0, lowerFunc },
|
||||
{ "coalesce", -1, 0, SQLITE_UTF8, 0, ifnullFunc },
|
||||
{ "coalesce", 0, 0, SQLITE_UTF8, 0, 0 },
|
||||
{ "coalesce", 1, 0, SQLITE_UTF8, 0, 0 },
|
||||
{ "hex", 1, 0, SQLITE_UTF8, 0, hexFunc },
|
||||
{ "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc },
|
||||
{ "random", -1, 0, SQLITE_UTF8, 0, randomFunc },
|
||||
{ "randomblob", 1, 0, SQLITE_UTF8, 0, randomBlob },
|
||||
{ "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
|
||||
{ "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},
|
||||
{ "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc },
|
||||
{ "last_insert_rowid", 0, 0, SQLITE_UTF8, 0, last_insert_rowid },
|
||||
{ "changes", 0, 0, SQLITE_UTF8, 0, changes },
|
||||
{ "total_changes", 0, 0, SQLITE_UTF8, 0, total_changes },
|
||||
{ "replace", 3, 0, SQLITE_UTF8, 0, replaceFunc },
|
||||
{ "ltrim", 1, 1, SQLITE_UTF8, 0, trimFunc },
|
||||
{ "ltrim", 2, 1, SQLITE_UTF8, 0, trimFunc },
|
||||
{ "rtrim", 1, 2, SQLITE_UTF8, 0, trimFunc },
|
||||
{ "rtrim", 2, 2, SQLITE_UTF8, 0, trimFunc },
|
||||
{ "trim", 1, 3, SQLITE_UTF8, 0, trimFunc },
|
||||
{ "trim", 2, 3, SQLITE_UTF8, 0, trimFunc },
|
||||
{ "zeroblob", 1, 0, SQLITE_UTF8, 0, zeroblobFunc },
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
{ "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc},
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
{ "load_extension", 1, 0, SQLITE_UTF8, 0, loadExt },
|
||||
{ "load_extension", 2, 0, SQLITE_UTF8, 0, loadExt },
|
||||
#endif
|
||||
};
|
||||
static const struct {
|
||||
char *zName;
|
||||
signed char nArg;
|
||||
u8 argType;
|
||||
u8 needCollSeq;
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**);
|
||||
void (*xFinalize)(sqlite3_context*);
|
||||
} aAggs[] = {
|
||||
{ "min", 1, 0, 1, minmaxStep, minMaxFinalize },
|
||||
{ "max", 1, 1, 1, minmaxStep, minMaxFinalize },
|
||||
{ "sum", 1, 0, 0, sumStep, sumFinalize },
|
||||
{ "total", 1, 0, 0, sumStep, totalFinalize },
|
||||
{ "avg", 1, 0, 0, sumStep, avgFinalize },
|
||||
{ "count", 0, 0, 0, countStep, countFinalize },
|
||||
{ "count", 1, 0, 0, countStep, countFinalize },
|
||||
{ "group_concat", -1, 0, 0, groupConcatStep, groupConcatFinalize },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
|
||||
void *pArg;
|
||||
u8 argType = aFuncs[i].argType;
|
||||
pArg = SQLITE_INT_TO_PTR(argType);
|
||||
sqlite3CreateFunc(db, aFuncs[i].zName, aFuncs[i].nArg,
|
||||
aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0);
|
||||
if( aFuncs[i].needCollSeq ){
|
||||
FuncDef *pFunc = sqlite3FindFunction(db, aFuncs[i].zName,
|
||||
strlen(aFuncs[i].zName), aFuncs[i].nArg, aFuncs[i].eTextRep, 0);
|
||||
if( pFunc && aFuncs[i].needCollSeq ){
|
||||
pFunc->needCollSeq = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions(db);
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_PARSER
|
||||
sqlite3AttachFunctions(db);
|
||||
#endif
|
||||
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
|
||||
void *pArg = SQLITE_INT_TO_PTR(aAggs[i].argType);
|
||||
sqlite3CreateFunc(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8,
|
||||
pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize);
|
||||
if( aAggs[i].needCollSeq ){
|
||||
FuncDef *pFunc = sqlite3FindFunction( db, aAggs[i].zName,
|
||||
strlen(aAggs[i].zName), aAggs[i].nArg, SQLITE_UTF8, 0);
|
||||
if( pFunc && aAggs[i].needCollSeq ){
|
||||
pFunc->needCollSeq = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3RegisterDateTimeFunctions(db);
|
||||
if( !db->mallocFailed ){
|
||||
int rc = sqlite3_overload_function(db, "MATCH", 2);
|
||||
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
|
||||
@@ -1326,19 +1279,15 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
|
||||
#ifdef SQLITE_SSE
|
||||
(void)sqlite3SseFunctions(db);
|
||||
#endif
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
sqlite3RegisterLikeFunctions(db, 1);
|
||||
#else
|
||||
sqlite3RegisterLikeFunctions(db, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the LIKEOPT flag on the 2-argument function with the given name.
|
||||
*/
|
||||
static void setLikeOptFlag(sqlite3 *db, const char *zName, int flagVal){
|
||||
static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
|
||||
FuncDef *pDef;
|
||||
pDef = sqlite3FindFunction(db, zName, strlen(zName), 2, SQLITE_UTF8, 0);
|
||||
pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
|
||||
2, SQLITE_UTF8, 0);
|
||||
if( pDef ){
|
||||
pDef->flags = flagVal;
|
||||
}
|
||||
@@ -1397,3 +1346,93 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
*pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** All all of the FuncDef structures in the aBuiltinFunc[] array above
|
||||
** to the global function hash table. This occurs at start-time (as
|
||||
** a consequence of calling sqlite3_initialize()).
|
||||
**
|
||||
** After this routine runs
|
||||
*/
|
||||
void sqlite3RegisterGlobalFunctions(void){
|
||||
/*
|
||||
** The following array holds FuncDef structures for all of the functions
|
||||
** defined in this file.
|
||||
**
|
||||
** The array cannot be constant since changes are made to the
|
||||
** FuncDef.pHash elements at start-time. The elements of this array
|
||||
** are read-only after initialization is complete.
|
||||
*/
|
||||
static SQLITE_WSD FuncDef aBuiltinFunc[] = {
|
||||
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
|
||||
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
|
||||
FUNCTION(rtrim, 1, 2, 0, trimFunc ),
|
||||
FUNCTION(rtrim, 2, 2, 0, trimFunc ),
|
||||
FUNCTION(trim, 1, 3, 0, trimFunc ),
|
||||
FUNCTION(trim, 2, 3, 0, trimFunc ),
|
||||
FUNCTION(min, -1, 0, 1, minmaxFunc ),
|
||||
FUNCTION(min, 0, 0, 1, 0 ),
|
||||
AGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize ),
|
||||
FUNCTION(max, -1, 1, 1, minmaxFunc ),
|
||||
FUNCTION(max, 0, 1, 1, 0 ),
|
||||
AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ),
|
||||
FUNCTION(typeof, 1, 0, 0, typeofFunc ),
|
||||
FUNCTION(length, 1, 0, 0, lengthFunc ),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
FUNCTION(abs, 1, 0, 0, absFunc ),
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
FUNCTION(round, 1, 0, 0, roundFunc ),
|
||||
FUNCTION(round, 2, 0, 0, roundFunc ),
|
||||
#endif
|
||||
FUNCTION(upper, 1, 0, 0, upperFunc ),
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, -1, 0, 0, ifnullFunc ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
FUNCTION(ifnull, 2, 0, 1, ifnullFunc ),
|
||||
FUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
|
||||
FUNCTION(quote, 1, 0, 0, quoteFunc ),
|
||||
FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
|
||||
FUNCTION(changes, 0, 0, 0, changes ),
|
||||
FUNCTION(total_changes, 0, 0, 0, total_changes ),
|
||||
FUNCTION(replace, 3, 0, 0, replaceFunc ),
|
||||
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
FUNCTION(load_extension, 1, 0, 0, loadExt ),
|
||||
FUNCTION(load_extension, 2, 0, 0, loadExt ),
|
||||
#endif
|
||||
AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
|
||||
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
|
||||
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
|
||||
AGGREGATE(count, 0, 0, 0, countStep, countFinalize ),
|
||||
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
|
||||
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
|
||||
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#else
|
||||
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
||||
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
||||
#endif
|
||||
};
|
||||
|
||||
int i;
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc);
|
||||
|
||||
for(i=0; i<ArraySize(aBuiltinFunc); i++){
|
||||
sqlite3FuncDefInsert(pHash, &aFunc[i]);
|
||||
}
|
||||
sqlite3RegisterDateTimeFunctions();
|
||||
}
|
||||
|
||||
+122
-9
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** This file contains definitions of global variables and contants.
|
||||
**
|
||||
** $Id: global.c,v 1.4 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: global.c,v 1.12 2009/02/05 16:31:46 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -62,16 +62,129 @@ const unsigned char sqlite3UpperToLower[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** The following 256 byte lookup table is used to support SQLites built-in
|
||||
** equivalents to the following standard library functions:
|
||||
**
|
||||
** isspace() 0x01
|
||||
** isalpha() 0x02
|
||||
** isdigit() 0x04
|
||||
** isalnum() 0x06
|
||||
** isxdigit() 0x08
|
||||
** toupper() 0x20
|
||||
**
|
||||
** Bit 0x20 is set if the mapped character requires translation to upper
|
||||
** case. i.e. if the character is a lower-case ASCII character.
|
||||
** If x is a lower-case ASCII character, then its upper-case equivalent
|
||||
** is (x - 0x20). Therefore toupper() can be implemented as:
|
||||
**
|
||||
** (x & ~(map[x]&0x20))
|
||||
**
|
||||
** Standard function tolower() is implemented using the sqlite3UpperToLower[]
|
||||
** array. tolower() is used more often than toupper() by SQLite.
|
||||
**
|
||||
** SQLite's versions are identical to the standard versions assuming a
|
||||
** locale of "C". They are implemented as macros in sqliteInt.h.
|
||||
*/
|
||||
#ifdef SQLITE_ASCII
|
||||
const unsigned char sqlite3CtypeMap[256] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
|
||||
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
|
||||
0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
|
||||
|
||||
0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
|
||||
0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, /* 58..5f XYZ[\]^_ */
|
||||
0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
|
||||
0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80..87 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 88..8f ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 90..97 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 98..9f ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a0..a7 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a8..af ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b0..b7 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b8..bf ........ */
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c0..c7 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c8..cf ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d0..d7 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d8..df ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e0..e7 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e8..ef ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f0..f7 ........ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* f8..ff ........ */
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The following singleton contains the global configuration for
|
||||
** the SQLite library.
|
||||
*/
|
||||
struct Sqlite3Config sqlite3Config = {
|
||||
1, /* bMemstat */
|
||||
1, /* bCoreMutex */
|
||||
1, /* bFullMutex */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
100, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
/* Other fields all default to zero */
|
||||
SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */
|
||||
1, /* bCoreMutex */
|
||||
SQLITE_THREADSAFE==1, /* bFullMutex */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
100, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
{0,0,0,0,0,0,0,0}, /* m */
|
||||
{0,0,0,0,0,0,0,0,0}, /* mutex */
|
||||
{0,0,0,0,0,0,0,0,0,0,0}, /* pcache */
|
||||
(void*)0, /* pHeap */
|
||||
0, /* nHeap */
|
||||
0, 0, /* mnHeap, mxHeap */
|
||||
(void*)0, /* pScratch */
|
||||
0, /* szScratch */
|
||||
0, /* nScratch */
|
||||
(void*)0, /* pPage */
|
||||
0, /* szPage */
|
||||
0, /* nPage */
|
||||
0, /* mxParserStack */
|
||||
0, /* sharedCacheEnabled */
|
||||
/* All the rest need to always be zero */
|
||||
0, /* isInit */
|
||||
0, /* inProgress */
|
||||
0, /* isMallocInit */
|
||||
0, /* pInitMutex */
|
||||
0, /* nRefInitMutex */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Hash table for global functions - functions common to all
|
||||
** database connections. After initialization, this table is
|
||||
** read-only.
|
||||
*/
|
||||
SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
|
||||
|
||||
/*
|
||||
** The value of the "pending" byte must be 0x40000000 (1 byte past the
|
||||
** 1-gibabyte boundary) in a compatible database. SQLite never uses
|
||||
** the database page that contains the pending byte. It never attempts
|
||||
** to read or write that page. The pending byte page is set assign
|
||||
** for use by the VFS layers as space for managing file locks.
|
||||
**
|
||||
** During testing, it is often desirable to move the pending byte to
|
||||
** a different position in the file. This allows code that has to
|
||||
** deal with the pending byte to run on files that are much smaller
|
||||
** than 1 GiB. The sqlite3_test_control() interface can be used to
|
||||
** move the pending byte.
|
||||
**
|
||||
** IMPORTANT: Changing the pending byte to any value other than
|
||||
** 0x40000000 results in an incompatible database file format!
|
||||
** Changing the pending byte during operating results in undefined
|
||||
** and dileterious behavior.
|
||||
*/
|
||||
int sqlite3PendingByte = 0x40000000;
|
||||
|
||||
+11
-131
@@ -12,7 +12,7 @@
|
||||
** This is the implementation of generic hash-tables
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.c,v 1.30 2008/06/20 14:59:51 danielk1977 Exp $
|
||||
** $Id: hash.c,v 1.33 2009/01/09 01:12:28 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <assert.h>
|
||||
@@ -21,22 +21,12 @@
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER,
|
||||
** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING. The value of keyClass
|
||||
** determines what kind of key the hash table will use. "copyKey" is
|
||||
** true if the hash table should make its own private copy of keys and
|
||||
** false if it should just use the supplied pointer. CopyKey only makes
|
||||
** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored
|
||||
** for other key classes.
|
||||
** "copyKey" is true if the hash table should make its own private
|
||||
** copy of keys and false if it should just use the supplied pointer.
|
||||
*/
|
||||
void sqlite3HashInit(Hash *pNew, int keyClass, int copyKey){
|
||||
void sqlite3HashInit(Hash *pNew, int copyKey){
|
||||
assert( pNew!=0 );
|
||||
assert( keyClass>=SQLITE_HASH_STRING && keyClass<=SQLITE_HASH_BINARY );
|
||||
pNew->keyClass = keyClass;
|
||||
#if 0
|
||||
if( keyClass==SQLITE_HASH_POINTER || keyClass==SQLITE_HASH_INT ) copyKey = 0;
|
||||
#endif
|
||||
pNew->copyKey = copyKey;
|
||||
pNew->copyKey = copyKey!=0;
|
||||
pNew->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
@@ -58,7 +48,7 @@ void sqlite3HashClear(Hash *pH){
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
if( pH->copyKey ){
|
||||
sqlite3_free(elem->pKey);
|
||||
}
|
||||
sqlite3_free(elem);
|
||||
@@ -67,40 +57,13 @@ void sqlite3HashClear(Hash *pH){
|
||||
pH->count = 0;
|
||||
}
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_INT
|
||||
*/
|
||||
static int intHash(const void *pKey, int nKey){
|
||||
return nKey ^ (nKey<<8) ^ (nKey>>8);
|
||||
}
|
||||
static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
return n2 - n1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
|
||||
*/
|
||||
static int ptrHash(const void *pKey, int nKey){
|
||||
uptr x = Addr(pKey);
|
||||
return x ^ (x<<8) ^ (x>>8);
|
||||
}
|
||||
static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( pKey1==pKey2 ) return 0;
|
||||
if( pKey1<pKey2 ) return -1;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_STRING
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
if( nKey<=0 ) nKey = strlen(z);
|
||||
if( nKey<=0 ) nKey = sqlite3Strlen30(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
||||
nKey--;
|
||||
@@ -112,79 +75,6 @@ static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
return sqlite3StrNICmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
|
||||
*/
|
||||
static int binHash(const void *pKey, int nKey){
|
||||
int h = 0;
|
||||
const char *z = (const char *)pKey;
|
||||
while( nKey-- > 0 ){
|
||||
h = (h<<3) ^ h ^ *(z++);
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return memcmp(pKey1,pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** The C syntax in this function definition may be unfamilar to some
|
||||
** programmers, so we provide the following additional explanation:
|
||||
**
|
||||
** The name of the function is "hashFunction". The function takes a
|
||||
** single parameter "keyClass". The return value of hashFunction()
|
||||
** is a pointer to another function. Specifically, the return value
|
||||
** of hashFunction() is a pointer to a function that takes two parameters
|
||||
** with types "const void*" and "int" and returns an "int".
|
||||
*/
|
||||
static int (*hashFunction(int keyClass))(const void*,int){
|
||||
#if 0 /* HASH_INT and HASH_POINTER are never used */
|
||||
switch( keyClass ){
|
||||
case SQLITE_HASH_INT: return &intHash;
|
||||
case SQLITE_HASH_POINTER: return &ptrHash;
|
||||
case SQLITE_HASH_STRING: return &strHash;
|
||||
case SQLITE_HASH_BINARY: return &binHash;;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if( keyClass==SQLITE_HASH_STRING ){
|
||||
return &strHash;
|
||||
}else{
|
||||
assert( keyClass==SQLITE_HASH_BINARY );
|
||||
return &binHash;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** For help in interpreted the obscure C code in the function definition,
|
||||
** see the header comment on the previous function.
|
||||
*/
|
||||
static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
|
||||
#if 0 /* HASH_INT and HASH_POINTER are never used */
|
||||
switch( keyClass ){
|
||||
case SQLITE_HASH_INT: return &intCompare;
|
||||
case SQLITE_HASH_POINTER: return &ptrCompare;
|
||||
case SQLITE_HASH_STRING: return &strCompare;
|
||||
case SQLITE_HASH_BINARY: return &binCompare;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if( keyClass==SQLITE_HASH_STRING ){
|
||||
return &strCompare;
|
||||
}else{
|
||||
assert( keyClass==SQLITE_HASH_BINARY );
|
||||
return &binCompare;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Link an element into the hash table
|
||||
*/
|
||||
@@ -219,7 +109,6 @@ static void insertElement(
|
||||
static void rehash(Hash *pH, int new_size){
|
||||
struct _ht *new_ht; /* The new hash table */
|
||||
HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
#ifdef SQLITE_MALLOC_SOFT_LIMIT
|
||||
if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){
|
||||
@@ -241,9 +130,8 @@ static void rehash(Hash *pH, int new_size){
|
||||
sqlite3_free(pH->ht);
|
||||
pH->ht = new_ht;
|
||||
pH->htsize = new_size;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
|
||||
int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
|
||||
int h = strHash(elem->pKey, elem->nKey) & (new_size-1);
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
@@ -261,15 +149,13 @@ static HashElem *findElementGivenHash(
|
||||
){
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
int (*xCompare)(const void*,int,const void*,int); /* comparison function */
|
||||
|
||||
if( pH->ht ){
|
||||
struct _ht *pEntry = &pH->ht[h];
|
||||
elem = pEntry->chain;
|
||||
count = pEntry->count;
|
||||
xCompare = compareFunction(pH->keyClass);
|
||||
while( count-- && elem ){
|
||||
if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
|
||||
if( strCompare(elem->pKey,elem->nKey,pKey,nKey)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
@@ -323,12 +209,9 @@ static void removeElementGivenHash(
|
||||
HashElem *sqlite3HashFindElem(const Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
HashElem *elem; /* The element that matches key */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
if( pH==0 || pH->ht==0 ) return 0;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
h = (*xHash)(pKey,nKey);
|
||||
h = strHash(pKey,nKey);
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h % pH->htsize);
|
||||
return elem;
|
||||
}
|
||||
@@ -363,12 +246,9 @@ void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
HashElem *new_elem; /* New element added to the pH */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( pH!=0 );
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
hraw = (*xHash)(pKey, nKey);
|
||||
hraw = strHash(pKey, nKey);
|
||||
if( pH->htsize ){
|
||||
h = hraw % pH->htsize;
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
|
||||
+9
-32
@@ -12,7 +12,7 @@
|
||||
** This is the header file for the generic hash-table implemenation
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.h,v 1.11 2007/09/04 14:31:47 danielk1977 Exp $
|
||||
** $Id: hash.h,v 1.12 2008/10/10 17:41:29 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_HASH_H_
|
||||
#define _SQLITE_HASH_H_
|
||||
@@ -30,14 +30,13 @@ typedef struct HashElem HashElem;
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct Hash {
|
||||
char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */
|
||||
char copyKey; /* True if copy of key made on insert */
|
||||
int count; /* Number of entries in this table */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
HashElem *first; /* The first element of the array */
|
||||
struct _ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
HashElem *chain; /* Pointer to first entry with this hash */
|
||||
unsigned int copyKey: 1; /* True if copy of key made on insert */
|
||||
unsigned int htsize : 31; /* Number of buckets in the hash table */
|
||||
unsigned int count; /* Number of entries in this table */
|
||||
HashElem *first; /* The first element of the array */
|
||||
struct _ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
@@ -53,32 +52,10 @@ struct HashElem {
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** There are 4 different modes of operation for a hash table:
|
||||
**
|
||||
** SQLITE_HASH_INT nKey is used as the key and pKey is ignored.
|
||||
**
|
||||
** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored.
|
||||
**
|
||||
** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long
|
||||
** (including the null-terminator, if any). Case
|
||||
** is ignored in comparisons.
|
||||
**
|
||||
** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long.
|
||||
** memcmp() is used to compare keys.
|
||||
**
|
||||
** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY
|
||||
** if the copyKey parameter to HashInit is 1.
|
||||
*/
|
||||
/* #define SQLITE_HASH_INT 1 // NOT USED */
|
||||
/* #define SQLITE_HASH_POINTER 2 // NOT USED */
|
||||
#define SQLITE_HASH_STRING 3
|
||||
#define SQLITE_HASH_BINARY 4
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void sqlite3HashInit(Hash*, int keytype, int copyKey);
|
||||
void sqlite3HashInit(Hash*, int copyKey);
|
||||
void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqlite3HashFind(const Hash*, const void *pKey, int nKey);
|
||||
HashElem *sqlite3HashFindElem(const Hash*, const void *pKey, int nKey);
|
||||
|
||||
+45
-46
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.248 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: insert.c,v 1.256 2008/12/10 21:19:57 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -165,7 +165,7 @@ static int autoIncBegin(
|
||||
Table *pTab /* The table we are writing to */
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
if( pTab->autoInc ){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Db *pDb = &pParse->db->aDb[iDb];
|
||||
int iCur = pParse->nTab;
|
||||
@@ -216,7 +216,7 @@ static void autoIncEnd(
|
||||
Table *pTab, /* Table we are inserting into */
|
||||
int memId /* Memory cell holding the maximum rowid */
|
||||
){
|
||||
if( pTab->autoInc ){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
int iCur = pParse->nTab;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Db *pDb = &pParse->db->aDb[iDb];
|
||||
@@ -388,14 +388,14 @@ void sqlite3Insert(
|
||||
int appendFlag = 0; /* True if the insert is likely to be an append */
|
||||
|
||||
/* Register allocations */
|
||||
int regFromSelect; /* Base register for data coming from SELECT */
|
||||
int regFromSelect = 0;/* Base register for data coming from SELECT */
|
||||
int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */
|
||||
int regRowCount = 0; /* Memory cell used for the row counter */
|
||||
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; /* Register recording end of SELECT data */
|
||||
int regEof = 0; /* Register recording end of SELECT data */
|
||||
int *aRegIdx = 0; /* One register allocated to each index */
|
||||
|
||||
|
||||
@@ -405,6 +405,7 @@ void sqlite3Insert(
|
||||
#endif
|
||||
|
||||
db = pParse->db;
|
||||
memset(&dest, 0, sizeof(dest));
|
||||
if( pParse->nErr || db->mallocFailed ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
@@ -430,7 +431,7 @@ void sqlite3Insert(
|
||||
** inserted into is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0);
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_INSERT, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
@@ -532,7 +533,7 @@ void sqlite3Insert(
|
||||
VdbeComment((v, "Jump over SELECT coroutine"));
|
||||
|
||||
/* Resolve the expressions in the SELECT statement and execute it. */
|
||||
rc = sqlite3Select(pParse, pSelect, &dest, 0, 0, 0);
|
||||
rc = sqlite3Select(pParse, pSelect, &dest);
|
||||
if( rc || pParse->nErr || db->mallocFailed ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
@@ -572,24 +573,24 @@ void sqlite3Insert(
|
||||
** goto L
|
||||
** M: ...
|
||||
*/
|
||||
int regRec; /* Register to hold packed record */
|
||||
int regRowid; /* Register to hold temp table ROWID */
|
||||
int addrTop; /* Label "L" */
|
||||
int addrIf; /* Address of jump to M */
|
||||
int regRec; /* Register to hold packed record */
|
||||
int regTempRowid; /* Register to hold temp table ROWID */
|
||||
int addrTop; /* Label "L" */
|
||||
int addrIf; /* Address of jump to M */
|
||||
|
||||
srcTab = pParse->nTab++;
|
||||
regRec = sqlite3GetTempReg(pParse);
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
regTempRowid = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
|
||||
addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
||||
sqlite3VdbeJumpHere(v, addrIf);
|
||||
sqlite3ReleaseTempReg(pParse, regRec);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
sqlite3ReleaseTempReg(pParse, regTempRowid);
|
||||
}
|
||||
}else{
|
||||
/* This is the case if the data for the INSERT is coming from a VALUES
|
||||
@@ -602,7 +603,7 @@ void sqlite3Insert(
|
||||
assert( useTempTable==0 );
|
||||
nColumn = pList ? pList->nExpr : 0;
|
||||
for(i=0; i<nColumn; i++){
|
||||
if( sqlite3ExprResolveNames(&sNC, pList->a[i].pExpr) ){
|
||||
if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
@@ -690,7 +691,6 @@ void sqlite3Insert(
|
||||
/* If this is not a view, open the table and and all indices */
|
||||
if( !isView ){
|
||||
int nIdx;
|
||||
int i;
|
||||
|
||||
baseCur = pParse->nTab;
|
||||
nIdx = sqlite3OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite);
|
||||
@@ -746,7 +746,7 @@ void sqlite3Insert(
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
if( triggers_exist & TRIGGER_BEFORE ){
|
||||
int regRowid;
|
||||
int regTrigRowid;
|
||||
int regCols;
|
||||
int regRec;
|
||||
|
||||
@@ -756,19 +756,19 @@ void sqlite3Insert(
|
||||
** we do not know what the unique ID will be (because the insert has
|
||||
** not happened yet) so we substitute a rowid of -1
|
||||
*/
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
regTrigRowid = sqlite3GetTempReg(pParse);
|
||||
if( keyColumn<0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid);
|
||||
}else if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regTrigRowid);
|
||||
}else{
|
||||
int j1;
|
||||
assert( pSelect==0 ); /* Otherwise useTempTable is true */
|
||||
sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid);
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid);
|
||||
sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regTrigRowid);
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regTrigRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, regTrigRowid);
|
||||
}
|
||||
|
||||
/* Cannot have triggers on a virtual table. If it were possible,
|
||||
@@ -807,9 +807,9 @@ void sqlite3Insert(
|
||||
if( !isView ){
|
||||
sqlite3TableAffinityStr(v, pTab);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regTrigRowid);
|
||||
sqlite3ReleaseTempReg(pParse, regRec);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
sqlite3ReleaseTempReg(pParse, regTrigRowid);
|
||||
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
|
||||
|
||||
/* Fire BEFORE or INSTEAD OF triggers */
|
||||
@@ -936,7 +936,6 @@ void sqlite3Insert(
|
||||
regIns,
|
||||
aRegIdx,
|
||||
0,
|
||||
0,
|
||||
(triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1,
|
||||
appendFlag
|
||||
);
|
||||
@@ -992,7 +991,7 @@ void sqlite3Insert(
|
||||
if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
|
||||
}
|
||||
|
||||
insert_cleanup:
|
||||
@@ -1095,7 +1094,8 @@ void sqlite3GenerateConstraintChecks(
|
||||
Vdbe *v;
|
||||
int nCol;
|
||||
int onError;
|
||||
int j1, j2, j3; /* Addresses of jump instructions */
|
||||
int j1; /* Addresss of jump instruction */
|
||||
int j2 = 0, j3; /* Addresses of jump instructions */
|
||||
int regData; /* Register containing first data column */
|
||||
int iCur;
|
||||
Index *pIdx;
|
||||
@@ -1272,26 +1272,26 @@ void sqlite3GenerateConstraintChecks(
|
||||
case OE_Fail: {
|
||||
int j, n1, n2;
|
||||
char zErrMsg[200];
|
||||
sqlite3_snprintf(sizeof(zErrMsg), zErrMsg,
|
||||
sqlite3_snprintf(ArraySize(zErrMsg), zErrMsg,
|
||||
pIdx->nColumn>1 ? "columns " : "column ");
|
||||
n1 = strlen(zErrMsg);
|
||||
for(j=0; j<pIdx->nColumn && n1<sizeof(zErrMsg)-30; j++){
|
||||
n1 = sqlite3Strlen30(zErrMsg);
|
||||
for(j=0; j<pIdx->nColumn && n1<ArraySize(zErrMsg)-30; j++){
|
||||
char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
|
||||
n2 = strlen(zCol);
|
||||
n2 = sqlite3Strlen30(zCol);
|
||||
if( j>0 ){
|
||||
sqlite3_snprintf(sizeof(zErrMsg)-n1, &zErrMsg[n1], ", ");
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1], ", ");
|
||||
n1 += 2;
|
||||
}
|
||||
if( n1+n2>sizeof(zErrMsg)-30 ){
|
||||
sqlite3_snprintf(sizeof(zErrMsg)-n1, &zErrMsg[n1], "...");
|
||||
if( n1+n2>ArraySize(zErrMsg)-30 ){
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1], "...");
|
||||
n1 += 3;
|
||||
break;
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zErrMsg)-n1, &zErrMsg[n1], "%s", zCol);
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1], "%s", zCol);
|
||||
n1 += n2;
|
||||
}
|
||||
}
|
||||
sqlite3_snprintf(sizeof(zErrMsg)-n1, &zErrMsg[n1],
|
||||
sqlite3_snprintf(ArraySize(zErrMsg)-n1, &zErrMsg[n1],
|
||||
pIdx->nColumn>1 ? " are not unique" : " is not unique");
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErrMsg,0);
|
||||
break;
|
||||
@@ -1328,7 +1328,6 @@ void sqlite3CompleteInsertion(
|
||||
int baseCur, /* Index of a read/write cursor pointing at pTab */
|
||||
int regRowid, /* Range of content */
|
||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||
int rowidChng, /* True if the record number will change */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int newIdx, /* Index of NEW table for triggers. -1 if none */
|
||||
int appendBias /* True if this is likely to be an append */
|
||||
@@ -1337,7 +1336,7 @@ void sqlite3CompleteInsertion(
|
||||
Vdbe *v;
|
||||
int nIdx;
|
||||
Index *pIdx;
|
||||
int pik_flags;
|
||||
u8 pik_flags;
|
||||
int regData;
|
||||
int regRec;
|
||||
|
||||
@@ -1385,7 +1384,7 @@ void sqlite3CompleteInsertion(
|
||||
int sqlite3OpenTableAndIndices(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pTab, /* Table to be opened */
|
||||
int baseCur, /* Cursor number assigned to the table */
|
||||
int baseCur, /* Cursor number assigned to the table */
|
||||
int op /* OP_OpenRead or OP_OpenWrite */
|
||||
){
|
||||
int i;
|
||||
@@ -1535,7 +1534,7 @@ static int xferOptimization(
|
||||
return 0; /* tab1 must not have triggers */
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( pDest->isVirtual ){
|
||||
if( pDest->tabFlags & TF_Virtual ){
|
||||
return 0; /* tab1 must not be a virtual table */
|
||||
}
|
||||
#endif
|
||||
@@ -1570,7 +1569,7 @@ static int xferOptimization(
|
||||
if( pSelect->pPrior ){
|
||||
return 0; /* SELECT may not be a compound query */
|
||||
}
|
||||
if( pSelect->isDistinct ){
|
||||
if( pSelect->selFlags & SF_Distinct ){
|
||||
return 0; /* SELECT may not be DISTINCT */
|
||||
}
|
||||
pEList = pSelect->pEList;
|
||||
@@ -1596,7 +1595,7 @@ static int xferOptimization(
|
||||
return 0; /* tab1 and tab2 may not be the same table */
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( pSrc->isVirtual ){
|
||||
if( pSrc->tabFlags & TF_Virtual ){
|
||||
return 0; /* tab2 must not be a virtual table */
|
||||
}
|
||||
#endif
|
||||
@@ -1687,7 +1686,7 @@ static int xferOptimization(
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
|
||||
}else{
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
|
||||
assert( pDest->autoInc==0 );
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
|
||||
|
||||
+4
-3
@@ -10,7 +10,7 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** @(#) $Id: journal.c,v 1.8 2008/05/01 18:01:47 drh Exp $
|
||||
** @(#) $Id: journal.c,v 1.9 2009/01/20 17:06:27 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
@@ -28,7 +28,7 @@
|
||||
**
|
||||
** 1) The in-memory representation grows too large for the allocated
|
||||
** buffer, or
|
||||
** 2) The xSync() method is called.
|
||||
** 2) The sqlite3JournalCreate() function is called.
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -95,8 +95,9 @@ static int jrnlRead(
|
||||
JournalFile *p = (JournalFile *)pJfd;
|
||||
if( p->pReal ){
|
||||
rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
}else if( (iAmt+iOfst)>p->iSize ){
|
||||
rc = SQLITE_IOERR_SHORT_READ;
|
||||
}else{
|
||||
assert( iAmt+iOfst<=p->iSize );
|
||||
memcpy(zBuf, &p->zBuf[iOfst], iAmt);
|
||||
}
|
||||
return rc;
|
||||
|
||||
+3
-4
@@ -14,11 +14,10 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: legacy.c,v 1.29 2008/08/02 03:50:39 drh Exp $
|
||||
** $Id: legacy.c,v 1.31 2009/01/20 16:53:40 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Execute SQL code. Return one of the SQLITE_ success/failure
|
||||
@@ -115,7 +114,7 @@ int sqlite3_exec(
|
||||
if( rc!=SQLITE_SCHEMA ){
|
||||
nRetry = 0;
|
||||
zSql = zLeftover;
|
||||
while( isspace((unsigned char)zSql[0]) ) zSql++;
|
||||
while( sqlite3Isspace(zSql[0]) ) zSql++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -131,7 +130,7 @@ exec_out:
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){
|
||||
int nErrMsg = 1 + strlen(sqlite3_errmsg(db));
|
||||
int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db));
|
||||
*pzErrMsg = sqlite3Malloc(nErrMsg);
|
||||
if( *pzErrMsg ){
|
||||
memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg);
|
||||
|
||||
+60
-26
@@ -12,7 +12,7 @@
|
||||
** This file contains code used to dynamically load extensions into
|
||||
** the SQLite library.
|
||||
**
|
||||
** $Id: loadext.c,v 1.53 2008/08/02 03:50:39 drh Exp $
|
||||
** $Id: loadext.c,v 1.58 2009/01/20 16:53:40 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#ifndef SQLITE_CORE
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "sqlite3ext.h"
|
||||
#include "sqliteInt.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
@@ -124,7 +123,11 @@
|
||||
*/
|
||||
static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_aggregate_context,
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_aggregate_count,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
sqlite3_bind_blob,
|
||||
sqlite3_bind_double,
|
||||
sqlite3_bind_int,
|
||||
@@ -179,7 +182,11 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_errmsg,
|
||||
sqlite3_errmsg16,
|
||||
sqlite3_exec,
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_expired,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
sqlite3_finalize,
|
||||
sqlite3_free,
|
||||
sqlite3_free_table,
|
||||
@@ -219,10 +226,18 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_snprintf,
|
||||
sqlite3_step,
|
||||
sqlite3_table_column_metadata,
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_thread_cleanup,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
sqlite3_total_changes,
|
||||
sqlite3_trace,
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_transfer_bindings,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
sqlite3_update_hook,
|
||||
sqlite3_user_data,
|
||||
sqlite3_value_blob,
|
||||
@@ -273,7 +288,7 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_file_control,
|
||||
sqlite3_memory_highwater,
|
||||
sqlite3_memory_used,
|
||||
#ifdef SQLITE_MUTEX_NOOP
|
||||
#ifdef SQLITE_MUTEX_OMIT
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
@@ -466,17 +481,33 @@ static const sqlite3_api_routines sqlite3Apis = { 0 };
|
||||
** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER
|
||||
** mutex must be held while accessing this list.
|
||||
*/
|
||||
static struct {
|
||||
int nExt; /* Number of entries in aExt[] */
|
||||
void **aExt; /* Pointers to the extension init functions */
|
||||
} autoext = { 0, 0 };
|
||||
typedef struct sqlite3AutoExtList sqlite3AutoExtList;
|
||||
static SQLITE_WSD struct sqlite3AutoExtList {
|
||||
int nExt; /* Number of entries in aExt[] */
|
||||
void (**aExt)(void); /* Pointers to the extension init functions */
|
||||
} sqlite3Autoext = { 0, 0 };
|
||||
|
||||
/* The "wsdAutoext" macro will resolve to the autoextension
|
||||
** state vector. If writable static data is unsupported on the target,
|
||||
** we have to locate the state vector at run-time. In the more common
|
||||
** case where writable static data is supported, wsdStat can refer directly
|
||||
** to the "sqlite3Autoext" state vector declared above.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
# define wsdAutoextInit \
|
||||
sqlite3AutoExtList *x = &GLOBAL(sqlite3AutoExtList,sqlite3Autoext)
|
||||
# define wsdAutoext x[0]
|
||||
#else
|
||||
# define wsdAutoextInit
|
||||
# define wsdAutoext sqlite3Autoext
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register a statically linked extension that is automatically
|
||||
** loaded by every new database connection.
|
||||
*/
|
||||
int sqlite3_auto_extension(void *xInit){
|
||||
int sqlite3_auto_extension(void (*xInit)(void)){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
@@ -486,23 +517,24 @@ int sqlite3_auto_extension(void *xInit){
|
||||
#endif
|
||||
{
|
||||
int i;
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
wsdAutoextInit;
|
||||
sqlite3_mutex_enter(mutex);
|
||||
for(i=0; i<autoext.nExt; i++){
|
||||
if( autoext.aExt[i]==xInit ) break;
|
||||
for(i=0; i<wsdAutoext.nExt; i++){
|
||||
if( wsdAutoext.aExt[i]==xInit ) break;
|
||||
}
|
||||
if( i==autoext.nExt ){
|
||||
int nByte = (autoext.nExt+1)*sizeof(autoext.aExt[0]);
|
||||
void **aNew;
|
||||
aNew = sqlite3_realloc(autoext.aExt, nByte);
|
||||
if( i==wsdAutoext.nExt ){
|
||||
int nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]);
|
||||
void (**aNew)(void);
|
||||
aNew = sqlite3_realloc(wsdAutoext.aExt, nByte);
|
||||
if( aNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
autoext.aExt = aNew;
|
||||
autoext.aExt[autoext.nExt] = xInit;
|
||||
autoext.nExt++;
|
||||
wsdAutoext.aExt = aNew;
|
||||
wsdAutoext.aExt[wsdAutoext.nExt] = xInit;
|
||||
wsdAutoext.nExt++;
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
@@ -519,13 +551,14 @@ void sqlite3_reset_auto_extension(void){
|
||||
if( sqlite3_initialize()==SQLITE_OK )
|
||||
#endif
|
||||
{
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
wsdAutoextInit;
|
||||
sqlite3_mutex_enter(mutex);
|
||||
sqlite3_free(autoext.aExt);
|
||||
autoext.aExt = 0;
|
||||
autoext.nExt = 0;
|
||||
sqlite3_free(wsdAutoext.aExt);
|
||||
wsdAutoext.aExt = 0;
|
||||
wsdAutoext.nExt = 0;
|
||||
sqlite3_mutex_leave(mutex);
|
||||
}
|
||||
}
|
||||
@@ -539,22 +572,23 @@ int sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
int rc = SQLITE_OK;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
|
||||
if( autoext.nExt==0 ){
|
||||
wsdAutoextInit;
|
||||
if( wsdAutoext.nExt==0 ){
|
||||
/* Common case: early out without every having to acquire a mutex */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
for(i=0; go; i++){
|
||||
char *zErrmsg = 0;
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
if( i>=autoext.nExt ){
|
||||
if( i>=wsdAutoext.nExt ){
|
||||
xInit = 0;
|
||||
go = 0;
|
||||
}else{
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
autoext.aExt[i];
|
||||
wsdAutoext.aExt[i];
|
||||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){
|
||||
|
||||
+323
-152
@@ -14,10 +14,9 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.486 2008/08/04 20:13:27 drh Exp $
|
||||
** $Id: main.c,v 1.528 2009/02/05 16:31:46 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
# include "fts3.h"
|
||||
@@ -25,11 +24,16 @@
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
# include "rtree.h"
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
# include "sqliteicu.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The version of the library
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
const char sqlite3_version[] = SQLITE_VERSION;
|
||||
#endif
|
||||
const char *sqlite3_libversion(void){ return sqlite3_version; }
|
||||
int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; }
|
||||
int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
|
||||
@@ -57,69 +61,137 @@ char *sqlite3_temp_directory = 0;
|
||||
** Initialize SQLite.
|
||||
**
|
||||
** This routine must be called to initialize the memory allocation,
|
||||
** VFS, and mutex subsystesms prior to doing any serious work with
|
||||
** VFS, and mutex subsystems prior to doing any serious work with
|
||||
** SQLite. But as long as you do not compile with SQLITE_OMIT_AUTOINIT
|
||||
** this routine will be called automatically by key routines such as
|
||||
** sqlite3_open().
|
||||
**
|
||||
** This routine is a no-op except on its very first call for the process,
|
||||
** or for the first call after a call to sqlite3_shutdown.
|
||||
**
|
||||
** The first thread to call this routine runs the initialization to
|
||||
** completion. If subsequent threads call this routine before the first
|
||||
** thread has finished the initialization process, then the subsequent
|
||||
** threads must block until the first thread finishes with the initialization.
|
||||
**
|
||||
** The first thread might call this routine recursively. Recursive
|
||||
** calls to this routine should not block, of course. Otherwise the
|
||||
** initialization process would never complete.
|
||||
**
|
||||
** Let X be the first thread to enter this routine. Let Y be some other
|
||||
** thread. Then while the initial invocation of this routine by X is
|
||||
** incomplete, it is required that:
|
||||
**
|
||||
** * Calls to this routine from Y must block until the outer-most
|
||||
** call by X completes.
|
||||
**
|
||||
** * Recursive calls to this routine from thread X return immediately
|
||||
** without blocking.
|
||||
*/
|
||||
int sqlite3_initialize(void){
|
||||
static int inProgress = 0;
|
||||
int rc;
|
||||
sqlite3_mutex *pMaster; /* The main static mutex */
|
||||
int rc; /* Result code */
|
||||
|
||||
/* If SQLite is already initialized, this call is a no-op. */
|
||||
if( sqlite3Config.isInit ) return SQLITE_OK;
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
rc = sqlite3_wsd_init(4096, 24);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Make sure the mutex system is initialized. */
|
||||
/* If SQLite is already completely initialized, then this call
|
||||
** to sqlite3_initialize() should be a no-op. But the initialization
|
||||
** must be complete. So isInit must not be set until the very end
|
||||
** of this routine.
|
||||
*/
|
||||
if( sqlite3GlobalConfig.isInit ) return SQLITE_OK;
|
||||
|
||||
/* Make sure the mutex subsystem is initialized. If unable to
|
||||
** initialize the mutex subsystem, return early with the error.
|
||||
** If the system is so sick that we are unable to allocate a mutex,
|
||||
** there is not much SQLite is going to be able to do.
|
||||
**
|
||||
** The mutex subsystem must take care of serializing its own
|
||||
** initialization.
|
||||
*/
|
||||
rc = sqlite3MutexInit();
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Initialize the malloc() system and the recursive pInitMutex mutex.
|
||||
** This operation is protected by the STATIC_MASTER mutex. Note that
|
||||
** MutexAlloc() is called for a static mutex prior to initializing the
|
||||
** malloc subsystem - this implies that the allocation of a static
|
||||
** mutex must not require support from the malloc subsystem.
|
||||
*/
|
||||
pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
if( !sqlite3GlobalConfig.isMallocInit ){
|
||||
rc = sqlite3MallocInit();
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
/* Initialize the malloc() system and the recursive pInitMutex mutex.
|
||||
** This operation is protected by the STATIC_MASTER mutex.
|
||||
*/
|
||||
sqlite3_mutex *pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
if( !sqlite3Config.isMallocInit ){
|
||||
rc = sqlite3MallocInit();
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Config.isMallocInit = 1;
|
||||
if( !sqlite3Config.pInitMutex ){
|
||||
sqlite3Config.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
if( sqlite3Config.bCoreMutex && !sqlite3Config.pInitMutex ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3GlobalConfig.isMallocInit = 1;
|
||||
if( !sqlite3GlobalConfig.pInitMutex ){
|
||||
sqlite3GlobalConfig.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(pMaster);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3GlobalConfig.nRefInitMutex++;
|
||||
}
|
||||
sqlite3_mutex_leave(pMaster);
|
||||
|
||||
/* Enter the recursive pInitMutex mutex. After doing so, if the
|
||||
** sqlite3Config.isInit flag is true, then some other thread has
|
||||
** finished doing the initialization. If the inProgress flag is
|
||||
** true, then this function is being called recursively from within
|
||||
** the sqlite3_os_init() call below. In either case, exit early.
|
||||
*/
|
||||
sqlite3_mutex_enter(sqlite3Config.pInitMutex);
|
||||
if( sqlite3Config.isInit || inProgress ){
|
||||
sqlite3_mutex_leave(sqlite3Config.pInitMutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3StatusReset();
|
||||
inProgress = 1;
|
||||
rc = sqlite3_os_init();
|
||||
inProgress = 0;
|
||||
sqlite3Config.isInit = (rc==SQLITE_OK ? 1 : 0);
|
||||
sqlite3_mutex_leave(sqlite3Config.pInitMutex);
|
||||
/* If unable to initialize the malloc subsystem, then return early.
|
||||
** There is little hope of getting SQLite to run if the malloc
|
||||
** subsystem cannot be initialized.
|
||||
*/
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check NaN support. */
|
||||
/* Do the rest of the initialization under the recursive mutex so
|
||||
** that we will be able to handle recursive calls into
|
||||
** sqlite3_initialize(). The recursive calls normally come through
|
||||
** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other
|
||||
** recursive calls might also be possible.
|
||||
*/
|
||||
sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex);
|
||||
if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
sqlite3GlobalConfig.inProgress = 1;
|
||||
memset(pHash, 0, sizeof(sqlite3GlobalFunctions));
|
||||
sqlite3RegisterGlobalFunctions();
|
||||
rc = sqlite3_os_init();
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PcacheInitialize();
|
||||
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
|
||||
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
|
||||
}
|
||||
sqlite3GlobalConfig.inProgress = 0;
|
||||
sqlite3GlobalConfig.isInit = (rc==SQLITE_OK ? 1 : 0);
|
||||
}
|
||||
sqlite3_mutex_leave(sqlite3GlobalConfig.pInitMutex);
|
||||
|
||||
/* Go back under the static mutex and clean up the recursive
|
||||
** mutex to prevent a resource leak.
|
||||
*/
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
sqlite3GlobalConfig.nRefInitMutex--;
|
||||
if( sqlite3GlobalConfig.nRefInitMutex<=0 ){
|
||||
assert( sqlite3GlobalConfig.nRefInitMutex==0 );
|
||||
sqlite3_mutex_free(sqlite3GlobalConfig.pInitMutex);
|
||||
sqlite3GlobalConfig.pInitMutex = 0;
|
||||
}
|
||||
sqlite3_mutex_leave(pMaster);
|
||||
|
||||
/* The following is just a sanity check to make sure SQLite has
|
||||
** been compiled correctly. It is important to run this code, but
|
||||
** we don't want to run it too often and soak up CPU cycles for no
|
||||
** reason. So we run it once during initialization.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
/* This section of code's only "output" is via assert() statements. */
|
||||
if ( rc==SQLITE_OK ){
|
||||
u64 x = (((u64)1)<<63)-1;
|
||||
@@ -129,6 +201,7 @@ int sqlite3_initialize(void){
|
||||
memcpy(&y, &x, 8);
|
||||
assert( sqlite3IsNaN(y) );
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
@@ -141,19 +214,14 @@ int sqlite3_initialize(void){
|
||||
** routine is not threadsafe. Not by a long shot.
|
||||
*/
|
||||
int sqlite3_shutdown(void){
|
||||
sqlite3_mutex_free(sqlite3Config.pInitMutex);
|
||||
sqlite3Config.pInitMutex = 0;
|
||||
sqlite3Config.isMallocInit = 0;
|
||||
if( sqlite3Config.isInit ){
|
||||
sqlite3GlobalConfig.isMallocInit = 0;
|
||||
sqlite3PcacheShutdown();
|
||||
if( sqlite3GlobalConfig.isInit ){
|
||||
sqlite3_os_end();
|
||||
}
|
||||
if( sqlite3Config.m.xShutdown ){
|
||||
sqlite3MallocEnd();
|
||||
}
|
||||
if( sqlite3Config.mutex.xMutexEnd ){
|
||||
sqlite3MutexEnd();
|
||||
}
|
||||
sqlite3Config.isInit = 0;
|
||||
sqlite3MallocEnd();
|
||||
sqlite3MutexEnd();
|
||||
sqlite3GlobalConfig.isInit = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -172,84 +240,106 @@ int sqlite3_config(int op, ...){
|
||||
|
||||
/* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while
|
||||
** the SQLite library is in use. */
|
||||
if( sqlite3Config.isInit ) return SQLITE_MISUSE;
|
||||
if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE;
|
||||
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
|
||||
/* Mutex configuration options are only available in a threadsafe
|
||||
** compile.
|
||||
*/
|
||||
#if SQLITE_THREADSAFE
|
||||
case SQLITE_CONFIG_SINGLETHREAD: {
|
||||
/* Disable all mutexing */
|
||||
sqlite3Config.bCoreMutex = 0;
|
||||
sqlite3Config.bFullMutex = 0;
|
||||
sqlite3GlobalConfig.bCoreMutex = 0;
|
||||
sqlite3GlobalConfig.bFullMutex = 0;
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_MULTITHREAD: {
|
||||
/* Disable mutexing of database connections */
|
||||
/* Enable mutexing of core data structures */
|
||||
sqlite3Config.bCoreMutex = 1;
|
||||
sqlite3Config.bFullMutex = 0;
|
||||
sqlite3GlobalConfig.bCoreMutex = 1;
|
||||
sqlite3GlobalConfig.bFullMutex = 0;
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_SERIALIZED: {
|
||||
/* Enable all mutexing */
|
||||
sqlite3Config.bCoreMutex = 1;
|
||||
sqlite3Config.bFullMutex = 1;
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_MALLOC: {
|
||||
/* Specify an alternative malloc implementation */
|
||||
sqlite3Config.m = *va_arg(ap, sqlite3_mem_methods*);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETMALLOC: {
|
||||
/* Retrieve the current malloc() implementation */
|
||||
if( sqlite3Config.m.xMalloc==0 ) sqlite3MemSetDefault();
|
||||
*va_arg(ap, sqlite3_mem_methods*) = sqlite3Config.m;
|
||||
sqlite3GlobalConfig.bCoreMutex = 1;
|
||||
sqlite3GlobalConfig.bFullMutex = 1;
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_MUTEX: {
|
||||
/* Specify an alternative mutex implementation */
|
||||
sqlite3Config.mutex = *va_arg(ap, sqlite3_mutex_methods*);
|
||||
sqlite3GlobalConfig.mutex = *va_arg(ap, sqlite3_mutex_methods*);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETMUTEX: {
|
||||
/* Retrieve the current mutex implementation */
|
||||
*va_arg(ap, sqlite3_mutex_methods*) = sqlite3Config.mutex;
|
||||
*va_arg(ap, sqlite3_mutex_methods*) = sqlite3GlobalConfig.mutex;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
case SQLITE_CONFIG_MALLOC: {
|
||||
/* Specify an alternative malloc implementation */
|
||||
sqlite3GlobalConfig.m = *va_arg(ap, sqlite3_mem_methods*);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETMALLOC: {
|
||||
/* Retrieve the current malloc() implementation */
|
||||
if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault();
|
||||
*va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m;
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_MEMSTATUS: {
|
||||
/* Enable or disable the malloc status collection */
|
||||
sqlite3Config.bMemstat = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_SCRATCH: {
|
||||
/* Designate a buffer for scratch memory space */
|
||||
sqlite3Config.pScratch = va_arg(ap, void*);
|
||||
sqlite3Config.szScratch = va_arg(ap, int);
|
||||
sqlite3Config.nScratch = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.pScratch = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.szScratch = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.nScratch = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_PAGECACHE: {
|
||||
/* Designate a buffer for scratch memory space */
|
||||
sqlite3Config.pPage = va_arg(ap, void*);
|
||||
sqlite3Config.szPage = va_arg(ap, int);
|
||||
sqlite3Config.nPage = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.pPage = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.szPage = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.nPage = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_PCACHE: {
|
||||
/* Specify an alternative malloc implementation */
|
||||
sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_GETPCACHE: {
|
||||
if( sqlite3GlobalConfig.pcache.xInit==0 ){
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
*va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||
case SQLITE_CONFIG_HEAP: {
|
||||
/* Designate a buffer for heap memory space */
|
||||
sqlite3Config.pHeap = va_arg(ap, void*);
|
||||
sqlite3Config.nHeap = va_arg(ap, int);
|
||||
sqlite3Config.mnReq = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.pHeap = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.nHeap = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.mnReq = va_arg(ap, int);
|
||||
|
||||
if( sqlite3Config.pHeap==0 ){
|
||||
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
|
||||
** back to its default implementation when sqlite3_initialize() is
|
||||
** run.
|
||||
*/
|
||||
memset(&sqlite3Config.m, 0, sizeof(sqlite3Config.m));
|
||||
memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m));
|
||||
}else{
|
||||
/* The heap pointer is not NULL, then install one of the
|
||||
** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor
|
||||
@@ -257,27 +347,19 @@ int sqlite3_config(int op, ...){
|
||||
** the default case and return an error.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_MEMSYS3
|
||||
sqlite3Config.m = *sqlite3MemGetMemsys3();
|
||||
sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3();
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_MEMSYS5
|
||||
sqlite3Config.m = *sqlite3MemGetMemsys5();
|
||||
sqlite3GlobalConfig.m = *sqlite3MemGetMemsys5();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_ENABLE_MEMSYS6)
|
||||
case SQLITE_CONFIG_CHUNKALLOC: {
|
||||
sqlite3Config.nSmall = va_arg(ap, int);
|
||||
sqlite3Config.m = *sqlite3MemGetMemsys6();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case SQLITE_CONFIG_LOOKASIDE: {
|
||||
sqlite3Config.szLookaside = va_arg(ap, int);
|
||||
sqlite3Config.nLookaside = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.szLookaside = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.nLookaside = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -306,26 +388,37 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
if( db->lookaside.nOut ){
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
if( sz<0 ) sz = 0;
|
||||
/* Free any existing lookaside buffer for this handle before
|
||||
** allocating a new one so we don't have to have space for
|
||||
** both at the same time.
|
||||
*/
|
||||
if( db->lookaside.bMalloced ){
|
||||
sqlite3_free(db->lookaside.pStart);
|
||||
}
|
||||
/* The size of a lookaside slot needs to be larger than a pointer
|
||||
** to be useful.
|
||||
*/
|
||||
if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0;
|
||||
if( cnt<0 ) cnt = 0;
|
||||
sz = (sz+7)&~7;
|
||||
if( pBuf==0 ){
|
||||
if( sz==0 || cnt==0 ){
|
||||
sz = 0;
|
||||
pStart = 0;
|
||||
}else if( pBuf==0 ){
|
||||
sz = (sz + 7)&~7;
|
||||
sqlite3BeginBenignMalloc();
|
||||
pStart = sqlite3Malloc( sz*cnt );
|
||||
sqlite3EndBenignMalloc();
|
||||
}else{
|
||||
sz = sz&~7;
|
||||
pStart = pBuf;
|
||||
}
|
||||
if( db->lookaside.bMalloced ){
|
||||
sqlite3_free(db->lookaside.pStart);
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
db->lookaside.pFree = 0;
|
||||
db->lookaside.sz = sz;
|
||||
db->lookaside.bMalloced = pBuf==0;
|
||||
db->lookaside.sz = (u16)sz;
|
||||
if( pStart ){
|
||||
int i;
|
||||
LookasideSlot *p;
|
||||
assert( sz > sizeof(LookasideSlot*) );
|
||||
p = (LookasideSlot*)pStart;
|
||||
for(i=cnt-1; i>=0; i--){
|
||||
p->pNext = db->lookaside.pFree;
|
||||
@@ -334,13 +427,22 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
}
|
||||
db->lookaside.pEnd = p;
|
||||
db->lookaside.bEnabled = 1;
|
||||
db->lookaside.bMalloced = pBuf==0 ?1:0;
|
||||
}else{
|
||||
db->lookaside.pEnd = 0;
|
||||
db->lookaside.bEnabled = 0;
|
||||
db->lookaside.bMalloced = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the mutex associated with a database connection.
|
||||
*/
|
||||
sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){
|
||||
return db->mutex;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configuration settings for an individual database connection
|
||||
*/
|
||||
@@ -365,16 +467,6 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Routine needed to support the testcase() macro.
|
||||
*/
|
||||
#ifdef SQLITE_COVERAGE_TEST
|
||||
void sqlite3Coverage(int x){
|
||||
static int dummy = 0;
|
||||
dummy += x;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Return true if the buffer z[0..n-1] contains all spaces.
|
||||
@@ -428,6 +520,7 @@ static int nocaseCollatingFunc(
|
||||
){
|
||||
int r = sqlite3StrNICmp(
|
||||
(const char *)pKey1, (const char *)pKey2, (nKey1<nKey2)?nKey1:nKey2);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
if( 0==r ){
|
||||
r = nKey1-nKey2;
|
||||
}
|
||||
@@ -455,6 +548,21 @@ int sqlite3_total_changes(sqlite3 *db){
|
||||
return db->nTotalChange;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close all open savepoints. This function only manipulates fields of the
|
||||
** database handle object, it does not close any savepoints that may be open
|
||||
** at the b-tree/pager level.
|
||||
*/
|
||||
void sqlite3CloseSavepoints(sqlite3 *db){
|
||||
while( db->pSavepoint ){
|
||||
Savepoint *pTmp = db->pSavepoint;
|
||||
db->pSavepoint = pTmp->pNext;
|
||||
sqlite3DbFree(db, pTmp);
|
||||
}
|
||||
db->nSavepoint = 0;
|
||||
db->isTransactionSavepoint = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an existing SQLite database
|
||||
*/
|
||||
@@ -491,12 +599,25 @@ int sqlite3_close(sqlite3 *db){
|
||||
/* If there are any outstanding VMs, return SQLITE_BUSY. */
|
||||
if( db->pVdbe ){
|
||||
sqlite3Error(db, SQLITE_BUSY,
|
||||
"Unable to close due to unfinalised statements");
|
||||
"unable to close due to unfinalised statements");
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
assert( sqlite3SafetyCheckSickOrOk(db) );
|
||||
|
||||
for(j=0; j<db->nDb; j++){
|
||||
Btree *pBt = db->aDb[j].pBt;
|
||||
if( pBt && sqlite3BtreeIsInBackup(pBt) ){
|
||||
sqlite3Error(db, SQLITE_BUSY,
|
||||
"unable to close due to unfinished backup operation");
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free any outstanding Savepoint structures. */
|
||||
sqlite3CloseSavepoints(db);
|
||||
|
||||
for(j=0; j<db->nDb; j++){
|
||||
struct Db *pDb = &db->aDb[j];
|
||||
if( pDb->pBt ){
|
||||
@@ -510,14 +631,17 @@ int sqlite3_close(sqlite3 *db){
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
assert( db->nDb<=2 );
|
||||
assert( db->aDb==db->aDbStatic );
|
||||
for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){
|
||||
FuncDef *pFunc, *pNext;
|
||||
for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){
|
||||
pNext = pFunc->pNext;
|
||||
sqlite3DbFree(db, pFunc);
|
||||
for(j=0; j<ArraySize(db->aFunc.a); j++){
|
||||
FuncDef *pNext, *pHash, *p;
|
||||
for(p=db->aFunc.a[j]; p; p=pHash){
|
||||
pHash = p->pHash;
|
||||
while( p ){
|
||||
pNext = p->pNext;
|
||||
sqlite3DbFree(db, p);
|
||||
p = pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){
|
||||
CollSeq *pColl = (CollSeq *)sqliteHashData(i);
|
||||
/* Invoke any destructors registered for collation sequence user data. */
|
||||
@@ -540,7 +664,6 @@ int sqlite3_close(sqlite3 *db){
|
||||
sqlite3HashClear(&db->aModule);
|
||||
#endif
|
||||
|
||||
sqlite3HashClear(&db->aFunc);
|
||||
sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
|
||||
if( db->pErr ){
|
||||
sqlite3ValueFree(db->pErr);
|
||||
@@ -559,6 +682,7 @@ int sqlite3_close(sqlite3 *db){
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
db->magic = SQLITE_MAGIC_CLOSED;
|
||||
sqlite3_mutex_free(db->mutex);
|
||||
assert( db->lookaside.nOut==0 ); /* Fails on a lookaside memory leak */
|
||||
if( db->lookaside.bMalloced ){
|
||||
sqlite3_free(db->lookaside.pStart);
|
||||
}
|
||||
@@ -827,11 +951,11 @@ int sqlite3CreateFunc(
|
||||
** is being overridden/deleted but there are no active VMs, allow the
|
||||
** operation to continue but invalidate all precompiled statements.
|
||||
*/
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 0);
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
|
||||
if( p && p->iPrefEnc==enc && p->nArg==nArg ){
|
||||
if( db->activeVdbeCnt ){
|
||||
sqlite3Error(db, SQLITE_BUSY,
|
||||
"Unable to delete/modify user-function due to active statements");
|
||||
"unable to delete/modify user-function due to active statements");
|
||||
assert( !db->mallocFailed );
|
||||
return SQLITE_BUSY;
|
||||
}else{
|
||||
@@ -839,7 +963,7 @@ int sqlite3CreateFunc(
|
||||
}
|
||||
}
|
||||
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 1);
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1);
|
||||
assert(p || db->mallocFailed);
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
@@ -849,7 +973,7 @@ int sqlite3CreateFunc(
|
||||
p->xStep = xStep;
|
||||
p->xFinalize = xFinal;
|
||||
p->pUserData = pUserData;
|
||||
p->nArg = nArg;
|
||||
p->nArg = (u16)nArg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -1114,6 +1238,9 @@ const char *sqlite3_errmsg(sqlite3 *db){
|
||||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return sqlite3ErrStr(SQLITE_MISUSE);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
return sqlite3ErrStr(SQLITE_NOMEM);
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
@@ -1189,6 +1316,15 @@ int sqlite3_errcode(sqlite3 *db){
|
||||
}
|
||||
return db->errCode & db->errMask;
|
||||
}
|
||||
int sqlite3_extended_errcode(sqlite3 *db){
|
||||
if( db && !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
if( !db || db->mallocFailed ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
return db->errCode;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new collating function for database "db". The name is zName
|
||||
@@ -1229,7 +1365,7 @@ static int createCollation(
|
||||
if( pColl && pColl->xCmp ){
|
||||
if( db->activeVdbeCnt ){
|
||||
sqlite3Error(db, SQLITE_BUSY,
|
||||
"Unable to delete/modify collation sequence due to active statements");
|
||||
"unable to delete/modify collation sequence due to active statements");
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
@@ -1260,7 +1396,7 @@ static int createCollation(
|
||||
pColl->xCmp = xCompare;
|
||||
pColl->pUser = pCtx;
|
||||
pColl->xDel = xDel;
|
||||
pColl->enc = enc2 | (enc & SQLITE_UTF16_ALIGNED);
|
||||
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
|
||||
}
|
||||
sqlite3Error(db, SQLITE_OK, 0);
|
||||
return SQLITE_OK;
|
||||
@@ -1303,11 +1439,11 @@ static const int aHardLimit[] = {
|
||||
#if SQLITE_MAX_VDBE_OP<40
|
||||
# error SQLITE_MAX_VDBE_OP must be at least 40
|
||||
#endif
|
||||
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
|
||||
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
|
||||
#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_ATTACH<0 || SQLITE_MAX_ATTACH>30
|
||||
# error SQLITE_MAX_ATTACH must be between 0 and 30
|
||||
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>30
|
||||
# error SQLITE_MAX_ATTACHED must be between 0 and 30
|
||||
#endif
|
||||
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
|
||||
# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
|
||||
@@ -1315,6 +1451,9 @@ static const int aHardLimit[] = {
|
||||
#if SQLITE_MAX_VARIABLE_NUMBER<1
|
||||
# error SQLITE_MAX_VARIABLE_NUMBER must be at least 1
|
||||
#endif
|
||||
#if SQLITE_MAX_COLUMN>32767
|
||||
# error SQLITE_MAX_COLUMN must not exceed 32767
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -1356,15 +1495,21 @@ static int openDatabase(
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
CollSeq *pColl;
|
||||
int isThreadsafe = 1;
|
||||
int isThreadsafe;
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
if( rc ) return rc;
|
||||
#endif
|
||||
|
||||
if( flags&SQLITE_OPEN_NOMUTEX ){
|
||||
if( sqlite3GlobalConfig.bCoreMutex==0 ){
|
||||
isThreadsafe = 0;
|
||||
}else if( flags & SQLITE_OPEN_NOMUTEX ){
|
||||
isThreadsafe = 0;
|
||||
}else if( flags & SQLITE_OPEN_FULLMUTEX ){
|
||||
isThreadsafe = 1;
|
||||
}else{
|
||||
isThreadsafe = sqlite3GlobalConfig.bFullMutex;
|
||||
}
|
||||
|
||||
/* Remove harmful bits from the flags parameter */
|
||||
@@ -1376,13 +1521,14 @@ static int openDatabase(
|
||||
SQLITE_OPEN_TEMP_JOURNAL |
|
||||
SQLITE_OPEN_SUBJOURNAL |
|
||||
SQLITE_OPEN_MASTER_JOURNAL |
|
||||
SQLITE_OPEN_NOMUTEX
|
||||
SQLITE_OPEN_NOMUTEX |
|
||||
SQLITE_OPEN_FULLMUTEX
|
||||
);
|
||||
|
||||
/* Allocate the sqlite data structure */
|
||||
db = sqlite3MallocZero( sizeof(sqlite3) );
|
||||
if( db==0 ) goto opendb_out;
|
||||
if( sqlite3Config.bFullMutex && isThreadsafe ){
|
||||
if( isThreadsafe ){
|
||||
db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
if( db->mutex==0 ){
|
||||
sqlite3_free(db);
|
||||
@@ -1410,16 +1556,14 @@ static int openDatabase(
|
||||
| SQLITE_LoadExtension
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&db->aCollSeq, 0);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3HashInit(&db->aModule, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashInit(&db->aModule, 0);
|
||||
#endif
|
||||
|
||||
db->pVfs = sqlite3_vfs_find(zVfs);
|
||||
if( !db->pVfs ){
|
||||
rc = SQLITE_ERROR;
|
||||
db->magic = SQLITE_MAGIC_SICK;
|
||||
sqlite3Error(db, rc, "no such vfs: %s", zVfs);
|
||||
goto opendb_out;
|
||||
}
|
||||
@@ -1433,7 +1577,6 @@ static int openDatabase(
|
||||
createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
|
||||
createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
|
||||
if( db->mallocFailed ){
|
||||
db->magic = SQLITE_MAGIC_SICK;
|
||||
goto opendb_out;
|
||||
}
|
||||
db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0);
|
||||
@@ -1455,8 +1598,10 @@ static int openDatabase(
|
||||
flags | SQLITE_OPEN_MAIN_DB,
|
||||
&db->aDb[0].pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_IOERR_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3Error(db, rc, 0);
|
||||
db->magic = SQLITE_MAGIC_SICK;
|
||||
goto opendb_out;
|
||||
}
|
||||
db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
|
||||
@@ -1515,7 +1660,6 @@ static int openDatabase(
|
||||
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
extern int sqlite3IcuInit(sqlite3*);
|
||||
rc = sqlite3IcuInit(db);
|
||||
}
|
||||
#endif
|
||||
@@ -1539,16 +1683,20 @@ static int openDatabase(
|
||||
#endif
|
||||
|
||||
/* Enable the lookaside-malloc subsystem */
|
||||
setupLookaside(db, 0, sqlite3Config.szLookaside, sqlite3Config.nLookaside);
|
||||
setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
|
||||
sqlite3GlobalConfig.nLookaside);
|
||||
|
||||
opendb_out:
|
||||
if( db ){
|
||||
assert( db->mutex!=0 || isThreadsafe==0 || sqlite3Config.bFullMutex==0 );
|
||||
assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
}
|
||||
if( SQLITE_NOMEM==(rc = sqlite3_errcode(db)) ){
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
db->magic = SQLITE_MAGIC_SICK;
|
||||
}
|
||||
*ppDb = db;
|
||||
return sqlite3ApiExit(0, rc);
|
||||
@@ -1713,6 +1861,7 @@ int sqlite3_collation_needed16(
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
|
||||
#ifndef SQLITE_OMIT_GLOBALRECOVER
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** This function is now an anachronism. It used to be used to recover from a
|
||||
** malloc() failure, but SQLite now does this automatically.
|
||||
@@ -1721,6 +1870,7 @@ int sqlite3_global_recover(void){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Test to see whether or not the database connection is in autocommit
|
||||
@@ -1745,6 +1895,7 @@ int sqlite3Corrupt(void){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** This is a convenience routine that makes sure that all thread-specific
|
||||
** data for this thread has been deallocated.
|
||||
@@ -1754,6 +1905,7 @@ int sqlite3Corrupt(void){
|
||||
*/
|
||||
void sqlite3_thread_cleanup(void){
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return meta information about a specific column of a database table.
|
||||
@@ -1834,7 +1986,7 @@ int sqlite3_table_column_metadata(
|
||||
zCollSeq = pCol->zColl;
|
||||
notnull = pCol->notNull!=0;
|
||||
primarykey = pCol->isPrimKey!=0;
|
||||
autoinc = pTab->iPKey==iCol && pTab->autoInc;
|
||||
autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0;
|
||||
}else{
|
||||
zDataType = "INTEGER";
|
||||
primarykey = 1;
|
||||
@@ -1998,6 +2150,25 @@ int sqlite3_test_control(int op, ...){
|
||||
sqlite3BenignMallocHooks(xBenignBegin, xBenignEnd);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_test_control(PENDING_BYTE, unsigned int X)
|
||||
**
|
||||
** Set the PENDING byte to the value in the argument, if X>0.
|
||||
** Make no changes if X==0. Return the value of the pending byte
|
||||
** as it existing before this routine was called.
|
||||
**
|
||||
** IMPORTANT: Changing the PENDING byte from 0x40000000 results in
|
||||
** an incompatible database file format. Changing the PENDING byte
|
||||
** while any database connection is open results in undefined and
|
||||
** dileterious behavior.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_PENDING_BYTE: {
|
||||
unsigned int newVal = va_arg(ap, unsigned int);
|
||||
rc = sqlite3PendingByte;
|
||||
if( newVal ) sqlite3PendingByte = newVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
+151
-91
@@ -12,11 +12,10 @@
|
||||
**
|
||||
** Memory allocation functions used throughout sqlite.
|
||||
**
|
||||
** $Id: malloc.c,v 1.34 2008/08/05 17:53:23 drh Exp $
|
||||
** $Id: malloc.c,v 1.56 2009/02/17 18:37:29 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** This routine runs when the memory allocator sees that the
|
||||
@@ -25,9 +24,10 @@
|
||||
*/
|
||||
static void softHeapLimitEnforcer(
|
||||
void *NotUsed,
|
||||
sqlite3_int64 inUse,
|
||||
sqlite3_int64 NotUsed2,
|
||||
int allocSize
|
||||
){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_release_memory(allocSize);
|
||||
}
|
||||
|
||||
@@ -45,11 +45,11 @@ void sqlite3_soft_heap_limit(int n){
|
||||
}
|
||||
sqlite3_initialize();
|
||||
if( iLimit>0 ){
|
||||
sqlite3_memory_alarm(softHeapLimitEnforcer, 0, iLimit);
|
||||
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, iLimit);
|
||||
}else{
|
||||
sqlite3_memory_alarm(0, 0, 0);
|
||||
sqlite3MemoryAlarm(0, 0, 0);
|
||||
}
|
||||
overage = sqlite3_memory_used() - n;
|
||||
overage = (int)(sqlite3_memory_used() - (i64)n);
|
||||
if( overage>0 ){
|
||||
sqlite3_release_memory(overage);
|
||||
}
|
||||
@@ -62,10 +62,14 @@ void sqlite3_soft_heap_limit(int n){
|
||||
*/
|
||||
int sqlite3_release_memory(int n){
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
int nRet = sqlite3VdbeReleaseMemory(n);
|
||||
nRet += sqlite3PagerReleaseMemory(n-nRet);
|
||||
int nRet = 0;
|
||||
#if 0
|
||||
nRet += sqlite3VdbeReleaseMemory(n);
|
||||
#endif
|
||||
nRet += sqlite3PcacheReleaseMemory(n-nRet);
|
||||
return nRet;
|
||||
#else
|
||||
UNUSED_PARAMETER(n);
|
||||
return SQLITE_OK;
|
||||
#endif
|
||||
}
|
||||
@@ -73,7 +77,11 @@ int sqlite3_release_memory(int n){
|
||||
/*
|
||||
** State information local to the memory allocation subsystem.
|
||||
*/
|
||||
static struct {
|
||||
static SQLITE_WSD struct Mem0Global {
|
||||
/* Number of free pages for scratch and page-cache memory */
|
||||
u32 nScratchFree;
|
||||
u32 nPageFree;
|
||||
|
||||
sqlite3_mutex *mutex; /* Mutex to serialize access */
|
||||
|
||||
/*
|
||||
@@ -89,65 +97,65 @@ static struct {
|
||||
int alarmBusy;
|
||||
|
||||
/*
|
||||
** Pointers to the end of sqlite3Config.pScratch and
|
||||
** sqlite3Config.pPage to a block of memory that records
|
||||
** Pointers to the end of sqlite3GlobalConfig.pScratch and
|
||||
** sqlite3GlobalConfig.pPage to a block of memory that records
|
||||
** which pages are available.
|
||||
*/
|
||||
u32 *aScratchFree;
|
||||
u32 *aPageFree;
|
||||
} mem0 = { 62560955, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
/* Number of free pages for scratch and page-cache memory */
|
||||
u32 nScratchFree;
|
||||
u32 nPageFree;
|
||||
} mem0;
|
||||
#define mem0 GLOBAL(struct Mem0Global, mem0)
|
||||
|
||||
/*
|
||||
** Initialize the memory allocation subsystem.
|
||||
*/
|
||||
int sqlite3MallocInit(void){
|
||||
if( sqlite3Config.m.xMalloc==0 ){
|
||||
if( sqlite3GlobalConfig.m.xMalloc==0 ){
|
||||
sqlite3MemSetDefault();
|
||||
}
|
||||
memset(&mem0, 0, sizeof(mem0));
|
||||
if( sqlite3Config.bCoreMutex ){
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
}
|
||||
if( sqlite3Config.pScratch && sqlite3Config.szScratch>=100
|
||||
&& sqlite3Config.nScratch>=0 ){
|
||||
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
||||
&& sqlite3GlobalConfig.nScratch>=0 ){
|
||||
int i;
|
||||
sqlite3Config.szScratch -= 4;
|
||||
mem0.aScratchFree = (u32*)&((char*)sqlite3Config.pScratch)
|
||||
[sqlite3Config.szScratch*sqlite3Config.nScratch];
|
||||
for(i=0; i<sqlite3Config.nScratch; i++){ mem0.aScratchFree[i] = i; }
|
||||
mem0.nScratchFree = sqlite3Config.nScratch;
|
||||
sqlite3GlobalConfig.szScratch = (sqlite3GlobalConfig.szScratch - 4) & ~7;
|
||||
mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch)
|
||||
[sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch];
|
||||
for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; }
|
||||
mem0.nScratchFree = sqlite3GlobalConfig.nScratch;
|
||||
}else{
|
||||
sqlite3Config.pScratch = 0;
|
||||
sqlite3Config.szScratch = 0;
|
||||
sqlite3GlobalConfig.pScratch = 0;
|
||||
sqlite3GlobalConfig.szScratch = 0;
|
||||
}
|
||||
if( sqlite3Config.pPage && sqlite3Config.szPage>=512
|
||||
&& sqlite3Config.nPage>=1 ){
|
||||
if( sqlite3GlobalConfig.pPage && sqlite3GlobalConfig.szPage>=512
|
||||
&& sqlite3GlobalConfig.nPage>=1 ){
|
||||
int i;
|
||||
int overhead;
|
||||
int sz = sqlite3Config.szPage;
|
||||
int n = sqlite3Config.nPage;
|
||||
int sz = sqlite3GlobalConfig.szPage & ~7;
|
||||
int n = sqlite3GlobalConfig.nPage;
|
||||
overhead = (4*n + sz - 1)/sz;
|
||||
sqlite3Config.nPage -= overhead;
|
||||
mem0.aPageFree = (u32*)&((char*)sqlite3Config.pPage)
|
||||
[sqlite3Config.szPage*sqlite3Config.nPage];
|
||||
for(i=0; i<sqlite3Config.nPage; i++){ mem0.aPageFree[i] = i; }
|
||||
mem0.nPageFree = sqlite3Config.nPage;
|
||||
sqlite3GlobalConfig.nPage -= overhead;
|
||||
mem0.aPageFree = (u32*)&((char*)sqlite3GlobalConfig.pPage)
|
||||
[sqlite3GlobalConfig.szPage*sqlite3GlobalConfig.nPage];
|
||||
for(i=0; i<sqlite3GlobalConfig.nPage; i++){ mem0.aPageFree[i] = i; }
|
||||
mem0.nPageFree = sqlite3GlobalConfig.nPage;
|
||||
}else{
|
||||
sqlite3Config.pPage = 0;
|
||||
sqlite3Config.szPage = 0;
|
||||
sqlite3GlobalConfig.pPage = 0;
|
||||
sqlite3GlobalConfig.szPage = 0;
|
||||
}
|
||||
return sqlite3Config.m.xInit(sqlite3Config.m.pAppData);
|
||||
return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
|
||||
}
|
||||
|
||||
/*
|
||||
** Deinitialize the memory allocation subsystem.
|
||||
*/
|
||||
void sqlite3MallocEnd(void){
|
||||
sqlite3Config.m.xShutdown(sqlite3Config.m.pAppData);
|
||||
if( sqlite3GlobalConfig.m.xShutdown ){
|
||||
sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData);
|
||||
}
|
||||
memset(&mem0, 0, sizeof(mem0));
|
||||
}
|
||||
|
||||
@@ -178,7 +186,7 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
|
||||
/*
|
||||
** Change the alarm callback
|
||||
*/
|
||||
int sqlite3_memory_alarm(
|
||||
int sqlite3MemoryAlarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
@@ -191,6 +199,20 @@ int sqlite3_memory_alarm(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** Deprecated external interface. Internal/core SQLite code
|
||||
** should call sqlite3MemoryAlarm.
|
||||
*/
|
||||
int sqlite3_memory_alarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
){
|
||||
return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Trigger the alarm
|
||||
*/
|
||||
@@ -217,7 +239,7 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
int nFull;
|
||||
void *p;
|
||||
assert( sqlite3_mutex_held(mem0.mutex) );
|
||||
nFull = sqlite3Config.m.xRoundup(n);
|
||||
nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
||||
sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
|
||||
if( mem0.alarmCallback!=0 ){
|
||||
int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
@@ -225,10 +247,10 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
sqlite3MallocAlarm(nFull);
|
||||
}
|
||||
}
|
||||
p = sqlite3Config.m.xMalloc(nFull);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
if( p==0 && mem0.alarmCallback ){
|
||||
sqlite3MallocAlarm(nFull);
|
||||
p = sqlite3Config.m.xMalloc(nFull);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
}
|
||||
if( p ){
|
||||
nFull = sqlite3MallocSize(p);
|
||||
@@ -244,14 +266,22 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
*/
|
||||
void *sqlite3Malloc(int n){
|
||||
void *p;
|
||||
if( n<=0 ){
|
||||
if( n<=0 || NEVER(n>=0x7fffff00) ){
|
||||
/* The NEVER(n>=0x7fffff00) term is added out of paranoia. We want to make
|
||||
** absolutely sure that there is nothing within SQLite that can cause a
|
||||
** memory allocation of a number of bytes which is near the maximum signed
|
||||
** integer value and thus cause an integer overflow inside of the xMalloc()
|
||||
** implementation. The n>=0x7fffff00 gives us 255 bytes of headroom. The
|
||||
** test should never be true because SQLITE_MAX_LENGTH should be much
|
||||
** less than 0x7fffff00 and it should catch large memory allocations
|
||||
** before they reach this point. */
|
||||
p = 0;
|
||||
}else if( sqlite3Config.bMemstat ){
|
||||
}else if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
mallocWithAlarm(n, &p);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
p = sqlite3Config.m.xMalloc(n);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -299,7 +329,7 @@ void *sqlite3ScratchMalloc(int n){
|
||||
assert( scratchAllocOut==0 );
|
||||
#endif
|
||||
|
||||
if( sqlite3Config.szScratch<n ){
|
||||
if( sqlite3GlobalConfig.szScratch<n ){
|
||||
goto scratch_overflow;
|
||||
}else{
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
@@ -309,11 +339,12 @@ void *sqlite3ScratchMalloc(int n){
|
||||
}else{
|
||||
int i;
|
||||
i = mem0.aScratchFree[--mem0.nScratchFree];
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
i *= sqlite3Config.szScratch;
|
||||
i *= sqlite3GlobalConfig.szScratch;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
|
||||
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
p = (void*)&((char*)sqlite3Config.pScratch)[i];
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
p = (void*)&((char*)sqlite3GlobalConfig.pScratch)[i];
|
||||
assert( (((u8*)p - (u8*)0) & 7)==0 );
|
||||
}
|
||||
}
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
@@ -323,14 +354,14 @@ void *sqlite3ScratchMalloc(int n){
|
||||
return p;
|
||||
|
||||
scratch_overflow:
|
||||
if( sqlite3Config.bMemstat ){
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
||||
n = mallocWithAlarm(n, &p);
|
||||
if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
p = sqlite3Config.m.xMalloc(n);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
scratchAllocOut = p!=0;
|
||||
@@ -349,26 +380,26 @@ void sqlite3ScratchFree(void *p){
|
||||
scratchAllocOut = 0;
|
||||
#endif
|
||||
|
||||
if( sqlite3Config.pScratch==0
|
||||
|| p<sqlite3Config.pScratch
|
||||
if( sqlite3GlobalConfig.pScratch==0
|
||||
|| p<sqlite3GlobalConfig.pScratch
|
||||
|| p>=(void*)mem0.aScratchFree ){
|
||||
if( sqlite3Config.bMemstat ){
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
int iSize = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
|
||||
sqlite3Config.m.xFree(p);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3Config.m.xFree(p);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
}
|
||||
}else{
|
||||
int i;
|
||||
i = (u8 *)p - (u8 *)sqlite3Config.pScratch;
|
||||
i /= sqlite3Config.szScratch;
|
||||
assert( i>=0 && i<sqlite3Config.nScratch );
|
||||
i = (int)((u8*)p - (u8*)sqlite3GlobalConfig.pScratch);
|
||||
i /= sqlite3GlobalConfig.szScratch;
|
||||
assert( i>=0 && i<sqlite3GlobalConfig.nScratch );
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
assert( mem0.nScratchFree<sqlite3Config.nScratch );
|
||||
assert( mem0.nScratchFree<(u32)sqlite3GlobalConfig.nScratch );
|
||||
mem0.aScratchFree[mem0.nScratchFree++] = i;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
@@ -382,13 +413,14 @@ void sqlite3ScratchFree(void *p){
|
||||
** and that memory is of the right size and is not completely
|
||||
** consumed. Otherwise, failover to sqlite3Malloc().
|
||||
*/
|
||||
#if 0
|
||||
void *sqlite3PageMalloc(int n){
|
||||
void *p;
|
||||
assert( n>0 );
|
||||
assert( (n & (n-1))==0 );
|
||||
assert( n>=512 && n<=32768 );
|
||||
|
||||
if( sqlite3Config.szPage<n ){
|
||||
if( sqlite3GlobalConfig.szPage<n ){
|
||||
goto page_overflow;
|
||||
}else{
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
@@ -399,57 +431,57 @@ void *sqlite3PageMalloc(int n){
|
||||
int i;
|
||||
i = mem0.aPageFree[--mem0.nPageFree];
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
i *= sqlite3Config.szPage;
|
||||
i *= sqlite3GlobalConfig.szPage;
|
||||
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
|
||||
p = (void*)&((char*)sqlite3Config.pPage)[i];
|
||||
p = (void*)&((char*)sqlite3GlobalConfig.pPage)[i];
|
||||
}
|
||||
}
|
||||
return p;
|
||||
|
||||
page_overflow:
|
||||
if( sqlite3Config.bMemstat ){
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
|
||||
n = mallocWithAlarm(n, &p);
|
||||
if( p ) sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
p = sqlite3Config.m.xMalloc(n);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
void sqlite3PageFree(void *p){
|
||||
if( p ){
|
||||
if( sqlite3Config.pPage==0
|
||||
|| p<sqlite3Config.pPage
|
||||
if( sqlite3GlobalConfig.pPage==0
|
||||
|| p<sqlite3GlobalConfig.pPage
|
||||
|| p>=(void*)mem0.aPageFree ){
|
||||
/* In this case, the page allocation was obtained from a regular
|
||||
** call to sqlite3_mem_methods.xMalloc() (a page-cache-memory
|
||||
** "overflow"). Free the block with sqlite3_mem_methods.xFree().
|
||||
*/
|
||||
if( sqlite3Config.bMemstat ){
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
int iSize = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
|
||||
sqlite3Config.m.xFree(p);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3Config.m.xFree(p);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
}
|
||||
}else{
|
||||
/* The page allocation was allocated from the sqlite3Config.pPage
|
||||
/* The page allocation was allocated from the sqlite3GlobalConfig.pPage
|
||||
** buffer. In this case all that is add the index of the page in
|
||||
** the sqlite3Config.pPage array to the set of free indexes stored
|
||||
** the sqlite3GlobalConfig.pPage array to the set of free indexes stored
|
||||
** in the mem0.aPageFree[] array.
|
||||
*/
|
||||
int i;
|
||||
i = (u8 *)p - (u8 *)sqlite3Config.pPage;
|
||||
i /= sqlite3Config.szPage;
|
||||
assert( i>=0 && i<sqlite3Config.nPage );
|
||||
i = (u8 *)p - (u8 *)sqlite3GlobalConfig.pPage;
|
||||
i /= sqlite3GlobalConfig.szPage;
|
||||
assert( i>=0 && i<sqlite3GlobalConfig.nPage );
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
assert( mem0.nPageFree<sqlite3Config.nPage );
|
||||
assert( mem0.nPageFree<sqlite3GlobalConfig.nPage );
|
||||
mem0.aPageFree[mem0.nPageFree++] = i;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
@@ -462,26 +494,33 @@ void sqlite3PageFree(void *p){
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** TRUE if p is a lookaside memory allocation from db
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
static int isLookaside(sqlite3 *db, void *p){
|
||||
return db && p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
|
||||
}
|
||||
#else
|
||||
#define isLookaside(A,B) 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the size of a memory allocation previously obtained from
|
||||
** sqlite3Malloc() or sqlite3_malloc().
|
||||
*/
|
||||
int sqlite3MallocSize(void *p){
|
||||
return sqlite3Config.m.xSize(p);
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
if( isLookaside(db, p) ){
|
||||
if( p==0 ){
|
||||
return 0;
|
||||
}else if( isLookaside(db, p) ){
|
||||
return db->lookaside.sz;
|
||||
}else{
|
||||
return sqlite3Config.m.xSize(p);
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,13 +529,13 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
*/
|
||||
void sqlite3_free(void *p){
|
||||
if( p==0 ) return;
|
||||
if( sqlite3Config.bMemstat ){
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
|
||||
sqlite3Config.m.xFree(p);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3Config.m.xFree(p);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,15 +563,16 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
if( pOld==0 ){
|
||||
return sqlite3Malloc(nBytes);
|
||||
}
|
||||
if( nBytes<=0 ){
|
||||
if( nBytes<=0 || NEVER(nBytes>=0x7fffff00) ){
|
||||
/* The NEVER(...) term is explained in comments on sqlite3Malloc() */
|
||||
sqlite3_free(pOld);
|
||||
return 0;
|
||||
}
|
||||
nOld = sqlite3MallocSize(pOld);
|
||||
if( sqlite3Config.bMemstat ){
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
|
||||
nNew = sqlite3Config.m.xRoundup(nBytes);
|
||||
nNew = sqlite3GlobalConfig.m.xRoundup(nBytes);
|
||||
if( nOld==nNew ){
|
||||
pNew = pOld;
|
||||
}else{
|
||||
@@ -540,10 +580,10 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
mem0.alarmThreshold ){
|
||||
sqlite3MallocAlarm(nNew-nOld);
|
||||
}
|
||||
pNew = sqlite3Config.m.xRealloc(pOld, nNew);
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
||||
if( pNew==0 && mem0.alarmCallback ){
|
||||
sqlite3MallocAlarm(nBytes);
|
||||
pNew = sqlite3Config.m.xRealloc(pOld, nNew);
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
||||
}
|
||||
if( pNew ){
|
||||
nNew = sqlite3MallocSize(pNew);
|
||||
@@ -552,7 +592,7 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
}
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
pNew = sqlite3Config.m.xRealloc(pOld, nBytes);
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nBytes);
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
@@ -595,9 +635,24 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
||||
/*
|
||||
** Allocate and zero memory. If the allocation fails, make
|
||||
** the mallocFailed flag in the connection pointer.
|
||||
**
|
||||
** If db!=0 and db->mallocFailed is true (indicating a prior malloc
|
||||
** failure on the same database connection) then always return 0.
|
||||
** Hence for a particular database connection, once malloc starts
|
||||
** failing, it fails consistently until mallocFailed is reset.
|
||||
** This is an important assumption. There are many places in the
|
||||
** code that do things like this:
|
||||
**
|
||||
** int *a = (int*)sqlite3DbMallocRaw(db, 100);
|
||||
** int *b = (int*)sqlite3DbMallocRaw(db, 200);
|
||||
** if( b ) a[10] = 9;
|
||||
**
|
||||
** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
|
||||
** that all prior mallocs (ex: "a") worked too.
|
||||
*/
|
||||
void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
void *p;
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
if( db ){
|
||||
LookasideSlot *pBuf;
|
||||
if( db->mallocFailed ){
|
||||
@@ -613,6 +668,11 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
return (void*)pBuf;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if( db && db->mallocFailed ){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
p = sqlite3Malloc(n);
|
||||
if( !p && db ){
|
||||
db->mallocFailed = 1;
|
||||
@@ -675,7 +735,7 @@ char *sqlite3DbStrDup(sqlite3 *db, const char *z){
|
||||
if( z==0 ){
|
||||
return 0;
|
||||
}
|
||||
n = strlen(z)+1;
|
||||
n = (db ? sqlite3Strlen(db, z) : sqlite3Strlen30(z))+1;
|
||||
assert( (n&0x7fffffff)==n );
|
||||
zNew = sqlite3DbMallocRaw(db, (int)n);
|
||||
if( zNew ){
|
||||
@@ -733,7 +793,7 @@ int sqlite3ApiExit(sqlite3* db, int rc){
|
||||
** is unsafe, as is the call to sqlite3Error().
|
||||
*/
|
||||
assert( !db || sqlite3_mutex_held(db->mutex) );
|
||||
if( db && db->mallocFailed ){
|
||||
if( db && (db->mallocFailed || rc==SQLITE_IOERR_NOMEM) ){
|
||||
sqlite3Error(db, SQLITE_NOMEM, 0);
|
||||
db->mallocFailed = 0;
|
||||
rc = SQLITE_NOMEM;
|
||||
|
||||
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
** SQLite uses this code for testing only. It is not a part of
|
||||
** the SQLite library. This file implements two new TCL commands
|
||||
** "md5" and "md5file" that compute md5 checksums on arbitrary text
|
||||
** and on complete files. These commands are used by the "testfixture"
|
||||
** program to help verify the correct operation of the SQLite library.
|
||||
**
|
||||
** The original use of these TCL commands was to test the ROLLBACK
|
||||
** feature of SQLite. First compute the MD5-checksum of the database.
|
||||
** Then make some changes but rollback the changes rather than commit
|
||||
** them. Compute a second MD5-checksum of the file and verify that the
|
||||
** two checksums are the same. Such is the original use of this code.
|
||||
** New uses may have been added since this comment was written.
|
||||
**
|
||||
** $Id: md5.c,v 1.17 2008/05/16 04:51:55 danielk1977 Exp $
|
||||
*/
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
#include <tcl.h>
|
||||
#include <string.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
* If compiled on a machine that doesn't have a 32-bit integer,
|
||||
* you just set "uint32" to the appropriate datatype for an
|
||||
* unsigned 32-bit integer. For example:
|
||||
*
|
||||
* cc -Duint32='unsigned long' md5.c
|
||||
*
|
||||
*/
|
||||
#ifndef uint32
|
||||
# define uint32 unsigned int
|
||||
#endif
|
||||
|
||||
struct Context {
|
||||
uint32 buf[4];
|
||||
uint32 bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
typedef char MD5Context[88];
|
||||
|
||||
/*
|
||||
* Note: this code is harmless on little-endian machines.
|
||||
*/
|
||||
static void byteReverse (unsigned char *buf, unsigned longs){
|
||||
uint32 t;
|
||||
do {
|
||||
t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
|
||||
((unsigned)buf[1]<<8 | buf[0]);
|
||||
*(uint32 *)buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
static void MD5Transform(uint32 buf[4], const uint32 in[16]){
|
||||
register uint32 a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
static void MD5Init(MD5Context *pCtx){
|
||||
struct Context *ctx = (struct Context *)pCtx;
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
static
|
||||
void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){
|
||||
struct Context *ctx = (struct Context *)pCtx;
|
||||
uint32 t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if ( t ) {
|
||||
unsigned char *p = (unsigned char *)ctx->in + t;
|
||||
|
||||
t = 64-t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
static void MD5Final(unsigned char digest[16], MD5Context *pCtx){
|
||||
struct Context *ctx = (struct Context *)pCtx;
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count-8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
|
||||
((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
byteReverse((unsigned char *)ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a digest into base-16. digest should be declared as
|
||||
** "unsigned char digest[16]" in the calling function. The MD5
|
||||
** digest is stored in the first 16 bytes. zBuf should
|
||||
** be "char zBuf[33]".
|
||||
*/
|
||||
static void DigestToBase16(unsigned char *digest, char *zBuf){
|
||||
static char const zEncode[] = "0123456789abcdef";
|
||||
int i, j;
|
||||
|
||||
for(j=i=0; i<16; i++){
|
||||
int a = digest[i];
|
||||
zBuf[j++] = zEncode[(a>>4)&0xf];
|
||||
zBuf[j++] = zEncode[a & 0xf];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** A TCL command for md5. The argument is the text to be hashed. The
|
||||
** Result is the hash in base64.
|
||||
*/
|
||||
static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
|
||||
MD5Context ctx;
|
||||
unsigned char digest[16];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" TEXT\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
|
||||
MD5Final(digest, &ctx);
|
||||
DigestToBase16(digest, interp->result);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A TCL command to take the md5 hash of a file. The argument is the
|
||||
** name of the file.
|
||||
*/
|
||||
static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
|
||||
FILE *in;
|
||||
MD5Context ctx;
|
||||
unsigned char digest[16];
|
||||
char zBuf[10240];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" FILENAME\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
in = fopen(argv[1],"rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
"\" for reading", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
for(;;){
|
||||
int n;
|
||||
n = fread(zBuf, 1, sizeof(zBuf), in);
|
||||
if( n<=0 ) break;
|
||||
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
|
||||
}
|
||||
fclose(in);
|
||||
MD5Final(digest, &ctx);
|
||||
DigestToBase16(digest, interp->result);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the two TCL commands above with the TCL interpreter.
|
||||
*/
|
||||
int Md5_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
|
||||
Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** During testing, the special md5sum() aggregate function is available.
|
||||
** inside SQLite. The following routines implement that function.
|
||||
*/
|
||||
static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
MD5Context *p;
|
||||
int i;
|
||||
if( argc<1 ) return;
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( sqlite3_aggregate_count(context)==1 ){
|
||||
MD5Init(p);
|
||||
}
|
||||
for(i=0; i<argc; i++){
|
||||
const char *zData = (char*)sqlite3_value_text(argv[i]);
|
||||
if( zData ){
|
||||
MD5Update(p, (unsigned char*)zData, strlen(zData));
|
||||
}
|
||||
}
|
||||
}
|
||||
static void md5finalize(sqlite3_context *context){
|
||||
MD5Context *p;
|
||||
unsigned char digest[16];
|
||||
char zBuf[33];
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
MD5Final(digest,p);
|
||||
DigestToBase16(digest, zBuf);
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
void Md5_Register(sqlite3 *db){
|
||||
sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
|
||||
md5step, md5finalize);
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
** 2008 October 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains a no-op memory allocation drivers for use when
|
||||
** SQLITE_ZERO_MALLOC is defined. The allocation drivers implemented
|
||||
** here always fail. SQLite will not operate with these drivers. These
|
||||
** are merely placeholders. Real drivers must be substituted using
|
||||
** sqlite3_config() before SQLite will operate.
|
||||
**
|
||||
** $Id: mem0.c,v 1.1 2008/10/28 18:58:20 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This version of the memory allocator is the default. It is
|
||||
** used when no other memory allocator is specified using compile-time
|
||||
** macros.
|
||||
*/
|
||||
#ifdef SQLITE_ZERO_MALLOC
|
||||
|
||||
/*
|
||||
** No-op versions of all memory allocation routines
|
||||
*/
|
||||
static void *sqlite3MemMalloc(int nByte){ return 0; }
|
||||
static void sqlite3MemFree(void *pPrior){ return; }
|
||||
static void *sqlite3MemRealloc(void *pPrior, int nByte){ return 0; }
|
||||
static int sqlite3MemSize(void *pPrior){ return 0; }
|
||||
static int sqlite3MemRoundup(int n){ return n; }
|
||||
static int sqlite3MemInit(void *NotUsed){ return SQLITE_OK; }
|
||||
static void sqlite3MemShutdown(void *NotUsed){ return; }
|
||||
|
||||
/*
|
||||
** This routine is the only routine in this file with external linkage.
|
||||
**
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
||||
*/
|
||||
void sqlite3MemSetDefault(void){
|
||||
static const sqlite3_mem_methods defaultMethods = {
|
||||
sqlite3MemMalloc,
|
||||
sqlite3MemFree,
|
||||
sqlite3MemRealloc,
|
||||
sqlite3MemSize,
|
||||
sqlite3MemRoundup,
|
||||
sqlite3MemInit,
|
||||
sqlite3MemShutdown,
|
||||
0
|
||||
};
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
||||
}
|
||||
|
||||
#endif /* SQLITE_ZERO_MALLOC */
|
||||
+12
-14
@@ -17,7 +17,7 @@
|
||||
** This file contains implementations of the low-level memory allocation
|
||||
** routines specified in the sqlite3_mem_methods object.
|
||||
**
|
||||
** $Id: mem1.c,v 1.25 2008/07/25 08:49:00 danielk1977 Exp $
|
||||
** $Id: mem1.c,v 1.29 2008/12/10 21:19:57 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -96,7 +96,7 @@ static int sqlite3MemSize(void *pPrior){
|
||||
if( pPrior==0 ) return 0;
|
||||
p = (sqlite3_int64*)pPrior;
|
||||
p--;
|
||||
return p[0];
|
||||
return (int)p[0];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -110,6 +110,7 @@ static int sqlite3MemRoundup(int n){
|
||||
** Initialize this module.
|
||||
*/
|
||||
static int sqlite3MemInit(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -117,10 +118,17 @@ static int sqlite3MemInit(void *NotUsed){
|
||||
** Deinitialize this module.
|
||||
*/
|
||||
static void sqlite3MemShutdown(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return;
|
||||
}
|
||||
|
||||
const sqlite3_mem_methods *sqlite3MemGetDefault(void){
|
||||
/*
|
||||
** This routine is the only routine in this file with external linkage.
|
||||
**
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
||||
*/
|
||||
void sqlite3MemSetDefault(void){
|
||||
static const sqlite3_mem_methods defaultMethods = {
|
||||
sqlite3MemMalloc,
|
||||
sqlite3MemFree,
|
||||
@@ -131,17 +139,7 @@ const sqlite3_mem_methods *sqlite3MemGetDefault(void){
|
||||
sqlite3MemShutdown,
|
||||
0
|
||||
};
|
||||
return &defaultMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is the only routine in this file with external linkage.
|
||||
**
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3Config.m with pointers to the routines in this file.
|
||||
*/
|
||||
void sqlite3MemSetDefault(void){
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetDefault());
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
||||
}
|
||||
|
||||
#endif /* SQLITE_SYSTEM_MALLOC */
|
||||
|
||||
+21
-21
@@ -19,7 +19,7 @@
|
||||
** This file contains implementations of the low-level memory allocation
|
||||
** routines specified in the sqlite3_mem_methods object.
|
||||
**
|
||||
** $Id: mem2.c,v 1.37 2008/07/25 08:49:00 danielk1977 Exp $
|
||||
** $Id: mem2.c,v 1.43 2009/02/05 03:00:06 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
extern int backtrace(void**,int);
|
||||
extern void backtrace_symbols_fd(void*const*,int,int);
|
||||
#else
|
||||
# define backtrace(A,B) 0
|
||||
# define backtrace(A,B) 1
|
||||
# define backtrace_symbols_fd(A,B,C)
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
@@ -158,14 +158,16 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
|
||||
|
||||
p = (struct MemBlockHdr*)pAllocation;
|
||||
p--;
|
||||
assert( p->iForeGuard==FOREGUARD );
|
||||
assert( p->iForeGuard==(int)FOREGUARD );
|
||||
nReserve = (p->iSize+7)&~7;
|
||||
pInt = (int*)pAllocation;
|
||||
pU8 = (u8*)pAllocation;
|
||||
assert( pInt[nReserve/sizeof(int)]==REARGUARD );
|
||||
assert( (nReserve-0)<=p->iSize || pU8[nReserve-1]==0x65 );
|
||||
assert( (nReserve-1)<=p->iSize || pU8[nReserve-2]==0x65 );
|
||||
assert( (nReserve-2)<=p->iSize || pU8[nReserve-3]==0x65 );
|
||||
assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD );
|
||||
/* This checks any of the "extra" bytes allocated due
|
||||
** to rounding up to an 8 byte boundary to ensure
|
||||
** they haven't been overwritten.
|
||||
*/
|
||||
while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 );
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -185,7 +187,9 @@ static int sqlite3MemSize(void *p){
|
||||
** Initialize the memory allocation subsystem.
|
||||
*/
|
||||
static int sqlite3MemInit(void *NotUsed){
|
||||
if( !sqlite3Config.bMemstat ){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
assert( (sizeof(struct MemBlockHdr)&7) == 0 );
|
||||
if( !sqlite3GlobalConfig.bMemstat ){
|
||||
/* If memory status is enabled, then the malloc.c wrapper will already
|
||||
** hold the STATIC_MEM mutex when the routines here are invoked. */
|
||||
mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
@@ -197,6 +201,7 @@ static int sqlite3MemInit(void *NotUsed){
|
||||
** Deinitialize the memory allocation subsystem.
|
||||
*/
|
||||
static void sqlite3MemShutdown(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
mem.mutex = 0;
|
||||
}
|
||||
|
||||
@@ -270,7 +275,7 @@ static void sqlite3MemFree(void *pPrior){
|
||||
struct MemBlockHdr *pHdr;
|
||||
void **pBt;
|
||||
char *z;
|
||||
assert( sqlite3Config.bMemstat || mem.mutex!=0 );
|
||||
assert( sqlite3GlobalConfig.bMemstat || mem.mutex!=0 );
|
||||
pHdr = sqlite3MemsysGetHeader(pPrior);
|
||||
pBt = (void**)pHdr;
|
||||
pBt -= pHdr->nBacktraceSlots;
|
||||
@@ -323,8 +328,11 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
return pNew;
|
||||
}
|
||||
|
||||
|
||||
const sqlite3_mem_methods *sqlite3MemGetDefault(void){
|
||||
/*
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
||||
*/
|
||||
void sqlite3MemSetDefault(void){
|
||||
static const sqlite3_mem_methods defaultMethods = {
|
||||
sqlite3MemMalloc,
|
||||
sqlite3MemFree,
|
||||
@@ -335,15 +343,7 @@ const sqlite3_mem_methods *sqlite3MemGetDefault(void){
|
||||
sqlite3MemShutdown,
|
||||
0
|
||||
};
|
||||
return &defaultMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3Config.m with pointers to the routines in this file.
|
||||
*/
|
||||
void sqlite3MemSetDefault(void){
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetDefault());
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -366,7 +366,7 @@ void sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){
|
||||
** Set the title string for subsequent allocations.
|
||||
*/
|
||||
void sqlite3MemdebugSettitle(const char *zTitle){
|
||||
int n = strlen(zTitle) + 1;
|
||||
unsigned int n = sqlite3Strlen30(zTitle) + 1;
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1;
|
||||
memcpy(mem.zTitle, zTitle, n);
|
||||
|
||||
+28
-22
@@ -23,7 +23,7 @@
|
||||
** This version of the memory allocation subsystem is included
|
||||
** in the build only if SQLITE_ENABLE_MEMSYS3 is defined.
|
||||
**
|
||||
** $Id: mem3.c,v 1.20 2008/07/18 18:56:17 drh Exp $
|
||||
** $Id: mem3.c,v 1.25 2008/11/19 16:52:44 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -99,7 +99,14 @@ struct Mem3Block {
|
||||
** static variables organized and to reduce namespace pollution
|
||||
** when this module is combined with other in the amalgamation.
|
||||
*/
|
||||
static struct {
|
||||
static SQLITE_WSD struct Mem3Global {
|
||||
/*
|
||||
** Memory available for allocation. nPool is the size of the array
|
||||
** (in Mem3Blocks) pointed to by aPool less 2.
|
||||
*/
|
||||
u32 nPool;
|
||||
Mem3Block *aPool;
|
||||
|
||||
/*
|
||||
** True if we are evaluating an out-of-memory callback.
|
||||
*/
|
||||
@@ -131,14 +138,9 @@ static struct {
|
||||
*/
|
||||
u32 aiSmall[MX_SMALL-1]; /* For sizes 2 through MX_SMALL, inclusive */
|
||||
u32 aiHash[N_HASH]; /* For sizes MX_SMALL+1 and larger */
|
||||
} mem3 = { 97535575 };
|
||||
|
||||
/*
|
||||
** Memory available for allocation. nPool is the size of the array
|
||||
** (in Mem3Blocks) pointed to by aPool less 2.
|
||||
*/
|
||||
u32 nPool;
|
||||
Mem3Block *aPool;
|
||||
} mem3;
|
||||
#define mem3 GLOBAL(struct Mem3Global, mem3)
|
||||
|
||||
/*
|
||||
** Unlink the chunk at mem3.aPool[i] from list it is currently
|
||||
@@ -217,10 +219,10 @@ static void memsys3Link(u32 i){
|
||||
/*
|
||||
** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
|
||||
** will already be held (obtained by code in malloc.c) if
|
||||
** sqlite3Config.bMemStat is true.
|
||||
** sqlite3GlobalConfig.bMemStat is true.
|
||||
*/
|
||||
static void memsys3Enter(void){
|
||||
if( sqlite3Config.bMemstat==0 && mem3.mutex==0 ){
|
||||
if( sqlite3GlobalConfig.bMemstat==0 && mem3.mutex==0 ){
|
||||
mem3.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
}
|
||||
sqlite3_mutex_enter(mem3.mutex);
|
||||
@@ -249,7 +251,7 @@ static void memsys3OutOfMemory(int nByte){
|
||||
** size parameters for check-out and return a pointer to the
|
||||
** user portion of the chunk.
|
||||
*/
|
||||
static void *memsys3Checkout(u32 i, int nBlock){
|
||||
static void *memsys3Checkout(u32 i, u32 nBlock){
|
||||
u32 x;
|
||||
assert( sqlite3_mutex_held(mem3.mutex) );
|
||||
assert( i>=1 );
|
||||
@@ -267,7 +269,7 @@ static void *memsys3Checkout(u32 i, int nBlock){
|
||||
** Return a pointer to the new allocation. Or, if the master chunk
|
||||
** is not large enough, return 0.
|
||||
*/
|
||||
static void *memsys3FromMaster(int nBlock){
|
||||
static void *memsys3FromMaster(u32 nBlock){
|
||||
assert( sqlite3_mutex_held(mem3.mutex) );
|
||||
assert( mem3.szMaster>=nBlock );
|
||||
if( nBlock>=mem3.szMaster-1 ){
|
||||
@@ -353,8 +355,8 @@ static void memsys3Merge(u32 *pRoot){
|
||||
*/
|
||||
static void *memsys3MallocUnsafe(int nByte){
|
||||
u32 i;
|
||||
int nBlock;
|
||||
int toFree;
|
||||
u32 nBlock;
|
||||
u32 toFree;
|
||||
|
||||
assert( sqlite3_mutex_held(mem3.mutex) );
|
||||
assert( sizeof(Mem3Block)==8 );
|
||||
@@ -550,14 +552,15 @@ void *memsys3Realloc(void *pPrior, int nBytes){
|
||||
** Initialize this module.
|
||||
*/
|
||||
static int memsys3Init(void *NotUsed){
|
||||
if( !sqlite3Config.pHeap ){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
if( !sqlite3GlobalConfig.pHeap ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Store a pointer to the memory block in global structure mem3. */
|
||||
assert( sizeof(Mem3Block)==8 );
|
||||
mem3.aPool = (Mem3Block *)sqlite3Config.pHeap;
|
||||
mem3.nPool = (sqlite3Config.nHeap / sizeof(Mem3Block)) - 2;
|
||||
mem3.aPool = (Mem3Block *)sqlite3GlobalConfig.pHeap;
|
||||
mem3.nPool = (sqlite3GlobalConfig.nHeap / sizeof(Mem3Block)) - 2;
|
||||
|
||||
/* Initialize the master block. */
|
||||
mem3.szMaster = mem3.nPool;
|
||||
@@ -574,6 +577,7 @@ static int memsys3Init(void *NotUsed){
|
||||
** Deinitialize this module.
|
||||
*/
|
||||
static void memsys3Shutdown(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -583,10 +587,10 @@ static void memsys3Shutdown(void *NotUsed){
|
||||
** Open the file indicated and write a log of all unfreed memory
|
||||
** allocations into that log.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3Memsys3Dump(const char *zFilename){
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *out;
|
||||
int i, j;
|
||||
u32 i, j;
|
||||
u32 size;
|
||||
if( zFilename==0 || zFilename[0]==0 ){
|
||||
out = stdout;
|
||||
@@ -651,15 +655,17 @@ void sqlite3Memsys3Dump(const char *zFilename){
|
||||
}else{
|
||||
fclose(out);
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(zFilename);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is the only routine in this file with external
|
||||
** linkage.
|
||||
**
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3Config.m with pointers to the routines in this file. The
|
||||
** sqlite3GlobalConfig.m with pointers to the routines in this file. The
|
||||
** arguments specify the block of memory to manage.
|
||||
**
|
||||
** This routine is only called by sqlite3_config(), and therefore
|
||||
|
||||
-393
@@ -1,393 +0,0 @@
|
||||
/*
|
||||
** 2007 August 14
|
||||
**
|
||||
** 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 the C functions that implement a memory
|
||||
** allocation subsystem for use by SQLite.
|
||||
**
|
||||
** $Id: mem4.c,v 1.3 2008/06/18 17:09:10 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This version of the memory allocator attempts to obtain memory
|
||||
** from mmap() if the size of the allocation is close to the size
|
||||
** of a virtual memory page. If the size of the allocation is different
|
||||
** from the virtual memory page size, then ordinary malloc() is used.
|
||||
** Ordinary malloc is also used if space allocated to mmap() is
|
||||
** exhausted.
|
||||
**
|
||||
** Enable this memory allocation by compiling with -DSQLITE_MMAP_HEAP_SIZE=nnn
|
||||
** where nnn is the maximum number of bytes of mmap-ed memory you want
|
||||
** to support. This module may choose to use less memory than requested.
|
||||
**
|
||||
*/
|
||||
#ifdef SQLITE_MMAP_HEAP_SIZE
|
||||
|
||||
/*
|
||||
** This is a test version of the memory allocator that attempts to
|
||||
** use mmap() and madvise() for allocations and frees of approximately
|
||||
** the virtual memory page size.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
/*
|
||||
** All of the static variables used by this module are collected
|
||||
** into a single structure named "mem". This is to keep the
|
||||
** static variables organized and to reduce namespace pollution
|
||||
** when this module is combined with other in the amalgamation.
|
||||
*/
|
||||
static struct {
|
||||
/*
|
||||
** The alarm callback and its arguments. The mem.mutex lock will
|
||||
** be held while the callback is running. Recursive calls into
|
||||
** the memory subsystem are allowed, but no new callbacks will be
|
||||
** issued. The alarmBusy variable is set to prevent recursive
|
||||
** callbacks.
|
||||
*/
|
||||
sqlite3_int64 alarmThreshold;
|
||||
void (*alarmCallback)(void*, sqlite3_int64,int);
|
||||
void *alarmArg;
|
||||
int alarmBusy;
|
||||
|
||||
/*
|
||||
** Mutex to control access to the memory allocation subsystem.
|
||||
*/
|
||||
sqlite3_mutex *mutex;
|
||||
|
||||
/*
|
||||
** Current allocation and high-water mark.
|
||||
*/
|
||||
sqlite3_int64 nowUsed;
|
||||
sqlite3_int64 mxUsed;
|
||||
|
||||
/*
|
||||
** Current allocation and high-water marks for mmap allocated memory.
|
||||
*/
|
||||
sqlite3_int64 nowUsedMMap;
|
||||
sqlite3_int64 mxUsedMMap;
|
||||
|
||||
/*
|
||||
** Size of a single mmap page. Obtained from sysconf().
|
||||
*/
|
||||
int szPage;
|
||||
int mnPage;
|
||||
|
||||
/*
|
||||
** The number of available mmap pages.
|
||||
*/
|
||||
int nPage;
|
||||
|
||||
/*
|
||||
** Index of the first free page. 0 means no pages have been freed.
|
||||
*/
|
||||
int firstFree;
|
||||
|
||||
/* First unused page on the top of the heap.
|
||||
*/
|
||||
int firstUnused;
|
||||
|
||||
/*
|
||||
** Bulk memory obtained from from mmap().
|
||||
*/
|
||||
char *mmapHeap; /* first byte of the heap */
|
||||
|
||||
} mem;
|
||||
|
||||
|
||||
/*
|
||||
** Enter the mutex mem.mutex. Allocate it if it is not already allocated.
|
||||
** The mmap() region is initialized the first time this routine is called.
|
||||
*/
|
||||
static void memsys4Enter(void){
|
||||
if( mem.mutex==0 ){
|
||||
mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
}
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to free memory to the mmap heap. This only works if
|
||||
** the pointer p is within the range of memory addresses that
|
||||
** comprise the mmap heap. Return 1 if the memory was freed
|
||||
** successfully. Return 0 if the pointer is out of range.
|
||||
*/
|
||||
static int mmapFree(void *p){
|
||||
char *z;
|
||||
int idx, *a;
|
||||
if( mem.mmapHeap==MAP_FAILED || mem.nPage==0 ){
|
||||
return 0;
|
||||
}
|
||||
z = (char*)p;
|
||||
idx = (z - mem.mmapHeap)/mem.szPage;
|
||||
if( idx<1 || idx>=mem.nPage ){
|
||||
return 0;
|
||||
}
|
||||
a = (int*)mem.mmapHeap;
|
||||
a[idx] = a[mem.firstFree];
|
||||
mem.firstFree = idx;
|
||||
mem.nowUsedMMap -= mem.szPage;
|
||||
madvise(p, mem.szPage, MADV_DONTNEED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to allocate nBytes from the mmap heap. Return a pointer
|
||||
** to the allocated page. Or, return NULL if the allocation fails.
|
||||
**
|
||||
** The allocation will fail if nBytes is not the right size.
|
||||
** Or, the allocation will fail if the mmap heap has been exhausted.
|
||||
*/
|
||||
static void *mmapAlloc(int nBytes){
|
||||
int idx = 0;
|
||||
if( nBytes>mem.szPage || nBytes<mem.mnPage ){
|
||||
return 0;
|
||||
}
|
||||
if( mem.nPage==0 ){
|
||||
mem.szPage = sysconf(_SC_PAGE_SIZE);
|
||||
mem.mnPage = mem.szPage - mem.szPage/10;
|
||||
mem.nPage = SQLITE_MMAP_HEAP_SIZE/mem.szPage;
|
||||
if( mem.nPage * sizeof(int) > mem.szPage ){
|
||||
mem.nPage = mem.szPage/sizeof(int);
|
||||
}
|
||||
mem.mmapHeap = mmap(0, mem.szPage*mem.nPage, PROT_WRITE|PROT_READ,
|
||||
MAP_ANONYMOUS|MAP_SHARED, -1, 0);
|
||||
if( mem.mmapHeap==MAP_FAILED ){
|
||||
mem.firstUnused = errno;
|
||||
}else{
|
||||
mem.firstUnused = 1;
|
||||
mem.nowUsedMMap = mem.szPage;
|
||||
}
|
||||
}
|
||||
if( mem.mmapHeap==MAP_FAILED ){
|
||||
return 0;
|
||||
}
|
||||
if( mem.firstFree ){
|
||||
int idx = mem.firstFree;
|
||||
int *a = (int*)mem.mmapHeap;
|
||||
mem.firstFree = a[idx];
|
||||
}else if( mem.firstUnused<mem.nPage ){
|
||||
idx = mem.firstUnused++;
|
||||
}
|
||||
if( idx ){
|
||||
mem.nowUsedMMap += mem.szPage;
|
||||
if( mem.nowUsedMMap>mem.mxUsedMMap ){
|
||||
mem.mxUsedMMap = mem.nowUsedMMap;
|
||||
}
|
||||
return (void*)&mem.mmapHeap[idx*mem.szPage];
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Release the mmap-ed memory region if it is currently allocated and
|
||||
** is not in use.
|
||||
*/
|
||||
static void mmapUnmap(void){
|
||||
if( mem.mmapHeap==MAP_FAILED ) return;
|
||||
if( mem.nPage==0 ) return;
|
||||
if( mem.nowUsedMMap>mem.szPage ) return;
|
||||
munmap(mem.mmapHeap, mem.nPage*mem.szPage);
|
||||
mem.nowUsedMMap = 0;
|
||||
mem.nPage = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the amount of memory currently checked out.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_memory_used(void){
|
||||
sqlite3_int64 n;
|
||||
memsys4Enter();
|
||||
n = mem.nowUsed + mem.nowUsedMMap;
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the maximum amount of memory that has ever been
|
||||
** checked out since either the beginning of this process
|
||||
** or since the most recent reset.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
|
||||
sqlite3_int64 n;
|
||||
memsys4Enter();
|
||||
n = mem.mxUsed + mem.mxUsedMMap;
|
||||
if( resetFlag ){
|
||||
mem.mxUsed = mem.nowUsed;
|
||||
mem.mxUsedMMap = mem.nowUsedMMap;
|
||||
}
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the alarm callback
|
||||
*/
|
||||
int sqlite3_memory_alarm(
|
||||
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
||||
void *pArg,
|
||||
sqlite3_int64 iThreshold
|
||||
){
|
||||
memsys4Enter();
|
||||
mem.alarmCallback = xCallback;
|
||||
mem.alarmArg = pArg;
|
||||
mem.alarmThreshold = iThreshold;
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Trigger the alarm
|
||||
*/
|
||||
static void sqlite3MemsysAlarm(int nByte){
|
||||
void (*xCallback)(void*,sqlite3_int64,int);
|
||||
sqlite3_int64 nowUsed;
|
||||
void *pArg;
|
||||
if( mem.alarmCallback==0 || mem.alarmBusy ) return;
|
||||
mem.alarmBusy = 1;
|
||||
xCallback = mem.alarmCallback;
|
||||
nowUsed = mem.nowUsed;
|
||||
pArg = mem.alarmArg;
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
xCallback(pArg, nowUsed, nByte);
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
mem.alarmBusy = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate nBytes of memory
|
||||
*/
|
||||
static void *memsys4Malloc(int nBytes){
|
||||
sqlite3_int64 *p = 0;
|
||||
if( mem.alarmCallback!=0
|
||||
&& mem.nowUsed+mem.nowUsedMMap+nBytes>=mem.alarmThreshold ){
|
||||
sqlite3MemsysAlarm(nBytes);
|
||||
}
|
||||
if( (p = mmapAlloc(nBytes))==0 ){
|
||||
p = malloc(nBytes+8);
|
||||
if( p==0 ){
|
||||
sqlite3MemsysAlarm(nBytes);
|
||||
p = malloc(nBytes+8);
|
||||
}
|
||||
if( p ){
|
||||
p[0] = nBytes;
|
||||
p++;
|
||||
mem.nowUsed += nBytes;
|
||||
if( mem.nowUsed>mem.mxUsed ){
|
||||
mem.mxUsed = mem.nowUsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (void*)p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of a memory allocation
|
||||
*/
|
||||
static int memsys4Size(void *pPrior){
|
||||
char *z = (char*)pPrior;
|
||||
int idx = mem.nPage ? (z - mem.mmapHeap)/mem.szPage : 0;
|
||||
int nByte;
|
||||
if( idx>=1 && idx<mem.nPage ){
|
||||
nByte = mem.szPage;
|
||||
}else{
|
||||
sqlite3_int64 *p = pPrior;
|
||||
p--;
|
||||
nByte = (int)*p;
|
||||
}
|
||||
return nByte;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free memory.
|
||||
*/
|
||||
static void memsys4Free(void *pPrior){
|
||||
sqlite3_int64 *p;
|
||||
int nByte;
|
||||
if( mmapFree(pPrior)==0 ){
|
||||
p = pPrior;
|
||||
p--;
|
||||
nByte = (int)*p;
|
||||
mem.nowUsed -= nByte;
|
||||
free(p);
|
||||
if( mem.nowUsed==0 ){
|
||||
mmapUnmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate nBytes of memory
|
||||
*/
|
||||
void *sqlite3_malloc(int nBytes){
|
||||
sqlite3_int64 *p = 0;
|
||||
if( nBytes>0 ){
|
||||
memsys4Enter();
|
||||
p = memsys4Malloc(nBytes);
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
}
|
||||
return (void*)p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free memory.
|
||||
*/
|
||||
void sqlite3_free(void *pPrior){
|
||||
if( pPrior==0 ){
|
||||
return;
|
||||
}
|
||||
assert( mem.mutex!=0 );
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
memsys4Free(pPrior);
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Change the size of an existing memory allocation
|
||||
*/
|
||||
void *sqlite3_realloc(void *pPrior, int nBytes){
|
||||
int nOld;
|
||||
sqlite3_int64 *p;
|
||||
if( pPrior==0 ){
|
||||
return sqlite3_malloc(nBytes);
|
||||
}
|
||||
if( nBytes<=0 ){
|
||||
sqlite3_free(pPrior);
|
||||
return 0;
|
||||
}
|
||||
nOld = memsys4Size(pPrior);
|
||||
if( nBytes<=nOld && nBytes>=nOld-128 ){
|
||||
return pPrior;
|
||||
}
|
||||
assert( mem.mutex!=0 );
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
p = memsys4Malloc(nBytes);
|
||||
if( p ){
|
||||
if( nOld<nBytes ){
|
||||
memcpy(p, pPrior, nOld);
|
||||
}else{
|
||||
memcpy(p, pPrior, nBytes);
|
||||
}
|
||||
memsys4Free(pPrior);
|
||||
}
|
||||
assert( mem.mutex!=0 );
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
return (void*)p;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_MMAP_HEAP_SIZE */
|
||||
+24
-51
@@ -23,38 +23,16 @@
|
||||
** This version of the memory allocation subsystem is included
|
||||
** in the build only if SQLITE_ENABLE_MEMSYS5 is defined.
|
||||
**
|
||||
** $Id: mem5.c,v 1.11 2008/07/16 12:25:32 drh Exp $
|
||||
** $Id: mem5.c,v 1.19 2008/11/19 16:52:44 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This version of the memory allocator is used only when
|
||||
** SQLITE_POW2_MEMORY_SIZE is defined.
|
||||
** SQLITE_ENABLE_MEMSYS5 is defined.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_MEMSYS5
|
||||
|
||||
/*
|
||||
** Log2 of the minimum size of an allocation. For example, if
|
||||
** 4 then all allocations will be rounded up to at least 16 bytes.
|
||||
** If 5 then all allocations will be rounded up to at least 32 bytes.
|
||||
*/
|
||||
#ifndef SQLITE_POW2_LOGMIN
|
||||
# define SQLITE_POW2_LOGMIN 6
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Log2 of the maximum size of an allocation.
|
||||
*/
|
||||
#ifndef SQLITE_POW2_LOGMAX
|
||||
# define SQLITE_POW2_LOGMAX 20
|
||||
#endif
|
||||
#define POW2_MAX (((unsigned int)1)<<SQLITE_POW2_LOGMAX)
|
||||
|
||||
/*
|
||||
** Number of distinct allocation sizes.
|
||||
*/
|
||||
#define NSIZE (SQLITE_POW2_LOGMAX - SQLITE_POW2_LOGMIN + 1)
|
||||
|
||||
/*
|
||||
** A minimum allocation is an instance of the following structure.
|
||||
** Larger allocations are an array of these structures where the
|
||||
@@ -85,18 +63,13 @@ struct Mem5Link {
|
||||
** static variables organized and to reduce namespace pollution
|
||||
** when this module is combined with other in the amalgamation.
|
||||
*/
|
||||
static struct {
|
||||
static SQLITE_WSD struct Mem5Global {
|
||||
/*
|
||||
** The alarm callback and its arguments. The mem5.mutex lock will
|
||||
** be held while the callback is running. Recursive calls into
|
||||
** the memory subsystem are allowed, but no new callbacks will be
|
||||
** issued. The alarmBusy variable is set to prevent recursive
|
||||
** callbacks.
|
||||
** Memory available for allocation
|
||||
*/
|
||||
sqlite3_int64 alarmThreshold;
|
||||
void (*alarmCallback)(void*, sqlite3_int64,int);
|
||||
void *alarmArg;
|
||||
int alarmBusy;
|
||||
int nAtom; /* Smallest possible allocation in bytes */
|
||||
int nBlock; /* Number of nAtom sized blocks in zPool */
|
||||
u8 *zPool;
|
||||
|
||||
/*
|
||||
** Mutex to control access to the memory allocation subsystem.
|
||||
@@ -126,13 +99,9 @@ static struct {
|
||||
*/
|
||||
u8 *aCtrl;
|
||||
|
||||
/*
|
||||
** Memory available for allocation
|
||||
*/
|
||||
int nAtom; /* Smallest possible allocation in bytes */
|
||||
int nBlock; /* Number of nAtom sized blocks in zPool */
|
||||
u8 *zPool;
|
||||
} mem5;
|
||||
} mem5 = { 19804167 };
|
||||
|
||||
#define mem5 GLOBAL(struct Mem5Global, mem5)
|
||||
|
||||
#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.nAtom]))
|
||||
|
||||
@@ -181,10 +150,10 @@ static void memsys5Link(int i, int iLogsize){
|
||||
/*
|
||||
** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
|
||||
** will already be held (obtained by code in malloc.c) if
|
||||
** sqlite3Config.bMemStat is true.
|
||||
** sqlite3GlobalConfig.bMemStat is true.
|
||||
*/
|
||||
static void memsys5Enter(void){
|
||||
if( sqlite3Config.bMemstat==0 && mem5.mutex==0 ){
|
||||
if( sqlite3GlobalConfig.bMemstat==0 && mem5.mutex==0 ){
|
||||
mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
}
|
||||
sqlite3_mutex_enter(mem5.mutex);
|
||||
@@ -239,12 +208,11 @@ static void *memsys5MallocUnsafe(int nByte){
|
||||
|
||||
/* Keep track of the maximum allocation request. Even unfulfilled
|
||||
** requests are counted */
|
||||
if( nByte>mem5.maxRequest ){
|
||||
if( (u32)nByte>mem5.maxRequest ){
|
||||
mem5.maxRequest = nByte;
|
||||
}
|
||||
|
||||
/* Round nByte up to the next valid power of two */
|
||||
if( nByte>POW2_MAX ) return 0;
|
||||
for(iFullSz=mem5.nAtom, iLogsize=0; iFullSz<nByte; iFullSz *= 2, iLogsize++){}
|
||||
|
||||
/* Make sure mem5.aiFreelist[iLogsize] contains at least one free
|
||||
@@ -296,12 +264,12 @@ static void memsys5FreeUnsafe(void *pOld){
|
||||
|
||||
iLogsize = mem5.aCtrl[iBlock] & CTRL_LOGSIZE;
|
||||
size = 1<<iLogsize;
|
||||
assert( iBlock+size-1<mem5.nBlock );
|
||||
assert( iBlock+size-1<(u32)mem5.nBlock );
|
||||
|
||||
mem5.aCtrl[iBlock] |= CTRL_FREE;
|
||||
mem5.aCtrl[iBlock+size-1] |= CTRL_FREE;
|
||||
assert( mem5.currentCount>0 );
|
||||
assert( mem5.currentOut>=0 );
|
||||
assert( mem5.currentOut>=(size*mem5.nAtom) );
|
||||
mem5.currentCount--;
|
||||
mem5.currentOut -= size*mem5.nAtom;
|
||||
assert( mem5.currentOut>0 || mem5.currentCount==0 );
|
||||
@@ -406,18 +374,20 @@ static int memsys5Log(int iValue){
|
||||
*/
|
||||
static int memsys5Init(void *NotUsed){
|
||||
int ii;
|
||||
int nByte = sqlite3Config.nHeap;
|
||||
u8 *zByte = (u8 *)sqlite3Config.pHeap;
|
||||
int nByte = sqlite3GlobalConfig.nHeap;
|
||||
u8 *zByte = (u8 *)sqlite3GlobalConfig.pHeap;
|
||||
int nMinLog; /* Log of minimum allocation size in bytes*/
|
||||
int iOffset;
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
if( !zByte ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
nMinLog = memsys5Log(sqlite3Config.mnReq);
|
||||
nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq);
|
||||
mem5.nAtom = (1<<nMinLog);
|
||||
while( sizeof(Mem5Link)>mem5.nAtom ){
|
||||
while( (int)sizeof(Mem5Link)>mem5.nAtom ){
|
||||
mem5.nAtom = mem5.nAtom << 1;
|
||||
}
|
||||
|
||||
@@ -447,6 +417,7 @@ static int memsys5Init(void *NotUsed){
|
||||
** Deinitialize this module.
|
||||
*/
|
||||
static void memsys5Shutdown(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -490,6 +461,8 @@ void sqlite3Memsys5Dump(const char *zFilename){
|
||||
}else{
|
||||
fclose(out);
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(zFilename);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
-496
@@ -1,496 +0,0 @@
|
||||
/*
|
||||
** 2008 July 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains an alternative memory allocation system for SQLite.
|
||||
** This system is implemented as a wrapper around the system provided
|
||||
** by the operating system - vanilla malloc(), realloc() and free().
|
||||
**
|
||||
** This system differentiates between requests for "small" allocations
|
||||
** (by default those of 128 bytes or less) and "large" allocations (all
|
||||
** others). The 256 byte threshhold is configurable at runtime.
|
||||
**
|
||||
** All requests for large allocations are passed through to the
|
||||
** default system.
|
||||
**
|
||||
** Requests for small allocations are met by allocating space within
|
||||
** one or more larger "chunks" of memory obtained from the default
|
||||
** memory allocation system. Chunks of memory are usually 64KB or
|
||||
** larger. The algorithm used to manage space within each chunk is
|
||||
** the same as that used by mem5.c.
|
||||
**
|
||||
** This strategy is designed to prevent the default memory allocation
|
||||
** system (usually the system malloc) from suffering from heap
|
||||
** fragmentation. On some systems, heap fragmentation can cause a
|
||||
** significant real-time slowdown.
|
||||
**
|
||||
** $Id: mem6.c,v 1.7 2008/07/28 19:34:53 drh Exp $
|
||||
*/
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMSYS6
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Maximum size of any "small" allocation is ((1<<LOGMAX)*Mem6Chunk.nAtom).
|
||||
** Mem6Chunk.nAtom is always at least 8, so this is not a practical
|
||||
** limitation
|
||||
*/
|
||||
#define LOGMAX 30
|
||||
|
||||
/*
|
||||
** Default value for the "small" allocation size threshold.
|
||||
*/
|
||||
#define SMALL_MALLOC_DEFAULT_THRESHOLD 256
|
||||
|
||||
/*
|
||||
** Minimum size for a memory chunk.
|
||||
*/
|
||||
#define MIN_CHUNKSIZE (1<<16)
|
||||
|
||||
#define LOG2_MINALLOC 4
|
||||
|
||||
|
||||
typedef struct Mem6Chunk Mem6Chunk;
|
||||
typedef struct Mem6Link Mem6Link;
|
||||
|
||||
/*
|
||||
** A minimum allocation is an instance of the following structure.
|
||||
** Larger allocations are an array of these structures where the
|
||||
** size of the array is a power of 2.
|
||||
*/
|
||||
struct Mem6Link {
|
||||
int next; /* Index of next free chunk */
|
||||
int prev; /* Index of previous free chunk */
|
||||
};
|
||||
|
||||
/*
|
||||
** Masks used for mem5.aCtrl[] elements.
|
||||
*/
|
||||
#define CTRL_LOGSIZE 0x1f /* Log2 Size of this block relative to POW2_MIN */
|
||||
#define CTRL_FREE 0x20 /* True if not checked out */
|
||||
|
||||
struct Mem6Chunk {
|
||||
Mem6Chunk *pNext;
|
||||
|
||||
/*
|
||||
** Lists of free blocks of various sizes.
|
||||
*/
|
||||
int aiFreelist[LOGMAX+1];
|
||||
|
||||
int nCheckedOut; /* Number of currently outstanding allocations */
|
||||
|
||||
/*
|
||||
** Space for tracking which blocks are checked out and the size
|
||||
** of each block. One byte per block.
|
||||
*/
|
||||
u8 *aCtrl;
|
||||
|
||||
/*
|
||||
** Memory available for allocation
|
||||
*/
|
||||
int nAtom; /* Smallest possible allocation in bytes */
|
||||
int nBlock; /* Number of nAtom sized blocks in zPool */
|
||||
u8 *zPool; /* Pointer to memory chunk from which allocations are made */
|
||||
};
|
||||
|
||||
#define MEM6LINK(idx) ((Mem6Link *)(&pChunk->zPool[(idx)*pChunk->nAtom]))
|
||||
|
||||
struct Mem6Global {
|
||||
int nMinAlloc; /* Minimum allowed allocation size */
|
||||
int nThreshold; /* Allocs larger than this go to malloc() */
|
||||
int nLogThreshold; /* log2 of (nThreshold/nMinAlloc) */
|
||||
sqlite3_mutex *mutex;
|
||||
Mem6Chunk *pChunk; /* Singly linked list of all memory chunks */
|
||||
} mem6;
|
||||
|
||||
/*
|
||||
** Unlink the chunk at pChunk->aPool[i] from list it is currently
|
||||
** on. It should be found on pChunk->aiFreelist[iLogsize].
|
||||
*/
|
||||
static void memsys6Unlink(Mem6Chunk *pChunk, int i, int iLogsize){
|
||||
int next, prev;
|
||||
assert( i>=0 && i<pChunk->nBlock );
|
||||
assert( iLogsize>=0 && iLogsize<=mem6.nLogThreshold );
|
||||
assert( (pChunk->aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
|
||||
|
||||
next = MEM6LINK(i)->next;
|
||||
prev = MEM6LINK(i)->prev;
|
||||
if( prev<0 ){
|
||||
pChunk->aiFreelist[iLogsize] = next;
|
||||
}else{
|
||||
MEM6LINK(prev)->next = next;
|
||||
}
|
||||
if( next>=0 ){
|
||||
MEM6LINK(next)->prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Link the chunk at mem5.aPool[i] so that is on the iLogsize
|
||||
** free list.
|
||||
*/
|
||||
static void memsys6Link(Mem6Chunk *pChunk, int i, int iLogsize){
|
||||
int x;
|
||||
assert( i>=0 && i<pChunk->nBlock );
|
||||
assert( iLogsize>=0 && iLogsize<=mem6.nLogThreshold );
|
||||
assert( (pChunk->aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
|
||||
|
||||
x = MEM6LINK(i)->next = pChunk->aiFreelist[iLogsize];
|
||||
MEM6LINK(i)->prev = -1;
|
||||
if( x>=0 ){
|
||||
assert( x<pChunk->nBlock );
|
||||
MEM6LINK(x)->prev = i;
|
||||
}
|
||||
pChunk->aiFreelist[iLogsize] = i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find the first entry on the freelist iLogsize. Unlink that
|
||||
** entry and return its index.
|
||||
*/
|
||||
static int memsys6UnlinkFirst(Mem6Chunk *pChunk, int iLogsize){
|
||||
int i;
|
||||
int iFirst;
|
||||
|
||||
assert( iLogsize>=0 && iLogsize<=mem6.nLogThreshold );
|
||||
i = iFirst = pChunk->aiFreelist[iLogsize];
|
||||
assert( iFirst>=0 );
|
||||
memsys6Unlink(pChunk, iFirst, iLogsize);
|
||||
return iFirst;
|
||||
}
|
||||
|
||||
static int roundupLog2(int n){
|
||||
static const char LogTable256[256] = {
|
||||
0, /* 1 */
|
||||
1, /* 2 */
|
||||
2, 2, /* 3..4 */
|
||||
3, 3, 3, 3, /* 5..8 */
|
||||
4, 4, 4, 4, 4, 4, 4, 4, /* 9..16 */
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 17..32 */
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 33..64 */
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 65..128 */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 129..256 */
|
||||
};
|
||||
|
||||
assert(n<=(1<<16) && n>0);
|
||||
if( n<=256 ) return LogTable256[n-1];
|
||||
return LogTable256[(n>>8) - ((n&0xFF)?0:1)] + 8;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return a block of (pChunk->nAtom << iLogsize) bytes from chunk
|
||||
** pChunk. If the allocation request cannot be satisfied, return 0.
|
||||
*/
|
||||
static void *chunkMalloc(Mem6Chunk *pChunk, int iLogsize){
|
||||
int i; /* Index of a mem5.aPool[] slot */
|
||||
int iBin; /* Index into mem5.aiFreelist[] */
|
||||
|
||||
/* Make sure mem5.aiFreelist[iLogsize] contains at least one free
|
||||
** block. If not, then split a block of the next larger power of
|
||||
** two in order to create a new free block of size iLogsize.
|
||||
*/
|
||||
for(iBin=iLogsize; pChunk->aiFreelist[iBin]<0 && iBin<=mem6.nLogThreshold; iBin++){}
|
||||
if( iBin>mem6.nLogThreshold ) return 0;
|
||||
i = memsys6UnlinkFirst(pChunk, iBin);
|
||||
while( iBin>iLogsize ){
|
||||
int newSize;
|
||||
iBin--;
|
||||
newSize = 1 << iBin;
|
||||
pChunk->aCtrl[i+newSize] = CTRL_FREE | iBin;
|
||||
memsys6Link(pChunk, i+newSize, iBin);
|
||||
}
|
||||
pChunk->aCtrl[i] = iLogsize;
|
||||
|
||||
/* Return a pointer to the allocated memory. */
|
||||
pChunk->nCheckedOut++;
|
||||
return (void*)&pChunk->zPool[i*pChunk->nAtom];
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the allocation pointed to by p, which is guaranteed to be non-zero
|
||||
** and a part of chunk object pChunk.
|
||||
*/
|
||||
static void chunkFree(Mem6Chunk *pChunk, void *pOld){
|
||||
u32 size, iLogsize;
|
||||
int iBlock;
|
||||
|
||||
/* Set iBlock to the index of the block pointed to by pOld in
|
||||
** the array of pChunk->nAtom byte blocks pointed to by pChunk->zPool.
|
||||
*/
|
||||
iBlock = ((u8 *)pOld-pChunk->zPool)/pChunk->nAtom;
|
||||
|
||||
/* Check that the pointer pOld points to a valid, non-free block. */
|
||||
assert( iBlock>=0 && iBlock<pChunk->nBlock );
|
||||
assert( ((u8 *)pOld-pChunk->zPool)%pChunk->nAtom==0 );
|
||||
assert( (pChunk->aCtrl[iBlock] & CTRL_FREE)==0 );
|
||||
|
||||
iLogsize = pChunk->aCtrl[iBlock] & CTRL_LOGSIZE;
|
||||
size = 1<<iLogsize;
|
||||
assert( iBlock+size-1<pChunk->nBlock );
|
||||
|
||||
pChunk->aCtrl[iBlock] |= CTRL_FREE;
|
||||
pChunk->aCtrl[iBlock+size-1] |= CTRL_FREE;
|
||||
|
||||
pChunk->aCtrl[iBlock] = CTRL_FREE | iLogsize;
|
||||
while( iLogsize<mem6.nLogThreshold ){
|
||||
int iBuddy;
|
||||
if( (iBlock>>iLogsize) & 1 ){
|
||||
iBuddy = iBlock - size;
|
||||
}else{
|
||||
iBuddy = iBlock + size;
|
||||
}
|
||||
assert( iBuddy>=0 );
|
||||
if( (iBuddy+(1<<iLogsize))>pChunk->nBlock ) break;
|
||||
if( pChunk->aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
|
||||
memsys6Unlink(pChunk, iBuddy, iLogsize);
|
||||
iLogsize++;
|
||||
if( iBuddy<iBlock ){
|
||||
pChunk->aCtrl[iBuddy] = CTRL_FREE | iLogsize;
|
||||
pChunk->aCtrl[iBlock] = 0;
|
||||
iBlock = iBuddy;
|
||||
}else{
|
||||
pChunk->aCtrl[iBlock] = CTRL_FREE | iLogsize;
|
||||
pChunk->aCtrl[iBuddy] = 0;
|
||||
}
|
||||
size *= 2;
|
||||
}
|
||||
pChunk->nCheckedOut--;
|
||||
memsys6Link(pChunk, iBlock, iLogsize);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the actual size of the block pointed to by p, which is guaranteed
|
||||
** to have been allocated from chunk pChunk.
|
||||
*/
|
||||
static int chunkSize(Mem6Chunk *pChunk, void *p){
|
||||
int iSize = 0;
|
||||
if( p ){
|
||||
int i = ((u8 *)p-pChunk->zPool)/pChunk->nAtom;
|
||||
assert( i>=0 && i<pChunk->nBlock );
|
||||
iSize = pChunk->nAtom * (1 << (pChunk->aCtrl[i]&CTRL_LOGSIZE));
|
||||
}
|
||||
return iSize;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if there are currently no outstanding allocations.
|
||||
*/
|
||||
static int chunkIsEmpty(Mem6Chunk *pChunk){
|
||||
return (pChunk->nCheckedOut==0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize the buffer zChunk, which is nChunk bytes in size, as
|
||||
** an Mem6Chunk object. Return a copy of the zChunk pointer.
|
||||
*/
|
||||
static Mem6Chunk *chunkInit(u8 *zChunk, int nChunk, int nMinAlloc){
|
||||
int ii;
|
||||
int iOffset;
|
||||
Mem6Chunk *pChunk = (Mem6Chunk *)zChunk;
|
||||
|
||||
assert( nChunk>sizeof(Mem6Chunk) );
|
||||
assert( nMinAlloc>sizeof(Mem6Link) );
|
||||
|
||||
memset(pChunk, 0, sizeof(Mem6Chunk));
|
||||
pChunk->nAtom = nMinAlloc;
|
||||
pChunk->nBlock = ((nChunk-sizeof(Mem6Chunk)) / (pChunk->nAtom+sizeof(u8)));
|
||||
|
||||
pChunk->zPool = (u8 *)&pChunk[1];
|
||||
pChunk->aCtrl = &pChunk->zPool[pChunk->nBlock*pChunk->nAtom];
|
||||
|
||||
for(ii=0; ii<=mem6.nLogThreshold; ii++){
|
||||
pChunk->aiFreelist[ii] = -1;
|
||||
}
|
||||
|
||||
iOffset = 0;
|
||||
for(ii=mem6.nLogThreshold; ii>=0; ii--){
|
||||
int nAlloc = (1<<ii);
|
||||
while( (iOffset+nAlloc)<=pChunk->nBlock ){
|
||||
pChunk->aCtrl[iOffset] = ii | CTRL_FREE;
|
||||
memsys6Link(pChunk, iOffset, ii);
|
||||
iOffset += nAlloc;
|
||||
}
|
||||
}
|
||||
|
||||
return pChunk;
|
||||
}
|
||||
|
||||
|
||||
static void mem6Enter(void){
|
||||
sqlite3_mutex_enter(mem6.mutex);
|
||||
}
|
||||
|
||||
static void mem6Leave(void){
|
||||
sqlite3_mutex_leave(mem6.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
** Based on the number and size of the currently allocated chunks, return
|
||||
** the size of the next chunk to allocate, in bytes.
|
||||
*/
|
||||
static int nextChunkSize(void){
|
||||
int iTotal = MIN_CHUNKSIZE;
|
||||
Mem6Chunk *p;
|
||||
for(p=mem6.pChunk; p; p=p->pNext){
|
||||
iTotal = iTotal*2;
|
||||
}
|
||||
return iTotal;
|
||||
}
|
||||
|
||||
static void freeChunk(Mem6Chunk *pChunk){
|
||||
Mem6Chunk **pp = &mem6.pChunk;
|
||||
for( pp=&mem6.pChunk; *pp!=pChunk; pp = &(*pp)->pNext );
|
||||
*pp = (*pp)->pNext;
|
||||
free(pChunk);
|
||||
}
|
||||
|
||||
static void *memsys6Malloc(int nByte){
|
||||
Mem6Chunk *pChunk;
|
||||
void *p = 0;
|
||||
int nTotal = nByte+8;
|
||||
int iOffset = 0;
|
||||
|
||||
if( nTotal>mem6.nThreshold ){
|
||||
p = malloc(nTotal);
|
||||
}else{
|
||||
int iLogsize = 0;
|
||||
if( nTotal>(1<<LOG2_MINALLOC) ){
|
||||
iLogsize = roundupLog2(nTotal) - LOG2_MINALLOC;
|
||||
}
|
||||
mem6Enter();
|
||||
for(pChunk=mem6.pChunk; pChunk; pChunk=pChunk->pNext){
|
||||
p = chunkMalloc(pChunk, iLogsize);
|
||||
if( p ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !p ){
|
||||
int iSize = nextChunkSize();
|
||||
p = malloc(iSize);
|
||||
if( p ){
|
||||
pChunk = chunkInit((u8 *)p, iSize, mem6.nMinAlloc);
|
||||
pChunk->pNext = mem6.pChunk;
|
||||
mem6.pChunk = pChunk;
|
||||
p = chunkMalloc(pChunk, iLogsize);
|
||||
assert(p);
|
||||
}
|
||||
}
|
||||
iOffset = ((u8*)p - (u8*)pChunk);
|
||||
mem6Leave();
|
||||
}
|
||||
|
||||
if( !p ){
|
||||
return 0;
|
||||
}
|
||||
((u32 *)p)[0] = iOffset;
|
||||
((u32 *)p)[1] = nByte;
|
||||
return &((u32 *)p)[2];
|
||||
}
|
||||
|
||||
static int memsys6Size(void *pPrior){
|
||||
if( pPrior==0 ) return 0;
|
||||
return ((u32*)pPrior)[-1];
|
||||
}
|
||||
|
||||
static void memsys6Free(void *pPrior){
|
||||
int iSlot;
|
||||
void *p = &((u32 *)pPrior)[-2];
|
||||
iSlot = ((u32 *)p)[0];
|
||||
if( iSlot ){
|
||||
Mem6Chunk *pChunk;
|
||||
mem6Enter();
|
||||
pChunk = (Mem6Chunk *)(&((u8 *)p)[-1 * iSlot]);
|
||||
chunkFree(pChunk, p);
|
||||
if( chunkIsEmpty(pChunk) ){
|
||||
freeChunk(pChunk);
|
||||
}
|
||||
mem6Leave();
|
||||
}else{
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void *memsys6Realloc(void *p, int nByte){
|
||||
void *p2;
|
||||
|
||||
if( p && nByte<=memsys6Size(p) ){
|
||||
p2 = p;
|
||||
}else{
|
||||
p2 = memsys6Malloc(nByte);
|
||||
if( p && p2 ){
|
||||
memcpy(p2, p, memsys6Size(p));
|
||||
memsys6Free(p);
|
||||
}
|
||||
}
|
||||
|
||||
return p2;
|
||||
}
|
||||
|
||||
static int memsys6Roundup(int n){
|
||||
if( n>mem6.nThreshold ){
|
||||
return n;
|
||||
}else{
|
||||
return (1<<roundupLog2(n));
|
||||
}
|
||||
}
|
||||
|
||||
static int memsys6Init(void *pCtx){
|
||||
u8 bMemstat = sqlite3Config.bMemstat;
|
||||
mem6.nMinAlloc = (1 << LOG2_MINALLOC);
|
||||
mem6.pChunk = 0;
|
||||
mem6.nThreshold = sqlite3Config.nSmall;
|
||||
if( mem6.nThreshold<=0 ){
|
||||
mem6.nThreshold = SMALL_MALLOC_DEFAULT_THRESHOLD;
|
||||
}
|
||||
mem6.nLogThreshold = roundupLog2(mem6.nThreshold) - LOG2_MINALLOC;
|
||||
if( !bMemstat ){
|
||||
mem6.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void memsys6Shutdown(void *pCtx){
|
||||
memset(&mem6, 0, sizeof(mem6));
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is the only routine in this file with external
|
||||
** linkage. It returns a pointer to a static sqlite3_mem_methods
|
||||
** struct populated with the memsys6 methods.
|
||||
*/
|
||||
const sqlite3_mem_methods *sqlite3MemGetMemsys6(void){
|
||||
static const sqlite3_mem_methods memsys6Methods = {
|
||||
memsys6Malloc,
|
||||
memsys6Free,
|
||||
memsys6Realloc,
|
||||
memsys6Size,
|
||||
memsys6Roundup,
|
||||
memsys6Init,
|
||||
memsys6Shutdown,
|
||||
0
|
||||
};
|
||||
return &memsys6Methods;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
** 2008 October 7
|
||||
**
|
||||
** 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 use to implement an in-memory rollback journal.
|
||||
** The in-memory rollback journal is used to journal transactions for
|
||||
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
||||
**
|
||||
** @(#) $Id: memjournal.c,v 1.8 2008/12/20 02:14:40 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* Forward references to internal structures */
|
||||
typedef struct MemJournal MemJournal;
|
||||
typedef struct FilePoint FilePoint;
|
||||
typedef struct FileChunk FileChunk;
|
||||
|
||||
/* Space to hold the rollback journal is allocated in increments of
|
||||
** this many bytes.
|
||||
*/
|
||||
#define JOURNAL_CHUNKSIZE 1024
|
||||
|
||||
/* Macro to find the minimum of two numeric values.
|
||||
*/
|
||||
#ifndef MIN
|
||||
# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The rollback journal is composed of a linked list of these structures.
|
||||
*/
|
||||
struct FileChunk {
|
||||
FileChunk *pNext; /* Next chunk in the journal */
|
||||
u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this object serves as a cursor into the rollback journal.
|
||||
** The cursor can be either for reading or writing.
|
||||
*/
|
||||
struct FilePoint {
|
||||
sqlite3_int64 iOffset; /* Offset from the beginning of the file */
|
||||
FileChunk *pChunk; /* Specific chunk into which cursor points */
|
||||
};
|
||||
|
||||
/*
|
||||
** This subclass is a subclass of sqlite3_file. Each open memory-journal
|
||||
** is an instance of this class.
|
||||
*/
|
||||
struct MemJournal {
|
||||
sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
|
||||
FileChunk *pFirst; /* Head of in-memory chunk-list */
|
||||
FilePoint endpoint; /* Pointer to the end of the file */
|
||||
FilePoint readpoint; /* Pointer to the end of the last xRead() */
|
||||
};
|
||||
|
||||
/*
|
||||
** Read data from the file.
|
||||
*/
|
||||
static int memjrnlRead(
|
||||
sqlite3_file *pJfd, /* The journal file from which to read */
|
||||
void *zBuf, /* Put the results here */
|
||||
int iAmt, /* Number of bytes to read */
|
||||
sqlite_int64 iOfst /* Begin reading at this offset */
|
||||
){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
u8 *zOut = zBuf;
|
||||
int nRead = iAmt;
|
||||
int iChunkOffset;
|
||||
FileChunk *pChunk;
|
||||
|
||||
assert( iOfst+iAmt<=p->endpoint.iOffset );
|
||||
|
||||
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
|
||||
sqlite3_int64 iOff = 0;
|
||||
for(pChunk=p->pFirst;
|
||||
pChunk && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
|
||||
pChunk=pChunk->pNext
|
||||
){
|
||||
iOff += JOURNAL_CHUNKSIZE;
|
||||
}
|
||||
}else{
|
||||
pChunk = p->readpoint.pChunk;
|
||||
}
|
||||
|
||||
iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
|
||||
do {
|
||||
int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
|
||||
int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
|
||||
memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
|
||||
zOut += nCopy;
|
||||
nRead -= iSpace;
|
||||
iChunkOffset = 0;
|
||||
} while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
|
||||
p->readpoint.iOffset = iOfst+iAmt;
|
||||
p->readpoint.pChunk = pChunk;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to the file.
|
||||
*/
|
||||
static int memjrnlWrite(
|
||||
sqlite3_file *pJfd, /* The journal file into which to write */
|
||||
const void *zBuf, /* Take data to be written from here */
|
||||
int iAmt, /* Number of bytes to write */
|
||||
sqlite_int64 iOfst /* Begin writing at this offset into the file */
|
||||
){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
int nWrite = iAmt;
|
||||
u8 *zWrite = (u8 *)zBuf;
|
||||
|
||||
/* An in-memory journal file should only ever be appended to. Random
|
||||
** access writes are not required by sqlite.
|
||||
*/
|
||||
assert(iOfst==p->endpoint.iOffset);
|
||||
UNUSED_PARAMETER(iOfst);
|
||||
|
||||
while( nWrite>0 ){
|
||||
FileChunk *pChunk = p->endpoint.pChunk;
|
||||
int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
|
||||
int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
|
||||
|
||||
if( iChunkOffset==0 ){
|
||||
/* New chunk is required to extend the file. */
|
||||
FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
|
||||
if( !pNew ){
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
pNew->pNext = 0;
|
||||
if( pChunk ){
|
||||
assert( p->pFirst );
|
||||
pChunk->pNext = pNew;
|
||||
}else{
|
||||
assert( !p->pFirst );
|
||||
p->pFirst = pNew;
|
||||
}
|
||||
p->endpoint.pChunk = pNew;
|
||||
}
|
||||
|
||||
memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
|
||||
zWrite += iSpace;
|
||||
nWrite -= iSpace;
|
||||
p->endpoint.iOffset += iSpace;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate the file.
|
||||
*/
|
||||
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
FileChunk *pChunk;
|
||||
assert(size==0);
|
||||
UNUSED_PARAMETER(size);
|
||||
pChunk = p->pFirst;
|
||||
while( pChunk ){
|
||||
FileChunk *pTmp = pChunk;
|
||||
pChunk = pChunk->pNext;
|
||||
sqlite3_free(pTmp);
|
||||
}
|
||||
sqlite3MemJournalOpen(pJfd);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the file.
|
||||
*/
|
||||
static int memjrnlClose(sqlite3_file *pJfd){
|
||||
memjrnlTruncate(pJfd, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Sync the file.
|
||||
*/
|
||||
static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the size of the file in bytes.
|
||||
*/
|
||||
static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
*pSize = (sqlite_int64) p->endpoint.iOffset;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table of methods for MemJournal sqlite3_file object.
|
||||
*/
|
||||
static struct sqlite3_io_methods MemJournalMethods = {
|
||||
1, /* iVersion */
|
||||
memjrnlClose, /* xClose */
|
||||
memjrnlRead, /* xRead */
|
||||
memjrnlWrite, /* xWrite */
|
||||
memjrnlTruncate, /* xTruncate */
|
||||
memjrnlSync, /* xSync */
|
||||
memjrnlFileSize, /* xFileSize */
|
||||
0, /* xLock */
|
||||
0, /* xUnlock */
|
||||
0, /* xCheckReservedLock */
|
||||
0, /* xFileControl */
|
||||
0, /* xSectorSize */
|
||||
0 /* xDeviceCharacteristics */
|
||||
};
|
||||
|
||||
/*
|
||||
** Open a journal file.
|
||||
*/
|
||||
void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
memset(p, 0, sqlite3MemJournalSize());
|
||||
p->pMethod = &MemJournalMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the file-handle passed as an argument is
|
||||
** an in-memory journal
|
||||
*/
|
||||
int sqlite3IsMemJournal(sqlite3_file *pJfd){
|
||||
return pJfd->pMethods==&MemJournalMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to store a MemJournal that uses vfs
|
||||
** pVfs to create the underlying on-disk files.
|
||||
*/
|
||||
int sqlite3MemJournalSize(void){
|
||||
return sizeof(MemJournal);
|
||||
}
|
||||
+26
-150
@@ -11,35 +11,30 @@
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement mutexes.
|
||||
**
|
||||
** The implementation in this file does not provide any mutual
|
||||
** exclusion and is thus suitable for use only in applications
|
||||
** that use SQLite in a single thread. But this implementation
|
||||
** does do a lot of error checking on mutexes to make sure they
|
||||
** are called correctly and at appropriate times. Hence, this
|
||||
** implementation is suitable for testing.
|
||||
** debugging purposes
|
||||
** This file contains code that is common across all mutex implementations.
|
||||
|
||||
**
|
||||
** $Id: mutex.c,v 1.27 2008/06/19 08:51:24 danielk1977 Exp $
|
||||
** $Id: mutex.c,v 1.30 2009/02/17 16:29:11 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#ifndef SQLITE_MUTEX_OMIT
|
||||
/*
|
||||
** Initialize the mutex system.
|
||||
*/
|
||||
int sqlite3MutexInit(void){
|
||||
int rc = SQLITE_OK;
|
||||
if( sqlite3Config.bCoreMutex ){
|
||||
if( !sqlite3Config.mutex.xMutexAlloc ){
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){
|
||||
/* If the xMutexAlloc method has not been set, then the user did not
|
||||
** install a mutex implementation via sqlite3_config() prior to
|
||||
** sqlite3_initialize() being called. This block copies pointers to
|
||||
** the default implementation into the sqlite3Config structure.
|
||||
** the default implementation into the sqlite3GlobalConfig structure.
|
||||
**
|
||||
** The danger is that although sqlite3_config() is not a threadsafe
|
||||
** API, sqlite3_initialize() is, and so multiple threads may be
|
||||
** attempting to run this function simultaneously. To guard write
|
||||
** access to the sqlite3Config structure, the 'MASTER' static mutex
|
||||
** access to the sqlite3GlobalConfig structure, the 'MASTER' static mutex
|
||||
** is obtained before modifying it.
|
||||
*/
|
||||
sqlite3_mutex_methods *p = sqlite3DefaultMutex();
|
||||
@@ -50,16 +45,16 @@ int sqlite3MutexInit(void){
|
||||
pMaster = p->xMutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
assert(pMaster);
|
||||
p->xMutexEnter(pMaster);
|
||||
assert( sqlite3Config.mutex.xMutexAlloc==0
|
||||
|| sqlite3Config.mutex.xMutexAlloc==p->xMutexAlloc
|
||||
assert( sqlite3GlobalConfig.mutex.xMutexAlloc==0
|
||||
|| sqlite3GlobalConfig.mutex.xMutexAlloc==p->xMutexAlloc
|
||||
);
|
||||
if( !sqlite3Config.mutex.xMutexAlloc ){
|
||||
sqlite3Config.mutex = *p;
|
||||
if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){
|
||||
sqlite3GlobalConfig.mutex = *p;
|
||||
}
|
||||
p->xMutexLeave(pMaster);
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3Config.mutex.xMutexInit();
|
||||
rc = sqlite3GlobalConfig.mutex.xMutexInit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +67,9 @@ int sqlite3MutexInit(void){
|
||||
*/
|
||||
int sqlite3MutexEnd(void){
|
||||
int rc = SQLITE_OK;
|
||||
rc = sqlite3Config.mutex.xMutexEnd();
|
||||
if( sqlite3GlobalConfig.mutex.xMutexEnd ){
|
||||
rc = sqlite3GlobalConfig.mutex.xMutexEnd();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -83,14 +80,14 @@ sqlite3_mutex *sqlite3_mutex_alloc(int id){
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
#endif
|
||||
return sqlite3Config.mutex.xMutexAlloc(id);
|
||||
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
||||
}
|
||||
|
||||
sqlite3_mutex *sqlite3MutexAlloc(int id){
|
||||
if( !sqlite3Config.bCoreMutex ){
|
||||
if( !sqlite3GlobalConfig.bCoreMutex ){
|
||||
return 0;
|
||||
}
|
||||
return sqlite3Config.mutex.xMutexAlloc(id);
|
||||
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,7 +95,7 @@ sqlite3_mutex *sqlite3MutexAlloc(int id){
|
||||
*/
|
||||
void sqlite3_mutex_free(sqlite3_mutex *p){
|
||||
if( p ){
|
||||
sqlite3Config.mutex.xMutexFree(p);
|
||||
sqlite3GlobalConfig.mutex.xMutexFree(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +105,7 @@ void sqlite3_mutex_free(sqlite3_mutex *p){
|
||||
*/
|
||||
void sqlite3_mutex_enter(sqlite3_mutex *p){
|
||||
if( p ){
|
||||
sqlite3Config.mutex.xMutexEnter(p);
|
||||
sqlite3GlobalConfig.mutex.xMutexEnter(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +116,7 @@ void sqlite3_mutex_enter(sqlite3_mutex *p){
|
||||
int sqlite3_mutex_try(sqlite3_mutex *p){
|
||||
int rc = SQLITE_OK;
|
||||
if( p ){
|
||||
return sqlite3Config.mutex.xMutexTry(p);
|
||||
return sqlite3GlobalConfig.mutex.xMutexTry(p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -132,7 +129,7 @@ int sqlite3_mutex_try(sqlite3_mutex *p){
|
||||
*/
|
||||
void sqlite3_mutex_leave(sqlite3_mutex *p){
|
||||
if( p ){
|
||||
sqlite3Config.mutex.xMutexLeave(p);
|
||||
sqlite3GlobalConfig.mutex.xMutexLeave(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,132 +139,11 @@ void sqlite3_mutex_leave(sqlite3_mutex *p){
|
||||
** intended for use inside assert() statements.
|
||||
*/
|
||||
int sqlite3_mutex_held(sqlite3_mutex *p){
|
||||
return p==0 || sqlite3Config.mutex.xMutexHeld(p);
|
||||
return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
|
||||
}
|
||||
int sqlite3_mutex_notheld(sqlite3_mutex *p){
|
||||
return p==0 || sqlite3Config.mutex.xMutexNotheld(p);
|
||||
return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_MUTEX_NOOP_DEBUG
|
||||
/*
|
||||
** In this implementation, mutexes do not provide any mutual exclusion.
|
||||
** But the error checking is provided. This implementation is useful
|
||||
** for test purposes.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The mutex object
|
||||
*/
|
||||
struct sqlite3_mutex {
|
||||
int id; /* The mutex type */
|
||||
int cnt; /* Number of entries without a matching leave */
|
||||
};
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
||||
** intended for use inside assert() statements.
|
||||
*/
|
||||
static int noopMutexHeld(sqlite3_mutex *p){
|
||||
return p==0 || p->cnt>0;
|
||||
}
|
||||
static int noopMutexNotheld(sqlite3_mutex *p){
|
||||
return p==0 || p->cnt==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static int noopMutexInit(void){ return SQLITE_OK; }
|
||||
static int noopMutexEnd(void){ return SQLITE_OK; }
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_alloc() routine allocates a new
|
||||
** mutex and returns a pointer to it. If it returns NULL
|
||||
** that means that a mutex could not be allocated.
|
||||
*/
|
||||
static sqlite3_mutex *noopMutexAlloc(int id){
|
||||
static sqlite3_mutex aStatic[6];
|
||||
sqlite3_mutex *pNew = 0;
|
||||
switch( id ){
|
||||
case SQLITE_MUTEX_FAST:
|
||||
case SQLITE_MUTEX_RECURSIVE: {
|
||||
pNew = sqlite3Malloc(sizeof(*pNew));
|
||||
if( pNew ){
|
||||
pNew->id = id;
|
||||
pNew->cnt = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( id-2 >= 0 );
|
||||
assert( id-2 < sizeof(aStatic)/sizeof(aStatic[0]) );
|
||||
pNew = &aStatic[id-2];
|
||||
pNew->id = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine deallocates a previously allocated mutex.
|
||||
*/
|
||||
static void noopMutexFree(sqlite3_mutex *p){
|
||||
assert( p->cnt==0 );
|
||||
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
||||
** to enter a mutex. If another thread is already within the mutex,
|
||||
** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
||||
** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
|
||||
** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
|
||||
** be entered multiple times by the same thread. In such cases the,
|
||||
** mutex must be exited an equal number of times before another thread
|
||||
** can enter. If the same thread tries to enter any other kind of mutex
|
||||
** more than once, the behavior is undefined.
|
||||
*/
|
||||
static void noopMutexEnter(sqlite3_mutex *p){
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || noopMutexNotheld(p) );
|
||||
p->cnt++;
|
||||
}
|
||||
static int noopMutexTry(sqlite3_mutex *p){
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || noopMutexNotheld(p) );
|
||||
p->cnt++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_leave() routine exits a mutex that was
|
||||
** previously entered by the same thread. The behavior
|
||||
** is undefined if the mutex is not currently entered or
|
||||
** is not currently allocated. SQLite will never do either.
|
||||
*/
|
||||
static void noopMutexLeave(sqlite3_mutex *p){
|
||||
assert( noopMutexHeld(p) );
|
||||
p->cnt--;
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || noopMutexNotheld(p) );
|
||||
}
|
||||
|
||||
sqlite3_mutex_methods *sqlite3DefaultMutex(void){
|
||||
static sqlite3_mutex_methods sMutex = {
|
||||
noopMutexInit,
|
||||
noopMutexEnd,
|
||||
noopMutexAlloc,
|
||||
noopMutexFree,
|
||||
noopMutexEnter,
|
||||
noopMutexTry,
|
||||
noopMutexLeave,
|
||||
|
||||
noopMutexHeld,
|
||||
noopMutexNotheld
|
||||
};
|
||||
|
||||
return &sMutex;
|
||||
}
|
||||
#endif /* SQLITE_MUTEX_NOOP_DEBUG */
|
||||
#endif /* SQLITE_OMIT_MUTEX */
|
||||
|
||||
+22
-34
@@ -19,28 +19,21 @@
|
||||
** Source files should #include the sqliteInt.h file and let that file
|
||||
** include this one indirectly.
|
||||
**
|
||||
** $Id: mutex.h,v 1.8 2008/06/26 10:41:19 danielk1977 Exp $
|
||||
** $Id: mutex.h,v 1.9 2008/10/07 15:25:48 drh Exp $
|
||||
*/
|
||||
|
||||
|
||||
#ifdef SQLITE_MUTEX_APPDEF
|
||||
/*
|
||||
** If SQLITE_MUTEX_APPDEF is defined, then this whole module is
|
||||
** omitted and equivalent functionality must be provided by the
|
||||
** application that links against the SQLite library.
|
||||
*/
|
||||
#else
|
||||
/*
|
||||
** Figure out what version of the code to use. The choices are
|
||||
**
|
||||
** SQLITE_MUTEX_NOOP For single-threaded applications that
|
||||
** do not desire error checking.
|
||||
** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The
|
||||
** mutexes implemention cannot be overridden
|
||||
** at start-time.
|
||||
**
|
||||
** SQLITE_MUTEX_NOOP_DEBUG For single-threaded applications with
|
||||
** error checking to help verify that mutexes
|
||||
** are being used correctly even though they
|
||||
** are not needed. Used when SQLITE_DEBUG is
|
||||
** defined on single-threaded builds.
|
||||
** SQLITE_MUTEX_NOOP For single-threaded applications. No
|
||||
** mutual exclusion is provided. But this
|
||||
** implementation can be overridden at
|
||||
** start-time.
|
||||
**
|
||||
** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix.
|
||||
**
|
||||
@@ -48,25 +41,22 @@
|
||||
**
|
||||
** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2.
|
||||
*/
|
||||
#define SQLITE_MUTEX_NOOP 1 /* The default */
|
||||
#if defined(SQLITE_DEBUG) && !SQLITE_THREADSAFE
|
||||
# undef SQLITE_MUTEX_NOOP
|
||||
# define SQLITE_MUTEX_NOOP_DEBUG
|
||||
#if !SQLITE_THREADSAFE
|
||||
# define SQLITE_MUTEX_OMIT
|
||||
#endif
|
||||
#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && SQLITE_OS_UNIX
|
||||
# undef SQLITE_MUTEX_NOOP
|
||||
# define SQLITE_MUTEX_PTHREADS
|
||||
#endif
|
||||
#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && SQLITE_OS_WIN
|
||||
# undef SQLITE_MUTEX_NOOP
|
||||
# define SQLITE_MUTEX_W32
|
||||
#endif
|
||||
#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && SQLITE_OS_OS2
|
||||
# undef SQLITE_MUTEX_NOOP
|
||||
# define SQLITE_MUTEX_OS2
|
||||
#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP)
|
||||
# if SQLITE_OS_UNIX
|
||||
# define SQLITE_MUTEX_PTHREADS
|
||||
# elif SQLITE_OS_WIN
|
||||
# define SQLITE_MUTEX_W32
|
||||
# elif SQLITE_OS_OS2
|
||||
# define SQLITE_MUTEX_OS2
|
||||
# else
|
||||
# define SQLITE_MUTEX_NOOP
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_MUTEX_NOOP
|
||||
#ifdef SQLITE_MUTEX_OMIT
|
||||
/*
|
||||
** If this is a no-op implementation, implement everything as macros.
|
||||
*/
|
||||
@@ -80,6 +70,4 @@
|
||||
#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
|
||||
#define sqlite3MutexInit() SQLITE_OK
|
||||
#define sqlite3MutexEnd()
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE_MUTEX_APPDEF */
|
||||
#endif /* defined(SQLITE_OMIT_MUTEX) */
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
** 2008 October 07
|
||||
**
|
||||
** 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 the C functions that implement mutexes.
|
||||
**
|
||||
** This implementation in this file does not provide any mutual
|
||||
** exclusion and is thus suitable for use only in applications
|
||||
** that use SQLite in a single thread. The routines defined
|
||||
** here are place-holders. Applications can substitute working
|
||||
** mutex routines at start-time using the
|
||||
**
|
||||
** sqlite3_config(SQLITE_CONFIG_MUTEX,...)
|
||||
**
|
||||
** interface.
|
||||
**
|
||||
** If compiled with SQLITE_DEBUG, then additional logic is inserted
|
||||
** that does error checking on mutexes to make sure they are being
|
||||
** called correctly.
|
||||
**
|
||||
** $Id: mutex_noop.c,v 1.3 2008/12/05 17:17:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
||||
#if defined(SQLITE_MUTEX_NOOP) && !defined(SQLITE_DEBUG)
|
||||
/*
|
||||
** Stub routines for all mutex methods.
|
||||
**
|
||||
** This routines provide no mutual exclusion or error checking.
|
||||
*/
|
||||
static int noopMutexHeld(sqlite3_mutex *p){ return 1; }
|
||||
static int noopMutexNotheld(sqlite3_mutex *p){ return 1; }
|
||||
static int noopMutexInit(void){ return SQLITE_OK; }
|
||||
static int noopMutexEnd(void){ return SQLITE_OK; }
|
||||
static sqlite3_mutex *noopMutexAlloc(int id){ return (sqlite3_mutex*)8; }
|
||||
static void noopMutexFree(sqlite3_mutex *p){ return; }
|
||||
static void noopMutexEnter(sqlite3_mutex *p){ return; }
|
||||
static int noopMutexTry(sqlite3_mutex *p){ return SQLITE_OK; }
|
||||
static void noopMutexLeave(sqlite3_mutex *p){ return; }
|
||||
|
||||
sqlite3_mutex_methods *sqlite3DefaultMutex(void){
|
||||
static sqlite3_mutex_methods sMutex = {
|
||||
noopMutexInit,
|
||||
noopMutexEnd,
|
||||
noopMutexAlloc,
|
||||
noopMutexFree,
|
||||
noopMutexEnter,
|
||||
noopMutexTry,
|
||||
noopMutexLeave,
|
||||
|
||||
noopMutexHeld,
|
||||
noopMutexNotheld
|
||||
};
|
||||
|
||||
return &sMutex;
|
||||
}
|
||||
#endif /* defined(SQLITE_MUTEX_NOOP) && !defined(SQLITE_DEBUG) */
|
||||
|
||||
#if defined(SQLITE_MUTEX_NOOP) && defined(SQLITE_DEBUG)
|
||||
/*
|
||||
** In this implementation, error checking is provided for testing
|
||||
** and debugging purposes. The mutexes still do not provide any
|
||||
** mutual exclusion.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The mutex object
|
||||
*/
|
||||
struct sqlite3_mutex {
|
||||
int id; /* The mutex type */
|
||||
int cnt; /* Number of entries without a matching leave */
|
||||
};
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
||||
** intended for use inside assert() statements.
|
||||
*/
|
||||
static int debugMutexHeld(sqlite3_mutex *p){
|
||||
return p==0 || p->cnt>0;
|
||||
}
|
||||
static int debugMutexNotheld(sqlite3_mutex *p){
|
||||
return p==0 || p->cnt==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static int debugMutexInit(void){ return SQLITE_OK; }
|
||||
static int debugMutexEnd(void){ return SQLITE_OK; }
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_alloc() routine allocates a new
|
||||
** mutex and returns a pointer to it. If it returns NULL
|
||||
** that means that a mutex could not be allocated.
|
||||
*/
|
||||
static sqlite3_mutex *debugMutexAlloc(int id){
|
||||
static sqlite3_mutex aStatic[6];
|
||||
sqlite3_mutex *pNew = 0;
|
||||
switch( id ){
|
||||
case SQLITE_MUTEX_FAST:
|
||||
case SQLITE_MUTEX_RECURSIVE: {
|
||||
pNew = sqlite3Malloc(sizeof(*pNew));
|
||||
if( pNew ){
|
||||
pNew->id = id;
|
||||
pNew->cnt = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( id-2 >= 0 );
|
||||
assert( id-2 < (int)(sizeof(aStatic)/sizeof(aStatic[0])) );
|
||||
pNew = &aStatic[id-2];
|
||||
pNew->id = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine deallocates a previously allocated mutex.
|
||||
*/
|
||||
static void debugMutexFree(sqlite3_mutex *p){
|
||||
assert( p->cnt==0 );
|
||||
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
||||
** to enter a mutex. If another thread is already within the mutex,
|
||||
** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
||||
** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
|
||||
** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
|
||||
** be entered multiple times by the same thread. In such cases the,
|
||||
** mutex must be exited an equal number of times before another thread
|
||||
** can enter. If the same thread tries to enter any other kind of mutex
|
||||
** more than once, the behavior is undefined.
|
||||
*/
|
||||
static void debugMutexEnter(sqlite3_mutex *p){
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(p) );
|
||||
p->cnt++;
|
||||
}
|
||||
static int debugMutexTry(sqlite3_mutex *p){
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(p) );
|
||||
p->cnt++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_mutex_leave() routine exits a mutex that was
|
||||
** previously entered by the same thread. The behavior
|
||||
** is undefined if the mutex is not currently entered or
|
||||
** is not currently allocated. SQLite will never do either.
|
||||
*/
|
||||
static void debugMutexLeave(sqlite3_mutex *p){
|
||||
assert( debugMutexHeld(p) );
|
||||
p->cnt--;
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(p) );
|
||||
}
|
||||
|
||||
sqlite3_mutex_methods *sqlite3DefaultMutex(void){
|
||||
static sqlite3_mutex_methods sMutex = {
|
||||
debugMutexInit,
|
||||
debugMutexEnd,
|
||||
debugMutexAlloc,
|
||||
debugMutexFree,
|
||||
debugMutexEnter,
|
||||
debugMutexTry,
|
||||
debugMutexLeave,
|
||||
|
||||
debugMutexHeld,
|
||||
debugMutexNotheld
|
||||
};
|
||||
|
||||
return &sMutex;
|
||||
}
|
||||
#endif /* defined(SQLITE_MUTEX_NOOP) && defined(SQLITE_DEBUG) */
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement mutexes for OS/2
|
||||
**
|
||||
** $Id: mutex_os2.c,v 1.10 2008/06/23 22:13:28 pweilbacher Exp $
|
||||
** $Id: mutex_os2.c,v 1.11 2008/11/22 19:50:54 pweilbacher Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -122,7 +122,7 @@ static sqlite3_mutex *os2MutexAlloc(int iType){
|
||||
mutex = 0;
|
||||
rc = DosCreateMutexSem( name, &mutex, 0, FALSE);
|
||||
if( rc == NO_ERROR ){
|
||||
int i;
|
||||
unsigned int i;
|
||||
if( !isInit ){
|
||||
for( i = 0; i < sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++ ){
|
||||
DosCreateMutexSem( 0, &staticMutexes[i].mutex, 0, FALSE );
|
||||
|
||||
+6
-3
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement mutexes for pthreads
|
||||
**
|
||||
** $Id: mutex_unix.c,v 1.13 2008/07/16 12:33:24 drh Exp $
|
||||
** $Id: mutex_unix.c,v 1.16 2008/12/08 18:19:18 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -61,7 +61,7 @@ struct sqlite3_mutex {
|
||||
** make sure no assert() statements are evaluated and hence these
|
||||
** routines are never called.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
#if !defined(NDEBUG) || defined(SQLITE_DEBUG)
|
||||
static int pthreadMutexHeld(sqlite3_mutex *p){
|
||||
return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
|
||||
}
|
||||
@@ -157,7 +157,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
}
|
||||
default: {
|
||||
assert( iType-2 >= 0 );
|
||||
assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
|
||||
assert( iType-2 < ArraySize(staticMutexes) );
|
||||
p = &staticMutexes[iType-2];
|
||||
p->id = iType;
|
||||
break;
|
||||
@@ -316,6 +316,9 @@ sqlite3_mutex_methods *sqlite3DefaultMutex(void){
|
||||
#ifdef SQLITE_DEBUG
|
||||
pthreadMutexHeld,
|
||||
pthreadMutexNotheld
|
||||
#else
|
||||
0,
|
||||
0
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
+14
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement mutexes for win32
|
||||
**
|
||||
** $Id: mutex_w32.c,v 1.11 2008/06/26 10:41:19 danielk1977 Exp $
|
||||
** $Id: mutex_w32.c,v 1.15 2009/01/30 16:09:23 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -41,7 +41,14 @@ struct sqlite3_mutex {
|
||||
** this routine is used to determine if the host is Win95/98/ME or
|
||||
** WinNT/2K/XP so that we will know whether or not we can safely call
|
||||
** the LockFileEx() API.
|
||||
**
|
||||
** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
|
||||
** which is only available if your application was compiled with
|
||||
** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
|
||||
** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
|
||||
** this out as well.
|
||||
*/
|
||||
#if 0
|
||||
#if SQLITE_OS_WINCE
|
||||
# define mutexIsNT() (1)
|
||||
#else
|
||||
@@ -56,7 +63,7 @@ struct sqlite3_mutex {
|
||||
return osType==2;
|
||||
}
|
||||
#endif /* SQLITE_OS_WINCE */
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
@@ -206,6 +213,8 @@ static int winMutexTry(sqlite3_mutex *p){
|
||||
p->nRef++;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(p);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
@@ -236,6 +245,9 @@ sqlite3_mutex_methods *sqlite3DefaultMutex(void){
|
||||
#ifdef SQLITE_DEBUG
|
||||
winMutexHeld,
|
||||
winMutexNotheld
|
||||
#else
|
||||
0,
|
||||
0
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** This file contains OS interface code that is common to all
|
||||
** architectures.
|
||||
**
|
||||
** $Id: os.c,v 1.120 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: os.c,v 1.125 2008/12/08 18:19:18 drh Exp $
|
||||
*/
|
||||
#define _SQLITE_OS_C_ 1
|
||||
#include "sqliteInt.h"
|
||||
@@ -36,7 +36,7 @@
|
||||
** sqlite3OsLock()
|
||||
**
|
||||
*/
|
||||
#if defined(SQLITE_TEST) && (SQLITE_OS_WIN==0) && 0
|
||||
#if defined(SQLITE_TEST) && (SQLITE_OS_WIN==0)
|
||||
#define DO_OS_MALLOC_TEST if (1) { \
|
||||
void *pTstAlloc = sqlite3Malloc(10); \
|
||||
if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \
|
||||
@@ -142,8 +142,8 @@ void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
pVfs->xDlError(pVfs, nByte, zBufOut);
|
||||
}
|
||||
void *sqlite3OsDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
|
||||
return pVfs->xDlSym(pVfs, pHandle, zSymbol);
|
||||
void (*sqlite3OsDlSym(sqlite3_vfs *pVfs, void *pHdle, const char *zSym))(void){
|
||||
return pVfs->xDlSym(pVfs, pHdle, zSym);
|
||||
}
|
||||
void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
pVfs->xDlClose(pVfs, pHandle);
|
||||
@@ -190,7 +190,8 @@ int sqlite3OsCloseFree(sqlite3_file *pFile){
|
||||
/*
|
||||
** The list of all registered VFS implementations.
|
||||
*/
|
||||
static sqlite3_vfs *vfsList = 0;
|
||||
static sqlite3_vfs * SQLITE_WSD vfsList = 0;
|
||||
#define vfsList GLOBAL(sqlite3_vfs *, vfsList)
|
||||
|
||||
/*
|
||||
** Locate a VFS by name. If no name is given, simply return the
|
||||
@@ -198,14 +199,14 @@ static sqlite3_vfs *vfsList = 0;
|
||||
*/
|
||||
sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
|
||||
sqlite3_vfs *pVfs = 0;
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex;
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
if( rc ) return 0;
|
||||
#endif
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
@@ -267,7 +268,7 @@ int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
|
||||
** Unregister a VFS so that it is no longer accessible.
|
||||
*/
|
||||
int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
** This header file is #include-ed by sqliteInt.h and thus ends up
|
||||
** being included by every source file.
|
||||
**
|
||||
** $Id: os.h,v 1.105 2008/06/26 10:41:19 danielk1977 Exp $
|
||||
** $Id: os.h,v 1.108 2009/02/05 16:31:46 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_OS_H_
|
||||
#define _SQLITE_OS_H_
|
||||
@@ -195,9 +195,7 @@
|
||||
** a random byte is selected for a shared lock. The pool of bytes for
|
||||
** shared locks begins at SHARED_FIRST.
|
||||
**
|
||||
** These #defines are available in sqlite_aux.h so that adaptors for
|
||||
** connecting SQLite to other operating systems can use the same byte
|
||||
** ranges for locking. In particular, the same locking strategy and
|
||||
** The same locking strategy and
|
||||
** byte ranges are used for Unix. This leaves open the possiblity of having
|
||||
** clients on win95, winNT, and unix all talking to the same shared file
|
||||
** and all locking correctly. To do so would require that samba (or whatever
|
||||
@@ -221,13 +219,7 @@
|
||||
** 1GB boundary.
|
||||
**
|
||||
*/
|
||||
#ifndef SQLITE_TEST
|
||||
#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */
|
||||
#else
|
||||
extern unsigned int sqlite3_pending_byte;
|
||||
#define PENDING_BYTE sqlite3_pending_byte
|
||||
#endif
|
||||
|
||||
#define PENDING_BYTE sqlite3PendingByte
|
||||
#define RESERVED_BYTE (PENDING_BYTE+1)
|
||||
#define SHARED_FIRST (PENDING_BYTE+2)
|
||||
#define SHARED_SIZE 510
|
||||
@@ -245,6 +237,7 @@ int sqlite3OsLock(sqlite3_file*, int);
|
||||
int sqlite3OsUnlock(sqlite3_file*, int);
|
||||
int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
|
||||
int sqlite3OsFileControl(sqlite3_file*,int,void*);
|
||||
#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
|
||||
int sqlite3OsSectorSize(sqlite3_file *id);
|
||||
int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
|
||||
|
||||
@@ -258,7 +251,7 @@ int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *);
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
void *sqlite3OsDlOpen(sqlite3_vfs *, const char *);
|
||||
void sqlite3OsDlError(sqlite3_vfs *, int, char *);
|
||||
void *sqlite3OsDlSym(sqlite3_vfs *, void *, const char *);
|
||||
void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);
|
||||
void sqlite3OsDlClose(sqlite3_vfs *, void *);
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
|
||||
|
||||
+25
-17
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** This file contains code that is specific to OS/2.
|
||||
**
|
||||
** $Id: os_os2.c,v 1.55 2008/07/29 18:49:29 pweilbacher Exp $
|
||||
** $Id: os_os2.c,v 1.63 2008/12/10 19:26:24 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -124,6 +124,7 @@ static int os2Read(
|
||||
if( got == (ULONG)amt )
|
||||
return SQLITE_OK;
|
||||
else {
|
||||
/* Unread portions of the input buffer must be zero-filled */
|
||||
memset(&((char*)pBuf)[got], 0, amt-got);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
@@ -171,7 +172,7 @@ static int os2Truncate( sqlite3_file *id, i64 nByte ){
|
||||
OSTRACE3( "TRUNCATE %d %lld\n", pFile->h, nByte );
|
||||
SimulateIOError( return SQLITE_IOERR_TRUNCATE );
|
||||
rc = DosSetFileSize( pFile->h, nByte );
|
||||
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
||||
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
@@ -195,7 +196,15 @@ static int os2Sync( sqlite3_file *id, int flags ){
|
||||
}
|
||||
sqlite3_sync_count++;
|
||||
#endif
|
||||
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
|
||||
** no-op
|
||||
*/
|
||||
#ifdef SQLITE_NO_SYNC
|
||||
UNUSED_PARAMETER(pFile);
|
||||
return SQLITE_OK;
|
||||
#else
|
||||
return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -206,13 +215,13 @@ static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
|
||||
FILESTATUS3 fsts3FileInfo;
|
||||
memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
|
||||
assert( id!=0 );
|
||||
SimulateIOError( return SQLITE_IOERR );
|
||||
SimulateIOError( return SQLITE_IOERR_FSTAT );
|
||||
rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
|
||||
if( rc == NO_ERROR ){
|
||||
*pSize = fsts3FileInfo.cbFile;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
return SQLITE_IOERR;
|
||||
return SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +694,7 @@ static int getTempname(int nBuf, char *zBuf ){
|
||||
/* Strip off a trailing slashes or backslashes, otherwise we would get *
|
||||
* multiple (back)slashes which causes DosOpen() to fail. *
|
||||
* Trailing spaces are not allowed, either. */
|
||||
j = strlen(zTempPath);
|
||||
j = sqlite3Strlen30(zTempPath);
|
||||
while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/'
|
||||
|| zTempPath[j-1] == ' ' ) ){
|
||||
j--;
|
||||
@@ -700,7 +709,7 @@ static int getTempname(int nBuf, char *zBuf ){
|
||||
sqlite3_snprintf( nBuf-30, zBuf,
|
||||
"%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath );
|
||||
}
|
||||
j = strlen( zBuf );
|
||||
j = sqlite3Strlen30( zBuf );
|
||||
sqlite3_randomness( 20, &zBuf[j] );
|
||||
for( i = 0; i < 20; i++, j++ ){
|
||||
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
||||
@@ -746,7 +755,7 @@ static int os2Open(
|
||||
int *pOutFlags /* Status return flags */
|
||||
){
|
||||
HFILE h;
|
||||
ULONG ulFileAttribute = 0;
|
||||
ULONG ulFileAttribute = FILE_NORMAL;
|
||||
ULONG ulOpenFlags = 0;
|
||||
ULONG ulOpenMode = 0;
|
||||
os2File *pFile = (os2File*)id;
|
||||
@@ -771,7 +780,6 @@ static int os2Open(
|
||||
|
||||
OSTRACE2( "OPEN want %d\n", flags );
|
||||
|
||||
/*ulOpenMode = flags & SQLITE_OPEN_READWRITE ? OPEN_ACCESS_READWRITE : OPEN_ACCESS_READONLY;*/
|
||||
if( flags & SQLITE_OPEN_READWRITE ){
|
||||
ulOpenMode |= OPEN_ACCESS_READWRITE;
|
||||
OSTRACE1( "OPEN read/write\n" );
|
||||
@@ -780,7 +788,6 @@ static int os2Open(
|
||||
OSTRACE1( "OPEN read only\n" );
|
||||
}
|
||||
|
||||
/*ulOpenFlags = flags & SQLITE_OPEN_CREATE ? OPEN_ACTION_CREATE_IF_NEW : OPEN_ACTION_FAIL_IF_NEW;*/
|
||||
if( flags & SQLITE_OPEN_CREATE ){
|
||||
ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
|
||||
OSTRACE1( "OPEN open new/create\n" );
|
||||
@@ -789,7 +796,6 @@ static int os2Open(
|
||||
OSTRACE1( "OPEN open existing\n" );
|
||||
}
|
||||
|
||||
/*ulOpenMode |= flags & SQLITE_OPEN_MAIN_DB ? OPEN_SHARE_DENYNONE : OPEN_SHARE_DENYWRITE;*/
|
||||
if( flags & SQLITE_OPEN_MAIN_DB ){
|
||||
ulOpenMode |= OPEN_SHARE_DENYNONE;
|
||||
OSTRACE1( "OPEN share read/write\n" );
|
||||
@@ -798,18 +804,15 @@ static int os2Open(
|
||||
OSTRACE1( "OPEN share read only\n" );
|
||||
}
|
||||
|
||||
if( flags & (SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TEMP_JOURNAL
|
||||
| SQLITE_OPEN_SUBJOURNAL) ){
|
||||
if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
char pathUtf8[CCHMAXPATH];
|
||||
#ifdef NDEBUG /* when debugging we want to make sure it is deleted */
|
||||
ulFileAttribute = FILE_HIDDEN;
|
||||
#endif
|
||||
ulFileAttribute = FILE_NORMAL;
|
||||
os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 );
|
||||
pFile->pathToDel = convertUtf8PathToCp( pathUtf8 );
|
||||
OSTRACE1( "OPEN hidden/delete on close file attributes\n" );
|
||||
}else{
|
||||
ulFileAttribute = FILE_ARCHIVED | FILE_NORMAL;
|
||||
pFile->pathToDel = NULL;
|
||||
OSTRACE1( "OPEN normal file attribute\n" );
|
||||
}
|
||||
@@ -870,7 +873,7 @@ static int os2Delete(
|
||||
rc = DosDelete( (PSZ)zFilenameCp );
|
||||
free( zFilenameCp );
|
||||
OSTRACE2( "DELETE \"%s\"\n", zFilename );
|
||||
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
||||
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -964,9 +967,13 @@ static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
** Write up to nBuf bytes of randomness into zBuf.
|
||||
*/
|
||||
static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
|
||||
ULONG sizeofULong = sizeof(ULONG);
|
||||
int n = 0;
|
||||
if( sizeof(DATETIME) <= nBuf - n ){
|
||||
#if defined(SQLITE_TEST)
|
||||
n = nBuf;
|
||||
memset(zBuf, 0, nBuf);
|
||||
#else
|
||||
int sizeofULong = sizeof(ULONG);
|
||||
if( (int)sizeof(DATETIME) <= nBuf - n ){
|
||||
DATETIME x;
|
||||
DosGetDateTime(&x);
|
||||
memcpy(&zBuf[n], &x, sizeof(x));
|
||||
@@ -1013,6 +1020,7 @@ static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
|
||||
n += sizeofULong;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
+3680
-1343
File diff suppressed because it is too large
Load Diff
+207
-44
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** This file contains code that is specific to windows.
|
||||
**
|
||||
** $Id: os_win.c,v 1.132 2008/07/31 01:34:34 shane Exp $
|
||||
** $Id: os_win.c,v 1.148 2009/02/05 03:16:21 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if SQLITE_OS_WIN /* This file is used for windows only */
|
||||
@@ -62,11 +62,18 @@
|
||||
*/
|
||||
#include "os_common.h"
|
||||
|
||||
/*
|
||||
** Some microsoft compilers lack this definition.
|
||||
*/
|
||||
#ifndef INVALID_FILE_ATTRIBUTES
|
||||
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WindowsCE - which has a much
|
||||
** reduced API.
|
||||
*/
|
||||
#if defined(SQLITE_OS_WINCE)
|
||||
#if SQLITE_OS_WINCE
|
||||
# define AreFileApisANSI() 1
|
||||
#endif
|
||||
|
||||
@@ -93,6 +100,7 @@ struct winFile {
|
||||
HANDLE h; /* Handle for accessing the file */
|
||||
unsigned char locktype; /* Type of lock currently held on this file */
|
||||
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
|
||||
DWORD lastErrno; /* The Windows errno from the last I/O error */
|
||||
#if SQLITE_OS_WINCE
|
||||
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
|
||||
HANDLE hMutex; /* Mutex used to control access to shared lock */
|
||||
@@ -245,7 +253,7 @@ static char *unicodeToMbcs(const WCHAR *zWideFilename){
|
||||
** Convert multibyte character string to UTF-8. Space to hold the
|
||||
** returned string is obtained from malloc().
|
||||
*/
|
||||
static char *mbcsToUtf8(const char *zFilename){
|
||||
char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
|
||||
char *zFilenameUtf8;
|
||||
WCHAR *zTmpWide;
|
||||
|
||||
@@ -351,6 +359,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
|
||||
/* Create/open the named mutex */
|
||||
pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
|
||||
if (!pFile->hMutex){
|
||||
pFile->lastErrno = GetLastError();
|
||||
free(zName);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -381,6 +390,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
|
||||
FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock));
|
||||
/* If mapping failed, close the shared memory handle and erase it */
|
||||
if (!pFile->shared){
|
||||
pFile->lastErrno = GetLastError();
|
||||
CloseHandle(pFile->hShared);
|
||||
pFile->hShared = NULL;
|
||||
}
|
||||
@@ -603,7 +613,7 @@ static int winClose(sqlite3_file *id){
|
||||
OSTRACE2("CLOSE %d\n", pFile->h);
|
||||
do{
|
||||
rc = CloseHandle(pFile->h);
|
||||
}while( rc==0 && cnt++ < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
|
||||
}while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
|
||||
#if SQLITE_OS_WINCE
|
||||
#define WINCE_DELETION_ATTEMPTS 3
|
||||
winceDestroyLock(pFile);
|
||||
@@ -641,24 +651,28 @@ static int winRead(
|
||||
int amt, /* Number of bytes to read */
|
||||
sqlite3_int64 offset /* Begin reading at this offset */
|
||||
){
|
||||
LONG upperBits = (offset>>32) & 0x7fffffff;
|
||||
LONG lowerBits = offset & 0xffffffff;
|
||||
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(offset & 0xffffffff);
|
||||
DWORD rc;
|
||||
DWORD got;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error;
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){
|
||||
if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
if( got==(DWORD)amt ){
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
/* Unread parts of the buffer must be zero-filled */
|
||||
memset(&((char*)pBuf)[got], 0, amt-got);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
@@ -674,17 +688,19 @@ static int winWrite(
|
||||
int amt, /* Number of bytes to write */
|
||||
sqlite3_int64 offset /* Offset into the file to begin writing at */
|
||||
){
|
||||
LONG upperBits = (offset>>32) & 0x7fffffff;
|
||||
LONG lowerBits = offset & 0xffffffff;
|
||||
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(offset & 0xffffffff);
|
||||
DWORD rc;
|
||||
DWORD wrote;
|
||||
DWORD wrote = 0;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error;
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_WRITE);
|
||||
SimulateDiskfullError(return SQLITE_FULL);
|
||||
OSTRACE3("WRITE %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){
|
||||
if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
assert( amt>0 );
|
||||
@@ -697,6 +713,7 @@ static int winWrite(
|
||||
pBuf = &((char*)pBuf)[wrote];
|
||||
}
|
||||
if( !rc || amt>(int)wrote ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@@ -706,14 +723,26 @@ static int winWrite(
|
||||
** Truncate an open file to a specified size
|
||||
*/
|
||||
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
LONG upperBits = (nByte>>32) & 0x7fffffff;
|
||||
LONG lowerBits = nByte & 0xffffffff;
|
||||
DWORD rc;
|
||||
LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(nByte & 0xffffffff);
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error = NO_ERROR;
|
||||
OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte);
|
||||
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
||||
SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
SetEndOfFile(pFile->h);
|
||||
return SQLITE_OK;
|
||||
rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
if( INVALID_SET_FILE_POINTER == rc ){
|
||||
error = GetLastError();
|
||||
}
|
||||
if( error == NO_ERROR ){
|
||||
/* SetEndOfFile will fail if nByte is negative */
|
||||
if( SetEndOfFile(pFile->h) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
error = GetLastError();
|
||||
}
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
@@ -729,19 +758,33 @@ int sqlite3_fullsync_count = 0;
|
||||
** Make sure all writes to a particular file are committed to disk.
|
||||
*/
|
||||
static int winSync(sqlite3_file *id, int flags){
|
||||
#ifndef SQLITE_NO_SYNC
|
||||
winFile *pFile = (winFile*)id;
|
||||
OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
#ifdef SQLITE_TEST
|
||||
#else
|
||||
UNUSED_PARAMETER(id);
|
||||
#endif
|
||||
#ifndef SQLITE_TEST
|
||||
UNUSED_PARAMETER(flags);
|
||||
#else
|
||||
if( flags & SQLITE_SYNC_FULL ){
|
||||
sqlite3_fullsync_count++;
|
||||
}
|
||||
sqlite3_sync_count++;
|
||||
#endif
|
||||
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
|
||||
** no-op
|
||||
*/
|
||||
#ifdef SQLITE_NO_SYNC
|
||||
return SQLITE_OK;
|
||||
#else
|
||||
if( FlushFileBuffers(pFile->h) ){
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -750,8 +793,15 @@ static int winSync(sqlite3_file *id, int flags){
|
||||
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD upperBits, lowerBits;
|
||||
DWORD error;
|
||||
SimulateIOError(return SQLITE_IOERR_FSTAT);
|
||||
lowerBits = GetFileSize(pFile->h, &upperBits);
|
||||
if( (lowerBits == INVALID_FILE_SIZE)
|
||||
&& ((error = GetLastError()) != NO_ERROR) )
|
||||
{
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
*pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -777,11 +827,18 @@ static int getReadLock(winFile *pFile){
|
||||
ovlp.hEvent = 0;
|
||||
res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,
|
||||
0, SHARED_SIZE, 0, &ovlp);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
int lk;
|
||||
sqlite3_randomness(sizeof(lk), &lk);
|
||||
pFile->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
|
||||
pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
|
||||
res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
|
||||
#endif
|
||||
}
|
||||
if( res == 0 ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -793,8 +850,15 @@ static int unlockReadLock(winFile *pFile){
|
||||
int res;
|
||||
if( isNT() ){
|
||||
res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
|
||||
#endif
|
||||
}
|
||||
if( res == 0 ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -831,6 +895,7 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
int newLocktype; /* Set pFile->locktype to this value before exiting */
|
||||
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error = NO_ERROR;
|
||||
|
||||
assert( pFile!=0 );
|
||||
OSTRACE5("LOCK %d %d was %d(%d)\n",
|
||||
@@ -855,8 +920,9 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
** the PENDING_LOCK byte is temporary.
|
||||
*/
|
||||
newLocktype = pFile->locktype;
|
||||
if( pFile->locktype==NO_LOCK
|
||||
|| (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
|
||||
if( (pFile->locktype==NO_LOCK)
|
||||
|| ( (locktype==EXCLUSIVE_LOCK)
|
||||
&& (pFile->locktype==RESERVED_LOCK))
|
||||
){
|
||||
int cnt = 3;
|
||||
while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
|
||||
@@ -867,6 +933,9 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
Sleep(1);
|
||||
}
|
||||
gotPendingLock = res;
|
||||
if( !res ){
|
||||
error = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
/* Acquire a shared lock
|
||||
@@ -876,6 +945,8 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
res = getReadLock(pFile);
|
||||
if( res ){
|
||||
newLocktype = SHARED_LOCK;
|
||||
}else{
|
||||
error = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,6 +957,8 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
|
||||
if( res ){
|
||||
newLocktype = RESERVED_LOCK;
|
||||
}else{
|
||||
error = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,7 +979,8 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
if( res ){
|
||||
newLocktype = EXCLUSIVE_LOCK;
|
||||
}else{
|
||||
OSTRACE2("error-code = %d\n", GetLastError());
|
||||
error = GetLastError();
|
||||
OSTRACE2("error-code = %d\n", error);
|
||||
getReadLock(pFile);
|
||||
}
|
||||
}
|
||||
@@ -926,9 +1000,10 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
}else{
|
||||
OSTRACE4("LOCK FAILED %d trying for %d but got %d\n", pFile->h,
|
||||
locktype, newLocktype);
|
||||
pFile->lastErrno = error;
|
||||
rc = SQLITE_BUSY;
|
||||
}
|
||||
pFile->locktype = newLocktype;
|
||||
pFile->locktype = (u8)newLocktype;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -993,7 +1068,7 @@ static int winUnlock(sqlite3_file *id, int locktype){
|
||||
if( type>=PENDING_LOCK ){
|
||||
UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
|
||||
}
|
||||
pFile->locktype = locktype;
|
||||
pFile->locktype = (u8)locktype;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1006,6 +1081,10 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
*(int*)pArg = ((winFile*)id)->locktype;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_LAST_ERRNO: {
|
||||
*(int*)pArg = (int)((winFile*)id)->lastErrno;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@@ -1021,6 +1100,7 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
** same for both.
|
||||
*/
|
||||
static int winSectorSize(sqlite3_file *id){
|
||||
UNUSED_PARAMETER(id);
|
||||
return SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
@@ -1028,6 +1108,7 @@ static int winSectorSize(sqlite3_file *id){
|
||||
** Return a vector of device characteristics.
|
||||
*/
|
||||
static int winDeviceCharacteristics(sqlite3_file *id){
|
||||
UNUSED_PARAMETER(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1067,8 +1148,12 @@ static void *convertUtf8Filename(const char *zFilename){
|
||||
void *zConverted = 0;
|
||||
if( isNT() ){
|
||||
zConverted = utf8ToUnicode(zFilename);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
zConverted = utf8ToMbcs(zFilename);
|
||||
#endif
|
||||
}
|
||||
/* caller will handle out of memory */
|
||||
return zConverted;
|
||||
@@ -1098,23 +1183,29 @@ static int getTempname(int nBuf, char *zBuf){
|
||||
}else{
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
char *zUtf8;
|
||||
char zMbcsPath[MAX_PATH];
|
||||
GetTempPathA(MAX_PATH-30, zMbcsPath);
|
||||
zUtf8 = mbcsToUtf8(zMbcsPath);
|
||||
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
|
||||
if( zUtf8 ){
|
||||
sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
|
||||
free(zUtf8);
|
||||
}else{
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
|
||||
for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
|
||||
zTempPath[i] = 0;
|
||||
sqlite3_snprintf(nBuf-30, zBuf,
|
||||
"%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
|
||||
j = strlen(zBuf);
|
||||
j = sqlite3Strlen30(zBuf);
|
||||
sqlite3_randomness(20, &zBuf[j]);
|
||||
for(i=0; i<20; i++, j++){
|
||||
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
||||
@@ -1170,12 +1261,16 @@ static int winOpen(
|
||||
DWORD dwShareMode;
|
||||
DWORD dwCreationDisposition;
|
||||
DWORD dwFlagsAndAttributes = 0;
|
||||
int isTemp;
|
||||
#if SQLITE_OS_WINCE
|
||||
int isTemp = 0;
|
||||
#endif
|
||||
winFile *pFile = (winFile*)id;
|
||||
void *zConverted; /* Filename in OS encoding */
|
||||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
|
||||
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
|
||||
/* If the second argument to this function is NULL, generate a
|
||||
** temporary file name to use
|
||||
*/
|
||||
@@ -1211,19 +1306,20 @@ static int winOpen(
|
||||
if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
#if SQLITE_OS_WINCE
|
||||
dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
|
||||
isTemp = 1;
|
||||
#else
|
||||
dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
|
||||
| FILE_ATTRIBUTE_HIDDEN
|
||||
| FILE_FLAG_DELETE_ON_CLOSE;
|
||||
#endif
|
||||
isTemp = 1;
|
||||
}else{
|
||||
dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
isTemp = 0;
|
||||
}
|
||||
/* Reports from the internet are that performance is always
|
||||
** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */
|
||||
#if SQLITE_OS_WINCE
|
||||
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
|
||||
#endif
|
||||
if( isNT() ){
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
dwDesiredAccess,
|
||||
@@ -1233,6 +1329,11 @@ static int winOpen(
|
||||
dwFlagsAndAttributes,
|
||||
NULL
|
||||
);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
h = CreateFileA((char*)zConverted,
|
||||
dwDesiredAccess,
|
||||
@@ -1242,6 +1343,7 @@ static int winOpen(
|
||||
dwFlagsAndAttributes,
|
||||
NULL
|
||||
);
|
||||
#endif
|
||||
}
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
free(zConverted);
|
||||
@@ -1262,6 +1364,7 @@ static int winOpen(
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
pFile->pMethod = &winIoMethod;
|
||||
pFile->h = h;
|
||||
pFile->lastErrno = NO_ERROR;
|
||||
#if SQLITE_OS_WINCE
|
||||
if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
|
||||
(SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
|
||||
@@ -1301,9 +1404,11 @@ static int winDelete(
|
||||
int syncDir /* Not used on win32 */
|
||||
){
|
||||
int cnt = 0;
|
||||
int rc;
|
||||
DWORD error;
|
||||
DWORD rc;
|
||||
DWORD error = 0;
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
UNUSED_PARAMETER(syncDir);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -1313,19 +1418,25 @@ static int winDelete(
|
||||
DeleteFileW(zConverted);
|
||||
}while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
|
||||
|| ((error = GetLastError()) == ERROR_ACCESS_DENIED))
|
||||
&& (cnt++ < MX_DELETION_ATTEMPTS)
|
||||
&& (++cnt < MX_DELETION_ATTEMPTS)
|
||||
&& (Sleep(100), 1) );
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
do{
|
||||
DeleteFileA(zConverted);
|
||||
}while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
|
||||
|| ((error = GetLastError()) == ERROR_ACCESS_DENIED))
|
||||
&& (cnt++ < MX_DELETION_ATTEMPTS)
|
||||
&& (++cnt < MX_DELETION_ATTEMPTS)
|
||||
&& (Sleep(100), 1) );
|
||||
#endif
|
||||
}
|
||||
free(zConverted);
|
||||
OSTRACE2("DELETE \"%s\"\n", zFilename);
|
||||
return ( (rc==INVALID_FILE_ATTRIBUTES)
|
||||
return ( (rc == INVALID_FILE_ATTRIBUTES)
|
||||
&& (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE;
|
||||
}
|
||||
|
||||
@@ -1339,15 +1450,22 @@ static int winAccess(
|
||||
int *pResOut /* OUT: Result */
|
||||
){
|
||||
DWORD attr;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
attr = GetFileAttributesW((WCHAR*)zConverted);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
attr = GetFileAttributesA((char*)zConverted);
|
||||
#endif
|
||||
}
|
||||
free(zConverted);
|
||||
switch( flags ){
|
||||
@@ -1377,13 +1495,15 @@ static int winFullPathname(
|
||||
int nFull, /* Size of output buffer in bytes */
|
||||
char *zFull /* Output buffer */
|
||||
){
|
||||
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
UNUSED_PARAMETER(nFull);
|
||||
cygwin_conv_to_full_win32_path(zRelative, zFull);
|
||||
return SQLITE_OK;
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
UNUSED_PARAMETER(nFull);
|
||||
/* WinCE has no concept of a relative pathname, or so I am told. */
|
||||
sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
|
||||
return SQLITE_OK;
|
||||
@@ -1393,6 +1513,7 @@ static int winFullPathname(
|
||||
int nByte;
|
||||
void *zConverted;
|
||||
char *zOut;
|
||||
UNUSED_PARAMETER(nFull);
|
||||
zConverted = convertUtf8Filename(zRelative);
|
||||
if( isNT() ){
|
||||
WCHAR *zTemp;
|
||||
@@ -1406,6 +1527,11 @@ static int winFullPathname(
|
||||
free(zConverted);
|
||||
zOut = unicodeToUtf8(zTemp);
|
||||
free(zTemp);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
char *zTemp;
|
||||
nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
|
||||
@@ -1416,8 +1542,9 @@ static int winFullPathname(
|
||||
}
|
||||
GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
|
||||
free(zConverted);
|
||||
zOut = mbcsToUtf8(zTemp);
|
||||
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
|
||||
free(zTemp);
|
||||
#endif
|
||||
}
|
||||
if( zOut ){
|
||||
sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
|
||||
@@ -1441,31 +1568,41 @@ static int winFullPathname(
|
||||
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
|
||||
HANDLE h;
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
if( zConverted==0 ){
|
||||
return 0;
|
||||
}
|
||||
if( isNT() ){
|
||||
h = LoadLibraryW((WCHAR*)zConverted);
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
h = LoadLibraryA((char*)zConverted);
|
||||
#endif
|
||||
}
|
||||
free(zConverted);
|
||||
return (void*)h;
|
||||
}
|
||||
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
getLastErrorMsg(nBuf, zBufOut);
|
||||
}
|
||||
void *winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
|
||||
void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
#if SQLITE_OS_WINCE
|
||||
/* The GetProcAddressA() routine is only available on wince. */
|
||||
return GetProcAddressA((HANDLE)pHandle, zSymbol);
|
||||
return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol);
|
||||
#else
|
||||
/* All other windows platforms expect GetProcAddress() to take
|
||||
** an Ansi string regardless of the _UNICODE setting */
|
||||
return GetProcAddress((HANDLE)pHandle, zSymbol);
|
||||
return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol);
|
||||
#endif
|
||||
}
|
||||
void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
FreeLibrary((HANDLE)pHandle);
|
||||
}
|
||||
#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
|
||||
@@ -1481,6 +1618,11 @@ void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
*/
|
||||
static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
||||
int n = 0;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
#if defined(SQLITE_TEST)
|
||||
n = nBuf;
|
||||
memset(zBuf, 0, nBuf);
|
||||
#else
|
||||
if( sizeof(SYSTEMTIME)<=nBuf-n ){
|
||||
SYSTEMTIME x;
|
||||
GetSystemTime(&x);
|
||||
@@ -1503,6 +1645,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
||||
memcpy(&zBuf[n], &i, sizeof(i));
|
||||
n += sizeof(i);
|
||||
}
|
||||
#endif
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -1512,6 +1655,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
||||
*/
|
||||
static int winSleep(sqlite3_vfs *pVfs, int microsec){
|
||||
Sleep((microsec+999)/1000);
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
return ((microsec+999)/1000)*1000;
|
||||
}
|
||||
|
||||
@@ -1533,7 +1677,7 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
||||
/* FILETIME structure is a 64-bit value representing the number of
|
||||
100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
|
||||
*/
|
||||
double now;
|
||||
sqlite3_int64 timeW, timeF;
|
||||
#if SQLITE_OS_WINCE
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
@@ -1544,11 +1688,29 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
||||
#else
|
||||
GetSystemTimeAsFileTime( &ft );
|
||||
#endif
|
||||
now = ((double)ft.dwHighDateTime) * 4294967296.0;
|
||||
*prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
#if defined(_MSC_VER)
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296) + ft.dwLowDateTime;
|
||||
timeF = timeW % 864000000000; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / 864000000000; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + 432000000000; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF / 864000000000); /* add whole day if half day made one */
|
||||
timeF = timeF % 864000000000; /* compute new fractional days */
|
||||
*prNow = (double)timeW + ((double)timeF / (double)864000000000);
|
||||
#else
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296LL) + ft.dwLowDateTime;
|
||||
timeF = timeW % 864000000000LL; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / 864000000000LL; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + 432000000000LL; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF / 864000000000LL); /* add whole day if half day made one */
|
||||
timeF = timeF % 864000000000LL; /* compute new fractional days */
|
||||
*prNow = (double)timeW + ((double)timeF / (double)864000000000LL);
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
if( sqlite3_current_time ){
|
||||
*prNow = sqlite3_current_time/86400.0 + 2440587.5;
|
||||
*prNow = ((double)sqlite3_current_time + (double)43200) / (double)86400 + (double)2440587;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
@@ -1585,6 +1747,7 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
||||
** sqlite3_errmsg(), possibly making IO errors easier to debug.
|
||||
*/
|
||||
static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
return getLastErrorMsg(nBuf, zBuf);
|
||||
}
|
||||
|
||||
|
||||
+3039
-3220
File diff suppressed because it is too large
Load Diff
+70
-50
@@ -13,15 +13,16 @@
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
**
|
||||
** @(#) $Id: pager.h,v 1.77 2008/07/16 18:17:56 danielk1977 Exp $
|
||||
** @(#) $Id: pager.h,v 1.100 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PAGER_H_
|
||||
#define _PAGER_H_
|
||||
|
||||
/*
|
||||
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
|
||||
** it must be turned on for each database using "PRAGMA auto_vacuum = 1".
|
||||
** Default maximum size for persistent journal files. A negative
|
||||
** value means no limit. This value may be overridden using the
|
||||
** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit".
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
|
||||
#define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1
|
||||
@@ -43,10 +44,20 @@ typedef struct Pager Pager;
|
||||
*/
|
||||
typedef struct PgHdr DbPage;
|
||||
|
||||
/*
|
||||
** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
|
||||
** reserved for working around a windows/posix incompatibility). It is
|
||||
** used in the journal to signify that the remainder of the journal file
|
||||
** is devoted to storing a master journal name - there are no more pages to
|
||||
** roll back. See comments for function writeMasterJournal() in pager.c
|
||||
** for details.
|
||||
*/
|
||||
#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1))
|
||||
|
||||
/*
|
||||
** Allowed values for the flags parameter to sqlite3PagerOpen().
|
||||
**
|
||||
** NOTE: This values must match the corresponding BTREE_ values in btree.h.
|
||||
** NOTE: These values must match the corresponding BTREE_ values in btree.h.
|
||||
*/
|
||||
#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
|
||||
#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */
|
||||
@@ -65,77 +76,86 @@ typedef struct PgHdr DbPage;
|
||||
#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
|
||||
#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
|
||||
#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
|
||||
#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
|
||||
#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
|
||||
|
||||
/*
|
||||
** See source code comments for a detailed description of the following
|
||||
** routines:
|
||||
** The remainder of this file contains the declarations of the functions
|
||||
** that make up the Pager sub-system API. See source code comments for
|
||||
** a detailed description of each routine.
|
||||
*/
|
||||
|
||||
/* Open and close a Pager connection. */
|
||||
int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int);
|
||||
void sqlite3PagerSetBusyhandler(Pager*, BusyHandler *pBusyHandler);
|
||||
void sqlite3PagerSetDestructor(Pager*, void(*)(DbPage*,int));
|
||||
void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*,int));
|
||||
int sqlite3PagerClose(Pager *pPager);
|
||||
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
|
||||
|
||||
/* Functions used to configure a Pager object. */
|
||||
void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
|
||||
void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*));
|
||||
int sqlite3PagerSetPagesize(Pager*, u16*);
|
||||
int sqlite3PagerMaxPageCount(Pager*, int);
|
||||
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
|
||||
void sqlite3PagerSetCachesize(Pager*, int);
|
||||
int sqlite3PagerClose(Pager *pPager);
|
||||
int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
|
||||
#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
|
||||
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
|
||||
int sqlite3PagerRef(DbPage*);
|
||||
int sqlite3PagerUnref(DbPage*);
|
||||
int sqlite3PagerWrite(DbPage*);
|
||||
int sqlite3PagerPagecount(Pager*, int*);
|
||||
int sqlite3PagerTruncate(Pager*,Pgno);
|
||||
int sqlite3PagerBegin(DbPage*, int exFlag);
|
||||
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, Pgno, int);
|
||||
int sqlite3PagerCommitPhaseTwo(Pager*);
|
||||
int sqlite3PagerRollback(Pager*);
|
||||
int sqlite3PagerIsreadonly(Pager*);
|
||||
int sqlite3PagerStmtBegin(Pager*);
|
||||
int sqlite3PagerStmtCommit(Pager*);
|
||||
int sqlite3PagerStmtRollback(Pager*);
|
||||
void sqlite3PagerDontRollback(DbPage*);
|
||||
void sqlite3PagerDontWrite(DbPage*);
|
||||
int sqlite3PagerRefcount(Pager*);
|
||||
void sqlite3PagerSetSafetyLevel(Pager*,int,int);
|
||||
const char *sqlite3PagerFilename(Pager*);
|
||||
const sqlite3_vfs *sqlite3PagerVfs(Pager*);
|
||||
sqlite3_file *sqlite3PagerFile(Pager*);
|
||||
const char *sqlite3PagerDirname(Pager*);
|
||||
const char *sqlite3PagerJournalname(Pager*);
|
||||
int sqlite3PagerNosync(Pager*);
|
||||
int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int);
|
||||
void *sqlite3PagerGetData(DbPage *);
|
||||
void *sqlite3PagerGetExtra(DbPage *);
|
||||
int sqlite3PagerLockingMode(Pager *, int);
|
||||
int sqlite3PagerJournalMode(Pager *, int);
|
||||
i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
|
||||
|
||||
/* Functions used to obtain and release page references. */
|
||||
int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
|
||||
#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
|
||||
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
|
||||
void sqlite3PagerRef(DbPage*);
|
||||
void sqlite3PagerUnref(DbPage*);
|
||||
|
||||
/* Operations on page references. */
|
||||
int sqlite3PagerWrite(DbPage*);
|
||||
void sqlite3PagerDontWrite(DbPage*);
|
||||
int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int);
|
||||
int sqlite3PagerPageRefcount(DbPage*);
|
||||
void *sqlite3PagerGetData(DbPage *);
|
||||
void *sqlite3PagerGetExtra(DbPage *);
|
||||
|
||||
/* Functions used to manage pager transactions and savepoints. */
|
||||
int sqlite3PagerPagecount(Pager*, int*);
|
||||
int sqlite3PagerBegin(Pager*, int exFlag);
|
||||
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
|
||||
int sqlite3PagerSync(Pager *pPager);
|
||||
int sqlite3PagerCommitPhaseTwo(Pager*);
|
||||
int sqlite3PagerRollback(Pager*);
|
||||
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
|
||||
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
|
||||
|
||||
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO)
|
||||
int sqlite3PagerReleaseMemory(int);
|
||||
#endif
|
||||
/* Functions used to query pager state and configuration. */
|
||||
u8 sqlite3PagerIsreadonly(Pager*);
|
||||
int sqlite3PagerRefcount(Pager*);
|
||||
const char *sqlite3PagerFilename(Pager*);
|
||||
const sqlite3_vfs *sqlite3PagerVfs(Pager*);
|
||||
sqlite3_file *sqlite3PagerFile(Pager*);
|
||||
const char *sqlite3PagerJournalname(Pager*);
|
||||
int sqlite3PagerNosync(Pager*);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
void sqlite3PagerTruncateImage(Pager*,Pgno);
|
||||
|
||||
/* Used by encryption extensions. */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
|
||||
#endif
|
||||
|
||||
/* Functions to support testing and debugging. */
|
||||
#if !defined(NDEBUG) || defined(SQLITE_TEST)
|
||||
Pgno sqlite3PagerPagenumber(DbPage*);
|
||||
int sqlite3PagerIswriteable(DbPage*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
int *sqlite3PagerStats(Pager*);
|
||||
void sqlite3PagerRefdump(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
void disable_simulated_io_errors(void);
|
||||
void enable_simulated_io_errors(void);
|
||||
void disable_simulated_io_errors(void);
|
||||
void enable_simulated_io_errors(void);
|
||||
#else
|
||||
# define disable_simulated_io_errors()
|
||||
# define enable_simulated_io_errors()
|
||||
|
||||
+111
-38
@@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.248 2008/07/31 01:40:42 shane Exp $
|
||||
** @(#) $Id: parse.y,v 1.268 2009/01/29 19:27:47 drh Exp $
|
||||
*/
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
@@ -32,11 +32,13 @@
|
||||
// This code runs whenever there is a syntax error
|
||||
//
|
||||
%syntax_error {
|
||||
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
|
||||
assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
|
||||
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
|
||||
pParse->parseError = 1;
|
||||
}
|
||||
%stack_overflow {
|
||||
UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */
|
||||
sqlite3ErrorMsg(pParse, "parser stack overflow");
|
||||
pParse->parseError = 1;
|
||||
}
|
||||
@@ -91,7 +93,6 @@ struct AttachKey { int type; Token key; };
|
||||
input ::= cmdlist.
|
||||
cmdlist ::= cmdlist ecmd.
|
||||
cmdlist ::= ecmd.
|
||||
cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
|
||||
ecmd ::= SEMI.
|
||||
ecmd ::= explain cmdx SEMI.
|
||||
explain ::= . { sqlite3BeginParse(pParse, 0); }
|
||||
@@ -99,6 +100,7 @@ explain ::= . { sqlite3BeginParse(pParse, 0); }
|
||||
explain ::= EXPLAIN. { sqlite3BeginParse(pParse, 1); }
|
||||
explain ::= EXPLAIN QUERY PLAN. { sqlite3BeginParse(pParse, 2); }
|
||||
%endif SQLITE_OMIT_EXPLAIN
|
||||
cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
|
||||
|
||||
///////////////////// Begin and end transactions. ////////////////////////////
|
||||
//
|
||||
@@ -116,6 +118,18 @@ cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);}
|
||||
|
||||
savepoint_opt ::= SAVEPOINT.
|
||||
savepoint_opt ::= .
|
||||
cmd ::= SAVEPOINT nm(X). {
|
||||
sqlite3Savepoint(pParse, SAVEPOINT_BEGIN, &X);
|
||||
}
|
||||
cmd ::= RELEASE savepoint_opt nm(X). {
|
||||
sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &X);
|
||||
}
|
||||
cmd ::= ROLLBACK trans_opt TO savepoint_opt nm(X). {
|
||||
sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &X);
|
||||
}
|
||||
|
||||
///////////////////// The CREATE TABLE statement ////////////////////////////
|
||||
//
|
||||
cmd ::= create_table create_table_args.
|
||||
@@ -147,7 +161,7 @@ columnlist ::= column.
|
||||
//
|
||||
column(A) ::= columnid(X) type carglist. {
|
||||
A.z = X.z;
|
||||
A.n = (pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
|
||||
A.n = (int)(pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
|
||||
}
|
||||
columnid(A) ::= nm(X). {
|
||||
sqlite3AddColumn(pParse,&X);
|
||||
@@ -166,11 +180,11 @@ id(A) ::= ID(X). {A = X;}
|
||||
// This obviates the need for the "id" nonterminal.
|
||||
//
|
||||
%fallback ID
|
||||
ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN CASCADE CAST CONFLICT
|
||||
ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW CONFLICT
|
||||
DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
|
||||
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH PLAN
|
||||
QUERY KEY OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW
|
||||
TEMP TRIGGER VACUUM VIEW VIRTUAL
|
||||
QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK
|
||||
SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL
|
||||
%ifdef SQLITE_OMIT_COMPOUND_SELECT
|
||||
EXCEPT INTERSECT UNION
|
||||
%endif SQLITE_OMIT_COMPOUND_SELECT
|
||||
@@ -224,15 +238,15 @@ type ::= typetoken(X). {sqlite3AddColumnType(pParse,&X);}
|
||||
typetoken(A) ::= typename(X). {A = X;}
|
||||
typetoken(A) ::= typename(X) LP signed RP(Y). {
|
||||
A.z = X.z;
|
||||
A.n = &Y.z[Y.n] - X.z;
|
||||
A.n = (int)(&Y.z[Y.n] - X.z);
|
||||
}
|
||||
typetoken(A) ::= typename(X) LP signed COMMA signed RP(Y). {
|
||||
A.z = X.z;
|
||||
A.n = &Y.z[Y.n] - X.z;
|
||||
A.n = (int)(&Y.z[Y.n] - X.z);
|
||||
}
|
||||
%type typename {Token}
|
||||
typename(A) ::= ids(X). {A = X;}
|
||||
typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(Y.z-X.z);}
|
||||
typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(int)(Y.z-X.z);}
|
||||
signed ::= plus_num.
|
||||
signed ::= minus_num.
|
||||
|
||||
@@ -246,8 +260,9 @@ carg ::= ccons.
|
||||
ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,X);}
|
||||
ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,X);}
|
||||
ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,X);}
|
||||
ccons ::= DEFAULT MINUS term(X). {
|
||||
ccons ::= DEFAULT MINUS(A) term(X). {
|
||||
Expr *p = sqlite3PExpr(pParse, TK_UMINUS, X, 0, 0);
|
||||
sqlite3ExprSpan(p,&A,&X->span);
|
||||
sqlite3AddDefaultValue(pParse,p);
|
||||
}
|
||||
ccons ::= DEFAULT id(X). {
|
||||
@@ -281,7 +296,7 @@ autoinc(X) ::= AUTOINCR. {X = 1;}
|
||||
//
|
||||
%type refargs {int}
|
||||
refargs(A) ::= . { A = OE_Restrict * 0x010101; }
|
||||
refargs(A) ::= refargs(X) refarg(Y). { A = (X & Y.mask) | Y.value; }
|
||||
refargs(A) ::= refargs(X) refarg(Y). { A = (X & ~Y.mask) | Y.value; }
|
||||
%type refarg {struct {int value; int mask;}}
|
||||
refarg(A) ::= MATCH nm. { A.value = 0; A.mask = 0x000000; }
|
||||
refarg(A) ::= ON DELETE refact(X). { A.value = X; A.mask = 0x0000ff; }
|
||||
@@ -360,8 +375,8 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
|
||||
//////////////////////// The SELECT statement /////////////////////////////////
|
||||
//
|
||||
cmd ::= select(X). {
|
||||
SelectDest dest = {SRT_Callback, 0, 0, 0, 0};
|
||||
sqlite3Select(pParse, X, &dest, 0, 0, 0);
|
||||
SelectDest dest = {SRT_Output, 0, 0, 0, 0};
|
||||
sqlite3Select(pParse, X, &dest);
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
}
|
||||
|
||||
@@ -374,7 +389,7 @@ select(A) ::= oneselect(X). {A = X;}
|
||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
select(A) ::= select(X) multiselect_op(Y) oneselect(Z). {
|
||||
if( Z ){
|
||||
Z->op = Y;
|
||||
Z->op = (u8)Y;
|
||||
Z->pPrior = X;
|
||||
}else{
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
@@ -443,7 +458,7 @@ as(X) ::= . {X.n = 0;}
|
||||
// A complete FROM clause.
|
||||
//
|
||||
from(A) ::= . {A = sqlite3DbMallocZero(pParse->db, sizeof(*A));}
|
||||
from(A) ::= FROM seltablist(X). {
|
||||
from(A) ::= FROM seltablist(X). {
|
||||
A = X;
|
||||
sqlite3SrcListShiftJoinType(A);
|
||||
}
|
||||
@@ -453,29 +468,41 @@ from(A) ::= FROM seltablist(X). {
|
||||
//
|
||||
stl_prefix(A) ::= seltablist(X) joinop(Y). {
|
||||
A = X;
|
||||
if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y;
|
||||
if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = (u8)Y;
|
||||
}
|
||||
stl_prefix(A) ::= . {A = 0;}
|
||||
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
|
||||
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) using_opt(U). {
|
||||
A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U);
|
||||
sqlite3SrcListIndexedBy(pParse, A, &I);
|
||||
}
|
||||
%ifndef SQLITE_OMIT_SUBQUERY
|
||||
seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP
|
||||
seltablist(A) ::= stl_prefix(X) LP select(S) RP
|
||||
as(Z) on_opt(N) using_opt(U). {
|
||||
A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U);
|
||||
}
|
||||
seltablist(A) ::= stl_prefix(X) LP seltablist(F) RP
|
||||
as(Z) on_opt(N) using_opt(U). {
|
||||
if( X==0 && Z.n==0 && N==0 && U==0 ){
|
||||
A = F;
|
||||
}else{
|
||||
Select *pSubquery;
|
||||
sqlite3SrcListShiftJoinType(F);
|
||||
pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
|
||||
A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,pSubquery,N,U);
|
||||
}
|
||||
}
|
||||
|
||||
// A seltablist_paren nonterminal represents anything in a FROM that
|
||||
// is contained inside parentheses. This can be either a subquery or
|
||||
// a grouping of table and subqueries.
|
||||
//
|
||||
%type seltablist_paren {Select*}
|
||||
%destructor seltablist_paren {sqlite3SelectDelete(pParse->db, $$);}
|
||||
seltablist_paren(A) ::= select(S). {A = S;}
|
||||
seltablist_paren(A) ::= seltablist(F). {
|
||||
sqlite3SrcListShiftJoinType(F);
|
||||
A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
|
||||
}
|
||||
// %type seltablist_paren {Select*}
|
||||
// %destructor seltablist_paren {sqlite3SelectDelete(pParse->db, $$);}
|
||||
// seltablist_paren(A) ::= select(S). {A = S;}
|
||||
// seltablist_paren(A) ::= seltablist(F). {
|
||||
// sqlite3SrcListShiftJoinType(F);
|
||||
// A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
|
||||
// }
|
||||
%endif SQLITE_OMIT_SUBQUERY
|
||||
|
||||
%type dbnm {Token}
|
||||
@@ -499,6 +526,21 @@ joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
|
||||
on_opt(N) ::= ON expr(E). {N = E;}
|
||||
on_opt(N) ::= . {N = 0;}
|
||||
|
||||
// Note that this block abuses the Token type just a little. If there is
|
||||
// no "INDEXED BY" clause, the returned token is empty (z==0 && n==0). If
|
||||
// there is an INDEXED BY clause, then the token is populated as per normal,
|
||||
// with z pointing to the token data and n containing the number of bytes
|
||||
// in the token.
|
||||
//
|
||||
// If there is a "NOT INDEXED" clause, then (z==0 && n==1), which is
|
||||
// normally illegal. The sqlite3SrcListIndexedBy() function
|
||||
// recognizes and interprets this as a special case.
|
||||
//
|
||||
%type indexed_opt {Token}
|
||||
indexed_opt(A) ::= . {A.z=0; A.n=0;}
|
||||
indexed_opt(A) ::= INDEXED BY nm(X). {A = X;}
|
||||
indexed_opt(A) ::= NOT INDEXED. {A.z=0; A.n=1;}
|
||||
|
||||
%type using_opt {IdList*}
|
||||
%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);}
|
||||
using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
|
||||
@@ -516,11 +558,11 @@ orderby_opt(A) ::= . {A = 0;}
|
||||
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
||||
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,X,Y,0);
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = Z;
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
|
||||
}
|
||||
sortlist(A) ::= sortitem(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,0,Y,0);
|
||||
if( A && A->a ) A->a[0].sortOrder = Z;
|
||||
if( A && A->a ) A->a[0].sortOrder = (u8)Z;
|
||||
}
|
||||
sortitem(A) ::= expr(X). {A = X;}
|
||||
|
||||
@@ -562,7 +604,20 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
|
||||
|
||||
/////////////////////////// The DELETE statement /////////////////////////////
|
||||
//
|
||||
cmd ::= DELETE FROM fullname(X) where_opt(Y). {sqlite3DeleteFrom(pParse,X,Y);}
|
||||
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
|
||||
orderby_opt(O) limit_opt(L). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
|
||||
sqlite3DeleteFrom(pParse,X,W);
|
||||
}
|
||||
%endif
|
||||
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3DeleteFrom(pParse,X,W);
|
||||
}
|
||||
%endif
|
||||
|
||||
%type where_opt {Expr*}
|
||||
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
@@ -572,10 +627,21 @@ where_opt(A) ::= WHERE expr(X). {A = X;}
|
||||
|
||||
////////////////////////// The UPDATE command ////////////////////////////////
|
||||
//
|
||||
cmd ::= UPDATE orconf(R) fullname(X) SET setlist(Y) where_opt(Z). {
|
||||
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) orderby_opt(O) limit_opt(L). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
sqlite3Update(pParse,X,Y,Z,R);
|
||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
|
||||
sqlite3Update(pParse,X,Y,W,R);
|
||||
}
|
||||
%endif
|
||||
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
sqlite3Update(pParse,X,Y,W,R);
|
||||
}
|
||||
%endif
|
||||
|
||||
%type setlist {ExprList*}
|
||||
%destructor setlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
@@ -885,11 +951,10 @@ uniqueflag(A) ::= . {A = OE_None;}
|
||||
%destructor idxlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
%type idxlist_opt {ExprList*}
|
||||
%destructor idxlist_opt {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
%type idxitem {Token}
|
||||
|
||||
idxlist_opt(A) ::= . {A = 0;}
|
||||
idxlist_opt(A) ::= LP idxlist(X) RP. {A = X;}
|
||||
idxlist(A) ::= idxlist(X) COMMA idxitem(Y) collate(C) sortorder(Z). {
|
||||
idxlist(A) ::= idxlist(X) COMMA nm(Y) collate(C) sortorder(Z). {
|
||||
Expr *p = 0;
|
||||
if( C.n>0 ){
|
||||
p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
|
||||
@@ -897,9 +962,9 @@ idxlist(A) ::= idxlist(X) COMMA idxitem(Y) collate(C) sortorder(Z). {
|
||||
}
|
||||
A = sqlite3ExprListAppend(pParse,X, p, &Y);
|
||||
sqlite3ExprListCheckLength(pParse, A, "index");
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = Z;
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
|
||||
}
|
||||
idxlist(A) ::= idxitem(Y) collate(C) sortorder(Z). {
|
||||
idxlist(A) ::= nm(Y) collate(C) sortorder(Z). {
|
||||
Expr *p = 0;
|
||||
if( C.n>0 ){
|
||||
p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
|
||||
@@ -907,9 +972,8 @@ idxlist(A) ::= idxitem(Y) collate(C) sortorder(Z). {
|
||||
}
|
||||
A = sqlite3ExprListAppend(pParse,0, p, &Y);
|
||||
sqlite3ExprListCheckLength(pParse, A, "index");
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = Z;
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
|
||||
}
|
||||
idxitem(A) ::= nm(X). {A = X;}
|
||||
|
||||
%type collate {Token}
|
||||
collate(C) ::= . {C.z = 0; C.n = 0;}
|
||||
@@ -958,7 +1022,7 @@ plus_opt ::= .
|
||||
cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
|
||||
Token all;
|
||||
all.z = A.z;
|
||||
all.n = (Z.z - A.z) + Z.n;
|
||||
all.n = (int)(Z.z - A.z) + Z.n;
|
||||
sqlite3FinishTrigger(pParse, S, &all);
|
||||
}
|
||||
|
||||
@@ -992,15 +1056,24 @@ when_clause(A) ::= WHEN expr(X). { A = X; }
|
||||
%type trigger_cmd_list {TriggerStep*}
|
||||
%destructor trigger_cmd_list {sqlite3DeleteTriggerStep(pParse->db, $$);}
|
||||
trigger_cmd_list(A) ::= trigger_cmd_list(Y) trigger_cmd(X) SEMI. {
|
||||
/*
|
||||
if( Y ){
|
||||
Y->pLast->pNext = X;
|
||||
}else{
|
||||
Y = X;
|
||||
}
|
||||
*/
|
||||
assert( Y!=0 );
|
||||
Y->pLast->pNext = X;
|
||||
Y->pLast = X;
|
||||
A = Y;
|
||||
}
|
||||
trigger_cmd_list(A) ::= . { A = 0; }
|
||||
trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. {
|
||||
/* if( X ) */
|
||||
assert( X!=0 );
|
||||
X->pLast = X;
|
||||
A = X;
|
||||
}
|
||||
|
||||
%type trigger_cmd {TriggerStep*}
|
||||
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
|
||||
|
||||
+584
@@ -0,0 +1,584 @@
|
||||
/*
|
||||
** 2008 August 05
|
||||
**
|
||||
** 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 that page cache.
|
||||
**
|
||||
** @(#) $Id: pcache.c,v 1.43 2009/01/23 16:45:01 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** A complete page cache is an instance of this structure.
|
||||
*/
|
||||
struct PCache {
|
||||
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
|
||||
PgHdr *pSynced; /* Last synced page in dirty page list */
|
||||
int nRef; /* Number of referenced pages */
|
||||
int nMax; /* Configured cache size */
|
||||
int szPage; /* Size of every page in this cache */
|
||||
int szExtra; /* Size of extra space for each page */
|
||||
int bPurgeable; /* True if pages are on backing store */
|
||||
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
|
||||
void *pStress; /* Argument to xStress */
|
||||
sqlite3_pcache *pCache; /* Pluggable cache module */
|
||||
PgHdr *pPage1; /* Reference to page 1 */
|
||||
};
|
||||
|
||||
/*
|
||||
** Some of the assert() macros in this code are too expensive to run
|
||||
** even during normal debugging. Use them only rarely on long-running
|
||||
** tests. Enable the expensive asserts using the
|
||||
** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
|
||||
# define expensive_assert(X) assert(X)
|
||||
#else
|
||||
# define expensive_assert(X)
|
||||
#endif
|
||||
|
||||
/********************************** Linked List Management ********************/
|
||||
|
||||
#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
|
||||
/*
|
||||
** Check that the pCache->pSynced variable is set correctly. If it
|
||||
** is not, either fail an assert or return zero. Otherwise, return
|
||||
** non-zero. This is only used in debugging builds, as follows:
|
||||
**
|
||||
** expensive_assert( pcacheCheckSynced(pCache) );
|
||||
*/
|
||||
static int pcacheCheckSynced(PCache *pCache){
|
||||
PgHdr *p;
|
||||
for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){
|
||||
assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) );
|
||||
}
|
||||
return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);
|
||||
}
|
||||
#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
|
||||
|
||||
/*
|
||||
** Remove page pPage from the list of dirty pages.
|
||||
*/
|
||||
static void pcacheRemoveFromDirtyList(PgHdr *pPage){
|
||||
PCache *p = pPage->pCache;
|
||||
|
||||
assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
|
||||
assert( pPage->pDirtyPrev || pPage==p->pDirty );
|
||||
|
||||
/* Update the PCache1.pSynced variable if necessary. */
|
||||
if( p->pSynced==pPage ){
|
||||
PgHdr *pSynced = pPage->pDirtyPrev;
|
||||
while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
|
||||
pSynced = pSynced->pDirtyPrev;
|
||||
}
|
||||
p->pSynced = pSynced;
|
||||
}
|
||||
|
||||
if( pPage->pDirtyNext ){
|
||||
pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
|
||||
}else{
|
||||
assert( pPage==p->pDirtyTail );
|
||||
p->pDirtyTail = pPage->pDirtyPrev;
|
||||
}
|
||||
if( pPage->pDirtyPrev ){
|
||||
pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
|
||||
}else{
|
||||
assert( pPage==p->pDirty );
|
||||
p->pDirty = pPage->pDirtyNext;
|
||||
}
|
||||
pPage->pDirtyNext = 0;
|
||||
pPage->pDirtyPrev = 0;
|
||||
|
||||
expensive_assert( pcacheCheckSynced(p) );
|
||||
}
|
||||
|
||||
/*
|
||||
** Add page pPage to the head of the dirty list (PCache1.pDirty is set to
|
||||
** pPage).
|
||||
*/
|
||||
static void pcacheAddToDirtyList(PgHdr *pPage){
|
||||
PCache *p = pPage->pCache;
|
||||
|
||||
assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
|
||||
|
||||
pPage->pDirtyNext = p->pDirty;
|
||||
if( pPage->pDirtyNext ){
|
||||
assert( pPage->pDirtyNext->pDirtyPrev==0 );
|
||||
pPage->pDirtyNext->pDirtyPrev = pPage;
|
||||
}
|
||||
p->pDirty = pPage;
|
||||
if( !p->pDirtyTail ){
|
||||
p->pDirtyTail = pPage;
|
||||
}
|
||||
if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
|
||||
p->pSynced = pPage;
|
||||
}
|
||||
expensive_assert( pcacheCheckSynced(p) );
|
||||
}
|
||||
|
||||
/*
|
||||
** Wrapper around the pluggable caches xUnpin method. If the cache is
|
||||
** being used for an in-memory database, this function is a no-op.
|
||||
*/
|
||||
static void pcacheUnpin(PgHdr *p){
|
||||
PCache *pCache = p->pCache;
|
||||
if( pCache->bPurgeable ){
|
||||
if( p->pgno==1 ){
|
||||
pCache->pPage1 = 0;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************** General Interfaces ******
|
||||
**
|
||||
** Initialize and shutdown the page cache subsystem. Neither of these
|
||||
** functions are threadsafe.
|
||||
*/
|
||||
int sqlite3PcacheInitialize(void){
|
||||
if( sqlite3GlobalConfig.pcache.xInit==0 ){
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
|
||||
}
|
||||
void sqlite3PcacheShutdown(void){
|
||||
if( sqlite3GlobalConfig.pcache.xShutdown ){
|
||||
sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size in bytes of a PCache object.
|
||||
*/
|
||||
int sqlite3PcacheSize(void){ return sizeof(PCache); }
|
||||
|
||||
/*
|
||||
** Create a new PCache object. Storage space to hold the object
|
||||
** has already been allocated and is passed in as the p pointer.
|
||||
** The caller discovers how much space needs to be allocated by
|
||||
** calling sqlite3PcacheSize().
|
||||
*/
|
||||
void sqlite3PcacheOpen(
|
||||
int szPage, /* Size of every page */
|
||||
int szExtra, /* Extra space associated with each page */
|
||||
int bPurgeable, /* True if pages are on backing store */
|
||||
int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
|
||||
void *pStress, /* Argument to xStress */
|
||||
PCache *p /* Preallocated space for the PCache */
|
||||
){
|
||||
memset(p, 0, sizeof(PCache));
|
||||
p->szPage = szPage;
|
||||
p->szExtra = szExtra;
|
||||
p->bPurgeable = bPurgeable;
|
||||
p->xStress = xStress;
|
||||
p->pStress = pStress;
|
||||
p->nMax = 100;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the page size for PCache object. The caller must ensure that there
|
||||
** are no outstanding page references when this function is called.
|
||||
*/
|
||||
void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
||||
assert( pCache->nRef==0 && pCache->pDirty==0 );
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
|
||||
pCache->pCache = 0;
|
||||
}
|
||||
pCache->szPage = szPage;
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to obtain a page from the cache.
|
||||
*/
|
||||
int sqlite3PcacheFetch(
|
||||
PCache *pCache, /* Obtain the page from this cache */
|
||||
Pgno pgno, /* Page number to obtain */
|
||||
int createFlag, /* If true, create page if it does not exist already */
|
||||
PgHdr **ppPage /* Write the page here */
|
||||
){
|
||||
PgHdr *pPage = 0;
|
||||
int eCreate;
|
||||
|
||||
assert( pCache!=0 );
|
||||
assert( pgno>0 );
|
||||
|
||||
/* If the pluggable cache (sqlite3_pcache*) has not been allocated,
|
||||
** allocate it now.
|
||||
*/
|
||||
if( !pCache->pCache && createFlag ){
|
||||
sqlite3_pcache *p;
|
||||
int nByte;
|
||||
nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr);
|
||||
p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable);
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax);
|
||||
pCache->pCache = p;
|
||||
}
|
||||
|
||||
eCreate = createFlag ? 1 : 0;
|
||||
if( eCreate && (!pCache->bPurgeable || !pCache->pDirty) ){
|
||||
eCreate = 2;
|
||||
}
|
||||
if( pCache->pCache ){
|
||||
pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate);
|
||||
}
|
||||
|
||||
if( !pPage && eCreate==1 ){
|
||||
PgHdr *pPg;
|
||||
|
||||
/* Find a dirty page to write-out and recycle. First try to find a
|
||||
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
||||
** cleared), but if that is not possible settle for any other
|
||||
** unreferenced dirty page.
|
||||
*/
|
||||
expensive_assert( pcacheCheckSynced(pCache) );
|
||||
for(pPg=pCache->pSynced;
|
||||
pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
|
||||
pPg=pPg->pDirtyPrev
|
||||
);
|
||||
if( !pPg ){
|
||||
for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
|
||||
}
|
||||
if( pPg ){
|
||||
int rc;
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2);
|
||||
}
|
||||
|
||||
if( pPage ){
|
||||
if( !pPage->pData ){
|
||||
memset(pPage, 0, sizeof(PgHdr) + pCache->szExtra);
|
||||
pPage->pExtra = (void*)&pPage[1];
|
||||
pPage->pData = (void *)&((char *)pPage)[sizeof(PgHdr) + pCache->szExtra];
|
||||
pPage->pCache = pCache;
|
||||
pPage->pgno = pgno;
|
||||
}
|
||||
assert( pPage->pCache==pCache );
|
||||
assert( pPage->pgno==pgno );
|
||||
assert( pPage->pExtra==(void *)&pPage[1] );
|
||||
|
||||
if( 0==pPage->nRef ){
|
||||
pCache->nRef++;
|
||||
}
|
||||
pPage->nRef++;
|
||||
if( pgno==1 ){
|
||||
pCache->pPage1 = pPage;
|
||||
}
|
||||
}
|
||||
*ppPage = pPage;
|
||||
return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decrement the reference count on a page. If the page is clean and the
|
||||
** reference count drops to 0, then it is made elible for recycling.
|
||||
*/
|
||||
void sqlite3PcacheRelease(PgHdr *p){
|
||||
assert( p->nRef>0 );
|
||||
p->nRef--;
|
||||
if( p->nRef==0 ){
|
||||
PCache *pCache = p->pCache;
|
||||
pCache->nRef--;
|
||||
if( (p->flags&PGHDR_DIRTY)==0 ){
|
||||
pcacheUnpin(p);
|
||||
}else{
|
||||
/* Move the page to the head of the dirty list. */
|
||||
pcacheRemoveFromDirtyList(p);
|
||||
pcacheAddToDirtyList(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Increase the reference count of a supplied page by 1.
|
||||
*/
|
||||
void sqlite3PcacheRef(PgHdr *p){
|
||||
assert(p->nRef>0);
|
||||
p->nRef++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop a page from the cache. There must be exactly one reference to the
|
||||
** page. This function deletes that reference, so after it returns the
|
||||
** page pointed to by p is invalid.
|
||||
*/
|
||||
void sqlite3PcacheDrop(PgHdr *p){
|
||||
PCache *pCache;
|
||||
assert( p->nRef==1 );
|
||||
if( p->flags&PGHDR_DIRTY ){
|
||||
pcacheRemoveFromDirtyList(p);
|
||||
}
|
||||
pCache = p->pCache;
|
||||
pCache->nRef--;
|
||||
if( p->pgno==1 ){
|
||||
pCache->pPage1 = 0;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure the page is marked as dirty. If it isn't dirty already,
|
||||
** make it so.
|
||||
*/
|
||||
void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
PCache *pCache;
|
||||
p->flags &= ~PGHDR_DONT_WRITE;
|
||||
assert( p->nRef>0 );
|
||||
if( 0==(p->flags & PGHDR_DIRTY) ){
|
||||
pCache = p->pCache;
|
||||
p->flags |= PGHDR_DIRTY;
|
||||
pcacheAddToDirtyList( p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure the page is marked as clean. If it isn't clean already,
|
||||
** make it so.
|
||||
*/
|
||||
void sqlite3PcacheMakeClean(PgHdr *p){
|
||||
if( (p->flags & PGHDR_DIRTY) ){
|
||||
pcacheRemoveFromDirtyList(p);
|
||||
p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC);
|
||||
if( p->nRef==0 ){
|
||||
pcacheUnpin(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Make every page in the cache clean.
|
||||
*/
|
||||
void sqlite3PcacheCleanAll(PCache *pCache){
|
||||
PgHdr *p;
|
||||
while( (p = pCache->pDirty)!=0 ){
|
||||
sqlite3PcacheMakeClean(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
|
||||
*/
|
||||
void sqlite3PcacheClearSyncFlags(PCache *pCache){
|
||||
PgHdr *p;
|
||||
for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
||||
p->flags &= ~PGHDR_NEED_SYNC;
|
||||
}
|
||||
pCache->pSynced = pCache->pDirtyTail;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the page number of page p to newPgno.
|
||||
*/
|
||||
void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
||||
PCache *pCache = p->pCache;
|
||||
assert( p->nRef>0 );
|
||||
assert( newPgno>0 );
|
||||
sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno);
|
||||
p->pgno = newPgno;
|
||||
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
||||
pcacheRemoveFromDirtyList(p);
|
||||
pcacheAddToDirtyList(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop every cache entry whose page number is greater than "pgno". The
|
||||
** caller must ensure that there are no outstanding references to any pages
|
||||
** other than page 1 with a page number greater than pgno.
|
||||
**
|
||||
** If there is a reference to page 1 and the pgno parameter passed to this
|
||||
** function is 0, then the data area associated with page 1 is zeroed, but
|
||||
** the page object is not dropped.
|
||||
*/
|
||||
void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
||||
if( pCache->pCache ){
|
||||
PgHdr *p;
|
||||
PgHdr *pNext;
|
||||
for(p=pCache->pDirty; p; p=pNext){
|
||||
pNext = p->pDirtyNext;
|
||||
if( p->pgno>pgno ){
|
||||
assert( p->flags&PGHDR_DIRTY );
|
||||
sqlite3PcacheMakeClean(p);
|
||||
}
|
||||
}
|
||||
if( pgno==0 && pCache->pPage1 ){
|
||||
memset(pCache->pPage1->pData, 0, pCache->szPage);
|
||||
pgno = 1;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a cache.
|
||||
*/
|
||||
void sqlite3PcacheClose(PCache *pCache){
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Discard the contents of the cache.
|
||||
*/
|
||||
void sqlite3PcacheClear(PCache *pCache){
|
||||
sqlite3PcacheTruncate(pCache, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Merge two lists of pages connected by pDirty and in pgno order.
|
||||
** Do not both fixing the pDirtyPrev pointers.
|
||||
*/
|
||||
static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
|
||||
PgHdr result, *pTail;
|
||||
pTail = &result;
|
||||
while( pA && pB ){
|
||||
if( pA->pgno<pB->pgno ){
|
||||
pTail->pDirty = pA;
|
||||
pTail = pA;
|
||||
pA = pA->pDirty;
|
||||
}else{
|
||||
pTail->pDirty = pB;
|
||||
pTail = pB;
|
||||
pB = pB->pDirty;
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
pTail->pDirty = pA;
|
||||
}else if( pB ){
|
||||
pTail->pDirty = pB;
|
||||
}else{
|
||||
pTail->pDirty = 0;
|
||||
}
|
||||
return result.pDirty;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort the list of pages in accending order by pgno. Pages are
|
||||
** connected by pDirty pointers. The pDirtyPrev pointers are
|
||||
** corrupted by this sort.
|
||||
*/
|
||||
#define N_SORT_BUCKET_ALLOC 25
|
||||
#define N_SORT_BUCKET 25
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3_pager_n_sort_bucket = 0;
|
||||
#undef N_SORT_BUCKET
|
||||
#define N_SORT_BUCKET \
|
||||
(sqlite3_pager_n_sort_bucket?sqlite3_pager_n_sort_bucket:N_SORT_BUCKET_ALLOC)
|
||||
#endif
|
||||
static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
|
||||
PgHdr *a[N_SORT_BUCKET_ALLOC], *p;
|
||||
int i;
|
||||
memset(a, 0, sizeof(a));
|
||||
while( pIn ){
|
||||
p = pIn;
|
||||
pIn = p->pDirty;
|
||||
p->pDirty = 0;
|
||||
for(i=0; i<N_SORT_BUCKET-1; i++){
|
||||
if( a[i]==0 ){
|
||||
a[i] = p;
|
||||
break;
|
||||
}else{
|
||||
p = pcacheMergeDirtyList(a[i], p);
|
||||
a[i] = 0;
|
||||
}
|
||||
}
|
||||
if( i==N_SORT_BUCKET-1 ){
|
||||
/* Coverage: To get here, there need to be 2^(N_SORT_BUCKET)
|
||||
** elements in the input list. This is possible, but impractical.
|
||||
** Testing this line is the point of global variable
|
||||
** sqlite3_pager_n_sort_bucket.
|
||||
*/
|
||||
a[i] = pcacheMergeDirtyList(a[i], p);
|
||||
}
|
||||
}
|
||||
p = a[0];
|
||||
for(i=1; i<N_SORT_BUCKET; i++){
|
||||
p = pcacheMergeDirtyList(p, a[i]);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a list of all dirty pages in the cache, sorted by page number.
|
||||
*/
|
||||
PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
|
||||
PgHdr *p;
|
||||
for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
||||
p->pDirty = p->pDirtyNext;
|
||||
}
|
||||
return pcacheSortDirtyList(pCache->pDirty);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of referenced pages held by the cache.
|
||||
*/
|
||||
int sqlite3PcacheRefCount(PCache *pCache){
|
||||
return pCache->nRef;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of references to the page supplied as an argument.
|
||||
*/
|
||||
int sqlite3PcachePageRefcount(PgHdr *p){
|
||||
return p->nRef;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of pages in the cache.
|
||||
*/
|
||||
int sqlite3PcachePagecount(PCache *pCache){
|
||||
int nPage = 0;
|
||||
if( pCache->pCache ){
|
||||
nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache);
|
||||
}
|
||||
return nPage;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** Get the suggested cache-size value.
|
||||
*/
|
||||
int sqlite3PcacheGetCachesize(PCache *pCache){
|
||||
return pCache->nMax;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Set the suggested cache-size value.
|
||||
*/
|
||||
void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
|
||||
pCache->nMax = mxPage;
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
/*
|
||||
** For all dirty pages currently in the cache, invoke the specified
|
||||
** callback. This is only used if the SQLITE_CHECK_PAGES macro is
|
||||
** defined.
|
||||
*/
|
||||
void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){
|
||||
PgHdr *pDirty;
|
||||
for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){
|
||||
xIter(pDirty);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
** 2008 August 05
|
||||
**
|
||||
** 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 header file defines the interface that the sqlite page cache
|
||||
** subsystem.
|
||||
**
|
||||
** @(#) $Id: pcache.h,v 1.19 2009/01/20 17:06:27 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PCACHE_H_
|
||||
|
||||
typedef struct PgHdr PgHdr;
|
||||
typedef struct PCache PCache;
|
||||
|
||||
/*
|
||||
** Every page in the cache is controlled by an instance of the following
|
||||
** structure.
|
||||
*/
|
||||
struct PgHdr {
|
||||
void *pData; /* Content of this page */
|
||||
void *pExtra; /* Extra content */
|
||||
PgHdr *pDirty; /* Transient list of dirty pages */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
Pager *pPager; /* The pager this page is part of */
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
u32 pageHash; /* Hash of page content */
|
||||
#endif
|
||||
u16 flags; /* PGHDR flags defined below */
|
||||
|
||||
/**********************************************************************
|
||||
** Elements above are public. All that follows is private to pcache.c
|
||||
** and should not be accessed by other modules.
|
||||
*/
|
||||
i16 nRef; /* Number of users of this page */
|
||||
PCache *pCache; /* Cache that owns this page */
|
||||
|
||||
PgHdr *pDirtyNext; /* Next element in list of dirty pages */
|
||||
PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */
|
||||
};
|
||||
|
||||
/* Bit values for PgHdr.flags */
|
||||
#define PGHDR_DIRTY 0x002 /* Page has changed */
|
||||
#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before
|
||||
** writing this page to the database */
|
||||
#define PGHDR_NEED_READ 0x008 /* Content is unread */
|
||||
#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */
|
||||
#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */
|
||||
|
||||
/* Initialize and shutdown the page cache subsystem */
|
||||
int sqlite3PcacheInitialize(void);
|
||||
void sqlite3PcacheShutdown(void);
|
||||
|
||||
/* Page cache buffer management:
|
||||
** These routines implement SQLITE_CONFIG_PAGECACHE.
|
||||
*/
|
||||
void sqlite3PCacheBufferSetup(void *, int sz, int n);
|
||||
|
||||
/* Create a new pager cache.
|
||||
** Under memory stress, invoke xStress to try to make pages clean.
|
||||
** Only clean and unpinned pages can be reclaimed.
|
||||
*/
|
||||
void sqlite3PcacheOpen(
|
||||
int szPage, /* Size of every page */
|
||||
int szExtra, /* Extra space associated with each page */
|
||||
int bPurgeable, /* True if pages are on backing store */
|
||||
int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */
|
||||
void *pStress, /* Argument to xStress */
|
||||
PCache *pToInit /* Preallocated space for the PCache */
|
||||
);
|
||||
|
||||
/* Modify the page-size after the cache has been created. */
|
||||
void sqlite3PcacheSetPageSize(PCache *, int);
|
||||
|
||||
/* Return the size in bytes of a PCache object. Used to preallocate
|
||||
** storage space.
|
||||
*/
|
||||
int sqlite3PcacheSize(void);
|
||||
|
||||
/* One release per successful fetch. Page is pinned until released.
|
||||
** Reference counted.
|
||||
*/
|
||||
int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**);
|
||||
void sqlite3PcacheRelease(PgHdr*);
|
||||
|
||||
void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */
|
||||
void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */
|
||||
void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */
|
||||
void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */
|
||||
|
||||
/* Change a page number. Used by incr-vacuum. */
|
||||
void sqlite3PcacheMove(PgHdr*, Pgno);
|
||||
|
||||
/* Remove all pages with pgno>x. Reset the cache if x==0 */
|
||||
void sqlite3PcacheTruncate(PCache*, Pgno x);
|
||||
|
||||
/* Get a list of all dirty pages in the cache, sorted by page number */
|
||||
PgHdr *sqlite3PcacheDirtyList(PCache*);
|
||||
|
||||
/* Reset and close the cache object */
|
||||
void sqlite3PcacheClose(PCache*);
|
||||
|
||||
/* Clear flags from pages of the page cache */
|
||||
void sqlite3PcacheClearSyncFlags(PCache *);
|
||||
|
||||
/* Discard the contents of the cache */
|
||||
void sqlite3PcacheClear(PCache*);
|
||||
|
||||
/* Return the total number of outstanding page references */
|
||||
int sqlite3PcacheRefCount(PCache*);
|
||||
|
||||
/* Increment the reference count of an existing page */
|
||||
void sqlite3PcacheRef(PgHdr*);
|
||||
|
||||
int sqlite3PcachePageRefcount(PgHdr*);
|
||||
|
||||
/* Return the total number of pages stored in the cache */
|
||||
int sqlite3PcachePagecount(PCache*);
|
||||
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
/* Iterate through all dirty pages currently stored in the cache. This
|
||||
** interface is only available if SQLITE_CHECK_PAGES is defined when the
|
||||
** library is built.
|
||||
*/
|
||||
void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
|
||||
#endif
|
||||
|
||||
/* Set and get the suggested cache-size for the specified pager-cache.
|
||||
**
|
||||
** If no global maximum is configured, then the system attempts to limit
|
||||
** the total number of pages cached by purgeable pager-caches to the sum
|
||||
** of the suggested cache-sizes.
|
||||
*/
|
||||
void sqlite3PcacheSetCachesize(PCache *, int);
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3PcacheGetCachesize(PCache *);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/* Try to return memory used by the pcache module to the main memory heap */
|
||||
int sqlite3PcacheReleaseMemory(int);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
void sqlite3PcacheStats(int*,int*,int*,int*);
|
||||
#endif
|
||||
|
||||
void sqlite3PCacheSetDefault(void);
|
||||
|
||||
#endif /* _PCACHE_H_ */
|
||||
+752
@@ -0,0 +1,752 @@
|
||||
/*
|
||||
** 2008 November 05
|
||||
**
|
||||
** 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 the default page cache implementation (the
|
||||
** sqlite3_pcache interface). It also contains part of the implementation
|
||||
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
|
||||
** If the default page cache implementation is overriden, then neither of
|
||||
** these two features are available.
|
||||
**
|
||||
** @(#) $Id: pcache1.c,v 1.8 2009/01/23 16:45:01 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
typedef struct PCache1 PCache1;
|
||||
typedef struct PgHdr1 PgHdr1;
|
||||
typedef struct PgFreeslot PgFreeslot;
|
||||
|
||||
/* Pointers to structures of this type are cast and returned as
|
||||
** opaque sqlite3_pcache* handles
|
||||
*/
|
||||
struct PCache1 {
|
||||
/* Cache configuration parameters. Page size (szPage) and the purgeable
|
||||
** flag (bPurgeable) are set when the cache is created. nMax may be
|
||||
** modified at any time by a call to the pcache1CacheSize() method.
|
||||
** The global mutex must be held when accessing nMax.
|
||||
*/
|
||||
int szPage; /* Size of allocated pages in bytes */
|
||||
int bPurgeable; /* True if cache is purgeable */
|
||||
unsigned int nMin; /* Minimum number of pages reserved */
|
||||
unsigned int nMax; /* Configured "cache_size" value */
|
||||
|
||||
/* Hash table of all pages. The following variables may only be accessed
|
||||
** when the accessor is holding the global mutex (see pcache1EnterMutex()
|
||||
** and pcache1LeaveMutex()).
|
||||
*/
|
||||
unsigned int nRecyclable; /* Number of pages in the LRU list */
|
||||
unsigned int nPage; /* Total number of pages in apHash */
|
||||
unsigned int nHash; /* Number of slots in apHash[] */
|
||||
PgHdr1 **apHash; /* Hash table for fast lookup by key */
|
||||
|
||||
unsigned int iMaxKey; /* Largest key seen since xTruncate() */
|
||||
};
|
||||
|
||||
/*
|
||||
** Each cache entry is represented by an instance of the following
|
||||
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
|
||||
** directly after the structure in memory (see the PGHDR1_TO_PAGE()
|
||||
** macro below).
|
||||
*/
|
||||
struct PgHdr1 {
|
||||
unsigned int iKey; /* Key value (page number) */
|
||||
PgHdr1 *pNext; /* Next in hash table chain */
|
||||
PCache1 *pCache; /* Cache that currently owns this page */
|
||||
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
|
||||
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
|
||||
};
|
||||
|
||||
/*
|
||||
** Free slots in the allocator used to divide up the buffer provided using
|
||||
** the SQLITE_CONFIG_PAGECACHE mechanism.
|
||||
*/
|
||||
struct PgFreeslot {
|
||||
PgFreeslot *pNext; /* Next free slot */
|
||||
};
|
||||
|
||||
/*
|
||||
** Global data used by this cache.
|
||||
*/
|
||||
static SQLITE_WSD struct PCacheGlobal {
|
||||
sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */
|
||||
|
||||
int nMaxPage; /* Sum of nMaxPage for purgeable caches */
|
||||
int nMinPage; /* Sum of nMinPage for purgeable caches */
|
||||
int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
|
||||
|
||||
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
|
||||
int szSlot; /* Size of each free slot */
|
||||
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
|
||||
PgFreeslot *pFree; /* Free page blocks */
|
||||
} pcache1_g;
|
||||
|
||||
/*
|
||||
** All code in this file should access the global structure above via the
|
||||
** alias "pcache1". This ensures that the WSD emulation is used when
|
||||
** compiling for systems that do not support real WSD.
|
||||
*/
|
||||
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
|
||||
|
||||
/*
|
||||
** When a PgHdr1 structure is allocated, the associated PCache1.szPage
|
||||
** bytes of data are located directly after it in memory (i.e. the total
|
||||
** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
|
||||
** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
|
||||
** an argument and returns a pointer to the associated block of szPage
|
||||
** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
|
||||
** a pointer to a block of szPage bytes of data and the return value is
|
||||
** a pointer to the associated PgHdr1 structure.
|
||||
**
|
||||
** assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(X))==X );
|
||||
*/
|
||||
#define PGHDR1_TO_PAGE(p) (void *)(&((unsigned char *)p)[sizeof(PgHdr1)])
|
||||
#define PAGE_TO_PGHDR1(p) (PgHdr1 *)(&((unsigned char *)p)[-1*(int)sizeof(PgHdr1)])
|
||||
|
||||
/*
|
||||
** Macros to enter and leave the global LRU mutex.
|
||||
*/
|
||||
#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)
|
||||
#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)
|
||||
|
||||
/******************************************************************************/
|
||||
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
|
||||
|
||||
/*
|
||||
** This function is called during initialization if a static buffer is
|
||||
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
|
||||
** verb to sqlite3_config(). Parameter pBuf points to an allocation large
|
||||
** enough to contain 'n' buffers of 'sz' bytes each.
|
||||
*/
|
||||
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||
PgFreeslot *p;
|
||||
sz &= ~7;
|
||||
pcache1.szSlot = sz;
|
||||
pcache1.pStart = pBuf;
|
||||
pcache1.pFree = 0;
|
||||
while( n-- ){
|
||||
p = (PgFreeslot*)pBuf;
|
||||
p->pNext = pcache1.pFree;
|
||||
pcache1.pFree = p;
|
||||
pBuf = (void*)&((char*)pBuf)[sz];
|
||||
}
|
||||
pcache1.pEnd = pBuf;
|
||||
}
|
||||
|
||||
/*
|
||||
** Malloc function used within this file to allocate space from the buffer
|
||||
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
|
||||
** such buffer exists or there is no space left in it, this function falls
|
||||
** back to sqlite3Malloc().
|
||||
*/
|
||||
static void *pcache1Alloc(int nByte){
|
||||
void *p;
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
if( nByte<=pcache1.szSlot && pcache1.pFree ){
|
||||
p = (PgHdr1 *)pcache1.pFree;
|
||||
pcache1.pFree = pcache1.pFree->pNext;
|
||||
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
|
||||
}else{
|
||||
|
||||
/* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the
|
||||
** global pcache mutex and unlock the pager-cache object pCache. This is
|
||||
** so that if the attempt to allocate a new buffer causes the the
|
||||
** configured soft-heap-limit to be breached, it will be possible to
|
||||
** reclaim memory from this pager-cache.
|
||||
*/
|
||||
pcache1LeaveMutex();
|
||||
p = sqlite3Malloc(nByte);
|
||||
pcache1EnterMutex();
|
||||
if( p ){
|
||||
int sz = sqlite3MallocSize(p);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free an allocated buffer obtained from pcache1Alloc().
|
||||
*/
|
||||
static void pcache1Free(void *p){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
if( p==0 ) return;
|
||||
if( p>=pcache1.pStart && p<pcache1.pEnd ){
|
||||
PgFreeslot *pSlot;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
|
||||
pSlot = (PgFreeslot*)p;
|
||||
pSlot->pNext = pcache1.pFree;
|
||||
pcache1.pFree = pSlot;
|
||||
}else{
|
||||
int iSize = sqlite3MallocSize(p);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a new page object initially associated with cache pCache.
|
||||
*/
|
||||
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
int nByte = sizeof(PgHdr1) + pCache->szPage;
|
||||
PgHdr1 *p = (PgHdr1 *)pcache1Alloc(nByte);
|
||||
if( p ){
|
||||
if( pCache->bPurgeable ){
|
||||
pcache1.nCurrentPage++;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a page object allocated by pcache1AllocPage().
|
||||
*/
|
||||
static void pcache1FreePage(PgHdr1 *p){
|
||||
if( p ){
|
||||
if( p->pCache->bPurgeable ){
|
||||
pcache1.nCurrentPage--;
|
||||
}
|
||||
pcache1Free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Malloc function used by SQLite to obtain space from the buffer configured
|
||||
** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
|
||||
** exists, this function falls back to sqlite3Malloc().
|
||||
*/
|
||||
void *sqlite3PageMalloc(int sz){
|
||||
void *p;
|
||||
pcache1EnterMutex();
|
||||
p = pcache1Alloc(sz);
|
||||
pcache1LeaveMutex();
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free an allocated buffer obtained from sqlite3PageMalloc().
|
||||
*/
|
||||
void sqlite3PageFree(void *p){
|
||||
pcache1EnterMutex();
|
||||
pcache1Free(p);
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/******** General Implementation Functions ************************************/
|
||||
|
||||
/*
|
||||
** This function is used to resize the hash table used by the cache passed
|
||||
** as the first argument.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
*/
|
||||
static int pcache1ResizeHash(PCache1 *p){
|
||||
PgHdr1 **apNew;
|
||||
unsigned int nNew;
|
||||
unsigned int i;
|
||||
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
|
||||
nNew = p->nHash*2;
|
||||
if( nNew<256 ){
|
||||
nNew = 256;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex();
|
||||
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
|
||||
apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
|
||||
if( p->nHash ){ sqlite3EndBenignMalloc(); }
|
||||
pcache1EnterMutex();
|
||||
if( apNew ){
|
||||
memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
|
||||
for(i=0; i<p->nHash; i++){
|
||||
PgHdr1 *pPage;
|
||||
PgHdr1 *pNext = p->apHash[i];
|
||||
while( (pPage = pNext)!=0 ){
|
||||
unsigned int h = pPage->iKey % nNew;
|
||||
pNext = pPage->pNext;
|
||||
pPage->pNext = apNew[h];
|
||||
apNew[h] = pPage;
|
||||
}
|
||||
}
|
||||
sqlite3_free(p->apHash);
|
||||
p->apHash = apNew;
|
||||
p->nHash = nNew;
|
||||
}
|
||||
|
||||
return (p->apHash ? SQLITE_OK : SQLITE_NOMEM);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used internally to remove the page pPage from the
|
||||
** global LRU list, if is part of it. If pPage is not part of the global
|
||||
** LRU list, then this function is a no-op.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
*/
|
||||
static void pcache1PinPage(PgHdr1 *pPage){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
if( pPage && (pPage->pLruNext || pPage==pcache1.pLruTail) ){
|
||||
if( pPage->pLruPrev ){
|
||||
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||
}
|
||||
if( pPage->pLruNext ){
|
||||
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||
}
|
||||
if( pcache1.pLruHead==pPage ){
|
||||
pcache1.pLruHead = pPage->pLruNext;
|
||||
}
|
||||
if( pcache1.pLruTail==pPage ){
|
||||
pcache1.pLruTail = pPage->pLruPrev;
|
||||
}
|
||||
pPage->pLruNext = 0;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pCache->nRecyclable--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove the page supplied as an argument from the hash table
|
||||
** (PCache1.apHash structure) that it is currently stored in.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
*/
|
||||
static void pcache1RemoveFromHash(PgHdr1 *pPage){
|
||||
unsigned int h;
|
||||
PCache1 *pCache = pPage->pCache;
|
||||
PgHdr1 **pp;
|
||||
|
||||
h = pPage->iKey % pCache->nHash;
|
||||
for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext);
|
||||
*pp = (*pp)->pNext;
|
||||
|
||||
pCache->nPage--;
|
||||
}
|
||||
|
||||
/*
|
||||
** If there are currently more than pcache.nMaxPage pages allocated, try
|
||||
** to recycle pages to reduce the number allocated to pcache.nMaxPage.
|
||||
*/
|
||||
static void pcache1EnforceMaxPage(void){
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){
|
||||
PgHdr1 *p = pcache1.pLruTail;
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Discard all pages from cache pCache with a page number (key value)
|
||||
** greater than or equal to iLimit. Any pinned pages that meet this
|
||||
** criteria are unpinned before they are discarded.
|
||||
**
|
||||
** The global mutex must be held when this function is called.
|
||||
*/
|
||||
static void pcache1TruncateUnsafe(
|
||||
PCache1 *pCache,
|
||||
unsigned int iLimit
|
||||
){
|
||||
unsigned int h;
|
||||
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||
for(h=0; h<pCache->nHash; h++){
|
||||
PgHdr1 **pp = &pCache->apHash[h];
|
||||
PgHdr1 *pPage;
|
||||
while( (pPage = *pp)!=0 ){
|
||||
if( pPage->iKey>=iLimit ){
|
||||
pcache1PinPage(pPage);
|
||||
*pp = pPage->pNext;
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
pp = &pPage->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/******** sqlite3_pcache Methods **********************************************/
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xInit method.
|
||||
*/
|
||||
static int pcache1Init(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
memset(&pcache1, 0, sizeof(pcache1));
|
||||
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xShutdown method.
|
||||
*/
|
||||
static void pcache1Shutdown(void *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xCreate method.
|
||||
**
|
||||
** Allocate a new cache.
|
||||
*/
|
||||
static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
||||
PCache1 *pCache;
|
||||
|
||||
pCache = (PCache1 *)sqlite3_malloc(sizeof(PCache1));
|
||||
if( pCache ){
|
||||
memset(pCache, 0, sizeof(PCache1));
|
||||
pCache->szPage = szPage;
|
||||
pCache->bPurgeable = (bPurgeable ? 1 : 0);
|
||||
if( bPurgeable ){
|
||||
pCache->nMin = 10;
|
||||
pcache1EnterMutex();
|
||||
pcache1.nMinPage += pCache->nMin;
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
}
|
||||
return (sqlite3_pcache *)pCache;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xCachesize method.
|
||||
**
|
||||
** Configure the cache_size limit for a cache.
|
||||
*/
|
||||
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
if( pCache->bPurgeable ){
|
||||
pcache1EnterMutex();
|
||||
pcache1.nMaxPage += (nMax - pCache->nMax);
|
||||
pCache->nMax = nMax;
|
||||
pcache1EnforceMaxPage();
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xPagecount method.
|
||||
*/
|
||||
static int pcache1Pagecount(sqlite3_pcache *p){
|
||||
int n;
|
||||
pcache1EnterMutex();
|
||||
n = ((PCache1 *)p)->nPage;
|
||||
pcache1LeaveMutex();
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xFetch method.
|
||||
**
|
||||
** Fetch a page by key value.
|
||||
**
|
||||
** Whether or not a new page may be allocated by this function depends on
|
||||
** the value of the createFlag argument.
|
||||
**
|
||||
** There are three different approaches to obtaining space for a page,
|
||||
** depending on the value of parameter createFlag (which may be 0, 1 or 2).
|
||||
**
|
||||
** 1. Regardless of the value of createFlag, the cache is searched for a
|
||||
** copy of the requested page. If one is found, it is returned.
|
||||
**
|
||||
** 2. If createFlag==0 and the page is not already in the cache, NULL is
|
||||
** returned.
|
||||
**
|
||||
** 3. If createFlag is 1, the cache is marked as purgeable and the page is
|
||||
** not already in the cache, and if either of the following are true,
|
||||
** return NULL:
|
||||
**
|
||||
** (a) the number of pages pinned by the cache is greater than
|
||||
** PCache1.nMax, or
|
||||
** (b) the number of pages pinned by the cache is greater than
|
||||
** the sum of nMax for all purgeable caches, less the sum of
|
||||
** nMin for all other purgeable caches.
|
||||
**
|
||||
** 4. If none of the first three conditions apply and the cache is marked
|
||||
** as purgeable, and if one of the following is true:
|
||||
**
|
||||
** (a) The number of pages allocated for the cache is already
|
||||
** PCache1.nMax, or
|
||||
**
|
||||
** (b) The number of pages allocated for all purgeable caches is
|
||||
** already equal to or greater than the sum of nMax for all
|
||||
** purgeable caches,
|
||||
**
|
||||
** then attempt to recycle a page from the LRU list. If it is the right
|
||||
** size, return the recycled buffer. Otherwise, free the buffer and
|
||||
** proceed to step 5.
|
||||
**
|
||||
** 5. Otherwise, allocate and return a new page buffer.
|
||||
*/
|
||||
static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
unsigned int nPinned;
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PgHdr1 *pPage = 0;
|
||||
|
||||
pcache1EnterMutex();
|
||||
if( createFlag==1 ) sqlite3BeginBenignMalloc();
|
||||
|
||||
/* Search the hash table for an existing entry. */
|
||||
if( pCache->nHash>0 ){
|
||||
unsigned int h = iKey % pCache->nHash;
|
||||
for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext);
|
||||
}
|
||||
|
||||
if( pPage || createFlag==0 ){
|
||||
pcache1PinPage(pPage);
|
||||
goto fetch_out;
|
||||
}
|
||||
|
||||
/* Step 3 of header comment. */
|
||||
nPinned = pCache->nPage - pCache->nRecyclable;
|
||||
if( createFlag==1 && pCache->bPurgeable && (
|
||||
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|
||||
|| nPinned>=(pCache->nMax)
|
||||
)){
|
||||
goto fetch_out;
|
||||
}
|
||||
|
||||
if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){
|
||||
goto fetch_out;
|
||||
}
|
||||
|
||||
/* Step 4. Try to recycle a page buffer if appropriate. */
|
||||
if( pCache->bPurgeable && pcache1.pLruTail && (
|
||||
pCache->nPage>=pCache->nMax-1 || pcache1.nCurrentPage>=pcache1.nMaxPage
|
||||
)){
|
||||
pPage = pcache1.pLruTail;
|
||||
pcache1RemoveFromHash(pPage);
|
||||
pcache1PinPage(pPage);
|
||||
if( pPage->pCache->szPage!=pCache->szPage ){
|
||||
pcache1FreePage(pPage);
|
||||
pPage = 0;
|
||||
}else{
|
||||
pcache1.nCurrentPage -= (pPage->pCache->bPurgeable - pCache->bPurgeable);
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 5. If a usable page buffer has still not been found,
|
||||
** attempt to allocate a new one.
|
||||
*/
|
||||
if( !pPage ){
|
||||
pPage = pcache1AllocPage(pCache);
|
||||
}
|
||||
|
||||
if( pPage ){
|
||||
unsigned int h = iKey % pCache->nHash;
|
||||
*(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
|
||||
pCache->nPage++;
|
||||
pPage->iKey = iKey;
|
||||
pPage->pNext = pCache->apHash[h];
|
||||
pPage->pCache = pCache;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pLruNext = 0;
|
||||
pCache->apHash[h] = pPage;
|
||||
}
|
||||
|
||||
fetch_out:
|
||||
if( pPage && iKey>pCache->iMaxKey ){
|
||||
pCache->iMaxKey = iKey;
|
||||
}
|
||||
if( createFlag==1 ) sqlite3EndBenignMalloc();
|
||||
pcache1LeaveMutex();
|
||||
return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xUnpin method.
|
||||
**
|
||||
** Mark a page as unpinned (eligible for asynchronous recycling).
|
||||
*/
|
||||
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg);
|
||||
|
||||
pcache1EnterMutex();
|
||||
|
||||
/* It is an error to call this function if the page is already
|
||||
** part of the global LRU list.
|
||||
*/
|
||||
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
||||
assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage );
|
||||
|
||||
if( reuseUnlikely || pcache1.nCurrentPage>pcache1.nMaxPage ){
|
||||
pcache1RemoveFromHash(pPage);
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
/* Add the page to the global LRU list. Normally, the page is added to
|
||||
** the head of the list (last page to be recycled). However, if the
|
||||
** reuseUnlikely flag passed to this function is true, the page is added
|
||||
** to the tail of the list (first page to be recycled).
|
||||
*/
|
||||
if( pcache1.pLruHead ){
|
||||
pcache1.pLruHead->pLruPrev = pPage;
|
||||
pPage->pLruNext = pcache1.pLruHead;
|
||||
pcache1.pLruHead = pPage;
|
||||
}else{
|
||||
pcache1.pLruTail = pPage;
|
||||
pcache1.pLruHead = pPage;
|
||||
}
|
||||
pCache->nRecyclable++;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xRekey method.
|
||||
*/
|
||||
static void pcache1Rekey(
|
||||
sqlite3_pcache *p,
|
||||
void *pPg,
|
||||
unsigned int iOld,
|
||||
unsigned int iNew
|
||||
){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg);
|
||||
PgHdr1 **pp;
|
||||
unsigned int h;
|
||||
assert( pPage->iKey==iOld );
|
||||
|
||||
pcache1EnterMutex();
|
||||
|
||||
h = iOld%pCache->nHash;
|
||||
pp = &pCache->apHash[h];
|
||||
while( (*pp)!=pPage ){
|
||||
pp = &(*pp)->pNext;
|
||||
}
|
||||
*pp = pPage->pNext;
|
||||
|
||||
h = iNew%pCache->nHash;
|
||||
pPage->iKey = iNew;
|
||||
pPage->pNext = pCache->apHash[h];
|
||||
pCache->apHash[h] = pPage;
|
||||
|
||||
if( iNew>pCache->iMaxKey ){
|
||||
pCache->iMaxKey = iNew;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xTruncate method.
|
||||
**
|
||||
** Discard all unpinned pages in the cache with a page number equal to
|
||||
** or greater than parameter iLimit. Any pinned pages with a page number
|
||||
** equal to or greater than iLimit are implicitly unpinned.
|
||||
*/
|
||||
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
pcache1EnterMutex();
|
||||
if( iLimit<=pCache->iMaxKey ){
|
||||
pcache1TruncateUnsafe(pCache, iLimit);
|
||||
pCache->iMaxKey = iLimit-1;
|
||||
}
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xDestroy method.
|
||||
**
|
||||
** Destroy a cache allocated using pcache1Create().
|
||||
*/
|
||||
static void pcache1Destroy(sqlite3_pcache *p){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
pcache1EnterMutex();
|
||||
pcache1TruncateUnsafe(pCache, 0);
|
||||
pcache1.nMaxPage -= pCache->nMax;
|
||||
pcache1.nMinPage -= pCache->nMin;
|
||||
pcache1EnforceMaxPage();
|
||||
pcache1LeaveMutex();
|
||||
sqlite3_free(pCache->apHash);
|
||||
sqlite3_free(pCache);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called during initialization (sqlite3_initialize()) to
|
||||
** install the default pluggable cache module, assuming the user has not
|
||||
** already provided an alternative.
|
||||
*/
|
||||
void sqlite3PCacheSetDefault(void){
|
||||
static sqlite3_pcache_methods defaultMethods = {
|
||||
0, /* pArg */
|
||||
pcache1Init, /* xInit */
|
||||
pcache1Shutdown, /* xShutdown */
|
||||
pcache1Create, /* xCreate */
|
||||
pcache1Cachesize, /* xCachesize */
|
||||
pcache1Pagecount, /* xPagecount */
|
||||
pcache1Fetch, /* xFetch */
|
||||
pcache1Unpin, /* xUnpin */
|
||||
pcache1Rekey, /* xRekey */
|
||||
pcache1Truncate, /* xTruncate */
|
||||
pcache1Destroy /* xDestroy */
|
||||
};
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/*
|
||||
** This function is called to free superfluous dynamically allocated memory
|
||||
** held by the pager system. Memory in use by any SQLite pager allocated
|
||||
** by the current thread may be sqlite3_free()ed.
|
||||
**
|
||||
** nReq is the number of bytes of memory required. Once this much has
|
||||
** been released, the function returns. The return value is the total number
|
||||
** of bytes of memory released.
|
||||
*/
|
||||
int sqlite3PcacheReleaseMemory(int nReq){
|
||||
int nFree = 0;
|
||||
if( pcache1.pStart==0 ){
|
||||
PgHdr1 *p;
|
||||
pcache1EnterMutex();
|
||||
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
|
||||
nFree += sqlite3MallocSize(p);
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
}
|
||||
pcache1LeaveMutex();
|
||||
}
|
||||
return nFree;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** This function is used by test procedures to inspect the internal state
|
||||
** of the global cache.
|
||||
*/
|
||||
void sqlite3PcacheStats(
|
||||
int *pnCurrent, /* OUT: Total number of pages cached */
|
||||
int *pnMax, /* OUT: Global maximum cache size */
|
||||
int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */
|
||||
int *pnRecyclable /* OUT: Total number of pages available for recycling */
|
||||
){
|
||||
PgHdr1 *p;
|
||||
int nRecyclable = 0;
|
||||
for(p=pcache1.pLruHead; p; p=p->pLruNext){
|
||||
nRecyclable++;
|
||||
}
|
||||
*pnCurrent = pcache1.nCurrentPage;
|
||||
*pnMax = pcache1.nMaxPage;
|
||||
*pnMin = pcache1.nMinPage;
|
||||
*pnRecyclable = nRecyclable;
|
||||
}
|
||||
#endif
|
||||
+206
-102
@@ -11,10 +11,9 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the PRAGMA command.
|
||||
**
|
||||
** $Id: pragma.c,v 1.183 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: pragma.c,v 1.202 2009/01/20 16:53:41 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/* Ignore this whole file if pragmas are disabled
|
||||
*/
|
||||
@@ -30,18 +29,18 @@
|
||||
** to support legacy SQL code. The safety level used to be boolean
|
||||
** and older scripts may have used numbers 0 for OFF and 1 for ON.
|
||||
*/
|
||||
static int getSafetyLevel(const char *z){
|
||||
static u8 getSafetyLevel(const char *z){
|
||||
/* 123456789 123456789 */
|
||||
static const char zText[] = "onoffalseyestruefull";
|
||||
static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16};
|
||||
static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 4};
|
||||
static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2};
|
||||
int i, n;
|
||||
if( isdigit(*z) ){
|
||||
return atoi(z);
|
||||
if( sqlite3Isdigit(*z) ){
|
||||
return (u8)atoi(z);
|
||||
}
|
||||
n = strlen(z);
|
||||
for(i=0; i<sizeof(iLength); i++){
|
||||
n = sqlite3Strlen30(z);
|
||||
for(i=0; i<ArraySize(iLength); i++){
|
||||
if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0 ){
|
||||
return iValue[i];
|
||||
}
|
||||
@@ -52,7 +51,7 @@ static int getSafetyLevel(const char *z){
|
||||
/*
|
||||
** Interpret the given string as a boolean value.
|
||||
*/
|
||||
static int getBoolean(const char *z){
|
||||
static u8 getBoolean(const char *z){
|
||||
return getSafetyLevel(z)&1;
|
||||
}
|
||||
|
||||
@@ -80,7 +79,7 @@ static int getAutoVacuum(const char *z){
|
||||
if( 0==sqlite3StrICmp(z, "full") ) return BTREE_AUTOVACUUM_FULL;
|
||||
if( 0==sqlite3StrICmp(z, "incremental") ) return BTREE_AUTOVACUUM_INCR;
|
||||
i = atoi(z);
|
||||
return ((i>=0&&i<=2)?i:0);
|
||||
return (u8)((i>=0&&i<=2)?i:0);
|
||||
}
|
||||
#endif /* ifndef SQLITE_OMIT_AUTOVACUUM */
|
||||
|
||||
@@ -137,7 +136,7 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){
|
||||
if( invalidateTempStorage( pParse ) != SQLITE_OK ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
db->temp_store = ts;
|
||||
db->temp_store = (u8)ts;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* SQLITE_PAGER_PRAGMAS */
|
||||
@@ -151,7 +150,7 @@ static void returnSingleInt(Parse *pParse, const char *zLabel, int value){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, value, mem);
|
||||
if( pParse->explain==0 ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
|
||||
}
|
||||
@@ -191,12 +190,13 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
||||
};
|
||||
int i;
|
||||
const struct sPragmaType *p;
|
||||
for(i=0, p=aPragma; i<sizeof(aPragma)/sizeof(aPragma[0]); i++, p++){
|
||||
for(i=0, p=aPragma; i<ArraySize(aPragma); i++, p++){
|
||||
if( sqlite3StrICmp(zLeft, p->zName)==0 ){
|
||||
sqlite3 *db = pParse->db;
|
||||
Vdbe *v;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v ){
|
||||
assert( v!=0 ); /* Already allocated by sqlite3Pragma() */
|
||||
if( ALWAYS(v) ){
|
||||
if( zRight==0 ){
|
||||
returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 );
|
||||
}else{
|
||||
@@ -221,6 +221,21 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
||||
}
|
||||
#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
|
||||
|
||||
/*
|
||||
** Return a human-readable name for a constraint resolution action.
|
||||
*/
|
||||
static const char *actionName(u8 action){
|
||||
const char *zName;
|
||||
switch( action ){
|
||||
case OE_SetNull: zName = "SET NULL"; break;
|
||||
case OE_SetDflt: zName = "SET DEFAULT"; break;
|
||||
case OE_Cascade: zName = "CASCADE"; break;
|
||||
default: zName = "RESTRICT";
|
||||
assert( action==OE_Restrict ); break;
|
||||
}
|
||||
return zName;
|
||||
}
|
||||
|
||||
/*
|
||||
** Process a pragma statement.
|
||||
**
|
||||
@@ -275,7 +290,8 @@ void sqlite3Pragma(
|
||||
zRight = sqlite3NameFromToken(db, pValue);
|
||||
}
|
||||
|
||||
zDb = ((iDb>0)?pDb->zName:0);
|
||||
assert( pId2 );
|
||||
zDb = pId2->n>0 ? pDb->zName : 0;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
|
||||
goto pragma_out;
|
||||
}
|
||||
@@ -312,7 +328,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
if( !zRight ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC);
|
||||
pParse->nMem += 2;
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
@@ -343,8 +359,9 @@ void sqlite3Pragma(
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"page_size")==0 ){
|
||||
Btree *pBt = pDb->pBt;
|
||||
assert( pBt!=0 );
|
||||
if( !zRight ){
|
||||
int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
|
||||
int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0;
|
||||
returnSingleInt(pParse, "page_size", size);
|
||||
}else{
|
||||
/* Malloc may fail when setting the page-size, as there is an internal
|
||||
@@ -369,10 +386,11 @@ void sqlite3Pragma(
|
||||
if( sqlite3StrICmp(zLeft,"max_page_count")==0 ){
|
||||
Btree *pBt = pDb->pBt;
|
||||
int newMax = 0;
|
||||
assert( pBt!=0 );
|
||||
if( zRight ){
|
||||
newMax = atoi(zRight);
|
||||
}
|
||||
if( pBt ){
|
||||
if( ALWAYS(pBt) ){
|
||||
newMax = sqlite3BtreeMaxPageCount(pBt, newMax);
|
||||
}
|
||||
returnSingleInt(pParse, "max_page_count", newMax);
|
||||
@@ -384,16 +402,14 @@ void sqlite3Pragma(
|
||||
** Return the number of pages in the specified database.
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"page_count")==0 ){
|
||||
Vdbe *v;
|
||||
int iReg;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( !v || sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
iReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "page_count", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "page_count", SQLITE_STATIC);
|
||||
}else
|
||||
|
||||
/*
|
||||
@@ -427,7 +443,7 @@ void sqlite3Pragma(
|
||||
pPager = sqlite3BtreePager(db->aDb[ii].pBt);
|
||||
sqlite3PagerLockingMode(pPager, eMode);
|
||||
}
|
||||
db->dfltLockMode = eMode;
|
||||
db->dfltLockMode = (u8)eMode;
|
||||
}
|
||||
pPager = sqlite3BtreePager(pDb->pBt);
|
||||
eMode = sqlite3PagerLockingMode(pPager, eMode);
|
||||
@@ -438,24 +454,26 @@ void sqlite3Pragma(
|
||||
zRet = "exclusive";
|
||||
}
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}else
|
||||
|
||||
/*
|
||||
** PRAGMA [database.]journal_mode
|
||||
** PRAGMA [database.]journal_mode = (delete|persist|off)
|
||||
** PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory)
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
|
||||
int eMode;
|
||||
static const char *azModeName[] = {"delete", "persist", "off"};
|
||||
static char * const azModeName[] = {
|
||||
"delete", "persist", "off", "truncate", "memory"
|
||||
};
|
||||
|
||||
if( zRight==0 ){
|
||||
eMode = PAGER_JOURNALMODE_QUERY;
|
||||
}else{
|
||||
int n = strlen(zRight);
|
||||
eMode = 2;
|
||||
int n = sqlite3Strlen30(zRight);
|
||||
eMode = sizeof(azModeName)/sizeof(azModeName[0]) - 1;
|
||||
while( eMode>=0 && sqlite3StrNICmp(zRight, azModeName[eMode], n)!=0 ){
|
||||
eMode--;
|
||||
}
|
||||
@@ -485,16 +503,18 @@ void sqlite3Pragma(
|
||||
sqlite3PagerJournalMode(pPager, eMode);
|
||||
}
|
||||
}
|
||||
db->dfltJournalMode = eMode;
|
||||
db->dfltJournalMode = (u8)eMode;
|
||||
}
|
||||
pPager = sqlite3BtreePager(pDb->pBt);
|
||||
eMode = sqlite3PagerJournalMode(pPager, eMode);
|
||||
}
|
||||
assert( eMode==PAGER_JOURNALMODE_DELETE
|
||||
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|
||||
|| eMode==PAGER_JOURNALMODE_PERSIST
|
||||
|| eMode==PAGER_JOURNALMODE_OFF );
|
||||
|| eMode==PAGER_JOURNALMODE_OFF
|
||||
|| eMode==PAGER_JOURNALMODE_MEMORY );
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0,
|
||||
azModeName[eMode], P4_STATIC);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
@@ -504,7 +524,7 @@ void sqlite3Pragma(
|
||||
** PRAGMA [database.]journal_size_limit
|
||||
** PRAGMA [database.]journal_size_limit=N
|
||||
**
|
||||
** Get or set the (boolean) value of the database 'auto-vacuum' parameter.
|
||||
** Get or set the size limit on rollback journal files.
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
|
||||
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
||||
@@ -526,22 +546,29 @@ void sqlite3Pragma(
|
||||
** PRAGMA [database.]auto_vacuum
|
||||
** PRAGMA [database.]auto_vacuum=N
|
||||
**
|
||||
** Get or set the (boolean) value of the database 'auto-vacuum' parameter.
|
||||
** Get or set the value of the database 'auto-vacuum' parameter.
|
||||
** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( sqlite3StrICmp(zLeft,"auto_vacuum")==0 ){
|
||||
Btree *pBt = pDb->pBt;
|
||||
assert( pBt!=0 );
|
||||
if( sqlite3ReadSchema(pParse) ){
|
||||
goto pragma_out;
|
||||
}
|
||||
if( !zRight ){
|
||||
int auto_vacuum =
|
||||
pBt ? sqlite3BtreeGetAutoVacuum(pBt) : SQLITE_DEFAULT_AUTOVACUUM;
|
||||
int auto_vacuum;
|
||||
if( ALWAYS(pBt) ){
|
||||
auto_vacuum = sqlite3BtreeGetAutoVacuum(pBt);
|
||||
}else{
|
||||
auto_vacuum = SQLITE_DEFAULT_AUTOVACUUM;
|
||||
}
|
||||
returnSingleInt(pParse, "auto_vacuum", auto_vacuum);
|
||||
}else{
|
||||
int eAuto = getAutoVacuum(zRight);
|
||||
db->nextAutovac = eAuto;
|
||||
if( eAuto>=0 ){
|
||||
assert( eAuto>=0 && eAuto<=2 );
|
||||
db->nextAutovac = (u8)eAuto;
|
||||
if( ALWAYS(eAuto>=0) ){
|
||||
/* Call SetAutoVacuum() to set initialize the internal auto and
|
||||
** incr-vacuum flags. This is required in case this connection
|
||||
** creates the database file. It is important that it is created
|
||||
@@ -661,15 +688,17 @@ void sqlite3Pragma(
|
||||
if( sqlite3_temp_directory ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
|
||||
"temp_store_directory", P4_STATIC);
|
||||
"temp_store_directory", SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_temp_directory, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
}else{
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
if( zRight[0] ){
|
||||
int rc;
|
||||
int res;
|
||||
sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
||||
if( res==0 ){
|
||||
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
||||
if( rc!=SQLITE_OK || res==0 ){
|
||||
sqlite3ErrorMsg(pParse, "not a writable directory");
|
||||
goto pragma_out;
|
||||
}
|
||||
@@ -686,9 +715,60 @@ void sqlite3Pragma(
|
||||
}else{
|
||||
sqlite3_temp_directory = 0;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_WSD */
|
||||
}
|
||||
}else
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
# if defined(__APPLE__)
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 1
|
||||
# else
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 0
|
||||
# endif
|
||||
#endif
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
/*
|
||||
** PRAGMA [database.]lock_proxy_file
|
||||
** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
|
||||
**
|
||||
** Return or set the value of the lock_proxy_file flag. Changing
|
||||
** the value sets a specific file to be used for database access locks.
|
||||
**
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){
|
||||
if( !zRight ){
|
||||
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
||||
char *proxy_file_path = NULL;
|
||||
sqlite3_file *pFile = sqlite3PagerFile(pPager);
|
||||
sqlite3OsFileControl(pFile, SQLITE_GET_LOCKPROXYFILE,
|
||||
&proxy_file_path);
|
||||
|
||||
if( proxy_file_path ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
|
||||
"lock_proxy_file", SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, proxy_file_path, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
}else{
|
||||
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
||||
sqlite3_file *pFile = sqlite3PagerFile(pPager);
|
||||
int res;
|
||||
if( zRight[0] ){
|
||||
res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
|
||||
zRight);
|
||||
} else {
|
||||
res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
|
||||
NULL);
|
||||
}
|
||||
if( res!=SQLITE_OK ){
|
||||
sqlite3ErrorMsg(pParse, "failed to set lock proxy file");
|
||||
goto pragma_out;
|
||||
}
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
|
||||
|
||||
/*
|
||||
** PRAGMA [database.]synchronous
|
||||
** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL
|
||||
@@ -743,12 +823,12 @@ void sqlite3Pragma(
|
||||
Column *pCol;
|
||||
sqlite3VdbeSetNumCols(v, 6);
|
||||
pParse->nMem = 6;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC);
|
||||
sqlite3ViewGetColumnNames(pParse, pTab);
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const Token *pDflt;
|
||||
@@ -760,8 +840,10 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pCol->zName, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
pCol->zType ? pCol->zType : "", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pCol->notNull, 4);
|
||||
if( pCol->pDflt && (pDflt = &pCol->pDflt->span)->z ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4);
|
||||
if( pCol->pDflt ){
|
||||
pDflt = &pCol->pDflt->span;
|
||||
assert( pDflt->z );
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pDflt->z, pDflt->n);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
|
||||
@@ -782,9 +864,9 @@ void sqlite3Pragma(
|
||||
pTab = pIdx->pTable;
|
||||
sqlite3VdbeSetNumCols(v, 3);
|
||||
pParse->nMem = 3;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC);
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int cnum = pIdx->aiColumn[i];
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
|
||||
@@ -808,9 +890,9 @@ void sqlite3Pragma(
|
||||
int i = 0;
|
||||
sqlite3VdbeSetNumCols(v, 3);
|
||||
pParse->nMem = 3;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC);
|
||||
while(pIdx){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
|
||||
@@ -828,9 +910,9 @@ void sqlite3Pragma(
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
sqlite3VdbeSetNumCols(v, 3);
|
||||
pParse->nMem = 3;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", SQLITE_STATIC);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 ) continue;
|
||||
assert( db->aDb[i].zName!=0 );
|
||||
@@ -847,8 +929,8 @@ void sqlite3Pragma(
|
||||
HashElem *p;
|
||||
sqlite3VdbeSetNumCols(v, 2);
|
||||
pParse->nMem = 2;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
|
||||
for(p=sqliteHashFirst(&db->aCollSeq); p; p=sqliteHashNext(p)){
|
||||
CollSeq *pColl = (CollSeq *)sqliteHashData(p);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i++, 1);
|
||||
@@ -869,24 +951,32 @@ void sqlite3Pragma(
|
||||
pFK = pTab->pFKey;
|
||||
if( pFK ){
|
||||
int i = 0;
|
||||
sqlite3VdbeSetNumCols(v, 5);
|
||||
pParse->nMem = 5;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", P4_STATIC);
|
||||
sqlite3VdbeSetNumCols(v, 8);
|
||||
pParse->nMem = 8;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "on_update", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 6, COLNAME_NAME, "on_delete", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 7, COLNAME_NAME, "match", SQLITE_STATIC);
|
||||
while(pFK){
|
||||
int j;
|
||||
for(j=0; j<pFK->nCol; j++){
|
||||
char *zCol = pFK->aCol[j].zCol;
|
||||
char *zOnUpdate = (char *)actionName(pFK->updateConf);
|
||||
char *zOnDelete = (char *)actionName(pFK->deleteConf);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, j, 2);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
|
||||
pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
|
||||
sqlite3VdbeAddOp4(v, zCol ? OP_String8 : OP_Null, 0, 5, 0, zCol, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 6, 0, zOnUpdate, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 7, 0, zOnDelete, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 8, 0, "NONE", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8);
|
||||
}
|
||||
++i;
|
||||
pFK = pFK->pNextFrom;
|
||||
@@ -948,7 +1038,7 @@ void sqlite3Pragma(
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
pParse->nMem = 6;
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC);
|
||||
|
||||
/* Set the maximum error count */
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
@@ -998,7 +1088,7 @@ void sqlite3Pragma(
|
||||
|
||||
/* Do the b-tree integrity checks */
|
||||
sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
|
||||
sqlite3VdbeChangeP5(v, i);
|
||||
sqlite3VdbeChangeP5(v, (u8)i);
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
|
||||
@@ -1113,11 +1203,11 @@ void sqlite3Pragma(
|
||||
char *zName;
|
||||
u8 enc;
|
||||
} encnames[] = {
|
||||
{ "UTF-8", SQLITE_UTF8 },
|
||||
{ "UTF8", SQLITE_UTF8 },
|
||||
{ "UTF-16le", SQLITE_UTF16LE },
|
||||
{ "UTF-8", SQLITE_UTF8 }, /* Must be element [1] */
|
||||
{ "UTF-16le", SQLITE_UTF16LE }, /* Must be element [2] */
|
||||
{ "UTF-16be", SQLITE_UTF16BE }, /* Must be element [3] */
|
||||
{ "UTF16le", SQLITE_UTF16LE },
|
||||
{ "UTF-16be", SQLITE_UTF16BE },
|
||||
{ "UTF16be", SQLITE_UTF16BE },
|
||||
{ "UTF-16", 0 }, /* SQLITE_UTF16NATIVE */
|
||||
{ "UTF16", 0 }, /* SQLITE_UTF16NATIVE */
|
||||
@@ -1127,14 +1217,12 @@ void sqlite3Pragma(
|
||||
if( !zRight ){ /* "PRAGMA encoding" */
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp2(v, OP_String8, 0, 1);
|
||||
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
|
||||
if( pEnc->enc==ENC(pParse->db) ){
|
||||
sqlite3VdbeChangeP4(v, -1, pEnc->zName, P4_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( encnames[SQLITE_UTF8].enc==SQLITE_UTF8 );
|
||||
assert( encnames[SQLITE_UTF16LE].enc==SQLITE_UTF16LE );
|
||||
assert( encnames[SQLITE_UTF16BE].enc==SQLITE_UTF16BE );
|
||||
sqlite3VdbeChangeP4(v, -1, encnames[ENC(pParse->db)].zName, P4_STATIC);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}else{ /* "PRAGMA encoding = XXX" */
|
||||
/* Only change the value of sqlite.enc if the database handle is not
|
||||
@@ -1190,7 +1278,6 @@ void sqlite3Pragma(
|
||||
|| sqlite3StrICmp(zLeft, "user_version")==0
|
||||
|| sqlite3StrICmp(zLeft, "freelist_count")==0
|
||||
){
|
||||
|
||||
int iCookie; /* Cookie index. 0 for schema-cookie, 6 for user-cookie. */
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
switch( zLeft[0] ){
|
||||
@@ -1229,7 +1316,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
sqlite3VdbeChangeP3(v, addr, iCookie);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, P4_TRANSIENT);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */
|
||||
@@ -1243,11 +1330,10 @@ void sqlite3Pragma(
|
||||
"unlocked", "shared", "reserved", "pending", "exclusive"
|
||||
};
|
||||
int i;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
sqlite3VdbeSetNumCols(v, 2);
|
||||
pParse->nMem = 2;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", P4_STATIC);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", SQLITE_STATIC);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt;
|
||||
Pager *pPager;
|
||||
@@ -1265,6 +1351,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
||||
}
|
||||
|
||||
}else
|
||||
#endif
|
||||
|
||||
@@ -1280,8 +1367,26 @@ void sqlite3Pragma(
|
||||
#endif
|
||||
|
||||
#if SQLITE_HAS_CODEC
|
||||
if( sqlite3StrICmp(zLeft, "key")==0 ){
|
||||
sqlite3_key(db, zRight, strlen(zRight));
|
||||
if( sqlite3StrICmp(zLeft, "key")==0 && zRight ){
|
||||
sqlite3_key(db, zRight, sqlite3Strlen30(zRight));
|
||||
}else
|
||||
if( sqlite3StrICmp(zLeft, "rekey")==0 && zRight ){
|
||||
sqlite3_rekey(db, zRight, sqlite3Strlen30(zRight));
|
||||
}else
|
||||
if( zRight && (sqlite3StrICmp(zLeft, "hexkey")==0 ||
|
||||
sqlite3StrICmp(zLeft, "hexrekey")==0) ){
|
||||
int i, h1, h2;
|
||||
char zKey[40];
|
||||
for(i=0; (h1 = zRight[i])!=0 && (h2 = zRight[i+1])!=0; i+=2){
|
||||
h1 += 9*(1&(h1>>6));
|
||||
h2 += 9*(1&(h2>>6));
|
||||
zKey[i/2] = (h2 & 0x0f) | ((h1 & 0xf)<<4);
|
||||
}
|
||||
if( (zLeft[3] & 0xf)==0xb ){
|
||||
sqlite3_key(db, zKey, i/2);
|
||||
}else{
|
||||
sqlite3_rekey(db, zKey, i/2);
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
#if SQLITE_HAS_CODEC || defined(SQLITE_ENABLE_CEROD)
|
||||
@@ -1298,29 +1403,28 @@ void sqlite3Pragma(
|
||||
sqlite3_activate_cerod(&zRight[6]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
|
||||
{}
|
||||
|
||||
{/* Empty ELSE clause */}
|
||||
|
||||
if( v ){
|
||||
/* Code an OP_Expire at the end of each PRAGMA program to cause
|
||||
** the VDBE implementing the pragma to expire. Most (all?) pragmas
|
||||
** are only valid for a single execution.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 1, 0);
|
||||
/* Code an OP_Expire at the end of each PRAGMA program to cause
|
||||
** the VDBE implementing the pragma to expire. Most (all?) pragmas
|
||||
** are only valid for a single execution.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 1, 0);
|
||||
|
||||
/*
|
||||
** Reset the safety level, in case the fullfsync flag or synchronous
|
||||
** setting changed.
|
||||
*/
|
||||
/*
|
||||
** Reset the safety level, in case the fullfsync flag or synchronous
|
||||
** setting changed.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
if( db->autoCommit ){
|
||||
sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
|
||||
(db->flags&SQLITE_FullFSync)!=0);
|
||||
}
|
||||
#endif
|
||||
if( db->autoCommit ){
|
||||
sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
|
||||
(db->flags&SQLITE_FullFSync)!=0);
|
||||
}
|
||||
#endif
|
||||
pragma_out:
|
||||
sqlite3DbFree(db, zLeft);
|
||||
sqlite3DbFree(db, zRight);
|
||||
|
||||
+32
-35
@@ -13,10 +13,9 @@
|
||||
** interface, and routines that contribute to loading the database schema
|
||||
** from disk.
|
||||
**
|
||||
** $Id: prepare.c,v 1.91 2008/08/02 03:50:39 drh Exp $
|
||||
** $Id: prepare.c,v 1.105 2009/01/20 16:53:41 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Fill the InitData structure with an error message that indicates
|
||||
@@ -27,7 +26,8 @@ static void corruptSchema(
|
||||
const char *zObj, /* Object being parsed at the point of error */
|
||||
const char *zExtra /* Error information */
|
||||
){
|
||||
if( !pData->db->mallocFailed ){
|
||||
sqlite3 *db = pData->db;
|
||||
if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){
|
||||
if( zObj==0 ) zObj = "?";
|
||||
sqlite3SetString(pData->pzErrMsg, pData->db,
|
||||
"malformed database schema (%s)", zObj);
|
||||
@@ -51,27 +51,25 @@ static void corruptSchema(
|
||||
** argv[2] = SQL text for the CREATE statement.
|
||||
**
|
||||
*/
|
||||
int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
|
||||
int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
InitData *pData = (InitData*)pInit;
|
||||
sqlite3 *db = pData->db;
|
||||
int iDb = pData->iDb;
|
||||
|
||||
assert( argc==3 );
|
||||
UNUSED_PARAMETER2(NotUsed, argc);
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
pData->rc = SQLITE_OK;
|
||||
DbClearProperty(db, iDb, DB_Empty);
|
||||
if( db->mallocFailed ){
|
||||
corruptSchema(pData, argv[0], 0);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
assert( argc==3 );
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
|
||||
if( argv[1]==0 ){
|
||||
corruptSchema(pData, argv[0], 0);
|
||||
return 1;
|
||||
}
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( argv[2] && argv[2][0] ){
|
||||
}else if( argv[2] && argv[2][0] ){
|
||||
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
|
||||
** But because db->init.busy is set to 1, no VDBE code is generated
|
||||
** or executed. All the parser does is build the internal data
|
||||
@@ -97,7 +95,6 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
|
||||
corruptSchema(pData, argv[0], zErr);
|
||||
}
|
||||
sqlite3DbFree(db, zErr);
|
||||
return 1;
|
||||
}
|
||||
}else if( argv[0]==0 ){
|
||||
corruptSchema(pData, 0, 0);
|
||||
@@ -193,17 +190,18 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
azArg[3] = 0;
|
||||
initData.db = db;
|
||||
initData.iDb = iDb;
|
||||
initData.rc = SQLITE_OK;
|
||||
initData.pzErrMsg = pzErrMsg;
|
||||
(void)sqlite3SafetyOff(db);
|
||||
rc = sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
|
||||
sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
|
||||
(void)sqlite3SafetyOn(db);
|
||||
if( rc ){
|
||||
if( initData.rc ){
|
||||
rc = initData.rc;
|
||||
goto error_out;
|
||||
}
|
||||
pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
|
||||
if( pTab ){
|
||||
pTab->readOnly = 1;
|
||||
pTab->tabFlags |= TF_Readonly;
|
||||
}
|
||||
|
||||
/* Create a cursor to hold the database open
|
||||
@@ -246,7 +244,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
for(i=0; i<sizeof(meta)/sizeof(meta[0]); i++){
|
||||
for(i=0; i<ArraySize(meta); i++){
|
||||
rc = sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
|
||||
@@ -296,7 +294,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
** file_format==3 Version 3.1.4. // ditto but with non-NULL defaults
|
||||
** file_format==4 Version 3.3.0. // DESC indices. Boolean constants
|
||||
*/
|
||||
pDb->pSchema->file_format = meta[1];
|
||||
pDb->pSchema->file_format = (u8)meta[1];
|
||||
if( pDb->pSchema->file_format==0 ){
|
||||
pDb->pSchema->file_format = 1;
|
||||
}
|
||||
@@ -338,7 +336,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
db->xAuth = xAuth;
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_ABORT ) rc = initData.rc;
|
||||
if( rc==SQLITE_OK ) rc = initData.rc;
|
||||
(void)sqlite3SafetyOn(db);
|
||||
sqlite3DbFree(db, zSql);
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
@@ -502,18 +500,18 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
** function should never be used.
|
||||
**
|
||||
** We return -1000000 instead of the more usual -1 simply because using
|
||||
** -1000000 as incorrectly using -1000000 index into db->aDb[] is much
|
||||
** -1000000 as the incorrect index into db->aDb[] is much
|
||||
** more likely to cause a segfault than -1 (of course there are assert()
|
||||
** statements too, but it never hurts to play the odds).
|
||||
*/
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( pSchema ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
for(i=0; ALWAYS(i<db->nDb); i++){
|
||||
if( db->aDb[i].pSchema==pSchema ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( i>=0 &&i>=0 && i<db->nDb );
|
||||
assert( i>=0 && i<db->nDb );
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -548,7 +546,6 @@ static int sqlite3Prepare(
|
||||
for(i=0; i<db->nDb; i++) {
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
int rc;
|
||||
rc = sqlite3BtreeSchemaLocked(pBt);
|
||||
if( rc ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
@@ -603,19 +600,19 @@ static int sqlite3Prepare(
|
||||
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
|
||||
if( sParse.explain==2 ){
|
||||
sqlite3VdbeSetNumCols(sParse.pVdbe, 3);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "order", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "from", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "detail", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "order", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "from", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "detail", SQLITE_STATIC);
|
||||
}else{
|
||||
sqlite3VdbeSetNumCols(sParse.pVdbe, 8);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "addr", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "opcode", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "p1", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 5, COLNAME_NAME, "p4", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 6, COLNAME_NAME, "p5", P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 7, COLNAME_NAME, "comment",P4_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "addr", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "opcode", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "p1", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 5, COLNAME_NAME, "p4", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 6, COLNAME_NAME, "p5", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 7, COLNAME_NAME, "comment", SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -625,7 +622,7 @@ static int sqlite3Prepare(
|
||||
}
|
||||
|
||||
if( saveSqlFlag ){
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, sParse.zTail - zSql);
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail - zSql));
|
||||
}
|
||||
if( rc!=SQLITE_OK || db->mallocFailed ){
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
@@ -692,7 +689,7 @@ int sqlite3Reprepare(Vdbe *p){
|
||||
assert( pNew!=0 );
|
||||
}
|
||||
sqlite3VdbeSwap((Vdbe*)pNew, p);
|
||||
sqlite3_transfer_bindings(pNew, (sqlite3_stmt*)p);
|
||||
sqlite3TransferBindings(pNew, (sqlite3_stmt*)p);
|
||||
sqlite3VdbeResetStepResult((Vdbe*)pNew);
|
||||
sqlite3VdbeFinalize((Vdbe*)pNew);
|
||||
return 1;
|
||||
@@ -768,7 +765,7 @@ static int sqlite3Prepare16(
|
||||
** characters between zSql8 and zTail8, and then returning a pointer
|
||||
** the same number of characters into the UTF-16 string.
|
||||
*/
|
||||
int chars_parsed = sqlite3Utf8CharLen(zSql8, zTail8-zSql8);
|
||||
int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8));
|
||||
*pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed);
|
||||
}
|
||||
sqlite3DbFree(db, zSql8);
|
||||
|
||||
+29
-27
@@ -5,7 +5,7 @@
|
||||
** an historical reference. Most of the "enhancements" have been backed
|
||||
** out so that the functionality is now the same as standard printf().
|
||||
**
|
||||
** $Id: printf.c,v 1.93 2008/07/28 19:34:53 drh Exp $
|
||||
** $Id: printf.c,v 1.99 2008/12/10 19:26:24 drh Exp $
|
||||
**
|
||||
**************************************************************************
|
||||
**
|
||||
@@ -137,7 +137,6 @@ static const et_info fmtinfo[] = {
|
||||
{ 'S', 0, 2, etSRCLIST, 0, 0 },
|
||||
{ 'r', 10, 3, etORDINAL, 0, 0 },
|
||||
};
|
||||
#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
|
||||
|
||||
/*
|
||||
** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point
|
||||
@@ -157,7 +156,7 @@ static const et_info fmtinfo[] = {
|
||||
** 16 (the number of significant digits in a 64-bit float) '0' is
|
||||
** always returned.
|
||||
*/
|
||||
static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
int digit;
|
||||
LONGDOUBLE_TYPE d;
|
||||
if( (*cnt)++ >= 16 ) return '0';
|
||||
@@ -165,7 +164,7 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
d = digit;
|
||||
digit += '0';
|
||||
*val = (*val - d)*10.0;
|
||||
return digit;
|
||||
return (char)digit;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
|
||||
@@ -174,7 +173,7 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
*/
|
||||
static void appendSpace(StrAccum *pAccum, int N){
|
||||
static const char zSpaces[] = " ";
|
||||
while( N>=sizeof(zSpaces)-1 ){
|
||||
while( N>=(int)sizeof(zSpaces)-1 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
||||
N -= sizeof(zSpaces)-1;
|
||||
}
|
||||
@@ -246,8 +245,7 @@ void sqlite3VXPrintf(
|
||||
const et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
etByte errorflag = 0; /* True if an error is encountered */
|
||||
etByte xtype; /* Conversion paradigm */
|
||||
etByte xtype = 0; /* Conversion paradigm */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
@@ -270,7 +268,6 @@ void sqlite3VXPrintf(
|
||||
if( c==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
errorflag = 1;
|
||||
sqlite3StrAccumAppend(pAccum, "%", 1);
|
||||
break;
|
||||
}
|
||||
@@ -339,7 +336,7 @@ void sqlite3VXPrintf(
|
||||
}
|
||||
/* Fetch the info entry for the field */
|
||||
infop = 0;
|
||||
for(idx=0; idx<etNINFO; idx++){
|
||||
for(idx=0; idx<ArraySize(fmtinfo); idx++){
|
||||
if( c==fmtinfo[idx].fmttype ){
|
||||
infop = &fmtinfo[idx];
|
||||
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
||||
@@ -416,7 +413,7 @@ void sqlite3VXPrintf(
|
||||
bufpt = &buf[etBUFSIZE-1];
|
||||
if( xtype==etORDINAL ){
|
||||
static const char zOrd[] = "thstndrd";
|
||||
int x = longvalue % 10;
|
||||
int x = (int)(longvalue % 10);
|
||||
if( x>=4 || (longvalue/10)%10==1 ){
|
||||
x = 0;
|
||||
}
|
||||
@@ -434,7 +431,7 @@ void sqlite3VXPrintf(
|
||||
longvalue = longvalue/base;
|
||||
}while( longvalue>0 );
|
||||
}
|
||||
length = &buf[etBUFSIZE-1]-bufpt;
|
||||
length = (int)(&buf[etBUFSIZE-1]-bufpt);
|
||||
for(idx=precision-length; idx>0; idx--){
|
||||
*(--bufpt) = '0'; /* Zero pad */
|
||||
}
|
||||
@@ -445,7 +442,7 @@ void sqlite3VXPrintf(
|
||||
pre = &aPrefix[infop->prefix];
|
||||
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
|
||||
}
|
||||
length = &buf[etBUFSIZE-1]-bufpt;
|
||||
length = (int)(&buf[etBUFSIZE-1]-bufpt);
|
||||
break;
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
@@ -473,7 +470,7 @@ void sqlite3VXPrintf(
|
||||
if( xtype==etFLOAT ) realvalue += rounder;
|
||||
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
||||
exp = 0;
|
||||
if( sqlite3IsNaN(realvalue) ){
|
||||
if( sqlite3IsNaN((double)realvalue) ){
|
||||
bufpt = "NaN";
|
||||
length = 3;
|
||||
break;
|
||||
@@ -492,7 +489,7 @@ void sqlite3VXPrintf(
|
||||
}else{
|
||||
bufpt = "Inf";
|
||||
}
|
||||
length = strlen(bufpt);
|
||||
length = sqlite3Strlen30(bufpt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -523,7 +520,7 @@ void sqlite3VXPrintf(
|
||||
e2 = exp;
|
||||
}
|
||||
nsd = 0;
|
||||
flag_dp = (precision>0) | flag_alternateform | flag_altform2;
|
||||
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
|
||||
/* The sign in front of the number */
|
||||
if( prefix ){
|
||||
*(bufpt++) = prefix;
|
||||
@@ -571,18 +568,18 @@ void sqlite3VXPrintf(
|
||||
*(bufpt++) = '+';
|
||||
}
|
||||
if( exp>=100 ){
|
||||
*(bufpt++) = (exp/100)+'0'; /* 100's digit */
|
||||
*(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */
|
||||
exp %= 100;
|
||||
}
|
||||
*(bufpt++) = exp/10+'0'; /* 10's digit */
|
||||
*(bufpt++) = exp%10+'0'; /* 1's digit */
|
||||
*(bufpt++) = (char)(exp/10+'0'); /* 10's digit */
|
||||
*(bufpt++) = (char)(exp%10+'0'); /* 1's digit */
|
||||
}
|
||||
*bufpt = 0;
|
||||
|
||||
/* The converted number is in buf[] and zero terminated. Output it.
|
||||
** Note that the number is in the usual order, not reversed as with
|
||||
** integer conversions. */
|
||||
length = bufpt-buf;
|
||||
length = (int)(bufpt-buf);
|
||||
bufpt = buf;
|
||||
|
||||
/* Special case: Add leading zeros if the flag_zeropad flag is
|
||||
@@ -609,9 +606,10 @@ void sqlite3VXPrintf(
|
||||
length = 1;
|
||||
break;
|
||||
case etCHARX:
|
||||
c = buf[0] = va_arg(ap,int);
|
||||
c = va_arg(ap,int);
|
||||
buf[0] = (char)c;
|
||||
if( precision>=0 ){
|
||||
for(idx=1; idx<precision; idx++) buf[idx] = c;
|
||||
for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
|
||||
length = precision;
|
||||
}else{
|
||||
length =1;
|
||||
@@ -629,14 +627,15 @@ void sqlite3VXPrintf(
|
||||
if( precision>=0 ){
|
||||
for(length=0; length<precision && bufpt[length]; length++){}
|
||||
}else{
|
||||
length = strlen(bufpt);
|
||||
length = sqlite3Strlen30(bufpt);
|
||||
}
|
||||
break;
|
||||
case etSQLESCAPE:
|
||||
case etSQLESCAPE2:
|
||||
case etSQLESCAPE3: {
|
||||
int i, j, n, ch, isnull;
|
||||
int i, j, n, isnull;
|
||||
int needQuote;
|
||||
char ch;
|
||||
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
|
||||
char *escarg = va_arg(ap,char*);
|
||||
isnull = escarg==0;
|
||||
@@ -648,7 +647,10 @@ void sqlite3VXPrintf(
|
||||
n += i + 1 + needQuote*2;
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqlite3Malloc( n );
|
||||
if( bufpt==0 ) return;
|
||||
if( bufpt==0 ){
|
||||
pAccum->mallocFailed = 1;
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
bufpt = buf;
|
||||
}
|
||||
@@ -723,9 +725,9 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
return;
|
||||
}
|
||||
if( N<0 ){
|
||||
N = strlen(z);
|
||||
N = sqlite3Strlen30(z);
|
||||
}
|
||||
if( N==0 ){
|
||||
if( N==0 || z==0 ){
|
||||
return;
|
||||
}
|
||||
if( p->nChar+N >= p->nAlloc ){
|
||||
@@ -744,7 +746,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
p->tooBig = 1;
|
||||
return;
|
||||
}else{
|
||||
p->nAlloc = szNew;
|
||||
p->nAlloc = (int)szNew;
|
||||
}
|
||||
zNew = sqlite3DbMallocRaw(p->db, p->nAlloc );
|
||||
if( zNew ){
|
||||
|
||||
+50
-25
@@ -15,7 +15,7 @@
|
||||
** Random numbers are used by some of the database backends in order
|
||||
** to generate random integer keys for tables or random filenames.
|
||||
**
|
||||
** $Id: random.c,v 1.25 2008/06/19 01:03:18 drh Exp $
|
||||
** $Id: random.c,v 1.29 2008/12/10 19:26:24 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
/* All threads share a single random number generator.
|
||||
** This structure is the current state of the generator.
|
||||
*/
|
||||
static struct sqlite3PrngType {
|
||||
static SQLITE_WSD struct sqlite3PrngType {
|
||||
unsigned char isInit; /* True if initialized */
|
||||
unsigned char i, j; /* State variables */
|
||||
unsigned char s[256]; /* State variables */
|
||||
@@ -45,10 +45,24 @@ static struct sqlite3PrngType {
|
||||
** (Later): Actually, OP_NewRowid does not depend on a good source of
|
||||
** randomness any more. But we will leave this code in all the same.
|
||||
*/
|
||||
static int randomByte(void){
|
||||
static u8 randomByte(void){
|
||||
unsigned char t;
|
||||
|
||||
|
||||
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
|
||||
** state vector. If writable static data is unsupported on the target,
|
||||
** we have to locate the state vector at run-time. In the more common
|
||||
** case where writable static data is supported, wsdPrng can refer directly
|
||||
** to the "sqlite3Prng" state vector declared above.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
struct sqlite3PrngType *p = &GLOBAL(struct sqlite3PrngType, sqlite3Prng);
|
||||
# define wsdPrng p[0]
|
||||
#else
|
||||
# define wsdPrng sqlite3Prng
|
||||
#endif
|
||||
|
||||
|
||||
/* Initialize the state of the random number generator once,
|
||||
** the first time this routine is called. The seed value does
|
||||
** not need to contain a lot of randomness since we are not
|
||||
@@ -58,33 +72,33 @@ static int randomByte(void){
|
||||
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
|
||||
** number generator) not as an encryption device.
|
||||
*/
|
||||
if( !sqlite3Prng.isInit ){
|
||||
if( !wsdPrng.isInit ){
|
||||
int i;
|
||||
char k[256];
|
||||
sqlite3Prng.j = 0;
|
||||
sqlite3Prng.i = 0;
|
||||
wsdPrng.j = 0;
|
||||
wsdPrng.i = 0;
|
||||
sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
|
||||
for(i=0; i<256; i++){
|
||||
sqlite3Prng.s[i] = i;
|
||||
wsdPrng.s[i] = (u8)i;
|
||||
}
|
||||
for(i=0; i<256; i++){
|
||||
sqlite3Prng.j += sqlite3Prng.s[i] + k[i];
|
||||
t = sqlite3Prng.s[sqlite3Prng.j];
|
||||
sqlite3Prng.s[sqlite3Prng.j] = sqlite3Prng.s[i];
|
||||
sqlite3Prng.s[i] = t;
|
||||
wsdPrng.j += wsdPrng.s[i] + k[i];
|
||||
t = wsdPrng.s[wsdPrng.j];
|
||||
wsdPrng.s[wsdPrng.j] = wsdPrng.s[i];
|
||||
wsdPrng.s[i] = t;
|
||||
}
|
||||
sqlite3Prng.isInit = 1;
|
||||
wsdPrng.isInit = 1;
|
||||
}
|
||||
|
||||
/* Generate and return single random byte
|
||||
*/
|
||||
sqlite3Prng.i++;
|
||||
t = sqlite3Prng.s[sqlite3Prng.i];
|
||||
sqlite3Prng.j += t;
|
||||
sqlite3Prng.s[sqlite3Prng.i] = sqlite3Prng.s[sqlite3Prng.j];
|
||||
sqlite3Prng.s[sqlite3Prng.j] = t;
|
||||
t += sqlite3Prng.s[sqlite3Prng.i];
|
||||
return sqlite3Prng.s[t];
|
||||
wsdPrng.i++;
|
||||
t = wsdPrng.s[wsdPrng.i];
|
||||
wsdPrng.j += t;
|
||||
wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
|
||||
wsdPrng.s[wsdPrng.j] = t;
|
||||
t += wsdPrng.s[wsdPrng.i];
|
||||
return wsdPrng.s[t];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -92,7 +106,7 @@ static int randomByte(void){
|
||||
*/
|
||||
void sqlite3_randomness(int N, void *pBuf){
|
||||
unsigned char *zBuf = pBuf;
|
||||
#ifndef SQLITE_MUTEX_NOOP
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
@@ -105,18 +119,29 @@ void sqlite3_randomness(int N, void *pBuf){
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
/*
|
||||
** For testing purposes, we sometimes want to preserve the state of
|
||||
** PRNG and restore the PRNG to its saved state at a later time.
|
||||
** PRNG and restore the PRNG to its saved state at a later time, or
|
||||
** to reset the PRNG to its initial state. These routines accomplish
|
||||
** those tasks.
|
||||
**
|
||||
** The sqlite3_test_control() interface calls these routines to
|
||||
** control the PRNG.
|
||||
*/
|
||||
static struct sqlite3PrngType sqlite3SavedPrng;
|
||||
static SQLITE_WSD struct sqlite3PrngType sqlite3SavedPrng;
|
||||
void sqlite3PrngSaveState(void){
|
||||
memcpy(&sqlite3SavedPrng, &sqlite3Prng, sizeof(sqlite3Prng));
|
||||
memcpy(
|
||||
&GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
||||
&GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
||||
sizeof(sqlite3Prng)
|
||||
);
|
||||
}
|
||||
void sqlite3PrngRestoreState(void){
|
||||
memcpy(&sqlite3Prng, &sqlite3SavedPrng, sizeof(sqlite3Prng));
|
||||
memcpy(
|
||||
&GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
||||
&GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
||||
sizeof(sqlite3Prng)
|
||||
);
|
||||
}
|
||||
void sqlite3PrngResetState(void){
|
||||
sqlite3Prng.isInit = 0;
|
||||
GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
+1169
File diff suppressed because it is too large
Load Diff
+238
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
** 2008 December 3
|
||||
**
|
||||
** 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 module implements an object we call a "Row Set".
|
||||
**
|
||||
** The RowSet object is a bag of rowids. Rowids
|
||||
** are inserted into the bag in an arbitrary order. Then they are
|
||||
** pulled from the bag in sorted order. Rowids only appear in the
|
||||
** bag once. If the same rowid is inserted multiple times, the
|
||||
** second and subsequent inserts make no difference on the output.
|
||||
**
|
||||
** This implementation accumulates rowids in a linked list. For
|
||||
** output, it first sorts the linked list (removing duplicates during
|
||||
** the sort) then returns elements one by one by walking the list.
|
||||
**
|
||||
** Big chunks of rowid/next-ptr pairs are allocated at a time, to
|
||||
** reduce the malloc overhead.
|
||||
**
|
||||
** $Id: rowset.c,v 1.3 2009/01/13 20:14:16 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** The number of rowset entries per allocation chunk.
|
||||
*/
|
||||
#define ROWSET_ENTRY_PER_CHUNK 63
|
||||
|
||||
/*
|
||||
** Each entry in a RowSet is an instance of the following
|
||||
** structure:
|
||||
*/
|
||||
struct RowSetEntry {
|
||||
i64 v; /* ROWID value for this entry */
|
||||
struct RowSetEntry *pNext; /* Next entry on a list of all entries */
|
||||
};
|
||||
|
||||
/*
|
||||
** Index entries are allocated in large chunks (instances of the
|
||||
** following structure) to reduce memory allocation overhead. The
|
||||
** chunks are kept on a linked list so that they can be deallocated
|
||||
** when the RowSet is destroyed.
|
||||
*/
|
||||
struct RowSetChunk {
|
||||
struct RowSetChunk *pNext; /* Next chunk on list of them all */
|
||||
struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */
|
||||
};
|
||||
|
||||
/*
|
||||
** A RowSet in an instance of the following structure.
|
||||
**
|
||||
** A typedef of this structure if found in sqliteInt.h.
|
||||
*/
|
||||
struct RowSet {
|
||||
struct RowSetChunk *pChunk; /* List of all chunk allocations */
|
||||
sqlite3 *db; /* The database connection */
|
||||
struct RowSetEntry *pEntry; /* List of entries in the rowset */
|
||||
struct RowSetEntry *pLast; /* Last entry on the pEntry list */
|
||||
struct RowSetEntry *pFresh; /* Source of new entry objects */
|
||||
u16 nFresh; /* Number of objects on pFresh */
|
||||
u8 isSorted; /* True if content is sorted */
|
||||
};
|
||||
|
||||
/*
|
||||
** Turn bulk memory into a RowSet object. N bytes of memory
|
||||
** are available at pSpace. The db pointer is used as a memory context
|
||||
** for any subsequent allocations that need to occur.
|
||||
** Return a pointer to the new RowSet object.
|
||||
**
|
||||
** It must be the case that N is sufficient to make a Rowset. If not
|
||||
** an assertion fault occurs.
|
||||
**
|
||||
** If N is larger than the minimum, use the surplus as an initial
|
||||
** allocation of entries available to be filled.
|
||||
*/
|
||||
RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
|
||||
RowSet *p;
|
||||
assert( N >= sizeof(*p) );
|
||||
p = pSpace;
|
||||
p->pChunk = 0;
|
||||
p->db = db;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pFresh = (struct RowSetEntry*)&p[1];
|
||||
p->nFresh = (u16)((N - sizeof(*p))/sizeof(struct RowSetEntry));
|
||||
p->isSorted = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate all chunks from a RowSet.
|
||||
*/
|
||||
void sqlite3RowSetClear(RowSet *p){
|
||||
struct RowSetChunk *pChunk, *pNextChunk;
|
||||
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
|
||||
pNextChunk = pChunk->pNext;
|
||||
sqlite3DbFree(p->db, pChunk);
|
||||
}
|
||||
p->pChunk = 0;
|
||||
p->nFresh = 0;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->isSorted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a new value into a RowSet.
|
||||
**
|
||||
** The mallocFailed flag of the database connection is set if a
|
||||
** memory allocation fails.
|
||||
*/
|
||||
void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
struct RowSetEntry *pEntry;
|
||||
struct RowSetEntry *pLast;
|
||||
if( p==0 ) return; /* Must have been a malloc failure */
|
||||
if( p->nFresh==0 ){
|
||||
struct RowSetChunk *pNew;
|
||||
pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
return;
|
||||
}
|
||||
pNew->pNext = p->pChunk;
|
||||
p->pChunk = pNew;
|
||||
p->pFresh = pNew->aEntry;
|
||||
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
|
||||
}
|
||||
pEntry = p->pFresh++;
|
||||
p->nFresh--;
|
||||
pEntry->v = rowid;
|
||||
pEntry->pNext = 0;
|
||||
pLast = p->pLast;
|
||||
if( pLast ){
|
||||
if( p->isSorted && rowid<=pLast->v ){
|
||||
p->isSorted = 0;
|
||||
}
|
||||
pLast->pNext = pEntry;
|
||||
}else{
|
||||
assert( p->pEntry==0 );
|
||||
p->pEntry = pEntry;
|
||||
}
|
||||
p->pLast = pEntry;
|
||||
}
|
||||
|
||||
/*
|
||||
** Merge two lists of RowSet entries. Remove duplicates.
|
||||
**
|
||||
** The input lists are assumed to be in sorted order.
|
||||
*/
|
||||
static struct RowSetEntry *boolidxMerge(
|
||||
struct RowSetEntry *pA, /* First sorted list to be merged */
|
||||
struct RowSetEntry *pB /* Second sorted list to be merged */
|
||||
){
|
||||
struct RowSetEntry head;
|
||||
struct RowSetEntry *pTail;
|
||||
|
||||
pTail = &head;
|
||||
while( pA && pB ){
|
||||
assert( pA->pNext==0 || pA->v<=pA->pNext->v );
|
||||
assert( pB->pNext==0 || pB->v<=pB->pNext->v );
|
||||
if( pA->v<pB->v ){
|
||||
pTail->pNext = pA;
|
||||
pA = pA->pNext;
|
||||
pTail = pTail->pNext;
|
||||
}else if( pB->v<pA->v ){
|
||||
pTail->pNext = pB;
|
||||
pB = pB->pNext;
|
||||
pTail = pTail->pNext;
|
||||
}else{
|
||||
pA = pA->pNext;
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
assert( pA->pNext==0 || pA->v<=pA->pNext->v );
|
||||
pTail->pNext = pA;
|
||||
}else{
|
||||
assert( pB==0 || pB->pNext==0 || pB->v<=pB->pNext->v );
|
||||
pTail->pNext = pB;
|
||||
}
|
||||
return head.pNext;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort all elements of the RowSet into ascending order.
|
||||
*/
|
||||
static void sqlite3RowSetSort(RowSet *p){
|
||||
unsigned int i;
|
||||
struct RowSetEntry *pEntry;
|
||||
struct RowSetEntry *aBucket[40];
|
||||
|
||||
assert( p->isSorted==0 );
|
||||
memset(aBucket, 0, sizeof(aBucket));
|
||||
while( p->pEntry ){
|
||||
pEntry = p->pEntry;
|
||||
p->pEntry = pEntry->pNext;
|
||||
pEntry->pNext = 0;
|
||||
for(i=0; aBucket[i]; i++){
|
||||
pEntry = boolidxMerge(aBucket[i],pEntry);
|
||||
aBucket[i] = 0;
|
||||
}
|
||||
aBucket[i] = pEntry;
|
||||
}
|
||||
pEntry = 0;
|
||||
for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
|
||||
pEntry = boolidxMerge(pEntry,aBucket[i]);
|
||||
}
|
||||
p->pEntry = pEntry;
|
||||
p->pLast = 0;
|
||||
p->isSorted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the next (smallest) element from the RowSet.
|
||||
** Write the element into *pRowid. Return 1 on success. Return
|
||||
** 0 if the RowSet is already empty.
|
||||
*/
|
||||
int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
|
||||
if( !p->isSorted ){
|
||||
sqlite3RowSetSort(p);
|
||||
}
|
||||
if( p->pEntry ){
|
||||
*pRowid = p->pEntry->v;
|
||||
p->pEntry = p->pEntry->pNext;
|
||||
if( p->pEntry==0 ){
|
||||
sqlite3RowSetClear(p);
|
||||
}
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+886
-994
File diff suppressed because it is too large
Load Diff
+182
-43
@@ -12,8 +12,13 @@
|
||||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
**
|
||||
** $Id: shell.c,v 1.184 2008/07/11 17:23:25 drh Exp $
|
||||
** $Id: shell.c,v 1.201 2009/02/04 22:46:47 drh Exp $
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
/* This needs to come before any includes for MSVC compiler */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -24,7 +29,9 @@
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
|
||||
# include <signal.h>
|
||||
# include <pwd.h>
|
||||
# if !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
# include <pwd.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
@@ -46,6 +53,8 @@
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
# include <io.h>
|
||||
#define isatty(h) _isatty(h)
|
||||
#define access(f,m) _access((f),(m))
|
||||
#else
|
||||
/* Make sure isatty() has a prototype.
|
||||
*/
|
||||
@@ -60,7 +69,7 @@ extern int isatty();
|
||||
#define isatty(x) 1
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
@@ -106,6 +115,10 @@ static void endTimer(void){
|
||||
#define HAS_TIMER 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Used to prevent warnings about unused parameters
|
||||
*/
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
|
||||
/*
|
||||
** If the following flag is set, then command execution stops
|
||||
@@ -214,6 +227,8 @@ static void shellstaticFunc(
|
||||
){
|
||||
assert( 0==argc );
|
||||
assert( zShellStatic );
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
|
||||
}
|
||||
|
||||
@@ -353,7 +368,17 @@ static const char *modeDescr[] = {
|
||||
/*
|
||||
** Number of elements in an array
|
||||
*/
|
||||
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
|
||||
#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
|
||||
|
||||
/*
|
||||
** Compute a string length that is limited to what can be stored in
|
||||
** lower 30 bits of a 32-bit signed integer.
|
||||
*/
|
||||
static int strlen30(const char *z){
|
||||
const char *z2 = z;
|
||||
while( *z2 ){ z2++; }
|
||||
return 0x3fffffff & (int)(z2 - z);
|
||||
}
|
||||
|
||||
/*
|
||||
** Output the given string as a quoted string using SQL quoting conventions.
|
||||
@@ -470,7 +495,7 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
|
||||
fprintf(out,"%s",p->nullvalue);
|
||||
}else{
|
||||
int i;
|
||||
int nSep = strlen(p->separator);
|
||||
int nSep = strlen30(p->separator);
|
||||
for(i=0; z[i]; i++){
|
||||
if( needCsvQuote[((unsigned char*)z)[i]]
|
||||
|| (z[i]==p->separator[0] &&
|
||||
@@ -500,6 +525,7 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
|
||||
** This routine runs when the user presses Ctrl-C
|
||||
*/
|
||||
static void interrupt_handler(int NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
seenInterrupt = 1;
|
||||
if( db ) sqlite3_interrupt(db);
|
||||
}
|
||||
@@ -517,7 +543,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
int w = 5;
|
||||
if( azArg==0 ) break;
|
||||
for(i=0; i<nArg; i++){
|
||||
int len = strlen(azCol[i] ? azCol[i] : "");
|
||||
int len = strlen30(azCol[i] ? azCol[i] : "");
|
||||
if( len>w ) w = len;
|
||||
}
|
||||
if( p->cnt++>0 ) fprintf(p->out,"\n");
|
||||
@@ -538,9 +564,9 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
w = 0;
|
||||
}
|
||||
if( w<=0 ){
|
||||
w = strlen(azCol[i] ? azCol[i] : "");
|
||||
w = strlen30(azCol[i] ? azCol[i] : "");
|
||||
if( w<10 ) w = 10;
|
||||
n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue);
|
||||
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue);
|
||||
if( w<n ) w = n;
|
||||
}
|
||||
if( i<ArraySize(p->actualWidth) ){
|
||||
@@ -572,8 +598,9 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
}else{
|
||||
w = 10;
|
||||
}
|
||||
if( p->mode==MODE_Explain && azArg[i] && strlen(azArg[i])>w ){
|
||||
w = strlen(azArg[i]);
|
||||
if( p->mode==MODE_Explain && azArg[i] &&
|
||||
strlen30(azArg[i])>w ){
|
||||
w = strlen30(azArg[i]);
|
||||
}
|
||||
fprintf(p->out,"%-*.*s%s",w,w,
|
||||
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
|
||||
@@ -720,8 +747,8 @@ static void set_table_name(struct callback_data *p, const char *zName){
|
||||
static char *appendText(char *zIn, char const *zAppend, char quote){
|
||||
int len;
|
||||
int i;
|
||||
int nAppend = strlen(zAppend);
|
||||
int nIn = (zIn?strlen(zIn):0);
|
||||
int nAppend = strlen30(zAppend);
|
||||
int nIn = (zIn?strlen30(zIn):0);
|
||||
|
||||
len = nAppend+nIn+1;
|
||||
if( quote ){
|
||||
@@ -791,6 +818,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
const char *zSql;
|
||||
struct callback_data *p = (struct callback_data *)pArg;
|
||||
|
||||
UNUSED_PARAMETER(azCol);
|
||||
if( nArg!=3 ) return 1;
|
||||
zTable = azArg[0];
|
||||
zType = azArg[1];
|
||||
@@ -887,7 +915,7 @@ static int run_schema_dump_query(
|
||||
rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg);
|
||||
if( rc==SQLITE_CORRUPT ){
|
||||
char *zQ2;
|
||||
int len = strlen(zQuery);
|
||||
int len = strlen30(zQuery);
|
||||
if( pzErrMsg ) sqlite3_free(*pzErrMsg);
|
||||
zQ2 = malloc( len+100 );
|
||||
if( zQ2==0 ) return rc;
|
||||
@@ -902,6 +930,7 @@ static int run_schema_dump_query(
|
||||
** Text of a help message
|
||||
*/
|
||||
static char zHelp[] =
|
||||
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
|
||||
".bail ON|OFF Stop after hitting an error. Default OFF\n"
|
||||
".databases List names and files of attached databases\n"
|
||||
".dump ?TABLE? ... Dump the database in an SQL text format\n"
|
||||
@@ -933,6 +962,7 @@ static char zHelp[] =
|
||||
".prompt MAIN CONTINUE Replace the standard prompts\n"
|
||||
".quit Exit this program\n"
|
||||
".read FILENAME Execute SQL in FILENAME\n"
|
||||
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
|
||||
".schema ?TABLE? Show the CREATE statements\n"
|
||||
".separator STRING Change separator used by output mode and .import\n"
|
||||
".show Show the current values for various settings\n"
|
||||
@@ -980,7 +1010,8 @@ static void open_db(struct callback_data *p){
|
||||
** \\ -> backslash
|
||||
*/
|
||||
static void resolve_backslashes(char *z){
|
||||
int i, j, c;
|
||||
int i, j;
|
||||
char c;
|
||||
for(i=j=0; (c = z[i])!=0; i++, j++){
|
||||
if( c=='\\' ){
|
||||
c = z[++i];
|
||||
@@ -1014,7 +1045,7 @@ static int booleanValue(char *zArg){
|
||||
int val = atoi(zArg);
|
||||
int j;
|
||||
for(j=0; zArg[j]; j++){
|
||||
zArg[j] = tolower(zArg[j]);
|
||||
zArg[j] = (char)tolower(zArg[j]);
|
||||
}
|
||||
if( strcmp(zArg,"on")==0 ){
|
||||
val = 1;
|
||||
@@ -1061,9 +1092,45 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
/* Process the input line.
|
||||
*/
|
||||
if( nArg==0 ) return rc;
|
||||
n = strlen(azArg[0]);
|
||||
n = strlen30(azArg[0]);
|
||||
c = azArg[0][0];
|
||||
if( c=='b' && n>1 && strncmp(azArg[0], "bail", n)==0 && nArg>1 ){
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 ){
|
||||
const char *zDestFile;
|
||||
const char *zDb;
|
||||
sqlite3 *pDest;
|
||||
sqlite3_backup *pBackup;
|
||||
int rc;
|
||||
if( nArg==2 ){
|
||||
zDestFile = azArg[1];
|
||||
zDb = "main";
|
||||
}else{
|
||||
zDestFile = azArg[2];
|
||||
zDb = azArg[1];
|
||||
}
|
||||
rc = sqlite3_open(zDestFile, &pDest);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "Error: cannot open %s\n", zDestFile);
|
||||
sqlite3_close(pDest);
|
||||
return 1;
|
||||
}
|
||||
open_db(p);
|
||||
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
|
||||
if( pBackup==0 ){
|
||||
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
|
||||
sqlite3_close(pDest);
|
||||
return 1;
|
||||
}
|
||||
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
|
||||
sqlite3_backup_finish(pBackup);
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
|
||||
}
|
||||
sqlite3_close(pDest);
|
||||
}else
|
||||
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 ){
|
||||
bail_on_error = booleanValue(azArg[1]);
|
||||
}else
|
||||
|
||||
@@ -1090,6 +1157,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
open_db(p);
|
||||
fprintf(p->out, "BEGIN TRANSACTION;\n");
|
||||
p->writableSchema = 0;
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=ON", 0, 0, 0);
|
||||
if( nArg==1 ){
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
@@ -1120,6 +1188,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
|
||||
p->writableSchema = 0;
|
||||
}
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF", 0, 0, 0);
|
||||
if( zErrMsg ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
@@ -1177,7 +1246,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else
|
||||
|
||||
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
||||
fprintf(stderr,zHelp);
|
||||
fprintf(stderr,"%s",zHelp);
|
||||
}else
|
||||
|
||||
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg>=3 ){
|
||||
@@ -1197,14 +1266,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
int lineno = 0; /* Line number of input file */
|
||||
|
||||
open_db(p);
|
||||
nSep = strlen(p->separator);
|
||||
nSep = strlen30(p->separator);
|
||||
if( nSep==0 ){
|
||||
fprintf(stderr, "non-null separator required for import\n");
|
||||
return 0;
|
||||
}
|
||||
zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
|
||||
if( zSql==0 ) return 0;
|
||||
nByte = strlen(zSql);
|
||||
nByte = strlen30(zSql);
|
||||
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
@@ -1219,7 +1288,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
zSql = malloc( nByte + 20 + nCol*2 );
|
||||
if( zSql==0 ) return 0;
|
||||
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
|
||||
j = strlen(zSql);
|
||||
j = strlen30(zSql);
|
||||
for(i=1; i<nCol; i++){
|
||||
zSql[j++] = ',';
|
||||
zSql[j++] = '?';
|
||||
@@ -1266,6 +1335,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(stderr,"%s line %d: expected %d columns of data but found %d\n",
|
||||
zFile, lineno, nCol, i+1);
|
||||
zCommit = "ROLLBACK";
|
||||
free(zLine);
|
||||
break;
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
@@ -1351,7 +1421,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
#endif
|
||||
|
||||
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){
|
||||
int n2 = strlen(azArg[1]);
|
||||
int n2 = strlen30(azArg[1]);
|
||||
if( strncmp(azArg[1],"line",n2)==0
|
||||
||
|
||||
strncmp(azArg[1],"lines",n2)==0 ){
|
||||
@@ -1421,7 +1491,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
rc = 2;
|
||||
}else
|
||||
|
||||
if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
|
||||
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
|
||||
FILE *alt = fopen(azArg[1], "rb");
|
||||
if( alt==0 ){
|
||||
fprintf(stderr,"can't open \"%s\"\n", azArg[1]);
|
||||
@@ -1431,6 +1501,52 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 ){
|
||||
const char *zSrcFile;
|
||||
const char *zDb;
|
||||
sqlite3 *pSrc;
|
||||
sqlite3_backup *pBackup;
|
||||
int rc;
|
||||
int nTimeout = 0;
|
||||
|
||||
if( nArg==2 ){
|
||||
zSrcFile = azArg[1];
|
||||
zDb = "main";
|
||||
}else{
|
||||
zSrcFile = azArg[2];
|
||||
zDb = azArg[1];
|
||||
}
|
||||
rc = sqlite3_open(zSrcFile, &pSrc);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "Error: cannot open %s\n", zSrcFile);
|
||||
sqlite3_close(pSrc);
|
||||
return 1;
|
||||
}
|
||||
open_db(p);
|
||||
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
|
||||
if( pBackup==0 ){
|
||||
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
||||
sqlite3_close(pSrc);
|
||||
return 1;
|
||||
}
|
||||
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
|
||||
|| rc==SQLITE_BUSY ){
|
||||
if( rc==SQLITE_BUSY ){
|
||||
if( nTimeout++ >= 3 ) break;
|
||||
sqlite3_sleep(100);
|
||||
}
|
||||
}
|
||||
sqlite3_backup_finish(pBackup);
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
|
||||
fprintf(stderr, "source database is busy\n");
|
||||
}else{
|
||||
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
||||
}
|
||||
sqlite3_close(pSrc);
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
|
||||
struct callback_data data;
|
||||
char *zErrMsg = 0;
|
||||
@@ -1440,7 +1556,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
data.mode = MODE_Semi;
|
||||
if( nArg>1 ){
|
||||
int i;
|
||||
for(i=0; azArg[1][i]; i++) azArg[1][i] = tolower(azArg[1][i]);
|
||||
for(i=0; azArg[1][i]; i++) azArg[1][i] = (char)tolower(azArg[1][i]);
|
||||
if( strcmp(azArg[1],"sqlite_master")==0 ){
|
||||
char *new_argv[2], *new_colv[2];
|
||||
new_argv[0] = "CREATE TABLE sqlite_master (\n"
|
||||
@@ -1471,8 +1587,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
zShellStatic = azArg[1];
|
||||
sqlite3_exec(p->db,
|
||||
"SELECT sql FROM "
|
||||
" (SELECT * FROM sqlite_master UNION ALL"
|
||||
" SELECT * FROM sqlite_temp_master) "
|
||||
" (SELECT sql sql, type type, tbl_name tbl_name, name name"
|
||||
" FROM sqlite_master UNION ALL"
|
||||
" SELECT sql, type, tbl_name, name FROM sqlite_temp_master) "
|
||||
"WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL "
|
||||
"ORDER BY substr(type,2,1), name",
|
||||
callback, &data, &zErrMsg);
|
||||
@@ -1481,8 +1598,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else{
|
||||
sqlite3_exec(p->db,
|
||||
"SELECT sql FROM "
|
||||
" (SELECT * FROM sqlite_master UNION ALL"
|
||||
" SELECT * FROM sqlite_temp_master) "
|
||||
" (SELECT sql sql, type type, tbl_name tbl_name, name name"
|
||||
" FROM sqlite_master UNION ALL"
|
||||
" SELECT sql, type, tbl_name, name FROM sqlite_temp_master) "
|
||||
"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
|
||||
"ORDER BY substr(type,2,1), name",
|
||||
callback, &data, &zErrMsg
|
||||
@@ -1509,7 +1627,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
output_c_string(p->out, p->nullvalue);
|
||||
fprintf(p->out, "\n");
|
||||
fprintf(p->out,"%9.9s: %s\n","output",
|
||||
strlen(p->outfile) ? p->outfile : "stdout");
|
||||
strlen30(p->outfile) ? p->outfile : "stdout");
|
||||
fprintf(p->out,"%9.9s: ", "separator");
|
||||
output_c_string(p->out, p->separator);
|
||||
fprintf(p->out, "\n");
|
||||
@@ -1558,7 +1676,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
int nPrintCol, nPrintRow;
|
||||
for(i=1; i<=nRow; i++){
|
||||
if( azResult[i]==0 ) continue;
|
||||
len = strlen(azResult[i]);
|
||||
len = strlen30(azResult[i]);
|
||||
if( len>maxlen ) maxlen = len;
|
||||
}
|
||||
nPrintCol = 80/(maxlen+2);
|
||||
@@ -1646,7 +1764,9 @@ static int _all_whitespace(const char *z){
|
||||
*/
|
||||
static int _is_command_terminator(const char *zLine){
|
||||
while( isspace(*(unsigned char*)zLine) ){ zLine++; };
|
||||
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ) return 1; /* Oracle */
|
||||
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
|
||||
return 1; /* Oracle */
|
||||
}
|
||||
if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o'
|
||||
&& _all_whitespace(&zLine[2]) ){
|
||||
return 1; /* SQL Server */
|
||||
@@ -1654,6 +1774,20 @@ static int _is_command_terminator(const char *zLine){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if zSql is a complete SQL statement. Return false if it
|
||||
** ends in the middle of a string literal or C-style comment.
|
||||
*/
|
||||
static int _is_complete(char *zSql, int nSql){
|
||||
int rc;
|
||||
if( zSql==0 ) return 1;
|
||||
zSql[nSql] = ';';
|
||||
zSql[nSql+1] = 0;
|
||||
rc = sqlite3_complete(zSql);
|
||||
zSql[nSql] = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read input from *in and process it. If *in==0 then input
|
||||
** is interactive - the user is typing it it. Otherwise, input
|
||||
@@ -1697,7 +1831,7 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( _is_command_terminator(zLine) ){
|
||||
if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){
|
||||
memcpy(zLine,";",2);
|
||||
}
|
||||
nSqlPrior = nSql;
|
||||
@@ -1705,8 +1839,8 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
int i;
|
||||
for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){}
|
||||
if( zLine[i]!=0 ){
|
||||
nSql = strlen(zLine);
|
||||
zSql = malloc( nSql+1 );
|
||||
nSql = strlen30(zLine);
|
||||
zSql = malloc( nSql+3 );
|
||||
if( zSql==0 ){
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
@@ -1715,8 +1849,8 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
startline = lineno;
|
||||
}
|
||||
}else{
|
||||
int len = strlen(zLine);
|
||||
zSql = realloc( zSql, nSql + len + 2 );
|
||||
int len = strlen30(zLine);
|
||||
zSql = realloc( zSql, nSql + len + 4 );
|
||||
if( zSql==0 ){
|
||||
fprintf(stderr,"%s: out of memory!\n", Argv0);
|
||||
exit(1);
|
||||
@@ -1755,7 +1889,7 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
}
|
||||
}
|
||||
if( zSql ){
|
||||
if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql);
|
||||
if( !_all_whitespace(zSql) ) fprintf(stderr, "Incomplete SQL: %s\n", zSql);
|
||||
free(zSql);
|
||||
}
|
||||
free(zLine);
|
||||
@@ -1771,7 +1905,7 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
static char *find_home_dir(void){
|
||||
char *home_dir = NULL;
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE)
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
struct passwd *pwent;
|
||||
uid_t uid = getuid();
|
||||
if( (pwent=getpwuid(uid)) != NULL) {
|
||||
@@ -1802,7 +1936,7 @@ static char *find_home_dir(void){
|
||||
zDrive = getenv("HOMEDRIVE");
|
||||
zPath = getenv("HOMEPATH");
|
||||
if( zDrive && zPath ){
|
||||
n = strlen(zDrive) + strlen(zPath) + 1;
|
||||
n = strlen30(zDrive) + strlen30(zPath) + 1;
|
||||
home_dir = malloc( n );
|
||||
if( home_dir==0 ) return 0;
|
||||
sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath);
|
||||
@@ -1815,7 +1949,7 @@ static char *find_home_dir(void){
|
||||
#endif /* !_WIN32_WCE */
|
||||
|
||||
if( home_dir ){
|
||||
int n = strlen(home_dir) + 1;
|
||||
int n = strlen30(home_dir) + 1;
|
||||
char *z = malloc( n );
|
||||
if( z ) memcpy(z, home_dir, n);
|
||||
home_dir = z;
|
||||
@@ -1841,10 +1975,12 @@ static void process_sqliterc(
|
||||
if (sqliterc == NULL) {
|
||||
home_dir = find_home_dir();
|
||||
if( home_dir==0 ){
|
||||
#if !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
nBuf = strlen(home_dir) + 16;
|
||||
nBuf = strlen30(home_dir) + 16;
|
||||
zBuf = malloc( nBuf );
|
||||
if( zBuf==0 ){
|
||||
fprintf(stderr,"%s: out of memory!\n", Argv0);
|
||||
@@ -2067,8 +2203,11 @@ int main(int argc, char **argv){
|
||||
sqlite3_libversion()
|
||||
);
|
||||
zHome = find_home_dir();
|
||||
if( zHome && (zHistory = malloc(nHistory = strlen(zHome)+20))!=0 ){
|
||||
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
|
||||
if( zHome ){
|
||||
nHistory = strlen30(zHome) + 20;
|
||||
if( (zHistory = malloc(nHistory))!=0 ){
|
||||
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
|
||||
}
|
||||
}
|
||||
#if defined(HAVE_READLINE) && HAVE_READLINE==1
|
||||
if( zHistory ) read_history(zHistory);
|
||||
|
||||
+851
-192
File diff suppressed because it is too large
Load Diff
+9
-1
@@ -15,7 +15,7 @@
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
**
|
||||
** @(#) $Id: sqlite3ext.h,v 1.24 2008/06/30 15:09:29 danielk1977 Exp $
|
||||
** @(#) $Id: sqlite3ext.h,v 1.25 2008/10/12 00:27:54 shane Exp $
|
||||
*/
|
||||
#ifndef _SQLITE3EXT_H_
|
||||
#define _SQLITE3EXT_H_
|
||||
@@ -208,7 +208,9 @@ struct sqlite3_api_routines {
|
||||
*/
|
||||
#ifndef SQLITE_CORE
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
@@ -264,14 +266,18 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
@@ -309,7 +315,9 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
|
||||
+561
-207
File diff suppressed because it is too large
Load Diff
+9
-2
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** This file defines various limits of what SQLite can process.
|
||||
**
|
||||
** @(#) $Id: sqliteLimit.h,v 1.8 2008/03/26 15:56:22 drh Exp $
|
||||
** @(#) $Id: sqliteLimit.h,v 1.10 2009/01/10 16:15:09 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -96,7 +96,7 @@
|
||||
** The maximum number of arguments to an SQL function.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_FUNCTION_ARG
|
||||
# define SQLITE_MAX_FUNCTION_ARG 100
|
||||
# define SQLITE_MAX_FUNCTION_ARG 127
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -130,6 +130,13 @@
|
||||
/* Maximum page size. The upper bound on this value is 32768. This a limit
|
||||
** imposed by the necessity of storing the value in a 2-byte unsigned integer
|
||||
** and the fact that the page size must be a power of 2.
|
||||
**
|
||||
** If this limit is changed, then the compiled library is technically
|
||||
** incompatible with an SQLite library compiled with a different limit. If
|
||||
** a process operating on a database with a page-size of 65536 bytes
|
||||
** crashes, then an instance of SQLite compiled with the default page-size
|
||||
** limit will not be able to rollback the aborted transaction. This could
|
||||
** lead to database corruption.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_PAGE_SIZE
|
||||
# define SQLITE_MAX_PAGE_SIZE 32768
|
||||
|
||||
+34
-23
@@ -13,33 +13,41 @@
|
||||
** This module implements the sqlite3_status() interface and related
|
||||
** functionality.
|
||||
**
|
||||
** $Id: status.c,v 1.7 2008/08/05 17:53:23 drh Exp $
|
||||
** $Id: status.c,v 1.9 2008/09/02 00:52:52 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Variables in which to record status information.
|
||||
*/
|
||||
static struct {
|
||||
typedef struct sqlite3StatType sqlite3StatType;
|
||||
static SQLITE_WSD struct sqlite3StatType {
|
||||
int nowValue[9]; /* Current value */
|
||||
int mxValue[9]; /* Maximum value */
|
||||
} sqlite3Stat;
|
||||
} sqlite3Stat = { {0,}, {0,} };
|
||||
|
||||
|
||||
/*
|
||||
** Reset the status records. This routine is called by
|
||||
** sqlite3_initialize().
|
||||
/* The "wsdStat" macro will resolve to the status information
|
||||
** state vector. If writable static data is unsupported on the target,
|
||||
** we have to locate the state vector at run-time. In the more common
|
||||
** case where writable static data is supported, wsdStat can refer directly
|
||||
** to the "sqlite3Stat" state vector declared above.
|
||||
*/
|
||||
void sqlite3StatusReset(void){
|
||||
memset(&sqlite3Stat, 0, sizeof(sqlite3Stat));
|
||||
}
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
# define wsdStatInit sqlite3StatType *x = &GLOBAL(sqlite3StatType,sqlite3Stat)
|
||||
# define wsdStat x[0]
|
||||
#else
|
||||
# define wsdStatInit
|
||||
# define wsdStat sqlite3Stat
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the current value of a status parameter.
|
||||
*/
|
||||
int sqlite3StatusValue(int op){
|
||||
assert( op>=0 && op<ArraySize(sqlite3Stat.nowValue) );
|
||||
return sqlite3Stat.nowValue[op];
|
||||
wsdStatInit;
|
||||
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
|
||||
return wsdStat.nowValue[op];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -47,10 +55,11 @@ int sqlite3StatusValue(int op){
|
||||
** caller holds appropriate locks.
|
||||
*/
|
||||
void sqlite3StatusAdd(int op, int N){
|
||||
assert( op>=0 && op<ArraySize(sqlite3Stat.nowValue) );
|
||||
sqlite3Stat.nowValue[op] += N;
|
||||
if( sqlite3Stat.nowValue[op]>sqlite3Stat.mxValue[op] ){
|
||||
sqlite3Stat.mxValue[op] = sqlite3Stat.nowValue[op];
|
||||
wsdStatInit;
|
||||
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
|
||||
wsdStat.nowValue[op] += N;
|
||||
if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){
|
||||
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +67,11 @@ void sqlite3StatusAdd(int op, int N){
|
||||
** Set the value of a status to X.
|
||||
*/
|
||||
void sqlite3StatusSet(int op, int X){
|
||||
assert( op>=0 && op<ArraySize(sqlite3Stat.nowValue) );
|
||||
sqlite3Stat.nowValue[op] = X;
|
||||
if( sqlite3Stat.nowValue[op]>sqlite3Stat.mxValue[op] ){
|
||||
sqlite3Stat.mxValue[op] = sqlite3Stat.nowValue[op];
|
||||
wsdStatInit;
|
||||
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
|
||||
wsdStat.nowValue[op] = X;
|
||||
if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){
|
||||
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +83,14 @@ void sqlite3StatusSet(int op, int X){
|
||||
** then this routine is not threadsafe.
|
||||
*/
|
||||
int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
|
||||
if( op<0 || op>=ArraySize(sqlite3Stat.nowValue) ){
|
||||
wsdStatInit;
|
||||
if( op<0 || op>=ArraySize(wsdStat.nowValue) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
*pCurrent = sqlite3Stat.nowValue[op];
|
||||
*pHighwater = sqlite3Stat.mxValue[op];
|
||||
*pCurrent = wsdStat.nowValue[op];
|
||||
*pHighwater = wsdStat.mxValue[op];
|
||||
if( resetFlag ){
|
||||
sqlite3Stat.mxValue[op] = sqlite3Stat.nowValue[op];
|
||||
wsdStat.mxValue[op] = wsdStat.nowValue[op];
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
+3
-2
@@ -16,7 +16,7 @@
|
||||
** These routines are in a separate files so that they will not be linked
|
||||
** if they are not used.
|
||||
**
|
||||
** $Id: table.c,v 1.36 2008/07/08 22:28:49 shane Exp $
|
||||
** $Id: table.c,v 1.39 2009/01/19 20:49:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
@@ -92,7 +92,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
|
||||
if( argv[i]==0 ){
|
||||
z = 0;
|
||||
}else{
|
||||
int n = strlen(argv[i])+1;
|
||||
int n = sqlite3Strlen30(argv[i])+1;
|
||||
z = sqlite3_malloc( n );
|
||||
if( z==0 ) goto malloc_failed;
|
||||
memcpy(z, argv[i], n);
|
||||
@@ -132,6 +132,7 @@ int sqlite3_get_table(
|
||||
*pazResult = 0;
|
||||
if( pnColumn ) *pnColumn = 0;
|
||||
if( pnRow ) *pnRow = 0;
|
||||
if( pzErrMsg ) *pzErrMsg = 0;
|
||||
res.zErrMsg = 0;
|
||||
res.nResult = 0;
|
||||
res.nRow = 0;
|
||||
|
||||
+292
-56
@@ -12,7 +12,7 @@
|
||||
** A TCL Interface to SQLite. Append this file to sqlite3.c and
|
||||
** compile the whole thing to build a TCL-enabled version of SQLite.
|
||||
**
|
||||
** $Id: tclsqlite.c,v 1.219 2008/07/10 17:52:49 danielk1977 Exp $
|
||||
** $Id: tclsqlite.c,v 1.237 2009/02/17 16:29:11 danielk1977 Exp $
|
||||
*/
|
||||
#include "tcl.h"
|
||||
#include <errno.h>
|
||||
@@ -104,6 +104,7 @@ struct SqliteDb {
|
||||
char *zProfile; /* The profile callback routine */
|
||||
char *zProgress; /* The progress callback routine */
|
||||
char *zAuth; /* The authorization callback routine */
|
||||
int disableAuth; /* Disable the authorizer if it exists */
|
||||
char *zNull; /* Text to substitute for an SQL NULL value */
|
||||
SqlFunc *pFunc; /* List of SQL functions */
|
||||
Tcl_Obj *pUpdateHook; /* Update hook script (if any) */
|
||||
@@ -116,6 +117,8 @@ struct SqliteDb {
|
||||
int maxStmt; /* The next maximum number of stmtList */
|
||||
int nStmt; /* Number of statements in stmtList */
|
||||
IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
|
||||
int nStep, nSort; /* Statistics for most recent operation */
|
||||
int nTransaction; /* Number of nested [transaction] methods */
|
||||
};
|
||||
|
||||
struct IncrblobChannel {
|
||||
@@ -127,6 +130,17 @@ struct IncrblobChannel {
|
||||
IncrblobChannel *pPrev; /* Linked list of all open incrblob channels */
|
||||
};
|
||||
|
||||
/*
|
||||
** Compute a string length that is limited to what can be stored in
|
||||
** lower 30 bits of a 32-bit signed integer.
|
||||
*/
|
||||
static int strlen30(const char *z){
|
||||
const char *z2 = z;
|
||||
while( *z2 ){ z2++; }
|
||||
return 0x3fffffff & (int)(z2 - z);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
/*
|
||||
** Close all incrblob channels opened using database connection pDb.
|
||||
@@ -382,7 +396,7 @@ static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){
|
||||
static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
|
||||
SqlFunc *p, *pNew;
|
||||
int i;
|
||||
pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen(zName) + 1 );
|
||||
pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen30(zName) + 1 );
|
||||
pNew->zName = (char*)&pNew[1];
|
||||
for(i=0; zName[i]; i++){ pNew->zName[i] = tolower(zName[i]); }
|
||||
pNew->zName[i] = 0;
|
||||
@@ -761,6 +775,7 @@ static int auth_callback(
|
||||
int rc;
|
||||
const char *zReply;
|
||||
SqliteDb *pDb = (SqliteDb*)pArg;
|
||||
if( pDb->disableAuth ) return SQLITE_OK;
|
||||
|
||||
switch( code ){
|
||||
case SQLITE_COPY : zCode="SQLITE_COPY"; break;
|
||||
@@ -795,6 +810,7 @@ static int auth_callback(
|
||||
case SQLITE_CREATE_VTABLE : zCode="SQLITE_CREATE_VTABLE"; break;
|
||||
case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break;
|
||||
case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break;
|
||||
case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break;
|
||||
default : zCode="????"; break;
|
||||
}
|
||||
Tcl_DStringInit(&str);
|
||||
@@ -967,29 +983,31 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
int choice;
|
||||
int rc = TCL_OK;
|
||||
static const char *DB_strs[] = {
|
||||
"authorizer", "busy", "cache",
|
||||
"changes", "close", "collate",
|
||||
"collation_needed", "commit_hook", "complete",
|
||||
"copy", "enable_load_extension","errorcode",
|
||||
"eval", "exists", "function",
|
||||
"incrblob", "interrupt", "last_insert_rowid",
|
||||
"nullvalue", "onecolumn", "profile",
|
||||
"progress", "rekey", "rollback_hook",
|
||||
"authorizer", "backup", "busy",
|
||||
"cache", "changes", "close",
|
||||
"collate", "collation_needed", "commit_hook",
|
||||
"complete", "copy", "enable_load_extension",
|
||||
"errorcode", "eval", "exists",
|
||||
"function", "incrblob", "interrupt",
|
||||
"last_insert_rowid", "nullvalue", "onecolumn",
|
||||
"profile", "progress", "rekey",
|
||||
"restore", "rollback_hook", "status",
|
||||
"timeout", "total_changes", "trace",
|
||||
"transaction", "update_hook", "version",
|
||||
0
|
||||
};
|
||||
enum DB_enum {
|
||||
DB_AUTHORIZER, DB_BUSY, DB_CACHE,
|
||||
DB_CHANGES, DB_CLOSE, DB_COLLATE,
|
||||
DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE,
|
||||
DB_COPY, DB_ENABLE_LOAD_EXTENSION,DB_ERRORCODE,
|
||||
DB_EVAL, DB_EXISTS, DB_FUNCTION,
|
||||
DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID,
|
||||
DB_NULLVALUE, DB_ONECOLUMN, DB_PROFILE,
|
||||
DB_PROGRESS, DB_REKEY, DB_ROLLBACK_HOOK,
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
|
||||
DB_CACHE, DB_CHANGES, DB_CLOSE,
|
||||
DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK,
|
||||
DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION,
|
||||
DB_ERRORCODE, DB_EVAL, DB_EXISTS,
|
||||
DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT,
|
||||
DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN,
|
||||
DB_PROFILE, DB_PROGRESS, DB_REKEY,
|
||||
DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS,
|
||||
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
|
||||
DB_TRANSACTION, DB_UPDATE_HOOK, DB_VERSION
|
||||
DB_TRANSACTION, DB_UPDATE_HOOK, DB_VERSION,
|
||||
};
|
||||
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
|
||||
|
||||
@@ -1057,6 +1075,55 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db backup ?DATABASE? FILENAME
|
||||
**
|
||||
** Open or create a database file named FILENAME. Transfer the
|
||||
** content of local database DATABASE (default: "main") into the
|
||||
** FILENAME database.
|
||||
*/
|
||||
case DB_BACKUP: {
|
||||
const char *zDestFile;
|
||||
const char *zSrcDb;
|
||||
sqlite3 *pDest;
|
||||
sqlite3_backup *pBackup;
|
||||
|
||||
if( objc==3 ){
|
||||
zSrcDb = "main";
|
||||
zDestFile = Tcl_GetString(objv[2]);
|
||||
}else if( objc==4 ){
|
||||
zSrcDb = Tcl_GetString(objv[2]);
|
||||
zDestFile = Tcl_GetString(objv[3]);
|
||||
}else{
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_open(zDestFile, &pDest);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "cannot open target database: ",
|
||||
sqlite3_errmsg(pDest), (char*)0);
|
||||
sqlite3_close(pDest);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pBackup = sqlite3_backup_init(pDest, "main", pDb->db, zSrcDb);
|
||||
if( pBackup==0 ){
|
||||
Tcl_AppendResult(interp, "backup failed: ",
|
||||
sqlite3_errmsg(pDest), (char*)0);
|
||||
sqlite3_close(pDest);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
|
||||
sqlite3_backup_finish(pBackup);
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = TCL_OK;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "backup failed: ",
|
||||
sqlite3_errmsg(pDest), (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
sqlite3_close(pDest);
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db busy ?CALLBACK?
|
||||
**
|
||||
** Invoke the given callback if an SQL statement attempts to open
|
||||
@@ -1335,17 +1402,17 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
zConflict = Tcl_GetStringFromObj(objv[2], 0);
|
||||
zTable = Tcl_GetStringFromObj(objv[3], 0);
|
||||
zFile = Tcl_GetStringFromObj(objv[4], 0);
|
||||
nSep = strlen(zSep);
|
||||
nNull = strlen(zNull);
|
||||
nSep = strlen30(zSep);
|
||||
nNull = strlen30(zNull);
|
||||
if( nSep==0 ){
|
||||
Tcl_AppendResult(interp,"Error: non-null separator required for copy",0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if(strcasecmp(zConflict, "rollback") != 0 &&
|
||||
strcasecmp(zConflict, "abort" ) != 0 &&
|
||||
strcasecmp(zConflict, "fail" ) != 0 &&
|
||||
strcasecmp(zConflict, "ignore" ) != 0 &&
|
||||
strcasecmp(zConflict, "replace" ) != 0 ) {
|
||||
if(strcmp(zConflict, "rollback") != 0 &&
|
||||
strcmp(zConflict, "abort" ) != 0 &&
|
||||
strcmp(zConflict, "fail" ) != 0 &&
|
||||
strcmp(zConflict, "ignore" ) != 0 &&
|
||||
strcmp(zConflict, "replace" ) != 0 ) {
|
||||
Tcl_AppendResult(interp, "Error: \"", zConflict,
|
||||
"\", conflict-algorithm must be one of: rollback, "
|
||||
"abort, fail, ignore, or replace", 0);
|
||||
@@ -1356,7 +1423,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
nByte = strlen(zSql);
|
||||
nByte = strlen30(zSql);
|
||||
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
@@ -1376,7 +1443,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?",
|
||||
zConflict, zTable);
|
||||
j = strlen(zSql);
|
||||
j = strlen30(zSql);
|
||||
for(i=1; i<nCol; i++){
|
||||
zSql[j++] = ',';
|
||||
zSql[j++] = '?';
|
||||
@@ -1421,7 +1488,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
if( i+1!=nCol ){
|
||||
char *zErr;
|
||||
int nErr = strlen(zFile) + 200;
|
||||
int nErr = strlen30(zFile) + 200;
|
||||
zErr = malloc(nErr);
|
||||
if( zErr ){
|
||||
sqlite3_snprintf(nErr, zErr,
|
||||
@@ -1435,7 +1502,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
/* check for null data, if so, bind as null */
|
||||
if ((nNull>0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) {
|
||||
if( (nNull>0 && strcmp(azCol[i], zNull)==0)
|
||||
|| strlen30(azCol[i])==0
|
||||
){
|
||||
sqlite3_bind_null(pStmt, i+1);
|
||||
}else{
|
||||
sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
|
||||
@@ -1576,7 +1645,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** which matches the next sequence of SQL.
|
||||
*/
|
||||
pStmt = 0;
|
||||
len = strlen(zSql);
|
||||
len = strlen30(zSql);
|
||||
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
|
||||
int n = pPreStmt->nSql;
|
||||
if( len>=n
|
||||
@@ -1758,6 +1827,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
|
||||
if( pScript ){
|
||||
pDb->nStep = sqlite3_stmt_status(pStmt,
|
||||
SQLITE_STMTSTATUS_FULLSCAN_STEP, 0);
|
||||
pDb->nSort = sqlite3_stmt_status(pStmt,
|
||||
SQLITE_STMTSTATUS_SORT, 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc==TCL_CONTINUE ){
|
||||
rc = TCL_OK;
|
||||
@@ -1796,6 +1869,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** flush the statement cache and try the statement again.
|
||||
*/
|
||||
rc2 = sqlite3_reset(pStmt);
|
||||
pDb->nStep = sqlite3_stmt_status(pStmt,
|
||||
SQLITE_STMTSTATUS_FULLSCAN_STEP, 1);
|
||||
pDb->nSort = sqlite3_stmt_status(pStmt,
|
||||
SQLITE_STMTSTATUS_SORT, 1);
|
||||
if( SQLITE_OK!=rc2 ){
|
||||
/* If a run-time error occurs, report the error and stop reading
|
||||
** the SQL
|
||||
@@ -1821,7 +1898,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
pPreStmt->pStmt = pStmt;
|
||||
pPreStmt->nSql = len;
|
||||
pPreStmt->zSql = sqlite3_sql(pStmt);
|
||||
assert( strlen(pPreStmt->zSql)==len );
|
||||
assert( strlen30(pPreStmt->zSql)==len );
|
||||
assert( 0==memcmp(pPreStmt->zSql, zSql, len) );
|
||||
}
|
||||
|
||||
@@ -1870,7 +1947,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
|
||||
/*
|
||||
** $db function NAME SCRIPT
|
||||
** $db function NAME [-argcount N] SCRIPT
|
||||
**
|
||||
** Create a new SQL function called NAME. Whenever that function is
|
||||
** called, invoke SCRIPT to evaluate the function.
|
||||
@@ -1879,12 +1956,26 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
SqlFunc *pFunc;
|
||||
Tcl_Obj *pScript;
|
||||
char *zName;
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
|
||||
int nArg = -1;
|
||||
if( objc==6 ){
|
||||
const char *z = Tcl_GetString(objv[3]);
|
||||
int n = strlen30(z);
|
||||
if( n>2 && strncmp(z, "-argcount",n)==0 ){
|
||||
if( Tcl_GetIntFromObj(interp, objv[4], &nArg) ) return TCL_ERROR;
|
||||
if( nArg<0 ){
|
||||
Tcl_AppendResult(interp, "number of arguments must be non-negative",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
pScript = objv[5];
|
||||
}else if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "NAME [-argcount N] SCRIPT");
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
pScript = objv[3];
|
||||
}
|
||||
zName = Tcl_GetStringFromObj(objv[2], 0);
|
||||
pScript = objv[3];
|
||||
pFunc = findSqlFunc(pDb, zName);
|
||||
if( pFunc==0 ) return TCL_ERROR;
|
||||
if( pFunc->pScript ){
|
||||
@@ -1893,7 +1984,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
pFunc->pScript = pScript;
|
||||
Tcl_IncrRefCount(pScript);
|
||||
pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript);
|
||||
rc = sqlite3_create_function(pDb->db, zName, -1, SQLITE_UTF8,
|
||||
rc = sqlite3_create_function(pDb->db, zName, nArg, SQLITE_UTF8,
|
||||
pFunc, tclSqlFunc, 0, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = TCL_ERROR;
|
||||
@@ -2110,6 +2201,92 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db restore ?DATABASE? FILENAME
|
||||
**
|
||||
** Open a database file named FILENAME. Transfer the content
|
||||
** of FILENAME into the local database DATABASE (default: "main").
|
||||
*/
|
||||
case DB_RESTORE: {
|
||||
const char *zSrcFile;
|
||||
const char *zDestDb;
|
||||
sqlite3 *pSrc;
|
||||
sqlite3_backup *pBackup;
|
||||
int nTimeout = 0;
|
||||
|
||||
if( objc==3 ){
|
||||
zDestDb = "main";
|
||||
zSrcFile = Tcl_GetString(objv[2]);
|
||||
}else if( objc==4 ){
|
||||
zDestDb = Tcl_GetString(objv[2]);
|
||||
zSrcFile = Tcl_GetString(objv[3]);
|
||||
}else{
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_open_v2(zSrcFile, &pSrc, SQLITE_OPEN_READONLY, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "cannot open source database: ",
|
||||
sqlite3_errmsg(pSrc), (char*)0);
|
||||
sqlite3_close(pSrc);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pBackup = sqlite3_backup_init(pDb->db, zDestDb, pSrc, "main");
|
||||
if( pBackup==0 ){
|
||||
Tcl_AppendResult(interp, "restore failed: ",
|
||||
sqlite3_errmsg(pDb->db), (char*)0);
|
||||
sqlite3_close(pSrc);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
|
||||
|| rc==SQLITE_BUSY ){
|
||||
if( rc==SQLITE_BUSY ){
|
||||
if( nTimeout++ >= 3 ) break;
|
||||
sqlite3_sleep(100);
|
||||
}
|
||||
}
|
||||
sqlite3_backup_finish(pBackup);
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = TCL_OK;
|
||||
}else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
|
||||
Tcl_AppendResult(interp, "restore failed: source database busy",
|
||||
(char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "restore failed: ",
|
||||
sqlite3_errmsg(pDb->db), (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
sqlite3_close(pSrc);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** $db status (step|sort)
|
||||
**
|
||||
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
|
||||
** SQLITE_STMTSTATUS_SORT for the most recent eval.
|
||||
*/
|
||||
case DB_STATUS: {
|
||||
int v;
|
||||
const char *zOp;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "(step|sort)");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zOp = Tcl_GetString(objv[2]);
|
||||
if( strcmp(zOp, "step")==0 ){
|
||||
v = pDb->nStep;
|
||||
}else if( strcmp(zOp, "sort")==0 ){
|
||||
v = pDb->nSort;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "bad argument: should be step or sort",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(v));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** $db timeout MILLESECONDS
|
||||
**
|
||||
@@ -2194,16 +2371,17 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** 2005 O'Reilly Open Source Convention (OSCON).
|
||||
*/
|
||||
case DB_TRANSACTION: {
|
||||
int inTrans;
|
||||
Tcl_Obj *pScript;
|
||||
const char *zBegin = "BEGIN";
|
||||
const char *zBegin = "SAVEPOINT _tcl_transaction";
|
||||
const char *zEnd;
|
||||
if( objc!=3 && objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "[TYPE] SCRIPT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( objc==3 ){
|
||||
pScript = objv[2];
|
||||
} else {
|
||||
|
||||
if( pDb->nTransaction ){
|
||||
zBegin = "SAVEPOINT _tcl_transaction";
|
||||
}else if( pDb->nTransaction==0 && objc==4 ){
|
||||
static const char *TTYPE_strs[] = {
|
||||
"deferred", "exclusive", "immediate", 0
|
||||
};
|
||||
@@ -2220,24 +2398,55 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
case TTYPE_EXCLUSIVE: zBegin = "BEGIN EXCLUSIVE"; break;
|
||||
case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break;
|
||||
}
|
||||
pScript = objv[3];
|
||||
}
|
||||
inTrans = !sqlite3_get_autocommit(pDb->db);
|
||||
if( !inTrans ){
|
||||
(void)sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
|
||||
pScript = objv[objc-1];
|
||||
|
||||
pDb->disableAuth++;
|
||||
rc = sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
|
||||
pDb->disableAuth--;
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pDb->nTransaction++;
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( !inTrans ){
|
||||
const char *zEnd;
|
||||
if( rc==TCL_ERROR ){
|
||||
zEnd = "ROLLBACK";
|
||||
} else {
|
||||
pDb->nTransaction--;
|
||||
|
||||
if( rc!=TCL_ERROR ){
|
||||
if( pDb->nTransaction ){
|
||||
zEnd = "RELEASE _tcl_transaction";
|
||||
}else{
|
||||
zEnd = "COMMIT";
|
||||
}
|
||||
if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
|
||||
sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
|
||||
}else{
|
||||
if( pDb->nTransaction ){
|
||||
zEnd = "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction";
|
||||
}else{
|
||||
zEnd = "ROLLBACK";
|
||||
}
|
||||
}
|
||||
|
||||
pDb->disableAuth++;
|
||||
if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){
|
||||
/* This is a tricky scenario to handle. The most likely cause of an
|
||||
** error is that the exec() above was an attempt to commit the
|
||||
** top-level transaction that returned SQLITE_BUSY. Or, less likely,
|
||||
** that an IO-error has occured. In either case, throw a Tcl exception
|
||||
** and try to rollback the transaction.
|
||||
**
|
||||
** But it could also be that the user executed one or more BEGIN,
|
||||
** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing
|
||||
** this method's logic. Not clear how this would be best handled.
|
||||
*/
|
||||
if( rc!=TCL_ERROR ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
|
||||
}
|
||||
pDb->disableAuth--;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2321,7 +2530,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
int i;
|
||||
const char *zFile;
|
||||
const char *zVfs = 0;
|
||||
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX;
|
||||
Tcl_DString translatedFilename;
|
||||
if( objc==2 ){
|
||||
zArg = Tcl_GetStringFromObj(objv[1], 0);
|
||||
@@ -2368,9 +2577,19 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
||||
if( b ){
|
||||
flags |= SQLITE_OPEN_NOMUTEX;
|
||||
flags &= ~SQLITE_OPEN_FULLMUTEX;
|
||||
}else{
|
||||
flags &= ~SQLITE_OPEN_NOMUTEX;
|
||||
}
|
||||
}else if( strcmp(zArg, "-fullmutex")==0 ){
|
||||
int b;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
||||
if( b ){
|
||||
flags |= SQLITE_OPEN_FULLMUTEX;
|
||||
flags &= ~SQLITE_OPEN_NOMUTEX;
|
||||
}else{
|
||||
flags &= ~SQLITE_OPEN_FULLMUTEX;
|
||||
}
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
|
||||
return TCL_ERROR;
|
||||
@@ -2379,7 +2598,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( objc<3 || (objc&1)!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv,
|
||||
"HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
|
||||
" ?-nomutex BOOLEAN?"
|
||||
" ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN?"
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
" ?-key CODECKEY?"
|
||||
#endif
|
||||
@@ -2458,12 +2677,21 @@ EXTERN int Sqlite3_Init(Tcl_Interp *interp){
|
||||
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
|
||||
|
||||
|
||||
#ifndef SQLITE_3_SUFFIX_ONLY
|
||||
EXTERN int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
EXTERN int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
EXTERN int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Sqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
|
||||
#endif
|
||||
|
||||
#ifdef TCLSH
|
||||
@@ -2516,6 +2744,12 @@ static char zMainloop[] =
|
||||
#define TCLSH_MAIN main /* Needed to fake out mktclapp */
|
||||
int TCLSH_MAIN(int argc, char **argv){
|
||||
Tcl_Interp *interp;
|
||||
|
||||
/* Call sqlite3_shutdown() once before doing anything else. This is to
|
||||
** test that sqlite3_shutdown() can be safely called by a process before
|
||||
** sqlite3_initialize() is. */
|
||||
sqlite3_shutdown();
|
||||
|
||||
Tcl_FindExecutable(argv[0]);
|
||||
interp = Tcl_CreateInterp();
|
||||
Sqlite3_Init(interp);
|
||||
@@ -2544,6 +2778,7 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
extern int SqlitetestThread_Init(Tcl_Interp*);
|
||||
extern int SqlitetestOnefile_Init();
|
||||
extern int SqlitetestOsinst_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestbackup_Init(Tcl_Interp*);
|
||||
|
||||
Md5_Init(interp);
|
||||
Sqliteconfig_Init(interp);
|
||||
@@ -2567,6 +2802,7 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
SqlitetestThread_Init(interp);
|
||||
SqlitetestOnefile_Init(interp);
|
||||
SqlitetestOsinst_Init(interp);
|
||||
Sqlitetestbackup_Init(interp);
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
Sqlitetestsse_Init(interp);
|
||||
@@ -2586,7 +2822,7 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
}
|
||||
if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){
|
||||
const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
|
||||
if( zInfo==0 ) zInfo = interp->result;
|
||||
if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp);
|
||||
fprintf(stderr,"%s: %s\n", *argv, zInfo);
|
||||
return 1;
|
||||
}
|
||||
|
||||
+449
-119
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.317 2008/07/31 02:05:04 shane Exp $
|
||||
** $Id: test1.c,v 1.347 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -117,35 +117,53 @@ int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
|
||||
|
||||
const char *sqlite3TestErrorName(int rc){
|
||||
const char *zName = 0;
|
||||
switch( rc & 0xff ){
|
||||
case SQLITE_OK: zName = "SQLITE_OK"; break;
|
||||
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
|
||||
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
|
||||
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
|
||||
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
|
||||
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
|
||||
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
|
||||
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
|
||||
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
||||
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
|
||||
case SQLITE_FULL: zName = "SQLITE_FULL"; break;
|
||||
case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
|
||||
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
|
||||
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
|
||||
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
|
||||
case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
|
||||
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
|
||||
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
|
||||
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
|
||||
case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
|
||||
case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
|
||||
case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
|
||||
case SQLITE_ROW: zName = "SQLITE_ROW"; break;
|
||||
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
|
||||
case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break;
|
||||
case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
|
||||
default: zName = "SQLITE_Unknown"; break;
|
||||
switch( rc ){
|
||||
case SQLITE_OK: zName = "SQLITE_OK"; break;
|
||||
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
|
||||
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
|
||||
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
|
||||
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
|
||||
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
|
||||
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
|
||||
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
|
||||
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
|
||||
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
||||
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
|
||||
case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
|
||||
case SQLITE_FULL: zName = "SQLITE_FULL"; break;
|
||||
case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
|
||||
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
|
||||
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
|
||||
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
|
||||
case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
|
||||
case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
|
||||
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
|
||||
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
|
||||
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
|
||||
case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
|
||||
case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
|
||||
case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
|
||||
case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break;
|
||||
case SQLITE_ROW: zName = "SQLITE_ROW"; break;
|
||||
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
|
||||
case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
|
||||
case SQLITE_IOERR_SHORT_READ: zName = "SQLITE_IOERR_SHORT_READ"; break;
|
||||
case SQLITE_IOERR_WRITE: zName = "SQLITE_IOERR_WRITE"; break;
|
||||
case SQLITE_IOERR_FSYNC: zName = "SQLITE_IOERR_FSYNC"; break;
|
||||
case SQLITE_IOERR_DIR_FSYNC: zName = "SQLITE_IOERR_DIR_FSYNC"; break;
|
||||
case SQLITE_IOERR_TRUNCATE: zName = "SQLITE_IOERR_TRUNCATE"; break;
|
||||
case SQLITE_IOERR_FSTAT: zName = "SQLITE_IOERR_FSTAT"; break;
|
||||
case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break;
|
||||
case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break;
|
||||
case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break;
|
||||
case SQLITE_IOERR_BLOCKED: zName = "SQLITE_IOERR_BLOCKED"; break;
|
||||
case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break;
|
||||
case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break;
|
||||
case SQLITE_IOERR_CHECKRESERVEDLOCK:
|
||||
zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break;
|
||||
case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break;
|
||||
default: zName = "SQLITE_Unknown"; break;
|
||||
}
|
||||
return zName;
|
||||
}
|
||||
@@ -162,7 +180,8 @@ const char *sqlite3TestErrorName(int rc){
|
||||
** from sqlite3_errcode.
|
||||
*/
|
||||
int sqlite3TestErrCode(Tcl_Interp *interp, sqlite3 *db, int rc){
|
||||
if( rc!=SQLITE_MISUSE && rc!=SQLITE_OK && sqlite3_errcode(db)!=rc ){
|
||||
if( sqlite3_threadsafe()==0 && rc!=SQLITE_MISUSE && rc!=SQLITE_OK
|
||||
&& sqlite3_errcode(db)!=rc ){
|
||||
char zBuf[200];
|
||||
int r2 = sqlite3_errcode(db);
|
||||
sprintf(zBuf, "error code %s (%d) does not match sqlite3_errcode %s (%d)",
|
||||
@@ -315,6 +334,53 @@ static int test_exec_printf(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_exec_hex DB HEX
|
||||
**
|
||||
** Invoke the sqlite3_exec() on a string that is obtained by translating
|
||||
** HEX into ASCII. Most characters are translated as is. %HH becomes
|
||||
** a hex character.
|
||||
*/
|
||||
static int test_exec_hex(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
sqlite3 *db;
|
||||
Tcl_DString str;
|
||||
int rc, i, j;
|
||||
char *zErr = 0;
|
||||
char *zHex;
|
||||
char zSql[500];
|
||||
char zBuf[30];
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" DB HEX", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
|
||||
zHex = argv[2];
|
||||
for(i=j=0; i<sizeof(zSql) && zHex[j]; i++, j++){
|
||||
if( zHex[j]=='%' && zHex[j+2] && zHex[j+2] ){
|
||||
zSql[i] = (testHexToInt(zHex[j+1])<<4) + testHexToInt(zHex[j+2]);
|
||||
j += 2;
|
||||
}else{
|
||||
zSql[i] = zHex[j];
|
||||
}
|
||||
}
|
||||
zSql[i] = 0;
|
||||
Tcl_DStringInit(&str);
|
||||
rc = sqlite3_exec(db, zSql, exec_printf_cb, &str, &zErr);
|
||||
sprintf(zBuf, "%d", rc);
|
||||
Tcl_AppendElement(interp, zBuf);
|
||||
Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr);
|
||||
Tcl_DStringFree(&str);
|
||||
if( zErr ) sqlite3_free(zErr);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: db_enter DB
|
||||
** db_leave DB
|
||||
@@ -929,7 +995,6 @@ static int test_create_function(
|
||||
){
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
extern void Md5_Register(sqlite3*);
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
@@ -1040,9 +1105,12 @@ static void legacyCountStep(
|
||||
){
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
static void legacyCountFinalize(sqlite3_context *context){
|
||||
sqlite3_result_int(context, sqlite3_aggregate_count(context));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_create_aggregate DB
|
||||
@@ -1083,11 +1151,13 @@ static int test_create_aggregate(
|
||||
rc = sqlite3_create_function(db, "x_count", 1, SQLITE_UTF8, 0, 0,
|
||||
t1CountStep,t1CountFinalize);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "legacy_count", 0, SQLITE_ANY, 0, 0,
|
||||
legacyCountStep, legacyCountFinalize
|
||||
);
|
||||
}
|
||||
#endif
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
|
||||
return TCL_OK;
|
||||
@@ -1189,6 +1259,39 @@ static int sqlite3_mprintf_int64(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_mprintf_long FORMAT INTEGER INTEGER INTEGER
|
||||
**
|
||||
** Call mprintf with three long integer arguments. This might be the
|
||||
** same as sqlite3_mprintf_int or sqlite3_mprintf_int64, depending on
|
||||
** platform.
|
||||
*/
|
||||
static int sqlite3_mprintf_long(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
int i;
|
||||
long int a[3];
|
||||
int b[3];
|
||||
char *z;
|
||||
if( argc!=5 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" FORMAT INT INT INT\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for(i=2; i<5; i++){
|
||||
if( Tcl_GetInt(interp, argv[i], &b[i-2]) ) return TCL_ERROR;
|
||||
a[i-2] = (long int)b[i-2];
|
||||
a[i-2] &= (((u64)1)<<(sizeof(int)*8))-1;
|
||||
}
|
||||
z = sqlite3_mprintf(argv[1], a[0], a[1], a[2]);
|
||||
Tcl_AppendResult(interp, z, 0);
|
||||
sqlite3_free(z);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_mprintf_str FORMAT INTEGER INTEGER STRING
|
||||
**
|
||||
@@ -1369,7 +1472,7 @@ static int sqlite3_mprintf_hexdouble(
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_enable_shared_cache BOOLEAN
|
||||
** Usage: sqlite3_enable_shared_cache ?BOOLEAN?
|
||||
**
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
@@ -1382,20 +1485,22 @@ static int test_enable_shared(
|
||||
int rc;
|
||||
int enable;
|
||||
int ret = 0;
|
||||
extern int sqlite3SharedCacheEnabled;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
|
||||
if( objc!=2 && objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?BOOLEAN?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
ret = sqlite3SharedCacheEnabled;
|
||||
rc = sqlite3_enable_shared_cache(enable);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrStr(rc), TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
ret = sqlite3GlobalConfig.sharedCacheEnabled;
|
||||
|
||||
if( objc==2 ){
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_enable_shared_cache(enable);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrStr(rc), TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ret));
|
||||
return TCL_OK;
|
||||
@@ -1558,7 +1663,7 @@ static int test_blob_read(
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_write CHANNEL OFFSET DATA
|
||||
** sqlite3_blob_write CHANNEL OFFSET DATA ?NDATA?
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_write() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
@@ -1587,16 +1692,13 @@ static int test_blob_write(
|
||||
unsigned char *zBuf;
|
||||
int nBuf;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA");
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA ?NDATA?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), ¬Used);
|
||||
if( !channel
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
|| iOffset<0
|
||||
){
|
||||
if( !channel || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -1604,6 +1706,9 @@ static int test_blob_write(
|
||||
pBlob = *((sqlite3_blob **)instanceData);
|
||||
|
||||
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
|
||||
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
|
||||
@@ -1911,6 +2016,50 @@ static int test_finalize(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_stmt_status STMT CODE RESETFLAG
|
||||
**
|
||||
** Get the value of a status counter from a statement.
|
||||
*/
|
||||
static int test_stmt_status(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int iValue;
|
||||
int i, op, resetFlag;
|
||||
const char *zOpName;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
static const struct {
|
||||
const char *zName;
|
||||
int op;
|
||||
} aOp[] = {
|
||||
{ "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP },
|
||||
{ "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT },
|
||||
};
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
zOpName = Tcl_GetString(objv[2]);
|
||||
for(i=0; i<ArraySize(aOp); i++){
|
||||
if( strcmp(aOp[i].zName, zOpName)==0 ){
|
||||
op = aOp[i].op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i>=ArraySize(aOp) ){
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &op) ) return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[3], &resetFlag) ) return TCL_ERROR;
|
||||
iValue = sqlite3_stmt_status(pStmt, op, resetFlag);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iValue));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_next_stmt DB STMT
|
||||
**
|
||||
@@ -1989,6 +2138,7 @@ static int test_expired(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_stmt *pStmt;
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
@@ -1997,6 +2147,7 @@ static int test_expired(
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(sqlite3_expired(pStmt)));
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -2011,6 +2162,7 @@ static int test_transfer_bind(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_stmt *pStmt1, *pStmt2;
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
@@ -2021,6 +2173,7 @@ static int test_transfer_bind(
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[2]), &pStmt2)) return TCL_ERROR;
|
||||
Tcl_SetObjResult(interp,
|
||||
Tcl_NewIntObj(sqlite3_transfer_bindings(pStmt1,pStmt2)));
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -3010,6 +3163,33 @@ static int test_sleep(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_extended_errcode DB
|
||||
**
|
||||
** Return the string representation of the most recent sqlite3_* API
|
||||
** error code. e.g. "SQLITE_ERROR".
|
||||
*/
|
||||
static int test_ex_errcode(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_extended_errcode(db);
|
||||
Tcl_AppendResult(interp, (char *)t1ErrorName(rc), 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_errcode DB
|
||||
**
|
||||
@@ -3024,7 +3204,6 @@ static int test_errcode(
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
char zBuf[30];
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
@@ -3033,17 +3212,12 @@ static int test_errcode(
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_errcode(db);
|
||||
if( (rc&0xff)==rc ){
|
||||
zBuf[0] = 0;
|
||||
}else{
|
||||
sprintf(zBuf,"+%d", rc>>8);
|
||||
}
|
||||
Tcl_AppendResult(interp, (char *)t1ErrorName(rc), zBuf, 0);
|
||||
Tcl_AppendResult(interp, (char *)t1ErrorName(rc), 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: test_errmsg DB
|
||||
** Usage: sqlite3_errmsg DB
|
||||
**
|
||||
** Returns the UTF-8 representation of the error message string for the
|
||||
** most recent sqlite3_* API call.
|
||||
@@ -3105,7 +3279,7 @@ static int test_errmsg16(
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_prepare DB sql bytes tailvar
|
||||
** Usage: sqlite3_prepare DB sql bytes ?tailvar?
|
||||
**
|
||||
** Compile up to <bytes> bytes of the supplied SQL string <sql> using
|
||||
** database handle <DB>. The parameter <tailval> is the name of a global
|
||||
@@ -3126,18 +3300,18 @@ static int test_prepare(
|
||||
char zBuf[50];
|
||||
int rc;
|
||||
|
||||
if( objc!=5 ){
|
||||
if( objc!=5 && objc!=4 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
Tcl_GetString(objv[0]), " DB sql bytes ?tailvar?", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare(db, zSql, bytes, &pStmt, &zTail);
|
||||
rc = sqlite3_prepare(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( zTail ){
|
||||
if( zTail && objc>=5 ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (zTail-zSql);
|
||||
}
|
||||
@@ -3161,7 +3335,7 @@ static int test_prepare(
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_prepare_v2 DB sql bytes tailvar
|
||||
** Usage: sqlite3_prepare_v2 DB sql bytes ?tailvar?
|
||||
**
|
||||
** Compile up to <bytes> bytes of the supplied SQL string <sql> using
|
||||
** database handle <DB>. The parameter <tailval> is the name of a global
|
||||
@@ -3182,7 +3356,7 @@ static int test_prepare_v2(
|
||||
char zBuf[50];
|
||||
int rc;
|
||||
|
||||
if( objc!=5 ){
|
||||
if( objc!=5 && objc!=4 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
return TCL_ERROR;
|
||||
@@ -3191,10 +3365,10 @@ static int test_prepare_v2(
|
||||
zSql = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
|
||||
rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0);
|
||||
assert(rc==SQLITE_OK || pStmt==0);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( zTail ){
|
||||
if( zTail && objc>=5 ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (zTail-zSql);
|
||||
}
|
||||
@@ -3280,30 +3454,32 @@ static int test_prepare16(
|
||||
int bytes; /* The integer specified as arg 3 */
|
||||
int objlen; /* The byte-array length of arg 2 */
|
||||
|
||||
if( objc!=5 ){
|
||||
if( objc!=5 && objc!=4 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
Tcl_GetString(objv[0]), " DB sql bytes ?tailvar?", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare16(db, zSql, bytes, &pStmt, &zTail);
|
||||
rc = sqlite3_prepare16(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( rc ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
if( objc>=5 ){
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
}
|
||||
pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
|
||||
Tcl_IncrRefCount(pTail);
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
|
||||
Tcl_DecrRefCount(pTail);
|
||||
}
|
||||
pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
|
||||
Tcl_IncrRefCount(pTail);
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
|
||||
Tcl_DecrRefCount(pTail);
|
||||
|
||||
if( pStmt ){
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
|
||||
@@ -3314,7 +3490,7 @@ static int test_prepare16(
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_prepare16_v2 DB sql bytes tailvar
|
||||
** Usage: sqlite3_prepare16_v2 DB sql bytes ?tailvar?
|
||||
**
|
||||
** Compile up to <bytes> bytes of the supplied SQL string <sql> using
|
||||
** database handle <DB>. The parameter <tailval> is the name of a global
|
||||
@@ -3338,30 +3514,32 @@ static int test_prepare16_v2(
|
||||
int bytes; /* The integer specified as arg 3 */
|
||||
int objlen; /* The byte-array length of arg 2 */
|
||||
|
||||
if( objc!=5 ){
|
||||
if( objc!=5 && objc!=4 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
Tcl_GetString(objv[0]), " DB sql bytes ?tailvar?", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare16_v2(db, zSql, bytes, &pStmt, &zTail);
|
||||
rc = sqlite3_prepare16_v2(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( rc ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
if( objc>=5 ){
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
}
|
||||
pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
|
||||
Tcl_IncrRefCount(pTail);
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
|
||||
Tcl_DecrRefCount(pTail);
|
||||
}
|
||||
pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
|
||||
Tcl_IncrRefCount(pTail);
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
|
||||
Tcl_DecrRefCount(pTail);
|
||||
|
||||
if( pStmt ){
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
|
||||
@@ -3685,9 +3863,10 @@ static int test_stmt_utf8(
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int col;
|
||||
const char *(*xFunc)(sqlite3_stmt*, int) = clientData;
|
||||
const char *(*xFunc)(sqlite3_stmt*, int);
|
||||
const char *zRet;
|
||||
|
||||
xFunc = (const char *(*)(sqlite3_stmt*, int))clientData;
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " STMT column", 0);
|
||||
@@ -3710,6 +3889,7 @@ static int test_global_recover(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_GLOBALRECOVER
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
int rc;
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
@@ -3717,6 +3897,7 @@ static int test_global_recover(
|
||||
}
|
||||
rc = sqlite3_global_recover();
|
||||
Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
|
||||
#endif
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -3739,8 +3920,9 @@ static int test_stmt_utf16(
|
||||
int col;
|
||||
Tcl_Obj *pRet;
|
||||
const void *zName16;
|
||||
const void *(*xFunc)(sqlite3_stmt*, int) = clientData;
|
||||
const void *(*xFunc)(sqlite3_stmt*, int);
|
||||
|
||||
xFunc = (const void *(*)(sqlite3_stmt*, int))clientData;
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " STMT column", 0);
|
||||
@@ -3776,8 +3958,9 @@ static int test_stmt_int(
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int col;
|
||||
int (*xFunc)(sqlite3_stmt*, int) = clientData;
|
||||
int (*xFunc)(sqlite3_stmt*, int);
|
||||
|
||||
xFunc = (int (*)(sqlite3_stmt*, int))clientData;
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " STMT column", 0);
|
||||
@@ -3817,7 +4000,7 @@ static int sqlite_set_magic(
|
||||
db->magic = SQLITE_MAGIC_BUSY;
|
||||
}else if( strcmp(argv[2], "SQLITE_MAGIC_ERROR")==0 ){
|
||||
db->magic = SQLITE_MAGIC_ERROR;
|
||||
}else if( Tcl_GetInt(interp, argv[2], &db->magic) ){
|
||||
}else if( Tcl_GetInt(interp, argv[2], (int*)&db->magic) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
@@ -4091,11 +4274,12 @@ static int test_thread_cleanup(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_thread_cleanup();
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_pager_refcounts DB
|
||||
**
|
||||
@@ -4367,9 +4551,110 @@ static int file_control_test(
|
||||
assert( rc==SQLITE_ERROR );
|
||||
rc = sqlite3_file_control(db, "temp", -1, &iArg);
|
||||
assert( rc==SQLITE_ERROR );
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_lasterrno_test DB
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control interface and
|
||||
** verifies correct operation of the SQLITE_LAST_ERRNO verb.
|
||||
*/
|
||||
static int file_control_lasterrno_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 */
|
||||
){
|
||||
int iArg = 0;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_LAST_ERRNO, &iArg);
|
||||
if( rc ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( iArg!=0 ) {
|
||||
Tcl_AppendResult(interp, "Unexpected non-zero errno: ",
|
||||
Tcl_GetStringFromObj(Tcl_NewIntObj(iArg), 0), " ", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_lockproxy_test DB
|
||||
**
|
||||
** 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_lockproxy_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 *db;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
# if defined(__APPLE__)
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 1
|
||||
# else
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 0
|
||||
# endif
|
||||
#endif
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
||||
{
|
||||
char *proxyPath = "test.proxy";
|
||||
char *testPath;
|
||||
int rc;
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath);
|
||||
if( rc ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_GET_LOCKPROXYFILE, &testPath);
|
||||
if( strncmp(proxyPath,testPath,11) ){
|
||||
Tcl_AppendResult(interp, "Lock proxy file did not match the "
|
||||
"previously assigned value", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( rc ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath);
|
||||
if( rc ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_vfs_list
|
||||
**
|
||||
@@ -4499,6 +4784,38 @@ static int reset_prng_state(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: pcache_stats
|
||||
*/
|
||||
static int test_pcache_stats(
|
||||
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 */
|
||||
){
|
||||
int nMin;
|
||||
int nMax;
|
||||
int nCurrent;
|
||||
int nRecyclable;
|
||||
Tcl_Obj *pRet;
|
||||
|
||||
sqlite3PcacheStats(&nCurrent, &nMax, &nMin, &nRecyclable);
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("current", -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCurrent));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("max", -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nMax));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("min", -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nMin));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("recyclable", -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nRecyclable));
|
||||
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
@@ -4509,6 +4826,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
extern int sqlite3_open_file_count;
|
||||
extern int sqlite3_sort_count;
|
||||
extern int sqlite3_current_time;
|
||||
#if SQLITE_OS_UNIX && defined(__APPLE__)
|
||||
extern int sqlite3_hostid_num;
|
||||
#endif
|
||||
extern int sqlite3_max_blobsize;
|
||||
extern int sqlite3BtreeSharedCacheReport(void*,
|
||||
Tcl_Interp*,int,Tcl_Obj*CONST*);
|
||||
@@ -4520,6 +4840,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "db_leave", (Tcl_CmdProc*)db_leave },
|
||||
{ "sqlite3_mprintf_int", (Tcl_CmdProc*)sqlite3_mprintf_int },
|
||||
{ "sqlite3_mprintf_int64", (Tcl_CmdProc*)sqlite3_mprintf_int64 },
|
||||
{ "sqlite3_mprintf_long", (Tcl_CmdProc*)sqlite3_mprintf_long },
|
||||
{ "sqlite3_mprintf_str", (Tcl_CmdProc*)sqlite3_mprintf_str },
|
||||
{ "sqlite3_snprintf_str", (Tcl_CmdProc*)sqlite3_snprintf_str },
|
||||
{ "sqlite3_mprintf_stronly", (Tcl_CmdProc*)sqlite3_mprintf_stronly},
|
||||
@@ -4531,6 +4852,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_snprintf_int", (Tcl_CmdProc*)test_snprintf_int },
|
||||
{ "sqlite3_last_insert_rowid", (Tcl_CmdProc*)test_last_rowid },
|
||||
{ "sqlite3_exec_printf", (Tcl_CmdProc*)test_exec_printf },
|
||||
{ "sqlite3_exec_hex", (Tcl_CmdProc*)test_exec_hex },
|
||||
{ "sqlite3_exec", (Tcl_CmdProc*)test_exec },
|
||||
{ "sqlite3_exec_nr", (Tcl_CmdProc*)test_exec_nr },
|
||||
#ifndef SQLITE_OMIT_GET_TABLE
|
||||
@@ -4575,6 +4897,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_clear_bindings", test_clear_bindings, 0},
|
||||
{ "sqlite3_sleep", test_sleep, 0},
|
||||
{ "sqlite3_errcode", test_errcode ,0 },
|
||||
{ "sqlite3_extended_errcode", test_ex_errcode ,0 },
|
||||
{ "sqlite3_errmsg", test_errmsg ,0 },
|
||||
{ "sqlite3_errmsg16", test_errmsg16 ,0 },
|
||||
{ "sqlite3_open", test_open ,0 },
|
||||
@@ -4587,6 +4910,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_prepare_tkt3134", test_prepare_tkt3134, 0},
|
||||
{ "sqlite3_prepare16_v2", test_prepare16_v2 ,0 },
|
||||
{ "sqlite3_finalize", test_finalize ,0 },
|
||||
{ "sqlite3_stmt_status", test_stmt_status ,0 },
|
||||
{ "sqlite3_reset", test_reset ,0 },
|
||||
{ "sqlite3_expired", test_expired ,0 },
|
||||
{ "sqlite3_transfer_bindings", test_transfer_bind ,0 },
|
||||
@@ -4615,32 +4939,32 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_column_blob", test_column_blob ,0 },
|
||||
{ "sqlite3_column_double", test_column_double ,0 },
|
||||
{ "sqlite3_column_int64", test_column_int64 ,0 },
|
||||
{ "sqlite3_column_text", test_stmt_utf8, sqlite3_column_text },
|
||||
{ "sqlite3_column_name", test_stmt_utf8, sqlite3_column_name },
|
||||
{ "sqlite3_column_int", test_stmt_int, sqlite3_column_int },
|
||||
{ "sqlite3_column_bytes", test_stmt_int, sqlite3_column_bytes },
|
||||
{ "sqlite3_column_text", test_stmt_utf8, (void*)sqlite3_column_text },
|
||||
{ "sqlite3_column_name", test_stmt_utf8, (void*)sqlite3_column_name },
|
||||
{ "sqlite3_column_int", test_stmt_int, (void*)sqlite3_column_int },
|
||||
{ "sqlite3_column_bytes", test_stmt_int, (void*)sqlite3_column_bytes},
|
||||
#ifndef SQLITE_OMIT_DECLTYPE
|
||||
{ "sqlite3_column_decltype", test_stmt_utf8, sqlite3_column_decltype },
|
||||
{ "sqlite3_column_decltype",test_stmt_utf8,(void*)sqlite3_column_decltype},
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
{ "sqlite3_column_database_name", test_stmt_utf8, sqlite3_column_database_name},
|
||||
{ "sqlite3_column_table_name", test_stmt_utf8, sqlite3_column_table_name},
|
||||
{ "sqlite3_column_origin_name", test_stmt_utf8, sqlite3_column_origin_name},
|
||||
{ "sqlite3_column_database_name",test_stmt_utf8,(void*)sqlite3_column_database_name},
|
||||
{ "sqlite3_column_table_name",test_stmt_utf8,(void*)sqlite3_column_table_name},
|
||||
{ "sqlite3_column_origin_name",test_stmt_utf8,(void*)sqlite3_column_origin_name},
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
{ "sqlite3_column_bytes16", test_stmt_int, sqlite3_column_bytes16 },
|
||||
{ "sqlite3_column_text16", test_stmt_utf16, sqlite3_column_text16 },
|
||||
{ "sqlite3_column_name16", test_stmt_utf16, sqlite3_column_name16 },
|
||||
{ "sqlite3_column_bytes16", test_stmt_int, (void*)sqlite3_column_bytes16 },
|
||||
{ "sqlite3_column_text16", test_stmt_utf16, (void*)sqlite3_column_text16},
|
||||
{ "sqlite3_column_name16", test_stmt_utf16, (void*)sqlite3_column_name16},
|
||||
{ "add_alignment_test_collations", add_alignment_test_collations, 0 },
|
||||
#ifndef SQLITE_OMIT_DECLTYPE
|
||||
{ "sqlite3_column_decltype16", test_stmt_utf16, sqlite3_column_decltype16},
|
||||
{ "sqlite3_column_decltype16",test_stmt_utf16,(void*)sqlite3_column_decltype16},
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
{"sqlite3_column_database_name16",
|
||||
test_stmt_utf16, sqlite3_column_database_name16},
|
||||
{"sqlite3_column_table_name16", test_stmt_utf16, sqlite3_column_table_name16},
|
||||
{"sqlite3_column_origin_name16", test_stmt_utf16, sqlite3_column_origin_name16},
|
||||
{"sqlite3_column_table_name16", test_stmt_utf16, (void*)sqlite3_column_table_name16},
|
||||
{"sqlite3_column_origin_name16", test_stmt_utf16, (void*)sqlite3_column_origin_name16},
|
||||
#endif
|
||||
#endif
|
||||
{ "sqlite3_create_collation_v2", test_create_collation_v2, 0 },
|
||||
@@ -4651,6 +4975,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "vfs_unregister_all", vfs_unregister_all, 0 },
|
||||
{ "vfs_reregister_all", vfs_reregister_all, 0 },
|
||||
{ "file_control_test", file_control_test, 0 },
|
||||
{ "file_control_lasterrno_test", file_control_lasterrno_test, 0 },
|
||||
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
|
||||
{ "sqlite3_vfs_list", vfs_list, 0 },
|
||||
|
||||
/* Functions from os.h */
|
||||
@@ -4673,6 +4999,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_blob_read", test_blob_read, 0 },
|
||||
{ "sqlite3_blob_write", test_blob_write, 0 },
|
||||
#endif
|
||||
{ "pcache_stats", test_pcache_stats, 0 },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
int i;
|
||||
@@ -4683,8 +5010,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
extern int sqlite3_pager_readdb_count;
|
||||
extern int sqlite3_pager_writedb_count;
|
||||
extern int sqlite3_pager_writej_count;
|
||||
extern int sqlite3_pager_pgfree_count;
|
||||
#if SQLITE_OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
|
||||
#if defined(__linux__) && defined(SQLITE_TEST) && SQLITE_THREADSAFE
|
||||
extern int threadsOverrideEachOthersLocks;
|
||||
#endif
|
||||
#if SQLITE_OS_WIN
|
||||
@@ -4696,9 +5022,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
extern int sqlite3VdbeAddopTrace;
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
extern int sqlite3_enable_in_opt;
|
||||
extern char sqlite3_query_plan[];
|
||||
static char *query_plan = sqlite3_query_plan;
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
extern int sqlite3_fts3_enable_parentheses;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
@@ -4722,6 +5050,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
(char*)&sqlite3_open_file_count, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_current_time",
|
||||
(char*)&sqlite3_current_time, TCL_LINK_INT);
|
||||
#if SQLITE_OS_UNIX && defined(__APPLE__)
|
||||
Tcl_LinkVar(interp, "sqlite_hostid_num",
|
||||
(char*)&sqlite3_hostid_num, TCL_LINK_INT);
|
||||
#endif
|
||||
Tcl_LinkVar(interp, "sqlite3_xferopt_count",
|
||||
(char*)&sqlite3_xferopt_count, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite3_pager_readdb_count",
|
||||
@@ -4730,13 +5062,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
(char*)&sqlite3_pager_writedb_count, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite3_pager_writej_count",
|
||||
(char*)&sqlite3_pager_writej_count, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite3_pager_pgfree_count",
|
||||
(char*)&sqlite3_pager_pgfree_count, TCL_LINK_INT);
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
Tcl_LinkVar(interp, "unaligned_string_counter",
|
||||
(char*)&unaligned_string_counter, TCL_LINK_INT);
|
||||
#endif
|
||||
#if SQLITE_OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
|
||||
#if defined(__linux__) && defined(SQLITE_TEST) && SQLITE_THREADSAFE
|
||||
Tcl_LinkVar(interp, "threadsOverrideEachOthersLocks",
|
||||
(char*)&threadsOverrideEachOthersLocks, TCL_LINK_INT);
|
||||
#endif
|
||||
@@ -4776,9 +5106,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
(char*)&sqlite3_sync_count, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_fullsync_count",
|
||||
(char*)&sqlite3_fullsync_count, TCL_LINK_INT);
|
||||
#ifdef SQLITE_TEST
|
||||
Tcl_LinkVar(interp, "sqlite_enable_in_opt",
|
||||
(char*)&sqlite3_enable_in_opt, TCL_LINK_INT);
|
||||
#if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_TEST)
|
||||
Tcl_LinkVar(interp, "sqlite_fts3_enable_parentheses",
|
||||
(char*)&sqlite3_fts3_enable_parentheses, TCL_LINK_INT);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
+36
-21
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test2.c,v 1.59 2008/07/12 14:52:20 drh Exp $
|
||||
** $Id: test2.c,v 1.70 2009/02/05 16:31:46 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -165,7 +165,7 @@ static int pager_commit(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TestTextToPtr(argv[1]);
|
||||
rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0, 0);
|
||||
rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
@@ -197,7 +197,7 @@ static int pager_stmt_begin(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TestTextToPtr(argv[1]);
|
||||
rc = sqlite3PagerStmtBegin(pPager);
|
||||
rc = sqlite3PagerOpenSavepoint(pPager, 1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
@@ -224,7 +224,8 @@ static int pager_stmt_rollback(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TestTextToPtr(argv[1]);
|
||||
rc = sqlite3PagerStmtRollback(pPager);
|
||||
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, 0);
|
||||
sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
@@ -251,7 +252,7 @@ static int pager_stmt_commit(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TestTextToPtr(argv[1]);
|
||||
rc = sqlite3PagerStmtCommit(pPager);
|
||||
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
@@ -392,7 +393,6 @@ static int pager_truncate(
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
int pgno;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
@@ -401,11 +401,7 @@ static int pager_truncate(
|
||||
}
|
||||
pPager = sqlite3TestTextToPtr(argv[1]);
|
||||
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
|
||||
rc = sqlite3PagerTruncate(pPager, pgno);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3PagerTruncateImage(pPager, pgno);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -422,18 +418,13 @@ static int page_unref(
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
DbPage *pPage;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" PAGE\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
|
||||
rc = sqlite3PagerUnref(pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3PagerUnref(pPage);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -565,6 +556,29 @@ static int fake_big_file(
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** test_control_pending_byte PENDING_BYTE
|
||||
**
|
||||
** Set the PENDING_BYTE using the sqlite3_test_control() interface.
|
||||
*/
|
||||
static int testPendingByte(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
int pbyte;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" PENDING-BYTE\"", (void*)0);
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[1], &pbyte) ) return TCL_ERROR;
|
||||
rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3BitvecBuiltinTest SIZE PROGRAM
|
||||
**
|
||||
@@ -588,10 +602,10 @@ static int testBitvecBuiltinTest(
|
||||
if( Tcl_GetInt(interp, argv[1], &sz) ) return TCL_ERROR;
|
||||
z = argv[2];
|
||||
while( nProg<99 && *z ){
|
||||
while( *z && !isdigit(*z) ){ z++; }
|
||||
while( *z && !sqlite3Isdigit(*z) ){ z++; }
|
||||
if( *z==0 ) break;
|
||||
aProg[nProg++] = atoi(z);
|
||||
while( isdigit(*z) ){ z++; }
|
||||
while( sqlite3Isdigit(*z) ){ z++; }
|
||||
}
|
||||
aProg[nProg] = 0;
|
||||
rc = sqlite3_test_control(SQLITE_TESTCTRL_BITVEC_TEST, sz, aProg);
|
||||
@@ -633,7 +647,8 @@ int Sqlitetest2_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
{ "fake_big_file", (Tcl_CmdProc*)fake_big_file },
|
||||
#endif
|
||||
{ "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest},
|
||||
{ "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest },
|
||||
{ "sqlite3_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
@@ -652,7 +667,7 @@ int Sqlitetest2_Init(Tcl_Interp *interp){
|
||||
Tcl_LinkVar(interp, "sqlite_diskfull",
|
||||
(char*)&sqlite3_diskfull, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_pending_byte",
|
||||
(char*)&sqlite3_pending_byte, TCL_LINK_INT);
|
||||
(char*)&sqlite3PendingByte, TCL_LINK_INT | TCL_LINK_READ_ONLY);
|
||||
Tcl_LinkVar(interp, "sqlite_pager_n_sort_bucket",
|
||||
(char*)&sqlite3_pager_n_sort_bucket, TCL_LINK_INT);
|
||||
return TCL_OK;
|
||||
|
||||
+4
-4
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test3.c,v 1.100 2008/07/12 14:52:20 drh Exp $
|
||||
** $Id: test3.c,v 1.102 2008/10/27 13:59:34 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
@@ -389,7 +389,7 @@ static int btree_clear_table(
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeClearTable(pBt, iTable);
|
||||
rc = sqlite3BtreeClearTable(pBt, iTable, 0);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
@@ -697,9 +697,9 @@ static int btree_move_to(
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3BtreeMoveto(pCur, 0, 0, iKey, 0, &res);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, iKey, 0, &res);
|
||||
}else{
|
||||
rc = sqlite3BtreeMoveto(pCur, argv[2], 0, strlen(argv[2]), 0, &res);
|
||||
rc = sqlite3BtreeMoveto(pCur, argv[2], strlen(argv[2]), 0, &res);
|
||||
}
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
if( rc ){
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user