Require sqlite as a system dependency
This purges sqlite from our tree and requires it to be present on the system for building and running FreeSWITCH. FS-353
This commit is contained in:
parent
8574988c3a
commit
cde20f6fe6
18
Makefile.am
18
Makefile.am
|
@ -83,7 +83,6 @@ install-recursive: install-libLTLIBRARIES install-binPROGRAMS
|
|||
CORE_CFLAGS = $(AM_LIBAPR_CFLAGS) $(AM_LIBAPR_CPPFLAGS)
|
||||
CORE_CFLAGS += $(AM_LIBAPU_CPPFLAGS)
|
||||
CORE_CFLAGS += -I$(switch_srcdir)/libs/libtpl-1.5/src
|
||||
CORE_CFLAGS += -I$(switch_builddir)/libs/sqlite
|
||||
CORE_CFLAGS += -I$(switch_srcdir)/libs/srtp/include
|
||||
CORE_CFLAGS += -I$(switch_srcdir)/libs/srtp/crypto/include -Ilibs/srtp/crypto/include
|
||||
CORE_CFLAGS += -I$(switch_builddir)/libs/spandsp/src -I$(switch_srcdir)/libs/spandsp/src
|
||||
|
@ -91,7 +90,6 @@ CORE_CFLAGS += -I$(switch_builddir)/libs/tiff-4.0.2/libtiff -I$(switch_srcdir)/l
|
|||
|
||||
APR_LIBS = $(AM_LIBAPU_LIBS) $(AM_LIBAPR_LIBS)
|
||||
CORE_LIBS = libs/apr-util/libaprutil-1.la libs/apr/libapr-1.la
|
||||
CORE_LIBS += libs/sqlite/libsqlite3.la
|
||||
|
||||
if ENABLE_SRTP
|
||||
CORE_CFLAGS += -DENABLE_SRTP
|
||||
|
@ -118,9 +116,9 @@ libfreeswitch_spandsp_la_SOURCES = libs/spandsp/src/plc.c libs/spandsp/src/alloc
|
|||
libfreeswitch_spandsp_la_CFLAGS = -Ilibs/spandsp/src $(CORE_CFLAGS) $(AM_CFLAGS)
|
||||
CORE_LIBS+=libfreeswitch_spandsp.la
|
||||
lib_LTLIBRARIES = libfreeswitch.la
|
||||
libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(AM_CFLAGS)
|
||||
libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(AM_CFLAGS)
|
||||
libfreeswitch_la_LDFLAGS = -version-info 1:0:0 $(AM_LDFLAGS) $(PLATFORM_CORE_LDFLAGS) -no-undefined
|
||||
libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(PLATFORM_CORE_LIBS)
|
||||
libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(SQLITE_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(PLATFORM_CORE_LIBS)
|
||||
libfreeswitch_la_DEPENDENCIES = $(BUILT_SOURCES)
|
||||
|
||||
if HAVE_ODBC
|
||||
|
@ -429,17 +427,6 @@ libs/apr/libapr-1.la: libs/apr/Makefile libs/apr/.update
|
|||
libs/apr-util/libaprutil-1.la: libs/apr/libapr-1.la libs/apr-util libs/apr-util/.update
|
||||
@if [ $(MAKELEVEL) = 0 -o -z "`echo "$(MAKEARGS)" | grep "j"`" ] ; then touch $(switch_srcdir)/src/include/switch.h; cd libs/apr-util && $(MAKE) $(MFLAGS) && touch libaprutil-1.la; fi
|
||||
|
||||
libs/sqlite/libsqlite3.la: libs/sqlite libs/sqlite/Makefile libs/sqlite/.update
|
||||
touch $(switch_srcdir)/src/include/switch.h
|
||||
@cd libs/sqlite && $(MAKE) CFLAGS="$(SWITCH_AM_CFLAGS)"
|
||||
@$(TOUCH_TARGET)
|
||||
|
||||
libs/sqlite/Makefile: libs/sqlite/configure.ac
|
||||
cd libs/sqlite && autoconf
|
||||
cd libs/sqlite && ./config.status --recheck
|
||||
cd libs/sqlite && ./config.status
|
||||
@$(TOUCH_TARGET)
|
||||
|
||||
SRTP_SRC = libs/srtp/srtp/srtp.c libs/srtp/srtp/ekt.c libs/srtp/crypto/cipher/cipher.c libs/srtp/crypto/cipher/null_cipher.c \
|
||||
libs/srtp/crypto/cipher/aes.c libs/srtp/crypto/cipher/aes_icm.c \
|
||||
libs/srtp/crypto/cipher/aes_cbc.c \
|
||||
|
@ -589,7 +576,6 @@ update-clean: clean libs/openzap/Makefile python-reconf lua-reconf spandsp-recon
|
|||
cd libs/openzap && $(MAKE) clean
|
||||
cd libs/portaudio && $(MAKE) clean
|
||||
cd libs/esl && $(MAKE) clean
|
||||
cd libs/sqlite && $(MAKE) clean
|
||||
cd libs/srtp && $(MAKE) clean
|
||||
|
||||
swigall:
|
||||
|
|
|
@ -10,7 +10,7 @@ BASEDIR=`pwd`;
|
|||
LIBDIR=${BASEDIR}/libs;
|
||||
SUBDIRS="apr \
|
||||
libzrtp ilbc iksemel js js/nsprpub ldns libdingaling libsndfile sofia-sip \
|
||||
speex sqlite srtp openzap freetdm spandsp libg722_1 portaudio unimrcp tiff-4.0.2 broadvoice silk libcodec2 \
|
||||
speex srtp openzap freetdm spandsp libg722_1 portaudio unimrcp tiff-4.0.2 broadvoice silk libcodec2 \
|
||||
fs";
|
||||
|
||||
while getopts 'jhd:v' o; do
|
||||
|
|
|
@ -1075,6 +1075,7 @@ case $host in
|
|||
;;
|
||||
esac
|
||||
|
||||
PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.8.3.1])
|
||||
PKG_CHECK_MODULES([CURL], [libcurl >= 7.19])
|
||||
PKG_CHECK_MODULES([PCRE], [libpcre >= 7.8])
|
||||
PKG_CHECK_MODULES([SPEEX], [speex >= 1.2rc1 speexdsp >= 1.2rc1])
|
||||
|
@ -1524,7 +1525,6 @@ ac_configure_args="$ac_configure_args CONFIGURE_CFLAGS='$CFLAGS' CONFIGURE_CXXFL
|
|||
|
||||
# Run configure in all the subdirs
|
||||
AC_CONFIG_SUBDIRS([libs/srtp])
|
||||
AC_CONFIG_SUBDIRS([libs/sqlite])
|
||||
AC_CONFIG_SUBDIRS([libs/apr])
|
||||
AC_CONFIG_SUBDIRS([libs/apr-util])
|
||||
AC_CONFIG_SUBDIRS([libs/ilbc])
|
||||
|
|
|
@ -286,6 +286,7 @@ Build-Depends:
|
|||
libc6-dev (>= 2.11.3), make (>= 3.81),
|
||||
libpcre3-dev,
|
||||
libedit-dev (>= 2.11),
|
||||
libsqlite3-dev,
|
||||
wget, pkg-config,
|
||||
# core codecs
|
||||
libogg-dev, libspeex-dev, libspeexdsp-dev,
|
||||
|
|
|
@ -1896,11 +1896,6 @@ Files: libs/libzrtp/third_party/bnlib/test/md5.c
|
|||
Copyright: 1995 Abandoned Colin Plumb
|
||||
License: public-domain
|
||||
|
||||
Files: libs/sqlite/*
|
||||
Copyright: 2006 Abandoned D. Richard Hipp <drh@hwaci.com>
|
||||
1993 Abandoned Colin Plumb
|
||||
License: public-domain
|
||||
|
||||
Files: libs/win32/sqlite/sqlite3.[ch]
|
||||
libs/win32/sqlite/parse.c
|
||||
Copyright: 2006 Abandoned D. Richard Hipp <drh@hwaci.com>
|
||||
|
|
|
@ -86,31 +86,11 @@ Rules:
|
|||
Glob: libs/libzrtp/projects/symbian/DelayRuner.h
|
||||
Matches: Copyright\s+:\sCopyright\s\(c\)\s2010\sSoft\sIndustry
|
||||
Copyright: 2010 Soft Industry
|
||||
-
|
||||
Glob: libs/sqlite/*
|
||||
Matches: The\sauthor\sdisclaims\scopyright\sto\sthis\ssource\scode.
|
||||
Copyright: 2006 Abandoned D. Richard Hipp <drh@hwaci.com>
|
||||
License: public-domain
|
||||
-
|
||||
Glob: libs/win32/sqlite/*.[ch]
|
||||
Matches: The\sauthor\sdisclaims\scopyright\sto\sthis\ssource\scode.
|
||||
Copyright: 2006 Abandoned D. Richard Hipp <drh@hwaci.com>
|
||||
License: public-domain
|
||||
-
|
||||
Glob: libs/sqlite/src/test_md5.c
|
||||
Matches: written\sby\sColin\sPlumb\sin\s1993,\sno\scopyright\sis\sclaimed.
|
||||
Matches: This\scode\sis\sin\sthe\spublic\sdomain;\sdo\swith\sit\swhat\syou\swish.
|
||||
Copyright: 1993 Abandoned Colin Plumb
|
||||
License: public-domain
|
||||
-
|
||||
Glob: libs/sqlite/src/printf.c
|
||||
Matches: The\s\"printf\"\scode\sthat\sfollows\sdates\sfrom\sthe\s1980\'s.\s\sIt\sis\sin
|
||||
Matches: the\spublic\sdomain.
|
||||
License: public-domain
|
||||
-
|
||||
Glob: libs/sqlite/src/test_md5.c
|
||||
Matches: This\scode\sis\sin\sthe\spublic\sdomain;\sdo\swith\sit\swhat\syou\swish.
|
||||
License: public-domain
|
||||
-
|
||||
Glob: libs/libtpl-1.5/src/tpl.[ch]
|
||||
Matches: Copyright\s\(c\)\s2005-2010,\sTroy\sD.\sHanson\s+http://tpl.sourceforge.net
|
||||
|
|
|
@ -1245,7 +1245,7 @@ INCLUDE_PATH =../libs/apr ../libs/apr-util \
|
|||
../libs/libg722_1 ../libs/libnatpmp \
|
||||
../libs/libsndfile ../libs/miniupnpc \
|
||||
../libs/portaudio ../libs/sofia-sip ../libs/spandsp \
|
||||
../libs/sqlite ../libs/srtp \
|
||||
../libs/srtp \
|
||||
../libs/tiff-4.0.2 ../libs/udns \
|
||||
../libs/unimrcp ../libs/voipcodecs ../libs/win32 \
|
||||
../libs/xmlrpc-c ../libs/yaml
|
||||
|
|
|
@ -570,20 +570,6 @@ opal
|
|||
/spandsp/tests/Makefile.in
|
||||
/spandsp/src/make_cielab_luts
|
||||
/sphinxbase-*/
|
||||
/sqlite/keywordhash.h
|
||||
/sqlite/lemon
|
||||
/sqlite/lempar.c
|
||||
/sqlite/Makefile
|
||||
/sqlite/mkkeywordhash
|
||||
/sqlite/opcodes.c
|
||||
/sqlite/opcodes.h
|
||||
/sqlite/parse.c
|
||||
/sqlite/parse.h
|
||||
/sqlite/parse.h.temp
|
||||
/sqlite/parse.out
|
||||
/sqlite/parse.y
|
||||
/sqlite/sqlite3
|
||||
/sqlite/sqlite3.h
|
||||
/srtp/aes_tables
|
||||
/srtp/config_in.h
|
||||
/srtp/crypto/include/config.h
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Tue Aug 2 13:04:55 CDT 2011
|
|
@ -1,708 +0,0 @@
|
|||
#!/usr/make
|
||||
#
|
||||
# Makefile for SQLITE
|
||||
#
|
||||
# This makefile is suppose to be configured automatically using the
|
||||
# autoconf. But if that does not work for you, you can configure
|
||||
# the makefile manually. Just set the parameters below to values that
|
||||
# work well for your system.
|
||||
#
|
||||
# If the configure script does not work out-of-the-box, you might
|
||||
# be able to get it to work by giving it some hints. See the comment
|
||||
# at the beginning of configure.in for additional information.
|
||||
#
|
||||
|
||||
# The toplevel directory of the source tree. This is the directory
|
||||
# that contains this "Makefile.in" and the "configure.in" script.
|
||||
#
|
||||
TOP = @srcdir@
|
||||
|
||||
# C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
#
|
||||
BCC = @BUILD_CC@ @BUILD_CFLAGS@
|
||||
|
||||
# C Compile and options for use in building executables that
|
||||
# will run on the target platform. (BCC and TCC are usually the
|
||||
# same unless your are cross-compiling.)
|
||||
#
|
||||
TCC = @TARGET_CC@ -I. -I${TOP}/src @TARGET_CFLAGS@
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
#
|
||||
TCC += @TARGET_DEBUG@ @XTHREADCONNECT@
|
||||
|
||||
# Compiler options needed for programs that use the TCL library.
|
||||
#
|
||||
TCC += @TCL_INCLUDE_SPEC@
|
||||
|
||||
# The library that programs using TCL must link against.
|
||||
#
|
||||
LIBTCL = @TCL_LIB_SPEC@ @TCL_LIBS@
|
||||
|
||||
# Compiler options needed for programs that use the readline() library.
|
||||
#
|
||||
READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
|
||||
|
||||
# The library that programs using readline() must link against.
|
||||
#
|
||||
LIBREADLINE = @TARGET_READLINE_LIBS@
|
||||
|
||||
# Should the database engine be compiled threadsafe
|
||||
#
|
||||
TCC += -DTHREADSAFE=@THREADSAFE@
|
||||
|
||||
# The pthreads library if needed
|
||||
#
|
||||
LIBPTHREAD=@TARGET_THREAD_LIB@
|
||||
|
||||
# Do threads override each others locks by default (1), or do we test (-1)
|
||||
#
|
||||
TCC += -DSQLITE_THREAD_OVERRIDE_LOCK=@THREADSOVERRIDELOCKS@
|
||||
|
||||
# The fdatasync library
|
||||
TLIBS = @TARGET_LIBS@
|
||||
|
||||
# Flags controlling use of the in memory btree implementation
|
||||
#
|
||||
# TEMP_STORE is 0 to force temporary tables to be in a file, 1 to
|
||||
# default to file, 2 to default to memory, and 3 to force temporary
|
||||
# tables to always be in memory.
|
||||
#
|
||||
TEMP_STORE = -DTEMP_STORE=@TEMP_STORE@
|
||||
|
||||
# Version numbers and release number for the SQLite being compiled.
|
||||
#
|
||||
VERSION = @VERSION@
|
||||
VERSION_NUMBER = @VERSION_NUMBER@
|
||||
RELEASE = @RELEASE@
|
||||
|
||||
# Filename extensions
|
||||
#
|
||||
BEXE = @BUILD_EXEEXT@
|
||||
TEXE = @TARGET_EXEEXT@
|
||||
|
||||
# The following variable is "1" if the configure script was able to locate
|
||||
# the tclConfig.sh file. It is an empty string otherwise. When this
|
||||
# variable is "1", the TCL extension library (libtclsqlite3.so) is built
|
||||
# and installed.
|
||||
#
|
||||
HAVE_TCL = @HAVE_TCL@
|
||||
|
||||
# The suffix used on shared libraries. Ex: ".dll", ".so", ".dylib"
|
||||
#
|
||||
SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
|
||||
|
||||
# The directory into which to store package information for
|
||||
|
||||
# Some standard variables and programs
|
||||
#
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
libdir = @libdir@
|
||||
INSTALL = @INSTALL@
|
||||
LIBTOOL = ./libtool
|
||||
ALLOWRELEASE = @ALLOWRELEASE@
|
||||
|
||||
# libtool compile/link/install
|
||||
LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(TCC)
|
||||
LTLINK = $(LIBTOOL) --tag=CC --mode=link $(TCC)
|
||||
LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL)
|
||||
|
||||
# nawk compatible awk.
|
||||
NAWK = @AWK@
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
#TCC += -DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ = alter.lo analyze.lo attach.lo auth.lo btree.lo build.lo \
|
||||
callback.lo complete.lo date.lo \
|
||||
delete.lo expr.lo func.lo hash.lo insert.lo loadext.lo \
|
||||
main.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 table.lo tokenize.lo trigger.lo update.lo \
|
||||
util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbefifo.lo vdbemem.lo \
|
||||
where.lo utf.lo legacy.lo vtab.lo
|
||||
|
||||
# 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/btree.c \
|
||||
$(TOP)/src/btree.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/func.c \
|
||||
$(TOP)/src/hash.c \
|
||||
$(TOP)/src/hash.h \
|
||||
$(TOP)/src/insert.c \
|
||||
$(TOP)/src/legacy.c \
|
||||
$(TOP)/src/loadext.c \
|
||||
$(TOP)/src/main.c \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os_unix.c \
|
||||
$(TOP)/src/os_win.c \
|
||||
$(TOP)/src/os_os2.c \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/shell.c \
|
||||
$(TOP)/src/sqlite.h.in \
|
||||
$(TOP)/src/sqliteInt.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/vdbefifo.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.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 \
|
||||
$(TOP)/src/sqliteInt.h
|
||||
|
||||
|
||||
# Source code to the test files.
|
||||
#
|
||||
TESTSRC = \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/date.c \
|
||||
$(TOP)/src/func.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/printf.c \
|
||||
$(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/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_md5.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_tclvar.c \
|
||||
$(TOP)/src/utf.c \
|
||||
$(TOP)/src/util.c \
|
||||
$(TOP)/src/vdbe.c \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/where.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
HDR = \
|
||||
sqlite3.h \
|
||||
$(TOP)/src/btree.h \
|
||||
$(TOP)/src/hash.h \
|
||||
opcodes.h \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/sqlite3ext.h \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/vdbe.h \
|
||||
parse.h
|
||||
|
||||
# Header files used by extensions
|
||||
#
|
||||
HDR += \
|
||||
$(TOP)/ext/fts1/fts1.h \
|
||||
$(TOP)/ext/fts1/fts1_hash.h \
|
||||
$(TOP)/ext/fts1/fts1_tokenizer.h
|
||||
|
||||
# Header files used by the VDBE submodule
|
||||
#
|
||||
VDBEHDR = \
|
||||
$(HDR) \
|
||||
$(TOP)/src/vdbeInt.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.la sqlite3$(TEXE) $(HAVE_TCL:1=libtclsqlite3.la)
|
||||
|
||||
Makefile: $(TOP)/Makefile.in
|
||||
./config.status
|
||||
|
||||
# Generate the file "last_change" which contains the date of change
|
||||
# of the most recently modified source code file
|
||||
#
|
||||
last_change: $(SRC)
|
||||
cat $(SRC) | grep '$$Id: ' | sort -k 5 | tail -1 \
|
||||
| $(NAWK) '{print $$5,$$6}' >last_change
|
||||
|
||||
libsqlite3.la: $(LIBOBJ)
|
||||
$(LTLINK) -o libsqlite3.la $(LIBOBJ) $(LIBPTHREAD) \
|
||||
${ALLOWRELEASE} -rpath $(libdir) -version-info "8:6:8"
|
||||
|
||||
libtclsqlite3.la: tclsqlite.lo libsqlite3.la
|
||||
$(LTLINK) -o libtclsqlite3.la tclsqlite.lo \
|
||||
$(LIBOBJ) @TCL_STUB_LIB_SPEC@ $(LIBPTHREAD) \
|
||||
-rpath $(libdir)/sqlite \
|
||||
-version-info "8:6:8"
|
||||
|
||||
sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
|
||||
$(LTLINK) $(READLINE_FLAGS) $(LIBPTHREAD) \
|
||||
-o $@ $(TOP)/src/shell.c libsqlite3.la \
|
||||
$(LIBREADLINE) $(TLIBS)
|
||||
|
||||
# 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) parse.c opcodes.c keywordhash.h $(VDBEHDR)
|
||||
rm -rf tsrc
|
||||
mkdir -p tsrc
|
||||
cp $(SRC) $(VDBEHDR) tsrc
|
||||
rm tsrc/sqlite.h.in tsrc/parse.y
|
||||
cp parse.c opcodes.c keywordhash.h tsrc
|
||||
|
||||
# Rules to build the LEMON compiler generator
|
||||
#
|
||||
lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
||||
$(BCC) -o lemon$(BEXE) $(TOP)/tool/lemon.c
|
||||
cp $(TOP)/tool/lempar.c .
|
||||
|
||||
|
||||
# Rules to build individual files
|
||||
#
|
||||
alter.lo: $(TOP)/src/alter.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/alter.c
|
||||
|
||||
analyze.lo: $(TOP)/src/analyze.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/analyze.c
|
||||
|
||||
attach.lo: $(TOP)/src/attach.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/attach.c
|
||||
|
||||
auth.lo: $(TOP)/src/auth.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/auth.c
|
||||
|
||||
btree.lo: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
|
||||
$(LTCOMPILE) -c $(TOP)/src/btree.c
|
||||
|
||||
build.lo: $(TOP)/src/build.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/build.c
|
||||
|
||||
callback.lo: $(TOP)/src/callback.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/callback.c
|
||||
|
||||
complete.lo: $(TOP)/src/complete.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/complete.c
|
||||
|
||||
date.lo: $(TOP)/src/date.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/date.c
|
||||
|
||||
delete.lo: $(TOP)/src/delete.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/delete.c
|
||||
|
||||
expr.lo: $(TOP)/src/expr.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/expr.c
|
||||
|
||||
func.lo: $(TOP)/src/func.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/func.c
|
||||
|
||||
hash.lo: $(TOP)/src/hash.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/hash.c
|
||||
|
||||
insert.lo: $(TOP)/src/insert.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/insert.c
|
||||
|
||||
legacy.lo: $(TOP)/src/legacy.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/legacy.c
|
||||
|
||||
loadext.lo: $(TOP)/src/loadext.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/loadext.c
|
||||
|
||||
main.lo: $(TOP)/src/main.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/main.c
|
||||
|
||||
pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
|
||||
$(LTCOMPILE) -c $(TOP)/src/pager.c
|
||||
|
||||
opcodes.lo: opcodes.c
|
||||
$(LTCOMPILE) -c opcodes.c
|
||||
|
||||
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
|
||||
|
||||
os.lo: $(TOP)/src/os.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os.c
|
||||
|
||||
os_unix.lo: $(TOP)/src/os_unix.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os_unix.c
|
||||
|
||||
os_win.lo: $(TOP)/src/os_win.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os_win.c
|
||||
|
||||
os_os2.lo: $(TOP)/src/os_os2.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/os_os2.c
|
||||
|
||||
parse.lo: parse.c $(HDR)
|
||||
$(LTCOMPILE) -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) parse.y
|
||||
mv parse.h parse.h.temp
|
||||
awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
pragma.lo: $(TOP)/src/pragma.c $(HDR)
|
||||
$(LTCOMPILE) -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
|
||||
|
||||
random.lo: $(TOP)/src/random.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/random.c
|
||||
|
||||
select.lo: $(TOP)/src/select.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/select.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
|
||||
|
||||
tclsqlite.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
tokenize.lo: $(TOP)/src/tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/tokenize.c
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash$(BEXE) $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash$(BEXE) >keywordhash.h
|
||||
|
||||
trigger.lo: $(TOP)/src/trigger.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/trigger.c
|
||||
|
||||
update.lo: $(TOP)/src/update.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/update.c
|
||||
|
||||
utf.lo: $(TOP)/src/utf.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/utf.c
|
||||
|
||||
util.lo: $(TOP)/src/util.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/util.c
|
||||
|
||||
vacuum.lo: $(TOP)/src/vacuum.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vacuum.c
|
||||
|
||||
vdbe.lo: $(TOP)/src/vdbe.c $(VDBEHDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbe.c
|
||||
|
||||
vdbeapi.lo: $(TOP)/src/vdbeapi.c $(VDBEHDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbeapi.c
|
||||
|
||||
vdbeaux.lo: $(TOP)/src/vdbeaux.c $(VDBEHDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbeaux.c
|
||||
|
||||
vdbefifo.lo: $(TOP)/src/vdbefifo.c $(VDBEHDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbefifo.c
|
||||
|
||||
vdbemem.lo: $(TOP)/src/vdbemem.c $(VDBEHDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vdbemem.c
|
||||
|
||||
vtab.lo: $(TOP)/src/vtab.c $(VDBEHDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/vtab.c
|
||||
|
||||
where.lo: $(TOP)/src/where.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/where.c
|
||||
|
||||
tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
tclsqlite-stubs.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DTCL_USE_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
tclsqlite3: tclsqlite-shell.lo libsqlite3.la
|
||||
$(LTLINK) -o tclsqlite3 tclsqlite-shell.lo \
|
||||
libsqlite3.la $(LIBTCL)
|
||||
|
||||
testfixture$(TEXE): $(TOP)/src/tclsqlite.c libsqlite3.la $(TESTSRC)
|
||||
$(LTLINK) -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
|
||||
-DSQLITE_NO_SYNC=1 $(TEMP_STORE) \
|
||||
-o testfixture $(TESTSRC) $(TOP)/src/tclsqlite.c \
|
||||
libsqlite3.la $(LIBTCL)
|
||||
|
||||
|
||||
fulltest: testfixture$(TEXE) sqlite3$(TEXE)
|
||||
./testfixture $(TOP)/test/all.test
|
||||
|
||||
test: testfixture$(TEXE) sqlite3$(TEXE)
|
||||
./testfixture $(TOP)/test/quick.test
|
||||
|
||||
sqlite3_analyzer$(TEXE): $(TOP)/src/tclsqlite.c libtclsqlite3.la \
|
||||
$(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
|
||||
$(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 $(TEMP_STORE)\
|
||||
-o sqlite3_analyzer$(EXE) $(TESTSRC) $(TOP)/src/tclsqlite.c \
|
||||
libtclsqlite3.la $(LIBTCL)
|
||||
|
||||
# Rules used to build documentation
|
||||
#
|
||||
arch.html: $(TOP)/www/arch.tcl
|
||||
tclsh $(TOP)/www/arch.tcl >arch.html
|
||||
|
||||
arch2.gif: $(TOP)/www/arch2.gif
|
||||
cp $(TOP)/www/arch2.gif .
|
||||
|
||||
autoinc.html: $(TOP)/www/autoinc.tcl
|
||||
tclsh $(TOP)/www/autoinc.tcl >autoinc.html
|
||||
|
||||
c_interface.html: $(TOP)/www/c_interface.tcl
|
||||
tclsh $(TOP)/www/c_interface.tcl >c_interface.html
|
||||
|
||||
capi3.html: $(TOP)/www/capi3.tcl
|
||||
tclsh $(TOP)/www/capi3.tcl >capi3.html
|
||||
|
||||
capi3ref.html: $(TOP)/www/capi3ref.tcl
|
||||
tclsh $(TOP)/www/capi3ref.tcl >capi3ref.html
|
||||
|
||||
changes.html: $(TOP)/www/changes.tcl
|
||||
tclsh $(TOP)/www/changes.tcl >changes.html
|
||||
|
||||
compile.html: $(TOP)/www/compile.tcl
|
||||
tclsh $(TOP)/www/compile.tcl >compile.html
|
||||
|
||||
copyright.html: $(TOP)/www/copyright.tcl
|
||||
tclsh $(TOP)/www/copyright.tcl >copyright.html
|
||||
|
||||
copyright-release.html: $(TOP)/www/copyright-release.html
|
||||
cp $(TOP)/www/copyright-release.html .
|
||||
|
||||
copyright-release.pdf: $(TOP)/www/copyright-release.pdf
|
||||
cp $(TOP)/www/copyright-release.pdf .
|
||||
|
||||
common.tcl: $(TOP)/www/common.tcl
|
||||
cp $(TOP)/www/common.tcl .
|
||||
|
||||
conflict.html: $(TOP)/www/conflict.tcl
|
||||
tclsh $(TOP)/www/conflict.tcl >conflict.html
|
||||
|
||||
datatypes.html: $(TOP)/www/datatypes.tcl
|
||||
tclsh $(TOP)/www/datatypes.tcl >datatypes.html
|
||||
|
||||
datatype3.html: $(TOP)/www/datatype3.tcl
|
||||
tclsh $(TOP)/www/datatype3.tcl >datatype3.html
|
||||
|
||||
docs.html: $(TOP)/www/docs.tcl
|
||||
tclsh $(TOP)/www/docs.tcl >docs.html
|
||||
|
||||
download.html: $(TOP)/www/download.tcl
|
||||
mkdir -p doc
|
||||
tclsh $(TOP)/www/download.tcl >download.html
|
||||
|
||||
faq.html: $(TOP)/www/faq.tcl
|
||||
tclsh $(TOP)/www/faq.tcl >faq.html
|
||||
|
||||
fileformat.html: $(TOP)/www/fileformat.tcl
|
||||
tclsh $(TOP)/www/fileformat.tcl >fileformat.html
|
||||
|
||||
formatchng.html: $(TOP)/www/formatchng.tcl
|
||||
tclsh $(TOP)/www/formatchng.tcl >formatchng.html
|
||||
|
||||
index.html: $(TOP)/www/index.tcl last_change
|
||||
tclsh $(TOP)/www/index.tcl >index.html
|
||||
|
||||
lang.html: $(TOP)/www/lang.tcl
|
||||
tclsh $(TOP)/www/lang.tcl >lang.html
|
||||
|
||||
pragma.html: $(TOP)/www/pragma.tcl
|
||||
tclsh $(TOP)/www/pragma.tcl >pragma.html
|
||||
|
||||
lockingv3.html: $(TOP)/www/lockingv3.tcl
|
||||
tclsh $(TOP)/www/lockingv3.tcl >lockingv3.html
|
||||
|
||||
oldnews.html: $(TOP)/www/oldnews.tcl
|
||||
tclsh $(TOP)/www/oldnews.tcl >oldnews.html
|
||||
|
||||
omitted.html: $(TOP)/www/omitted.tcl
|
||||
tclsh $(TOP)/www/omitted.tcl >omitted.html
|
||||
|
||||
opcode.html: $(TOP)/www/opcode.tcl $(TOP)/src/vdbe.c
|
||||
tclsh $(TOP)/www/opcode.tcl $(TOP)/src/vdbe.c >opcode.html
|
||||
|
||||
mingw.html: $(TOP)/www/mingw.tcl
|
||||
tclsh $(TOP)/www/mingw.tcl >mingw.html
|
||||
|
||||
nulls.html: $(TOP)/www/nulls.tcl
|
||||
tclsh $(TOP)/www/nulls.tcl >nulls.html
|
||||
|
||||
quickstart.html: $(TOP)/www/quickstart.tcl
|
||||
tclsh $(TOP)/www/quickstart.tcl >quickstart.html
|
||||
|
||||
speed.html: $(TOP)/www/speed.tcl
|
||||
tclsh $(TOP)/www/speed.tcl >speed.html
|
||||
|
||||
sqlite.gif: $(TOP)/art/SQLite.gif
|
||||
cp $(TOP)/art/SQLite.gif sqlite.gif
|
||||
|
||||
sqlite.html: $(TOP)/www/sqlite.tcl
|
||||
tclsh $(TOP)/www/sqlite.tcl >sqlite.html
|
||||
|
||||
support.html: $(TOP)/www/support.tcl
|
||||
tclsh $(TOP)/www/support.tcl >support.html
|
||||
|
||||
tclsqlite.html: $(TOP)/www/tclsqlite.tcl
|
||||
tclsh $(TOP)/www/tclsqlite.tcl >tclsqlite.html
|
||||
|
||||
vdbe.html: $(TOP)/www/vdbe.tcl
|
||||
tclsh $(TOP)/www/vdbe.tcl >vdbe.html
|
||||
|
||||
version3.html: $(TOP)/www/version3.tcl
|
||||
tclsh $(TOP)/www/version3.tcl >version3.html
|
||||
|
||||
|
||||
# Files to be published on the website.
|
||||
#
|
||||
DOC = \
|
||||
arch.html \
|
||||
arch2.gif \
|
||||
autoinc.html \
|
||||
c_interface.html \
|
||||
capi3.html \
|
||||
capi3ref.html \
|
||||
changes.html \
|
||||
compile.html \
|
||||
copyright.html \
|
||||
copyright-release.html \
|
||||
copyright-release.pdf \
|
||||
conflict.html \
|
||||
datatypes.html \
|
||||
datatype3.html \
|
||||
docs.html \
|
||||
download.html \
|
||||
faq.html \
|
||||
fileformat.html \
|
||||
formatchng.html \
|
||||
index.html \
|
||||
lang.html \
|
||||
lockingv3.html \
|
||||
mingw.html \
|
||||
nulls.html \
|
||||
oldnews.html \
|
||||
omitted.html \
|
||||
opcode.html \
|
||||
pragma.html \
|
||||
quickstart.html \
|
||||
speed.html \
|
||||
sqlite.gif \
|
||||
sqlite.html \
|
||||
support.html \
|
||||
tclsqlite.html \
|
||||
vdbe.html \
|
||||
version3.html
|
||||
|
||||
doc: common.tcl $(DOC)
|
||||
mkdir -p doc
|
||||
mv $(DOC) doc
|
||||
|
||||
install: sqlite3 libsqlite3.la sqlite3.h ${HAVE_TCL:1=tcl_install}
|
||||
$(INSTALL) -d $(DESTDIR)$(libdir)
|
||||
$(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir)
|
||||
$(INSTALL) -d $(DESTDIR)$(exec_prefix)/bin
|
||||
$(LTINSTALL) sqlite3 $(DESTDIR)$(exec_prefix)/bin
|
||||
$(INSTALL) -d $(DESTDIR)$(prefix)/include
|
||||
$(INSTALL) -m 0644 sqlite3.h $(DESTDIR)$(prefix)/include
|
||||
$(INSTALL) -m 0644 $(TOP)/src/sqlite3ext.h $(DESTDIR)$(prefix)/include
|
||||
$(INSTALL) -d $(DESTDIR)$(libdir)/pkgconfig;
|
||||
$(INSTALL) -m 0644 sqlite3.pc $(DESTDIR)$(libdir)/pkgconfig;
|
||||
|
||||
tcl_install: libtclsqlite3.la
|
||||
tclsh $(TOP)/tclinstaller.tcl $(VERSION)
|
||||
|
||||
clean:
|
||||
rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la
|
||||
rm -f sqlite3.h opcodes.*
|
||||
rm -rf .libs .deps
|
||||
rm -f lemon$(BEXE) lempar.c parse.* sqlite*.tar.gz
|
||||
rm -f mkkeywordhash$(BEXE) keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -f testfixture$(TEXE) test.db
|
||||
rm -rf doc
|
||||
rm -f common.tcl
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.def
|
||||
|
||||
distclean: clean
|
||||
rm -f config.log config.status libtool Makefile config.h
|
||||
|
||||
#
|
||||
# Windows section
|
||||
#
|
||||
dll: sqlite3.dll
|
||||
|
||||
REAL_LIBOBJ = $(LIBOBJ:%.lo=.libs/%.o)
|
||||
|
||||
$(REAL_LIBOBJ): $(LIBOBJ)
|
||||
|
||||
sqlite3.def: $(REAL_LIBOBJ)
|
||||
echo 'EXPORTS' >sqlite3.def
|
||||
nm $(REAL_LIBOBJ) | grep ' T ' | grep ' _sqlite3_' \
|
||||
| sed 's/^.* _//' >>sqlite3.def
|
||||
|
||||
sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def
|
||||
$(TCC) -shared -o sqlite3.dll sqlite3.def \
|
||||
-Wl,"--strip-all" $(REAL_LIBOBJ)
|
|
@ -1,130 +0,0 @@
|
|||
#!/usr/make
|
||||
#
|
||||
# Makefile for SQLITE
|
||||
#
|
||||
# This is a template makefile for SQLite. Most people prefer to
|
||||
# use the autoconf generated "configure" script to generate the
|
||||
# makefile automatically. But that does not work for everybody
|
||||
# and in every situation. If you are having problems with the
|
||||
# "configure" script, you might want to try this makefile as an
|
||||
# alternative. Create a copy of this file, edit the parameters
|
||||
# below and type "make".
|
||||
#
|
||||
|
||||
#### The toplevel directory of the source tree. This is the directory
|
||||
# that contains this "Makefile.in" and the "configure.in" script.
|
||||
#
|
||||
TOP = ../sqlite
|
||||
|
||||
#### 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 = -DTHREADSAFE=1
|
||||
THREADSAFE = -DTHREADSAFE=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
|
||||
TLIBS =
|
||||
|
||||
#### 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
|
||||
OPTS += -DHAVE_FDATASYNC=1
|
||||
|
||||
#### The suffix to add to executable files. ".exe" for windows.
|
||||
# Nothing for unix.
|
||||
#
|
||||
#EXE = .exe
|
||||
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 = /opt/ansic/bin/c89 -O +z -Wl,-a,archive
|
||||
|
||||
#### Tools used to build a static library.
|
||||
#
|
||||
AR = ar cr
|
||||
#AR = /opt/mingw/bin/i386-mingw32-ar cr
|
||||
RANLIB = ranlib
|
||||
#RANLIB = /opt/mingw/bin/i386-mingw32-ranlib
|
||||
|
||||
MKSHLIB = gcc -shared
|
||||
SO = so
|
||||
SHPREFIX = lib
|
||||
# SO = dll
|
||||
# SHPREFIX =
|
||||
|
||||
#### 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
|
||||
|
||||
#### 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
|
||||
|
||||
#### Should the database engine assume text is coded as UTF-8 or iso8859?
|
||||
#
|
||||
# ENCODING = UTF8
|
||||
ENCODING = ISO8859
|
||||
|
||||
|
||||
#### Which "awk" program provides nawk compatibilty
|
||||
#
|
||||
# NAWK = nawk
|
||||
NAWK = awk
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
include $(TOP)/main.mk
|
|
@ -1,35 +0,0 @@
|
|||
This directory contains source code to
|
||||
|
||||
SQLite: An Embeddable SQL Database Engine
|
||||
|
||||
To compile the project, first create a directory in which to place
|
||||
the build products. It is recommended, but not required, that the
|
||||
build directory be separate from the source directory. Cd into the
|
||||
build directory and then from the build directory run the configure
|
||||
script found at the root of the source tree. Then run "make".
|
||||
|
||||
For example:
|
||||
|
||||
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
|
||||
mkdir bld ;# Build will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
make ;# Run the makefile.
|
||||
make install ;# (Optional) Install the build products
|
||||
|
||||
The configure script uses autoconf 2.50 and libtool. If the configure
|
||||
script does not work out for you, there is a generic makefile named
|
||||
"Makefile.linux-gcc" in the top directory of the source tree that you
|
||||
can copy and edit to suite your needs. Comments on the generic makefile
|
||||
show what changes are needed.
|
||||
|
||||
The linux binaries on the website are created using the generic makefile,
|
||||
not the configure script. The configure script is unmaintained. (You
|
||||
can volunteer to take over maintenance of the configure script, if you want!)
|
||||
The windows binaries on the website are created using MinGW32 configured
|
||||
as a cross-compiler running under Linux. For details, see the ./publish.sh
|
||||
script at the top-level of the source tree.
|
||||
|
||||
Contacts:
|
||||
|
||||
http://www.sqlite.org/
|
|
@ -1 +0,0 @@
|
|||
3.3.13
|
|
@ -1,32 +0,0 @@
|
|||
#!/usr/bin/awk
|
||||
#
|
||||
# This script appends additional token codes to the end of the
|
||||
# parse.h file that lemon generates. These extra token codes are
|
||||
# not used by the parser. But they are used by the tokenizer and/or
|
||||
# the code generator.
|
||||
#
|
||||
#
|
||||
BEGIN {
|
||||
max = 0
|
||||
}
|
||||
/^#define TK_/ {
|
||||
print $0
|
||||
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
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
@ -1,728 +0,0 @@
|
|||
#
|
||||
# The build process allows for using a cross-compiler. But the default
|
||||
# action is to target the same platform that we are running on. The
|
||||
# configure script needs to discover the following properties of the
|
||||
# build and target systems:
|
||||
#
|
||||
# srcdir
|
||||
#
|
||||
# The is the name of the directory that contains the
|
||||
# "configure" shell script. All source files are
|
||||
# located relative to this directory.
|
||||
#
|
||||
# bindir
|
||||
#
|
||||
# The name of the directory where executables should be
|
||||
# written by the "install" target of the makefile.
|
||||
#
|
||||
# program_prefix
|
||||
#
|
||||
# Add this prefix to the names of all executables that run
|
||||
# on the target machine. Default: ""
|
||||
#
|
||||
# ENABLE_SHARED
|
||||
#
|
||||
# True if shared libraries should be generated.
|
||||
#
|
||||
# BUILD_CC
|
||||
#
|
||||
# The name of a command that is used to convert C
|
||||
# source files into executables that run on the build
|
||||
# platform.
|
||||
#
|
||||
# BUILD_CFLAGS
|
||||
#
|
||||
# Switches that the build compiler needs in order to construct
|
||||
# command-line programs.
|
||||
#
|
||||
# BUILD_LIBS
|
||||
#
|
||||
# Libraries that the build compiler needs in order to construct
|
||||
# command-line programs.
|
||||
#
|
||||
# BUILD_EXEEXT
|
||||
#
|
||||
# The filename extension for executables on the build
|
||||
# platform. "" for Unix and ".exe" for Windows.
|
||||
#
|
||||
# TARGET_CC
|
||||
#
|
||||
# The name of a command that runs on the build platform
|
||||
# and converts C source files into *.o files for the
|
||||
# target platform. In other words, the cross-compiler.
|
||||
#
|
||||
# TARGET_CFLAGS
|
||||
#
|
||||
# Switches that the target compiler needs to turn C source files
|
||||
# into *.o files. Do not include TARGET_TCL_INC in this list.
|
||||
# Makefiles might add additional switches such as "-I.".
|
||||
#
|
||||
# TCL_*
|
||||
#
|
||||
# Lots of values are read in from the tclConfig.sh script,
|
||||
# if that script is available. This values are used for
|
||||
# constructing and installing the TCL extension.
|
||||
#
|
||||
# TARGET_READLINE_LIBS
|
||||
#
|
||||
# This is the library directives passed to the target linker
|
||||
# that cause the executable to link against the readline library.
|
||||
# This might be a switch like "-lreadline" or pathnames of library
|
||||
# file like "../../src/libreadline.a".
|
||||
#
|
||||
# TARGET_READLINE_INC
|
||||
#
|
||||
# This variables define the directory that contain header
|
||||
# files for the readline library. If the compiler is able
|
||||
# to find <readline.h> on its own, then this can be blank.
|
||||
#
|
||||
# TARGET_LINK
|
||||
#
|
||||
# The name of the linker that combines *.o files generated
|
||||
# by TARGET_CC into executables for the target platform.
|
||||
#
|
||||
# TARGET_LIBS
|
||||
#
|
||||
# Additional libraries or other switch that the target linker needs
|
||||
# to build an executable on the target. Do not include
|
||||
# on this list any libraries in TARGET_TCL_LIBS and
|
||||
# TARGET_READLINE_LIBS, etc.
|
||||
#
|
||||
# TARGET_EXEEXT
|
||||
#
|
||||
# The filename extension for executables on the
|
||||
# target platform. "" for Unix and ".exe" for windows.
|
||||
#
|
||||
# The generated configure script will make an attempt to guess
|
||||
# at all of the above parameters. You can override any of
|
||||
# the guesses by setting the environment variable named
|
||||
# "config_AAAA" where "AAAA" is the name of the parameter
|
||||
# described above. (Exception: srcdir cannot be set this way.)
|
||||
# If you have a file that sets one or more of these environment
|
||||
# variables, you can invoke configure as follows:
|
||||
#
|
||||
# configure --with-hints=FILE
|
||||
#
|
||||
# where FILE is the name of the file that sets the environment
|
||||
# variables. FILE should be an absolute pathname.
|
||||
#
|
||||
# This configure.in file is easy to reuse on other projects. Just
|
||||
# change the argument to AC_INIT(). And disable any features that
|
||||
# you don't need (for example BLT) by erasing or commenting out
|
||||
# the corresponding code.
|
||||
#
|
||||
AC_INIT(src/sqlite.h.in)
|
||||
|
||||
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.26 $
|
||||
|
||||
#########
|
||||
# Programs needed
|
||||
#
|
||||
AC_PROG_LIBTOOL
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_AWK
|
||||
|
||||
#########
|
||||
# Set up an appropriate program prefix
|
||||
#
|
||||
if test "$program_prefix" = "NONE"; then
|
||||
program_prefix=""
|
||||
fi
|
||||
AC_SUBST(program_prefix)
|
||||
|
||||
VERSION=[`cat $srcdir/VERSION | sed 's/^\([0-9]*\.*[0-9]*\).*/\1/'`]
|
||||
echo "Version set to $VERSION"
|
||||
AC_SUBST(VERSION)
|
||||
RELEASE=`cat $srcdir/VERSION`
|
||||
echo "Release set to $RELEASE"
|
||||
AC_SUBST(RELEASE)
|
||||
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_SUBST(VERSION_NUMBER)
|
||||
|
||||
CFLAGS="$CFLAGS $CONFIGURE_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $CONFIGURE_CXXFLAGS"
|
||||
LDFLAGS="$LDFLAGS $CONFIGURE_LDFLAGS"
|
||||
|
||||
#########
|
||||
# Check to see if the --with-hints=FILE option is used. If there is none,
|
||||
# then check for a files named "$host.hints" and ../$hosts.hints where
|
||||
# $host is the hostname of the build system. If still no hints are
|
||||
# found, try looking in $system.hints and ../$system.hints where
|
||||
# $system is the result of uname -s.
|
||||
#
|
||||
AC_ARG_WITH(hints,
|
||||
AC_HELP_STRING([--with-hints=FILE],[Read configuration options from FILE]),
|
||||
hints=$withval)
|
||||
if test "$hints" = ""; then
|
||||
host=`hostname | sed 's/\..*//'`
|
||||
if test -r $host.hints; then
|
||||
hints=$host.hints
|
||||
else
|
||||
if test -r ../$host.hints; then
|
||||
hints=../$host.hints
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test "$hints" = ""; then
|
||||
sys=`uname -s`
|
||||
if test -r $sys.hints; then
|
||||
hints=$sys.hints
|
||||
else
|
||||
if test -r ../$sys.hints; then
|
||||
hints=../$sys.hints
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test "$hints" != ""; then
|
||||
AC_MSG_RESULT(reading hints from $hints)
|
||||
. $hints
|
||||
fi
|
||||
|
||||
AC_DEFUN([AX_COMPILER_VENDOR],
|
||||
[
|
||||
AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor,
|
||||
[ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown
|
||||
# note: don't check for gcc first since some other compilers define __GNUC__
|
||||
for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do
|
||||
vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[
|
||||
#if !($vencpp)
|
||||
thisisanerror;
|
||||
#endif
|
||||
])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break])
|
||||
done
|
||||
])
|
||||
])
|
||||
|
||||
AX_COMPILER_VENDOR
|
||||
|
||||
#########
|
||||
# Locate a compiler for the build machine. This compiler should
|
||||
# generate command-line programs that run on the build machine.
|
||||
#
|
||||
default_build_cflags="-g"
|
||||
if test "$config_BUILD_CC" = ""; then
|
||||
AC_PROG_CC
|
||||
if test "$cross_compiling" = "yes"; then
|
||||
AC_MSG_ERROR([unable to find a compiler for building build tools])
|
||||
fi
|
||||
BUILD_CC=$CC
|
||||
default_build_cflags=$CFLAGS
|
||||
else
|
||||
BUILD_CC=$config_BUILD_CC
|
||||
AC_MSG_CHECKING([host compiler])
|
||||
CC=$BUILD_CC
|
||||
AC_MSG_RESULT($BUILD_CC)
|
||||
fi
|
||||
AC_MSG_CHECKING([switches for the host compiler])
|
||||
if test "$config_BUILD_CFLAGS" != ""; then
|
||||
CFLAGS=$config_BUILD_CFLAGS
|
||||
BUILD_CFLAGS=$config_BUILD_CFLAGS
|
||||
else
|
||||
BUILD_CFLAGS=$default_build_cflags
|
||||
fi
|
||||
AC_MSG_RESULT($BUILD_CFLAGS)
|
||||
if test "$config_BUILD_LIBS" != ""; then
|
||||
BUILD_LIBS=$config_BUILD_LIBS
|
||||
fi
|
||||
AC_SUBST(BUILD_CC)
|
||||
AC_SUBST(BUILD_CFLAGS)
|
||||
AC_SUBST(BUILD_LIBS)
|
||||
|
||||
|
||||
# Enable 64 bit build
|
||||
AC_ARG_ENABLE(64,
|
||||
[AC_HELP_STRING([--enable-64],[build with 64 bit support])],[enable_64="$enable_64"],[enable_64="no"])
|
||||
|
||||
##########
|
||||
# Locate a compiler that converts C code into *.o files that run on
|
||||
# the target machine.
|
||||
#
|
||||
AC_MSG_CHECKING([target compiler])
|
||||
if test "$config_TARGET_CC" != ""; then
|
||||
TARGET_CC=$config_TARGET_CC
|
||||
else
|
||||
TARGET_CC=$BUILD_CC
|
||||
fi
|
||||
AC_MSG_RESULT($TARGET_CC)
|
||||
AC_MSG_CHECKING([switches on the target compiler])
|
||||
if test "$config_TARGET_CFLAGS" != ""; then
|
||||
TARGET_CFLAGS=$config_TARGET_CFLAGS
|
||||
else
|
||||
TARGET_CFLAGS=$BUILD_CFLAGS
|
||||
fi
|
||||
if test "x${ax_cv_c_compiler_vendor}" = "xsun" ; then
|
||||
if test "${enable_64}" = "yes"; then
|
||||
TARGET_CFLAGS="$TARGET_CFLAGS -m64"
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($TARGET_CFLAGS)
|
||||
AC_MSG_CHECKING([target linker])
|
||||
if test "$config_TARGET_LINK" = ""; then
|
||||
TARGET_LINK=$TARGET_CC
|
||||
else
|
||||
TARGET_LINK=$config_TARGET_LINK
|
||||
fi
|
||||
AC_MSG_RESULT($TARGET_LINK)
|
||||
AC_MSG_CHECKING([switches on the target compiler])
|
||||
if test "$config_TARGET_TFLAGS" != ""; then
|
||||
TARGET_TFLAGS=$config_TARGET_TFLAGS
|
||||
else
|
||||
TARGET_TFLAGS=$BUILD_CFLAGS
|
||||
fi
|
||||
if test "$config_TARGET_RANLIB" != ""; then
|
||||
TARGET_RANLIB=$config_TARGET_RANLIB
|
||||
else
|
||||
AC_PROG_RANLIB
|
||||
TARGET_RANLIB=$RANLIB
|
||||
fi
|
||||
if test "$config_TARGET_AR" != ""; then
|
||||
TARGET_AR=$config_TARGET_AR
|
||||
else
|
||||
TARGET_AR='ar cr'
|
||||
fi
|
||||
AC_MSG_RESULT($TARGET_TFLAGS)
|
||||
AC_SUBST(TARGET_CC)
|
||||
AC_SUBST(TARGET_CFLAGS)
|
||||
AC_SUBST(TARGET_LINK)
|
||||
AC_SUBST(TARGET_LFLAGS)
|
||||
AC_SUBST(TARGET_RANLIB)
|
||||
AC_SUBST(TARGET_AR)
|
||||
|
||||
# Set the $cross variable if we are cross-compiling. Make
|
||||
# it 0 if we are not.
|
||||
#
|
||||
AC_MSG_CHECKING([if host and target compilers are the same])
|
||||
if test "$BUILD_CC" = "$TARGET_CC"; then
|
||||
cross=0
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
cross=1
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
##########
|
||||
# Do we want to support multithreaded use of sqlite
|
||||
#
|
||||
AC_ARG_ENABLE(threadsafe,
|
||||
AC_HELP_STRING([--enable-threadsafe],[Support threadsafe operation]),,enable_threadsafe=no)
|
||||
AC_MSG_CHECKING([whether to support threadsafe operation])
|
||||
if test "$enable_threadsafe" = "no"; then
|
||||
THREADSAFE=0
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
THREADSAFE=1
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
AC_SUBST(THREADSAFE)
|
||||
|
||||
if test "$THREADSAFE" = "1"; then
|
||||
CC=$TARGET_CC
|
||||
LIBS=""
|
||||
AC_CHECK_LIB(pthread, pthread_create)
|
||||
TARGET_THREAD_LIB="$LIBS"
|
||||
LIBS=""
|
||||
else
|
||||
TARGET_THREAD_LIB=""
|
||||
fi
|
||||
AC_SUBST(TARGET_THREAD_LIB)
|
||||
|
||||
##########
|
||||
# Do we want to allow a connection created in one thread to be used
|
||||
# in another thread. This does not work on many Linux systems (ex: RedHat 9)
|
||||
# due to bugs in the threading implementations. This is thus off by default.
|
||||
#
|
||||
AC_ARG_ENABLE(cross-thread-connections,
|
||||
AC_HELP_STRING([--enable-cross-thread-connections],[Allow connection sharing across threads]),,enable_xthreadconnect=no)
|
||||
AC_MSG_CHECKING([whether to allow connections to be shared across threads])
|
||||
if test "$enable_xthreadconnect" = "no"; then
|
||||
XTHREADCONNECT=''
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1'
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
AC_SUBST(XTHREADCONNECT)
|
||||
|
||||
##########
|
||||
# Do we want to set threadsOverrideEachOthersLocks variable to be 1 (true) by
|
||||
# default. Normally, a test at runtime is performed to determine the
|
||||
# appropriate value of this variable. Use this option only if you're sure that
|
||||
# threads can safely override each others locks in all runtime situations.
|
||||
#
|
||||
AC_ARG_ENABLE(threads-override-locks,
|
||||
AC_HELP_STRING([--enable-threads-override-locks],[Threads can override each others locks]),,enable_threads_override_locks=no)
|
||||
AC_MSG_CHECKING([whether threads can override each others locks])
|
||||
if test "$enable_threads_override_locks" = "no"; then
|
||||
THREADSOVERRIDELOCKS='-1'
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
THREADSOVERRIDELOCKS='1'
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
AC_SUBST(THREADSOVERRIDELOCKS)
|
||||
|
||||
##########
|
||||
# Do we want to support release
|
||||
#
|
||||
AC_ARG_ENABLE(releasemode,
|
||||
AC_HELP_STRING([--enable-releasemode],[Support libtool link to release mode]),,enable_releasemode=no)
|
||||
AC_MSG_CHECKING([whether to support shared library linked as release mode or not])
|
||||
if test "$enable_releasemode" = "no"; then
|
||||
ALLOWRELEASE=""
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
ALLOWRELEASE="-release `cat VERSION`"
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
AC_SUBST(ALLOWRELEASE)
|
||||
|
||||
##########
|
||||
# Do we want temporary databases in memory
|
||||
#
|
||||
AC_ARG_ENABLE(tempstore,
|
||||
AC_HELP_STRING([--enable-tempstore],[Use an in-ram database for temporary tables (never,no,yes,always)]),,enable_tempstore=no)
|
||||
AC_MSG_CHECKING([whether to use an in-ram database for temporary tables])
|
||||
case "$enable_tempstore" in
|
||||
never )
|
||||
TEMP_STORE=0
|
||||
AC_MSG_RESULT([never])
|
||||
;;
|
||||
no )
|
||||
TEMP_STORE=1
|
||||
AC_MSG_RESULT([no])
|
||||
;;
|
||||
always )
|
||||
TEMP_STORE=3
|
||||
AC_MSG_RESULT([always])
|
||||
;;
|
||||
yes )
|
||||
TEMP_STORE=3
|
||||
AC_MSG_RESULT([always])
|
||||
;;
|
||||
* )
|
||||
TEMP_STORE=1
|
||||
AC_MSG_RESULT([yes])
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(TEMP_STORE)
|
||||
|
||||
###########
|
||||
# Lots of things are different if we are compiling for Windows using
|
||||
# the CYGWIN environment. So check for that special case and handle
|
||||
# things accordingly.
|
||||
#
|
||||
AC_MSG_CHECKING([if executables have the .exe suffix])
|
||||
if test "$config_BUILD_EXEEXT" = ".exe"; then
|
||||
CYGWIN=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(unknown)
|
||||
fi
|
||||
if test "$CYGWIN" != "yes"; then
|
||||
case $host_os in
|
||||
*cygwin* ) CYGWIN=yes;;
|
||||
* ) CYGWIN=no;;
|
||||
esac
|
||||
fi
|
||||
if test "$CYGWIN" = "yes"; then
|
||||
BUILD_EXEEXT=.exe
|
||||
else
|
||||
BUILD_EXEEXT=$EXEEXT
|
||||
fi
|
||||
if test "$cross" = "0"; then
|
||||
TARGET_EXEEXT=$BUILD_EXEEXT
|
||||
else
|
||||
TARGET_EXEEXT=$config_TARGET_EXEEXT
|
||||
fi
|
||||
if test "$TARGET_EXEEXT" = ".exe"; then
|
||||
if test $OS2_SHELL ; then
|
||||
OS_UNIX=0
|
||||
OS_WIN=0
|
||||
OS_OS2=1
|
||||
TARGET_CFLAGS="$TARGET_CFLAGS -DOS_OS2=1"
|
||||
if test "$ac_compiler_gnu" = "yes" ; then
|
||||
TARGET_CFLAGS="$TARGET_CFLAGS -Zomf -Zexe -Zmap"
|
||||
BUILD_CFLAGS="$BUILD_CFLAGS -Zomf -Zexe"
|
||||
fi
|
||||
else
|
||||
OS_UNIX=0
|
||||
OS_WIN=1
|
||||
OS_OS2=0
|
||||
tclsubdir=win
|
||||
TARGET_CFLAGS="$TARGET_CFLAGS -DOS_WIN=1"
|
||||
fi
|
||||
else
|
||||
OS_UNIX=1
|
||||
OS_WIN=0
|
||||
OS_OS2=0
|
||||
tclsubdir=unix
|
||||
TARGET_CFLAGS="$TARGET_CFLAGS -DOS_UNIX=1"
|
||||
fi
|
||||
|
||||
AC_SUBST(BUILD_EXEEXT)
|
||||
AC_SUBST(OS_UNIX)
|
||||
AC_SUBST(OS_WIN)
|
||||
AC_SUBST(OS_OS2)
|
||||
AC_SUBST(TARGET_EXEEXT)
|
||||
|
||||
##########
|
||||
# Extract generic linker options from the environment.
|
||||
#
|
||||
if test "$config_TARGET_LIBS" != ""; then
|
||||
TARGET_LIBS=$config_TARGET_LIBS
|
||||
else
|
||||
TARGET_LIBS=""
|
||||
fi
|
||||
|
||||
##########
|
||||
# Figure out all the parameters needed to compile against Tcl.
|
||||
#
|
||||
# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
|
||||
# macros in the in the tcl.m4 file of the standard TCL distribution.
|
||||
# Those macros could not be used directly since we have to make some
|
||||
# minor changes to accomodate systems that do not have TCL installed.
|
||||
#
|
||||
AC_ARG_ENABLE(tcl, AC_HELP_STRING([--disable-tcl],[do not build TCL extension]),
|
||||
[use_tcl=$enableval],[use_tcl=yes])
|
||||
if test "${use_tcl}" = "yes" ; then
|
||||
AC_ARG_WITH(tcl, AC_HELP_STRING([--with-tcl=DIR],[directory containing tcl configuration (tclConfig.sh)]), with_tclconfig=${withval})
|
||||
AC_MSG_CHECKING([for Tcl configuration])
|
||||
AC_CACHE_VAL(ac_cv_c_tclconfig,[
|
||||
# First check to see if --with-tcl was specified.
|
||||
if test x"${with_tclconfig}" != x ; then
|
||||
if test -f "${with_tclconfig}/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
|
||||
else
|
||||
AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
|
||||
fi
|
||||
fi
|
||||
# then check for a private Tcl installation
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
../tcl \
|
||||
`ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
|
||||
../../tcl \
|
||||
`ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
|
||||
../../../tcl \
|
||||
`ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/unix/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# check in a few common install locations
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
`ls -d ${libdir} 2>/dev/null` \
|
||||
`ls -d /usr/local/lib 2>/dev/null` \
|
||||
`ls -d /usr/contrib/lib 2>/dev/null` \
|
||||
`ls -d /usr/lib 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# check in a few other private locations
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
${srcdir}/../tcl \
|
||||
`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/unix/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
use_tcl=no
|
||||
AC_MSG_WARN(Can't find Tcl configuration definitions)
|
||||
AC_MSG_WARN(*** Without Tcl the regression tests cannot be executed ***)
|
||||
AC_MSG_WARN(*** Consider using --with-tcl=... to define location of Tcl ***)
|
||||
else
|
||||
TCL_BIN_DIR=${ac_cv_c_tclconfig}
|
||||
AC_MSG_RESULT(found $TCL_BIN_DIR/tclConfig.sh)
|
||||
|
||||
AC_MSG_CHECKING([for existence of $TCL_BIN_DIR/tclConfig.sh])
|
||||
if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
|
||||
AC_MSG_RESULT([loading])
|
||||
. $TCL_BIN_DIR/tclConfig.sh
|
||||
else
|
||||
AC_MSG_RESULT([file not found])
|
||||
fi
|
||||
|
||||
#
|
||||
# If the TCL_BIN_DIR is the build directory (not the install directory),
|
||||
# then set the common variable name to the value of the build variables.
|
||||
# For example, the variable TCL_LIB_SPEC will be set to the value
|
||||
# of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
|
||||
# instead of TCL_BUILD_LIB_SPEC since it will work with both an
|
||||
# installed and uninstalled version of Tcl.
|
||||
#
|
||||
|
||||
if test -f $TCL_BIN_DIR/Makefile ; then
|
||||
TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
|
||||
TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
|
||||
TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
|
||||
fi
|
||||
|
||||
#
|
||||
# eval is required to do the TCL_DBGX substitution
|
||||
#
|
||||
|
||||
eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
|
||||
eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
|
||||
eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
|
||||
|
||||
eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
|
||||
eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
|
||||
eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
|
||||
|
||||
AC_SUBST(TCL_VERSION)
|
||||
AC_SUBST(TCL_BIN_DIR)
|
||||
AC_SUBST(TCL_SRC_DIR)
|
||||
AC_SUBST(TCL_LIBS)
|
||||
AC_SUBST(TCL_INCLUDE_SPEC)
|
||||
|
||||
AC_SUBST(TCL_LIB_FILE)
|
||||
AC_SUBST(TCL_LIB_FLAG)
|
||||
AC_SUBST(TCL_LIB_SPEC)
|
||||
|
||||
AC_SUBST(TCL_STUB_LIB_FILE)
|
||||
AC_SUBST(TCL_STUB_LIB_FLAG)
|
||||
AC_SUBST(TCL_STUB_LIB_SPEC)
|
||||
fi
|
||||
fi
|
||||
if test "${use_tcl}" = "no" ; then
|
||||
HAVE_TCL=""
|
||||
else
|
||||
HAVE_TCL=1
|
||||
fi
|
||||
AC_SUBST(HAVE_TCL)
|
||||
|
||||
##########
|
||||
# Figure out what C libraries are required to compile programs
|
||||
# that use "readline()" library.
|
||||
#
|
||||
if test "$config_TARGET_READLINE_LIBS" != ""; then
|
||||
TARGET_READLINE_LIBS="$config_TARGET_READLINE_LIBS"
|
||||
else
|
||||
CC=$TARGET_CC
|
||||
LIBS=""
|
||||
AC_SEARCH_LIBS(tgetent, [readline ncurses curses termcap])
|
||||
AC_CHECK_LIB([readline], [readline])
|
||||
AC_CHECK_LIB([dl], [dlopen])
|
||||
TARGET_READLINE_LIBS="$LDFLAGS $LIBS"
|
||||
fi
|
||||
AC_SUBST(TARGET_READLINE_LIBS)
|
||||
|
||||
##########
|
||||
# Figure out what C libraries are required to compile programs
|
||||
# that use "fdatasync()" function.
|
||||
#
|
||||
CC=$TARGET_CC
|
||||
LIBS=$TARGET_LIBS
|
||||
AC_SEARCH_LIBS(fdatasync, [rt])
|
||||
TARGET_LIBS="$LIBS"
|
||||
|
||||
##########
|
||||
# Figure out where to get the READLINE header files.
|
||||
#
|
||||
AC_MSG_CHECKING([readline header files])
|
||||
found=no
|
||||
if test "$config_TARGET_READLINE_INC" != ""; then
|
||||
TARGET_READLINE_INC=$config_TARGET_READLINE_INC
|
||||
found=yes
|
||||
fi
|
||||
if test "$found" = "yes"; then
|
||||
AC_MSG_RESULT($TARGET_READLINE_INC)
|
||||
else
|
||||
AC_MSG_RESULT(not specified: still searching...)
|
||||
AC_CHECK_HEADER(readline.h, [found=yes])
|
||||
fi
|
||||
if test "$found" = "no"; then
|
||||
if test "$cross_compiling" != "yes"; then
|
||||
for dir in /usr /usr/local /usr/local/readline /usr/contrib /mingw; do
|
||||
AC_CHECK_FILE($dir/include/readline.h, found=yes)
|
||||
if test "$found" = "yes"; then
|
||||
TARGET_READLINE_INC="-I$dir/include"
|
||||
break
|
||||
fi
|
||||
AC_CHECK_FILE($dir/include/readline/readline.h, found=yes)
|
||||
if test "$found" = "yes"; then
|
||||
TARGET_READLINE_INC="-I$dir/include/readline"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
if test "$found" = "yes"; then
|
||||
if test "$TARGET_READLINE_LIBS" = ""; then
|
||||
TARGET_HAVE_READLINE=0
|
||||
else
|
||||
TARGET_HAVE_READLINE=1
|
||||
fi
|
||||
else
|
||||
TARGET_HAVE_READLINE=0
|
||||
fi
|
||||
AC_SUBST(TARGET_READLINE_INC)
|
||||
AC_SUBST(TARGET_HAVE_READLINE)
|
||||
|
||||
#########
|
||||
# check for debug enabled
|
||||
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]),
|
||||
[use_debug=$enableval],[use_debug=no])
|
||||
if test "${use_debug}" = "yes" ; then
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1"
|
||||
else
|
||||
TARGET_DEBUG="-DNDEBUG"
|
||||
fi
|
||||
AC_SUBST(TARGET_DEBUG)
|
||||
|
||||
#########
|
||||
# Figure out whether or not we have a "usleep()" function.
|
||||
#
|
||||
AC_CHECK_FUNC(usleep, [TARGET_CFLAGS="$TARGET_CFLAGS -DHAVE_USLEEP=1"])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Redefine fdatasync as fsync on systems that lack fdatasync
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_CHECK_FUNC(fdatasync, [TARGET_CFLAGS="$TARGET_CFLAGS -DHAVE_FDATASYNC=1"])
|
||||
|
||||
#########
|
||||
# Put out accumulated miscellaneous LIBRARIES
|
||||
#
|
||||
AC_SUBST(TARGET_LIBS)
|
||||
|
||||
#########
|
||||
# Generate the output files.
|
||||
#
|
||||
AC_OUTPUT([
|
||||
Makefile
|
||||
sqlite3.pc
|
||||
])
|
|
@ -1,4 +0,0 @@
|
|||
#! /bin/sh
|
||||
srcpath=$(dirname $0 2>/dev/null ) || srcpath="."
|
||||
$srcpath/configure "$@" --disable-tcl --enable-threadsafe --disable-shared --with-pic
|
||||
|
|
@ -1,679 +0,0 @@
|
|||
# A Tk console widget for SQLite. Invoke sqlitecon::create with a window name,
|
||||
# a prompt string, a title to set a new top-level window, and the SQLite
|
||||
# database handle. For example:
|
||||
#
|
||||
# sqlitecon::create .sqlcon {sql:- } {SQL Console} db
|
||||
#
|
||||
# A toplevel window is created that allows you to type in SQL commands to
|
||||
# be processed on the spot.
|
||||
#
|
||||
# A limited set of dot-commands are supported:
|
||||
#
|
||||
# .table
|
||||
# .schema ?TABLE?
|
||||
# .mode list|column|multicolumn|line
|
||||
# .exit
|
||||
#
|
||||
# In addition, a new SQL function named "edit()" is created. This function
|
||||
# takes a single text argument and returns a text result. Whenever the
|
||||
# the function is called, it pops up a new toplevel window containing a
|
||||
# text editor screen initialized to the argument. When the "OK" button
|
||||
# is pressed, whatever revised text is in the text editor is returned as
|
||||
# the result of the edit() function. This allows text fields of SQL tables
|
||||
# to be edited quickly and easily as follows:
|
||||
#
|
||||
# UPDATE table1 SET dscr = edit(dscr) WHERE rowid=15;
|
||||
#
|
||||
|
||||
|
||||
# Create a namespace to work in
|
||||
#
|
||||
namespace eval ::sqlitecon {
|
||||
# do nothing
|
||||
}
|
||||
|
||||
# Create a console widget named $w. The prompt string is $prompt.
|
||||
# The title at the top of the window is $title. The database connection
|
||||
# object is $db
|
||||
#
|
||||
proc sqlitecon::create {w prompt title db} {
|
||||
upvar #0 $w.t v
|
||||
if {[winfo exists $w]} {destroy $w}
|
||||
if {[info exists v]} {unset v}
|
||||
toplevel $w
|
||||
wm title $w $title
|
||||
wm iconname $w $title
|
||||
frame $w.mb -bd 2 -relief raised
|
||||
pack $w.mb -side top -fill x
|
||||
menubutton $w.mb.file -text File -menu $w.mb.file.m
|
||||
menubutton $w.mb.edit -text Edit -menu $w.mb.edit.m
|
||||
pack $w.mb.file $w.mb.edit -side left -padx 8 -pady 1
|
||||
set m [menu $w.mb.file.m -tearoff 0]
|
||||
$m add command -label {Close} -command "destroy $w"
|
||||
sqlitecon::create_child $w $prompt $w.mb.edit.m
|
||||
set v(db) $db
|
||||
$db function edit ::sqlitecon::_edit
|
||||
}
|
||||
|
||||
# This routine creates a console as a child window within a larger
|
||||
# window. It also creates an edit menu named "$editmenu" if $editmenu!="".
|
||||
# The calling function is responsible for posting the edit menu.
|
||||
#
|
||||
proc sqlitecon::create_child {w prompt editmenu} {
|
||||
upvar #0 $w.t v
|
||||
if {$editmenu!=""} {
|
||||
set m [menu $editmenu -tearoff 0]
|
||||
$m add command -label Cut -command "sqlitecon::Cut $w.t"
|
||||
$m add command -label Copy -command "sqlitecon::Copy $w.t"
|
||||
$m add command -label Paste -command "sqlitecon::Paste $w.t"
|
||||
$m add command -label {Clear Screen} -command "sqlitecon::Clear $w.t"
|
||||
$m add separator
|
||||
$m add command -label {Save As...} -command "sqlitecon::SaveFile $w.t"
|
||||
catch {$editmenu config -postcommand "sqlitecon::EnableEditMenu $w"}
|
||||
}
|
||||
scrollbar $w.sb -orient vertical -command "$w.t yview"
|
||||
pack $w.sb -side right -fill y
|
||||
text $w.t -font fixed -yscrollcommand "$w.sb set"
|
||||
pack $w.t -side right -fill both -expand 1
|
||||
bindtags $w.t Sqlitecon
|
||||
set v(editmenu) $editmenu
|
||||
set v(history) 0
|
||||
set v(historycnt) 0
|
||||
set v(current) -1
|
||||
set v(prompt) $prompt
|
||||
set v(prior) {}
|
||||
set v(plength) [string length $v(prompt)]
|
||||
set v(x) 0
|
||||
set v(y) 0
|
||||
set v(mode) column
|
||||
set v(header) on
|
||||
$w.t mark set insert end
|
||||
$w.t tag config ok -foreground blue
|
||||
$w.t tag config err -foreground red
|
||||
$w.t insert end $v(prompt)
|
||||
$w.t mark set out 1.0
|
||||
after idle "focus $w.t"
|
||||
}
|
||||
|
||||
bind Sqlitecon <1> {sqlitecon::Button1 %W %x %y}
|
||||
bind Sqlitecon <B1-Motion> {sqlitecon::B1Motion %W %x %y}
|
||||
bind Sqlitecon <B1-Leave> {sqlitecon::B1Leave %W %x %y}
|
||||
bind Sqlitecon <B1-Enter> {sqlitecon::cancelMotor %W}
|
||||
bind Sqlitecon <ButtonRelease-1> {sqlitecon::cancelMotor %W}
|
||||
bind Sqlitecon <KeyPress> {sqlitecon::Insert %W %A}
|
||||
bind Sqlitecon <Left> {sqlitecon::Left %W}
|
||||
bind Sqlitecon <Control-b> {sqlitecon::Left %W}
|
||||
bind Sqlitecon <Right> {sqlitecon::Right %W}
|
||||
bind Sqlitecon <Control-f> {sqlitecon::Right %W}
|
||||
bind Sqlitecon <BackSpace> {sqlitecon::Backspace %W}
|
||||
bind Sqlitecon <Control-h> {sqlitecon::Backspace %W}
|
||||
bind Sqlitecon <Delete> {sqlitecon::Delete %W}
|
||||
bind Sqlitecon <Control-d> {sqlitecon::Delete %W}
|
||||
bind Sqlitecon <Home> {sqlitecon::Home %W}
|
||||
bind Sqlitecon <Control-a> {sqlitecon::Home %W}
|
||||
bind Sqlitecon <End> {sqlitecon::End %W}
|
||||
bind Sqlitecon <Control-e> {sqlitecon::End %W}
|
||||
bind Sqlitecon <Return> {sqlitecon::Enter %W}
|
||||
bind Sqlitecon <KP_Enter> {sqlitecon::Enter %W}
|
||||
bind Sqlitecon <Up> {sqlitecon::Prior %W}
|
||||
bind Sqlitecon <Control-p> {sqlitecon::Prior %W}
|
||||
bind Sqlitecon <Down> {sqlitecon::Next %W}
|
||||
bind Sqlitecon <Control-n> {sqlitecon::Next %W}
|
||||
bind Sqlitecon <Control-k> {sqlitecon::EraseEOL %W}
|
||||
bind Sqlitecon <<Cut>> {sqlitecon::Cut %W}
|
||||
bind Sqlitecon <<Copy>> {sqlitecon::Copy %W}
|
||||
bind Sqlitecon <<Paste>> {sqlitecon::Paste %W}
|
||||
bind Sqlitecon <<Clear>> {sqlitecon::Clear %W}
|
||||
|
||||
# Insert a single character at the insertion cursor
|
||||
#
|
||||
proc sqlitecon::Insert {w a} {
|
||||
$w insert insert $a
|
||||
$w yview insert
|
||||
}
|
||||
|
||||
# Move the cursor one character to the left
|
||||
#
|
||||
proc sqlitecon::Left {w} {
|
||||
upvar #0 $w v
|
||||
scan [$w index insert] %d.%d row col
|
||||
if {$col>$v(plength)} {
|
||||
$w mark set insert "insert -1c"
|
||||
}
|
||||
}
|
||||
|
||||
# Erase the character to the left of the cursor
|
||||
#
|
||||
proc sqlitecon::Backspace {w} {
|
||||
upvar #0 $w v
|
||||
scan [$w index insert] %d.%d row col
|
||||
if {$col>$v(plength)} {
|
||||
$w delete {insert -1c}
|
||||
}
|
||||
}
|
||||
|
||||
# Erase to the end of the line
|
||||
#
|
||||
proc sqlitecon::EraseEOL {w} {
|
||||
upvar #0 $w v
|
||||
scan [$w index insert] %d.%d row col
|
||||
if {$col>=$v(plength)} {
|
||||
$w delete insert {insert lineend}
|
||||
}
|
||||
}
|
||||
|
||||
# Move the cursor one character to the right
|
||||
#
|
||||
proc sqlitecon::Right {w} {
|
||||
$w mark set insert "insert +1c"
|
||||
}
|
||||
|
||||
# Erase the character to the right of the cursor
|
||||
#
|
||||
proc sqlitecon::Delete w {
|
||||
$w delete insert
|
||||
}
|
||||
|
||||
# Move the cursor to the beginning of the current line
|
||||
#
|
||||
proc sqlitecon::Home w {
|
||||
upvar #0 $w v
|
||||
scan [$w index insert] %d.%d row col
|
||||
$w mark set insert $row.$v(plength)
|
||||
}
|
||||
|
||||
# Move the cursor to the end of the current line
|
||||
#
|
||||
proc sqlitecon::End w {
|
||||
$w mark set insert {insert lineend}
|
||||
}
|
||||
|
||||
# Add a line to the history
|
||||
#
|
||||
proc sqlitecon::addHistory {w line} {
|
||||
upvar #0 $w v
|
||||
if {$v(historycnt)>0} {
|
||||
set last [lindex $v(history) [expr $v(historycnt)-1]]
|
||||
if {[string compare $last $line]} {
|
||||
lappend v(history) $line
|
||||
incr v(historycnt)
|
||||
}
|
||||
} else {
|
||||
set v(history) [list $line]
|
||||
set v(historycnt) 1
|
||||
}
|
||||
set v(current) $v(historycnt)
|
||||
}
|
||||
|
||||
# Called when "Enter" is pressed. Do something with the line
|
||||
# of text that was entered.
|
||||
#
|
||||
proc sqlitecon::Enter w {
|
||||
upvar #0 $w v
|
||||
scan [$w index insert] %d.%d row col
|
||||
set start $row.$v(plength)
|
||||
set line [$w get $start "$start lineend"]
|
||||
$w insert end \n
|
||||
$w mark set out end
|
||||
if {$v(prior)==""} {
|
||||
set cmd $line
|
||||
} else {
|
||||
set cmd $v(prior)\n$line
|
||||
}
|
||||
if {[string index $cmd 0]=="." || [$v(db) complete $cmd]} {
|
||||
regsub -all {\n} [string trim $cmd] { } cmd2
|
||||
addHistory $w $cmd2
|
||||
set rc [catch {DoCommand $w $cmd} res]
|
||||
if {![winfo exists $w]} return
|
||||
if {$rc} {
|
||||
$w insert end $res\n err
|
||||
} elseif {[string length $res]>0} {
|
||||
$w insert end $res\n ok
|
||||
}
|
||||
set v(prior) {}
|
||||
$w insert end $v(prompt)
|
||||
} else {
|
||||
set v(prior) $cmd
|
||||
regsub -all {[^ ]} $v(prompt) . x
|
||||
$w insert end $x
|
||||
}
|
||||
$w mark set insert end
|
||||
$w mark set out {insert linestart}
|
||||
$w yview insert
|
||||
}
|
||||
|
||||
# Execute a single SQL command. Pay special attention to control
|
||||
# directives that begin with "."
|
||||
#
|
||||
# The return value is the text output from the command, properly
|
||||
# formatted.
|
||||
#
|
||||
proc sqlitecon::DoCommand {w cmd} {
|
||||
upvar #0 $w v
|
||||
set mode $v(mode)
|
||||
set header $v(header)
|
||||
if {[regexp {^(\.[a-z]+)} $cmd all word]} {
|
||||
if {$word==".mode"} {
|
||||
regexp {^.[a-z]+ +([a-z]+)} $cmd all v(mode)
|
||||
return {}
|
||||
} elseif {$word==".exit"} {
|
||||
destroy [winfo toplevel $w]
|
||||
return {}
|
||||
} elseif {$word==".header"} {
|
||||
regexp {^.[a-z]+ +([a-z]+)} $cmd all v(header)
|
||||
return {}
|
||||
} elseif {$word==".tables"} {
|
||||
set mode multicolumn
|
||||
set cmd {SELECT name FROM sqlite_master WHERE type='table'
|
||||
UNION ALL
|
||||
SELECT name FROM sqlite_temp_master WHERE type='table'}
|
||||
$v(db) eval {PRAGMA database_list} {
|
||||
if {$name!="temp" && $name!="main"} {
|
||||
append cmd "UNION ALL SELECT name FROM $name.sqlite_master\
|
||||
WHERE type='table'"
|
||||
}
|
||||
}
|
||||
append cmd { ORDER BY 1}
|
||||
} elseif {$word==".fullschema"} {
|
||||
set pattern %
|
||||
regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern
|
||||
set mode list
|
||||
set header 0
|
||||
set cmd "SELECT sql FROM sqlite_master WHERE tbl_name LIKE '$pattern'
|
||||
AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master
|
||||
WHERE tbl_name LIKE '$pattern' AND sql NOT NULL"
|
||||
$v(db) eval {PRAGMA database_list} {
|
||||
if {$name!="temp" && $name!="main"} {
|
||||
append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\
|
||||
WHERE tbl_name LIKE '$pattern' AND sql NOT NULL"
|
||||
}
|
||||
}
|
||||
} elseif {$word==".schema"} {
|
||||
set pattern %
|
||||
regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern
|
||||
set mode list
|
||||
set header 0
|
||||
set cmd "SELECT sql FROM sqlite_master WHERE name LIKE '$pattern'
|
||||
AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master
|
||||
WHERE name LIKE '$pattern' AND sql NOT NULL"
|
||||
$v(db) eval {PRAGMA database_list} {
|
||||
if {$name!="temp" && $name!="main"} {
|
||||
append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\
|
||||
WHERE name LIKE '$pattern' AND sql NOT NULL"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return \
|
||||
".exit\n.mode line|list|column\n.schema ?TABLENAME?\n.tables"
|
||||
}
|
||||
}
|
||||
set res {}
|
||||
if {$mode=="list"} {
|
||||
$v(db) eval $cmd x {
|
||||
set sep {}
|
||||
foreach col $x(*) {
|
||||
append res $sep$x($col)
|
||||
set sep |
|
||||
}
|
||||
append res \n
|
||||
}
|
||||
if {[info exists x(*)] && $header} {
|
||||
set sep {}
|
||||
set hdr {}
|
||||
foreach col $x(*) {
|
||||
append hdr $sep$col
|
||||
set sep |
|
||||
}
|
||||
set res $hdr\n$res
|
||||
}
|
||||
} elseif {[string range $mode 0 2]=="col"} {
|
||||
set y {}
|
||||
$v(db) eval $cmd x {
|
||||
foreach col $x(*) {
|
||||
if {![info exists cw($col)] || $cw($col)<[string length $x($col)]} {
|
||||
set cw($col) [string length $x($col)]
|
||||
}
|
||||
lappend y $x($col)
|
||||
}
|
||||
}
|
||||
if {[info exists x(*)] && $header} {
|
||||
set hdr {}
|
||||
set ln {}
|
||||
set dash ---------------------------------------------------------------
|
||||
append dash ------------------------------------------------------------
|
||||
foreach col $x(*) {
|
||||
if {![info exists cw($col)] || $cw($col)<[string length $col]} {
|
||||
set cw($col) [string length $col]
|
||||
}
|
||||
lappend hdr $col
|
||||
lappend ln [string range $dash 1 $cw($col)]
|
||||
}
|
||||
set y [concat $hdr $ln $y]
|
||||
}
|
||||
if {[info exists x(*)]} {
|
||||
set format {}
|
||||
set arglist {}
|
||||
set arglist2 {}
|
||||
set i 0
|
||||
foreach col $x(*) {
|
||||
lappend arglist x$i
|
||||
append arglist2 " \$x$i"
|
||||
incr i
|
||||
append format " %-$cw($col)s"
|
||||
}
|
||||
set format [string trimleft $format]\n
|
||||
if {[llength $arglist]>0} {
|
||||
foreach $arglist $y "append res \[format [list $format] $arglist2\]"
|
||||
}
|
||||
}
|
||||
} elseif {$mode=="multicolumn"} {
|
||||
set y [$v(db) eval $cmd]
|
||||
set max 0
|
||||
foreach e $y {
|
||||
if {$max<[string length $e]} {set max [string length $e]}
|
||||
}
|
||||
set ncol [expr {int(80/($max+2))}]
|
||||
if {$ncol<1} {set ncol 1}
|
||||
set nelem [llength $y]
|
||||
set nrow [expr {($nelem+$ncol-1)/$ncol}]
|
||||
set format "%-${max}s"
|
||||
for {set i 0} {$i<$nrow} {incr i} {
|
||||
set j $i
|
||||
while 1 {
|
||||
append res [format $format [lindex $y $j]]
|
||||
incr j $nrow
|
||||
if {$j>=$nelem} break
|
||||
append res { }
|
||||
}
|
||||
append res \n
|
||||
}
|
||||
} else {
|
||||
$v(db) eval $cmd x {
|
||||
foreach col $x(*) {append res "$col = $x($col)\n"}
|
||||
append res \n
|
||||
}
|
||||
}
|
||||
return [string trimright $res]
|
||||
}
|
||||
|
||||
# Change the line to the previous line
|
||||
#
|
||||
proc sqlitecon::Prior w {
|
||||
upvar #0 $w v
|
||||
if {$v(current)<=0} return
|
||||
incr v(current) -1
|
||||
set line [lindex $v(history) $v(current)]
|
||||
sqlitecon::SetLine $w $line
|
||||
}
|
||||
|
||||
# Change the line to the next line
|
||||
#
|
||||
proc sqlitecon::Next w {
|
||||
upvar #0 $w v
|
||||
if {$v(current)>=$v(historycnt)} return
|
||||
incr v(current) 1
|
||||
set line [lindex $v(history) $v(current)]
|
||||
sqlitecon::SetLine $w $line
|
||||
}
|
||||
|
||||
# Change the contents of the entry line
|
||||
#
|
||||
proc sqlitecon::SetLine {w line} {
|
||||
upvar #0 $w v
|
||||
scan [$w index insert] %d.%d row col
|
||||
set start $row.$v(plength)
|
||||
$w delete $start end
|
||||
$w insert end $line
|
||||
$w mark set insert end
|
||||
$w yview insert
|
||||
}
|
||||
|
||||
# Called when the mouse button is pressed at position $x,$y on
|
||||
# the console widget.
|
||||
#
|
||||
proc sqlitecon::Button1 {w x y} {
|
||||
global tkPriv
|
||||
upvar #0 $w v
|
||||
set v(mouseMoved) 0
|
||||
set v(pressX) $x
|
||||
set p [sqlitecon::nearestBoundry $w $x $y]
|
||||
scan [$w index insert] %d.%d ix iy
|
||||
scan $p %d.%d px py
|
||||
if {$px==$ix} {
|
||||
$w mark set insert $p
|
||||
}
|
||||
$w mark set anchor $p
|
||||
focus $w
|
||||
}
|
||||
|
||||
# Find the boundry between characters that is nearest
|
||||
# to $x,$y
|
||||
#
|
||||
proc sqlitecon::nearestBoundry {w x y} {
|
||||
set p [$w index @$x,$y]
|
||||
set bb [$w bbox $p]
|
||||
if {![string compare $bb ""]} {return $p}
|
||||
if {($x-[lindex $bb 0])<([lindex $bb 2]/2)} {return $p}
|
||||
$w index "$p + 1 char"
|
||||
}
|
||||
|
||||
# This routine extends the selection to the point specified by $x,$y
|
||||
#
|
||||
proc sqlitecon::SelectTo {w x y} {
|
||||
upvar #0 $w v
|
||||
set cur [sqlitecon::nearestBoundry $w $x $y]
|
||||
if {[catch {$w index anchor}]} {
|
||||
$w mark set anchor $cur
|
||||
}
|
||||
set anchor [$w index anchor]
|
||||
if {[$w compare $cur != $anchor] || (abs($v(pressX) - $x) >= 3)} {
|
||||
if {$v(mouseMoved)==0} {
|
||||
$w tag remove sel 0.0 end
|
||||
}
|
||||
set v(mouseMoved) 1
|
||||
}
|
||||
if {[$w compare $cur < anchor]} {
|
||||
set first $cur
|
||||
set last anchor
|
||||
} else {
|
||||
set first anchor
|
||||
set last $cur
|
||||
}
|
||||
if {$v(mouseMoved)} {
|
||||
$w tag remove sel 0.0 $first
|
||||
$w tag add sel $first $last
|
||||
$w tag remove sel $last end
|
||||
update idletasks
|
||||
}
|
||||
}
|
||||
|
||||
# Called whenever the mouse moves while button-1 is held down.
|
||||
#
|
||||
proc sqlitecon::B1Motion {w x y} {
|
||||
upvar #0 $w v
|
||||
set v(y) $y
|
||||
set v(x) $x
|
||||
sqlitecon::SelectTo $w $x $y
|
||||
}
|
||||
|
||||
# Called whenever the mouse leaves the boundries of the widget
|
||||
# while button 1 is held down.
|
||||
#
|
||||
proc sqlitecon::B1Leave {w x y} {
|
||||
upvar #0 $w v
|
||||
set v(y) $y
|
||||
set v(x) $x
|
||||
sqlitecon::motor $w
|
||||
}
|
||||
|
||||
# This routine is called to automatically scroll the window when
|
||||
# the mouse drags offscreen.
|
||||
#
|
||||
proc sqlitecon::motor w {
|
||||
upvar #0 $w v
|
||||
if {![winfo exists $w]} return
|
||||
if {$v(y)>=[winfo height $w]} {
|
||||
$w yview scroll 1 units
|
||||
} elseif {$v(y)<0} {
|
||||
$w yview scroll -1 units
|
||||
} else {
|
||||
return
|
||||
}
|
||||
sqlitecon::SelectTo $w $v(x) $v(y)
|
||||
set v(timer) [after 50 sqlitecon::motor $w]
|
||||
}
|
||||
|
||||
# This routine cancels the scrolling motor if it is active
|
||||
#
|
||||
proc sqlitecon::cancelMotor w {
|
||||
upvar #0 $w v
|
||||
catch {after cancel $v(timer)}
|
||||
catch {unset v(timer)}
|
||||
}
|
||||
|
||||
# Do a Copy operation on the stuff currently selected.
|
||||
#
|
||||
proc sqlitecon::Copy w {
|
||||
if {![catch {set text [$w get sel.first sel.last]}]} {
|
||||
clipboard clear -displayof $w
|
||||
clipboard append -displayof $w $text
|
||||
}
|
||||
}
|
||||
|
||||
# Return 1 if the selection exists and is contained
|
||||
# entirely on the input line. Return 2 if the selection
|
||||
# exists but is not entirely on the input line. Return 0
|
||||
# if the selection does not exist.
|
||||
#
|
||||
proc sqlitecon::canCut w {
|
||||
set r [catch {
|
||||
scan [$w index sel.first] %d.%d s1x s1y
|
||||
scan [$w index sel.last] %d.%d s2x s2y
|
||||
scan [$w index insert] %d.%d ix iy
|
||||
}]
|
||||
if {$r==1} {return 0}
|
||||
if {$s1x==$ix && $s2x==$ix} {return 1}
|
||||
return 2
|
||||
}
|
||||
|
||||
# Do a Cut operation if possible. Cuts are only allowed
|
||||
# if the current selection is entirely contained on the
|
||||
# current input line.
|
||||
#
|
||||
proc sqlitecon::Cut w {
|
||||
if {[sqlitecon::canCut $w]==1} {
|
||||
sqlitecon::Copy $w
|
||||
$w delete sel.first sel.last
|
||||
}
|
||||
}
|
||||
|
||||
# Do a paste opeation.
|
||||
#
|
||||
proc sqlitecon::Paste w {
|
||||
if {[sqlitecon::canCut $w]==1} {
|
||||
$w delete sel.first sel.last
|
||||
}
|
||||
if {[catch {selection get -displayof $w -selection CLIPBOARD} topaste]
|
||||
&& [catch {selection get -displayof $w -selection PRIMARY} topaste]} {
|
||||
return
|
||||
}
|
||||
if {[info exists ::$w]} {
|
||||
set prior 0
|
||||
foreach line [split $topaste \n] {
|
||||
if {$prior} {
|
||||
sqlitecon::Enter $w
|
||||
update
|
||||
}
|
||||
set prior 1
|
||||
$w insert insert $line
|
||||
}
|
||||
} else {
|
||||
$w insert insert $topaste
|
||||
}
|
||||
}
|
||||
|
||||
# Enable or disable entries in the Edit menu
|
||||
#
|
||||
proc sqlitecon::EnableEditMenu w {
|
||||
upvar #0 $w.t v
|
||||
set m $v(editmenu)
|
||||
if {$m=="" || ![winfo exists $m]} return
|
||||
switch [sqlitecon::canCut $w.t] {
|
||||
0 {
|
||||
$m entryconf Copy -state disabled
|
||||
$m entryconf Cut -state disabled
|
||||
}
|
||||
1 {
|
||||
$m entryconf Copy -state normal
|
||||
$m entryconf Cut -state normal
|
||||
}
|
||||
2 {
|
||||
$m entryconf Copy -state normal
|
||||
$m entryconf Cut -state disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Prompt the user for the name of a writable file. Then write the
|
||||
# entire contents of the console screen to that file.
|
||||
#
|
||||
proc sqlitecon::SaveFile w {
|
||||
set types {
|
||||
{{Text Files} {.txt}}
|
||||
{{All Files} *}
|
||||
}
|
||||
set f [tk_getSaveFile -filetypes $types -title "Write Screen To..."]
|
||||
if {$f!=""} {
|
||||
if {[catch {open $f w} fd]} {
|
||||
tk_messageBox -type ok -icon error -message $fd
|
||||
} else {
|
||||
puts $fd [string trimright [$w get 1.0 end] \n]
|
||||
close $fd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Erase everything from the console above the insertion line.
|
||||
#
|
||||
proc sqlitecon::Clear w {
|
||||
$w delete 1.0 {insert linestart}
|
||||
}
|
||||
|
||||
# An in-line editor for SQL
|
||||
#
|
||||
proc sqlitecon::_edit {origtxt {title {}}} {
|
||||
for {set i 0} {[winfo exists .ed$i]} {incr i} continue
|
||||
set w .ed$i
|
||||
toplevel $w
|
||||
wm protocol $w WM_DELETE_WINDOW "$w.b.can invoke"
|
||||
wm title $w {Inline SQL Editor}
|
||||
frame $w.b
|
||||
pack $w.b -side bottom -fill x
|
||||
button $w.b.can -text Cancel -width 6 -command [list set ::$w 0]
|
||||
button $w.b.ok -text OK -width 6 -command [list set ::$w 1]
|
||||
button $w.b.cut -text Cut -width 6 -command [list ::sqlitecon::Cut $w.t]
|
||||
button $w.b.copy -text Copy -width 6 -command [list ::sqlitecon::Copy $w.t]
|
||||
button $w.b.paste -text Paste -width 6 -command [list ::sqlitecon::Paste $w.t]
|
||||
set ::$w {}
|
||||
pack $w.b.cut $w.b.copy $w.b.paste $w.b.can $w.b.ok\
|
||||
-side left -padx 5 -pady 5 -expand 1
|
||||
if {$title!=""} {
|
||||
label $w.title -text $title
|
||||
pack $w.title -side top -padx 5 -pady 5
|
||||
}
|
||||
text $w.t -bg white -fg black -yscrollcommand [list $w.sb set]
|
||||
pack $w.t -side left -fill both -expand 1
|
||||
scrollbar $w.sb -orient vertical -command [list $w.t yview]
|
||||
pack $w.sb -side left -fill y
|
||||
$w.t insert end $origtxt
|
||||
|
||||
vwait ::$w
|
||||
|
||||
if {[set ::$w]} {
|
||||
set txt [string trimright [$w.t get 1.0 end]]
|
||||
} else {
|
||||
set txt $origtxt
|
||||
}
|
||||
destroy $w
|
||||
return $txt
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
Version loadable extensions to SQLite are found in subfolders
|
||||
of this folder.
|
|
@ -1,2 +0,0 @@
|
|||
This folder contains source code to the first full-text search
|
||||
extension for SQLite.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +0,0 @@
|
|||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sqlite3Fts1Init(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
|
@ -1,369 +0,0 @@
|
|||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the implementation of generic hash-tables used in SQLite.
|
||||
** We've modified it slightly to serve as a standalone hash table
|
||||
** implementation for the full-text indexing module.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS1 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS1 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
|
||||
|
||||
|
||||
#include "fts1_hash.h"
|
||||
|
||||
static void *malloc_and_zero(int n){
|
||||
void *p = malloc(n);
|
||||
if( p ){
|
||||
memset(p, 0, n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
** keyClass is one of the constants
|
||||
** FTS1_HASH_BINARY or FTS1_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.
|
||||
*/
|
||||
void sqlite3Fts1HashInit(fts1Hash *pNew, int keyClass, int copyKey){
|
||||
assert( pNew!=0 );
|
||||
assert( keyClass>=FTS1_HASH_STRING && keyClass<=FTS1_HASH_BINARY );
|
||||
pNew->keyClass = keyClass;
|
||||
pNew->copyKey = copyKey;
|
||||
pNew->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
pNew->ht = 0;
|
||||
pNew->xMalloc = malloc_and_zero;
|
||||
pNew->xFree = free;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
void sqlite3Fts1HashClear(fts1Hash *pH){
|
||||
fts1HashElem *elem; /* For looping over all elements of the table */
|
||||
|
||||
assert( pH!=0 );
|
||||
elem = pH->first;
|
||||
pH->first = 0;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = 0;
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
fts1HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
pH->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is FTS1_HASH_STRING
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
if( nKey<=0 ) nKey = (int) strlen(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ *z++;
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return strncmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is FTS1_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( keyClass==FTS1_HASH_STRING ){
|
||||
return &strHash;
|
||||
}else{
|
||||
assert( keyClass==FTS1_HASH_BINARY );
|
||||
return &binHash;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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( keyClass==FTS1_HASH_STRING ){
|
||||
return &strCompare;
|
||||
}else{
|
||||
assert( keyClass==FTS1_HASH_BINARY );
|
||||
return &binCompare;
|
||||
}
|
||||
}
|
||||
|
||||
/* Link an element into the hash table
|
||||
*/
|
||||
static void insertElement(
|
||||
fts1Hash *pH, /* The complete hash table */
|
||||
struct _fts1ht *pEntry, /* The entry into which pNew is inserted */
|
||||
fts1HashElem *pNew /* The element to be inserted */
|
||||
){
|
||||
fts1HashElem *pHead; /* First element already in pEntry */
|
||||
pHead = pEntry->chain;
|
||||
if( pHead ){
|
||||
pNew->next = pHead;
|
||||
pNew->prev = pHead->prev;
|
||||
if( pHead->prev ){ pHead->prev->next = pNew; }
|
||||
else { pH->first = pNew; }
|
||||
pHead->prev = pNew;
|
||||
}else{
|
||||
pNew->next = pH->first;
|
||||
if( pH->first ){ pH->first->prev = pNew; }
|
||||
pNew->prev = 0;
|
||||
pH->first = pNew;
|
||||
}
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqliteMalloc() fails.
|
||||
*/
|
||||
static void rehash(fts1Hash *pH, int new_size){
|
||||
struct _fts1ht *new_ht; /* The new hash table */
|
||||
fts1HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( (new_size & (new_size-1))==0 );
|
||||
new_ht = (struct _fts1ht *)pH->xMalloc( new_size*sizeof(struct _fts1ht) );
|
||||
if( new_ht==0 ) return;
|
||||
if( pH->ht ) pH->xFree(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);
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static fts1HashElem *findElementGivenHash(
|
||||
const fts1Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
fts1HashElem *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 _fts1ht *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 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
fts1Hash *pH, /* The pH containing "elem" */
|
||||
fts1HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
struct _fts1ht *pEntry;
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
pH->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
if( pEntry->count<=0 ){
|
||||
pEntry->chain = 0;
|
||||
}
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree( elem );
|
||||
pH->count--;
|
||||
if( pH->count<=0 ){
|
||||
assert( pH->first==0 );
|
||||
assert( pH->count==0 );
|
||||
fts1HashClear(pH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *sqlite3Fts1HashFind(const fts1Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
fts1HashElem *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);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *sqlite3Fts1HashInsert(
|
||||
fts1Hash *pH, /* The hash table to insert into */
|
||||
const void *pKey, /* The key */
|
||||
int nKey, /* Number of bytes in the key */
|
||||
void *data /* The data */
|
||||
){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
fts1HashElem *elem; /* Used to loop thru the element list */
|
||||
fts1HashElem *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);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (fts1HashElem*)pH->xMalloc( sizeof(fts1HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = pH->xMalloc( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
}
|
||||
assert( pH->htsize>0 );
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the header file for the generic hash-table implemenation
|
||||
** used in SQLite. We've modified it slightly to serve as a standalone
|
||||
** hash table implementation for the full-text indexing module.
|
||||
**
|
||||
*/
|
||||
#ifndef _FTS1_HASH_H_
|
||||
#define _FTS1_HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct fts1Hash fts1Hash;
|
||||
typedef struct fts1HashElem fts1HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct fts1Hash {
|
||||
char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
|
||||
char copyKey; /* True if copy of key made on insert */
|
||||
int count; /* Number of entries in this table */
|
||||
fts1HashElem *first; /* The first element of the array */
|
||||
void *(*xMalloc)(int); /* malloc() function to use */
|
||||
void (*xFree)(void *); /* free() function to use */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _fts1ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
fts1HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct fts1HashElem {
|
||||
fts1HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** There are 2 different modes of operation for a hash table:
|
||||
**
|
||||
** FTS1_HASH_STRING pKey points to a string that is nKey bytes long
|
||||
** (including the null-terminator, if any). Case
|
||||
** is respected in comparisons.
|
||||
**
|
||||
** FTS1_HASH_BINARY pKey points to binary data nKey bytes long.
|
||||
** memcmp() is used to compare keys.
|
||||
**
|
||||
** A copy of the key is made if the copyKey parameter to fts1HashInit is 1.
|
||||
*/
|
||||
#define FTS1_HASH_STRING 1
|
||||
#define FTS1_HASH_BINARY 2
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void sqlite3Fts1HashInit(fts1Hash*, int keytype, int copyKey);
|
||||
void *sqlite3Fts1HashInsert(fts1Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqlite3Fts1HashFind(const fts1Hash*, const void *pKey, int nKey);
|
||||
void sqlite3Fts1HashClear(fts1Hash*);
|
||||
|
||||
/*
|
||||
** Shorthand for the functions above
|
||||
*/
|
||||
#define fts1HashInit sqlite3Fts1HashInit
|
||||
#define fts1HashInsert sqlite3Fts1HashInsert
|
||||
#define fts1HashFind sqlite3Fts1HashFind
|
||||
#define fts1HashClear sqlite3Fts1HashClear
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** fts1Hash h;
|
||||
** fts1HashElem *p;
|
||||
** ...
|
||||
** for(p=fts1HashFirst(&h); p; p=fts1HashNext(p)){
|
||||
** SomeStructure *pData = fts1HashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
#define fts1HashFirst(H) ((H)->first)
|
||||
#define fts1HashNext(E) ((E)->next)
|
||||
#define fts1HashData(E) ((E)->data)
|
||||
#define fts1HashKey(E) ((E)->pKey)
|
||||
#define fts1HashKeysize(E) ((E)->nKey)
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define fts1HashCount(H) ((H)->count)
|
||||
|
||||
#endif /* _FTS1_HASH_H_ */
|
|
@ -1,642 +0,0 @@
|
|||
/*
|
||||
** 2006 September 30
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the full-text-search tokenizer that implements
|
||||
** a Porter stemmer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS1 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS1 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fts1_tokenizer.h"
|
||||
|
||||
/*
|
||||
** Class derived from sqlite3_tokenizer
|
||||
*/
|
||||
typedef struct porter_tokenizer {
|
||||
sqlite3_tokenizer base; /* Base class */
|
||||
} porter_tokenizer;
|
||||
|
||||
/*
|
||||
** Class derived from sqlit3_tokenizer_cursor
|
||||
*/
|
||||
typedef struct porter_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *zInput; /* input we are tokenizing */
|
||||
int nInput; /* size of the input */
|
||||
int iOffset; /* current position in zInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *zToken; /* storage for current token */
|
||||
int nAllocated; /* space allocated to zToken buffer */
|
||||
} porter_tokenizer_cursor;
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static const sqlite3_tokenizer_module porterTokenizerModule;
|
||||
|
||||
|
||||
/*
|
||||
** Create a new tokenizer instance.
|
||||
*/
|
||||
static int porterCreate(
|
||||
int argc, const char * const *argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
porter_tokenizer *t;
|
||||
t = (porter_tokenizer *) calloc(sizeof(porter_tokenizer), 1);
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a tokenizer
|
||||
*/
|
||||
static int porterDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
free(pTokenizer);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare to begin tokenizing a particular string. The input
|
||||
** string to be tokenized is zInput[0..nInput-1]. A cursor
|
||||
** used to incrementally tokenize this string is returned in
|
||||
** *ppCursor.
|
||||
*/
|
||||
static int porterOpen(
|
||||
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
|
||||
const char *zInput, int nInput, /* String to be tokenized */
|
||||
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
|
||||
){
|
||||
porter_tokenizer_cursor *c;
|
||||
|
||||
c = (porter_tokenizer_cursor *) malloc(sizeof(porter_tokenizer_cursor));
|
||||
c->zInput = zInput;
|
||||
if( zInput==0 ){
|
||||
c->nInput = 0;
|
||||
}else if( nInput<0 ){
|
||||
c->nInput = (int)strlen(zInput);
|
||||
}else{
|
||||
c->nInput = nInput;
|
||||
}
|
||||
c->iOffset = 0; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->zToken = NULL; /* no space allocated, yet. */
|
||||
c->nAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tokenization cursor previously opened by a call to
|
||||
** porterOpen() above.
|
||||
*/
|
||||
static int porterClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
|
||||
free(c->zToken);
|
||||
free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/*
|
||||
** Vowel or consonant
|
||||
*/
|
||||
static const char cType[] = {
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 2, 1
|
||||
};
|
||||
|
||||
/*
|
||||
** isConsonant() and isVowel() determine if their first character in
|
||||
** the string they point to is a consonant or a vowel, according
|
||||
** to Porter ruls.
|
||||
**
|
||||
** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.
|
||||
** 'Y' is a consonant unless it follows another consonant,
|
||||
** in which case it is a vowel.
|
||||
**
|
||||
** In these routine, the letters are in reverse order. So the 'y' rule
|
||||
** is that 'y' is a consonant unless it is followed by another
|
||||
** consonent.
|
||||
*/
|
||||
static int isVowel(const char*);
|
||||
static int isConsonant(const char *z){
|
||||
int j;
|
||||
char x = *z;
|
||||
if( x==0 ) return 0;
|
||||
assert( x>='a' && x<='z' );
|
||||
j = cType[x-'a'];
|
||||
if( j<2 ) return j;
|
||||
return z[1]==0 || isVowel(z + 1);
|
||||
}
|
||||
static int isVowel(const char *z){
|
||||
int j;
|
||||
char x = *z;
|
||||
if( x==0 ) return 0;
|
||||
assert( x>='a' && x<='z' );
|
||||
j = cType[x-'a'];
|
||||
if( j<2 ) return 1-j;
|
||||
return isConsonant(z + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Let any sequence of one or more vowels be represented by V and let
|
||||
** C be sequence of one or more consonants. Then every word can be
|
||||
** represented as:
|
||||
**
|
||||
** [C] (VC){m} [V]
|
||||
**
|
||||
** In prose: A word is an optional consonant followed by zero or
|
||||
** vowel-consonant pairs followed by an optional vowel. "m" is the
|
||||
** number of vowel consonant pairs. This routine computes the value
|
||||
** of m for the first i bytes of a word.
|
||||
**
|
||||
** Return true if the m-value for z is 1 or more. In other words,
|
||||
** return true if z contains at least one vowel that is followed
|
||||
** by a consonant.
|
||||
**
|
||||
** In this routine z[] is in reverse order. So we are really looking
|
||||
** for an instance of of a consonant followed by a vowel.
|
||||
*/
|
||||
static int m_gt_0(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/* Like mgt0 above except we are looking for a value of m which is
|
||||
** exactly 1
|
||||
*/
|
||||
static int m_eq_1(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 1;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z==0;
|
||||
}
|
||||
|
||||
/* Like mgt0 above except we are looking for a value of m>1 instead
|
||||
** or m>0
|
||||
*/
|
||||
static int m_gt_1(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if there is a vowel anywhere within z[0..n-1]
|
||||
*/
|
||||
static int hasVowel(const char *z){
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the word ends in a double consonant.
|
||||
**
|
||||
** The text is reversed here. So we are really looking at
|
||||
** the first two characters of z[].
|
||||
*/
|
||||
static int doubleConsonant(const char *z){
|
||||
return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the word ends with three letters which
|
||||
** are consonant-vowel-consonent and where the final consonant
|
||||
** is not 'w', 'x', or 'y'.
|
||||
**
|
||||
** The word is reversed here. So we are really checking the
|
||||
** first three letters and the first one cannot be in [wxy].
|
||||
*/
|
||||
static int star_oh(const char *z){
|
||||
return
|
||||
z[0]!=0 && isConsonant(z) &&
|
||||
z[0]!='w' && z[0]!='x' && z[0]!='y' &&
|
||||
z[1]!=0 && isVowel(z+1) &&
|
||||
z[2]!=0 && isConsonant(z+2);
|
||||
}
|
||||
|
||||
/*
|
||||
** If the word ends with zFrom and xCond() is true for the stem
|
||||
** of the word that preceeds the zFrom ending, then change the
|
||||
** ending to zTo.
|
||||
**
|
||||
** The input word *pz and zFrom are both in reverse order. zTo
|
||||
** is in normal order.
|
||||
**
|
||||
** Return TRUE if zFrom matches. Return FALSE if zFrom does not
|
||||
** match. Not that TRUE is returned even if xCond() fails and
|
||||
** no substitution occurs.
|
||||
*/
|
||||
static int stem(
|
||||
char **pz, /* The word being stemmed (Reversed) */
|
||||
const char *zFrom, /* If the ending matches this... (Reversed) */
|
||||
const char *zTo, /* ... change the ending to this (not reversed) */
|
||||
int (*xCond)(const char*) /* Condition that must be true */
|
||||
){
|
||||
char *z = *pz;
|
||||
while( *zFrom && *zFrom==*z ){ z++; zFrom++; }
|
||||
if( *zFrom!=0 ) return 0;
|
||||
if( xCond && !xCond(z) ) return 1;
|
||||
while( *zTo ){
|
||||
*(--z) = *(zTo++);
|
||||
}
|
||||
*pz = z;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the fallback stemmer used when the porter stemmer is
|
||||
** inappropriate. The input word is copied into the output with
|
||||
** US-ASCII case folding. If the input word is too long (more
|
||||
** than 20 bytes if it contains no digits or more than 6 bytes if
|
||||
** it contains digits) then word is truncated to 20 or 6 bytes
|
||||
** by taking 10 or 3 bytes from the beginning and end.
|
||||
*/
|
||||
static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
int i, mx, j;
|
||||
int hasDigit = 0;
|
||||
for(i=0; i<nIn; i++){
|
||||
int c = zIn[i];
|
||||
if( c>='A' && c<='Z' ){
|
||||
zOut[i] = c - 'A' + 'a';
|
||||
}else{
|
||||
if( c>='0' && c<='9' ) hasDigit = 1;
|
||||
zOut[i] = c;
|
||||
}
|
||||
}
|
||||
mx = hasDigit ? 3 : 10;
|
||||
if( nIn>mx*2 ){
|
||||
for(j=mx, i=nIn-mx; i<nIn; i++, j++){
|
||||
zOut[j] = zOut[i];
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
zOut[i] = 0;
|
||||
*pnOut = i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Stem the input word zIn[0..nIn-1]. Store the output in zOut.
|
||||
** zOut is at least big enough to hold nIn bytes. Write the actual
|
||||
** size of the output word (exclusive of the '\0' terminator) into *pnOut.
|
||||
**
|
||||
** Any upper-case characters in the US-ASCII character set ([A-Z])
|
||||
** are converted to lower case. Upper-case UTF characters are
|
||||
** unchanged.
|
||||
**
|
||||
** Words that are longer than about 20 bytes are stemmed by retaining
|
||||
** a few bytes from the beginning and the end of the word. If the
|
||||
** word contains digits, 3 bytes are taken from the beginning and
|
||||
** 3 bytes from the end. For long words without digits, 10 bytes
|
||||
** are taken from each end. US-ASCII case folding still applies.
|
||||
**
|
||||
** If the input word contains not digits but does characters not
|
||||
** in [a-zA-Z] then no stemming is attempted and this routine just
|
||||
** copies the input into the input into the output with US-ASCII
|
||||
** case folding.
|
||||
**
|
||||
** Stemming never increases the length of the word. So there is
|
||||
** no chance of overflowing the zOut buffer.
|
||||
*/
|
||||
static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
int i, j, c;
|
||||
char zReverse[28];
|
||||
char *z, *z2;
|
||||
if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
|
||||
/* The word is too big or too small for the porter stemmer.
|
||||
** Fallback to the copy stemmer */
|
||||
copy_stemmer(zIn, nIn, zOut, pnOut);
|
||||
return;
|
||||
}
|
||||
for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){
|
||||
c = zIn[i];
|
||||
if( c>='A' && c<='Z' ){
|
||||
zReverse[j] = c + 'a' - 'A';
|
||||
}else if( c>='a' && c<='z' ){
|
||||
zReverse[j] = c;
|
||||
}else{
|
||||
/* The use of a character not in [a-zA-Z] means that we fallback
|
||||
** to the copy stemmer */
|
||||
copy_stemmer(zIn, nIn, zOut, pnOut);
|
||||
return;
|
||||
}
|
||||
}
|
||||
memset(&zReverse[sizeof(zReverse)-5], 0, 5);
|
||||
z = &zReverse[j+1];
|
||||
|
||||
|
||||
/* Step 1a */
|
||||
if( z[0]=='s' ){
|
||||
if(
|
||||
!stem(&z, "sess", "ss", 0) &&
|
||||
!stem(&z, "sei", "i", 0) &&
|
||||
!stem(&z, "ss", "ss", 0)
|
||||
){
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1b */
|
||||
z2 = z;
|
||||
if( stem(&z, "dee", "ee", m_gt_0) ){
|
||||
/* Do nothing. The work was all in the test */
|
||||
}else if(
|
||||
(stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
|
||||
&& z!=z2
|
||||
){
|
||||
if( stem(&z, "ta", "ate", 0) ||
|
||||
stem(&z, "lb", "ble", 0) ||
|
||||
stem(&z, "zi", "ize", 0) ){
|
||||
/* Do nothing. The work was all in the test */
|
||||
}else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){
|
||||
z++;
|
||||
}else if( m_eq_1(z) && star_oh(z) ){
|
||||
*(--z) = 'e';
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1c */
|
||||
if( z[0]=='y' && hasVowel(z+1) ){
|
||||
z[0] = 'i';
|
||||
}
|
||||
|
||||
/* Step 2 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
stem(&z, "lanoita", "ate", m_gt_0) ||
|
||||
stem(&z, "lanoit", "tion", m_gt_0);
|
||||
break;
|
||||
case 'c':
|
||||
stem(&z, "icne", "ence", m_gt_0) ||
|
||||
stem(&z, "icna", "ance", m_gt_0);
|
||||
break;
|
||||
case 'e':
|
||||
stem(&z, "rezi", "ize", m_gt_0);
|
||||
break;
|
||||
case 'g':
|
||||
stem(&z, "igol", "log", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "ilb", "ble", m_gt_0) ||
|
||||
stem(&z, "illa", "al", m_gt_0) ||
|
||||
stem(&z, "iltne", "ent", m_gt_0) ||
|
||||
stem(&z, "ile", "e", m_gt_0) ||
|
||||
stem(&z, "ilsuo", "ous", m_gt_0);
|
||||
break;
|
||||
case 'o':
|
||||
stem(&z, "noitazi", "ize", m_gt_0) ||
|
||||
stem(&z, "noita", "ate", m_gt_0) ||
|
||||
stem(&z, "rota", "ate", m_gt_0);
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "msila", "al", m_gt_0) ||
|
||||
stem(&z, "ssenevi", "ive", m_gt_0) ||
|
||||
stem(&z, "ssenluf", "ful", m_gt_0) ||
|
||||
stem(&z, "ssensuo", "ous", m_gt_0);
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "itila", "al", m_gt_0) ||
|
||||
stem(&z, "itivi", "ive", m_gt_0) ||
|
||||
stem(&z, "itilib", "ble", m_gt_0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 3 */
|
||||
switch( z[0] ){
|
||||
case 'e':
|
||||
stem(&z, "etaci", "ic", m_gt_0) ||
|
||||
stem(&z, "evita", "", m_gt_0) ||
|
||||
stem(&z, "ezila", "al", m_gt_0);
|
||||
break;
|
||||
case 'i':
|
||||
stem(&z, "itici", "ic", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "laci", "ic", m_gt_0) ||
|
||||
stem(&z, "luf", "", m_gt_0);
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "ssen", "", m_gt_0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 4 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
if( z[0]=='l' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){
|
||||
z += 4;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if( z[0]=='r' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
if( z[0]=='c' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){
|
||||
z += 4;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if( z[0]=='t' ){
|
||||
if( z[2]=='a' ){
|
||||
if( m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
}else if( z[2]=='e' ){
|
||||
stem(&z, "tneme", "", m_gt_1) ||
|
||||
stem(&z, "tnem", "", m_gt_1) ||
|
||||
stem(&z, "tne", "", m_gt_1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if( z[0]=='u' ){
|
||||
if( m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
}else if( z[3]=='s' || z[3]=='t' ){
|
||||
stem(&z, "noi", "", m_gt_1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "eta", "", m_gt_1) ||
|
||||
stem(&z, "iti", "", m_gt_1);
|
||||
break;
|
||||
case 'u':
|
||||
if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
case 'z':
|
||||
if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 5a */
|
||||
if( z[0]=='e' ){
|
||||
if( m_gt_1(z+1) ){
|
||||
z++;
|
||||
}else if( m_eq_1(z+1) && !star_oh(z+1) ){
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 5b */
|
||||
if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){
|
||||
z++;
|
||||
}
|
||||
|
||||
/* z[] is now the stemmed word in reverse order. Flip it back
|
||||
** around into forward order and return.
|
||||
*/
|
||||
*pnOut = i = strlen(z);
|
||||
zOut[i] = 0;
|
||||
while( *z ){
|
||||
zOut[--i] = *(z++);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Characters that can be part of a token. We assume any character
|
||||
** whose value is greater than 0x80 (any UTF character) can be
|
||||
** part of a token. In other words, delimiters all must have
|
||||
** values of 0x7f or lower.
|
||||
*/
|
||||
static const char isIdChar[] = {
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||
};
|
||||
#define idChar(C) (((ch=C)&0x80)!=0 || (ch>0x2f && isIdChar[ch-0x30]))
|
||||
#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !isIdChar[ch-0x30]))
|
||||
|
||||
/*
|
||||
** Extract the next token from a tokenization cursor. The cursor must
|
||||
** have been opened by a prior call to porterOpen().
|
||||
*/
|
||||
static int porterNext(
|
||||
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */
|
||||
const char **pzToken, /* OUT: *pzToken is the token text */
|
||||
int *pnBytes, /* OUT: Number of bytes in token */
|
||||
int *piStartOffset, /* OUT: Starting offset of token */
|
||||
int *piEndOffset, /* OUT: Ending offset of token */
|
||||
int *piPosition /* OUT: Position integer of token */
|
||||
){
|
||||
porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
|
||||
const char *z = c->zInput;
|
||||
|
||||
while( c->iOffset<c->nInput ){
|
||||
int iStartOffset, ch;
|
||||
|
||||
/* Scan past delimiter characters */
|
||||
while( c->iOffset<c->nInput && isDelim(z[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
/* Count non-delimiter characters. */
|
||||
iStartOffset = c->iOffset;
|
||||
while( c->iOffset<c->nInput && !isDelim(z[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
if( c->iOffset>iStartOffset ){
|
||||
int n = c->iOffset-iStartOffset;
|
||||
if( n>c->nAllocated ){
|
||||
c->nAllocated = n+20;
|
||||
c->zToken = realloc(c->zToken, c->nAllocated);
|
||||
}
|
||||
porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes);
|
||||
*pzToken = c->zToken;
|
||||
*piStartOffset = iStartOffset;
|
||||
*piEndOffset = c->iOffset;
|
||||
*piPosition = c->iToken++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The set of routines that implement the porter-stemmer tokenizer
|
||||
*/
|
||||
static const sqlite3_tokenizer_module porterTokenizerModule = {
|
||||
0,
|
||||
porterCreate,
|
||||
porterDestroy,
|
||||
porterOpen,
|
||||
porterClose,
|
||||
porterNext,
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate a new porter tokenizer. Return a pointer to the new
|
||||
** tokenizer in *ppModule
|
||||
*/
|
||||
void sqlite3Fts1PorterTokenizerModule(
|
||||
sqlite3_tokenizer_module const**ppModule
|
||||
){
|
||||
*ppModule = &porterTokenizerModule;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
** 2006 July 10
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Defines the interface to tokenizers used by fulltext-search. There
|
||||
** are three basic components:
|
||||
**
|
||||
** sqlite3_tokenizer_module is a singleton defining the tokenizer
|
||||
** interface functions. This is essentially the class structure for
|
||||
** tokenizers.
|
||||
**
|
||||
** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
|
||||
** including customization information defined at creation time.
|
||||
**
|
||||
** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
|
||||
** tokens from a particular input.
|
||||
*/
|
||||
#ifndef _FTS1_TOKENIZER_H_
|
||||
#define _FTS1_TOKENIZER_H_
|
||||
|
||||
/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
|
||||
** If tokenizers are to be allowed to call sqlite3_*() functions, then
|
||||
** we will need a way to register the API consistently.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Structures used by the tokenizer interface.
|
||||
*/
|
||||
typedef struct sqlite3_tokenizer sqlite3_tokenizer;
|
||||
typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
|
||||
typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
|
||||
|
||||
struct sqlite3_tokenizer_module {
|
||||
int iVersion; /* currently 0 */
|
||||
|
||||
/*
|
||||
** Create and destroy a tokenizer. argc/argv are passed down from
|
||||
** the fulltext virtual table creation to allow customization.
|
||||
*/
|
||||
int (*xCreate)(int argc, const char *const*argv,
|
||||
sqlite3_tokenizer **ppTokenizer);
|
||||
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
|
||||
|
||||
/*
|
||||
** Tokenize a particular input. Call xOpen() to prepare to
|
||||
** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
|
||||
** xClose() to free any internal state. The pInput passed to
|
||||
** xOpen() must exist until the cursor is closed. The ppToken
|
||||
** result from xNext() is only valid until the next call to xNext()
|
||||
** or until xClose() is called.
|
||||
*/
|
||||
/* TODO(shess) current implementation requires pInput to be
|
||||
** nul-terminated. This should either be fixed, or pInput/nBytes
|
||||
** should be converted to zInput.
|
||||
*/
|
||||
int (*xOpen)(sqlite3_tokenizer *pTokenizer,
|
||||
const char *pInput, int nBytes,
|
||||
sqlite3_tokenizer_cursor **ppCursor);
|
||||
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
|
||||
int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
|
||||
const char **ppToken, int *pnBytes,
|
||||
int *piStartOffset, int *piEndOffset, int *piPosition);
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer {
|
||||
const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer_cursor {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
/*
|
||||
** Get the module for a tokenizer which generates tokens based on a
|
||||
** set of non-token characters. The default is to break tokens at any
|
||||
** non-alnum character, though the set of delimiters can also be
|
||||
** specified by the first argv argument to xCreate().
|
||||
*/
|
||||
/* TODO(shess) This doesn't belong here. Need some sort of
|
||||
** registration process.
|
||||
*/
|
||||
void sqlite3Fts1SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
void sqlite3Fts1PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
|
||||
#endif /* _FTS1_TOKENIZER_H_ */
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the "simple" full-text-search tokenizer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS1 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS1 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fts1_tokenizer.h"
|
||||
|
||||
typedef struct simple_tokenizer {
|
||||
sqlite3_tokenizer base;
|
||||
char delim[128]; /* flag ASCII delimiters */
|
||||
} simple_tokenizer;
|
||||
|
||||
typedef struct simple_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *pInput; /* input we are tokenizing */
|
||||
int nBytes; /* size of the input */
|
||||
int iOffset; /* current position in pInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *pToken; /* storage for current token */
|
||||
int nTokenAllocated; /* space allocated to zToken buffer */
|
||||
} simple_tokenizer_cursor;
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static const sqlite3_tokenizer_module simpleTokenizerModule;
|
||||
|
||||
static int isDelim(simple_tokenizer *t, unsigned char c){
|
||||
return c<0x80 && t->delim[c];
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new tokenizer instance.
|
||||
*/
|
||||
static int simpleCreate(
|
||||
int argc, const char * const *argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
simple_tokenizer *t;
|
||||
|
||||
t = (simple_tokenizer *) calloc(sizeof(simple_tokenizer), 1);
|
||||
/* TODO(shess) Delimiters need to remain the same from run to run,
|
||||
** else we need to reindex. One solution would be a meta-table to
|
||||
** track such information in the database, then we'd only want this
|
||||
** information on the initial create.
|
||||
*/
|
||||
if( argc>1 ){
|
||||
int i, n = strlen(argv[1]);
|
||||
for(i=0; i<n; i++){
|
||||
unsigned char ch = argv[1][i];
|
||||
/* We explicitly don't support UTF-8 delimiters for now. */
|
||||
if( ch>=0x80 ){
|
||||
free(t);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
t->delim[ch] = 1;
|
||||
}
|
||||
} else {
|
||||
/* Mark non-alphanumeric ASCII characters as delimiters */
|
||||
int i;
|
||||
for(i=1; i<0x80; i++){
|
||||
t->delim[i] = !isalnum(i);
|
||||
}
|
||||
}
|
||||
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a tokenizer
|
||||
*/
|
||||
static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
free(pTokenizer);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare to begin tokenizing a particular string. The input
|
||||
** string to be tokenized is pInput[0..nBytes-1]. A cursor
|
||||
** used to incrementally tokenize this string is returned in
|
||||
** *ppCursor.
|
||||
*/
|
||||
static int simpleOpen(
|
||||
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
|
||||
const char *pInput, int nBytes, /* String to be tokenized */
|
||||
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
|
||||
){
|
||||
simple_tokenizer_cursor *c;
|
||||
|
||||
c = (simple_tokenizer_cursor *) malloc(sizeof(simple_tokenizer_cursor));
|
||||
c->pInput = pInput;
|
||||
if( pInput==0 ){
|
||||
c->nBytes = 0;
|
||||
}else if( nBytes<0 ){
|
||||
c->nBytes = (int)strlen(pInput);
|
||||
}else{
|
||||
c->nBytes = nBytes;
|
||||
}
|
||||
c->iOffset = 0; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->pToken = NULL; /* no space allocated, yet. */
|
||||
c->nTokenAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tokenization cursor previously opened by a call to
|
||||
** simpleOpen() above.
|
||||
*/
|
||||
static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
free(c->pToken);
|
||||
free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the next token from a tokenization cursor. The cursor must
|
||||
** have been opened by a prior call to simpleOpen().
|
||||
*/
|
||||
static int simpleNext(
|
||||
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
|
||||
const char **ppToken, /* OUT: *ppToken is the token text */
|
||||
int *pnBytes, /* OUT: Number of bytes in token */
|
||||
int *piStartOffset, /* OUT: Starting offset of token */
|
||||
int *piEndOffset, /* OUT: Ending offset of token */
|
||||
int *piPosition /* OUT: Position integer of token */
|
||||
){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
|
||||
unsigned char *p = (unsigned char *)c->pInput;
|
||||
|
||||
while( c->iOffset<c->nBytes ){
|
||||
int iStartOffset;
|
||||
|
||||
/* Scan past delimiter characters */
|
||||
while( c->iOffset<c->nBytes && isDelim(t, p[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
/* Count non-delimiter characters. */
|
||||
iStartOffset = c->iOffset;
|
||||
while( c->iOffset<c->nBytes && !isDelim(t, p[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
if( c->iOffset>iStartOffset ){
|
||||
int i, n = c->iOffset-iStartOffset;
|
||||
if( n>c->nTokenAllocated ){
|
||||
c->nTokenAllocated = n+20;
|
||||
c->pToken = realloc(c->pToken, c->nTokenAllocated);
|
||||
}
|
||||
for(i=0; i<n; i++){
|
||||
/* TODO(shess) This needs expansion to handle UTF-8
|
||||
** case-insensitivity.
|
||||
*/
|
||||
unsigned char ch = p[iStartOffset+i];
|
||||
c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
|
||||
}
|
||||
*ppToken = c->pToken;
|
||||
*pnBytes = n;
|
||||
*piStartOffset = iStartOffset;
|
||||
*piEndOffset = c->iOffset;
|
||||
*piPosition = c->iToken++;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The set of routines that implement the simple tokenizer
|
||||
*/
|
||||
static const sqlite3_tokenizer_module simpleTokenizerModule = {
|
||||
0,
|
||||
simpleCreate,
|
||||
simpleDestroy,
|
||||
simpleOpen,
|
||||
simpleClose,
|
||||
simpleNext,
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate a new simple tokenizer. Return a pointer to the new
|
||||
** tokenizer in *ppModule
|
||||
*/
|
||||
void sqlite3Fts1SimpleTokenizerModule(
|
||||
sqlite3_tokenizer_module const**ppModule
|
||||
){
|
||||
*ppModule = &simpleTokenizerModule;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
|
|
@ -1,4 +0,0 @@
|
|||
This folder contains source code to the second full-text search
|
||||
extension for SQLite. While the API is the same, this version uses a
|
||||
substantially different storage schema from fts1, so tables will need
|
||||
to be rebuilt.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +0,0 @@
|
|||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sqlite3Fts2Init(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
|
@ -1,369 +0,0 @@
|
|||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the implementation of generic hash-tables used in SQLite.
|
||||
** We've modified it slightly to serve as a standalone hash table
|
||||
** implementation for the full-text indexing module.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS2 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS2 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
|
||||
|
||||
#include "fts2_hash.h"
|
||||
|
||||
static void *malloc_and_zero(int n){
|
||||
void *p = malloc(n);
|
||||
if( p ){
|
||||
memset(p, 0, n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
** keyClass is one of the constants
|
||||
** FTS2_HASH_BINARY or FTS2_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.
|
||||
*/
|
||||
void sqlite3Fts2HashInit(fts2Hash *pNew, int keyClass, int copyKey){
|
||||
assert( pNew!=0 );
|
||||
assert( keyClass>=FTS2_HASH_STRING && keyClass<=FTS2_HASH_BINARY );
|
||||
pNew->keyClass = keyClass;
|
||||
pNew->copyKey = copyKey;
|
||||
pNew->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
pNew->ht = 0;
|
||||
pNew->xMalloc = malloc_and_zero;
|
||||
pNew->xFree = free;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
void sqlite3Fts2HashClear(fts2Hash *pH){
|
||||
fts2HashElem *elem; /* For looping over all elements of the table */
|
||||
|
||||
assert( pH!=0 );
|
||||
elem = pH->first;
|
||||
pH->first = 0;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = 0;
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
fts2HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
pH->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is FTS2_HASH_STRING
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
if( nKey<=0 ) nKey = (int) strlen(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ *z++;
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return strncmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is FTS2_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( keyClass==FTS2_HASH_STRING ){
|
||||
return &strHash;
|
||||
}else{
|
||||
assert( keyClass==FTS2_HASH_BINARY );
|
||||
return &binHash;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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( keyClass==FTS2_HASH_STRING ){
|
||||
return &strCompare;
|
||||
}else{
|
||||
assert( keyClass==FTS2_HASH_BINARY );
|
||||
return &binCompare;
|
||||
}
|
||||
}
|
||||
|
||||
/* Link an element into the hash table
|
||||
*/
|
||||
static void insertElement(
|
||||
fts2Hash *pH, /* The complete hash table */
|
||||
struct _fts2ht *pEntry, /* The entry into which pNew is inserted */
|
||||
fts2HashElem *pNew /* The element to be inserted */
|
||||
){
|
||||
fts2HashElem *pHead; /* First element already in pEntry */
|
||||
pHead = pEntry->chain;
|
||||
if( pHead ){
|
||||
pNew->next = pHead;
|
||||
pNew->prev = pHead->prev;
|
||||
if( pHead->prev ){ pHead->prev->next = pNew; }
|
||||
else { pH->first = pNew; }
|
||||
pHead->prev = pNew;
|
||||
}else{
|
||||
pNew->next = pH->first;
|
||||
if( pH->first ){ pH->first->prev = pNew; }
|
||||
pNew->prev = 0;
|
||||
pH->first = pNew;
|
||||
}
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqliteMalloc() fails.
|
||||
*/
|
||||
static void rehash(fts2Hash *pH, int new_size){
|
||||
struct _fts2ht *new_ht; /* The new hash table */
|
||||
fts2HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( (new_size & (new_size-1))==0 );
|
||||
new_ht = (struct _fts2ht *)pH->xMalloc( new_size*sizeof(struct _fts2ht) );
|
||||
if( new_ht==0 ) return;
|
||||
if( pH->ht ) pH->xFree(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);
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static fts2HashElem *findElementGivenHash(
|
||||
const fts2Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
fts2HashElem *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 _fts2ht *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 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
fts2Hash *pH, /* The pH containing "elem" */
|
||||
fts2HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
struct _fts2ht *pEntry;
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
pH->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
if( pEntry->count<=0 ){
|
||||
pEntry->chain = 0;
|
||||
}
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree( elem );
|
||||
pH->count--;
|
||||
if( pH->count<=0 ){
|
||||
assert( pH->first==0 );
|
||||
assert( pH->count==0 );
|
||||
fts2HashClear(pH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *sqlite3Fts2HashFind(const fts2Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
fts2HashElem *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);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *sqlite3Fts2HashInsert(
|
||||
fts2Hash *pH, /* The hash table to insert into */
|
||||
const void *pKey, /* The key */
|
||||
int nKey, /* Number of bytes in the key */
|
||||
void *data /* The data */
|
||||
){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
fts2HashElem *elem; /* Used to loop thru the element list */
|
||||
fts2HashElem *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);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (fts2HashElem*)pH->xMalloc( sizeof(fts2HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = pH->xMalloc( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
}
|
||||
assert( pH->htsize>0 );
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the header file for the generic hash-table implemenation
|
||||
** used in SQLite. We've modified it slightly to serve as a standalone
|
||||
** hash table implementation for the full-text indexing module.
|
||||
**
|
||||
*/
|
||||
#ifndef _FTS2_HASH_H_
|
||||
#define _FTS2_HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct fts2Hash fts2Hash;
|
||||
typedef struct fts2HashElem fts2HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct fts2Hash {
|
||||
char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
|
||||
char copyKey; /* True if copy of key made on insert */
|
||||
int count; /* Number of entries in this table */
|
||||
fts2HashElem *first; /* The first element of the array */
|
||||
void *(*xMalloc)(int); /* malloc() function to use */
|
||||
void (*xFree)(void *); /* free() function to use */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _fts2ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
fts2HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct fts2HashElem {
|
||||
fts2HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** There are 2 different modes of operation for a hash table:
|
||||
**
|
||||
** FTS2_HASH_STRING pKey points to a string that is nKey bytes long
|
||||
** (including the null-terminator, if any). Case
|
||||
** is respected in comparisons.
|
||||
**
|
||||
** FTS2_HASH_BINARY pKey points to binary data nKey bytes long.
|
||||
** memcmp() is used to compare keys.
|
||||
**
|
||||
** A copy of the key is made if the copyKey parameter to fts2HashInit is 1.
|
||||
*/
|
||||
#define FTS2_HASH_STRING 1
|
||||
#define FTS2_HASH_BINARY 2
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void sqlite3Fts2HashInit(fts2Hash*, int keytype, int copyKey);
|
||||
void *sqlite3Fts2HashInsert(fts2Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqlite3Fts2HashFind(const fts2Hash*, const void *pKey, int nKey);
|
||||
void sqlite3Fts2HashClear(fts2Hash*);
|
||||
|
||||
/*
|
||||
** Shorthand for the functions above
|
||||
*/
|
||||
#define fts2HashInit sqlite3Fts2HashInit
|
||||
#define fts2HashInsert sqlite3Fts2HashInsert
|
||||
#define fts2HashFind sqlite3Fts2HashFind
|
||||
#define fts2HashClear sqlite3Fts2HashClear
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** fts2Hash h;
|
||||
** fts2HashElem *p;
|
||||
** ...
|
||||
** for(p=fts2HashFirst(&h); p; p=fts2HashNext(p)){
|
||||
** SomeStructure *pData = fts2HashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
#define fts2HashFirst(H) ((H)->first)
|
||||
#define fts2HashNext(E) ((E)->next)
|
||||
#define fts2HashData(E) ((E)->data)
|
||||
#define fts2HashKey(E) ((E)->pKey)
|
||||
#define fts2HashKeysize(E) ((E)->nKey)
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define fts2HashCount(H) ((H)->count)
|
||||
|
||||
#endif /* _FTS2_HASH_H_ */
|
|
@ -1,642 +0,0 @@
|
|||
/*
|
||||
** 2006 September 30
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the full-text-search tokenizer that implements
|
||||
** a Porter stemmer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS2 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS2 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fts2_tokenizer.h"
|
||||
|
||||
/*
|
||||
** Class derived from sqlite3_tokenizer
|
||||
*/
|
||||
typedef struct porter_tokenizer {
|
||||
sqlite3_tokenizer base; /* Base class */
|
||||
} porter_tokenizer;
|
||||
|
||||
/*
|
||||
** Class derived from sqlit3_tokenizer_cursor
|
||||
*/
|
||||
typedef struct porter_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *zInput; /* input we are tokenizing */
|
||||
int nInput; /* size of the input */
|
||||
int iOffset; /* current position in zInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *zToken; /* storage for current token */
|
||||
int nAllocated; /* space allocated to zToken buffer */
|
||||
} porter_tokenizer_cursor;
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static const sqlite3_tokenizer_module porterTokenizerModule;
|
||||
|
||||
|
||||
/*
|
||||
** Create a new tokenizer instance.
|
||||
*/
|
||||
static int porterCreate(
|
||||
int argc, const char * const *argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
porter_tokenizer *t;
|
||||
t = (porter_tokenizer *) calloc(sizeof(porter_tokenizer), 1);
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a tokenizer
|
||||
*/
|
||||
static int porterDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
free(pTokenizer);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare to begin tokenizing a particular string. The input
|
||||
** string to be tokenized is zInput[0..nInput-1]. A cursor
|
||||
** used to incrementally tokenize this string is returned in
|
||||
** *ppCursor.
|
||||
*/
|
||||
static int porterOpen(
|
||||
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
|
||||
const char *zInput, int nInput, /* String to be tokenized */
|
||||
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
|
||||
){
|
||||
porter_tokenizer_cursor *c;
|
||||
|
||||
c = (porter_tokenizer_cursor *) malloc(sizeof(porter_tokenizer_cursor));
|
||||
c->zInput = zInput;
|
||||
if( zInput==0 ){
|
||||
c->nInput = 0;
|
||||
}else if( nInput<0 ){
|
||||
c->nInput = (int)strlen(zInput);
|
||||
}else{
|
||||
c->nInput = nInput;
|
||||
}
|
||||
c->iOffset = 0; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->zToken = NULL; /* no space allocated, yet. */
|
||||
c->nAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tokenization cursor previously opened by a call to
|
||||
** porterOpen() above.
|
||||
*/
|
||||
static int porterClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
|
||||
free(c->zToken);
|
||||
free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/*
|
||||
** Vowel or consonant
|
||||
*/
|
||||
static const char cType[] = {
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 2, 1
|
||||
};
|
||||
|
||||
/*
|
||||
** isConsonant() and isVowel() determine if their first character in
|
||||
** the string they point to is a consonant or a vowel, according
|
||||
** to Porter ruls.
|
||||
**
|
||||
** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.
|
||||
** 'Y' is a consonant unless it follows another consonant,
|
||||
** in which case it is a vowel.
|
||||
**
|
||||
** In these routine, the letters are in reverse order. So the 'y' rule
|
||||
** is that 'y' is a consonant unless it is followed by another
|
||||
** consonent.
|
||||
*/
|
||||
static int isVowel(const char*);
|
||||
static int isConsonant(const char *z){
|
||||
int j;
|
||||
char x = *z;
|
||||
if( x==0 ) return 0;
|
||||
assert( x>='a' && x<='z' );
|
||||
j = cType[x-'a'];
|
||||
if( j<2 ) return j;
|
||||
return z[1]==0 || isVowel(z + 1);
|
||||
}
|
||||
static int isVowel(const char *z){
|
||||
int j;
|
||||
char x = *z;
|
||||
if( x==0 ) return 0;
|
||||
assert( x>='a' && x<='z' );
|
||||
j = cType[x-'a'];
|
||||
if( j<2 ) return 1-j;
|
||||
return isConsonant(z + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Let any sequence of one or more vowels be represented by V and let
|
||||
** C be sequence of one or more consonants. Then every word can be
|
||||
** represented as:
|
||||
**
|
||||
** [C] (VC){m} [V]
|
||||
**
|
||||
** In prose: A word is an optional consonant followed by zero or
|
||||
** vowel-consonant pairs followed by an optional vowel. "m" is the
|
||||
** number of vowel consonant pairs. This routine computes the value
|
||||
** of m for the first i bytes of a word.
|
||||
**
|
||||
** Return true if the m-value for z is 1 or more. In other words,
|
||||
** return true if z contains at least one vowel that is followed
|
||||
** by a consonant.
|
||||
**
|
||||
** In this routine z[] is in reverse order. So we are really looking
|
||||
** for an instance of of a consonant followed by a vowel.
|
||||
*/
|
||||
static int m_gt_0(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/* Like mgt0 above except we are looking for a value of m which is
|
||||
** exactly 1
|
||||
*/
|
||||
static int m_eq_1(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 1;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z==0;
|
||||
}
|
||||
|
||||
/* Like mgt0 above except we are looking for a value of m>1 instead
|
||||
** or m>0
|
||||
*/
|
||||
static int m_gt_1(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if there is a vowel anywhere within z[0..n-1]
|
||||
*/
|
||||
static int hasVowel(const char *z){
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the word ends in a double consonant.
|
||||
**
|
||||
** The text is reversed here. So we are really looking at
|
||||
** the first two characters of z[].
|
||||
*/
|
||||
static int doubleConsonant(const char *z){
|
||||
return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the word ends with three letters which
|
||||
** are consonant-vowel-consonent and where the final consonant
|
||||
** is not 'w', 'x', or 'y'.
|
||||
**
|
||||
** The word is reversed here. So we are really checking the
|
||||
** first three letters and the first one cannot be in [wxy].
|
||||
*/
|
||||
static int star_oh(const char *z){
|
||||
return
|
||||
z[0]!=0 && isConsonant(z) &&
|
||||
z[0]!='w' && z[0]!='x' && z[0]!='y' &&
|
||||
z[1]!=0 && isVowel(z+1) &&
|
||||
z[2]!=0 && isConsonant(z+2);
|
||||
}
|
||||
|
||||
/*
|
||||
** If the word ends with zFrom and xCond() is true for the stem
|
||||
** of the word that preceeds the zFrom ending, then change the
|
||||
** ending to zTo.
|
||||
**
|
||||
** The input word *pz and zFrom are both in reverse order. zTo
|
||||
** is in normal order.
|
||||
**
|
||||
** Return TRUE if zFrom matches. Return FALSE if zFrom does not
|
||||
** match. Not that TRUE is returned even if xCond() fails and
|
||||
** no substitution occurs.
|
||||
*/
|
||||
static int stem(
|
||||
char **pz, /* The word being stemmed (Reversed) */
|
||||
const char *zFrom, /* If the ending matches this... (Reversed) */
|
||||
const char *zTo, /* ... change the ending to this (not reversed) */
|
||||
int (*xCond)(const char*) /* Condition that must be true */
|
||||
){
|
||||
char *z = *pz;
|
||||
while( *zFrom && *zFrom==*z ){ z++; zFrom++; }
|
||||
if( *zFrom!=0 ) return 0;
|
||||
if( xCond && !xCond(z) ) return 1;
|
||||
while( *zTo ){
|
||||
*(--z) = *(zTo++);
|
||||
}
|
||||
*pz = z;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the fallback stemmer used when the porter stemmer is
|
||||
** inappropriate. The input word is copied into the output with
|
||||
** US-ASCII case folding. If the input word is too long (more
|
||||
** than 20 bytes if it contains no digits or more than 6 bytes if
|
||||
** it contains digits) then word is truncated to 20 or 6 bytes
|
||||
** by taking 10 or 3 bytes from the beginning and end.
|
||||
*/
|
||||
static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
int i, mx, j;
|
||||
int hasDigit = 0;
|
||||
for(i=0; i<nIn; i++){
|
||||
int c = zIn[i];
|
||||
if( c>='A' && c<='Z' ){
|
||||
zOut[i] = c - 'A' + 'a';
|
||||
}else{
|
||||
if( c>='0' && c<='9' ) hasDigit = 1;
|
||||
zOut[i] = c;
|
||||
}
|
||||
}
|
||||
mx = hasDigit ? 3 : 10;
|
||||
if( nIn>mx*2 ){
|
||||
for(j=mx, i=nIn-mx; i<nIn; i++, j++){
|
||||
zOut[j] = zOut[i];
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
zOut[i] = 0;
|
||||
*pnOut = i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Stem the input word zIn[0..nIn-1]. Store the output in zOut.
|
||||
** zOut is at least big enough to hold nIn bytes. Write the actual
|
||||
** size of the output word (exclusive of the '\0' terminator) into *pnOut.
|
||||
**
|
||||
** Any upper-case characters in the US-ASCII character set ([A-Z])
|
||||
** are converted to lower case. Upper-case UTF characters are
|
||||
** unchanged.
|
||||
**
|
||||
** Words that are longer than about 20 bytes are stemmed by retaining
|
||||
** a few bytes from the beginning and the end of the word. If the
|
||||
** word contains digits, 3 bytes are taken from the beginning and
|
||||
** 3 bytes from the end. For long words without digits, 10 bytes
|
||||
** are taken from each end. US-ASCII case folding still applies.
|
||||
**
|
||||
** If the input word contains not digits but does characters not
|
||||
** in [a-zA-Z] then no stemming is attempted and this routine just
|
||||
** copies the input into the input into the output with US-ASCII
|
||||
** case folding.
|
||||
**
|
||||
** Stemming never increases the length of the word. So there is
|
||||
** no chance of overflowing the zOut buffer.
|
||||
*/
|
||||
static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
int i, j, c;
|
||||
char zReverse[28];
|
||||
char *z, *z2;
|
||||
if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
|
||||
/* The word is too big or too small for the porter stemmer.
|
||||
** Fallback to the copy stemmer */
|
||||
copy_stemmer(zIn, nIn, zOut, pnOut);
|
||||
return;
|
||||
}
|
||||
for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){
|
||||
c = zIn[i];
|
||||
if( c>='A' && c<='Z' ){
|
||||
zReverse[j] = c + 'a' - 'A';
|
||||
}else if( c>='a' && c<='z' ){
|
||||
zReverse[j] = c;
|
||||
}else{
|
||||
/* The use of a character not in [a-zA-Z] means that we fallback
|
||||
** to the copy stemmer */
|
||||
copy_stemmer(zIn, nIn, zOut, pnOut);
|
||||
return;
|
||||
}
|
||||
}
|
||||
memset(&zReverse[sizeof(zReverse)-5], 0, 5);
|
||||
z = &zReverse[j+1];
|
||||
|
||||
|
||||
/* Step 1a */
|
||||
if( z[0]=='s' ){
|
||||
if(
|
||||
!stem(&z, "sess", "ss", 0) &&
|
||||
!stem(&z, "sei", "i", 0) &&
|
||||
!stem(&z, "ss", "ss", 0)
|
||||
){
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1b */
|
||||
z2 = z;
|
||||
if( stem(&z, "dee", "ee", m_gt_0) ){
|
||||
/* Do nothing. The work was all in the test */
|
||||
}else if(
|
||||
(stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
|
||||
&& z!=z2
|
||||
){
|
||||
if( stem(&z, "ta", "ate", 0) ||
|
||||
stem(&z, "lb", "ble", 0) ||
|
||||
stem(&z, "zi", "ize", 0) ){
|
||||
/* Do nothing. The work was all in the test */
|
||||
}else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){
|
||||
z++;
|
||||
}else if( m_eq_1(z) && star_oh(z) ){
|
||||
*(--z) = 'e';
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1c */
|
||||
if( z[0]=='y' && hasVowel(z+1) ){
|
||||
z[0] = 'i';
|
||||
}
|
||||
|
||||
/* Step 2 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
stem(&z, "lanoita", "ate", m_gt_0) ||
|
||||
stem(&z, "lanoit", "tion", m_gt_0);
|
||||
break;
|
||||
case 'c':
|
||||
stem(&z, "icne", "ence", m_gt_0) ||
|
||||
stem(&z, "icna", "ance", m_gt_0);
|
||||
break;
|
||||
case 'e':
|
||||
stem(&z, "rezi", "ize", m_gt_0);
|
||||
break;
|
||||
case 'g':
|
||||
stem(&z, "igol", "log", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "ilb", "ble", m_gt_0) ||
|
||||
stem(&z, "illa", "al", m_gt_0) ||
|
||||
stem(&z, "iltne", "ent", m_gt_0) ||
|
||||
stem(&z, "ile", "e", m_gt_0) ||
|
||||
stem(&z, "ilsuo", "ous", m_gt_0);
|
||||
break;
|
||||
case 'o':
|
||||
stem(&z, "noitazi", "ize", m_gt_0) ||
|
||||
stem(&z, "noita", "ate", m_gt_0) ||
|
||||
stem(&z, "rota", "ate", m_gt_0);
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "msila", "al", m_gt_0) ||
|
||||
stem(&z, "ssenevi", "ive", m_gt_0) ||
|
||||
stem(&z, "ssenluf", "ful", m_gt_0) ||
|
||||
stem(&z, "ssensuo", "ous", m_gt_0);
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "itila", "al", m_gt_0) ||
|
||||
stem(&z, "itivi", "ive", m_gt_0) ||
|
||||
stem(&z, "itilib", "ble", m_gt_0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 3 */
|
||||
switch( z[0] ){
|
||||
case 'e':
|
||||
stem(&z, "etaci", "ic", m_gt_0) ||
|
||||
stem(&z, "evita", "", m_gt_0) ||
|
||||
stem(&z, "ezila", "al", m_gt_0);
|
||||
break;
|
||||
case 'i':
|
||||
stem(&z, "itici", "ic", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "laci", "ic", m_gt_0) ||
|
||||
stem(&z, "luf", "", m_gt_0);
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "ssen", "", m_gt_0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 4 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
if( z[0]=='l' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){
|
||||
z += 4;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if( z[0]=='r' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
if( z[0]=='c' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){
|
||||
z += 4;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if( z[0]=='t' ){
|
||||
if( z[2]=='a' ){
|
||||
if( m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
}else if( z[2]=='e' ){
|
||||
stem(&z, "tneme", "", m_gt_1) ||
|
||||
stem(&z, "tnem", "", m_gt_1) ||
|
||||
stem(&z, "tne", "", m_gt_1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if( z[0]=='u' ){
|
||||
if( m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
}else if( z[3]=='s' || z[3]=='t' ){
|
||||
stem(&z, "noi", "", m_gt_1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "eta", "", m_gt_1) ||
|
||||
stem(&z, "iti", "", m_gt_1);
|
||||
break;
|
||||
case 'u':
|
||||
if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
case 'z':
|
||||
if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 5a */
|
||||
if( z[0]=='e' ){
|
||||
if( m_gt_1(z+1) ){
|
||||
z++;
|
||||
}else if( m_eq_1(z+1) && !star_oh(z+1) ){
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 5b */
|
||||
if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){
|
||||
z++;
|
||||
}
|
||||
|
||||
/* z[] is now the stemmed word in reverse order. Flip it back
|
||||
** around into forward order and return.
|
||||
*/
|
||||
*pnOut = i = strlen(z);
|
||||
zOut[i] = 0;
|
||||
while( *z ){
|
||||
zOut[--i] = *(z++);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Characters that can be part of a token. We assume any character
|
||||
** whose value is greater than 0x80 (any UTF character) can be
|
||||
** part of a token. In other words, delimiters all must have
|
||||
** values of 0x7f or lower.
|
||||
*/
|
||||
static const char isIdChar[] = {
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||
};
|
||||
#define idChar(C) (((ch=C)&0x80)!=0 || (ch>0x2f && isIdChar[ch-0x30]))
|
||||
#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !isIdChar[ch-0x30]))
|
||||
|
||||
/*
|
||||
** Extract the next token from a tokenization cursor. The cursor must
|
||||
** have been opened by a prior call to porterOpen().
|
||||
*/
|
||||
static int porterNext(
|
||||
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */
|
||||
const char **pzToken, /* OUT: *pzToken is the token text */
|
||||
int *pnBytes, /* OUT: Number of bytes in token */
|
||||
int *piStartOffset, /* OUT: Starting offset of token */
|
||||
int *piEndOffset, /* OUT: Ending offset of token */
|
||||
int *piPosition /* OUT: Position integer of token */
|
||||
){
|
||||
porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
|
||||
const char *z = c->zInput;
|
||||
|
||||
while( c->iOffset<c->nInput ){
|
||||
int iStartOffset, ch;
|
||||
|
||||
/* Scan past delimiter characters */
|
||||
while( c->iOffset<c->nInput && isDelim(z[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
/* Count non-delimiter characters. */
|
||||
iStartOffset = c->iOffset;
|
||||
while( c->iOffset<c->nInput && !isDelim(z[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
if( c->iOffset>iStartOffset ){
|
||||
int n = c->iOffset-iStartOffset;
|
||||
if( n>c->nAllocated ){
|
||||
c->nAllocated = n+20;
|
||||
c->zToken = realloc(c->zToken, c->nAllocated);
|
||||
}
|
||||
porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes);
|
||||
*pzToken = c->zToken;
|
||||
*piStartOffset = iStartOffset;
|
||||
*piEndOffset = c->iOffset;
|
||||
*piPosition = c->iToken++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The set of routines that implement the porter-stemmer tokenizer
|
||||
*/
|
||||
static const sqlite3_tokenizer_module porterTokenizerModule = {
|
||||
0,
|
||||
porterCreate,
|
||||
porterDestroy,
|
||||
porterOpen,
|
||||
porterClose,
|
||||
porterNext,
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate a new porter tokenizer. Return a pointer to the new
|
||||
** tokenizer in *ppModule
|
||||
*/
|
||||
void sqlite3Fts2PorterTokenizerModule(
|
||||
sqlite3_tokenizer_module const**ppModule
|
||||
){
|
||||
*ppModule = &porterTokenizerModule;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
** 2006 July 10
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Defines the interface to tokenizers used by fulltext-search. There
|
||||
** are three basic components:
|
||||
**
|
||||
** sqlite3_tokenizer_module is a singleton defining the tokenizer
|
||||
** interface functions. This is essentially the class structure for
|
||||
** tokenizers.
|
||||
**
|
||||
** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
|
||||
** including customization information defined at creation time.
|
||||
**
|
||||
** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
|
||||
** tokens from a particular input.
|
||||
*/
|
||||
#ifndef _FTS2_TOKENIZER_H_
|
||||
#define _FTS2_TOKENIZER_H_
|
||||
|
||||
/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
|
||||
** If tokenizers are to be allowed to call sqlite3_*() functions, then
|
||||
** we will need a way to register the API consistently.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Structures used by the tokenizer interface.
|
||||
*/
|
||||
typedef struct sqlite3_tokenizer sqlite3_tokenizer;
|
||||
typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
|
||||
typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
|
||||
|
||||
struct sqlite3_tokenizer_module {
|
||||
int iVersion; /* currently 0 */
|
||||
|
||||
/*
|
||||
** Create and destroy a tokenizer. argc/argv are passed down from
|
||||
** the fulltext virtual table creation to allow customization.
|
||||
*/
|
||||
int (*xCreate)(int argc, const char *const*argv,
|
||||
sqlite3_tokenizer **ppTokenizer);
|
||||
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
|
||||
|
||||
/*
|
||||
** Tokenize a particular input. Call xOpen() to prepare to
|
||||
** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
|
||||
** xClose() to free any internal state. The pInput passed to
|
||||
** xOpen() must exist until the cursor is closed. The ppToken
|
||||
** result from xNext() is only valid until the next call to xNext()
|
||||
** or until xClose() is called.
|
||||
*/
|
||||
/* TODO(shess) current implementation requires pInput to be
|
||||
** nul-terminated. This should either be fixed, or pInput/nBytes
|
||||
** should be converted to zInput.
|
||||
*/
|
||||
int (*xOpen)(sqlite3_tokenizer *pTokenizer,
|
||||
const char *pInput, int nBytes,
|
||||
sqlite3_tokenizer_cursor **ppCursor);
|
||||
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
|
||||
int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
|
||||
const char **ppToken, int *pnBytes,
|
||||
int *piStartOffset, int *piEndOffset, int *piPosition);
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer {
|
||||
const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer_cursor {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
/*
|
||||
** Get the module for a tokenizer which generates tokens based on a
|
||||
** set of non-token characters. The default is to break tokens at any
|
||||
** non-alnum character, though the set of delimiters can also be
|
||||
** specified by the first argv argument to xCreate().
|
||||
*/
|
||||
/* TODO(shess) This doesn't belong here. Need some sort of
|
||||
** registration process.
|
||||
*/
|
||||
void sqlite3Fts2SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
void sqlite3Fts2PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
|
||||
#endif /* _FTS2_TOKENIZER_H_ */
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the "simple" full-text-search tokenizer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS2 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS2 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fts2_tokenizer.h"
|
||||
|
||||
typedef struct simple_tokenizer {
|
||||
sqlite3_tokenizer base;
|
||||
char delim[128]; /* flag ASCII delimiters */
|
||||
} simple_tokenizer;
|
||||
|
||||
typedef struct simple_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *pInput; /* input we are tokenizing */
|
||||
int nBytes; /* size of the input */
|
||||
int iOffset; /* current position in pInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *pToken; /* storage for current token */
|
||||
int nTokenAllocated; /* space allocated to zToken buffer */
|
||||
} simple_tokenizer_cursor;
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static const sqlite3_tokenizer_module simpleTokenizerModule;
|
||||
|
||||
static int isDelim(simple_tokenizer *t, unsigned char c){
|
||||
return c<0x80 && t->delim[c];
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new tokenizer instance.
|
||||
*/
|
||||
static int simpleCreate(
|
||||
int argc, const char * const *argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
simple_tokenizer *t;
|
||||
|
||||
t = (simple_tokenizer *) calloc(sizeof(simple_tokenizer), 1);
|
||||
/* TODO(shess) Delimiters need to remain the same from run to run,
|
||||
** else we need to reindex. One solution would be a meta-table to
|
||||
** track such information in the database, then we'd only want this
|
||||
** information on the initial create.
|
||||
*/
|
||||
if( argc>1 ){
|
||||
int i, n = strlen(argv[1]);
|
||||
for(i=0; i<n; i++){
|
||||
unsigned char ch = argv[1][i];
|
||||
/* We explicitly don't support UTF-8 delimiters for now. */
|
||||
if( ch>=0x80 ){
|
||||
free(t);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
t->delim[ch] = 1;
|
||||
}
|
||||
} else {
|
||||
/* Mark non-alphanumeric ASCII characters as delimiters */
|
||||
int i;
|
||||
for(i=1; i<0x80; i++){
|
||||
t->delim[i] = !isalnum(i);
|
||||
}
|
||||
}
|
||||
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a tokenizer
|
||||
*/
|
||||
static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
free(pTokenizer);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare to begin tokenizing a particular string. The input
|
||||
** string to be tokenized is pInput[0..nBytes-1]. A cursor
|
||||
** used to incrementally tokenize this string is returned in
|
||||
** *ppCursor.
|
||||
*/
|
||||
static int simpleOpen(
|
||||
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
|
||||
const char *pInput, int nBytes, /* String to be tokenized */
|
||||
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
|
||||
){
|
||||
simple_tokenizer_cursor *c;
|
||||
|
||||
c = (simple_tokenizer_cursor *) malloc(sizeof(simple_tokenizer_cursor));
|
||||
c->pInput = pInput;
|
||||
if( pInput==0 ){
|
||||
c->nBytes = 0;
|
||||
}else if( nBytes<0 ){
|
||||
c->nBytes = (int)strlen(pInput);
|
||||
}else{
|
||||
c->nBytes = nBytes;
|
||||
}
|
||||
c->iOffset = 0; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->pToken = NULL; /* no space allocated, yet. */
|
||||
c->nTokenAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tokenization cursor previously opened by a call to
|
||||
** simpleOpen() above.
|
||||
*/
|
||||
static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
free(c->pToken);
|
||||
free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the next token from a tokenization cursor. The cursor must
|
||||
** have been opened by a prior call to simpleOpen().
|
||||
*/
|
||||
static int simpleNext(
|
||||
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
|
||||
const char **ppToken, /* OUT: *ppToken is the token text */
|
||||
int *pnBytes, /* OUT: Number of bytes in token */
|
||||
int *piStartOffset, /* OUT: Starting offset of token */
|
||||
int *piEndOffset, /* OUT: Ending offset of token */
|
||||
int *piPosition /* OUT: Position integer of token */
|
||||
){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
|
||||
unsigned char *p = (unsigned char *)c->pInput;
|
||||
|
||||
while( c->iOffset<c->nBytes ){
|
||||
int iStartOffset;
|
||||
|
||||
/* Scan past delimiter characters */
|
||||
while( c->iOffset<c->nBytes && isDelim(t, p[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
/* Count non-delimiter characters. */
|
||||
iStartOffset = c->iOffset;
|
||||
while( c->iOffset<c->nBytes && !isDelim(t, p[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
if( c->iOffset>iStartOffset ){
|
||||
int i, n = c->iOffset-iStartOffset;
|
||||
if( n>c->nTokenAllocated ){
|
||||
c->nTokenAllocated = n+20;
|
||||
c->pToken = realloc(c->pToken, c->nTokenAllocated);
|
||||
}
|
||||
for(i=0; i<n; i++){
|
||||
/* TODO(shess) This needs expansion to handle UTF-8
|
||||
** case-insensitivity.
|
||||
*/
|
||||
unsigned char ch = p[iStartOffset+i];
|
||||
c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
|
||||
}
|
||||
*ppToken = c->pToken;
|
||||
*pnBytes = n;
|
||||
*piStartOffset = iStartOffset;
|
||||
*piEndOffset = c->iOffset;
|
||||
*piPosition = c->iToken++;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The set of routines that implement the simple tokenizer
|
||||
*/
|
||||
static const sqlite3_tokenizer_module simpleTokenizerModule = {
|
||||
0,
|
||||
simpleCreate,
|
||||
simpleDestroy,
|
||||
simpleOpen,
|
||||
simpleClose,
|
||||
simpleNext,
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate a new simple tokenizer. Return a pointer to the new
|
||||
** tokenizer in *ppModule
|
||||
*/
|
||||
void sqlite3Fts2SimpleTokenizerModule(
|
||||
sqlite3_tokenizer_module const**ppModule
|
||||
){
|
||||
*ppModule = &simpleTokenizerModule;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
|
|
@ -1,618 +0,0 @@
|
|||
###############################################################################
|
||||
# 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.
|
||||
#
|
||||
# USLEEP If the target operating system supports the "usleep()" system
|
||||
# call, then define the HAVE_USLEEP macro for all C modules.
|
||||
#
|
||||
# THREADSAFE If you want the SQLite library to be safe for use within a
|
||||
# multi-threaded program, then define the following macro
|
||||
# appropriately:
|
||||
#
|
||||
# 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) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ+= alter.o analyze.o attach.o auth.o btree.o build.o \
|
||||
callback.o complete.o date.o delete.o \
|
||||
expr.o func.o hash.o insert.o loadext.o \
|
||||
main.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 table.o tclsqlite.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbefifo.o vdbemem.o \
|
||||
where.o utf.o legacy.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/btree.c \
|
||||
$(TOP)/src/btree.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/func.c \
|
||||
$(TOP)/src/hash.c \
|
||||
$(TOP)/src/hash.h \
|
||||
$(TOP)/src/insert.c \
|
||||
$(TOP)/src/legacy.c \
|
||||
$(TOP)/src/loadext.c \
|
||||
$(TOP)/src/main.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/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/shell.c \
|
||||
$(TOP)/src/sqlite.h.in \
|
||||
$(TOP)/src/sqliteInt.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/vdbefifo.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.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
|
||||
|
||||
|
||||
# Source code to the test files.
|
||||
#
|
||||
TESTSRC = \
|
||||
$(TOP)/src/btree.c \
|
||||
$(TOP)/src/date.c \
|
||||
$(TOP)/src/func.c \
|
||||
$(TOP)/src/main.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/printf.c \
|
||||
$(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/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_md5.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
$(TOP)/src/test_tclvar.c \
|
||||
$(TOP)/src/utf.c \
|
||||
$(TOP)/src/util.c \
|
||||
$(TOP)/src/vdbe.c \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/where.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
HDR = \
|
||||
sqlite3.h \
|
||||
$(TOP)/src/btree.h \
|
||||
$(TOP)/src/hash.h \
|
||||
opcodes.h \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/sqlite3ext.h \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/vdbe.h \
|
||||
parse.h
|
||||
|
||||
# Header files used by extensions
|
||||
#
|
||||
HDR += \
|
||||
$(TOP)/ext/fts1/fts1.h \
|
||||
$(TOP)/ext/fts1/fts1_hash.h \
|
||||
$(TOP)/ext/fts1/fts1_tokenizer.h
|
||||
|
||||
|
||||
# Header files used by the VDBE submodule
|
||||
#
|
||||
VDBEHDR = \
|
||||
$(HDR) \
|
||||
$(TOP)/src/vdbeInt.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)
|
||||
|
||||
# Generate the file "last_change" which contains the date of change
|
||||
# of the most recently modified source code file
|
||||
#
|
||||
last_change: $(SRC)
|
||||
cat $(SRC) | grep '$$Id: ' | sort -k 5 | tail -1 \
|
||||
| $(NAWK) '{print $$5,$$6}' >last_change
|
||||
|
||||
libsqlite3.a: $(LIBOBJ)
|
||||
$(AR) libsqlite3.a $(LIBOBJ)
|
||||
$(RANLIB) libsqlite3.a
|
||||
|
||||
sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
|
||||
$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(TOP)/src/shell.c \
|
||||
libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
|
||||
|
||||
objects: $(LIBOBJ_ORIG)
|
||||
|
||||
# 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) $(VDBEHDR) opcodes.c keywordhash.h
|
||||
rm -rf tsrc
|
||||
mkdir tsrc
|
||||
cp $(SRC) $(VDBEHDR) tsrc
|
||||
rm tsrc/sqlite.h.in tsrc/parse.y
|
||||
cp parse.c opcodes.c keywordhash.h tsrc
|
||||
|
||||
# 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 files
|
||||
#
|
||||
alter.o: $(TOP)/src/alter.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/alter.c
|
||||
|
||||
analyze.o: $(TOP)/src/analyze.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/analyze.c
|
||||
|
||||
attach.o: $(TOP)/src/attach.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/attach.c
|
||||
|
||||
auth.o: $(TOP)/src/auth.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/auth.c
|
||||
|
||||
btree.o: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h
|
||||
$(TCCX) -c $(TOP)/src/btree.c
|
||||
|
||||
build.o: $(TOP)/src/build.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/build.c
|
||||
|
||||
callback.o: $(TOP)/src/callback.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/callback.c
|
||||
|
||||
complete.o: $(TOP)/src/complete.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/complete.c
|
||||
|
||||
date.o: $(TOP)/src/date.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/date.c
|
||||
|
||||
delete.o: $(TOP)/src/delete.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/delete.c
|
||||
|
||||
expr.o: $(TOP)/src/expr.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/expr.c
|
||||
|
||||
func.o: $(TOP)/src/func.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/func.c
|
||||
|
||||
hash.o: $(TOP)/src/hash.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/hash.c
|
||||
|
||||
insert.o: $(TOP)/src/insert.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/insert.c
|
||||
|
||||
legacy.o: $(TOP)/src/legacy.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/legacy.c
|
||||
|
||||
loadext.o: $(TOP)/src/loadext.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/loadext.c
|
||||
|
||||
main.o: $(TOP)/src/main.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/main.c
|
||||
|
||||
pager.o: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
|
||||
$(TCCX) -c $(TOP)/src/pager.c
|
||||
|
||||
opcodes.o: opcodes.c
|
||||
$(TCCX) -c opcodes.c
|
||||
|
||||
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
|
||||
|
||||
os.o: $(TOP)/src/os.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/os.c
|
||||
|
||||
os_os2.o: $(TOP)/src/os_os2.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/os_os2.c
|
||||
|
||||
os_unix.o: $(TOP)/src/os_unix.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/os_unix.c
|
||||
|
||||
os_win.o: $(TOP)/src/os_win.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/os_win.c
|
||||
|
||||
parse.o: parse.c $(HDR)
|
||||
$(TCCX) -c parse.c
|
||||
|
||||
parse.h: parse.c
|
||||
|
||||
parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
|
||||
cp $(TOP)/src/parse.y .
|
||||
./lemon $(OPTS) parse.y
|
||||
mv parse.h parse.h.temp
|
||||
awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
pragma.o: $(TOP)/src/pragma.c $(HDR)
|
||||
$(TCCX) $(TCL_FLAGS) -c $(TOP)/src/pragma.c
|
||||
|
||||
prepare.o: $(TOP)/src/prepare.c $(HDR)
|
||||
$(TCCX) $(TCL_FLAGS) -c $(TOP)/src/prepare.c
|
||||
|
||||
printf.o: $(TOP)/src/printf.c $(HDR)
|
||||
$(TCCX) $(TCL_FLAGS) -c $(TOP)/src/printf.c
|
||||
|
||||
random.o: $(TOP)/src/random.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/random.c
|
||||
|
||||
select.o: $(TOP)/src/select.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/select.c
|
||||
|
||||
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
|
||||
|
||||
table.o: $(TOP)/src/table.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/table.c
|
||||
|
||||
tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(TCCX) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
tokenize.o: $(TOP)/src/tokenize.c keywordhash.h $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/tokenize.c
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash >keywordhash.h
|
||||
|
||||
trigger.o: $(TOP)/src/trigger.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/trigger.c
|
||||
|
||||
update.o: $(TOP)/src/update.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/update.c
|
||||
|
||||
utf.o: $(TOP)/src/utf.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/utf.c
|
||||
|
||||
util.o: $(TOP)/src/util.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/util.c
|
||||
|
||||
vacuum.o: $(TOP)/src/vacuum.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/vacuum.c
|
||||
|
||||
vdbe.o: $(TOP)/src/vdbe.c $(VDBEHDR)
|
||||
$(TCCX) -c $(TOP)/src/vdbe.c
|
||||
|
||||
vdbeapi.o: $(TOP)/src/vdbeapi.c $(VDBEHDR)
|
||||
$(TCCX) -c $(TOP)/src/vdbeapi.c
|
||||
|
||||
vdbeaux.o: $(TOP)/src/vdbeaux.c $(VDBEHDR)
|
||||
$(TCCX) -c $(TOP)/src/vdbeaux.c
|
||||
|
||||
vdbefifo.o: $(TOP)/src/vdbefifo.c $(VDBEHDR)
|
||||
$(TCCX) -c $(TOP)/src/vdbefifo.c
|
||||
|
||||
vdbemem.o: $(TOP)/src/vdbemem.c $(VDBEHDR)
|
||||
$(TCCX) -c $(TOP)/src/vdbemem.c
|
||||
|
||||
vtab.o: $(TOP)/src/vtab.c $(VDBEHDR)
|
||||
$(TCCX) -c $(TOP)/src/vtab.c
|
||||
|
||||
where.o: $(TOP)/src/where.c $(HDR)
|
||||
$(TCCX) -c $(TOP)/src/where.c
|
||||
|
||||
# Rules for building test programs and for running tests
|
||||
#
|
||||
tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a
|
||||
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
|
||||
$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
|
||||
|
||||
testfixture$(EXE): $(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC)
|
||||
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
|
||||
-DSQLITE_SERVER=1 -o testfixture$(EXE) \
|
||||
$(TESTSRC) $(TOP)/src/tclsqlite.c \
|
||||
libsqlite3.a $(LIBTCL) $(THREADLIB)
|
||||
|
||||
fulltest: testfixture$(EXE) sqlite3$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/all.test
|
||||
|
||||
test: testfixture$(EXE) sqlite3$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/quick.test
|
||||
|
||||
sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c libsqlite3.a $(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) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_DEBUG=1 -o \
|
||||
sqlite3_analyzer$(EXE) $(TESTSRC) $(TOP)/src/tclsqlite.c \
|
||||
libsqlite3.a $(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
|
||||
|
||||
# Rules used to build documentation
|
||||
#
|
||||
arch.html: $(TOP)/www/arch.tcl
|
||||
tclsh $(TOP)/www/arch.tcl >arch.html
|
||||
|
||||
autoinc.html: $(TOP)/www/autoinc.tcl
|
||||
tclsh $(TOP)/www/autoinc.tcl >autoinc.html
|
||||
|
||||
c_interface.html: $(TOP)/www/c_interface.tcl
|
||||
tclsh $(TOP)/www/c_interface.tcl >c_interface.html
|
||||
|
||||
capi3.html: $(TOP)/www/capi3.tcl
|
||||
tclsh $(TOP)/www/capi3.tcl >capi3.html
|
||||
|
||||
capi3ref.html: $(TOP)/www/capi3ref.tcl
|
||||
tclsh $(TOP)/www/capi3ref.tcl >capi3ref.html
|
||||
|
||||
changes.html: $(TOP)/www/changes.tcl
|
||||
tclsh $(TOP)/www/changes.tcl >changes.html
|
||||
|
||||
compile.html: $(TOP)/www/compile.tcl
|
||||
tclsh $(TOP)/www/compile.tcl >compile.html
|
||||
|
||||
copyright.html: $(TOP)/www/copyright.tcl
|
||||
tclsh $(TOP)/www/copyright.tcl >copyright.html
|
||||
|
||||
copyright-release.html: $(TOP)/www/copyright-release.html
|
||||
cp $(TOP)/www/copyright-release.html .
|
||||
|
||||
copyright-release.pdf: $(TOP)/www/copyright-release.pdf
|
||||
cp $(TOP)/www/copyright-release.pdf .
|
||||
|
||||
common.tcl: $(TOP)/www/common.tcl
|
||||
cp $(TOP)/www/common.tcl .
|
||||
|
||||
conflict.html: $(TOP)/www/conflict.tcl
|
||||
tclsh $(TOP)/www/conflict.tcl >conflict.html
|
||||
|
||||
datatypes.html: $(TOP)/www/datatypes.tcl
|
||||
tclsh $(TOP)/www/datatypes.tcl >datatypes.html
|
||||
|
||||
datatype3.html: $(TOP)/www/datatype3.tcl
|
||||
tclsh $(TOP)/www/datatype3.tcl >datatype3.html
|
||||
|
||||
different.html: $(TOP)/www/different.tcl
|
||||
tclsh $(TOP)/www/different.tcl >different.html
|
||||
|
||||
docs.html: $(TOP)/www/docs.tcl
|
||||
tclsh $(TOP)/www/docs.tcl >docs.html
|
||||
|
||||
download.html: $(TOP)/www/download.tcl
|
||||
mkdir -p doc
|
||||
tclsh $(TOP)/www/download.tcl >download.html
|
||||
|
||||
faq.html: $(TOP)/www/faq.tcl
|
||||
tclsh $(TOP)/www/faq.tcl >faq.html
|
||||
|
||||
fileformat.html: $(TOP)/www/fileformat.tcl
|
||||
tclsh $(TOP)/www/fileformat.tcl >fileformat.html
|
||||
|
||||
formatchng.html: $(TOP)/www/formatchng.tcl
|
||||
tclsh $(TOP)/www/formatchng.tcl >formatchng.html
|
||||
|
||||
index.html: $(TOP)/www/index.tcl last_change
|
||||
tclsh $(TOP)/www/index.tcl >index.html
|
||||
|
||||
lang.html: $(TOP)/www/lang.tcl
|
||||
tclsh $(TOP)/www/lang.tcl doc >lang.html
|
||||
|
||||
pragma.html: $(TOP)/www/pragma.tcl
|
||||
tclsh $(TOP)/www/pragma.tcl >pragma.html
|
||||
|
||||
lockingv3.html: $(TOP)/www/lockingv3.tcl
|
||||
tclsh $(TOP)/www/lockingv3.tcl >lockingv3.html
|
||||
|
||||
sharedcache.html: $(TOP)/www/sharedcache.tcl
|
||||
tclsh $(TOP)/www/sharedcache.tcl >sharedcache.html
|
||||
|
||||
mingw.html: $(TOP)/www/mingw.tcl
|
||||
tclsh $(TOP)/www/mingw.tcl >mingw.html
|
||||
|
||||
nulls.html: $(TOP)/www/nulls.tcl
|
||||
tclsh $(TOP)/www/nulls.tcl >nulls.html
|
||||
|
||||
oldnews.html: $(TOP)/www/oldnews.tcl
|
||||
tclsh $(TOP)/www/oldnews.tcl >oldnews.html
|
||||
|
||||
omitted.html: $(TOP)/www/omitted.tcl
|
||||
tclsh $(TOP)/www/omitted.tcl >omitted.html
|
||||
|
||||
opcode.html: $(TOP)/www/opcode.tcl $(TOP)/src/vdbe.c
|
||||
tclsh $(TOP)/www/opcode.tcl $(TOP)/src/vdbe.c >opcode.html
|
||||
|
||||
optimizer.html: $(TOP)/www/optimizer.tcl
|
||||
tclsh $(TOP)/www/optimizer.tcl >optimizer.html
|
||||
|
||||
optoverview.html: $(TOP)/www/optoverview.tcl
|
||||
tclsh $(TOP)/www/optoverview.tcl >optoverview.html
|
||||
|
||||
quickstart.html: $(TOP)/www/quickstart.tcl
|
||||
tclsh $(TOP)/www/quickstart.tcl >quickstart.html
|
||||
|
||||
speed.html: $(TOP)/www/speed.tcl
|
||||
tclsh $(TOP)/www/speed.tcl >speed.html
|
||||
|
||||
sqlite.html: $(TOP)/www/sqlite.tcl
|
||||
tclsh $(TOP)/www/sqlite.tcl >sqlite.html
|
||||
|
||||
support.html: $(TOP)/www/support.tcl
|
||||
tclsh $(TOP)/www/support.tcl >support.html
|
||||
|
||||
tclsqlite.html: $(TOP)/www/tclsqlite.tcl
|
||||
tclsh $(TOP)/www/tclsqlite.tcl >tclsqlite.html
|
||||
|
||||
vdbe.html: $(TOP)/www/vdbe.tcl
|
||||
tclsh $(TOP)/www/vdbe.tcl >vdbe.html
|
||||
|
||||
version3.html: $(TOP)/www/version3.tcl
|
||||
tclsh $(TOP)/www/version3.tcl >version3.html
|
||||
|
||||
whentouse.html: $(TOP)/www/whentouse.tcl
|
||||
tclsh $(TOP)/www/whentouse.tcl >whentouse.html
|
||||
|
||||
|
||||
# Files to be published on the website.
|
||||
#
|
||||
DOC = \
|
||||
arch.html \
|
||||
autoinc.html \
|
||||
c_interface.html \
|
||||
capi3.html \
|
||||
capi3ref.html \
|
||||
changes.html \
|
||||
compile.html \
|
||||
copyright.html \
|
||||
copyright-release.html \
|
||||
copyright-release.pdf \
|
||||
conflict.html \
|
||||
datatypes.html \
|
||||
datatype3.html \
|
||||
different.html \
|
||||
docs.html \
|
||||
download.html \
|
||||
faq.html \
|
||||
fileformat.html \
|
||||
formatchng.html \
|
||||
index.html \
|
||||
lang.html \
|
||||
lockingv3.html \
|
||||
mingw.html \
|
||||
nulls.html \
|
||||
oldnews.html \
|
||||
omitted.html \
|
||||
opcode.html \
|
||||
optimizer.html \
|
||||
optoverview.html \
|
||||
pragma.html \
|
||||
quickstart.html \
|
||||
sharedcache.html \
|
||||
speed.html \
|
||||
sqlite.html \
|
||||
support.html \
|
||||
tclsqlite.html \
|
||||
vdbe.html \
|
||||
version3.html \
|
||||
whentouse.html
|
||||
|
||||
doc: common.tcl $(DOC)
|
||||
mkdir -p doc
|
||||
mv $(DOC) doc
|
||||
cp $(TOP)/www/*.gif $(TOP)/art/*.gif doc
|
||||
|
||||
# Standard install and cleanup targets
|
||||
#
|
||||
install: sqlite3 libsqlite3.a sqlite3.h
|
||||
mv sqlite3 /usr/bin
|
||||
mv libsqlite3.a /usr/lib
|
||||
mv sqlite3.h /usr/include
|
||||
|
||||
clean:
|
||||
rm -f *.o sqlite3 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
|
||||
rm -f testloadext.dll libtestloadext.so
|
|
@ -1,50 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile SQLite into a DLL.
|
||||
#
|
||||
# Two separate DLLs are generated. "sqlite3.dll" is the core
|
||||
# library. "tclsqlite3.dll" contains the TCL bindings and is the
|
||||
# library that is loaded into TCL in order to run SQLite.
|
||||
#
|
||||
make target_source
|
||||
cd tsrc
|
||||
PATH=$PATH:/opt/mingw/bin
|
||||
TCLDIR=/home/drh/tcltk/846/win/846win
|
||||
TCLSTUBLIB=$TCLDIR/libtcl84stub.a
|
||||
OPTS='-DUSE_TCL_STUBS=1 -DNDEBUG=1 -DTHREADSAFE=1 -DBUILD_sqlite=1'
|
||||
CC="i386-mingw32msvc-gcc -O2 $OPTS -I. -I$TCLDIR"
|
||||
NM="i386-mingw32msvc-nm"
|
||||
rm shell.c
|
||||
for i in *.c; do
|
||||
CMD="$CC -c $i"
|
||||
echo $CMD
|
||||
$CMD
|
||||
done
|
||||
echo 'EXPORTS' >tclsqlite3.def
|
||||
$NM *.o | grep ' T ' >temp1
|
||||
grep '_Init$' temp1 >temp2
|
||||
grep '_SafeInit$' temp1 >>temp2
|
||||
grep ' T _sqlite3_' temp1 >>temp2
|
||||
echo 'EXPORTS' >tclsqlite3.def
|
||||
sed 's/^.* T _//' temp2 | sort | uniq >>tclsqlite3.def
|
||||
i386-mingw32msvc-dllwrap \
|
||||
--def tclsqlite3.def -v --export-all \
|
||||
--driver-name i386-mingw32msvc-gcc \
|
||||
--dlltool-name i386-mingw32msvc-dlltool \
|
||||
--as i386-mingw32msvc-as \
|
||||
--target i386-mingw32 \
|
||||
-dllname tclsqlite3.dll -lmsvcrt *.o $TCLSTUBLIB
|
||||
#i386-mingw32msvc-strip tclsqlite3.dll
|
||||
rm tclsqlite.o
|
||||
$NM *.o | grep ' T ' >temp1
|
||||
echo 'EXPORTS' >sqlite3.def
|
||||
grep ' _sqlite3_' temp1 | sed 's/^.* _//' >>sqlite3.def
|
||||
i386-mingw32msvc-dllwrap \
|
||||
--def sqlite3.def -v --export-all \
|
||||
--driver-name i386-mingw32msvc-gcc \
|
||||
--dlltool-name i386-mingw32msvc-dlltool \
|
||||
--as i386-mingw32msvc-as \
|
||||
--target i386-mingw32 \
|
||||
-dllname sqlite3.dll -lmsvcrt *.o
|
||||
#i386-mingw32msvc-strip sqlite3.dll
|
||||
cd ..
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/awk -f
|
||||
#
|
||||
# This AWK script scans the opcodes.h file (which is itself generated by
|
||||
# another awk script) and uses the information gleaned to create the
|
||||
# opcodes.c source file.
|
||||
#
|
||||
# Opcodes.c contains strings which are the symbolic names for the various
|
||||
# opcodes used by the VDBE. These strings are used when disassembling a
|
||||
# VDBE program during tracing or as a result of the EXPLAIN keyword.
|
||||
#
|
||||
BEGIN {
|
||||
print "/* Automatically generated. Do not edit */"
|
||||
print "/* See the mkopcodec.awk script for details. */"
|
||||
printf "#if !defined(SQLITE_OMIT_EXPLAIN)"
|
||||
printf " || !defined(NDEBUG)"
|
||||
printf " || defined(VDBE_PROFILE)"
|
||||
print " || defined(SQLITE_DEBUG)"
|
||||
print "const char *const sqlite3OpcodeNames[] = { \"?\","
|
||||
}
|
||||
/define OP_/ {
|
||||
sub("OP_","",$2)
|
||||
i++
|
||||
printf " /* %3d */ \"%s\",\n", $3, $2
|
||||
}
|
||||
END {
|
||||
print "};"
|
||||
print "#endif"
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
#!/usr/bin/awk -f
|
||||
#
|
||||
# Generate the file opcodes.h.
|
||||
#
|
||||
# This AWK script scans a concatenation of the parse.h output file from the
|
||||
# parser and the vdbe.c source file in order to generate the opcodes numbers
|
||||
# for all opcodes.
|
||||
#
|
||||
# The lines of the vdbe.c that we are interested in are of the form:
|
||||
#
|
||||
# case OP_aaaa: /* same as TK_bbbbb */
|
||||
#
|
||||
# The TK_ comment is optional. If it is present, then the value assigned to
|
||||
# the OP_ is the same as the TK_ value. If missing, the OP_ value is assigned
|
||||
# a small integer that is different from every other OP_ value.
|
||||
#
|
||||
# We go to the trouble of making some OP_ values the same as TK_ values
|
||||
# as an optimization. During parsing, things like expression operators
|
||||
# are coded with TK_ values such as TK_ADD, TK_DIVIDE, and so forth. Later
|
||||
# during code generation, we need to generate corresponding opcodes like
|
||||
# OP_Add and OP_Divide. By making TK_ADD==OP_Add and TK_DIVIDE==OP_Divide,
|
||||
# code to translate from one to the other is avoided. This makes the
|
||||
# code generator run (infinitesimally) faster and more importantly it makes
|
||||
# the library footprint smaller.
|
||||
#
|
||||
# This script also scans for lines of the form:
|
||||
#
|
||||
# case OP_aaaa: /* no-push */
|
||||
#
|
||||
# When the no-push comment is found on an opcode, it means that that
|
||||
# opcode does not leave a result on the stack. By identifying which
|
||||
# opcodes leave results on the stack it is possible to determine a
|
||||
# much smaller upper bound on the size of the stack. This allows
|
||||
# a smaller stack to be allocated, which is important to embedded
|
||||
# systems with limited memory space. This script generates a series
|
||||
# of "NOPUSH_MASK" defines that contain bitmaps of opcodes that leave
|
||||
# results on the stack. The NOPUSH_MASK defines are used in vdbeaux.c
|
||||
# to help determine the maximum stack size.
|
||||
#
|
||||
|
||||
|
||||
# Remember the TK_ values from the parse.h file
|
||||
/^#define TK_/ {
|
||||
tk[$2] = $3
|
||||
}
|
||||
|
||||
# Scan for "case OP_aaaa:" lines in the vdbe.c file
|
||||
/^case OP_/ {
|
||||
name = $2
|
||||
sub(/:/,"",name)
|
||||
sub("\r","",name)
|
||||
op[name] = -1
|
||||
for(i=3; i<NF; i++){
|
||||
if($i=="same" && $(i+1)=="as"){
|
||||
sym = $(i+2)
|
||||
sub(/,/,"",sym)
|
||||
op[name] = tk[sym]
|
||||
used[op[name]] = 1
|
||||
sameas[op[name]] = sym
|
||||
}
|
||||
if($i=="no-push"){
|
||||
nopush[name] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Assign numbers to all opcodes and output the result.
|
||||
END {
|
||||
cnt = 0
|
||||
max = 0
|
||||
print "/* Automatically generated. Do not edit */"
|
||||
print "/* See the mkopcodeh.awk script for details */"
|
||||
for(name in op){
|
||||
if( op[name]<0 ){
|
||||
cnt++
|
||||
while( used[cnt] ) cnt++
|
||||
op[name] = cnt
|
||||
}
|
||||
used[op[name]] = 1;
|
||||
if( op[name]>max ) max = op[name]
|
||||
printf "#define %-25s %15d", name, op[name]
|
||||
if( sameas[op[name]] ) {
|
||||
printf " /* same as %-12s*/", sameas[op[name]]
|
||||
}
|
||||
printf "\n"
|
||||
|
||||
}
|
||||
seenUnused = 0;
|
||||
for(i=1; i<max; i++){
|
||||
if( !used[i] ){
|
||||
if( !seenUnused ){
|
||||
printf "\n/* The following opcode values are never used */\n"
|
||||
seenUnused = 1
|
||||
}
|
||||
printf "#define %-25s %15d\n", sprintf( "OP_NotUsed_%-3d", i ), i
|
||||
}
|
||||
}
|
||||
|
||||
# Generate the 10 16-bit bitmasks used by function opcodeUsesStack()
|
||||
# in vdbeaux.c. See comments in that function for details.
|
||||
#
|
||||
nopush[0] = 0 # 0..15
|
||||
nopush[1] = 0 # 16..31
|
||||
nopush[2] = 0 # 32..47
|
||||
nopush[3] = 0 # 48..63
|
||||
nopush[4] = 0 # 64..79
|
||||
nopush[5] = 0 # 80..95
|
||||
nopush[6] = 0 # 96..111
|
||||
nopush[7] = 0 # 112..127
|
||||
nopush[8] = 0 # 128..143
|
||||
nopush[9] = 0 # 144..159
|
||||
for(name in op){
|
||||
if( nopush[name] ){
|
||||
n = op[name]
|
||||
j = n%16
|
||||
i = ((n - j)/16)
|
||||
nopush[i] = nopush[i] + (2^j)
|
||||
}
|
||||
}
|
||||
printf "\n"
|
||||
print "/* Opcodes that are guaranteed to never push a value onto the stack"
|
||||
print "** contain a 1 their corresponding position of the following mask"
|
||||
print "** set. See the opcodeNoPush() function in vdbeaux.c */"
|
||||
for(i=0; i<10; i++){
|
||||
printf "#define NOPUSH_MASK_%d 0x%04x\n", i, nopush[i]
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile SQLite into a shared library on Linux.
|
||||
#
|
||||
# Two separate shared libraries are generated. "sqlite3.so" is the core
|
||||
# library. "tclsqlite3.so" contains the TCL bindings and is the
|
||||
# library that is loaded into TCL in order to run SQLite.
|
||||
#
|
||||
make target_source
|
||||
cd tsrc
|
||||
rm shell.c
|
||||
TCLDIR=/home/drh/tcltk/846/linux/846linux
|
||||
TCLSTUBLIB=$TCLDIR/libtclstub8.4g.a
|
||||
OPTS='-DUSE_TCL_STUBS=1 -DNDEBUG=1 -DHAVE_DLOPEN=1'
|
||||
for i in *.c; do
|
||||
if test $i != 'keywordhash.c'; then
|
||||
CMD="cc -fPIC $OPTS -O2 -I. -I$TCLDIR -c $i"
|
||||
echo $CMD
|
||||
$CMD
|
||||
fi
|
||||
done
|
||||
echo gcc -shared *.o $TCLSTUBLIB -o tclsqlite3.so
|
||||
gcc -shared *.o $TCLSTUBLIB -o tclsqlite3.so
|
||||
strip tclsqlite3.so
|
||||
rm tclsqlite.c tclsqlite.o
|
||||
echo gcc -shared *.o -o sqlite3.so
|
||||
gcc -shared *.o -o sqlite3.so
|
||||
strip sqlite3.so
|
||||
cd ..
|
|
@ -1,117 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile SQLite and all its documentation and
|
||||
# ship everything up to the SQLite website. This script will only work
|
||||
# on the system "zadok" at the Hwaci offices. But others might find
|
||||
# the script useful as an example.
|
||||
#
|
||||
|
||||
# Set srcdir to the name of the directory that contains the publish.sh
|
||||
# script.
|
||||
#
|
||||
srcdir=`echo "$0" | sed 's%\(^.*\)/[^/][^/]*$%\1%'`
|
||||
|
||||
# Get the makefile.
|
||||
#
|
||||
cp $srcdir/Makefile.linux-gcc ./Makefile
|
||||
chmod +x $srcdir/install-sh
|
||||
|
||||
# Get the current version number - needed to help build filenames
|
||||
#
|
||||
VERS=`cat $srcdir/VERSION`
|
||||
VERSW=`sed 's/\./_/g' $srcdir/VERSION`
|
||||
|
||||
# Start by building an sqlite shell for linux.
|
||||
#
|
||||
make clean
|
||||
make sqlite3
|
||||
strip sqlite3
|
||||
mv sqlite3 sqlite3-$VERS.bin
|
||||
gzip sqlite3-$VERS.bin
|
||||
mv sqlite3-$VERS.bin.gz doc
|
||||
|
||||
# Build a source archive useful for windows.
|
||||
#
|
||||
make target_source
|
||||
cd tsrc
|
||||
zip ../doc/sqlite-source-$VERSW.zip *
|
||||
cd ..
|
||||
|
||||
# Build the sqlite.so and tclsqlite.so shared libraries
|
||||
# under Linux
|
||||
#
|
||||
. $srcdir/mkso.sh
|
||||
cd tsrc
|
||||
mv tclsqlite3.so tclsqlite-$VERS.so
|
||||
gzip tclsqlite-$VERS.so
|
||||
mv tclsqlite-$VERS.so.gz ../doc
|
||||
mv sqlite3.so sqlite-$VERS.so
|
||||
gzip sqlite-$VERS.so
|
||||
mv sqlite-$VERS.so.gz ../doc
|
||||
cd ..
|
||||
|
||||
# Build the tclsqlite3.dll and sqlite3.dll shared libraries.
|
||||
#
|
||||
. $srcdir/mkdll.sh
|
||||
cd tsrc
|
||||
echo zip ../doc/tclsqlite-$VERSW.zip tclsqlite3.dll
|
||||
zip ../doc/tclsqlite-$VERSW.zip tclsqlite3.dll
|
||||
echo zip ../doc/sqlitedll-$VERSW.zip sqlite3.dll sqlite3.def
|
||||
zip ../doc/sqlitedll-$VERSW.zip sqlite3.dll sqlite3.def
|
||||
cd ..
|
||||
|
||||
# Build the sqlite.exe executable for windows.
|
||||
#
|
||||
make target_source
|
||||
cd tsrc
|
||||
rm tclsqlite.c
|
||||
OPTS='-DSTATIC_BUILD=1 -DNDEBUG=1'
|
||||
i386-mingw32msvc-gcc -O2 $OPTS -I. -I$TCLDIR *.c -o sqlite3.exe
|
||||
zip ../doc/sqlite-$VERSW.zip sqlite3.exe
|
||||
cd ..
|
||||
|
||||
# Construct a tarball of the source tree
|
||||
#
|
||||
ORIGIN=`pwd`
|
||||
cd $srcdir
|
||||
cd ..
|
||||
mv sqlite sqlite-$VERS
|
||||
EXCLUDE=`find sqlite-$VERS -print | grep CVS | sed 's,^, --exclude ,'`
|
||||
tar czf $ORIGIN/doc/sqlite-$VERS.tar.gz $EXCLUDE sqlite-$VERS
|
||||
mv sqlite-$VERS sqlite
|
||||
cd $ORIGIN
|
||||
|
||||
#
|
||||
# Build RPMS (binary) and Source RPM
|
||||
#
|
||||
|
||||
# Make sure we are properly setup to build RPMs
|
||||
#
|
||||
echo "%HOME %{expand:%%(cd; pwd)}" > $HOME/.rpmmacros
|
||||
echo "%_topdir %{HOME}/rpm" >> $HOME/.rpmmacros
|
||||
mkdir $HOME/rpm
|
||||
mkdir $HOME/rpm/BUILD
|
||||
mkdir $HOME/rpm/SOURCES
|
||||
mkdir $HOME/rpm/RPMS
|
||||
mkdir $HOME/rpm/SRPMS
|
||||
mkdir $HOME/rpm/SPECS
|
||||
|
||||
# create the spec file from the template
|
||||
sed s/SQLITE_VERSION/$VERS/g $srcdir/spec.template > $HOME/rpm/SPECS/sqlite.spec
|
||||
|
||||
# copy the source tarball to the rpm directory
|
||||
cp doc/sqlite-$VERS.tar.gz $HOME/rpm/SOURCES/.
|
||||
|
||||
# build all the rpms
|
||||
rpm -ba $HOME/rpm/SPECS/sqlite.spec >& rpm-$vers.log
|
||||
|
||||
# copy the RPMs into the build directory.
|
||||
mv $HOME/rpm/RPMS/i386/sqlite*-$vers*.rpm doc
|
||||
mv $HOME/rpm/SRPMS/sqlite-$vers*.rpm doc
|
||||
|
||||
# Build the website
|
||||
#
|
||||
#cp $srcdir/../historical/* doc
|
||||
make doc
|
||||
cd doc
|
||||
chmod 644 *.gz
|
|
@ -1,62 +0,0 @@
|
|||
%define name sqlite
|
||||
%define version SQLITE_VERSION
|
||||
%define release 1
|
||||
|
||||
Name: %{name}
|
||||
Summary: SQLite is a C library that implements an embeddable SQL database engine
|
||||
Version: %{version}
|
||||
Release: %{release}
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
Group: System/Libraries
|
||||
URL: http://www.hwaci.com/sw/sqlite/
|
||||
License: Public Domain
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-root
|
||||
|
||||
%description
|
||||
SQLite is a C library that implements an embeddable SQL database engine.
|
||||
Programs that link with the SQLite library can have SQL database access
|
||||
without running a separate RDBMS process. The distribution comes with a
|
||||
standalone command-line access program (sqlite) that can be used to
|
||||
administer an SQLite database and which serves as an example of how to
|
||||
use the SQLite library.
|
||||
|
||||
%package -n %{name}-devel
|
||||
Summary: Header files and libraries for developing apps which will use sqlite
|
||||
Group: Development/C
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
|
||||
%description -n %{name}-devel
|
||||
The sqlite-devel package contains the header files and libraries needed
|
||||
to develop programs that use the sqlite database library.
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}
|
||||
|
||||
%build
|
||||
CFLAGS="%optflags -DNDEBUG=1" CXXFLAGS="%optflags -DNDEBUG=1" ./configure --prefix=%{_prefix}
|
||||
|
||||
make
|
||||
make doc
|
||||
|
||||
%install
|
||||
install -d $RPM_BUILD_ROOT/%{_prefix}
|
||||
install -d $RPM_BUILD_ROOT/%{_prefix}/bin
|
||||
install -d $RPM_BUILD_ROOT/%{_prefix}/include
|
||||
install -d $RPM_BUILD_ROOT/%{_prefix}/lib
|
||||
make install prefix=$RPM_BUILD_ROOT/%{_prefix}
|
||||
|
||||
%clean
|
||||
rm -fr $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-, root, root)
|
||||
%{_libdir}/*.so*
|
||||
%{_bindir}/*
|
||||
|
||||
%files -n %{name}-devel
|
||||
%defattr(-, root, root)
|
||||
%{_libdir}/pkgconfig/sqlite3.pc
|
||||
%{_libdir}/*.a
|
||||
%{_libdir}/*.la
|
||||
%{_includedir}/*
|
||||
%doc doc/*
|
|
@ -1,12 +0,0 @@
|
|||
# Package Information for pkg-config
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: SQLite
|
||||
Description: SQL database engine
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lsqlite
|
||||
Cflags: -I${includedir}
|
|
@ -1,229 +0,0 @@
|
|||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH SQLITE3 1 "Mon Apr 15 23:49:17 2002"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
.\" .nh disable hyphenation
|
||||
.\" .hy enable hyphenation
|
||||
.\" .ad l left justify
|
||||
.\" .ad b justify to both left and right margins
|
||||
.\" .nf disable filling
|
||||
.\" .fi enable filling
|
||||
.\" .br insert line break
|
||||
.\" .sp <n> insert n+1 empty lines
|
||||
.\" for manpage-specific macros, see man(7)
|
||||
.SH NAME
|
||||
.B sqlite3
|
||||
\- A command line interface for SQLite version 3
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B sqlite3
|
||||
.RI [ options ]
|
||||
.RI [ databasefile ]
|
||||
.RI [ SQL ]
|
||||
|
||||
.SH SUMMARY
|
||||
.PP
|
||||
.B sqlite3
|
||||
is a terminal-based front-end to the SQLite library that can evaluate
|
||||
queries interactively and display the results in multiple formats.
|
||||
.B sqlite3
|
||||
can also be used within shell scripts and other applications to provide
|
||||
batch processing features.
|
||||
|
||||
.SH DESCRIPTION
|
||||
To start a
|
||||
.B sqlite3
|
||||
interactive session, invoke the
|
||||
.B sqlite3
|
||||
command and optionally provide the name of a database file. If the
|
||||
database file does not exist, it will be created. If the database file
|
||||
does exist, it will be opened.
|
||||
|
||||
For example, to create a new database file named "mydata.db", create
|
||||
a table named "memos" and insert a couple of records into that table:
|
||||
.sp
|
||||
$
|
||||
.B sqlite3 mydata.db
|
||||
.br
|
||||
SQLite version 3.1.3
|
||||
.br
|
||||
Enter ".help" for instructions
|
||||
.br
|
||||
sqlite>
|
||||
.B create table memos(text, priority INTEGER);
|
||||
.br
|
||||
sqlite>
|
||||
.B insert into memos values('deliver project description', 10);
|
||||
.br
|
||||
sqlite>
|
||||
.B insert into memos values('lunch with Christine', 100);
|
||||
.br
|
||||
sqlite>
|
||||
.B select * from memos;
|
||||
.br
|
||||
deliver project description|10
|
||||
.br
|
||||
lunch with Christine|100
|
||||
.br
|
||||
sqlite>
|
||||
.sp
|
||||
|
||||
If no database name is supplied, the ATTACH sql command can be used
|
||||
to attach to existing or create new database files. ATTACH can also
|
||||
be used to attach to multiple databases within the same interactive
|
||||
session. This is useful for migrating data between databases,
|
||||
possibly changing the schema along the way.
|
||||
|
||||
Optionally, a SQL statement or set of SQL statements can be supplied as
|
||||
a single argument. Multiple statements should be separated by
|
||||
semi-colons.
|
||||
|
||||
For example:
|
||||
.sp
|
||||
$
|
||||
.B sqlite3 -line mydata.db 'select * from memos where priority > 20;'
|
||||
.br
|
||||
text = lunch with Christine
|
||||
.br
|
||||
priority = 100
|
||||
.br
|
||||
.sp
|
||||
|
||||
.SS SQLITE META-COMMANDS
|
||||
.PP
|
||||
The interactive interpreter offers a set of meta-commands that can be
|
||||
used to control the output format, examine the currently attached
|
||||
database files, or perform administrative operations upon the
|
||||
attached databases (such as rebuilding indices). Meta-commands are
|
||||
always prefixed with a dot (.).
|
||||
|
||||
A list of available meta-commands can be viewed at any time by issuing
|
||||
the '.help' command. For example:
|
||||
.sp
|
||||
sqlite>
|
||||
.B .help
|
||||
.nf
|
||||
.cc |
|
||||
.databases List names and files of attached databases
|
||||
.dump ?TABLE? ... Dump the database in an SQL text format
|
||||
.echo ON|OFF Turn command echo on or off
|
||||
.exit Exit this program
|
||||
.explain ON|OFF Turn output mode suitable for EXPLAIN on or off.
|
||||
.header(s) ON|OFF Turn display of headers on or off
|
||||
.help Show this message
|
||||
.import FILE TABLE Import data from FILE into TABLE
|
||||
.indices TABLE Show names of all indices on TABLE
|
||||
.mode MODE ?TABLE? Set output mode where MODE is one of:
|
||||
csv Comma-separated values
|
||||
column Left-aligned columns. (See .width)
|
||||
html HTML <table> code
|
||||
insert SQL insert statements for TABLE
|
||||
line One value per line
|
||||
list Values delimited by .separator string
|
||||
tabs Tab-separated values
|
||||
tcl TCL list elements
|
||||
.nullvalue STRING Print STRING in place of NULL values
|
||||
.output FILENAME Send output to FILENAME
|
||||
.output stdout Send output to the screen
|
||||
.prompt MAIN CONTINUE Replace the standard prompts
|
||||
.quit Exit this program
|
||||
.read FILENAME Execute SQL in FILENAME
|
||||
.schema ?TABLE? Show the CREATE statements
|
||||
.separator STRING Change separator used by output mode and .import
|
||||
.show Show the current values for various settings
|
||||
.tables ?PATTERN? List names of tables matching a LIKE pattern
|
||||
.timeout MS Try opening locked tables for MS milliseconds
|
||||
.width NUM NUM ... Set column widths for "column" mode
|
||||
sqlite>
|
||||
|cc .
|
||||
.sp
|
||||
.fi
|
||||
|
||||
.SH OPTIONS
|
||||
.B sqlite3
|
||||
has the following options:
|
||||
.TP
|
||||
.BI \-init\ file
|
||||
Read and execute commands from
|
||||
.I file
|
||||
, which can contain a mix of SQL statements and meta-commands.
|
||||
.TP
|
||||
.B \-echo
|
||||
Print commands before execution.
|
||||
.TP
|
||||
.B \-[no]header
|
||||
Turn headers on or off.
|
||||
.TP
|
||||
.B \-column
|
||||
Query results will be displayed in a table like form, using
|
||||
whitespace characters to separate the columns and align the
|
||||
output.
|
||||
.TP
|
||||
.B \-html
|
||||
Query results will be output as simple HTML tables.
|
||||
.TP
|
||||
.B \-line
|
||||
Query results will be displayed with one value per line, rows
|
||||
separated by a blank line. Designed to be easily parsed by
|
||||
scripts or other programs
|
||||
.TP
|
||||
.B \-list
|
||||
Query results will be displayed with the separator (|, by default)
|
||||
character between each field value. The default.
|
||||
.TP
|
||||
.BI \-separator\ separator
|
||||
Set output field separator. Default is '|'.
|
||||
.TP
|
||||
.BI \-nullvalue\ string
|
||||
Set string used to represent NULL values. Default is ''
|
||||
(empty string).
|
||||
.TP
|
||||
.B \-version
|
||||
Show SQLite version.
|
||||
.TP
|
||||
.B \-help
|
||||
Show help on options and exit.
|
||||
|
||||
|
||||
.SH INIT FILE
|
||||
.B sqlite3
|
||||
reads an initialization file to set the configuration of the
|
||||
interactive environment. Throughout initialization, any previously
|
||||
specified setting can be overridden. The sequence of initialization is
|
||||
as follows:
|
||||
|
||||
o The default configuration is established as follows:
|
||||
|
||||
.sp
|
||||
.nf
|
||||
.cc |
|
||||
mode = LIST
|
||||
separator = "|"
|
||||
main prompt = "sqlite> "
|
||||
continue prompt = " ...> "
|
||||
|cc .
|
||||
.sp
|
||||
.fi
|
||||
|
||||
o If the file
|
||||
.B ~/.sqliterc
|
||||
exists, it is processed first.
|
||||
can be found in the user's home directory, it is
|
||||
read and processed. It should generally only contain meta-commands.
|
||||
|
||||
o If the -init option is present, the specified file is processed.
|
||||
|
||||
o All other command line options are processed.
|
||||
|
||||
.SH SEE ALSO
|
||||
http://www.sqlite.org/
|
||||
.br
|
||||
The sqlite-doc package
|
||||
.SH AUTHOR
|
||||
This manual page was originally written by Andreas Rottmann
|
||||
<rotty@debian.org>, for the Debian GNU/Linux system (but may be used
|
||||
by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com>.
|
|
@ -1,12 +0,0 @@
|
|||
# Package Information for pkg-config
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: SQLite
|
||||
Description: SQL database engine
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lsqlite3
|
||||
Cflags: -I${includedir}
|
|
@ -1,575 +0,0 @@
|
|||
/*
|
||||
** 2005 February 15
|
||||
**
|
||||
** 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 C code routines that used to generate VDBE code
|
||||
** that implements the ALTER TABLE command.
|
||||
**
|
||||
** $Id: alter.c,v 1.22 2006/09/08 12:27:37 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** The code in this file only exists if we are not omitting the
|
||||
** ALTER TABLE logic from the build.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
|
||||
|
||||
/*
|
||||
** This function is used by SQL generated to implement the
|
||||
** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
|
||||
** CREATE INDEX command. The second is a table name. The table name in
|
||||
** the CREATE TABLE or CREATE INDEX statement is replaced with the third
|
||||
** argument and the result returned. Examples:
|
||||
**
|
||||
** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
|
||||
** -> 'CREATE TABLE def(a, b, c)'
|
||||
**
|
||||
** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
|
||||
** -> 'CREATE INDEX i ON def(a, b, c)'
|
||||
*/
|
||||
static void renameTableFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
||||
unsigned char const *zTableName = sqlite3_value_text(argv[1]);
|
||||
|
||||
int token;
|
||||
Token tname;
|
||||
unsigned char const *zCsr = zSql;
|
||||
int len = 0;
|
||||
char *zRet;
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TABLE
|
||||
** statement is that the table name is the first token that is immediatedly
|
||||
** followed by a left parenthesis - TK_LP.
|
||||
*/
|
||||
if( zSql ){
|
||||
do {
|
||||
/* Store the token that zCsr points to in tname. */
|
||||
tname.z = zCsr;
|
||||
tname.n = len;
|
||||
|
||||
/* Advance zCsr to the next token. Store that token type in 'token',
|
||||
** and it's length in 'len' (to be used next iteration of this loop).
|
||||
*/
|
||||
do {
|
||||
zCsr += len;
|
||||
len = sqlite3GetToken(zCsr, &token);
|
||||
} while( token==TK_SPACE );
|
||||
assert( len>0 );
|
||||
} while( token!=TK_LP );
|
||||
|
||||
zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql,
|
||||
zTableName, tname.z+tname.n);
|
||||
sqlite3_result_text(context, zRet, -1, sqlite3FreeX);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* This function is used by SQL generated to implement the
|
||||
** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER
|
||||
** statement. The second is a table name. The table name in the CREATE
|
||||
** TRIGGER statement is replaced with the third argument and the result
|
||||
** returned. This is analagous to renameTableFunc() above, except for CREATE
|
||||
** TRIGGER, not CREATE INDEX and CREATE TABLE.
|
||||
*/
|
||||
static void renameTriggerFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
||||
unsigned char const *zTableName = sqlite3_value_text(argv[1]);
|
||||
|
||||
int token;
|
||||
Token tname;
|
||||
int dist = 3;
|
||||
unsigned char const *zCsr = zSql;
|
||||
int len = 0;
|
||||
char *zRet;
|
||||
|
||||
/* 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
|
||||
** of TK_WHEN, TK_BEGIN or TK_FOR.
|
||||
*/
|
||||
if( zSql ){
|
||||
do {
|
||||
/* Store the token that zCsr points to in tname. */
|
||||
tname.z = zCsr;
|
||||
tname.n = len;
|
||||
|
||||
/* Advance zCsr to the next token. Store that token type in 'token',
|
||||
** and it's length in 'len' (to be used next iteration of this loop).
|
||||
*/
|
||||
do {
|
||||
zCsr += len;
|
||||
len = sqlite3GetToken(zCsr, &token);
|
||||
}while( token==TK_SPACE );
|
||||
assert( len>0 );
|
||||
|
||||
/* Variable 'dist' stores the number of tokens read since the most
|
||||
** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN
|
||||
** token is read and 'dist' equals 2, the condition stated above
|
||||
** to be met.
|
||||
**
|
||||
** Note that ON cannot be a database, table or column name, so
|
||||
** there is no need to worry about syntax like
|
||||
** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
|
||||
*/
|
||||
dist++;
|
||||
if( token==TK_DOT || token==TK_ON ){
|
||||
dist = 0;
|
||||
}
|
||||
} while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) );
|
||||
|
||||
/* Variable tname now contains the token that is the old table-name
|
||||
** in the CREATE TRIGGER statement.
|
||||
*/
|
||||
zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql,
|
||||
zTableName, tname.z+tname.n);
|
||||
sqlite3_result_text(context, zRet, -1, sqlite3FreeX);
|
||||
}
|
||||
}
|
||||
#endif /* !SQLITE_OMIT_TRIGGER */
|
||||
|
||||
/*
|
||||
** Register built-in functions used to help implement ALTER TABLE
|
||||
*/
|
||||
void sqlite3AlterFunctions(sqlite3 *db){
|
||||
static const struct {
|
||||
char *zName;
|
||||
signed char nArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
|
||||
} aFuncs[] = {
|
||||
{ "sqlite_rename_table", 2, renameTableFunc},
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
{ "sqlite_rename_trigger", 2, renameTriggerFunc},
|
||||
#endif
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate the text of a WHERE expression which can be used to select all
|
||||
** temporary triggers on table pTab from the sqlite_temp_master table. If
|
||||
** table pTab has no temporary triggers, or is itself stored in the
|
||||
** temporary database, NULL is returned.
|
||||
*/
|
||||
static char *whereTempTriggers(Parse *pParse, Table *pTab){
|
||||
Trigger *pTrig;
|
||||
char *zWhere = 0;
|
||||
char *tmp = 0;
|
||||
const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */
|
||||
|
||||
/* If the table is not located in the temp-db (in which case NULL is
|
||||
** returned, loop through the tables list of triggers. For each trigger
|
||||
** that is not part of the temp-db schema, add a clause to the WHERE
|
||||
** expression being built up in zWhere.
|
||||
*/
|
||||
if( pTab->pSchema!=pTempSchema ){
|
||||
for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
|
||||
if( pTrig->pSchema==pTempSchema ){
|
||||
if( !zWhere ){
|
||||
zWhere = sqlite3MPrintf("name=%Q", pTrig->name);
|
||||
}else{
|
||||
tmp = zWhere;
|
||||
zWhere = sqlite3MPrintf("%s OR name=%Q", zWhere, pTrig->name);
|
||||
sqliteFree(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return zWhere;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to drop and reload the internal representation of table
|
||||
** pTab from the database, including triggers and temporary triggers.
|
||||
** Argument zName is the name of the table in the database schema at
|
||||
** the time the generated code is executed. This can be different from
|
||||
** pTab->zName if this function is being called to code part of an
|
||||
** "ALTER TABLE RENAME TO" statement.
|
||||
*/
|
||||
static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
|
||||
Vdbe *v;
|
||||
char *zWhere;
|
||||
int iDb; /* Index of database containing pTab */
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
Trigger *pTrig;
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( !v ) return;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
assert( iDb>=0 );
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* Drop any table triggers from the internal schema. */
|
||||
for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
|
||||
int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
|
||||
assert( iTrigDb==iDb || iTrigDb==1 );
|
||||
sqlite3VdbeOp3(v, OP_DropTrigger, iTrigDb, 0, pTrig->name, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop the table and index from the internal schema */
|
||||
sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
|
||||
|
||||
/* Reload the table, index and permanent trigger schemas. */
|
||||
zWhere = sqlite3MPrintf("tbl_name=%Q", zName);
|
||||
if( !zWhere ) return;
|
||||
sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC);
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* Now, if the table is not stored in the temp database, reload any temp
|
||||
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
|
||||
*/
|
||||
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
|
||||
sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zWhere, P3_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
|
||||
** command.
|
||||
*/
|
||||
void sqlite3AlterRenameTable(
|
||||
Parse *pParse, /* Parser context. */
|
||||
SrcList *pSrc, /* The table to rename. */
|
||||
Token *pName /* The new table name. */
|
||||
){
|
||||
int iDb; /* Database that contains the table */
|
||||
char *zDb; /* Name of database iDb */
|
||||
Table *pTab; /* Table being renamed */
|
||||
char *zName = 0; /* NULL-terminated version of pName */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Vdbe *v;
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
char *zWhere = 0; /* Where clause to locate temp triggers */
|
||||
#endif
|
||||
|
||||
if( sqlite3MallocFailed() ) goto exit_rename_table;
|
||||
assert( pSrc->nSrc==1 );
|
||||
|
||||
pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
|
||||
if( !pTab ) goto exit_rename_table;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3ErrorMsg(pParse, "virtual tables may not be altered");
|
||||
goto exit_rename_table;
|
||||
}
|
||||
#endif
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
|
||||
/* Get a NULL terminated version of the new table name. */
|
||||
zName = sqlite3NameFromToken(pName);
|
||||
if( !zName ) goto exit_rename_table;
|
||||
|
||||
/* Check that a table or index named 'zName' does not already exist
|
||||
** in database iDb. If so, this is an error.
|
||||
*/
|
||||
if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"there is already another table or index with this name: %s", zName);
|
||||
goto exit_rename_table;
|
||||
}
|
||||
|
||||
/* 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) ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName);
|
||||
goto exit_rename_table;
|
||||
}
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
||||
goto exit_rename_table;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
/* Invoke the authorization callback. */
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
|
||||
goto exit_rename_table;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Begin a transaction and code the VerifyCookie for database iDb.
|
||||
** Then modify the schema cookie (since the ALTER TABLE modifies the
|
||||
** schema).
|
||||
*/
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ){
|
||||
goto exit_rename_table;
|
||||
}
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
sqlite3ChangeCookie(db, v, iDb);
|
||||
|
||||
/* Modify the sqlite_master table to use the new table name. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET "
|
||||
#ifdef SQLITE_OMIT_TRIGGER
|
||||
"sql = sqlite_rename_table(sql, %Q), "
|
||||
#else
|
||||
"sql = CASE "
|
||||
"WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)"
|
||||
"ELSE sqlite_rename_table(sql, %Q) END, "
|
||||
#endif
|
||||
"tbl_name = %Q, "
|
||||
"name = CASE "
|
||||
"WHEN type='table' THEN %Q "
|
||||
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
|
||||
"'sqlite_autoindex_' || %Q || substr(name, %d+18,10) "
|
||||
"ELSE name END "
|
||||
"WHERE tbl_name=%Q AND "
|
||||
"(type='table' OR type='index' OR type='trigger');",
|
||||
zDb, SCHEMA_TABLE(iDb), zName, zName, zName,
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
zName,
|
||||
#endif
|
||||
zName, strlen(pTab->zName), pTab->zName
|
||||
);
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
/* If the sqlite_sequence table exists in this database, then update
|
||||
** it with the new table name.
|
||||
*/
|
||||
if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.sqlite_sequence set name = %Q WHERE name = %Q",
|
||||
zDb, zName, pTab->zName);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* If there are TEMP triggers on this table, modify the sqlite_temp_master
|
||||
** table. Don't do this if the table being ALTERed is itself located in
|
||||
** the temp database.
|
||||
*/
|
||||
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE sqlite_temp_master SET "
|
||||
"sql = sqlite_rename_trigger(sql, %Q), "
|
||||
"tbl_name = %Q "
|
||||
"WHERE %s;", zName, zName, zWhere);
|
||||
sqliteFree(zWhere);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop and reload the internal table schema. */
|
||||
reloadTableSchema(pParse, pTab, zName);
|
||||
|
||||
exit_rename_table:
|
||||
sqlite3SrcListDelete(pSrc);
|
||||
sqliteFree(zName);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called after an "ALTER TABLE ... ADD" statement
|
||||
** has been parsed. Argument pColDef contains the text of the new
|
||||
** column definition.
|
||||
**
|
||||
** The Table structure pParse->pNewTable was extended to include
|
||||
** the new column during parsing.
|
||||
*/
|
||||
void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
Table *pNew; /* Copy of pParse->pNewTable */
|
||||
Table *pTab; /* Table being altered */
|
||||
int iDb; /* Database number */
|
||||
const char *zDb; /* Database name */
|
||||
const char *zTab; /* Table name */
|
||||
char *zCol; /* Null-terminated column definition */
|
||||
Column *pCol; /* The new column */
|
||||
Expr *pDflt; /* Default value for the new column */
|
||||
|
||||
if( pParse->nErr ) return;
|
||||
pNew = pParse->pNewTable;
|
||||
assert( pNew );
|
||||
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pNew->pSchema);
|
||||
zDb = pParse->db->aDb[iDb].zName;
|
||||
zTab = pNew->zName;
|
||||
pCol = &pNew->aCol[pNew->nCol-1];
|
||||
pDflt = pCol->pDflt;
|
||||
pTab = sqlite3FindTable(pParse->db, zTab, zDb);
|
||||
assert( pTab );
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
/* Invoke the authorization callback. */
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the default value for the new column was specified with a
|
||||
** literal NULL, then set pDflt to 0. This simplifies checking
|
||||
** for an SQL NULL default below.
|
||||
*/
|
||||
if( pDflt && pDflt->op==TK_NULL ){
|
||||
pDflt = 0;
|
||||
}
|
||||
|
||||
/* Check that the new column is not specified as PRIMARY KEY or UNIQUE.
|
||||
** If there is a NOT NULL constraint, then the default value for the
|
||||
** column must not be NULL.
|
||||
*/
|
||||
if( pCol->isPrimKey ){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
|
||||
return;
|
||||
}
|
||||
if( pNew->pIndex ){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
|
||||
return;
|
||||
}
|
||||
if( pCol->notNull && !pDflt ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"Cannot add a NOT NULL column with default value NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure the default expression is something that sqlite3ValueFromExpr()
|
||||
** can handle (i.e. not CURRENT_TIME etc.)
|
||||
*/
|
||||
if( pDflt ){
|
||||
sqlite3_value *pVal;
|
||||
if( sqlite3ValueFromExpr(pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
|
||||
/* malloc() has failed */
|
||||
return;
|
||||
}
|
||||
if( !pVal ){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default");
|
||||
return;
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
}
|
||||
|
||||
/* Modify the CREATE TABLE statement. */
|
||||
zCol = sqliteStrNDup((char*)pColDef->z, pColDef->n);
|
||||
if( zCol ){
|
||||
char *zEnd = &zCol[pColDef->n-1];
|
||||
while( (zEnd>zCol && *zEnd==';') || isspace(*(unsigned char *)zEnd) ){
|
||||
*zEnd-- = '\0';
|
||||
}
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET "
|
||||
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d,length(sql)) "
|
||||
"WHERE type = 'table' AND name = %Q",
|
||||
zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1,
|
||||
zTab
|
||||
);
|
||||
sqliteFree(zCol);
|
||||
}
|
||||
|
||||
/* If the default value of the new column is NULL, then set the file
|
||||
** format to 2. If the default value of the new column is not NULL,
|
||||
** the file format becomes 3.
|
||||
*/
|
||||
sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2);
|
||||
|
||||
/* Reload the schema of the modified table. */
|
||||
reloadTableSchema(pParse, pTab, pTab->zName);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the parser after the table-name in
|
||||
** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument
|
||||
** pSrc is the full-name of the table being altered.
|
||||
**
|
||||
** This routine makes a (partial) copy of the Table structure
|
||||
** for the table being altered and sets Parse.pNewTable to point
|
||||
** to it. Routines called by the parser as the column definition
|
||||
** is parsed (i.e. sqlite3AddColumn()) add the new Column data to
|
||||
** the copy. The copy of the Table structure is deleted by tokenize.c
|
||||
** after parsing is finished.
|
||||
**
|
||||
** Routine sqlite3AlterFinishAddColumn() will be called to complete
|
||||
** coding the "ALTER TABLE ... ADD" statement.
|
||||
*/
|
||||
void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
Table *pNew;
|
||||
Table *pTab;
|
||||
Vdbe *v;
|
||||
int iDb;
|
||||
int i;
|
||||
int nAlloc;
|
||||
|
||||
/* Look up the table being altered. */
|
||||
assert( pParse->pNewTable==0 );
|
||||
if( sqlite3MallocFailed() ) goto exit_begin_add_column;
|
||||
pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
|
||||
if( !pTab ) goto exit_begin_add_column;
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3ErrorMsg(pParse, "virtual tables may not be altered");
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Make sure this is not an attempt to ALTER a view. */
|
||||
if( pTab->pSelect ){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
|
||||
assert( pTab->addColOffset>0 );
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
|
||||
/* Put a copy of the Table struct in Parse.pNewTable for the
|
||||
** sqlite3AddColumn() function and friends to modify.
|
||||
*/
|
||||
pNew = (Table *)sqliteMalloc(sizeof(Table));
|
||||
if( !pNew ) goto exit_begin_add_column;
|
||||
pParse->pNewTable = pNew;
|
||||
pNew->nRef = 1;
|
||||
pNew->nCol = pTab->nCol;
|
||||
assert( pNew->nCol>0 );
|
||||
nAlloc = (((pNew->nCol-1)/8)*8)+8;
|
||||
assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
|
||||
pNew->aCol = (Column *)sqliteMalloc(sizeof(Column)*nAlloc);
|
||||
pNew->zName = sqliteStrDup(pTab->zName);
|
||||
if( !pNew->aCol || !pNew->zName ){
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
|
||||
for(i=0; i<pNew->nCol; i++){
|
||||
Column *pCol = &pNew->aCol[i];
|
||||
pCol->zName = sqliteStrDup(pCol->zName);
|
||||
pCol->zColl = 0;
|
||||
pCol->zType = 0;
|
||||
pCol->pDflt = 0;
|
||||
}
|
||||
pNew->pSchema = pParse->db->aDb[iDb].pSchema;
|
||||
pNew->addColOffset = pTab->addColOffset;
|
||||
pNew->nRef = 1;
|
||||
|
||||
/* Begin a transaction and increment the schema cookie. */
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( !v ) goto exit_begin_add_column;
|
||||
sqlite3ChangeCookie(pParse->db, v, iDb);
|
||||
|
||||
exit_begin_add_column:
|
||||
sqlite3SrcListDelete(pSrc);
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_ALTER_TABLE */
|
|
@ -1,403 +0,0 @@
|
|||
/*
|
||||
** 2005 July 8
|
||||
**
|
||||
** 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 associated with the ANALYZE command.
|
||||
**
|
||||
** @(#) $Id: analyze.c,v 1.16 2006/01/10 17:58:23 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** This routine generates code that opens the sqlite_stat1 table on cursor
|
||||
** iStatCur.
|
||||
**
|
||||
** If the sqlite_stat1 tables does not previously exist, it is created.
|
||||
** If it does previously exist, all entires associated with table zWhere
|
||||
** are removed. If zWhere==0 then all entries are removed.
|
||||
*/
|
||||
static void openStatTable(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int iDb, /* The database we are looking in */
|
||||
int iStatCur, /* Open the sqlite_stat1 table on this cursor */
|
||||
const char *zWhere /* Delete entries associated with this table */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
Db *pDb;
|
||||
int iRootPage;
|
||||
Table *pStat;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
|
||||
pDb = &db->aDb[iDb];
|
||||
if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){
|
||||
/* The sqlite_stat1 tables does not exist. Create it.
|
||||
** Note that a side-effect of the CREATE TABLE statement is to leave
|
||||
** the rootpage of the new table on the top of the stack. This is
|
||||
** important because the OpenWrite opcode below will be needing it. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)",
|
||||
pDb->zName
|
||||
);
|
||||
iRootPage = 0; /* Cause rootpage to be taken from top of stack */
|
||||
}else if( zWhere ){
|
||||
/* The sqlite_stat1 table exists. Delete all entries associated with
|
||||
** the table zWhere. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q",
|
||||
pDb->zName, zWhere
|
||||
);
|
||||
iRootPage = pStat->tnum;
|
||||
}else{
|
||||
/* The sqlite_stat1 table already exists. Delete all rows. */
|
||||
iRootPage = pStat->tnum;
|
||||
sqlite3VdbeAddOp(v, OP_Clear, pStat->tnum, iDb);
|
||||
}
|
||||
|
||||
/* Open the sqlite_stat1 table for writing. Unless it was created
|
||||
** by this vdbe program, lock it for writing at the shared-cache level.
|
||||
** If this vdbe did create the sqlite_stat1 table, then it must have
|
||||
** already obtained a schema-lock, making the write-lock redundant.
|
||||
*/
|
||||
if( iRootPage>0 ){
|
||||
sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
|
||||
sqlite3VdbeAddOp(v, OP_OpenWrite, iStatCur, iRootPage);
|
||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iStatCur, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to do an analysis of all indices associated with
|
||||
** a single table.
|
||||
*/
|
||||
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 iMem /* Available memory locations begin here */
|
||||
){
|
||||
Index *pIdx; /* An index to being analyzed */
|
||||
int iIdxCur; /* Cursor number for index being analyzed */
|
||||
int nCol; /* Number of columns in the index */
|
||||
Vdbe *v; /* The virtual machine being built up */
|
||||
int i; /* Loop counter */
|
||||
int topOfLoop; /* The top of the loop */
|
||||
int endOfLoop; /* The end of the loop */
|
||||
int addr; /* The address of an instruction */
|
||||
int iDb; /* Index of database containing pTab */
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( pTab==0 || pTab->pIndex==0 ){
|
||||
/* Do no analysis for tables that have no indices */
|
||||
return;
|
||||
}
|
||||
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
assert( iDb>=0 );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
|
||||
pParse->db->aDb[iDb].zName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Establish a read-lock on the table at the shared-cache level. */
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
iIdxCur = pParse->nTab;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
|
||||
/* Open a cursor to the index to be analyzed
|
||||
*/
|
||||
assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
|
||||
sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
|
||||
VdbeComment((v, "# %s", pIdx->zName));
|
||||
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,
|
||||
(char *)pKey, P3_KEYINFO_HANDOFF);
|
||||
nCol = pIdx->nColumn;
|
||||
if( iMem+nCol*2>=pParse->nMem ){
|
||||
pParse->nMem = iMem+nCol*2+1;
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, nCol+1);
|
||||
|
||||
/* Memory cells are used as follows:
|
||||
**
|
||||
** mem[iMem]: The total number of rows in the table.
|
||||
** mem[iMem+1]: Number of distinct values in column 1
|
||||
** ...
|
||||
** mem[iMem+nCol]: Number of distinct values in column N
|
||||
** mem[iMem+nCol+1] Last observed value of column 1
|
||||
** ...
|
||||
** mem[iMem+nCol+nCol]: Last observed value of column N
|
||||
**
|
||||
** Cells iMem through iMem+nCol are initialized to 0. The others
|
||||
** are initialized to NULL.
|
||||
*/
|
||||
for(i=0; i<=nCol; i++){
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, iMem+i);
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp(v, OP_MemNull, iMem+nCol+i+1, 0);
|
||||
}
|
||||
|
||||
/* Do the analysis.
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, iIdxCur, endOfLoop);
|
||||
topOfLoop = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp(v, OP_MemIncr, 1, iMem);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp(v, OP_Column, iIdxCur, i);
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, iMem+nCol+i+1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Ne, 0x100, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Goto, 0, endOfLoop);
|
||||
for(i=0; i<nCol; i++){
|
||||
addr = sqlite3VdbeAddOp(v, OP_MemIncr, 1, iMem+i+1);
|
||||
sqlite3VdbeChangeP2(v, topOfLoop + 3*i + 3, addr);
|
||||
sqlite3VdbeAddOp(v, OP_Column, iIdxCur, i);
|
||||
sqlite3VdbeAddOp(v, OP_MemStore, iMem+nCol+i+1, 1);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, endOfLoop);
|
||||
sqlite3VdbeAddOp(v, OP_Next, iIdxCur, topOfLoop);
|
||||
sqlite3VdbeAddOp(v, OP_Close, iIdxCur, 0);
|
||||
|
||||
/* Store the results.
|
||||
**
|
||||
** The result is a single row of the sqlite_stmt1 table. The first
|
||||
** two columns are the names of the table and index. The third column
|
||||
** is a string composed of a list of integer statistics about the
|
||||
** index. The first integer in the list is the total number of entires
|
||||
** in the index. There is one additional integer in the list for each
|
||||
** column of the table. This additional integer is a guess of how many
|
||||
** rows of the table the index will select. If D is the count of distinct
|
||||
** values and K is the total number of rows, then the integer is computed
|
||||
** as:
|
||||
**
|
||||
** I = (K+D-1)/D
|
||||
**
|
||||
** If K==0 then no entry is made into the sqlite_stat1 table.
|
||||
** If K>0 then it is always the case the D>0 so division by zero
|
||||
** is never possible.
|
||||
*/
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0);
|
||||
addr = sqlite3VdbeAddOp(v, OP_IfNot, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_NewRowid, iStatCur, 0);
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0);
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0, pIdx->zName, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0);
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0, " ", 0);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, iMem, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, iMem+i+1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Add, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_AddImm, -1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, iMem+i+1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Divide, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_ToInt, 0, 0);
|
||||
if( i==nCol-1 ){
|
||||
sqlite3VdbeAddOp(v, OP_Concat, nCol*2-1, 0);
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_Dup, 1, 0);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeOp3(v, OP_MakeRecord, 3, 0, "aaa", 0);
|
||||
sqlite3VdbeAddOp(v, OP_Insert, iStatCur, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will cause the most recent index analysis to
|
||||
** be laoded into internal hash tables where is can be used.
|
||||
*/
|
||||
static void loadAnalysis(Parse *pParse, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
sqlite3VdbeAddOp(v, OP_LoadAnalysis, iDb, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will do an analysis of an entire database
|
||||
*/
|
||||
static void analyzeDatabase(Parse *pParse, int iDb){
|
||||
sqlite3 *db = pParse->db;
|
||||
Schema *pSchema = db->aDb[iDb].pSchema; /* Schema of database iDb */
|
||||
HashElem *k;
|
||||
int iStatCur;
|
||||
int iMem;
|
||||
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
iStatCur = pParse->nTab++;
|
||||
openStatTable(pParse, iDb, iStatCur, 0);
|
||||
iMem = pParse->nMem;
|
||||
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
|
||||
Table *pTab = (Table*)sqliteHashData(k);
|
||||
analyzeOneTable(pParse, pTab, iStatCur, iMem);
|
||||
}
|
||||
loadAnalysis(pParse, iDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will do an analysis of a single table in
|
||||
** a database.
|
||||
*/
|
||||
static void analyzeTable(Parse *pParse, Table *pTab){
|
||||
int iDb;
|
||||
int iStatCur;
|
||||
|
||||
assert( pTab!=0 );
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
iStatCur = pParse->nTab++;
|
||||
openStatTable(pParse, iDb, iStatCur, pTab->zName);
|
||||
analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem);
|
||||
loadAnalysis(pParse, iDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for the ANALYZE command. The parser calls this routine
|
||||
** when it recognizes an ANALYZE command.
|
||||
**
|
||||
** ANALYZE -- 1
|
||||
** ANALYZE <database> -- 2
|
||||
** ANALYZE ?<database>.?<tablename> -- 3
|
||||
**
|
||||
** Form 1 causes all indices in all attached databases to be analyzed.
|
||||
** Form 2 analyzes all indices the single database named.
|
||||
** Form 3 analyzes all indices associated with the named table.
|
||||
*/
|
||||
void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
|
||||
sqlite3 *db = pParse->db;
|
||||
int iDb;
|
||||
int i;
|
||||
char *z, *zDb;
|
||||
Table *pTab;
|
||||
Token *pTableName;
|
||||
|
||||
/* Read the database schema. If an error occurs, leave an error message
|
||||
** and code in pParse and return NULL. */
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( pName1==0 ){
|
||||
/* Form 1: Analyze everything */
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( i==1 ) continue; /* Do not analyze the TEMP database */
|
||||
analyzeDatabase(pParse, i);
|
||||
}
|
||||
}else if( pName2==0 || pName2->n==0 ){
|
||||
/* Form 2: Analyze the database or table named */
|
||||
iDb = sqlite3FindDb(db, pName1);
|
||||
if( iDb>=0 ){
|
||||
analyzeDatabase(pParse, iDb);
|
||||
}else{
|
||||
z = sqlite3NameFromToken(pName1);
|
||||
pTab = sqlite3LocateTable(pParse, z, 0);
|
||||
sqliteFree(z);
|
||||
if( pTab ){
|
||||
analyzeTable(pParse, pTab);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* Form 3: Analyze the fully qualified table name */
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
|
||||
if( iDb>=0 ){
|
||||
zDb = db->aDb[iDb].zName;
|
||||
z = sqlite3NameFromToken(pTableName);
|
||||
pTab = sqlite3LocateTable(pParse, z, zDb);
|
||||
sqliteFree(z);
|
||||
if( pTab ){
|
||||
analyzeTable(pParse, pTab);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Used to pass information from the analyzer reader through to the
|
||||
** callback routine.
|
||||
*/
|
||||
typedef struct analysisInfo analysisInfo;
|
||||
struct analysisInfo {
|
||||
sqlite3 *db;
|
||||
const char *zDatabase;
|
||||
};
|
||||
|
||||
/*
|
||||
** This callback is invoked once for each index when reading the
|
||||
** sqlite_stat1 table.
|
||||
**
|
||||
** 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){
|
||||
analysisInfo *pInfo = (analysisInfo*)pData;
|
||||
Index *pIndex;
|
||||
int i, c;
|
||||
unsigned int v;
|
||||
const char *z;
|
||||
|
||||
assert( argc==2 );
|
||||
if( argv==0 || argv[0]==0 || argv[1]==0 ){
|
||||
return 0;
|
||||
}
|
||||
pIndex = sqlite3FindIndex(pInfo->db, argv[0], pInfo->zDatabase);
|
||||
if( pIndex==0 ){
|
||||
return 0;
|
||||
}
|
||||
z = argv[1];
|
||||
for(i=0; *z && i<=pIndex->nColumn; i++){
|
||||
v = 0;
|
||||
while( (c=z[0])>='0' && c<='9' ){
|
||||
v = v*10 + c - '0';
|
||||
z++;
|
||||
}
|
||||
pIndex->aiRowEst[i] = v;
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 table into the index hash tables.
|
||||
*/
|
||||
void sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
analysisInfo sInfo;
|
||||
HashElem *i;
|
||||
char *zSql;
|
||||
|
||||
/* Clear any prior statistics */
|
||||
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
sqlite3DefaultRowEst(pIdx);
|
||||
}
|
||||
|
||||
/* Check to make sure the sqlite_stat1 table existss */
|
||||
sInfo.db = db;
|
||||
sInfo.zDatabase = db->aDb[iDb].zName;
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf("SELECT idx, stat FROM %Q.sqlite_stat1",
|
||||
sInfo.zDatabase);
|
||||
sqlite3SafetyOff(db);
|
||||
sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
|
||||
sqlite3SafetyOn(db);
|
||||
sqliteFree(zSql);
|
||||
}
|
||||
|
||||
|
||||
#endif /* SQLITE_OMIT_ANALYZE */
|
|
@ -1,504 +0,0 @@
|
|||
/*
|
||||
** 2003 April 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||
**
|
||||
** $Id: attach.c,v 1.53 2006/06/27 16:34:57 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Resolve an expression that was part of an ATTACH or DETACH statement. This
|
||||
** is slightly different from resolving a normal SQL expression, because simple
|
||||
** identifiers are treated as strings, not possible column names or aliases.
|
||||
**
|
||||
** i.e. if the parser sees:
|
||||
**
|
||||
** ATTACH DATABASE abc AS def
|
||||
**
|
||||
** it treats the two expressions as literal strings 'abc' and 'def' instead of
|
||||
** looking for columns of the same name.
|
||||
**
|
||||
** This only applies to the root node of pExpr, so the statement:
|
||||
**
|
||||
** ATTACH DATABASE abc||def AS 'db2'
|
||||
**
|
||||
** will fail because neither abc or def can be resolved.
|
||||
*/
|
||||
static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
if( pExpr ){
|
||||
if( pExpr->op!=TK_ID ){
|
||||
rc = sqlite3ExprResolveNames(pName, pExpr);
|
||||
}else{
|
||||
pExpr->op = TK_STRING;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** An SQL user-function registered to do the work of an ATTACH statement. The
|
||||
** three arguments to the function come directly from an attach statement:
|
||||
**
|
||||
** ATTACH DATABASE x AS y KEY z
|
||||
**
|
||||
** SELECT sqlite_attach(x, y, z)
|
||||
**
|
||||
** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the
|
||||
** third argument.
|
||||
*/
|
||||
static void attachFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int i;
|
||||
int rc = 0;
|
||||
sqlite3 *db = sqlite3_user_data(context);
|
||||
const char *zName;
|
||||
const char *zFile;
|
||||
Db *aNew;
|
||||
char zErr[128];
|
||||
char *zErrDyn = 0;
|
||||
|
||||
zFile = (const char *)sqlite3_value_text(argv[0]);
|
||||
zName = (const char *)sqlite3_value_text(argv[1]);
|
||||
if( zFile==0 ) zFile = "";
|
||||
if( zName==0 ) zName = "";
|
||||
|
||||
/* Check for the following errors:
|
||||
**
|
||||
** * Too many attached databases,
|
||||
** * Transaction currently open
|
||||
** * Specified database name already being used.
|
||||
*/
|
||||
if( db->nDb>=MAX_ATTACHED+2 ){
|
||||
sqlite3_snprintf(
|
||||
sizeof(zErr), zErr, "too many attached databases - max %d", MAX_ATTACHED
|
||||
);
|
||||
goto attach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
strcpy(zErr, "cannot ATTACH database within transaction");
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zName;
|
||||
if( z && zName && sqlite3StrICmp(z, zName)==0 ){
|
||||
sqlite3_snprintf(sizeof(zErr), zErr, "database %s is already in use", zName);
|
||||
goto attach_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the new entry in the db->aDb[] array and initialise the schema
|
||||
** hash tables.
|
||||
*/
|
||||
if( db->aDb==db->aDbStatic ){
|
||||
aNew = sqliteMalloc( sizeof(db->aDb[0])*3 );
|
||||
if( aNew==0 ){
|
||||
return;
|
||||
}
|
||||
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
|
||||
}else{
|
||||
aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
|
||||
if( aNew==0 ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
db->aDb = aNew;
|
||||
aNew = &db->aDb[db->nDb++];
|
||||
memset(aNew, 0, sizeof(*aNew));
|
||||
|
||||
/* Open the database file. If the btree is successfully opened, use
|
||||
** it to obtain the database schema. At this point the schema may
|
||||
** or may not be initialised.
|
||||
*/
|
||||
rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
|
||||
if( rc==SQLITE_OK ){
|
||||
aNew->pSchema = sqlite3SchemaGet(aNew->pBt);
|
||||
if( !aNew->pSchema ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
|
||||
strcpy(zErr,
|
||||
"attached databases must use the same text encoding as main database");
|
||||
goto attach_error;
|
||||
}
|
||||
}
|
||||
aNew->zName = sqliteStrDup(zName);
|
||||
aNew->safety_level = 3;
|
||||
|
||||
#if SQLITE_HAS_CODEC
|
||||
{
|
||||
extern int sqlite3CodecAttach(sqlite3*, int, void*, int);
|
||||
extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
|
||||
int nKey;
|
||||
char *zKey;
|
||||
int t = sqlite3_value_type(argv[2]);
|
||||
switch( t ){
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT:
|
||||
zErrDyn = sqliteStrDup("Invalid key value");
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
|
||||
case SQLITE_TEXT:
|
||||
case SQLITE_BLOB:
|
||||
nKey = sqlite3_value_bytes(argv[2]);
|
||||
zKey = (char *)sqlite3_value_blob(argv[2]);
|
||||
sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
|
||||
break;
|
||||
|
||||
case SQLITE_NULL:
|
||||
/* No key specified. Use the key from the main database */
|
||||
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
|
||||
sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the file was opened successfully, read the schema for the new database.
|
||||
** If this fails, or if opening the file failed, then close the file and
|
||||
** remove the entry from the db->aDb[] array. i.e. put everything back the way
|
||||
** we found it.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3SafetyOn(db);
|
||||
rc = sqlite3Init(db, &zErrDyn);
|
||||
sqlite3SafetyOff(db);
|
||||
}
|
||||
if( rc ){
|
||||
int iDb = db->nDb - 1;
|
||||
assert( iDb>=2 );
|
||||
if( db->aDb[iDb].pBt ){
|
||||
sqlite3BtreeClose(db->aDb[iDb].pBt);
|
||||
db->aDb[iDb].pBt = 0;
|
||||
db->aDb[iDb].pSchema = 0;
|
||||
}
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
db->nDb = iDb;
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
if( !sqlite3MallocFailed() ) sqlite3FailedMalloc();
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "out of memory");
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "unable to open database: %s", zFile);
|
||||
}
|
||||
goto attach_error;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
attach_error:
|
||||
/* Return an error if we get here */
|
||||
if( zErrDyn ){
|
||||
sqlite3_result_error(context, zErrDyn, -1);
|
||||
sqliteFree(zErrDyn);
|
||||
}else{
|
||||
zErr[sizeof(zErr)-1] = 0;
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** An SQL user-function registered to do the work of an DETACH statement. The
|
||||
** three arguments to the function come directly from a detach statement:
|
||||
**
|
||||
** DETACH DATABASE x
|
||||
**
|
||||
** SELECT sqlite_detach(x)
|
||||
*/
|
||||
static void detachFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zName = (const char *)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_user_data(context);
|
||||
int i;
|
||||
Db *pDb = 0;
|
||||
char zErr[128];
|
||||
|
||||
if( zName==0 ) zName = "";
|
||||
for(i=0; i<db->nDb; i++){
|
||||
pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ) continue;
|
||||
if( sqlite3StrICmp(pDb->zName, zName)==0 ) break;
|
||||
}
|
||||
|
||||
if( i>=db->nDb ){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "no such database: %s", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
if( i<2 ){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
strcpy(zErr, "cannot DETACH database within transaction");
|
||||
goto detach_error;
|
||||
}
|
||||
if( sqlite3BtreeIsInReadTrans(pDb->pBt) ){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
pDb->pSchema = 0;
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
return;
|
||||
|
||||
detach_error:
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure generates VDBE code for a single invocation of either the
|
||||
** sqlite_detach() or sqlite_attach() SQL user functions.
|
||||
*/
|
||||
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 */
|
||||
Expr *pAuthArg, /* Expression to pass to authorization callback */
|
||||
Expr *pFilename, /* Name of database file */
|
||||
Expr *pDbname, /* Name of the database to use internally */
|
||||
Expr *pKey /* Database key for encryption extension */
|
||||
){
|
||||
int rc;
|
||||
NameContext sName;
|
||||
Vdbe *v;
|
||||
FuncDef *pFunc;
|
||||
sqlite3* db = pParse->db;
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
assert( sqlite3MallocFailed() || pAuthArg );
|
||||
if( pAuthArg ){
|
||||
char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);
|
||||
if( !zAuthArg ){
|
||||
goto attach_end;
|
||||
}
|
||||
rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
|
||||
sqliteFree(zAuthArg);
|
||||
if(rc!=SQLITE_OK ){
|
||||
goto attach_end;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
|
||||
memset(&sName, 0, sizeof(NameContext));
|
||||
sName.pParse = pParse;
|
||||
|
||||
if(
|
||||
SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) ||
|
||||
SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) ||
|
||||
SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey))
|
||||
){
|
||||
pParse->nErr++;
|
||||
goto attach_end;
|
||||
}
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
sqlite3ExprCode(pParse, pFilename);
|
||||
sqlite3ExprCode(pParse, pDbname);
|
||||
sqlite3ExprCode(pParse, pKey);
|
||||
|
||||
assert( v || sqlite3MallocFailed() );
|
||||
if( v ){
|
||||
sqlite3VdbeAddOp(v, OP_Function, 0, nFunc);
|
||||
pFunc = sqlite3FindFunction(db, zFunc, strlen(zFunc), nFunc, SQLITE_UTF8,0);
|
||||
sqlite3VdbeChangeP3(v, -1, (char *)pFunc, P3_FUNCDEF);
|
||||
|
||||
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
|
||||
** statement only). For DETACH, set it to false (expire all existing
|
||||
** statements).
|
||||
*/
|
||||
sqlite3VdbeAddOp(v, OP_Expire, (type==SQLITE_ATTACH), 0);
|
||||
}
|
||||
|
||||
attach_end:
|
||||
sqlite3ExprDelete(pFilename);
|
||||
sqlite3ExprDelete(pDbname);
|
||||
sqlite3ExprDelete(pKey);
|
||||
}
|
||||
|
||||
/*
|
||||
** Called by the parser to compile a DETACH statement.
|
||||
**
|
||||
** DETACH pDbname
|
||||
*/
|
||||
void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
codeAttach(pParse, SQLITE_DETACH, "sqlite_detach", 1, pDbname, 0, 0, pDbname);
|
||||
}
|
||||
|
||||
/*
|
||||
** Called by the parser to compile an ATTACH statement.
|
||||
**
|
||||
** 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);
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the functions sqlite_attach and sqlite_detach.
|
||||
*/
|
||||
void sqlite3AttachFunctions(sqlite3 *db){
|
||||
static const int enc = SQLITE_UTF8;
|
||||
sqlite3CreateFunc(db, "sqlite_attach", 3, enc, db, attachFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "sqlite_detach", 1, enc, db, detachFunc, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
**
|
||||
** The return value indicates whether or not fixation is required. TRUE
|
||||
** means we do need to fix the database references, FALSE means we do not.
|
||||
*/
|
||||
int sqlite3FixInit(
|
||||
DbFixer *pFix, /* The fixer to be initialized */
|
||||
Parse *pParse, /* Error messages will be written here */
|
||||
int iDb, /* This is the database that must be used */
|
||||
const char *zType, /* "view", "trigger", or "index" */
|
||||
const Token *pName /* Name of the view, trigger, or index */
|
||||
){
|
||||
sqlite3 *db;
|
||||
|
||||
if( iDb<0 || iDb==1 ) return 0;
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zName;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following set of routines walk through the parse tree and assign
|
||||
** a specific database to all table references where the database name
|
||||
** was left unspecified in the original SQL statement. The pFix structure
|
||||
** must have been initialized by a prior call to sqlite3FixInit().
|
||||
**
|
||||
** These routines are used to make sure that an index, trigger, or
|
||||
** view in one database does not refer to objects in a different database.
|
||||
** (Exception: indices, triggers, and views in the TEMP database are
|
||||
** allowed to refer to anything.) If a reference is explicitly made
|
||||
** to an object in a different database, an error message is added to
|
||||
** pParse->zErrMsg and these routines return non-zero. If everything
|
||||
** checks out, these routines return 0.
|
||||
*/
|
||||
int sqlite3FixSrcList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
SrcList *pList /* The Source list to check and modify */
|
||||
){
|
||||
int i;
|
||||
const char *zDb;
|
||||
struct SrcList_item *pItem;
|
||||
|
||||
if( pList==0 ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pItem->zDatabase==0 ){
|
||||
pItem->zDatabase = sqliteStrDup(zDb);
|
||||
}else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
return 1;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
|
||||
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
int sqlite3FixSelect(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Select *pSelect /* The SELECT statement to be fixed to one database */
|
||||
){
|
||||
while( pSelect ){
|
||||
if( sqlite3FixExprList(pFix, pSelect->pEList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
|
||||
return 1;
|
||||
}
|
||||
pSelect = pSelect->pPrior;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqlite3FixExpr(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pExpr->pList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pExpr->pRight) ){
|
||||
return 1;
|
||||
}
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqlite3FixExprList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
ExprList *pList /* The expression to be fixed to one database */
|
||||
){
|
||||
int i;
|
||||
struct ExprList_item *pItem;
|
||||
if( pList==0 ) return 0;
|
||||
for(i=0, pItem=pList->a; i<pList->nExpr; i++, pItem++){
|
||||
if( sqlite3FixExpr(pFix, pItem->pExpr) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int sqlite3FixTriggerStep(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
TriggerStep *pStep /* The trigger step be fixed to one database */
|
||||
){
|
||||
while( pStep ){
|
||||
if( sqlite3FixSelect(pFix, pStep->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pStep->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
||||
return 1;
|
||||
}
|
||||
pStep = pStep->pNext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -1,234 +0,0 @@
|
|||
/*
|
||||
** 2003 January 11
|
||||
**
|
||||
** 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 used to implement the sqlite3_set_authorizer()
|
||||
** API. This facility is an optional feature of the library. Embedded
|
||||
** systems that do not need this facility may omit it by recompiling
|
||||
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
|
||||
**
|
||||
** $Id: auth.c,v 1.25 2006/06/16 08:01:03 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** All of the code in this file may be omitted by defining a single
|
||||
** macro.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
|
||||
/*
|
||||
** Set or clear the access authorization function.
|
||||
**
|
||||
** The access authorization function is be called during the compilation
|
||||
** phase to verify that the user has read and/or write access permission on
|
||||
** various fields of the database. The first argument to the auth function
|
||||
** is a copy of the 3rd argument to this routine. The second argument
|
||||
** to the auth function is one of these constants:
|
||||
**
|
||||
** SQLITE_CREATE_INDEX
|
||||
** SQLITE_CREATE_TABLE
|
||||
** SQLITE_CREATE_TEMP_INDEX
|
||||
** SQLITE_CREATE_TEMP_TABLE
|
||||
** SQLITE_CREATE_TEMP_TRIGGER
|
||||
** SQLITE_CREATE_TEMP_VIEW
|
||||
** SQLITE_CREATE_TRIGGER
|
||||
** SQLITE_CREATE_VIEW
|
||||
** SQLITE_DELETE
|
||||
** SQLITE_DROP_INDEX
|
||||
** SQLITE_DROP_TABLE
|
||||
** SQLITE_DROP_TEMP_INDEX
|
||||
** SQLITE_DROP_TEMP_TABLE
|
||||
** SQLITE_DROP_TEMP_TRIGGER
|
||||
** SQLITE_DROP_TEMP_VIEW
|
||||
** SQLITE_DROP_TRIGGER
|
||||
** SQLITE_DROP_VIEW
|
||||
** SQLITE_INSERT
|
||||
** SQLITE_PRAGMA
|
||||
** SQLITE_READ
|
||||
** SQLITE_SELECT
|
||||
** SQLITE_TRANSACTION
|
||||
** SQLITE_UPDATE
|
||||
**
|
||||
** The third and fourth arguments to the auth function are the name of
|
||||
** the table and the column that are being accessed. The auth function
|
||||
** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If
|
||||
** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY
|
||||
** means that the SQL statement will never-run - the sqlite3_exec() call
|
||||
** will return with an error. SQLITE_IGNORE means that the SQL statement
|
||||
** should run but attempts to read the specified column will return NULL
|
||||
** and attempts to write the column will be ignored.
|
||||
**
|
||||
** Setting the auth function to NULL disables this hook. The default
|
||||
** setting of the auth function is NULL.
|
||||
*/
|
||||
int sqlite3_set_authorizer(
|
||||
sqlite3 *db,
|
||||
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
|
||||
void *pArg
|
||||
){
|
||||
db->xAuth = xAuth;
|
||||
db->pAuthArg = pArg;
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write an error message into pParse->zErrMsg that explains that the
|
||||
** user-supplied authorization function returned an illegal value.
|
||||
*/
|
||||
static void sqliteAuthBadReturnCode(Parse *pParse, int rc){
|
||||
sqlite3ErrorMsg(pParse, "illegal return value (%d) from the "
|
||||
"authorization function - should be SQLITE_OK, SQLITE_IGNORE, "
|
||||
"or SQLITE_DENY", rc);
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** The pExpr should be a TK_COLUMN expression. The table referred to
|
||||
** is in pTabList or else it is the NEW or OLD table of a trigger.
|
||||
** Check to see if it is OK to read this particular column.
|
||||
**
|
||||
** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN
|
||||
** instruction into a TK_NULL. If the auth function returns SQLITE_DENY,
|
||||
** then generate an error.
|
||||
*/
|
||||
void sqlite3AuthRead(
|
||||
Parse *pParse, /* The parser context */
|
||||
Expr *pExpr, /* The expression to check authorization on */
|
||||
SrcList *pTabList /* All table that pExpr might refer to */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
int rc;
|
||||
Table *pTab; /* The table being read */
|
||||
const char *zCol; /* Name of the column of the table */
|
||||
int iSrc; /* Index in pTabList->a[] of table being read */
|
||||
const char *zDBase; /* Name of database being accessed */
|
||||
TriggerStack *pStack; /* The stack of current triggers */
|
||||
int iDb; /* The index of the database the expression refers to */
|
||||
|
||||
if( db->xAuth==0 ) return;
|
||||
if( pExpr->op==TK_AS ) return;
|
||||
assert( pExpr->op==TK_COLUMN );
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pExpr->pSchema);
|
||||
if( iDb<0 ){
|
||||
/* An attempt to read a column out of a subquery or other
|
||||
** temporary table. */
|
||||
return;
|
||||
}
|
||||
for(iSrc=0; pTabList && iSrc<pTabList->nSrc; iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
|
||||
}
|
||||
if( iSrc>=0 && pTabList && iSrc<pTabList->nSrc ){
|
||||
pTab = pTabList->a[iSrc].pTab;
|
||||
}else if( (pStack = pParse->trigStack)!=0 ){
|
||||
/* This must be an attempt to read the NEW or OLD pseudo-tables
|
||||
** of a trigger.
|
||||
*/
|
||||
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
|
||||
pTab = pStack->pTab;
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
if( pTab==0 ) return;
|
||||
if( pExpr->iColumn>=0 ){
|
||||
assert( pExpr->iColumn<pTab->nCol );
|
||||
zCol = pTab->aCol[pExpr->iColumn].zName;
|
||||
}else if( pTab->iPKey>=0 ){
|
||||
assert( pTab->iPKey<pTab->nCol );
|
||||
zCol = pTab->aCol[pTab->iPKey].zName;
|
||||
}else{
|
||||
zCol = "ROWID";
|
||||
}
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
zDBase = db->aDb[iDb].zName;
|
||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase,
|
||||
pParse->zAuthContext);
|
||||
if( rc==SQLITE_IGNORE ){
|
||||
pExpr->op = TK_NULL;
|
||||
}else if( rc==SQLITE_DENY ){
|
||||
if( db->nDb>2 || iDb!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",
|
||||
zDBase, pTab->zName, zCol);
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited",pTab->zName,zCol);
|
||||
}
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Do an authorization check using the code and arguments given. Return
|
||||
** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY
|
||||
** is returned, then the error count and error message in pParse are
|
||||
** modified appropriately.
|
||||
*/
|
||||
int sqlite3AuthCheck(
|
||||
Parse *pParse,
|
||||
int code,
|
||||
const char *zArg1,
|
||||
const char *zArg2,
|
||||
const char *zArg3
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
int rc;
|
||||
|
||||
/* Don't do any authorization checks if the database is initialising
|
||||
** or if the parser is being invoked from within sqlite3_declare_vtab.
|
||||
*/
|
||||
if( db->init.busy || IN_DECLARE_VTAB ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
if( db->xAuth==0 ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
|
||||
if( rc==SQLITE_DENY ){
|
||||
sqlite3ErrorMsg(pParse, "not authorized");
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
|
||||
rc = SQLITE_DENY;
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Push an authorization context. After this routine is called, the
|
||||
** zArg3 argument to authorization callbacks will be zContext until
|
||||
** popped. Or if pParse==0, this routine is a no-op.
|
||||
*/
|
||||
void sqlite3AuthContextPush(
|
||||
Parse *pParse,
|
||||
AuthContext *pContext,
|
||||
const char *zContext
|
||||
){
|
||||
pContext->pParse = pParse;
|
||||
if( pParse ){
|
||||
pContext->zAuthContext = pParse->zAuthContext;
|
||||
pParse->zAuthContext = zContext;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop an authorization context that was previously pushed
|
||||
** by sqlite3AuthContextPush
|
||||
*/
|
||||
void sqlite3AuthContextPop(AuthContext *pContext){
|
||||
if( pContext->pParse ){
|
||||
pContext->pParse->zAuthContext = pContext->zAuthContext;
|
||||
pContext->pParse = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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 B-Tree file
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.72 2007/01/27 02:24:55 drh Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
|
||||
/* TODO: This definition is just included so other modules compile. It
|
||||
** needs to be revisited.
|
||||
*/
|
||||
#define SQLITE_N_BTREE_META 10
|
||||
|
||||
/*
|
||||
** 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".
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_AUTOVACUUM
|
||||
#define SQLITE_DEFAULT_AUTOVACUUM 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Forward declarations of structure
|
||||
*/
|
||||
typedef struct Btree Btree;
|
||||
typedef struct BtCursor BtCursor;
|
||||
typedef struct BtShared BtShared;
|
||||
|
||||
|
||||
int sqlite3BtreeOpen(
|
||||
const char *zFilename, /* Name of database file to open */
|
||||
sqlite3 *db, /* Associated database connection */
|
||||
Btree **, /* Return open Btree* here */
|
||||
int flags /* Flags */
|
||||
);
|
||||
|
||||
/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the
|
||||
** following values.
|
||||
**
|
||||
** NOTE: These values must match the corresponding PAGER_ values in
|
||||
** pager.h.
|
||||
*/
|
||||
#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */
|
||||
#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */
|
||||
#define BTREE_MEMORY 4 /* In-memory DB. No argument */
|
||||
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetBusyHandler(Btree*,BusyHandler*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
|
||||
int sqlite3BtreeSyncDisabled(Btree*);
|
||||
int sqlite3BtreeSetPageSize(Btree*,int,int);
|
||||
int sqlite3BtreeGetPageSize(Btree*);
|
||||
int sqlite3BtreeGetReserve(Btree*);
|
||||
int sqlite3BtreeSetAutoVacuum(Btree *, int);
|
||||
int sqlite3BtreeGetAutoVacuum(Btree *);
|
||||
int sqlite3BtreeBeginTrans(Btree*,int);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*);
|
||||
int sqlite3BtreeCommitStmt(Btree*);
|
||||
int sqlite3BtreeRollbackStmt(Btree*);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInStmt(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeSync(Btree*, const char *zMaster);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
int sqlite3BtreeSchemaLocked(Btree *);
|
||||
int sqlite3BtreeLockTable(Btree *, int, u8);
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
const char *sqlite3BtreeGetDirname(Btree *);
|
||||
const char *sqlite3BtreeGetJournalname(Btree *);
|
||||
int sqlite3BtreeCopyFile(Btree *, Btree *);
|
||||
|
||||
/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR
|
||||
** of the following flags:
|
||||
*/
|
||||
#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */
|
||||
#define BTREE_ZERODATA 2 /* Table has keys only - no data */
|
||||
#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */
|
||||
|
||||
int sqlite3BtreeDropTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTable(Btree*, int);
|
||||
int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue);
|
||||
int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
|
||||
|
||||
int sqlite3BtreeCursor(
|
||||
Btree*, /* BTree containing table to open */
|
||||
int iTable, /* Index of root page */
|
||||
int wrFlag, /* 1 for writing. 0 for read-only */
|
||||
int(*)(void*,int,const void*,int,const void*), /* Key comparison function */
|
||||
void*, /* First argument to compare function */
|
||||
BtCursor **ppCursor /* Returned cursor */
|
||||
);
|
||||
|
||||
void sqlite3BtreeSetCompare(
|
||||
BtCursor *,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void*
|
||||
);
|
||||
|
||||
int sqlite3BtreeCloseCursor(BtCursor*);
|
||||
int sqlite3BtreeMoveto(BtCursor*, const void *pKey, i64 nKey, int *pRes);
|
||||
int sqlite3BtreeDelete(BtCursor*);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeEof(BtCursor*);
|
||||
int sqlite3BtreeFlags(BtCursor*);
|
||||
int sqlite3BtreePrevious(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
|
||||
int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
|
||||
const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
|
||||
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
|
||||
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
|
||||
void sqlite3BtreeCursorList(Btree*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3BtreePageDump(Btree*, int, int recursive);
|
||||
#else
|
||||
#define sqlite3BtreePageDump(X,Y,Z) SQLITE_OK
|
||||
#endif
|
||||
|
||||
#endif /* _BTREE_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,373 +0,0 @@
|
|||
/*
|
||||
** 2005 May 23
|
||||
**
|
||||
** 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 functions used to access the internal hash tables
|
||||
** of user defined functions and collation sequences.
|
||||
**
|
||||
** $Id: callback.c,v 1.16 2007/02/02 12:44:37 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Invoke the 'collation needed' callback to request a collation sequence
|
||||
** in the database text encoding of name zName, length nName.
|
||||
** If the collation sequence
|
||||
*/
|
||||
static void callCollNeeded(sqlite3 *db, const char *zName, int nName){
|
||||
assert( !db->xCollNeeded || !db->xCollNeeded16 );
|
||||
if( nName<0 ) nName = strlen(zName);
|
||||
if( db->xCollNeeded ){
|
||||
char *zExternal = sqliteStrNDup(zName, nName);
|
||||
if( !zExternal ) return;
|
||||
db->xCollNeeded(db->pCollNeededArg, db, (int)ENC(db), zExternal);
|
||||
sqliteFree(zExternal);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( db->xCollNeeded16 ){
|
||||
char const *zExternal;
|
||||
sqlite3_value *pTmp = sqlite3ValueNew();
|
||||
sqlite3ValueSetStr(pTmp, nName, zName, SQLITE_UTF8, SQLITE_STATIC);
|
||||
zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE);
|
||||
if( zExternal ){
|
||||
db->xCollNeeded16(db->pCollNeededArg, db, (int)ENC(db), zExternal);
|
||||
}
|
||||
sqlite3ValueFree(pTmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called if the collation factory fails to deliver a
|
||||
** collation function in the best encoding but there may be other versions
|
||||
** of this collation function (for other text encodings) available. Use one
|
||||
** of these instead if they exist. Avoid a UTF-8 <-> UTF-16 conversion if
|
||||
** possible.
|
||||
*/
|
||||
static int synthCollSeq(sqlite3 *db, CollSeq *pColl){
|
||||
CollSeq *pColl2;
|
||||
char *z = pColl->zName;
|
||||
int n = strlen(z);
|
||||
int i;
|
||||
static const u8 aEnc[] = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 };
|
||||
for(i=0; i<3; i++){
|
||||
pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, n, 0);
|
||||
if( pColl2->xCmp!=0 ){
|
||||
memcpy(pColl, pColl2, sizeof(CollSeq));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is responsible for invoking the collation factory callback
|
||||
** or substituting a collation sequence of a different encoding when the
|
||||
** requested collation sequence is not available in the database native
|
||||
** encoding.
|
||||
**
|
||||
** If it is not NULL, then pColl must point to the database native encoding
|
||||
** collation sequence with name zName, length nName.
|
||||
**
|
||||
** The return value is either the collation sequence to be used in database
|
||||
** db for collation type name zName, length nName, or NULL, if no collation
|
||||
** sequence can be found.
|
||||
*/
|
||||
CollSeq *sqlite3GetCollSeq(
|
||||
sqlite3* db,
|
||||
CollSeq *pColl,
|
||||
const char *zName,
|
||||
int nName
|
||||
){
|
||||
CollSeq *p;
|
||||
|
||||
p = pColl;
|
||||
if( !p ){
|
||||
p = sqlite3FindCollSeq(db, ENC(db), zName, nName, 0);
|
||||
}
|
||||
if( !p || !p->xCmp ){
|
||||
/* No collation sequence of this type for this encoding is registered.
|
||||
** Call the collation factory to see if it can supply us with one.
|
||||
*/
|
||||
callCollNeeded(db, zName, nName);
|
||||
p = sqlite3FindCollSeq(db, ENC(db), zName, nName, 0);
|
||||
}
|
||||
if( p && !p->xCmp && synthCollSeq(db, p) ){
|
||||
p = 0;
|
||||
}
|
||||
assert( !p || p->xCmp );
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called on a collation sequence before it is used to
|
||||
** check that it is defined. An undefined collation sequence exists when
|
||||
** a database is loaded that contains references to collation sequences
|
||||
** that have not been defined by sqlite3_create_collation() etc.
|
||||
**
|
||||
** If required, this routine calls the 'collation needed' callback to
|
||||
** request a definition of the collating sequence. If this doesn't work,
|
||||
** an equivalent collating sequence that uses a text encoding different
|
||||
** from the main database is substituted, if one is available.
|
||||
*/
|
||||
int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){
|
||||
if( pColl ){
|
||||
const char *zName = pColl->zName;
|
||||
CollSeq *p = sqlite3GetCollSeq(pParse->db, pColl, zName, -1);
|
||||
if( !p ){
|
||||
if( pParse->nErr==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
||||
}
|
||||
pParse->nErr++;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
assert( p==pColl );
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Locate and return an entry from the db.aCollSeq hash table. If the entry
|
||||
** specified by zName and nName is not found and parameter 'create' is
|
||||
** true, then create a new entry. Otherwise return NULL.
|
||||
**
|
||||
** Each pointer stored in the sqlite3.aCollSeq hash table contains an
|
||||
** array of three CollSeq structures. The first is the collation sequence
|
||||
** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be.
|
||||
**
|
||||
** Stored immediately after the three collation sequences is a copy of
|
||||
** the collation sequence name. A pointer to this string is stored in
|
||||
** each collation sequence structure.
|
||||
*/
|
||||
static CollSeq *findCollSeqEntry(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
int nName,
|
||||
int create
|
||||
){
|
||||
CollSeq *pColl;
|
||||
if( nName<0 ) nName = strlen(zName);
|
||||
pColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
|
||||
|
||||
if( 0==pColl && create ){
|
||||
pColl = sqliteMalloc( 3*sizeof(*pColl) + nName + 1 );
|
||||
if( pColl ){
|
||||
CollSeq *pDel = 0;
|
||||
pColl[0].zName = (char*)&pColl[3];
|
||||
pColl[0].enc = SQLITE_UTF8;
|
||||
pColl[1].zName = (char*)&pColl[3];
|
||||
pColl[1].enc = SQLITE_UTF16LE;
|
||||
pColl[2].zName = (char*)&pColl[3];
|
||||
pColl[2].enc = SQLITE_UTF16BE;
|
||||
memcpy(pColl[0].zName, zName, nName);
|
||||
pColl[0].zName[nName] = 0;
|
||||
pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl);
|
||||
|
||||
/* If a malloc() failure occured in sqlite3HashInsert(), it will
|
||||
** return the pColl pointer to be deleted (because it wasn't added
|
||||
** to the hash table).
|
||||
*/
|
||||
assert( !pDel || (sqlite3MallocFailed() && pDel==pColl) );
|
||||
if( pDel ){
|
||||
sqliteFree(pDel);
|
||||
pColl = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zName points to a UTF-8 encoded string nName bytes long.
|
||||
** Return the CollSeq* pointer for the collation sequence named zName
|
||||
** for the encoding 'enc' from the database 'db'.
|
||||
**
|
||||
** If the entry specified is not found and 'create' is true, then create a
|
||||
** new entry. Otherwise return NULL.
|
||||
**
|
||||
** A separate function sqlite3LocateCollSeq() is a wrapper around
|
||||
** this routine. sqlite3LocateCollSeq() invokes the collation factory
|
||||
** if necessary and generates an error message if the collating sequence
|
||||
** cannot be found.
|
||||
*/
|
||||
CollSeq *sqlite3FindCollSeq(
|
||||
sqlite3 *db,
|
||||
u8 enc,
|
||||
const char *zName,
|
||||
int nName,
|
||||
int create
|
||||
){
|
||||
CollSeq *pColl;
|
||||
if( zName ){
|
||||
pColl = findCollSeqEntry(db, zName, nName, create);
|
||||
}else{
|
||||
pColl = db->pDfltColl;
|
||||
}
|
||||
assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
||||
assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
|
||||
if( pColl ) pColl += enc-1;
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
** pointer to the FuncDef structure that defines that function, or return
|
||||
** NULL if the function does not exist.
|
||||
**
|
||||
** If the createFlag argument is true, then a new (blank) FuncDef
|
||||
** structure is created and liked into the "db" structure if a
|
||||
** no matching function previously existed. When createFlag is true
|
||||
** and the nArg parameter is -1, then only a function that accepts
|
||||
** any number of arguments will be returned.
|
||||
**
|
||||
** If createFlag is false and nArg is -1, then the first valid
|
||||
** function found is returned. A function is valid if either xFunc
|
||||
** or xStep is non-zero.
|
||||
**
|
||||
** If createFlag is false, then a function with the required name and
|
||||
** number of arguments may be returned even if the eTextRep flag does not
|
||||
** match that requested.
|
||||
*/
|
||||
FuncDef *sqlite3FindFunction(
|
||||
sqlite3 *db, /* An open database */
|
||||
const char *zName, /* Name of the function. Not null-terminated */
|
||||
int nName, /* Number of characters in the name */
|
||||
int nArg, /* Number of arguments. -1 means any number */
|
||||
u8 enc, /* Preferred text encoding */
|
||||
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;
|
||||
|
||||
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
if( nArg<-1 ) nArg = -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if( match>bestmatch ){
|
||||
pBest = p;
|
||||
bestmatch = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the createFlag parameter is true, and the seach 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 = sqliteMalloc(sizeof(*pBest)+nName))!=0 ){
|
||||
pBest->nArg = nArg;
|
||||
pBest->pNext = pFirst;
|
||||
pBest->iPrefEnc = enc;
|
||||
memcpy(pBest->zName, zName, nName);
|
||||
pBest->zName[nName] = 0;
|
||||
if( pBest==sqlite3HashInsert(&db->aFunc,pBest->zName,nName,(void*)pBest) ){
|
||||
sqliteFree(pBest);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
|
||||
return pBest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all resources held by the schema structure. The void* argument points
|
||||
** at a Schema struct. This function does not call sqliteFree() on the
|
||||
** pointer itself, it just cleans up subsiduary resources (i.e. the contents
|
||||
** of the schema hash tables).
|
||||
*/
|
||||
void sqlite3SchemaFree(void *p){
|
||||
Hash temp1;
|
||||
Hash temp2;
|
||||
HashElem *pElem;
|
||||
Schema *pSchema = (Schema *)p;
|
||||
|
||||
temp1 = pSchema->tblHash;
|
||||
temp2 = pSchema->trigHash;
|
||||
sqlite3HashInit(&pSchema->trigHash, SQLITE_HASH_STRING, 0);
|
||||
sqlite3HashClear(&pSchema->aFKey);
|
||||
sqlite3HashClear(&pSchema->idxHash);
|
||||
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
|
||||
sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
|
||||
}
|
||||
sqlite3HashClear(&temp2);
|
||||
sqlite3HashInit(&pSchema->tblHash, SQLITE_HASH_STRING, 0);
|
||||
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
|
||||
Table *pTab = sqliteHashData(pElem);
|
||||
sqlite3DeleteTable(0, pTab);
|
||||
}
|
||||
sqlite3HashClear(&temp1);
|
||||
pSchema->pSeqTab = 0;
|
||||
pSchema->flags &= ~DB_SchemaLoaded;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find and return the schema associated with a BTree. Create
|
||||
** a new one if necessary.
|
||||
*/
|
||||
Schema *sqlite3SchemaGet(Btree *pBt){
|
||||
Schema * p;
|
||||
if( pBt ){
|
||||
p = (Schema *)sqlite3BtreeSchema(pBt,sizeof(Schema),sqlite3SchemaFree);
|
||||
}else{
|
||||
p = (Schema *)sqliteMalloc(sizeof(Schema));
|
||||
}
|
||||
if( p && 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);
|
||||
p->enc = SQLITE_UTF8;
|
||||
}
|
||||
return p;
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** An tokenizer for SQL
|
||||
**
|
||||
** This file contains C code that implements the sqlite3_complete() API.
|
||||
** This code used to be part of the tokenizer.c source file. But by
|
||||
** separating it out, the code will be automatically omitted from
|
||||
** static links that do not use it.
|
||||
**
|
||||
** $Id: complete.c,v 1.3 2006/01/18 15:25:17 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#ifndef SQLITE_OMIT_COMPLETE
|
||||
|
||||
/*
|
||||
** This is defined in tokenize.c. We just have to import the definition.
|
||||
*/
|
||||
extern const char sqlite3IsIdChar[];
|
||||
#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsIdChar[c-0x20]))
|
||||
|
||||
|
||||
/*
|
||||
** Token types used by the sqlite3_complete() routine. See the header
|
||||
** comments on that procedure for additional information.
|
||||
*/
|
||||
#define tkSEMI 0
|
||||
#define tkWS 1
|
||||
#define tkOTHER 2
|
||||
#define tkEXPLAIN 3
|
||||
#define tkCREATE 4
|
||||
#define tkTEMP 5
|
||||
#define tkTRIGGER 6
|
||||
#define tkEND 7
|
||||
|
||||
/*
|
||||
** Return TRUE if the given SQL string ends in a semicolon.
|
||||
**
|
||||
** Special handling is require for CREATE TRIGGER statements.
|
||||
** Whenever the CREATE TRIGGER keywords are seen, the statement
|
||||
** must end with ";END;".
|
||||
**
|
||||
** This implementation uses a state machine with 7 states:
|
||||
**
|
||||
** (0) START At the beginning or end of an SQL statement. This routine
|
||||
** returns 1 if it ends in the START state and 0 if it ends
|
||||
** in any other state.
|
||||
**
|
||||
** (1) NORMAL We are in the middle of statement which ends with a single
|
||||
** semicolon.
|
||||
**
|
||||
** (2) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
|
||||
** a statement.
|
||||
**
|
||||
** (3) CREATE The keyword CREATE has been seen at the beginning of a
|
||||
** statement, possibly preceeded by EXPLAIN and/or followed by
|
||||
** TEMP or TEMPORARY
|
||||
**
|
||||
** (4) TRIGGER We are in the middle of a trigger definition that must be
|
||||
** ended by a semicolon, the keyword END, and another semicolon.
|
||||
**
|
||||
** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at
|
||||
** the end of a trigger definition.
|
||||
**
|
||||
** (6) END We've seen the ";END" of the ";END;" that occurs at the end
|
||||
** of a trigger difinition.
|
||||
**
|
||||
** Transitions between states above are determined by tokens extracted
|
||||
** from the input. The following tokens are significant:
|
||||
**
|
||||
** (0) tkSEMI A semicolon.
|
||||
** (1) tkWS Whitespace
|
||||
** (2) tkOTHER Any other SQL token.
|
||||
** (3) tkEXPLAIN The "explain" keyword.
|
||||
** (4) tkCREATE The "create" keyword.
|
||||
** (5) tkTEMP The "temp" or "temporary" keyword.
|
||||
** (6) tkTRIGGER The "trigger" keyword.
|
||||
** (7) tkEND The "end" keyword.
|
||||
**
|
||||
** Whitespace never causes a state transition and is always ignored.
|
||||
**
|
||||
** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed
|
||||
** to recognize the end of a trigger can be omitted. All we have to do
|
||||
** is look for a semicolon that is not part of an string or comment.
|
||||
*/
|
||||
int sqlite3_complete(const char *zSql){
|
||||
u8 state = 0; /* Current state, using numbers defined in header comment */
|
||||
u8 token; /* Value of the next token */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* A complex statement machine used to detect the end of a CREATE TRIGGER
|
||||
** statement. This is the normal case.
|
||||
*/
|
||||
static const u8 trans[7][8] = {
|
||||
/* Token: */
|
||||
/* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */
|
||||
/* 0 START: */ { 0, 0, 1, 2, 3, 1, 1, 1, },
|
||||
/* 1 NORMAL: */ { 0, 1, 1, 1, 1, 1, 1, 1, },
|
||||
/* 2 EXPLAIN: */ { 0, 2, 1, 1, 3, 1, 1, 1, },
|
||||
/* 3 CREATE: */ { 0, 3, 1, 1, 1, 3, 4, 1, },
|
||||
/* 4 TRIGGER: */ { 5, 4, 4, 4, 4, 4, 4, 4, },
|
||||
/* 5 SEMI: */ { 5, 5, 4, 4, 4, 4, 4, 6, },
|
||||
/* 6 END: */ { 0, 6, 4, 4, 4, 4, 4, 4, },
|
||||
};
|
||||
#else
|
||||
/* If triggers are not suppored by this compile then the statement machine
|
||||
** used to detect the end of a statement is much simplier
|
||||
*/
|
||||
static const u8 trans[2][3] = {
|
||||
/* Token: */
|
||||
/* State: ** SEMI WS OTHER */
|
||||
/* 0 START: */ { 0, 0, 1, },
|
||||
/* 1 NORMAL: */ { 0, 1, 1, },
|
||||
};
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
|
||||
while( *zSql ){
|
||||
switch( *zSql ){
|
||||
case ';': { /* A semicolon */
|
||||
token = tkSEMI;
|
||||
break;
|
||||
}
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\f': { /* White space is ignored */
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '/': { /* C-style comments */
|
||||
if( zSql[1]!='*' ){
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
zSql += 2;
|
||||
while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; }
|
||||
if( zSql[0]==0 ) return 0;
|
||||
zSql++;
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '-': { /* SQL-style comments from "--" to end of line */
|
||||
if( zSql[1]!='-' ){
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
while( *zSql && *zSql!='\n' ){ zSql++; }
|
||||
if( *zSql==0 ) return state==0;
|
||||
token = tkWS;
|
||||
break;
|
||||
}
|
||||
case '[': { /* Microsoft-style identifiers in [...] */
|
||||
zSql++;
|
||||
while( *zSql && *zSql!=']' ){ zSql++; }
|
||||
if( *zSql==0 ) return 0;
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
case '`': /* Grave-accent quoted symbols used by MySQL */
|
||||
case '"': /* single- and double-quoted strings */
|
||||
case '\'': {
|
||||
int c = *zSql;
|
||||
zSql++;
|
||||
while( *zSql && *zSql!=c ){ zSql++; }
|
||||
if( *zSql==0 ) return 0;
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
int c;
|
||||
if( IdChar((u8)*zSql) ){
|
||||
/* Keywords and unquoted identifiers */
|
||||
int nId;
|
||||
for(nId=1; IdChar(zSql[nId]); nId++){}
|
||||
#ifdef SQLITE_OMIT_TRIGGER
|
||||
token = tkOTHER;
|
||||
#else
|
||||
switch( *zSql ){
|
||||
case 'c': case 'C': {
|
||||
if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){
|
||||
token = tkCREATE;
|
||||
}else{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't': case 'T': {
|
||||
if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){
|
||||
token = tkTRIGGER;
|
||||
}else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){
|
||||
token = tkTEMP;
|
||||
}else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){
|
||||
token = tkTEMP;
|
||||
}else{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'e': case 'E': {
|
||||
if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){
|
||||
token = tkEND;
|
||||
}else
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){
|
||||
token = tkEXPLAIN;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
token = tkOTHER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
zSql += nId-1;
|
||||
}else{
|
||||
/* Operators and special symbols */
|
||||
token = tkOTHER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
state = trans[state][token];
|
||||
zSql++;
|
||||
}
|
||||
return state==0;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/*
|
||||
** This routine is the same as the sqlite3_complete() routine described
|
||||
** above, except that the parameter is required to be UTF-16 encoded, not
|
||||
** UTF-8.
|
||||
*/
|
||||
int sqlite3_complete16(const void *zSql){
|
||||
sqlite3_value *pVal;
|
||||
char const *zSql8;
|
||||
int rc = 0;
|
||||
|
||||
pVal = sqlite3ValueNew();
|
||||
sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
|
||||
zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8);
|
||||
if( zSql8 ){
|
||||
rc = sqlite3_complete(zSql8);
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
return sqlite3ApiExit(0, rc);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
#endif /* SQLITE_OMIT_COMPLETE */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,467 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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 C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.128 2007/02/07 01:06:53 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Look up every table that is named in pSrc. If any table is not found,
|
||||
** add an error message to pParse->zErrMsg and return NULL. If all tables
|
||||
** are found, return a pointer to the last table.
|
||||
*/
|
||||
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, pItem->zName, pItem->zDatabase);
|
||||
sqlite3DeleteTable(pParse->db, pItem->pTab);
|
||||
pItem->pTab = pTab;
|
||||
if( pTab ){
|
||||
pTab->nRef++;
|
||||
}
|
||||
}
|
||||
return pTab;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to make sure the given table is writable. If it is not
|
||||
** writable, generate an error message and return 1. If it is
|
||||
** writable return 0;
|
||||
*/
|
||||
int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
|
||||
if( (pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0
|
||||
&& pParse->nested==0)
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|| (pTab->pMod && pTab->pMod->pModule->xUpdate==0)
|
||||
#endif
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
|
||||
return 1;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
if( !viewOk && pTab->pSelect ){
|
||||
sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will open a table for reading.
|
||||
*/
|
||||
void sqlite3OpenTable(
|
||||
Parse *p, /* Generate code into this VDBE */
|
||||
int iCur, /* The cursor number of the table */
|
||||
int iDb, /* The database index in sqlite3.aDb[] */
|
||||
Table *pTab, /* The table to be opened */
|
||||
int opcode /* OP_OpenRead or OP_OpenWrite */
|
||||
){
|
||||
Vdbe *v;
|
||||
if( IsVirtual(pTab) ) return;
|
||||
v = sqlite3GetVdbe(p);
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName);
|
||||
sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
|
||||
VdbeComment((v, "# %s", pTab->zName));
|
||||
sqlite3VdbeAddOp(v, opcode, iCur, pTab->tnum);
|
||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code for a DELETE FROM statement.
|
||||
**
|
||||
** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL;
|
||||
** \________/ \________________/
|
||||
** pTabList pWhere
|
||||
*/
|
||||
void sqlite3DeleteFrom(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* The table from which we should delete things */
|
||||
Expr *pWhere /* The WHERE clause. May be null */
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
const char *zDb; /* Name of database holding pTab */
|
||||
int end, addr = 0; /* A couple addresses of generated code */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int iCur; /* VDBE Cursor number for pTab */
|
||||
sqlite3 *db; /* Main database structure */
|
||||
AuthContext sContext; /* Authorization context */
|
||||
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 */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
int triggers_exist = 0; /* True if any triggers exist */
|
||||
#endif
|
||||
|
||||
sContext.pParse = 0;
|
||||
if( pParse->nErr || sqlite3MallocFailed() ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
db = pParse->db;
|
||||
assert( pTabList->nSrc==1 );
|
||||
|
||||
/* Locate the table which we want to delete. This table has to be
|
||||
** put in an SrcList structure because some of the subroutines we
|
||||
** will be calling are designed to work with multiple tables and expect
|
||||
** an SrcList* parameter instead of just a Table* parameter.
|
||||
*/
|
||||
pTab = sqlite3SrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Figure out if we have any triggers and if the table being
|
||||
** deleted from is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
# define isView 0
|
||||
#endif
|
||||
|
||||
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
zDb = db->aDb[iDb].zName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
/* Allocate a cursor used to store the old.* data for a trigger.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Resolve the column names in the WHERE clause.
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
iCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = pTabList;
|
||||
if( sqlite3ExprResolveNames(&sNC, pWhere) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
|
||||
/* Start the view context
|
||||
*/
|
||||
if( isView ){
|
||||
sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
|
||||
}
|
||||
|
||||
/* Begin generating code.
|
||||
*/
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
||||
sqlite3BeginWriteOperation(pParse, triggers_exist, iDb);
|
||||
|
||||
/* If we are trying to delete from a view, realize that view into
|
||||
** a ephemeral table.
|
||||
*/
|
||||
if( isView ){
|
||||
Select *pView = sqlite3SelectDup(pTab->pSelect);
|
||||
sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0);
|
||||
sqlite3SelectDelete(pView);
|
||||
}
|
||||
|
||||
/* Initialize the counter of the number of rows deleted, if
|
||||
** we are counting rows.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
memCnt = pParse->nMem++;
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
|
||||
}
|
||||
|
||||
/* 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 endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
int addr2;
|
||||
if( !isView ){
|
||||
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
|
||||
addr2 = sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
|
||||
sqlite3VdbeAddOp(v, OP_Next, iCur, addr2);
|
||||
sqlite3VdbeResolveLabel(v, endOfLoop);
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
if( !isView ){
|
||||
sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, iDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table and pick which records to delete.
|
||||
*/
|
||||
else{
|
||||
/* Begin the database scan
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Remember the rowid of every item to be deleted.
|
||||
*/
|
||||
sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
|
||||
}
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order.
|
||||
*/
|
||||
end = sqlite3VdbeMakeLabel(v);
|
||||
|
||||
/* This is the beginning of the delete loop when there are
|
||||
** row triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
|
||||
if( !isView ){
|
||||
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
|
||||
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0);
|
||||
if( !isView ){
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
|
||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
|
||||
-1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||
addr);
|
||||
}
|
||||
|
||||
if( !isView ){
|
||||
/* Open cursors for the table we are deleting from and all its
|
||||
** indices. If there are row triggers, this happens inside the
|
||||
** OP_FifoRead loop because the cursor have to all be closed
|
||||
** before the trigger fires. If there are no row triggers, the
|
||||
** cursors are opened only once on the outside the loop.
|
||||
*/
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
|
||||
|
||||
/* This is the beginning of the delete loop when there are no
|
||||
** row triggers */
|
||||
if( !triggers_exist ){
|
||||
addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
|
||||
}
|
||||
|
||||
/* Delete the row */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
pParse->pVirtualLock = pTab;
|
||||
sqlite3VdbeOp3(v, OP_VUpdate, 0, 1, (const char*)pTab->pVtab, P3_VTAB);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are row triggers, close all cursors then invoke
|
||||
** the AFTER triggers
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( !isView ){
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1,
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||
addr);
|
||||
}
|
||||
|
||||
/* End of the delete loop */
|
||||
sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqlite3VdbeResolveLabel(v, end);
|
||||
|
||||
/* Close the cursors after the loop if there are no row triggers */
|
||||
if( !triggers_exist && !IsVirtual(pTab) ){
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of rows that were deleted. If this routine is
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P3_STATIC);
|
||||
}
|
||||
|
||||
delete_from_cleanup:
|
||||
sqlite3AuthContextPop(&sContext);
|
||||
sqlite3SrcListDelete(pTabList);
|
||||
sqlite3ExprDelete(pWhere);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes a single row of a
|
||||
** single table to be deleted.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "base".
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number base+i for the i-th index.
|
||||
**
|
||||
** 3. The record number of the row to be deleted must be on the top
|
||||
** of the stack.
|
||||
**
|
||||
** This routine pops the top of the stack to remove the record number
|
||||
** and then generates code to remove both the table record and all index
|
||||
** entries that point to that record.
|
||||
*/
|
||||
void sqlite3GenerateRowDelete(
|
||||
sqlite3 *db, /* The database containing the index */
|
||||
Vdbe *v, /* Generate code into this VDBE */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int count /* Increment the row change counter */
|
||||
){
|
||||
int addr;
|
||||
addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0);
|
||||
sqlite3GenerateRowIndexDelete(v, pTab, iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( count ){
|
||||
sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes the deletion of all
|
||||
** index entries associated with a single row of a single table.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "iCur".
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number iCur+i for the i-th index.
|
||||
**
|
||||
** 3. The "iCur" cursor must be pointing to the row that is to be
|
||||
** deleted.
|
||||
*/
|
||||
void sqlite3GenerateRowIndexDelete(
|
||||
Vdbe *v, /* Generate code into this VDBE */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
|
||||
){
|
||||
int i;
|
||||
Index *pIdx;
|
||||
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
|
||||
sqlite3GenerateIndexKey(v, pIdx, iCur);
|
||||
sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will assemble an index key and put it on the top
|
||||
** of the tack. The key with be for index pIdx which is an index on pTab.
|
||||
** iCur is the index of a cursor open on the pTab table and pointing to
|
||||
** the entry that needs indexing.
|
||||
*/
|
||||
void sqlite3GenerateIndexKey(
|
||||
Vdbe *v, /* Generate code into this VDBE */
|
||||
Index *pIdx, /* The index for which to generate a key */
|
||||
int iCur /* Cursor number for the pIdx->pTable table */
|
||||
){
|
||||
int j;
|
||||
Table *pTab = pIdx->pTable;
|
||||
|
||||
sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
int idx = pIdx->aiColumn[j];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp(v, OP_Dup, j, 0);
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_Column, iCur, idx);
|
||||
sqlite3ColumnDefault(v, pTab, idx);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0);
|
||||
sqlite3IndexAffinityStr(v, pIdx);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,394 +0,0 @@
|
|||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the implementation of generic hash-tables
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.c,v 1.18 2006/02/14 10:48:39 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** 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.
|
||||
*/
|
||||
void sqlite3HashInit(Hash *pNew, int keyClass, 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->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
pNew->ht = 0;
|
||||
pNew->xMalloc = sqlite3MallocX;
|
||||
pNew->xFree = sqlite3FreeX;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
void sqlite3HashClear(Hash *pH){
|
||||
HashElem *elem; /* For looping over all elements of the table */
|
||||
|
||||
assert( pH!=0 );
|
||||
elem = pH->first;
|
||||
pH->first = 0;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = 0;
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
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);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
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
|
||||
*/
|
||||
static void insertElement(
|
||||
Hash *pH, /* The complete hash table */
|
||||
struct _ht *pEntry, /* The entry into which pNew is inserted */
|
||||
HashElem *pNew /* The element to be inserted */
|
||||
){
|
||||
HashElem *pHead; /* First element already in pEntry */
|
||||
pHead = pEntry->chain;
|
||||
if( pHead ){
|
||||
pNew->next = pHead;
|
||||
pNew->prev = pHead->prev;
|
||||
if( pHead->prev ){ pHead->prev->next = pNew; }
|
||||
else { pH->first = pNew; }
|
||||
pHead->prev = pNew;
|
||||
}else{
|
||||
pNew->next = pH->first;
|
||||
if( pH->first ){ pH->first->prev = pNew; }
|
||||
pNew->prev = 0;
|
||||
pH->first = pNew;
|
||||
}
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqliteMalloc() fails.
|
||||
*/
|
||||
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 */
|
||||
|
||||
assert( (new_size & (new_size-1))==0 );
|
||||
new_ht = (struct _ht *)pH->xMalloc( new_size*sizeof(struct _ht) );
|
||||
if( new_ht==0 ) return;
|
||||
if( pH->ht ) pH->xFree(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);
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static HashElem *findElementGivenHash(
|
||||
const Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
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 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
Hash *pH, /* The pH containing "elem" */
|
||||
HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
struct _ht *pEntry;
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
pH->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
if( pEntry->count<=0 ){
|
||||
pEntry->chain = 0;
|
||||
}
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree( elem );
|
||||
pH->count--;
|
||||
if( pH->count<=0 ){
|
||||
assert( pH->first==0 );
|
||||
assert( pH->count==0 );
|
||||
sqlite3HashClear(pH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *sqlite3HashFind(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);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
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);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (HashElem*)pH->xMalloc( sizeof(HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = pH->xMalloc( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
}
|
||||
assert( pH->htsize>0 );
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the header file for the generic hash-table implemenation
|
||||
** used in SQLite.
|
||||
**
|
||||
** $Id: hash.h,v 1.9 2006/02/14 10:48:39 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_HASH_H_
|
||||
#define _SQLITE_HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct Hash Hash;
|
||||
typedef struct HashElem HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** 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 */
|
||||
HashElem *first; /* The first element of the array */
|
||||
void *(*xMalloc)(int); /* malloc() function to use */
|
||||
void (*xFree)(void *); /* free() function to use */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct HashElem {
|
||||
HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
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 *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqlite3HashFind(const Hash*, const void *pKey, int nKey);
|
||||
void sqlite3HashClear(Hash*);
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** Hash h;
|
||||
** HashElem *p;
|
||||
** ...
|
||||
** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){
|
||||
** SomeStructure *pData = sqliteHashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
#define sqliteHashFirst(H) ((H)->first)
|
||||
#define sqliteHashNext(E) ((E)->next)
|
||||
#define sqliteHashData(E) ((E)->data)
|
||||
#define sqliteHashKey(E) ((E)->pKey)
|
||||
#define sqliteHashKeysize(E) ((E)->nKey)
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define sqliteHashCount(H) ((H)->count)
|
||||
|
||||
#endif /* _SQLITE_HASH_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Main file for the SQLite library. The routines in this file
|
||||
** implement the programmer interface to the library. Routines in
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: legacy.c,v 1.16 2006/09/15 07:28:50 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Execute SQL code. Return one of the SQLITE_ success/failure
|
||||
** codes. Also write an error message into memory obtained from
|
||||
** malloc() and make *pzErrMsg point to that message.
|
||||
**
|
||||
** If the SQL is a query, then for each row in the query result
|
||||
** the xCallback() function is called. pArg becomes the first
|
||||
** argument to xCallback(). If xCallback=NULL then no callback
|
||||
** is invoked, even for queries.
|
||||
*/
|
||||
int sqlite3_exec(
|
||||
sqlite3 *db, /* The database on which the SQL executes */
|
||||
const char *zSql, /* The SQL to be executed */
|
||||
sqlite3_callback xCallback, /* Invoke this callback routine */
|
||||
void *pArg, /* First argument to xCallback() */
|
||||
char **pzErrMsg /* Write error messages here */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
const char *zLeftover;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char **azCols = 0;
|
||||
|
||||
int nRetry = 0;
|
||||
int nChange = 0;
|
||||
int nCallback;
|
||||
|
||||
if( zSql==0 ) return SQLITE_OK;
|
||||
while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){
|
||||
int nCol;
|
||||
char **azVals = 0;
|
||||
|
||||
pStmt = 0;
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover);
|
||||
assert( rc==SQLITE_OK || pStmt==0 );
|
||||
if( rc!=SQLITE_OK ){
|
||||
continue;
|
||||
}
|
||||
if( !pStmt ){
|
||||
/* this happens for a comment or white-space */
|
||||
zSql = zLeftover;
|
||||
continue;
|
||||
}
|
||||
|
||||
db->nChange += nChange;
|
||||
nCallback = 0;
|
||||
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
azCols = sqliteMalloc(2*nCol*sizeof(const char *) + 1);
|
||||
if( azCols==0 ){
|
||||
goto exec_out;
|
||||
}
|
||||
|
||||
while( 1 ){
|
||||
int i;
|
||||
rc = sqlite3_step(pStmt);
|
||||
|
||||
/* Invoke the callback function if required */
|
||||
if( xCallback && (SQLITE_ROW==rc ||
|
||||
(SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){
|
||||
if( 0==nCallback ){
|
||||
for(i=0; i<nCol; i++){
|
||||
azCols[i] = (char *)sqlite3_column_name(pStmt, i);
|
||||
}
|
||||
nCallback++;
|
||||
}
|
||||
if( rc==SQLITE_ROW ){
|
||||
azVals = &azCols[nCol];
|
||||
for(i=0; i<nCol; i++){
|
||||
azVals[i] = (char *)sqlite3_column_text(pStmt, i);
|
||||
}
|
||||
}
|
||||
if( xCallback(pArg, nCol, azVals, azCols) ){
|
||||
rc = SQLITE_ABORT;
|
||||
goto exec_out;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
pStmt = 0;
|
||||
if( db->pVdbe==0 ){
|
||||
nChange = db->nChange;
|
||||
}
|
||||
if( rc!=SQLITE_SCHEMA ){
|
||||
nRetry = 0;
|
||||
zSql = zLeftover;
|
||||
while( isspace((unsigned char)zSql[0]) ) zSql++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sqliteFree(azCols);
|
||||
azCols = 0;
|
||||
}
|
||||
|
||||
exec_out:
|
||||
if( pStmt ) sqlite3_finalize(pStmt);
|
||||
if( azCols ) sqliteFree(azCols);
|
||||
|
||||
rc = sqlite3ApiExit(0, rc);
|
||||
if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_malloc(1+strlen(sqlite3_errmsg(db)));
|
||||
if( *pzErrMsg ){
|
||||
strcpy(*pzErrMsg, sqlite3_errmsg(db));
|
||||
}
|
||||
}else if( pzErrMsg ){
|
||||
*pzErrMsg = 0;
|
||||
}
|
||||
|
||||
assert( (rc&db->errMask)==rc );
|
||||
return rc;
|
||||
}
|
|
@ -1,422 +0,0 @@
|
|||
/*
|
||||
** 2006 June 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 used to dynamically load extensions into
|
||||
** the SQLite library.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
#define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
|
||||
#include "sqlite3ext.h"
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Some API routines are omitted when various features are
|
||||
** excluded from a build of SQLite. Substitute a NULL pointer
|
||||
** for any missing APIs.
|
||||
*/
|
||||
#ifndef SQLITE_ENABLE_COLUMN_METADATA
|
||||
# define sqlite3_column_database_name 0
|
||||
# define sqlite3_column_database_name16 0
|
||||
# define sqlite3_column_table_name 0
|
||||
# define sqlite3_column_table_name16 0
|
||||
# define sqlite3_column_origin_name 0
|
||||
# define sqlite3_column_origin_name16 0
|
||||
# define sqlite3_table_column_metadata 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_AUTHORIZATION
|
||||
# define sqlite3_set_authorizer 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_UTF16
|
||||
# define sqlite3_bind_text16 0
|
||||
# define sqlite3_collation_needed16 0
|
||||
# define sqlite3_column_decltype16 0
|
||||
# define sqlite3_column_name16 0
|
||||
# define sqlite3_column_text16 0
|
||||
# define sqlite3_complete16 0
|
||||
# define sqlite3_create_collation16 0
|
||||
# define sqlite3_create_function16 0
|
||||
# define sqlite3_errmsg16 0
|
||||
# define sqlite3_open16 0
|
||||
# define sqlite3_prepare16 0
|
||||
# define sqlite3_result_error16 0
|
||||
# define sqlite3_result_text16 0
|
||||
# define sqlite3_result_text16be 0
|
||||
# define sqlite3_result_text16le 0
|
||||
# define sqlite3_value_text16 0
|
||||
# define sqlite3_value_text16be 0
|
||||
# define sqlite3_value_text16le 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_COMPLETE
|
||||
# define sqlite3_complete 0
|
||||
# define sqlite3_complete16 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
# define sqlite3_progress_handler 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_VIRTUALTABLE
|
||||
# define sqlite3_create_module 0
|
||||
# define sqlite3_declare_vtab 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_SHARED_CACHE
|
||||
# define sqlite3_enable_shared_cache 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
# define sqlite3_profile 0
|
||||
# define sqlite3_trace 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_GET_TABLE
|
||||
# define sqlite3_free_table 0
|
||||
# define sqlite3_get_table 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following structure contains pointers to all SQLite API routines.
|
||||
** A pointer to this structure is passed into extensions when they are
|
||||
** loaded so that the extension can make calls back into the SQLite
|
||||
** library.
|
||||
**
|
||||
** When adding new APIs, add them to the bottom of this structure
|
||||
** in order to preserve backwards compatibility.
|
||||
**
|
||||
** Extensions that use newer APIs should first call the
|
||||
** sqlite3_libversion_number() to make sure that the API they
|
||||
** intend to use is supported by the library. Extensions should
|
||||
** also check to make sure that the pointer to the function is
|
||||
** not NULL before calling it.
|
||||
*/
|
||||
const sqlite3_api_routines sqlite3_apis = {
|
||||
sqlite3_aggregate_context,
|
||||
sqlite3_aggregate_count,
|
||||
sqlite3_bind_blob,
|
||||
sqlite3_bind_double,
|
||||
sqlite3_bind_int,
|
||||
sqlite3_bind_int64,
|
||||
sqlite3_bind_null,
|
||||
sqlite3_bind_parameter_count,
|
||||
sqlite3_bind_parameter_index,
|
||||
sqlite3_bind_parameter_name,
|
||||
sqlite3_bind_text,
|
||||
sqlite3_bind_text16,
|
||||
sqlite3_bind_value,
|
||||
sqlite3_busy_handler,
|
||||
sqlite3_busy_timeout,
|
||||
sqlite3_changes,
|
||||
sqlite3_close,
|
||||
sqlite3_collation_needed,
|
||||
sqlite3_collation_needed16,
|
||||
sqlite3_column_blob,
|
||||
sqlite3_column_bytes,
|
||||
sqlite3_column_bytes16,
|
||||
sqlite3_column_count,
|
||||
sqlite3_column_database_name,
|
||||
sqlite3_column_database_name16,
|
||||
sqlite3_column_decltype,
|
||||
sqlite3_column_decltype16,
|
||||
sqlite3_column_double,
|
||||
sqlite3_column_int,
|
||||
sqlite3_column_int64,
|
||||
sqlite3_column_name,
|
||||
sqlite3_column_name16,
|
||||
sqlite3_column_origin_name,
|
||||
sqlite3_column_origin_name16,
|
||||
sqlite3_column_table_name,
|
||||
sqlite3_column_table_name16,
|
||||
sqlite3_column_text,
|
||||
sqlite3_column_text16,
|
||||
sqlite3_column_type,
|
||||
sqlite3_column_value,
|
||||
sqlite3_commit_hook,
|
||||
sqlite3_complete,
|
||||
sqlite3_complete16,
|
||||
sqlite3_create_collation,
|
||||
sqlite3_create_collation16,
|
||||
sqlite3_create_function,
|
||||
sqlite3_create_function16,
|
||||
sqlite3_create_module,
|
||||
sqlite3_data_count,
|
||||
sqlite3_db_handle,
|
||||
sqlite3_declare_vtab,
|
||||
sqlite3_enable_shared_cache,
|
||||
sqlite3_errcode,
|
||||
sqlite3_errmsg,
|
||||
sqlite3_errmsg16,
|
||||
sqlite3_exec,
|
||||
sqlite3_expired,
|
||||
sqlite3_finalize,
|
||||
sqlite3_free,
|
||||
sqlite3_free_table,
|
||||
sqlite3_get_autocommit,
|
||||
sqlite3_get_auxdata,
|
||||
sqlite3_get_table,
|
||||
0, /* Was sqlite3_global_recover(), but that function is deprecated */
|
||||
sqlite3_interrupt,
|
||||
sqlite3_last_insert_rowid,
|
||||
sqlite3_libversion,
|
||||
sqlite3_libversion_number,
|
||||
sqlite3_malloc,
|
||||
sqlite3_mprintf,
|
||||
sqlite3_open,
|
||||
sqlite3_open16,
|
||||
sqlite3_prepare,
|
||||
sqlite3_prepare16,
|
||||
sqlite3_profile,
|
||||
sqlite3_progress_handler,
|
||||
sqlite3_realloc,
|
||||
sqlite3_reset,
|
||||
sqlite3_result_blob,
|
||||
sqlite3_result_double,
|
||||
sqlite3_result_error,
|
||||
sqlite3_result_error16,
|
||||
sqlite3_result_int,
|
||||
sqlite3_result_int64,
|
||||
sqlite3_result_null,
|
||||
sqlite3_result_text,
|
||||
sqlite3_result_text16,
|
||||
sqlite3_result_text16be,
|
||||
sqlite3_result_text16le,
|
||||
sqlite3_result_value,
|
||||
sqlite3_rollback_hook,
|
||||
sqlite3_set_authorizer,
|
||||
sqlite3_set_auxdata,
|
||||
sqlite3_snprintf,
|
||||
sqlite3_step,
|
||||
sqlite3_table_column_metadata,
|
||||
sqlite3_thread_cleanup,
|
||||
sqlite3_total_changes,
|
||||
sqlite3_trace,
|
||||
sqlite3_transfer_bindings,
|
||||
sqlite3_update_hook,
|
||||
sqlite3_user_data,
|
||||
sqlite3_value_blob,
|
||||
sqlite3_value_bytes,
|
||||
sqlite3_value_bytes16,
|
||||
sqlite3_value_double,
|
||||
sqlite3_value_int,
|
||||
sqlite3_value_int64,
|
||||
sqlite3_value_numeric_type,
|
||||
sqlite3_value_text,
|
||||
sqlite3_value_text16,
|
||||
sqlite3_value_text16be,
|
||||
sqlite3_value_text16le,
|
||||
sqlite3_value_type,
|
||||
sqlite3_vmprintf,
|
||||
/*
|
||||
** The original API set ends here. All extensions can call any
|
||||
** of the APIs above provided that the pointer is not NULL. But
|
||||
** before calling APIs that follow, extension should check the
|
||||
** sqlite3_libversion_number() to make sure they are dealing with
|
||||
** a library that is new enough to support that API.
|
||||
*************************************************************************
|
||||
*/
|
||||
sqlite3_overload_function,
|
||||
};
|
||||
|
||||
/*
|
||||
** Attempt to load an SQLite extension library contained in the file
|
||||
** zFile. The entry point is zProc. zProc may be 0 in which case a
|
||||
** default entry point name (sqlite3_extension_init) is used. Use
|
||||
** of the default name is recommended.
|
||||
**
|
||||
** Return SQLITE_OK on success and SQLITE_ERROR if something goes wrong.
|
||||
**
|
||||
** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with
|
||||
** error message text. The calling function should free this memory
|
||||
** by calling sqlite3_free().
|
||||
*/
|
||||
int sqlite3_load_extension(
|
||||
sqlite3 *db, /* Load the extension into this database connection */
|
||||
const char *zFile, /* Name of the shared library containing extension */
|
||||
const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */
|
||||
char **pzErrMsg /* Put error message here if not 0 */
|
||||
){
|
||||
void *handle;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
char *zErrmsg = 0;
|
||||
void **aHandle;
|
||||
|
||||
/* Ticket #1863. To avoid a creating security problems for older
|
||||
** applications that relink against newer versions of SQLite, the
|
||||
** ability to run load_extension is turned off by default. One
|
||||
** must call sqlite3_enable_load_extension() to turn on extension
|
||||
** loading. Otherwise you get the following error.
|
||||
*/
|
||||
if( (db->flags & SQLITE_LoadExtension)==0 ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("not authorized");
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( zProc==0 ){
|
||||
zProc = "sqlite3_extension_init";
|
||||
}
|
||||
|
||||
handle = sqlite3OsDlopen(zFile);
|
||||
if( handle==0 ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("unable to open shared library [%s]", zFile);
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
sqlite3OsDlsym(handle, zProc);
|
||||
if( xInit==0 ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("no entry point [%s] in shared library [%s]",
|
||||
zProc, zFile);
|
||||
}
|
||||
sqlite3OsDlclose(handle);
|
||||
return SQLITE_ERROR;
|
||||
}else if( xInit(db, &zErrmsg, &sqlite3_apis) ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
|
||||
}
|
||||
sqlite3_free(zErrmsg);
|
||||
sqlite3OsDlclose(handle);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Append the new shared library handle to the db->aExtension array. */
|
||||
db->nExtension++;
|
||||
aHandle = sqliteMalloc(sizeof(handle)*db->nExtension);
|
||||
if( aHandle==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( db->nExtension>0 ){
|
||||
memcpy(aHandle, db->aExtension, sizeof(handle)*(db->nExtension-1));
|
||||
}
|
||||
sqliteFree(db->aExtension);
|
||||
db->aExtension = aHandle;
|
||||
|
||||
db->aExtension[db->nExtension-1] = handle;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Call this routine when the database connection is closing in order
|
||||
** to clean up loaded extensions
|
||||
*/
|
||||
void sqlite3CloseExtensions(sqlite3 *db){
|
||||
int i;
|
||||
for(i=0; i<db->nExtension; i++){
|
||||
sqlite3OsDlclose(db->aExtension[i]);
|
||||
}
|
||||
sqliteFree(db->aExtension);
|
||||
}
|
||||
|
||||
/*
|
||||
** Enable or disable extension loading. Extension loading is disabled by
|
||||
** default so as not to open security holes in older applications.
|
||||
*/
|
||||
int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
|
||||
if( onoff ){
|
||||
db->flags |= SQLITE_LoadExtension;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_LoadExtension;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A list of automatically loaded extensions.
|
||||
**
|
||||
** This list is shared across threads, so be sure to hold the
|
||||
** mutex while accessing or changing it.
|
||||
*/
|
||||
static int nAutoExtension = 0;
|
||||
static void **aAutoExtension = 0;
|
||||
|
||||
|
||||
/*
|
||||
** Register a statically linked extension that is automatically
|
||||
** loaded by every new database connection.
|
||||
*/
|
||||
int sqlite3_auto_extension(void *xInit){
|
||||
int i;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3OsEnterMutex();
|
||||
for(i=0; i<nAutoExtension; i++){
|
||||
if( aAutoExtension[i]==xInit ) break;
|
||||
}
|
||||
if( i==nAutoExtension ){
|
||||
nAutoExtension++;
|
||||
aAutoExtension = sqlite3Realloc( aAutoExtension,
|
||||
nAutoExtension*sizeof(aAutoExtension[0]) );
|
||||
if( aAutoExtension==0 ){
|
||||
nAutoExtension = 0;
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
aAutoExtension[nAutoExtension-1] = xInit;
|
||||
}
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
assert( (rc&0xff)==rc );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset the automatic extension loading mechanism.
|
||||
*/
|
||||
void sqlite3_reset_auto_extension(void){
|
||||
sqlite3OsEnterMutex();
|
||||
sqliteFree(aAutoExtension);
|
||||
aAutoExtension = 0;
|
||||
nAutoExtension = 0;
|
||||
sqlite3OsLeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** Load all automatic extensions.
|
||||
*/
|
||||
int sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
int i;
|
||||
int go = 1;
|
||||
int rc = SQLITE_OK;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
|
||||
if( nAutoExtension==0 ){
|
||||
/* Common case: early out without every having to acquire a mutex */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
for(i=0; go; i++){
|
||||
char *zErrmsg = 0;
|
||||
sqlite3OsEnterMutex();
|
||||
if( i>=nAutoExtension ){
|
||||
xInit = 0;
|
||||
go = 0;
|
||||
}else{
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
aAutoExtension[i];
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
if( xInit && xInit(db, &zErrmsg, &sqlite3_apis) ){
|
||||
sqlite3Error(db, SQLITE_ERROR,
|
||||
"automatic extension loading failed: %s", zErrmsg);
|
||||
go = 0;
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
** 2005 November 29
|
||||
**
|
||||
** 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 OS interface code that is common to all
|
||||
** architectures.
|
||||
*/
|
||||
#define _SQLITE_OS_C_ 1
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
|
||||
/*
|
||||
** The following routines are convenience wrappers around methods
|
||||
** of the OsFile object. This is mostly just syntactic sugar. All
|
||||
** of this would be completely automatic if SQLite were coded using
|
||||
** C++ instead of plain old C.
|
||||
*/
|
||||
int sqlite3OsClose(OsFile **pId){
|
||||
OsFile *id;
|
||||
if( pId!=0 && (id = *pId)!=0 ){
|
||||
return id->pMethod->xClose(pId);
|
||||
}else{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
int sqlite3OsOpenDirectory(OsFile *id, const char *zName){
|
||||
return id->pMethod->xOpenDirectory(id, zName);
|
||||
}
|
||||
int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
|
||||
return id->pMethod->xRead(id, pBuf, amt);
|
||||
}
|
||||
int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
|
||||
return id->pMethod->xWrite(id, pBuf, amt);
|
||||
}
|
||||
int sqlite3OsSeek(OsFile *id, i64 offset){
|
||||
return id->pMethod->xSeek(id, offset);
|
||||
}
|
||||
int sqlite3OsTruncate(OsFile *id, i64 size){
|
||||
return id->pMethod->xTruncate(id, size);
|
||||
}
|
||||
int sqlite3OsSync(OsFile *id, int fullsync){
|
||||
return id->pMethod->xSync(id, fullsync);
|
||||
}
|
||||
void sqlite3OsSetFullSync(OsFile *id, int value){
|
||||
id->pMethod->xSetFullSync(id, value);
|
||||
}
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
||||
/* This method is currently only used while interactively debugging the
|
||||
** pager. More specificly, it can only be used when sqlite3DebugPrintf() is
|
||||
** included in the build. */
|
||||
int sqlite3OsFileHandle(OsFile *id){
|
||||
return id->pMethod->xFileHandle(id);
|
||||
}
|
||||
#endif
|
||||
int sqlite3OsFileSize(OsFile *id, i64 *pSize){
|
||||
return id->pMethod->xFileSize(id, pSize);
|
||||
}
|
||||
int sqlite3OsLock(OsFile *id, int lockType){
|
||||
return id->pMethod->xLock(id, lockType);
|
||||
}
|
||||
int sqlite3OsUnlock(OsFile *id, int lockType){
|
||||
return id->pMethod->xUnlock(id, lockType);
|
||||
}
|
||||
int sqlite3OsLockState(OsFile *id){
|
||||
return id->pMethod->xLockState(id);
|
||||
}
|
||||
int sqlite3OsCheckReservedLock(OsFile *id){
|
||||
return id->pMethod->xCheckReservedLock(id);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_REDEF_IO
|
||||
/*
|
||||
** A function to return a pointer to the virtual function table.
|
||||
** This routine really does not accomplish very much since the
|
||||
** virtual function table is a global variable and anybody who
|
||||
** can call this function can just as easily access the variable
|
||||
** for themselves. Nevertheless, we include this routine for
|
||||
** backwards compatibility with an earlier redefinable I/O
|
||||
** interface design.
|
||||
*/
|
||||
struct sqlite3OsVtbl *sqlite3_os_switch(void){
|
||||
return &sqlite3Os;
|
||||
}
|
||||
#endif
|
|
@ -1,518 +0,0 @@
|
|||
/*
|
||||
** 2001 September 16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This header file (together with is companion C source-code file
|
||||
** "os.c") attempt to abstract the underlying operating system so that
|
||||
** the SQLite library will work on both POSIX and windows systems.
|
||||
*/
|
||||
#ifndef _SQLITE_OS_H_
|
||||
#define _SQLITE_OS_H_
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other
|
||||
** operating system.
|
||||
*/
|
||||
#if !defined(OS_UNIX) && !defined(OS_OTHER)
|
||||
# define OS_OTHER 0
|
||||
# ifndef OS_WIN
|
||||
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
# define OS_WIN 1
|
||||
# define OS_UNIX 0
|
||||
# define OS_OS2 0
|
||||
# elif defined(_EMX_) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
|
||||
# define OS_WIN 0
|
||||
# define OS_UNIX 0
|
||||
# define OS_OS2 1
|
||||
# else
|
||||
# define OS_WIN 0
|
||||
# define OS_UNIX 1
|
||||
# define OS_OS2 0
|
||||
# endif
|
||||
# else
|
||||
# define OS_UNIX 0
|
||||
# define OS_OS2 0
|
||||
# endif
|
||||
#else
|
||||
# ifndef OS_WIN
|
||||
# define OS_WIN 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Define the maximum size of a temporary filename
|
||||
*/
|
||||
#if OS_WIN
|
||||
# include <windows.h>
|
||||
# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
|
||||
#elif OS_OS2
|
||||
# define INCL_DOSDATETIME
|
||||
# define INCL_DOSFILEMGR
|
||||
# define INCL_DOSERRORS
|
||||
# define INCL_DOSMISC
|
||||
# define INCL_DOSPROCESS
|
||||
# include <os2.h>
|
||||
# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
|
||||
#else
|
||||
# define SQLITE_TEMPNAME_SIZE 200
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* If the SET_FULLSYNC macro is not defined above, then make it
|
||||
** a no-op
|
||||
*/
|
||||
#ifndef SET_FULLSYNC
|
||||
# define SET_FULLSYNC(x,y)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Temporary files are named starting with this prefix followed by 16 random
|
||||
** alphanumeric characters, and no file extension. They are stored in the
|
||||
** OS's standard temporary file directory, and are deleted prior to exit.
|
||||
** If sqlite is being embedded in another program, you may wish to change the
|
||||
** prefix to reflect your program's name, so that if your program exits
|
||||
** prematurely, old temporary files can be easily identified. This can be done
|
||||
** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line.
|
||||
**
|
||||
** 2006-10-31: The default prefix used to be "sqlite_". But then
|
||||
** Mcafee started using SQLite in their anti-virus product and it
|
||||
** started putting files with the "sqlite" name in the c:/temp folder.
|
||||
** This annoyed many windows users. Those users would then do a
|
||||
** Google search for "sqlite", find the telephone numbers of the
|
||||
** developers and call to wake them up at night and complain.
|
||||
** For this reason, the default name prefix is changed to be "sqlite"
|
||||
** spelled backwards. So the temp files are still identified, but
|
||||
** anybody smart enough to figure out the code is also likely smart
|
||||
** enough to know that calling the developer will not help get rid
|
||||
** of the file.
|
||||
*/
|
||||
#ifndef TEMP_FILE_PREFIX
|
||||
# define TEMP_FILE_PREFIX "etilqs_"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Define the interfaces for Unix, Windows, and OS/2.
|
||||
*/
|
||||
#if OS_UNIX
|
||||
#define sqlite3OsOpenReadWrite sqlite3UnixOpenReadWrite
|
||||
#define sqlite3OsOpenExclusive sqlite3UnixOpenExclusive
|
||||
#define sqlite3OsOpenReadOnly sqlite3UnixOpenReadOnly
|
||||
#define sqlite3OsDelete sqlite3UnixDelete
|
||||
#define sqlite3OsFileExists sqlite3UnixFileExists
|
||||
#define sqlite3OsFullPathname sqlite3UnixFullPathname
|
||||
#define sqlite3OsIsDirWritable sqlite3UnixIsDirWritable
|
||||
#define sqlite3OsSyncDirectory sqlite3UnixSyncDirectory
|
||||
#define sqlite3OsTempFileName sqlite3UnixTempFileName
|
||||
#define sqlite3OsRandomSeed sqlite3UnixRandomSeed
|
||||
#define sqlite3OsSleep sqlite3UnixSleep
|
||||
#define sqlite3OsCurrentTime sqlite3UnixCurrentTime
|
||||
#define sqlite3OsEnterMutex sqlite3UnixEnterMutex
|
||||
#define sqlite3OsLeaveMutex sqlite3UnixLeaveMutex
|
||||
#define sqlite3OsInMutex sqlite3UnixInMutex
|
||||
#define sqlite3OsThreadSpecificData sqlite3UnixThreadSpecificData
|
||||
#define sqlite3OsMalloc sqlite3GenericMalloc
|
||||
#define sqlite3OsRealloc sqlite3GenericRealloc
|
||||
#define sqlite3OsFree sqlite3GenericFree
|
||||
#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
|
||||
#define sqlite3OsDlopen sqlite3UnixDlopen
|
||||
#define sqlite3OsDlsym sqlite3UnixDlsym
|
||||
#define sqlite3OsDlclose sqlite3UnixDlclose
|
||||
#endif
|
||||
#if OS_WIN
|
||||
#define sqlite3OsOpenReadWrite sqlite3WinOpenReadWrite
|
||||
#define sqlite3OsOpenExclusive sqlite3WinOpenExclusive
|
||||
#define sqlite3OsOpenReadOnly sqlite3WinOpenReadOnly
|
||||
#define sqlite3OsDelete sqlite3WinDelete
|
||||
#define sqlite3OsFileExists sqlite3WinFileExists
|
||||
#define sqlite3OsFullPathname sqlite3WinFullPathname
|
||||
#define sqlite3OsIsDirWritable sqlite3WinIsDirWritable
|
||||
#define sqlite3OsSyncDirectory sqlite3WinSyncDirectory
|
||||
#define sqlite3OsTempFileName sqlite3WinTempFileName
|
||||
#define sqlite3OsRandomSeed sqlite3WinRandomSeed
|
||||
#define sqlite3OsSleep sqlite3WinSleep
|
||||
#define sqlite3OsCurrentTime sqlite3WinCurrentTime
|
||||
#define sqlite3OsEnterMutex sqlite3WinEnterMutex
|
||||
#define sqlite3OsLeaveMutex sqlite3WinLeaveMutex
|
||||
#define sqlite3OsInMutex sqlite3WinInMutex
|
||||
#define sqlite3OsThreadSpecificData sqlite3WinThreadSpecificData
|
||||
#define sqlite3OsMalloc sqlite3GenericMalloc
|
||||
#define sqlite3OsRealloc sqlite3GenericRealloc
|
||||
#define sqlite3OsFree sqlite3GenericFree
|
||||
#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
|
||||
#define sqlite3OsDlopen sqlite3WinDlopen
|
||||
#define sqlite3OsDlsym sqlite3WinDlsym
|
||||
#define sqlite3OsDlclose sqlite3WinDlclose
|
||||
#endif
|
||||
#if OS_OS2
|
||||
#define sqlite3OsOpenReadWrite sqlite3Os2OpenReadWrite
|
||||
#define sqlite3OsOpenExclusive sqlite3Os2OpenExclusive
|
||||
#define sqlite3OsOpenReadOnly sqlite3Os2OpenReadOnly
|
||||
#define sqlite3OsDelete sqlite3Os2Delete
|
||||
#define sqlite3OsFileExists sqlite3Os2FileExists
|
||||
#define sqlite3OsFullPathname sqlite3Os2FullPathname
|
||||
#define sqlite3OsIsDirWritable sqlite3Os2IsDirWritable
|
||||
#define sqlite3OsSyncDirectory sqlite3Os2SyncDirectory
|
||||
#define sqlite3OsTempFileName sqlite3Os2TempFileName
|
||||
#define sqlite3OsRandomSeed sqlite3Os2RandomSeed
|
||||
#define sqlite3OsSleep sqlite3Os2Sleep
|
||||
#define sqlite3OsCurrentTime sqlite3Os2CurrentTime
|
||||
#define sqlite3OsEnterMutex sqlite3Os2EnterMutex
|
||||
#define sqlite3OsLeaveMutex sqlite3Os2LeaveMutex
|
||||
#define sqlite3OsInMutex sqlite3Os2InMutex
|
||||
#define sqlite3OsThreadSpecificData sqlite3Os2ThreadSpecificData
|
||||
#define sqlite3OsMalloc sqlite3GenericMalloc
|
||||
#define sqlite3OsRealloc sqlite3GenericRealloc
|
||||
#define sqlite3OsFree sqlite3GenericFree
|
||||
#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
|
||||
#define sqlite3OsDlopen sqlite3Os2Dlopen
|
||||
#define sqlite3OsDlsym sqlite3Os2Dlsym
|
||||
#define sqlite3OsDlclose sqlite3Os2Dlclose
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** If using an alternative OS interface, then we must have an "os_other.h"
|
||||
** header file available for that interface. Presumably the "os_other.h"
|
||||
** header file contains #defines similar to those above.
|
||||
*/
|
||||
#if OS_OTHER
|
||||
# include "os_other.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Forward declarations
|
||||
*/
|
||||
typedef struct OsFile OsFile;
|
||||
typedef struct IoMethod IoMethod;
|
||||
|
||||
/*
|
||||
** An instance of the following structure contains pointers to all
|
||||
** methods on an OsFile object.
|
||||
*/
|
||||
struct IoMethod {
|
||||
int (*xClose)(OsFile**);
|
||||
int (*xOpenDirectory)(OsFile*, const char*);
|
||||
int (*xRead)(OsFile*, void*, int amt);
|
||||
int (*xWrite)(OsFile*, const void*, int amt);
|
||||
int (*xSeek)(OsFile*, i64 offset);
|
||||
int (*xTruncate)(OsFile*, i64 size);
|
||||
int (*xSync)(OsFile*, int);
|
||||
void (*xSetFullSync)(OsFile *id, int setting);
|
||||
int (*xFileHandle)(OsFile *id);
|
||||
int (*xFileSize)(OsFile*, i64 *pSize);
|
||||
int (*xLock)(OsFile*, int);
|
||||
int (*xUnlock)(OsFile*, int);
|
||||
int (*xLockState)(OsFile *id);
|
||||
int (*xCheckReservedLock)(OsFile *id);
|
||||
};
|
||||
|
||||
/*
|
||||
** The OsFile object describes an open disk file in an OS-dependent way.
|
||||
** The version of OsFile defined here is a generic version. Each OS
|
||||
** implementation defines its own subclass of this structure that contains
|
||||
** additional information needed to handle file I/O. But the pMethod
|
||||
** entry (pointing to the virtual function table) always occurs first
|
||||
** so that we can always find the appropriate methods.
|
||||
*/
|
||||
struct OsFile {
|
||||
IoMethod const *pMethod;
|
||||
};
|
||||
|
||||
/*
|
||||
** The following values may be passed as the second argument to
|
||||
** sqlite3OsLock(). The various locks exhibit the following semantics:
|
||||
**
|
||||
** SHARED: Any number of processes may hold a SHARED lock simultaneously.
|
||||
** RESERVED: A single process may hold a RESERVED lock on a file at
|
||||
** any time. Other processes may hold and obtain new SHARED locks.
|
||||
** PENDING: A single process may hold a PENDING lock on a file at
|
||||
** any one time. Existing SHARED locks may persist, but no new
|
||||
** SHARED locks may be obtained by other processes.
|
||||
** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
|
||||
**
|
||||
** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
|
||||
** process that requests an EXCLUSIVE lock may actually obtain a PENDING
|
||||
** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
|
||||
** sqlite3OsLock().
|
||||
*/
|
||||
#define NO_LOCK 0
|
||||
#define SHARED_LOCK 1
|
||||
#define RESERVED_LOCK 2
|
||||
#define PENDING_LOCK 3
|
||||
#define EXCLUSIVE_LOCK 4
|
||||
|
||||
/*
|
||||
** File Locking Notes: (Mostly about windows but also some info for Unix)
|
||||
**
|
||||
** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
|
||||
** those functions are not available. So we use only LockFile() and
|
||||
** UnlockFile().
|
||||
**
|
||||
** LockFile() prevents not just writing but also reading by other processes.
|
||||
** A SHARED_LOCK is obtained by locking a single randomly-chosen
|
||||
** byte out of a specific range of bytes. The lock byte is obtained at
|
||||
** random so two separate readers can probably access the file at the
|
||||
** same time, unless they are unlucky and choose the same lock byte.
|
||||
** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
|
||||
** There can only be one writer. A RESERVED_LOCK is obtained by locking
|
||||
** a single byte of the file that is designated as the reserved lock byte.
|
||||
** A PENDING_LOCK is obtained by locking a designated byte different from
|
||||
** the RESERVED_LOCK byte.
|
||||
**
|
||||
** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
|
||||
** which means we can use reader/writer locks. When reader/writer locks
|
||||
** are used, the lock is placed on the same range of bytes that is used
|
||||
** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
|
||||
** will support two or more Win95 readers or two or more WinNT readers.
|
||||
** But a single Win95 reader will lock out all WinNT readers and a single
|
||||
** WinNT reader will lock out all other Win95 readers.
|
||||
**
|
||||
** The following #defines specify the range of bytes used for locking.
|
||||
** SHARED_SIZE is the number of bytes available in the pool from which
|
||||
** 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
|
||||
** 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
|
||||
** tool is being used for file sharing) implements locks correctly between
|
||||
** windows and unix. I'm guessing that isn't likely to happen, but by
|
||||
** using the same locking range we are at least open to the possibility.
|
||||
**
|
||||
** Locking in windows is manditory. For this reason, we cannot store
|
||||
** actual data in the bytes used for locking. The pager never allocates
|
||||
** the pages involved in locking therefore. SHARED_SIZE is selected so
|
||||
** that all locks will fit on a single page even at the minimum page size.
|
||||
** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
|
||||
** is set high so that we don't have to allocate an unused page except
|
||||
** for very large databases. But one should test the page skipping logic
|
||||
** by setting PENDING_BYTE low and running the entire regression suite.
|
||||
**
|
||||
** Changing the value of PENDING_BYTE results in a subtly incompatible
|
||||
** file format. Depending on how it is changed, you might not notice
|
||||
** the incompatibility right away, even running a full regression test.
|
||||
** The default location of PENDING_BYTE is the first byte past the
|
||||
** 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 RESERVED_BYTE (PENDING_BYTE+1)
|
||||
#define SHARED_FIRST (PENDING_BYTE+2)
|
||||
#define SHARED_SIZE 510
|
||||
|
||||
/*
|
||||
** Prototypes for operating system interface routines.
|
||||
*/
|
||||
int sqlite3OsClose(OsFile**);
|
||||
int sqlite3OsOpenDirectory(OsFile*, const char*);
|
||||
int sqlite3OsRead(OsFile*, void*, int amt);
|
||||
int sqlite3OsWrite(OsFile*, const void*, int amt);
|
||||
int sqlite3OsSeek(OsFile*, i64 offset);
|
||||
int sqlite3OsTruncate(OsFile*, i64 size);
|
||||
int sqlite3OsSync(OsFile*, int);
|
||||
void sqlite3OsSetFullSync(OsFile *id, int setting);
|
||||
int sqlite3OsFileHandle(OsFile *id);
|
||||
int sqlite3OsFileSize(OsFile*, i64 *pSize);
|
||||
int sqlite3OsLock(OsFile*, int);
|
||||
int sqlite3OsUnlock(OsFile*, int);
|
||||
int sqlite3OsLockState(OsFile *id);
|
||||
int sqlite3OsCheckReservedLock(OsFile *id);
|
||||
int sqlite3OsOpenReadWrite(const char*, OsFile**, int*);
|
||||
int sqlite3OsOpenExclusive(const char*, OsFile**, int);
|
||||
int sqlite3OsOpenReadOnly(const char*, OsFile**);
|
||||
int sqlite3OsDelete(const char*);
|
||||
int sqlite3OsFileExists(const char*);
|
||||
char *sqlite3OsFullPathname(const char*);
|
||||
int sqlite3OsIsDirWritable(char*);
|
||||
int sqlite3OsSyncDirectory(const char*);
|
||||
int sqlite3OsTempFileName(char*);
|
||||
int sqlite3OsRandomSeed(char*);
|
||||
int sqlite3OsSleep(int ms);
|
||||
int sqlite3OsCurrentTime(double*);
|
||||
void sqlite3OsEnterMutex(void);
|
||||
void sqlite3OsLeaveMutex(void);
|
||||
int sqlite3OsInMutex(int);
|
||||
ThreadData *sqlite3OsThreadSpecificData(int);
|
||||
void *sqlite3OsMalloc(int);
|
||||
void *sqlite3OsRealloc(void *, int);
|
||||
void sqlite3OsFree(void *);
|
||||
int sqlite3OsAllocationSize(void *);
|
||||
void *sqlite3OsDlopen(const char*);
|
||||
void *sqlite3OsDlsym(void*, const char*);
|
||||
int sqlite3OsDlclose(void*);
|
||||
|
||||
/*
|
||||
** If the SQLITE_ENABLE_REDEF_IO macro is defined, then the OS-layer
|
||||
** interface routines are not called directly but are invoked using
|
||||
** pointers to functions. This allows the implementation of various
|
||||
** OS-layer interface routines to be modified at run-time. There are
|
||||
** obscure but legitimate reasons for wanting to do this. But for
|
||||
** most users, a direct call to the underlying interface is preferable
|
||||
** so the the redefinable I/O interface is turned off by default.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_REDEF_IO
|
||||
|
||||
/*
|
||||
** When redefinable I/O is enabled, a single global instance of the
|
||||
** following structure holds pointers to the routines that SQLite
|
||||
** uses to talk with the underlying operating system. Modify this
|
||||
** structure (before using any SQLite API!) to accomodate perculiar
|
||||
** operating system interfaces or behaviors.
|
||||
*/
|
||||
struct sqlite3OsVtbl {
|
||||
int (*xOpenReadWrite)(const char*, OsFile**, int*);
|
||||
int (*xOpenExclusive)(const char*, OsFile**, int);
|
||||
int (*xOpenReadOnly)(const char*, OsFile**);
|
||||
|
||||
int (*xDelete)(const char*);
|
||||
int (*xFileExists)(const char*);
|
||||
char *(*xFullPathname)(const char*);
|
||||
int (*xIsDirWritable)(char*);
|
||||
int (*xSyncDirectory)(const char*);
|
||||
int (*xTempFileName)(char*);
|
||||
|
||||
int (*xRandomSeed)(char*);
|
||||
int (*xSleep)(int ms);
|
||||
int (*xCurrentTime)(double*);
|
||||
|
||||
void (*xEnterMutex)(void);
|
||||
void (*xLeaveMutex)(void);
|
||||
int (*xInMutex)(int);
|
||||
ThreadData *(*xThreadSpecificData)(int);
|
||||
|
||||
void *(*xMalloc)(int);
|
||||
void *(*xRealloc)(void *, int);
|
||||
void (*xFree)(void *);
|
||||
int (*xAllocationSize)(void *);
|
||||
|
||||
void *(*xDlopen)(const char*);
|
||||
void *(*xDlsym)(void*, const char*);
|
||||
int (*xDlclose)(void*);
|
||||
};
|
||||
|
||||
/* Macro used to comment out routines that do not exists when there is
|
||||
** no disk I/O or extension loading
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_DISKIO
|
||||
# define IF_DISKIO(X) 0
|
||||
#else
|
||||
# define IF_DISKIO(X) X
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
# define IF_DLOPEN(X) 0
|
||||
#else
|
||||
# define IF_DLOPEN(X) X
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _SQLITE_OS_C_
|
||||
/*
|
||||
** The os.c file implements the global virtual function table.
|
||||
*/
|
||||
struct sqlite3OsVtbl sqlite3Os = {
|
||||
IF_DISKIO( sqlite3OsOpenReadWrite ),
|
||||
IF_DISKIO( sqlite3OsOpenExclusive ),
|
||||
IF_DISKIO( sqlite3OsOpenReadOnly ),
|
||||
IF_DISKIO( sqlite3OsDelete ),
|
||||
IF_DISKIO( sqlite3OsFileExists ),
|
||||
IF_DISKIO( sqlite3OsFullPathname ),
|
||||
IF_DISKIO( sqlite3OsIsDirWritable ),
|
||||
IF_DISKIO( sqlite3OsSyncDirectory ),
|
||||
IF_DISKIO( sqlite3OsTempFileName ),
|
||||
sqlite3OsRandomSeed,
|
||||
sqlite3OsSleep,
|
||||
sqlite3OsCurrentTime,
|
||||
sqlite3OsEnterMutex,
|
||||
sqlite3OsLeaveMutex,
|
||||
sqlite3OsInMutex,
|
||||
sqlite3OsThreadSpecificData,
|
||||
sqlite3OsMalloc,
|
||||
sqlite3OsRealloc,
|
||||
sqlite3OsFree,
|
||||
sqlite3OsAllocationSize,
|
||||
IF_DLOPEN( sqlite3OsDlopen ),
|
||||
IF_DLOPEN( sqlite3OsDlsym ),
|
||||
IF_DLOPEN( sqlite3OsDlclose ),
|
||||
};
|
||||
#else
|
||||
/*
|
||||
** Files other than os.c just reference the global virtual function table.
|
||||
*/
|
||||
extern struct sqlite3OsVtbl sqlite3Os;
|
||||
#endif /* _SQLITE_OS_C_ */
|
||||
|
||||
|
||||
/* This additional API routine is available with redefinable I/O */
|
||||
struct sqlite3OsVtbl *sqlite3_os_switch(void);
|
||||
|
||||
|
||||
/*
|
||||
** Redefine the OS interface to go through the virtual function table
|
||||
** rather than calling routines directly.
|
||||
*/
|
||||
#undef sqlite3OsOpenReadWrite
|
||||
#undef sqlite3OsOpenExclusive
|
||||
#undef sqlite3OsOpenReadOnly
|
||||
#undef sqlite3OsDelete
|
||||
#undef sqlite3OsFileExists
|
||||
#undef sqlite3OsFullPathname
|
||||
#undef sqlite3OsIsDirWritable
|
||||
#undef sqlite3OsSyncDirectory
|
||||
#undef sqlite3OsTempFileName
|
||||
#undef sqlite3OsRandomSeed
|
||||
#undef sqlite3OsSleep
|
||||
#undef sqlite3OsCurrentTime
|
||||
#undef sqlite3OsEnterMutex
|
||||
#undef sqlite3OsLeaveMutex
|
||||
#undef sqlite3OsInMutex
|
||||
#undef sqlite3OsThreadSpecificData
|
||||
#undef sqlite3OsMalloc
|
||||
#undef sqlite3OsRealloc
|
||||
#undef sqlite3OsFree
|
||||
#undef sqlite3OsAllocationSize
|
||||
#define sqlite3OsOpenReadWrite sqlite3Os.xOpenReadWrite
|
||||
#define sqlite3OsOpenExclusive sqlite3Os.xOpenExclusive
|
||||
#define sqlite3OsOpenReadOnly sqlite3Os.xOpenReadOnly
|
||||
#define sqlite3OsDelete sqlite3Os.xDelete
|
||||
#define sqlite3OsFileExists sqlite3Os.xFileExists
|
||||
#define sqlite3OsFullPathname sqlite3Os.xFullPathname
|
||||
#define sqlite3OsIsDirWritable sqlite3Os.xIsDirWritable
|
||||
#define sqlite3OsSyncDirectory sqlite3Os.xSyncDirectory
|
||||
#define sqlite3OsTempFileName sqlite3Os.xTempFileName
|
||||
#define sqlite3OsRandomSeed sqlite3Os.xRandomSeed
|
||||
#define sqlite3OsSleep sqlite3Os.xSleep
|
||||
#define sqlite3OsCurrentTime sqlite3Os.xCurrentTime
|
||||
#define sqlite3OsEnterMutex sqlite3Os.xEnterMutex
|
||||
#define sqlite3OsLeaveMutex sqlite3Os.xLeaveMutex
|
||||
#define sqlite3OsInMutex sqlite3Os.xInMutex
|
||||
#define sqlite3OsThreadSpecificData sqlite3Os.xThreadSpecificData
|
||||
#define sqlite3OsMalloc sqlite3Os.xMalloc
|
||||
#define sqlite3OsRealloc sqlite3Os.xRealloc
|
||||
#define sqlite3OsFree sqlite3Os.xFree
|
||||
#define sqlite3OsAllocationSize sqlite3Os.xAllocationSize
|
||||
|
||||
#endif /* SQLITE_ENABLE_REDEF_IO */
|
||||
|
||||
#endif /* _SQLITE_OS_H_ */
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
** 2004 May 22
|
||||
**
|
||||
** 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 macros and a little bit of code that is common to
|
||||
** all of the platform-specific files (os_*.c) and is #included into those
|
||||
** files.
|
||||
**
|
||||
** This file should be #included by the os_*.c files only. It is not a
|
||||
** general purpose header file.
|
||||
*/
|
||||
|
||||
/*
|
||||
** At least two bugs have slipped in because we changed the MEMORY_DEBUG
|
||||
** macro to SQLITE_DEBUG and some older makefiles have not yet made the
|
||||
** switch. The following code should catch this problem at compile-time.
|
||||
*/
|
||||
#ifdef MEMORY_DEBUG
|
||||
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
||||
#endif
|
||||
#undef SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
/*
|
||||
* When testing, this global variable stores the location of the
|
||||
* pending-byte in the database file.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
unsigned int sqlite3_pending_byte = 0x40000000;
|
||||
#endif
|
||||
|
||||
int sqlite3_os_trace = 0;
|
||||
#ifdef SQLITE_DEBUG
|
||||
static int last_page = 0;
|
||||
#define SEEK(X) last_page=(X)
|
||||
#define TRACE1(X) if( sqlite3_os_trace ) sqlite3DebugPrintf(X)
|
||||
#define TRACE2(X,Y) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y)
|
||||
#define TRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z)
|
||||
#define TRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A)
|
||||
#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B)
|
||||
#define TRACE6(X,Y,Z,A,B,C) if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
|
||||
#define TRACE7(X,Y,Z,A,B,C,D) \
|
||||
if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D)
|
||||
#else
|
||||
#define SEEK(X)
|
||||
#define TRACE1(X)
|
||||
#define TRACE2(X,Y)
|
||||
#define TRACE3(X,Y,Z)
|
||||
#define TRACE4(X,Y,Z,A)
|
||||
#define TRACE5(X,Y,Z,A,B)
|
||||
#define TRACE6(X,Y,Z,A,B,C)
|
||||
#define TRACE7(X,Y,Z,A,B,C,D)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macros for performance tracing. Normally turned off. Only works
|
||||
** on i486 hardware.
|
||||
*/
|
||||
#ifdef SQLITE_PERFORMANCE_TRACE
|
||||
__inline__ unsigned long long int hwtime(void){
|
||||
unsigned long long int x;
|
||||
__asm__("rdtsc\n\t"
|
||||
"mov %%edx, %%ecx\n\t"
|
||||
:"=A" (x));
|
||||
return x;
|
||||
}
|
||||
static unsigned long long int g_start;
|
||||
static unsigned int elapse;
|
||||
#define TIMER_START g_start=hwtime()
|
||||
#define TIMER_END elapse=hwtime()-g_start
|
||||
#define TIMER_ELAPSED elapse
|
||||
#else
|
||||
#define TIMER_START
|
||||
#define TIMER_END
|
||||
#define TIMER_ELAPSED 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If we compile with the SQLITE_TEST macro set, then the following block
|
||||
** of code will give us the ability to simulate a disk I/O error. This
|
||||
** is used for testing the I/O recovery logic.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3_io_error_hit = 0;
|
||||
int sqlite3_io_error_pending = 0;
|
||||
int sqlite3_diskfull_pending = 0;
|
||||
int sqlite3_diskfull = 0;
|
||||
#define SimulateIOError(CODE) \
|
||||
if( sqlite3_io_error_pending ) \
|
||||
if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); CODE; }
|
||||
static void local_ioerr(){
|
||||
sqlite3_io_error_hit = 1; /* Really just a place to set a breakpoint */
|
||||
}
|
||||
#define SimulateDiskfullError(CODE) \
|
||||
if( sqlite3_diskfull_pending ){ \
|
||||
if( sqlite3_diskfull_pending == 1 ){ \
|
||||
local_ioerr(); \
|
||||
sqlite3_diskfull = 1; \
|
||||
CODE; \
|
||||
}else{ \
|
||||
sqlite3_diskfull_pending--; \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define SimulateIOError(A)
|
||||
#define SimulateDiskfullError(A)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** When testing, keep a count of the number of open files.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3_open_file_count = 0;
|
||||
#define OpenCounter(X) sqlite3_open_file_count+=(X)
|
||||
#else
|
||||
#define OpenCounter(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** sqlite3GenericMalloc
|
||||
** sqlite3GenericRealloc
|
||||
** sqlite3GenericOsFree
|
||||
** sqlite3GenericAllocationSize
|
||||
**
|
||||
** Implementation of the os level dynamic memory allocation interface in terms
|
||||
** of the standard malloc(), realloc() and free() found in many operating
|
||||
** systems. No rocket science here.
|
||||
**
|
||||
** There are two versions of these four functions here. The version
|
||||
** implemented here is only used if memory-management or memory-debugging is
|
||||
** enabled. This version allocates an extra 8-bytes at the beginning of each
|
||||
** block and stores the size of the allocation there.
|
||||
**
|
||||
** If neither memory-management or debugging is enabled, the second
|
||||
** set of implementations is used instead.
|
||||
*/
|
||||
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || defined (SQLITE_MEMDEBUG)
|
||||
void *sqlite3GenericMalloc(int n){
|
||||
char *p = (char *)malloc(n+8);
|
||||
assert(n>0);
|
||||
assert(sizeof(int)<=8);
|
||||
if( p ){
|
||||
*(int *)p = n;
|
||||
p += 8;
|
||||
}
|
||||
return (void *)p;
|
||||
}
|
||||
void *sqlite3GenericRealloc(void *p, int n){
|
||||
char *p2 = ((char *)p - 8);
|
||||
assert(n>0);
|
||||
p2 = (char*)realloc(p2, n+8);
|
||||
if( p2 ){
|
||||
*(int *)p2 = n;
|
||||
p2 += 8;
|
||||
}
|
||||
return (void *)p2;
|
||||
}
|
||||
void sqlite3GenericFree(void *p){
|
||||
assert(p);
|
||||
free((void *)((char *)p - 8));
|
||||
}
|
||||
int sqlite3GenericAllocationSize(void *p){
|
||||
return p ? *(int *)((char *)p - 8) : 0;
|
||||
}
|
||||
#else
|
||||
void *sqlite3GenericMalloc(int n){
|
||||
char *p = (char *)malloc(n);
|
||||
return (void *)p;
|
||||
}
|
||||
void *sqlite3GenericRealloc(void *p, int n){
|
||||
assert(n>0);
|
||||
p = realloc(p, n);
|
||||
return p;
|
||||
}
|
||||
void sqlite3GenericFree(void *p){
|
||||
assert(p);
|
||||
free(p);
|
||||
}
|
||||
/* Never actually used, but needed for the linker */
|
||||
int sqlite3GenericAllocationSize(void *p){ return 0; }
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
** 2004 May 22
|
||||
**
|
||||
** 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 defined OS-specific features for OS/2.
|
||||
*/
|
||||
#ifndef _SQLITE_OS_OS2_H_
|
||||
#define _SQLITE_OS_OS2_H_
|
||||
|
||||
/*
|
||||
** standard include files.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
** Macros used to determine whether or not to use threads. The
|
||||
** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
|
||||
** Posix threads and SQLITE_W32_THREADS is defined if we are
|
||||
** synchronizing using Win32 threads.
|
||||
*/
|
||||
/* this mutex implementation only available with EMX */
|
||||
#if defined(THREADSAFE) && THREADSAFE
|
||||
# include <sys/builtin.h>
|
||||
# include <sys/smutex.h>
|
||||
# define SQLITE_OS2_THREADS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The OsFile structure is a operating-system independing representation
|
||||
** of an open file handle. It is defined differently for each architecture.
|
||||
**
|
||||
** This is the definition for Unix.
|
||||
**
|
||||
** OsFile.locktype takes one of the values SHARED_LOCK, RESERVED_LOCK,
|
||||
** PENDING_LOCK or EXCLUSIVE_LOCK.
|
||||
*/
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
int h; /* The file descriptor (LHANDLE) */
|
||||
int locked; /* True if this user holds the lock */
|
||||
int delOnClose; /* True if file is to be deleted on close */
|
||||
char *pathToDel; /* Name of file to delete on close */
|
||||
unsigned char locktype; /* The type of lock held on this fd */
|
||||
unsigned char isOpen; /* True if needs to be closed */
|
||||
unsigned char fullSync;
|
||||
};
|
||||
|
||||
/*
|
||||
** Maximum number of characters in a temporary file name
|
||||
*/
|
||||
#define SQLITE_TEMPNAME_SIZE 200
|
||||
|
||||
/*
|
||||
** Minimum interval supported by sqlite3OsSleep().
|
||||
*/
|
||||
#define SQLITE_MIN_SLEEP_MS 1
|
||||
|
||||
#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS
|
||||
# define SQLITE_DEFAULT_FILE_PERMISSIONS 0600
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITE_OS_OS2_H_ */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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. 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.52 2006/11/06 21:20:26 drh Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PAGER_H_
|
||||
#define _PAGER_H_
|
||||
|
||||
/*
|
||||
** The default size of a database page.
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_PAGE_SIZE
|
||||
# define SQLITE_DEFAULT_PAGE_SIZE 1024
|
||||
#endif
|
||||
|
||||
/* Maximum page size. The upper bound on this value is 32768. This a limit
|
||||
** imposed by necessity of storing the value in a 2-byte unsigned integer
|
||||
** and the fact that the page size must be a power of 2.
|
||||
**
|
||||
** This value is used to initialize certain arrays on the stack at
|
||||
** various places in the code. On embedded machines where stack space
|
||||
** is limited and the flexibility of having large pages is not needed,
|
||||
** it makes good sense to reduce the maximum page size to something more
|
||||
** reasonable, like 1024.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_PAGE_SIZE
|
||||
# define SQLITE_MAX_PAGE_SIZE 32768
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Maximum number of pages in one database.
|
||||
*/
|
||||
#define SQLITE_MAX_PAGE 1073741823
|
||||
|
||||
/*
|
||||
** The type used to represent a page number. The first page in a file
|
||||
** is called page 1. 0 is used to represent "not a page".
|
||||
*/
|
||||
typedef unsigned int Pgno;
|
||||
|
||||
/*
|
||||
** Each open file is managed by a separate instance of the "Pager" structure.
|
||||
*/
|
||||
typedef struct Pager Pager;
|
||||
|
||||
/*
|
||||
** Allowed values for the flags parameter to sqlite3pager_open().
|
||||
**
|
||||
** NOTE: This 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 */
|
||||
|
||||
|
||||
/*
|
||||
** See source code comments for a detailed description of the following
|
||||
** routines:
|
||||
*/
|
||||
int sqlite3pager_open(Pager **ppPager, const char *zFilename,
|
||||
int nExtra, int flags);
|
||||
void sqlite3pager_set_busyhandler(Pager*, BusyHandler *pBusyHandler);
|
||||
void sqlite3pager_set_destructor(Pager*, void(*)(void*,int));
|
||||
void sqlite3pager_set_reiniter(Pager*, void(*)(void*,int));
|
||||
int sqlite3pager_set_pagesize(Pager*, int);
|
||||
int sqlite3pager_read_fileheader(Pager*, int, unsigned char*);
|
||||
void sqlite3pager_set_cachesize(Pager*, int);
|
||||
int sqlite3pager_close(Pager *pPager);
|
||||
int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage);
|
||||
void *sqlite3pager_lookup(Pager *pPager, Pgno pgno);
|
||||
int sqlite3pager_ref(void*);
|
||||
int sqlite3pager_unref(void*);
|
||||
Pgno sqlite3pager_pagenumber(void*);
|
||||
int sqlite3pager_write(void*);
|
||||
int sqlite3pager_iswriteable(void*);
|
||||
int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void*);
|
||||
int sqlite3pager_pagecount(Pager*);
|
||||
int sqlite3pager_truncate(Pager*,Pgno);
|
||||
int sqlite3pager_begin(void*, int exFlag);
|
||||
int sqlite3pager_commit(Pager*);
|
||||
int sqlite3pager_sync(Pager*,const char *zMaster, Pgno);
|
||||
int sqlite3pager_rollback(Pager*);
|
||||
int sqlite3pager_isreadonly(Pager*);
|
||||
int sqlite3pager_stmt_begin(Pager*);
|
||||
int sqlite3pager_stmt_commit(Pager*);
|
||||
int sqlite3pager_stmt_rollback(Pager*);
|
||||
void sqlite3pager_dont_rollback(void*);
|
||||
void sqlite3pager_dont_write(Pager*, Pgno);
|
||||
int sqlite3pager_refcount(Pager*);
|
||||
int *sqlite3pager_stats(Pager*);
|
||||
void sqlite3pager_set_safety_level(Pager*,int,int);
|
||||
const char *sqlite3pager_filename(Pager*);
|
||||
const char *sqlite3pager_dirname(Pager*);
|
||||
const char *sqlite3pager_journalname(Pager*);
|
||||
int sqlite3pager_nosync(Pager*);
|
||||
int sqlite3pager_rename(Pager*, const char *zNewName);
|
||||
void sqlite3pager_set_codec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
|
||||
int sqlite3pager_movepage(Pager*,void*,Pgno);
|
||||
int sqlite3pager_reset(Pager*);
|
||||
int sqlite3pager_release_memory(int);
|
||||
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
int sqlite3pager_lockstate(Pager*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
void sqlite3pager_refdump(Pager*);
|
||||
int pager3_refinfo_enable;
|
||||
#endif
|
||||
|
||||
#endif /* _PAGER_H_ */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,682 +0,0 @@
|
|||
/*
|
||||
** 2005 May 25
|
||||
**
|
||||
** 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_prepare()
|
||||
** interface, and routines that contribute to loading the database schema
|
||||
** from disk.
|
||||
**
|
||||
** $Id: prepare.c,v 1.43 2007/01/09 14:01:13 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Fill the InitData structure with an error message that indicates
|
||||
** that the database is corrupt.
|
||||
*/
|
||||
static void corruptSchema(InitData *pData, const char *zExtra){
|
||||
if( !sqlite3MallocFailed() ){
|
||||
sqlite3SetString(pData->pzErrMsg, "malformed database schema",
|
||||
zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0);
|
||||
}
|
||||
pData->rc = SQLITE_CORRUPT;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the callback routine for the code that initializes the
|
||||
** database. See sqlite3Init() below for additional information.
|
||||
** This routine is also called from the OP_ParseSchema opcode of the VDBE.
|
||||
**
|
||||
** Each callback contains the following information:
|
||||
**
|
||||
** argv[0] = name of thing being created
|
||||
** argv[1] = root page number for table or index. 0 for trigger or view.
|
||||
** argv[2] = SQL text for the CREATE statement.
|
||||
**
|
||||
*/
|
||||
int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
|
||||
InitData *pData = (InitData*)pInit;
|
||||
sqlite3 *db = pData->db;
|
||||
int iDb = pData->iDb;
|
||||
|
||||
pData->rc = SQLITE_OK;
|
||||
DbClearProperty(db, iDb, DB_Empty);
|
||||
if( sqlite3MallocFailed() ){
|
||||
corruptSchema(pData, 0);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
assert( argc==3 );
|
||||
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
|
||||
if( argv[1]==0 ){
|
||||
corruptSchema(pData, 0);
|
||||
return 1;
|
||||
}
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
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
|
||||
** structures that describe the table, index, or view.
|
||||
*/
|
||||
char *zErr;
|
||||
int rc;
|
||||
assert( db->init.busy );
|
||||
db->init.iDb = iDb;
|
||||
db->init.newTnum = atoi(argv[1]);
|
||||
rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
|
||||
db->init.iDb = 0;
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
if( SQLITE_OK!=rc ){
|
||||
pData->rc = rc;
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3FailedMalloc();
|
||||
}else if( rc!=SQLITE_INTERRUPT ){
|
||||
corruptSchema(pData, zErr);
|
||||
}
|
||||
sqlite3_free(zErr);
|
||||
return 1;
|
||||
}
|
||||
}else{
|
||||
/* If the SQL column is blank it means this is an index that
|
||||
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
|
||||
** constraint for a CREATE TABLE. The index should have already
|
||||
** been created when we processed the CREATE TABLE. All we have
|
||||
** to do here is record the root page number for that index.
|
||||
*/
|
||||
Index *pIndex;
|
||||
pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName);
|
||||
if( pIndex==0 || pIndex->tnum!=0 ){
|
||||
/* This can occur if there exists an index on a TEMP table which
|
||||
** has the same name as another index on a permanent index. Since
|
||||
** the permanent table is hidden by the TEMP table, we can also
|
||||
** safely ignore the index on the permanent table.
|
||||
*/
|
||||
/* Do Nothing */;
|
||||
}else{
|
||||
pIndex->tnum = atoi(argv[1]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to read the database schema and initialize internal
|
||||
** data structures for a single database file. The index of the
|
||||
** database file is given by iDb. iDb==0 is used for the main
|
||||
** database. iDb==1 should never be used. iDb>=2 is used for
|
||||
** auxiliary databases. Return one of the SQLITE_ error codes to
|
||||
** indicate success or failure.
|
||||
*/
|
||||
static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
int rc;
|
||||
BtCursor *curMain;
|
||||
int size;
|
||||
Table *pTab;
|
||||
Db *pDb;
|
||||
char const *azArg[4];
|
||||
int meta[10];
|
||||
InitData initData;
|
||||
char const *zMasterSchema;
|
||||
char const *zMasterName = SCHEMA_TABLE(iDb);
|
||||
|
||||
/*
|
||||
** The master database table has a structure like this
|
||||
*/
|
||||
static const char master_schema[] =
|
||||
"CREATE TABLE sqlite_master(\n"
|
||||
" type text,\n"
|
||||
" name text,\n"
|
||||
" tbl_name text,\n"
|
||||
" rootpage integer,\n"
|
||||
" sql text\n"
|
||||
")"
|
||||
;
|
||||
#ifndef SQLITE_OMIT_TEMPDB
|
||||
static const char temp_master_schema[] =
|
||||
"CREATE TEMP TABLE sqlite_temp_master(\n"
|
||||
" type text,\n"
|
||||
" name text,\n"
|
||||
" tbl_name text,\n"
|
||||
" rootpage integer,\n"
|
||||
" sql text\n"
|
||||
")"
|
||||
;
|
||||
#else
|
||||
#define temp_master_schema 0
|
||||
#endif
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pSchema );
|
||||
|
||||
/* zMasterSchema and zInitScript are set to point at the master schema
|
||||
** and initialisation script appropriate for the database being
|
||||
** initialised. zMasterName is the name of the master table.
|
||||
*/
|
||||
if( !OMIT_TEMPDB && iDb==1 ){
|
||||
zMasterSchema = temp_master_schema;
|
||||
}else{
|
||||
zMasterSchema = master_schema;
|
||||
}
|
||||
zMasterName = SCHEMA_TABLE(iDb);
|
||||
|
||||
/* Construct the schema tables. */
|
||||
sqlite3SafetyOff(db);
|
||||
azArg[0] = zMasterName;
|
||||
azArg[1] = "1";
|
||||
azArg[2] = zMasterSchema;
|
||||
azArg[3] = 0;
|
||||
initData.db = db;
|
||||
initData.iDb = iDb;
|
||||
initData.pzErrMsg = pzErrMsg;
|
||||
rc = sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
|
||||
if( rc ){
|
||||
sqlite3SafetyOn(db);
|
||||
return initData.rc;
|
||||
}
|
||||
pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
|
||||
if( pTab ){
|
||||
pTab->readOnly = 1;
|
||||
}
|
||||
sqlite3SafetyOn(db);
|
||||
|
||||
/* Create a cursor to hold the database open
|
||||
*/
|
||||
pDb = &db->aDb[iDb];
|
||||
if( pDb->pBt==0 ){
|
||||
if( !OMIT_TEMPDB && iDb==1 ){
|
||||
DbSetProperty(db, 1, DB_SchemaLoaded);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, 0, &curMain);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){
|
||||
sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Get the database meta information.
|
||||
**
|
||||
** Meta values are as follows:
|
||||
** meta[0] Schema cookie. Changes with each schema change.
|
||||
** meta[1] File format of schema layer.
|
||||
** meta[2] Size of the page cache.
|
||||
** meta[3] Use freelist if 0. Autovacuum if greater than zero.
|
||||
** meta[4] Db text encoding. 1:UTF-8 2:UTF-16LE 3:UTF-16BE
|
||||
** meta[5] The user cookie. Used by the application.
|
||||
** meta[6]
|
||||
** meta[7]
|
||||
** meta[8]
|
||||
** meta[9]
|
||||
**
|
||||
** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to
|
||||
** the possible values of meta[4].
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
for(i=0; rc==SQLITE_OK && i<sizeof(meta)/sizeof(meta[0]); i++){
|
||||
rc = sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
|
||||
}
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
memset(meta, 0, sizeof(meta));
|
||||
}
|
||||
pDb->pSchema->schema_cookie = meta[0];
|
||||
|
||||
/* If opening a non-empty database, check the text encoding. For the
|
||||
** main database, set sqlite3.enc to the encoding of the main database.
|
||||
** For an attached db, it is an error if the encoding is not the same
|
||||
** as sqlite3.enc.
|
||||
*/
|
||||
if( meta[4] ){ /* text encoding */
|
||||
if( iDb==0 ){
|
||||
/* If opening the main database, set ENC(db). */
|
||||
ENC(db) = (u8)meta[4];
|
||||
db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0);
|
||||
}else{
|
||||
/* If opening an attached database, the encoding much match ENC(db) */
|
||||
if( meta[4]!=ENC(db) ){
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
sqlite3SetString(pzErrMsg, "attached databases must use the same"
|
||||
" text encoding as main database", (char*)0);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
DbSetProperty(db, iDb, DB_Empty);
|
||||
}
|
||||
pDb->pSchema->enc = ENC(db);
|
||||
|
||||
size = meta[2];
|
||||
if( size==0 ){ size = MAX_PAGES; }
|
||||
pDb->pSchema->cache_size = size;
|
||||
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
|
||||
|
||||
/*
|
||||
** file_format==1 Version 3.0.0.
|
||||
** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN
|
||||
** 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];
|
||||
if( pDb->pSchema->file_format==0 ){
|
||||
pDb->pSchema->file_format = 1;
|
||||
}
|
||||
if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Read the schema information out of the schema tables
|
||||
*/
|
||||
assert( db->init.busy );
|
||||
if( rc==SQLITE_EMPTY ){
|
||||
/* For an empty database, there is nothing to read */
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf(
|
||||
"SELECT name, rootpage, sql FROM '%q'.%s",
|
||||
db->aDb[iDb].zName, zMasterName);
|
||||
sqlite3SafetyOff(db);
|
||||
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
|
||||
if( rc==SQLITE_ABORT ) rc = initData.rc;
|
||||
sqlite3SafetyOn(db);
|
||||
sqliteFree(zSql);
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3AnalysisLoad(db, iDb);
|
||||
}
|
||||
#endif
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
}
|
||||
if( sqlite3MallocFailed() ){
|
||||
/* sqlite3SetString(pzErrMsg, "out of memory", (char*)0); */
|
||||
rc = SQLITE_NOMEM;
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
DbSetProperty(db, iDb, DB_SchemaLoaded);
|
||||
}else{
|
||||
sqlite3ResetInternalSchema(db, iDb);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize all database files - the main database file, the file
|
||||
** used to store temporary tables, and any additional database files
|
||||
** created using ATTACH statements. Return a success code. If an
|
||||
** error occurs, write an error message into *pzErrMsg.
|
||||
**
|
||||
** After a database is initialized, the DB_SchemaLoaded bit is set
|
||||
** bit is set in the flags field of the Db structure. If the database
|
||||
** file was of zero-length, then the DB_Empty flag is also set.
|
||||
*/
|
||||
int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int i, rc;
|
||||
int called_initone = 0;
|
||||
|
||||
if( db->init.busy ) return SQLITE_OK;
|
||||
rc = SQLITE_OK;
|
||||
db->init.busy = 1;
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
|
||||
rc = sqlite3InitOne(db, i, pzErrMsg);
|
||||
if( rc ){
|
||||
sqlite3ResetInternalSchema(db, i);
|
||||
}
|
||||
called_initone = 1;
|
||||
}
|
||||
|
||||
/* Once all the other databases have been initialised, load the schema
|
||||
** for the TEMP database. This is loaded last, as the TEMP database
|
||||
** schema may contain references to objects in other databases.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TEMPDB
|
||||
if( rc==SQLITE_OK && db->nDb>1 && !DbHasProperty(db, 1, DB_SchemaLoaded) ){
|
||||
rc = sqlite3InitOne(db, 1, pzErrMsg);
|
||||
if( rc ){
|
||||
sqlite3ResetInternalSchema(db, 1);
|
||||
}
|
||||
called_initone = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
db->init.busy = 0;
|
||||
if( rc==SQLITE_OK && called_initone ){
|
||||
sqlite3CommitInternalChanges(db);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is a no-op if the database schema is already initialised.
|
||||
** Otherwise, the schema is loaded. An error code is returned.
|
||||
*/
|
||||
int sqlite3ReadSchema(Parse *pParse){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3 *db = pParse->db;
|
||||
if( !db->init.busy ){
|
||||
rc = sqlite3Init(db, &pParse->zErrMsg);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
pParse->rc = rc;
|
||||
pParse->nErr++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check schema cookies in all databases. If any cookie is out
|
||||
** of date, return 0. If all schema cookies are current, return 1.
|
||||
*/
|
||||
static int schemaIsValid(sqlite3 *db){
|
||||
int iDb;
|
||||
int rc;
|
||||
BtCursor *curTemp;
|
||||
int cookie;
|
||||
int allOk = 1;
|
||||
|
||||
for(iDb=0; allOk && iDb<db->nDb; iDb++){
|
||||
Btree *pBt;
|
||||
pBt = db->aDb[iDb].pBt;
|
||||
if( pBt==0 ) continue;
|
||||
rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
|
||||
if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
|
||||
allOk = 0;
|
||||
}
|
||||
sqlite3BtreeCloseCursor(curTemp);
|
||||
}
|
||||
}
|
||||
return allOk;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a schema pointer into the iDb index that indicates
|
||||
** which database file in db->aDb[] the schema refers to.
|
||||
**
|
||||
** If the same database is attached more than once, the first
|
||||
** attached database is returned.
|
||||
*/
|
||||
int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
int i = -1000000;
|
||||
|
||||
/* If pSchema is NULL, then return -1000000. This happens when code in
|
||||
** expr.c is trying to resolve a reference to a transient table (i.e. one
|
||||
** created by a sub-select). In this case the return value of this
|
||||
** 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
|
||||
** more likely to cause a segfault than -1 (of course there are assert()
|
||||
** statements too, but it never hurts to play the odds).
|
||||
*/
|
||||
if( pSchema ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pSchema==pSchema ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( i>=0 &&i>=0 && i<db->nDb );
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
|
||||
*/
|
||||
int sqlite3Prepare(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
Parse sParse;
|
||||
char *zErrMsg = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
/* Assert that malloc() has not failed */
|
||||
assert( !sqlite3MallocFailed() );
|
||||
|
||||
assert( ppStmt );
|
||||
*ppStmt = 0;
|
||||
if( sqlite3SafetyOn(db) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
/* If any attached database schemas are locked, do not proceed with
|
||||
** compilation. Instead return SQLITE_LOCKED immediately.
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++) {
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeSchemaLocked(pBt) ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb);
|
||||
sqlite3SafetyOff(db);
|
||||
return SQLITE_LOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = db;
|
||||
if( nBytes>=0 && zSql[nBytes]!=0 ){
|
||||
char *zSqlCopy = sqlite3StrNDup(zSql, nBytes);
|
||||
sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg);
|
||||
sParse.zTail += zSql - zSqlCopy;
|
||||
sqliteFree(zSqlCopy);
|
||||
}else{
|
||||
sqlite3RunParser(&sParse, zSql, &zErrMsg);
|
||||
}
|
||||
|
||||
if( sqlite3MallocFailed() ){
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
|
||||
if( sParse.checkSchema && !schemaIsValid(db) ){
|
||||
sParse.rc = SQLITE_SCHEMA;
|
||||
}
|
||||
if( sParse.rc==SQLITE_SCHEMA ){
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
}
|
||||
if( sqlite3MallocFailed() ){
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( pzTail ){
|
||||
*pzTail = sParse.zTail;
|
||||
}
|
||||
rc = sParse.rc;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
|
||||
if( sParse.explain==2 ){
|
||||
sqlite3VdbeSetNumCols(sParse.pVdbe, 3);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "order", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "from", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "detail", P3_STATIC);
|
||||
}else{
|
||||
sqlite3VdbeSetNumCols(sParse.pVdbe, 5);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 0, COLNAME_NAME, "addr", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 1, COLNAME_NAME, "opcode", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 2, COLNAME_NAME, "p1", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", P3_STATIC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( sqlite3SafetyOff(db) ){
|
||||
rc = SQLITE_MISUSE;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( saveSqlFlag ){
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, sParse.zTail - zSql);
|
||||
}
|
||||
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
|
||||
}else if( sParse.pVdbe ){
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
}
|
||||
|
||||
if( zErrMsg ){
|
||||
sqlite3Error(db, rc, "%s", zErrMsg);
|
||||
sqliteFree(zErrMsg);
|
||||
}else{
|
||||
sqlite3Error(db, rc, 0);
|
||||
}
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3ReleaseThreadData();
|
||||
assert( (rc&db->errMask)==rc );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rerun the compilation of a statement after a schema change.
|
||||
** Return true if the statement was recompiled successfully.
|
||||
** Return false if there is an error of some kind.
|
||||
*/
|
||||
int sqlite3Reprepare(Vdbe *p){
|
||||
int rc;
|
||||
Vdbe *pNew;
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
|
||||
zSql = sqlite3VdbeGetSql(p);
|
||||
if( zSql==0 ){
|
||||
return 0;
|
||||
}
|
||||
db = sqlite3VdbeDb(p);
|
||||
rc = sqlite3Prepare(db, zSql, -1, 0, (sqlite3_stmt**)&pNew, 0);
|
||||
if( rc ){
|
||||
assert( pNew==0 );
|
||||
return 0;
|
||||
}else{
|
||||
assert( pNew!=0 );
|
||||
}
|
||||
sqlite3VdbeSwap(pNew, p);
|
||||
sqlite3_transfer_bindings((sqlite3_stmt*)pNew, (sqlite3_stmt*)p);
|
||||
sqlite3VdbeResetStepResult(pNew);
|
||||
sqlite3VdbeFinalize(pNew);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Two versions of the official API. Legacy and new use. In the legacy
|
||||
** version, the original SQL text is not saved in the prepared statement
|
||||
** and so if a schema change occurs, SQLITE_SCHEMA is returned by
|
||||
** sqlite3_step(). In the new version, the original SQL text is retained
|
||||
** and the statement is automatically recompiled if an schema change
|
||||
** occurs.
|
||||
*/
|
||||
int sqlite3_prepare(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare(db,zSql,nBytes,0,ppStmt,pzTail);
|
||||
}
|
||||
int sqlite3_prepare_v2(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare(db,zSql,nBytes,1,ppStmt,pzTail);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/*
|
||||
** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
|
||||
*/
|
||||
static int sqlite3Prepare16(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
/* This function currently works by first transforming the UTF-16
|
||||
** encoded string to UTF-8, then invoking sqlite3_prepare(). The
|
||||
** tricky bit is figuring out the pointer to return in *pzTail.
|
||||
*/
|
||||
char *zSql8;
|
||||
const char *zTail8 = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( sqlite3SafetyCheck(db) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
zSql8 = sqlite3utf16to8(zSql, nBytes);
|
||||
if( zSql8 ){
|
||||
rc = sqlite3Prepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
|
||||
}
|
||||
|
||||
if( zTail8 && pzTail ){
|
||||
/* If sqlite3_prepare returns a tail pointer, we calculate the
|
||||
** equivalent pointer into the UTF-16 string by counting the unicode
|
||||
** 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);
|
||||
*pzTail = (u8 *)zSql + sqlite3utf16ByteLen(zSql, chars_parsed);
|
||||
}
|
||||
sqliteFree(zSql8);
|
||||
return sqlite3ApiExit(db, rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Two versions of the official API. Legacy and new use. In the legacy
|
||||
** version, the original SQL text is not saved in the prepared statement
|
||||
** and so if a schema change occurs, SQLITE_SCHEMA is returned by
|
||||
** sqlite3_step(). In the new version, the original SQL text is retained
|
||||
** and the statement is automatically recompiled if an schema change
|
||||
** occurs.
|
||||
*/
|
||||
int sqlite3_prepare16(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare16(db,zSql,nBytes,0,ppStmt,pzTail);
|
||||
}
|
||||
int sqlite3_prepare16_v2(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare16(db,zSql,nBytes,1,ppStmt,pzTail);
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
|
@ -1,863 +0,0 @@
|
|||
/*
|
||||
** The "printf" code that follows dates from the 1980's. It is in
|
||||
** the public domain. The original comments are included here for
|
||||
** completeness. They are very out-of-date but might be useful as
|
||||
** an historical reference. Most of the "enhancements" have been backed
|
||||
** out so that the functionality is now the same as standard printf().
|
||||
**
|
||||
**************************************************************************
|
||||
**
|
||||
** The following modules is an enhanced replacement for the "printf" subroutines
|
||||
** found in the standard C library. The following enhancements are
|
||||
** supported:
|
||||
**
|
||||
** + Additional functions. The standard set of "printf" functions
|
||||
** includes printf, fprintf, sprintf, vprintf, vfprintf, and
|
||||
** vsprintf. This module adds the following:
|
||||
**
|
||||
** * snprintf -- Works like sprintf, but has an extra argument
|
||||
** which is the size of the buffer written to.
|
||||
**
|
||||
** * mprintf -- Similar to sprintf. Writes output to memory
|
||||
** obtained from malloc.
|
||||
**
|
||||
** * xprintf -- Calls a function to dispose of output.
|
||||
**
|
||||
** * nprintf -- No output, but returns the number of characters
|
||||
** that would have been output by printf.
|
||||
**
|
||||
** * A v- version (ex: vsnprintf) of every function is also
|
||||
** supplied.
|
||||
**
|
||||
** + A few extensions to the formatting notation are supported:
|
||||
**
|
||||
** * The "=" flag (similar to "-") causes the output to be
|
||||
** be centered in the appropriately sized field.
|
||||
**
|
||||
** * The %b field outputs an integer in binary notation.
|
||||
**
|
||||
** * The %c field now accepts a precision. The character output
|
||||
** is repeated by the number of times the precision specifies.
|
||||
**
|
||||
** * The %' field works like %c, but takes as its character the
|
||||
** next character of the format string, instead of the next
|
||||
** argument. For example, printf("%.78'-") prints 78 minus
|
||||
** signs, the same as printf("%.78c",'-').
|
||||
**
|
||||
** + When compiled using GCC on a SPARC, this version of printf is
|
||||
** faster than the library printf for SUN OS 4.1.
|
||||
**
|
||||
** + All functions are fully reentrant.
|
||||
**
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Conversion types fall into various categories as defined by the
|
||||
** following enumeration.
|
||||
*/
|
||||
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
|
||||
#define etFLOAT 2 /* Floating point. %f */
|
||||
#define etEXP 3 /* Exponentional notation. %e and %E */
|
||||
#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
|
||||
#define etSIZE 5 /* Return number of characters processed so far. %n */
|
||||
#define etSTRING 6 /* Strings. %s */
|
||||
#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
|
||||
#define etPERCENT 8 /* Percent symbol. %% */
|
||||
#define etCHARX 9 /* Characters. %c */
|
||||
/* The rest are extensions, not normally found in printf() */
|
||||
#define etCHARLIT 10 /* Literal characters. %' */
|
||||
#define etSQLESCAPE 11 /* Strings with '\'' doubled. %q */
|
||||
#define etSQLESCAPE2 12 /* Strings with '\'' doubled and enclosed in '',
|
||||
NULL pointers replaced by SQL NULL. %Q */
|
||||
#define etTOKEN 13 /* a pointer to a Token structure */
|
||||
#define etSRCLIST 14 /* a pointer to a SrcList */
|
||||
#define etPOINTER 15 /* The %p conversion */
|
||||
|
||||
|
||||
/*
|
||||
** An "etByte" is an 8-bit unsigned value.
|
||||
*/
|
||||
typedef unsigned char etByte;
|
||||
|
||||
/*
|
||||
** Each builtin conversion character (ex: the 'd' in "%d") is described
|
||||
** by an instance of the following structure
|
||||
*/
|
||||
typedef struct et_info { /* Information about each format field */
|
||||
char fmttype; /* The format field code letter */
|
||||
etByte base; /* The base for radix conversion */
|
||||
etByte flags; /* One or more of FLAG_ constants below */
|
||||
etByte type; /* Conversion paradigm */
|
||||
etByte charset; /* Offset into aDigits[] of the digits string */
|
||||
etByte prefix; /* Offset into aPrefix[] of the prefix string */
|
||||
} et_info;
|
||||
|
||||
/*
|
||||
** Allowed values for et_info.flags
|
||||
*/
|
||||
#define FLAG_SIGNED 1 /* True if the value to convert is signed */
|
||||
#define FLAG_INTERN 2 /* True if for internal use only */
|
||||
#define FLAG_STRING 4 /* Allow infinity precision */
|
||||
|
||||
|
||||
/*
|
||||
** The following table is searched linearly, so it is good to put the
|
||||
** most frequently used conversion types first.
|
||||
*/
|
||||
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
|
||||
static const char aPrefix[] = "-x0\000X0";
|
||||
static const et_info fmtinfo[] = {
|
||||
{ 'd', 10, 1, etRADIX, 0, 0 },
|
||||
{ 's', 0, 4, etSTRING, 0, 0 },
|
||||
{ 'g', 0, 1, etGENERIC, 30, 0 },
|
||||
{ 'z', 0, 6, etDYNSTRING, 0, 0 },
|
||||
{ 'q', 0, 4, etSQLESCAPE, 0, 0 },
|
||||
{ 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
|
||||
{ 'c', 0, 0, etCHARX, 0, 0 },
|
||||
{ 'o', 8, 0, etRADIX, 0, 2 },
|
||||
{ 'u', 10, 0, etRADIX, 0, 0 },
|
||||
{ 'x', 16, 0, etRADIX, 16, 1 },
|
||||
{ 'X', 16, 0, etRADIX, 0, 4 },
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
{ 'f', 0, 1, etFLOAT, 0, 0 },
|
||||
{ 'e', 0, 1, etEXP, 30, 0 },
|
||||
{ 'E', 0, 1, etEXP, 14, 0 },
|
||||
{ 'G', 0, 1, etGENERIC, 14, 0 },
|
||||
#endif
|
||||
{ 'i', 10, 1, etRADIX, 0, 0 },
|
||||
{ 'n', 0, 0, etSIZE, 0, 0 },
|
||||
{ '%', 0, 0, etPERCENT, 0, 0 },
|
||||
{ 'p', 16, 0, etPOINTER, 0, 1 },
|
||||
{ 'T', 0, 2, etTOKEN, 0, 0 },
|
||||
{ 'S', 0, 2, etSRCLIST, 0, 0 },
|
||||
};
|
||||
#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
|
||||
|
||||
/*
|
||||
** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point
|
||||
** conversions will work.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
/*
|
||||
** "*val" is a double such that 0.1 <= *val < 10.0
|
||||
** Return the ascii code for the leading digit of *val, then
|
||||
** multiply "*val" by 10.0 to renormalize.
|
||||
**
|
||||
** Example:
|
||||
** input: *val = 3.14159
|
||||
** output: *val = 1.4159 function return = '3'
|
||||
**
|
||||
** The counter *cnt is incremented each time. After counter exceeds
|
||||
** 16 (the number of significant digits in a 64-bit float) '0' is
|
||||
** always returned.
|
||||
*/
|
||||
static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
int digit;
|
||||
LONGDOUBLE_TYPE d;
|
||||
if( (*cnt)++ >= 16 ) return '0';
|
||||
digit = (int)*val;
|
||||
d = digit;
|
||||
digit += '0';
|
||||
*val = (*val - d)*10.0;
|
||||
return digit;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
|
||||
/*
|
||||
** On machines with a small stack size, you can redefine the
|
||||
** SQLITE_PRINT_BUF_SIZE to be less than 350. But beware - for
|
||||
** smaller values some %f conversions may go into an infinite loop.
|
||||
*/
|
||||
#ifndef SQLITE_PRINT_BUF_SIZE
|
||||
# define SQLITE_PRINT_BUF_SIZE 350
|
||||
#endif
|
||||
#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
|
||||
|
||||
/*
|
||||
** The root program. All variations call this core.
|
||||
**
|
||||
** INPUTS:
|
||||
** func This is a pointer to a function taking three arguments
|
||||
** 1. A pointer to anything. Same as the "arg" parameter.
|
||||
** 2. A pointer to the list of characters to be output
|
||||
** (Note, this list is NOT null terminated.)
|
||||
** 3. An integer number of characters to be output.
|
||||
** (Note: This number might be zero.)
|
||||
**
|
||||
** arg This is the pointer to anything which will be passed as the
|
||||
** first argument to "func". Use it for whatever you like.
|
||||
**
|
||||
** fmt This is the format string, as in the usual print.
|
||||
**
|
||||
** ap This is a pointer to a list of arguments. Same as in
|
||||
** vfprint.
|
||||
**
|
||||
** OUTPUTS:
|
||||
** The return value is the total number of characters sent to
|
||||
** the function "func". Returns -1 on a error.
|
||||
**
|
||||
** Note that the order in which automatic variables are declared below
|
||||
** seems to make a big difference in determining how fast this beast
|
||||
** will run.
|
||||
*/
|
||||
static int vxprintf(
|
||||
void (*func)(void*,const char*,int), /* Consumer of text */
|
||||
void *arg, /* First argument to the consumer */
|
||||
int useExtended, /* Allow extended %-conversions */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
int c; /* Next character in the format string */
|
||||
char *bufpt; /* Pointer to the conversion buffer */
|
||||
int precision; /* Precision of the current field */
|
||||
int length; /* Length of the field */
|
||||
int idx; /* A general purpose loop counter */
|
||||
int count; /* Total number of characters output */
|
||||
int width; /* Width of the current field */
|
||||
etByte flag_leftjustify; /* True if "-" flag is present */
|
||||
etByte flag_plussign; /* True if "+" flag is present */
|
||||
etByte flag_blanksign; /* True if " " flag is present */
|
||||
etByte flag_alternateform; /* True if "#" flag is present */
|
||||
etByte flag_altform2; /* True if "!" flag is present */
|
||||
etByte flag_zeropad; /* True if field width constant starts with zero */
|
||||
etByte flag_long; /* True if "l" flag is present */
|
||||
etByte flag_longlong; /* True if the "ll" flag is present */
|
||||
etByte done; /* Loop termination flag */
|
||||
sqlite_uint64 longvalue; /* Value for integer types */
|
||||
LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
||||
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 */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
static const char spaces[] =
|
||||
" ";
|
||||
#define etSPACESIZE (sizeof(spaces)-1)
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
etByte flag_dp; /* True if decimal point should be shown */
|
||||
etByte flag_rtz; /* True if trailing zeros should be removed */
|
||||
etByte flag_exp; /* True to force display of the exponent */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
#endif
|
||||
|
||||
func(arg,"",0);
|
||||
count = length = 0;
|
||||
bufpt = 0;
|
||||
for(; (c=(*fmt))!=0; ++fmt){
|
||||
if( c!='%' ){
|
||||
int amt;
|
||||
bufpt = (char *)fmt;
|
||||
amt = 1;
|
||||
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
||||
(*func)(arg,bufpt,amt);
|
||||
count += amt;
|
||||
if( c==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
errorflag = 1;
|
||||
(*func)(arg,"%",1);
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
/* Find out what flags are present */
|
||||
flag_leftjustify = flag_plussign = flag_blanksign =
|
||||
flag_alternateform = flag_altform2 = flag_zeropad = 0;
|
||||
done = 0;
|
||||
do{
|
||||
switch( c ){
|
||||
case '-': flag_leftjustify = 1; break;
|
||||
case '+': flag_plussign = 1; break;
|
||||
case ' ': flag_blanksign = 1; break;
|
||||
case '#': flag_alternateform = 1; break;
|
||||
case '!': flag_altform2 = 1; break;
|
||||
case '0': flag_zeropad = 1; break;
|
||||
default: done = 1; break;
|
||||
}
|
||||
}while( !done && (c=(*++fmt))!=0 );
|
||||
/* Get the field width */
|
||||
width = 0;
|
||||
if( c=='*' ){
|
||||
width = va_arg(ap,int);
|
||||
if( width<0 ){
|
||||
flag_leftjustify = 1;
|
||||
width = -width;
|
||||
}
|
||||
c = *++fmt;
|
||||
}else{
|
||||
while( c>='0' && c<='9' ){
|
||||
width = width*10 + c - '0';
|
||||
c = *++fmt;
|
||||
}
|
||||
}
|
||||
if( width > etBUFSIZE-10 ){
|
||||
width = etBUFSIZE-10;
|
||||
}
|
||||
/* Get the precision */
|
||||
if( c=='.' ){
|
||||
precision = 0;
|
||||
c = *++fmt;
|
||||
if( c=='*' ){
|
||||
precision = va_arg(ap,int);
|
||||
if( precision<0 ) precision = -precision;
|
||||
c = *++fmt;
|
||||
}else{
|
||||
while( c>='0' && c<='9' ){
|
||||
precision = precision*10 + c - '0';
|
||||
c = *++fmt;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
precision = -1;
|
||||
}
|
||||
/* Get the conversion type modifier */
|
||||
if( c=='l' ){
|
||||
flag_long = 1;
|
||||
c = *++fmt;
|
||||
if( c=='l' ){
|
||||
flag_longlong = 1;
|
||||
c = *++fmt;
|
||||
}else{
|
||||
flag_longlong = 0;
|
||||
}
|
||||
}else{
|
||||
flag_long = flag_longlong = 0;
|
||||
}
|
||||
/* Fetch the info entry for the field */
|
||||
infop = 0;
|
||||
for(idx=0; idx<etNINFO; idx++){
|
||||
if( c==fmtinfo[idx].fmttype ){
|
||||
infop = &fmtinfo[idx];
|
||||
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
||||
xtype = infop->type;
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
zExtra = 0;
|
||||
if( infop==0 ){
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Limit the precision to prevent overflowing buf[] during conversion */
|
||||
if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
|
||||
precision = etBUFSIZE-40;
|
||||
}
|
||||
|
||||
/*
|
||||
** At this point, variables are initialized as follows:
|
||||
**
|
||||
** flag_alternateform TRUE if a '#' is present.
|
||||
** flag_altform2 TRUE if a '!' is present.
|
||||
** flag_plussign TRUE if a '+' is present.
|
||||
** flag_leftjustify TRUE if a '-' is present or if the
|
||||
** field width was negative.
|
||||
** flag_zeropad TRUE if the width began with 0.
|
||||
** flag_long TRUE if the letter 'l' (ell) prefixed
|
||||
** the conversion character.
|
||||
** flag_longlong TRUE if the letter 'll' (ell ell) prefixed
|
||||
** the conversion character.
|
||||
** flag_blanksign TRUE if a ' ' is present.
|
||||
** width The specified field width. This is
|
||||
** always non-negative. Zero is the default.
|
||||
** precision The specified precision. The default
|
||||
** is -1.
|
||||
** xtype The class of the conversion.
|
||||
** infop Pointer to the appropriate info struct.
|
||||
*/
|
||||
switch( xtype ){
|
||||
case etPOINTER:
|
||||
flag_longlong = sizeof(char*)==sizeof(i64);
|
||||
flag_long = sizeof(char*)==sizeof(long int);
|
||||
/* Fall through into the next case */
|
||||
case etRADIX:
|
||||
if( infop->flags & FLAG_SIGNED ){
|
||||
i64 v;
|
||||
if( flag_longlong ) v = va_arg(ap,i64);
|
||||
else if( flag_long ) v = va_arg(ap,long int);
|
||||
else v = va_arg(ap,int);
|
||||
if( v<0 ){
|
||||
longvalue = -v;
|
||||
prefix = '-';
|
||||
}else{
|
||||
longvalue = v;
|
||||
if( flag_plussign ) prefix = '+';
|
||||
else if( flag_blanksign ) prefix = ' ';
|
||||
else prefix = 0;
|
||||
}
|
||||
}else{
|
||||
if( flag_longlong ) longvalue = va_arg(ap,u64);
|
||||
else if( flag_long ) longvalue = va_arg(ap,unsigned long int);
|
||||
else longvalue = va_arg(ap,unsigned int);
|
||||
prefix = 0;
|
||||
}
|
||||
if( longvalue==0 ) flag_alternateform = 0;
|
||||
if( flag_zeropad && precision<width-(prefix!=0) ){
|
||||
precision = width-(prefix!=0);
|
||||
}
|
||||
bufpt = &buf[etBUFSIZE-1];
|
||||
{
|
||||
register const char *cset; /* Use registers for speed */
|
||||
register int base;
|
||||
cset = &aDigits[infop->charset];
|
||||
base = infop->base;
|
||||
do{ /* Convert to ascii */
|
||||
*(--bufpt) = cset[longvalue%base];
|
||||
longvalue = longvalue/base;
|
||||
}while( longvalue>0 );
|
||||
}
|
||||
length = &buf[etBUFSIZE-1]-bufpt;
|
||||
for(idx=precision-length; idx>0; idx--){
|
||||
*(--bufpt) = '0'; /* Zero pad */
|
||||
}
|
||||
if( prefix ) *(--bufpt) = prefix; /* Add sign */
|
||||
if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
|
||||
const char *pre;
|
||||
char x;
|
||||
pre = &aPrefix[infop->prefix];
|
||||
if( *bufpt!=pre[0] ){
|
||||
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
|
||||
}
|
||||
}
|
||||
length = &buf[etBUFSIZE-1]-bufpt;
|
||||
break;
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
case etGENERIC:
|
||||
realvalue = va_arg(ap,double);
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
if( precision<0 ) precision = 6; /* Set default precision */
|
||||
if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10;
|
||||
if( realvalue<0.0 ){
|
||||
realvalue = -realvalue;
|
||||
prefix = '-';
|
||||
}else{
|
||||
if( flag_plussign ) prefix = '+';
|
||||
else if( flag_blanksign ) prefix = ' ';
|
||||
else prefix = 0;
|
||||
}
|
||||
if( xtype==etGENERIC && precision>0 ) precision--;
|
||||
#if 0
|
||||
/* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
|
||||
for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
|
||||
#else
|
||||
/* It makes more sense to use 0.5 */
|
||||
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
|
||||
#endif
|
||||
if( xtype==etFLOAT ) realvalue += rounder;
|
||||
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
||||
exp = 0;
|
||||
if( realvalue>0.0 ){
|
||||
while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
|
||||
while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
|
||||
while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
|
||||
while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
|
||||
while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
|
||||
if( exp>350 || exp<-350 ){
|
||||
bufpt = "NaN";
|
||||
length = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bufpt = buf;
|
||||
/*
|
||||
** If the field type is etGENERIC, then convert to either etEXP
|
||||
** or etFLOAT, as appropriate.
|
||||
*/
|
||||
flag_exp = xtype==etEXP;
|
||||
if( xtype!=etFLOAT ){
|
||||
realvalue += rounder;
|
||||
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
|
||||
}
|
||||
if( xtype==etGENERIC ){
|
||||
flag_rtz = !flag_alternateform;
|
||||
if( exp<-4 || exp>precision ){
|
||||
xtype = etEXP;
|
||||
}else{
|
||||
precision = precision - exp;
|
||||
xtype = etFLOAT;
|
||||
}
|
||||
}else{
|
||||
flag_rtz = 0;
|
||||
}
|
||||
if( xtype==etEXP ){
|
||||
e2 = 0;
|
||||
}else{
|
||||
e2 = exp;
|
||||
}
|
||||
nsd = 0;
|
||||
flag_dp = (precision>0) | flag_alternateform | flag_altform2;
|
||||
/* The sign in front of the number */
|
||||
if( prefix ){
|
||||
*(bufpt++) = prefix;
|
||||
}
|
||||
/* Digits prior to the decimal point */
|
||||
if( e2<0 ){
|
||||
*(bufpt++) = '0';
|
||||
}else{
|
||||
for(; e2>=0; e2--){
|
||||
*(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
}
|
||||
}
|
||||
/* The decimal point */
|
||||
if( flag_dp ){
|
||||
*(bufpt++) = '.';
|
||||
}
|
||||
/* "0" digits after the decimal point but before the first
|
||||
** significant digit of the number */
|
||||
for(e2++; e2<0 && precision>0; precision--, e2++){
|
||||
*(bufpt++) = '0';
|
||||
}
|
||||
/* Significant digits after the decimal point */
|
||||
while( (precision--)>0 ){
|
||||
*(bufpt++) = et_getdigit(&realvalue,&nsd);
|
||||
}
|
||||
/* Remove trailing zeros and the "." if no digits follow the "." */
|
||||
if( flag_rtz && flag_dp ){
|
||||
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
|
||||
assert( bufpt>buf );
|
||||
if( bufpt[-1]=='.' ){
|
||||
if( flag_altform2 ){
|
||||
*(bufpt++) = '0';
|
||||
}else{
|
||||
*(--bufpt) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add the "eNNN" suffix */
|
||||
if( flag_exp || (xtype==etEXP && exp) ){
|
||||
*(bufpt++) = aDigits[infop->charset];
|
||||
if( exp<0 ){
|
||||
*(bufpt++) = '-'; exp = -exp;
|
||||
}else{
|
||||
*(bufpt++) = '+';
|
||||
}
|
||||
if( exp>=100 ){
|
||||
*(bufpt++) = (exp/100)+'0'; /* 100's digit */
|
||||
exp %= 100;
|
||||
}
|
||||
*(bufpt++) = exp/10+'0'; /* 10's digit */
|
||||
*(bufpt++) = 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;
|
||||
bufpt = buf;
|
||||
|
||||
/* Special case: Add leading zeros if the flag_zeropad flag is
|
||||
** set and we are not left justified */
|
||||
if( flag_zeropad && !flag_leftjustify && length < width){
|
||||
int i;
|
||||
int nPad = width - length;
|
||||
for(i=width; i>=nPad; i--){
|
||||
bufpt[i] = bufpt[i-nPad];
|
||||
}
|
||||
i = prefix!=0;
|
||||
while( nPad-- ) bufpt[i++] = '0';
|
||||
length = width;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case etSIZE:
|
||||
*(va_arg(ap,int*)) = count;
|
||||
length = width = 0;
|
||||
break;
|
||||
case etPERCENT:
|
||||
buf[0] = '%';
|
||||
bufpt = buf;
|
||||
length = 1;
|
||||
break;
|
||||
case etCHARLIT:
|
||||
case etCHARX:
|
||||
c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
|
||||
if( precision>=0 ){
|
||||
for(idx=1; idx<precision; idx++) buf[idx] = c;
|
||||
length = precision;
|
||||
}else{
|
||||
length =1;
|
||||
}
|
||||
bufpt = buf;
|
||||
break;
|
||||
case etSTRING:
|
||||
case etDYNSTRING:
|
||||
bufpt = va_arg(ap,char*);
|
||||
if( bufpt==0 ){
|
||||
bufpt = "";
|
||||
}else if( xtype==etDYNSTRING ){
|
||||
zExtra = bufpt;
|
||||
}
|
||||
length = strlen(bufpt);
|
||||
if( precision>=0 && precision<length ) length = precision;
|
||||
break;
|
||||
case etSQLESCAPE:
|
||||
case etSQLESCAPE2: {
|
||||
int i, j, n, ch, isnull;
|
||||
int needQuote;
|
||||
char *escarg = va_arg(ap,char*);
|
||||
isnull = escarg==0;
|
||||
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
|
||||
for(i=n=0; (ch=escarg[i])!=0; i++){
|
||||
if( ch=='\'' ) n++;
|
||||
}
|
||||
needQuote = !isnull && xtype==etSQLESCAPE2;
|
||||
n += i + 1 + needQuote*2;
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqliteMalloc( n );
|
||||
if( bufpt==0 ) return -1;
|
||||
}else{
|
||||
bufpt = buf;
|
||||
}
|
||||
j = 0;
|
||||
if( needQuote ) bufpt[j++] = '\'';
|
||||
for(i=0; (ch=escarg[i])!=0; i++){
|
||||
bufpt[j++] = ch;
|
||||
if( ch=='\'' ) bufpt[j++] = ch;
|
||||
}
|
||||
if( needQuote ) bufpt[j++] = '\'';
|
||||
bufpt[j] = 0;
|
||||
length = j;
|
||||
/* The precision is ignored on %q and %Q */
|
||||
/* if( precision>=0 && precision<length ) length = precision; */
|
||||
break;
|
||||
}
|
||||
case etTOKEN: {
|
||||
Token *pToken = va_arg(ap, Token*);
|
||||
if( pToken && pToken->z ){
|
||||
(*func)(arg, (char*)pToken->z, pToken->n);
|
||||
}
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
case etSRCLIST: {
|
||||
SrcList *pSrc = va_arg(ap, SrcList*);
|
||||
int k = va_arg(ap, int);
|
||||
struct SrcList_item *pItem = &pSrc->a[k];
|
||||
assert( k>=0 && k<pSrc->nSrc );
|
||||
if( pItem->zDatabase && pItem->zDatabase[0] ){
|
||||
(*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase));
|
||||
(*func)(arg, ".", 1);
|
||||
}
|
||||
(*func)(arg, pItem->zName, strlen(pItem->zName));
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
}/* End switch over the format type */
|
||||
/*
|
||||
** The text of the conversion is pointed to by "bufpt" and is
|
||||
** "length" characters long. The field width is "width". Do
|
||||
** the output.
|
||||
*/
|
||||
if( !flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
nspace -= etSPACESIZE;
|
||||
}
|
||||
if( nspace>0 ) (*func)(arg,spaces,nspace);
|
||||
}
|
||||
}
|
||||
if( length>0 ){
|
||||
(*func)(arg,bufpt,length);
|
||||
count += length;
|
||||
}
|
||||
if( flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
nspace -= etSPACESIZE;
|
||||
}
|
||||
if( nspace>0 ) (*func)(arg,spaces,nspace);
|
||||
}
|
||||
}
|
||||
if( zExtra ){
|
||||
sqliteFree(zExtra);
|
||||
}
|
||||
}/* End for loop over the format string */
|
||||
return errorflag ? -1 : count;
|
||||
} /* End of function */
|
||||
|
||||
|
||||
/* This structure is used to store state information about the
|
||||
** write to memory that is currently in progress.
|
||||
*/
|
||||
struct sgMprintf {
|
||||
char *zBase; /* A base allocation */
|
||||
char *zText; /* The string collected so far */
|
||||
int nChar; /* Length of the string so far */
|
||||
int nTotal; /* Output size if unconstrained */
|
||||
int nAlloc; /* Amount of space allocated in zText */
|
||||
void *(*xRealloc)(void*,int); /* Function used to realloc memory */
|
||||
};
|
||||
|
||||
/*
|
||||
** This function implements the callback from vxprintf.
|
||||
**
|
||||
** This routine add nNewChar characters of text in zNewText to
|
||||
** the sgMprintf structure pointed to by "arg".
|
||||
*/
|
||||
static void mout(void *arg, const char *zNewText, int nNewChar){
|
||||
struct sgMprintf *pM = (struct sgMprintf*)arg;
|
||||
pM->nTotal += nNewChar;
|
||||
if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
|
||||
if( pM->xRealloc==0 ){
|
||||
nNewChar = pM->nAlloc - pM->nChar - 1;
|
||||
}else{
|
||||
pM->nAlloc = pM->nChar + nNewChar*2 + 1;
|
||||
if( pM->zText==pM->zBase ){
|
||||
pM->zText = pM->xRealloc(0, pM->nAlloc);
|
||||
if( pM->zText && pM->nChar ){
|
||||
memcpy(pM->zText, pM->zBase, pM->nChar);
|
||||
}
|
||||
}else{
|
||||
char *zNew;
|
||||
zNew = pM->xRealloc(pM->zText, pM->nAlloc);
|
||||
if( zNew ){
|
||||
pM->zText = zNew;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pM->zText ){
|
||||
if( nNewChar>0 ){
|
||||
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
|
||||
pM->nChar += nNewChar;
|
||||
}
|
||||
pM->zText[pM->nChar] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is a wrapper around xprintf() that invokes mout() as
|
||||
** the consumer.
|
||||
*/
|
||||
static char *base_vprintf(
|
||||
void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */
|
||||
int useInternal, /* Use internal %-conversions if true */
|
||||
char *zInitBuf, /* Initially write here, before mallocing */
|
||||
int nInitBuf, /* Size of zInitBuf[] */
|
||||
const char *zFormat, /* format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
struct sgMprintf sM;
|
||||
sM.zBase = sM.zText = zInitBuf;
|
||||
sM.nChar = sM.nTotal = 0;
|
||||
sM.nAlloc = nInitBuf;
|
||||
sM.xRealloc = xRealloc;
|
||||
vxprintf(mout, &sM, useInternal, zFormat, ap);
|
||||
if( xRealloc ){
|
||||
if( sM.zText==sM.zBase ){
|
||||
sM.zText = xRealloc(0, sM.nChar+1);
|
||||
if( sM.zText ){
|
||||
memcpy(sM.zText, sM.zBase, sM.nChar+1);
|
||||
}
|
||||
}else if( sM.nAlloc>sM.nChar+10 ){
|
||||
char *zNew = xRealloc(sM.zText, sM.nChar+1);
|
||||
if( zNew ){
|
||||
sM.zText = zNew;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sM.zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** Realloc that is a real function, not a macro.
|
||||
*/
|
||||
static void *printf_realloc(void *old, int size){
|
||||
return sqliteRealloc(old,size);
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqliteMalloc(). Use the internal
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqlite3VMPrintf(const char *zFormat, va_list ap){
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqliteMalloc(). Use the internal
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqlite3MPrintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
va_start(ap, zFormat);
|
||||
z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
|
||||
va_end(ap);
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqlite3_malloc(). Omit the internal
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
return base_vprintf(sqlite3_realloc, 0, zBase, sizeof(zBase), zFormat, ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqlite3_malloc()(). Omit the internal
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqlite3_mprintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
va_start(ap, zFormat);
|
||||
z = base_vprintf(sqlite3_realloc, 0, zBase, sizeof(zBase), zFormat, ap);
|
||||
va_end(ap);
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_snprintf() works like snprintf() except that it ignores the
|
||||
** current locale settings. This is important for SQLite because we
|
||||
** are not able to use a "," as the decimal point in place of "." as
|
||||
** specified by some locales.
|
||||
*/
|
||||
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
char *z;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap,zFormat);
|
||||
z = base_vprintf(0, 0, zBuf, n, zFormat, ap);
|
||||
va_end(ap);
|
||||
return z;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
||||
/*
|
||||
** A version of printf() that understands %lld. Used for debugging.
|
||||
** The printf() built into some versions of windows does not understand %lld
|
||||
** and segfaults if you give it a long long int.
|
||||
*/
|
||||
void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
extern int getpid(void);
|
||||
va_list ap;
|
||||
char zBuf[500];
|
||||
va_start(ap, zFormat);
|
||||
base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
|
||||
va_end(ap);
|
||||
fprintf(stdout,"%s", zBuf);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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 to implement a pseudo-random number
|
||||
** generator (PRNG) for SQLite.
|
||||
**
|
||||
** 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.16 2007/01/05 14:38:56 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
|
||||
|
||||
/*
|
||||
** Get a single 8-bit random value from the RC4 PRNG. The Mutex
|
||||
** must be held while executing this routine.
|
||||
**
|
||||
** Why not just use a library random generator like lrand48() for this?
|
||||
** Because the OP_NewRowid opcode in the VDBE depends on having a very
|
||||
** good source of random numbers. The lrand48() library function may
|
||||
** well be good enough. But maybe not. Or maybe lrand48() has some
|
||||
** subtle problems on some systems that could cause problems. It is hard
|
||||
** to know. To minimize the risk of problems due to bad lrand48()
|
||||
** implementations, SQLite uses this random number generator based
|
||||
** on RC4, which we know works very well.
|
||||
**
|
||||
** (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){
|
||||
unsigned char t;
|
||||
|
||||
/* All threads share a single random number generator.
|
||||
** This structure is the current state of the generator.
|
||||
*/
|
||||
static struct {
|
||||
unsigned char isInit; /* True if initialized */
|
||||
unsigned char i, j; /* State variables */
|
||||
unsigned char s[256]; /* State variables */
|
||||
} prng;
|
||||
|
||||
/* 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
|
||||
** trying to do secure encryption or anything like that...
|
||||
**
|
||||
** Nothing in this file or anywhere else in SQLite does any kind of
|
||||
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
|
||||
** number generator) not as an encryption device.
|
||||
*/
|
||||
if( !prng.isInit ){
|
||||
int i;
|
||||
char k[256];
|
||||
prng.j = 0;
|
||||
prng.i = 0;
|
||||
sqlite3OsRandomSeed(k);
|
||||
for(i=0; i<256; i++){
|
||||
prng.s[i] = i;
|
||||
}
|
||||
for(i=0; i<256; i++){
|
||||
prng.j += prng.s[i] + k[i];
|
||||
t = prng.s[prng.j];
|
||||
prng.s[prng.j] = prng.s[i];
|
||||
prng.s[i] = t;
|
||||
}
|
||||
prng.isInit = 1;
|
||||
}
|
||||
|
||||
/* Generate and return single random byte
|
||||
*/
|
||||
prng.i++;
|
||||
t = prng.s[prng.i];
|
||||
prng.j += t;
|
||||
prng.s[prng.i] = prng.s[prng.j];
|
||||
prng.s[prng.j] = t;
|
||||
t += prng.s[prng.i];
|
||||
return prng.s[t];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return N random bytes.
|
||||
*/
|
||||
void sqlite3Randomness(int N, void *pBuf){
|
||||
unsigned char *zBuf = pBuf;
|
||||
sqlite3OsEnterMutex();
|
||||
while( N-- ){
|
||||
*(zBuf++) = randomByte();
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,282 +0,0 @@
|
|||
/*
|
||||
** 2006 June 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 header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
**
|
||||
** @(#) $Id: sqlite3ext.h,v 1.8 2007/01/09 14:37:18 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE3EXT_H_
|
||||
#define _SQLITE3EXT_H_
|
||||
#include "sqlite3.h"
|
||||
|
||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
|
||||
/*
|
||||
** The following structure hold pointers to all of the SQLite API
|
||||
** routines.
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*snprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char * (*vmprintf)(const char*,va_list);
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
};
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected throught the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#ifndef SQLITE_CORE
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#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
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#endif /* SQLITE_CORE */
|
||||
|
||||
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api;
|
||||
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
|
||||
|
||||
#endif /* _SQLITE3EXT_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** 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 sqlite3_get_table() and sqlite3_free_table()
|
||||
** interface routines. These are just wrappers around the main
|
||||
** interface routine of sqlite3_exec().
|
||||
**
|
||||
** These routines are in a separate files so that they will not be linked
|
||||
** if they are not used.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_GET_TABLE
|
||||
|
||||
/*
|
||||
** This structure is used to pass data from sqlite3_get_table() through
|
||||
** to the callback function is uses to build the result.
|
||||
*/
|
||||
typedef struct TabResult {
|
||||
char **azResult;
|
||||
char *zErrMsg;
|
||||
int nResult;
|
||||
int nAlloc;
|
||||
int nRow;
|
||||
int nColumn;
|
||||
int nData;
|
||||
int rc;
|
||||
} TabResult;
|
||||
|
||||
/*
|
||||
** This routine is called once for each row in the result table. Its job
|
||||
** is to fill in the TabResult structure appropriately, allocating new
|
||||
** memory as necessary.
|
||||
*/
|
||||
static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
|
||||
TabResult *p = (TabResult*)pArg;
|
||||
int need;
|
||||
int i;
|
||||
char *z;
|
||||
|
||||
/* Make sure there is enough space in p->azResult to hold everything
|
||||
** we need to remember from this invocation of the callback.
|
||||
*/
|
||||
if( p->nRow==0 && argv!=0 ){
|
||||
need = nCol*2;
|
||||
}else{
|
||||
need = nCol;
|
||||
}
|
||||
if( p->nData + need >= p->nAlloc ){
|
||||
char **azNew;
|
||||
p->nAlloc = p->nAlloc*2 + need + 1;
|
||||
azNew = sqlite3_realloc( p->azResult, sizeof(char*)*p->nAlloc );
|
||||
if( azNew==0 ) goto malloc_failed;
|
||||
p->azResult = azNew;
|
||||
}
|
||||
|
||||
/* If this is the first row, then generate an extra row containing
|
||||
** the names of all columns.
|
||||
*/
|
||||
if( p->nRow==0 ){
|
||||
p->nColumn = nCol;
|
||||
for(i=0; i<nCol; i++){
|
||||
if( colv[i]==0 ){
|
||||
z = sqlite3_mprintf("");
|
||||
}else{
|
||||
z = sqlite3_mprintf("%s", colv[i]);
|
||||
}
|
||||
p->azResult[p->nData++] = z;
|
||||
}
|
||||
}else if( p->nColumn!=nCol ){
|
||||
sqlite3SetString(&p->zErrMsg,
|
||||
"sqlite3_get_table() called with two or more incompatible queries",
|
||||
(char*)0);
|
||||
p->rc = SQLITE_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Copy over the row data
|
||||
*/
|
||||
if( argv!=0 ){
|
||||
for(i=0; i<nCol; i++){
|
||||
if( argv[i]==0 ){
|
||||
z = 0;
|
||||
}else{
|
||||
z = sqlite3_malloc( strlen(argv[i])+1 );
|
||||
if( z==0 ) goto malloc_failed;
|
||||
strcpy(z, argv[i]);
|
||||
}
|
||||
p->azResult[p->nData++] = z;
|
||||
}
|
||||
p->nRow++;
|
||||
}
|
||||
return 0;
|
||||
|
||||
malloc_failed:
|
||||
p->rc = SQLITE_NOMEM;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Query the database. But instead of invoking a callback for each row,
|
||||
** malloc() for space to hold the result and return the entire results
|
||||
** at the conclusion of the call.
|
||||
**
|
||||
** The result that is written to ***pazResult is held in memory obtained
|
||||
** from malloc(). But the caller cannot free this memory directly.
|
||||
** Instead, the entire table should be passed to sqlite3_free_table() when
|
||||
** the calling procedure is finished using it.
|
||||
*/
|
||||
int sqlite3_get_table(
|
||||
sqlite3 *db, /* The database on which the SQL executes */
|
||||
const char *zSql, /* The SQL to be executed */
|
||||
char ***pazResult, /* Write the result table here */
|
||||
int *pnRow, /* Write the number of rows in the result here */
|
||||
int *pnColumn, /* Write the number of columns of result here */
|
||||
char **pzErrMsg /* Write error messages here */
|
||||
){
|
||||
int rc;
|
||||
TabResult res;
|
||||
if( pazResult==0 ){ return SQLITE_ERROR; }
|
||||
*pazResult = 0;
|
||||
if( pnColumn ) *pnColumn = 0;
|
||||
if( pnRow ) *pnRow = 0;
|
||||
res.zErrMsg = 0;
|
||||
res.nResult = 0;
|
||||
res.nRow = 0;
|
||||
res.nColumn = 0;
|
||||
res.nData = 1;
|
||||
res.nAlloc = 20;
|
||||
res.rc = SQLITE_OK;
|
||||
res.azResult = sqlite3_malloc( sizeof(char*)*res.nAlloc );
|
||||
if( res.azResult==0 ) return SQLITE_NOMEM;
|
||||
res.azResult[0] = 0;
|
||||
rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
|
||||
if( res.azResult ){
|
||||
assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
|
||||
res.azResult[0] = (char*)(long)res.nData;
|
||||
}
|
||||
if( (rc&0xff)==SQLITE_ABORT ){
|
||||
sqlite3_free_table(&res.azResult[1]);
|
||||
if( res.zErrMsg ){
|
||||
if( pzErrMsg ){
|
||||
sqlite3_free(*pzErrMsg);
|
||||
*pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);
|
||||
}
|
||||
sqliteFree(res.zErrMsg);
|
||||
}
|
||||
db->errCode = res.rc;
|
||||
return res.rc & db->errMask;
|
||||
}
|
||||
sqliteFree(res.zErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free_table(&res.azResult[1]);
|
||||
return rc & db->errMask;
|
||||
}
|
||||
if( res.nAlloc>res.nData ){
|
||||
char **azNew;
|
||||
azNew = sqlite3_realloc( res.azResult, sizeof(char*)*(res.nData+1) );
|
||||
if( azNew==0 ){
|
||||
sqlite3_free_table(&res.azResult[1]);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
res.nAlloc = res.nData+1;
|
||||
res.azResult = azNew;
|
||||
}
|
||||
*pazResult = &res.azResult[1];
|
||||
if( pnColumn ) *pnColumn = res.nColumn;
|
||||
if( pnRow ) *pnRow = res.nRow;
|
||||
return rc & db->errMask;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine frees the space the sqlite3_get_table() malloced.
|
||||
*/
|
||||
void sqlite3_free_table(
|
||||
char **azResult /* Result returned from from sqlite3_get_table() */
|
||||
){
|
||||
if( azResult ){
|
||||
int i, n;
|
||||
azResult--;
|
||||
if( azResult==0 ) return;
|
||||
n = (long) azResult[0];
|
||||
for(i=1; i<n; i++){ if( azResult[i] ) sqlite3_free(azResult[i]); }
|
||||
sqlite3_free(azResult);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_GET_TABLE */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,606 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the pager.c module in SQLite. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test2.c,v 1.39 2006/01/06 14:32:20 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
#include "pager.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Interpret an SQLite error number
|
||||
*/
|
||||
static char *errorName(int rc){
|
||||
char *zName;
|
||||
switch( rc ){
|
||||
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_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;
|
||||
default: zName = "SQLITE_Unknown"; break;
|
||||
}
|
||||
return zName;
|
||||
}
|
||||
|
||||
/*
|
||||
** Page size and reserved size used for testing.
|
||||
*/
|
||||
static int test_pagesize = 1024;
|
||||
|
||||
/*
|
||||
** Usage: pager_open FILENAME N-PAGE
|
||||
**
|
||||
** Open a new pager
|
||||
*/
|
||||
static int pager_open(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int nPage;
|
||||
int rc;
|
||||
char zBuf[100];
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" FILENAME N-PAGE\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
|
||||
rc = sqlite3pager_open(&pPager, argv[1], 0, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3pager_set_cachesize(pPager, nPage);
|
||||
sqlite3pager_set_pagesize(pPager, test_pagesize);
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPager);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_close ID
|
||||
**
|
||||
** Close the given pager.
|
||||
*/
|
||||
static int pager_close(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_close(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_rollback ID
|
||||
**
|
||||
** Rollback changes
|
||||
*/
|
||||
static int pager_rollback(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_rollback(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_commit ID
|
||||
**
|
||||
** Commit all changes
|
||||
*/
|
||||
static int pager_commit(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_commit(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_stmt_begin ID
|
||||
**
|
||||
** Start a new checkpoint.
|
||||
*/
|
||||
static int pager_stmt_begin(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_stmt_begin(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_stmt_rollback ID
|
||||
**
|
||||
** Rollback changes to a checkpoint
|
||||
*/
|
||||
static int pager_stmt_rollback(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_stmt_rollback(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_stmt_commit ID
|
||||
**
|
||||
** Commit changes to a checkpoint
|
||||
*/
|
||||
static int pager_stmt_commit(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_stmt_commit(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_stats ID
|
||||
**
|
||||
** Return pager statistics.
|
||||
*/
|
||||
static int pager_stats(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
int i, *a;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
a = sqlite3pager_stats(pPager);
|
||||
for(i=0; i<9; i++){
|
||||
static char *zName[] = {
|
||||
"ref", "page", "max", "size", "state", "err",
|
||||
"hit", "miss", "ovfl",
|
||||
};
|
||||
char zBuf[100];
|
||||
Tcl_AppendElement(interp, zName[i]);
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",a[i]);
|
||||
Tcl_AppendElement(interp, zBuf);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_pagecount ID
|
||||
**
|
||||
** Return the size of the database file.
|
||||
*/
|
||||
static int pager_pagecount(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
char zBuf[100];
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",sqlite3pager_pagecount(pPager));
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: page_get ID PGNO
|
||||
**
|
||||
** Return a pointer to a page from the database.
|
||||
*/
|
||||
static int page_get(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
char zBuf[100];
|
||||
void *pPage;
|
||||
int pgno;
|
||||
int rc;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID PGNO\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
|
||||
rc = sqlite3pager_get(pPager, pgno, &pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: page_lookup ID PGNO
|
||||
**
|
||||
** Return a pointer to a page if the page is already in cache.
|
||||
** If not in cache, return an empty string.
|
||||
*/
|
||||
static int page_lookup(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Pager *pPager;
|
||||
char zBuf[100];
|
||||
void *pPage;
|
||||
int pgno;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID PGNO\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
|
||||
pPage = sqlite3pager_lookup(pPager, pgno);
|
||||
if( pPage ){
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: pager_truncate ID PGNO
|
||||
*/
|
||||
static int pager_truncate(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
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],
|
||||
" ID PGNO\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3TextToPtr(argv[1]);
|
||||
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
|
||||
rc = sqlite3pager_truncate(pPager, pgno);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: page_unref PAGE
|
||||
**
|
||||
** Drop a pointer to a page.
|
||||
*/
|
||||
static int page_unref(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
void *pPage;
|
||||
int rc;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" PAGE\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPage = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_unref(pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: page_read PAGE
|
||||
**
|
||||
** Return the content of a page
|
||||
*/
|
||||
static int page_read(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
char zBuf[100];
|
||||
void *pPage;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" PAGE\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPage = sqlite3TextToPtr(argv[1]);
|
||||
memcpy(zBuf, pPage, sizeof(zBuf));
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: page_number PAGE
|
||||
**
|
||||
** Return the page number for a page.
|
||||
*/
|
||||
static int page_number(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
char zBuf[100];
|
||||
void *pPage;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" PAGE\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPage = sqlite3TextToPtr(argv[1]);
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", sqlite3pager_pagenumber(pPage));
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: page_write PAGE DATA
|
||||
**
|
||||
** Write something into a page.
|
||||
*/
|
||||
static int page_write(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
void *pPage;
|
||||
int rc;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" PAGE DATA\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPage = sqlite3TextToPtr(argv[1]);
|
||||
rc = sqlite3pager_write(pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
strncpy((char*)pPage, argv[2], test_pagesize-1);
|
||||
((char*)pPage)[test_pagesize-1] = 0;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
/*
|
||||
** Usage: fake_big_file N FILENAME
|
||||
**
|
||||
** Write a few bytes at the N megabyte point of FILENAME. This will
|
||||
** create a large file. If the file was a valid SQLite database, then
|
||||
** the next time the database is opened, SQLite will begin allocating
|
||||
** new pages after N. If N is 2096 or bigger, this will test the
|
||||
** ability of SQLite to write to large files.
|
||||
*/
|
||||
static int fake_big_file(
|
||||
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 rc;
|
||||
int n;
|
||||
i64 offset;
|
||||
OsFile *fd = 0;
|
||||
int readOnly = 0;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" N-MEGABYTES FILE\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
|
||||
rc = sqlite3OsOpenReadWrite(argv[2], &fd, &readOnly);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
offset = n;
|
||||
offset *= 1024*1024;
|
||||
rc = sqlite3OsSeek(fd, offset);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "seek failed: ", errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3OsWrite(fd, "Hello, World!", 14);
|
||||
sqlite3OsClose(&fd);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest2_Init(Tcl_Interp *interp){
|
||||
extern int sqlite3_io_error_pending;
|
||||
extern int sqlite3_io_error_hit;
|
||||
extern int sqlite3_diskfull_pending;
|
||||
extern int sqlite3_diskfull;
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_CmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "pager_open", (Tcl_CmdProc*)pager_open },
|
||||
{ "pager_close", (Tcl_CmdProc*)pager_close },
|
||||
{ "pager_commit", (Tcl_CmdProc*)pager_commit },
|
||||
{ "pager_rollback", (Tcl_CmdProc*)pager_rollback },
|
||||
{ "pager_stmt_begin", (Tcl_CmdProc*)pager_stmt_begin },
|
||||
{ "pager_stmt_commit", (Tcl_CmdProc*)pager_stmt_commit },
|
||||
{ "pager_stmt_rollback", (Tcl_CmdProc*)pager_stmt_rollback },
|
||||
{ "pager_stats", (Tcl_CmdProc*)pager_stats },
|
||||
{ "pager_pagecount", (Tcl_CmdProc*)pager_pagecount },
|
||||
{ "page_get", (Tcl_CmdProc*)page_get },
|
||||
{ "page_lookup", (Tcl_CmdProc*)page_lookup },
|
||||
{ "page_unref", (Tcl_CmdProc*)page_unref },
|
||||
{ "page_read", (Tcl_CmdProc*)page_read },
|
||||
{ "page_write", (Tcl_CmdProc*)page_write },
|
||||
{ "page_number", (Tcl_CmdProc*)page_number },
|
||||
{ "pager_truncate", (Tcl_CmdProc*)pager_truncate },
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
{ "fake_big_file", (Tcl_CmdProc*)fake_big_file },
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
Tcl_LinkVar(interp, "sqlite_io_error_pending",
|
||||
(char*)&sqlite3_io_error_pending, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_io_error_hit",
|
||||
(char*)&sqlite3_io_error_hit, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_diskfull_pending",
|
||||
(char*)&sqlite3_diskfull_pending, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_diskfull",
|
||||
(char*)&sqlite3_diskfull, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite_pending_byte",
|
||||
(char*)&sqlite3_pending_byte, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "pager_pagesize",
|
||||
(char*)&test_pagesize, TCL_LINK_INT);
|
||||
return TCL_OK;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,717 +0,0 @@
|
|||
/*
|
||||
** 2003 December 18
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the the SQLite library in a multithreaded environment.
|
||||
**
|
||||
** $Id: test4.c,v 1.17 2006/02/23 21:43:56 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include "os.h"
|
||||
#if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Each thread is controlled by an instance of the following
|
||||
** structure.
|
||||
*/
|
||||
typedef struct Thread Thread;
|
||||
struct Thread {
|
||||
/* The first group of fields are writable by the master and read-only
|
||||
** to the thread. */
|
||||
char *zFilename; /* Name of database file */
|
||||
void (*xOp)(Thread*); /* next operation to do */
|
||||
char *zArg; /* argument usable by xOp */
|
||||
int opnum; /* Operation number */
|
||||
int busy; /* True if this thread is in use */
|
||||
|
||||
/* The next group of fields are writable by the thread but read-only to the
|
||||
** master. */
|
||||
int completed; /* Number of operations completed */
|
||||
sqlite3 *db; /* Open database */
|
||||
sqlite3_stmt *pStmt; /* Pending operation */
|
||||
char *zErr; /* operation error */
|
||||
char *zStaticErr; /* Static error message */
|
||||
int rc; /* operation return code */
|
||||
int argc; /* number of columns in result */
|
||||
const char *argv[100]; /* result columns */
|
||||
const char *colv[100]; /* result column names */
|
||||
};
|
||||
|
||||
/*
|
||||
** There can be as many as 26 threads running at once. Each is named
|
||||
** by a capital letter: A, B, C, ..., Y, Z.
|
||||
*/
|
||||
#define N_THREAD 26
|
||||
static Thread threadset[N_THREAD];
|
||||
|
||||
|
||||
/*
|
||||
** The main loop for a thread. Threads use busy waiting.
|
||||
*/
|
||||
static void *thread_main(void *pArg){
|
||||
Thread *p = (Thread*)pArg;
|
||||
if( p->db ){
|
||||
sqlite3_close(p->db);
|
||||
}
|
||||
sqlite3_open(p->zFilename, &p->db);
|
||||
if( SQLITE_OK!=sqlite3_errcode(p->db) ){
|
||||
p->zErr = strdup(sqlite3_errmsg(p->db));
|
||||
sqlite3_close(p->db);
|
||||
p->db = 0;
|
||||
}
|
||||
p->pStmt = 0;
|
||||
p->completed = 1;
|
||||
while( p->opnum<=p->completed ) sched_yield();
|
||||
while( p->xOp ){
|
||||
if( p->zErr && p->zErr!=p->zStaticErr ){
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = 0;
|
||||
}
|
||||
(*p->xOp)(p);
|
||||
p->completed++;
|
||||
while( p->opnum<=p->completed ) sched_yield();
|
||||
}
|
||||
if( p->pStmt ){
|
||||
sqlite3_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
if( p->db ){
|
||||
sqlite3_close(p->db);
|
||||
p->db = 0;
|
||||
}
|
||||
if( p->zErr && p->zErr!=p->zStaticErr ){
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = 0;
|
||||
}
|
||||
p->completed++;
|
||||
sqlite3_thread_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get a thread ID which is an upper case letter. Return the index.
|
||||
** If the argument is not a valid thread ID put an error message in
|
||||
** the interpreter and return -1.
|
||||
*/
|
||||
static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
|
||||
if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
|
||||
Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
|
||||
return -1;
|
||||
}
|
||||
return zArg[0] - 'A';
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_create NAME FILENAME
|
||||
**
|
||||
** NAME should be an upper case letter. Start the thread running with
|
||||
** an open connection to the given database.
|
||||
*/
|
||||
static int tcl_thread_create(
|
||||
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 i;
|
||||
pthread_t x;
|
||||
int rc;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID FILENAME", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
threadset[i].busy = 1;
|
||||
sqliteFree(threadset[i].zFilename);
|
||||
threadset[i].zFilename = sqliteStrDup(argv[2]);
|
||||
threadset[i].opnum = 1;
|
||||
threadset[i].completed = 0;
|
||||
rc = pthread_create(&x, 0, thread_main, &threadset[i]);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "failed to create the thread", 0);
|
||||
sqliteFree(threadset[i].zFilename);
|
||||
threadset[i].busy = 0;
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pthread_detach(x);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Wait for a thread to reach its idle state.
|
||||
*/
|
||||
static void thread_wait(Thread *p){
|
||||
while( p->opnum>p->completed ) sched_yield();
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_wait ID
|
||||
**
|
||||
** Wait on thread ID to reach its idle state.
|
||||
*/
|
||||
static int tcl_thread_wait(
|
||||
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 i;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Stop a thread.
|
||||
*/
|
||||
static void stop_thread(Thread *p){
|
||||
thread_wait(p);
|
||||
p->xOp = 0;
|
||||
p->opnum++;
|
||||
thread_wait(p);
|
||||
sqliteFree(p->zArg);
|
||||
p->zArg = 0;
|
||||
sqliteFree(p->zFilename);
|
||||
p->zFilename = 0;
|
||||
p->busy = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_halt ID
|
||||
**
|
||||
** Cause a thread to shut itself down. Wait for the shutdown to be
|
||||
** completed. If ID is "*" then stop all threads.
|
||||
*/
|
||||
static int tcl_thread_halt(
|
||||
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 i;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( argv[1][0]=='*' && argv[1][1]==0 ){
|
||||
for(i=0; i<N_THREAD; i++){
|
||||
if( threadset[i].busy ) stop_thread(&threadset[i]);
|
||||
}
|
||||
}else{
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
stop_thread(&threadset[i]);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_argc ID
|
||||
**
|
||||
** Wait on the most recent thread_step to complete, then return the
|
||||
** number of columns in the result set.
|
||||
*/
|
||||
static int tcl_thread_argc(
|
||||
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 i;
|
||||
char zBuf[100];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
sprintf(zBuf, "%d", threadset[i].argc);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_argv ID N
|
||||
**
|
||||
** Wait on the most recent thread_step to complete, then return the
|
||||
** value of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_thread_argv(
|
||||
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 i;
|
||||
int n;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID N", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
|
||||
thread_wait(&threadset[i]);
|
||||
if( n<0 || n>=threadset[i].argc ){
|
||||
Tcl_AppendResult(interp, "column number out of range", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_AppendResult(interp, threadset[i].argv[n], 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_colname ID N
|
||||
**
|
||||
** Wait on the most recent thread_step to complete, then return the
|
||||
** name of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_thread_colname(
|
||||
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 i;
|
||||
int n;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID N", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
|
||||
thread_wait(&threadset[i]);
|
||||
if( n<0 || n>=threadset[i].argc ){
|
||||
Tcl_AppendResult(interp, "column number out of range", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_AppendResult(interp, threadset[i].colv[n], 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_result ID
|
||||
**
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** result code from that operation.
|
||||
*/
|
||||
static int tcl_thread_result(
|
||||
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 i;
|
||||
const char *zName;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
switch( threadset[i].rc ){
|
||||
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;
|
||||
default: zName = "SQLITE_Unknown"; break;
|
||||
}
|
||||
Tcl_AppendResult(interp, zName, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_error ID
|
||||
**
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** error string.
|
||||
*/
|
||||
static int tcl_thread_error(
|
||||
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 i;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
Tcl_AppendResult(interp, threadset[i].zErr, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to compile an SQL statement.
|
||||
*/
|
||||
static void do_compile(Thread *p){
|
||||
if( p->db==0 ){
|
||||
p->zErr = p->zStaticErr = "no database is open";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
if( p->pStmt ){
|
||||
sqlite3_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_compile ID SQL
|
||||
**
|
||||
** Compile a new virtual machine.
|
||||
*/
|
||||
static int tcl_thread_compile(
|
||||
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 i;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID SQL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_compile;
|
||||
sqliteFree(threadset[i].zArg);
|
||||
threadset[i].zArg = sqliteStrDup(argv[2]);
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to step the virtual machine.
|
||||
*/
|
||||
static void do_step(Thread *p){
|
||||
int i;
|
||||
if( p->pStmt==0 ){
|
||||
p->zErr = p->zStaticErr = "no virtual machine available";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
p->rc = sqlite3_step(p->pStmt);
|
||||
if( p->rc==SQLITE_ROW ){
|
||||
p->argc = sqlite3_column_count(p->pStmt);
|
||||
for(i=0; i<sqlite3_data_count(p->pStmt); i++){
|
||||
p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
|
||||
}
|
||||
for(i=0; i<p->argc; i++){
|
||||
p->colv[i] = sqlite3_column_name(p->pStmt, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_step ID
|
||||
**
|
||||
** Advance the virtual machine by one step
|
||||
*/
|
||||
static int tcl_thread_step(
|
||||
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 i;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" IDL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_step;
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to finalize a virtual machine.
|
||||
*/
|
||||
static void do_finalize(Thread *p){
|
||||
if( p->pStmt==0 ){
|
||||
p->zErr = p->zStaticErr = "no virtual machine available";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
p->rc = sqlite3_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_finalize ID
|
||||
**
|
||||
** Finalize the virtual machine.
|
||||
*/
|
||||
static int tcl_thread_finalize(
|
||||
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 i;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" IDL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_finalize;
|
||||
sqliteFree(threadset[i].zArg);
|
||||
threadset[i].zArg = 0;
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_swap ID ID
|
||||
**
|
||||
** Interchange the sqlite* pointer between two threads.
|
||||
*/
|
||||
static int tcl_thread_swap(
|
||||
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 i, j;
|
||||
sqlite3 *temp;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID1 ID2", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
j = parse_thread_id(interp, argv[2]);
|
||||
if( j<0 ) return TCL_ERROR;
|
||||
if( !threadset[j].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[j]);
|
||||
temp = threadset[i].db;
|
||||
threadset[i].db = threadset[j].db;
|
||||
threadset[j].db = temp;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_db_get ID
|
||||
**
|
||||
** Return the database connection pointer for the given thread. Then
|
||||
** remove the pointer from the thread itself. Afterwards, the thread
|
||||
** can be stopped and the connection can be used by the main thread.
|
||||
*/
|
||||
static int tcl_thread_db_get(
|
||||
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 i;
|
||||
char zBuf[100];
|
||||
extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
|
||||
threadset[i].db = 0;
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: thread_stmt_get ID
|
||||
**
|
||||
** Return the database stmt pointer for the given thread. Then
|
||||
** remove the pointer from the thread itself.
|
||||
*/
|
||||
static int tcl_thread_stmt_get(
|
||||
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 i;
|
||||
char zBuf[100];
|
||||
extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_thread_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
thread_wait(&threadset[i]);
|
||||
sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
|
||||
threadset[i].pStmt = 0;
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest4_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_CmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "thread_create", (Tcl_CmdProc*)tcl_thread_create },
|
||||
{ "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
|
||||
{ "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
|
||||
{ "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
|
||||
{ "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
|
||||
{ "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
|
||||
{ "thread_result", (Tcl_CmdProc*)tcl_thread_result },
|
||||
{ "thread_error", (Tcl_CmdProc*)tcl_thread_error },
|
||||
{ "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
|
||||
{ "thread_step", (Tcl_CmdProc*)tcl_thread_step },
|
||||
{ "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
|
||||
{ "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
|
||||
{ "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get },
|
||||
{ "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#else
|
||||
int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
|
||||
#endif /* OS_UNIX */
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the utf.c module in SQLite. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library. Specifically, the code in this file
|
||||
** is used for testing the SQLite routines for converting between
|
||||
** the various supported unicode encodings.
|
||||
**
|
||||
** $Id: test5.c,v 1.15 2005/12/09 20:21:59 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
#include "os.h" /* to get SQLITE_BIGENDIAN */
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** The first argument is a TCL UTF-8 string. Return the byte array
|
||||
** object with the encoded representation of the string, including
|
||||
** the NULL terminator.
|
||||
*/
|
||||
static int binarize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int len;
|
||||
char *bytes;
|
||||
Tcl_Obj *pRet;
|
||||
assert(objc==2);
|
||||
|
||||
bytes = Tcl_GetStringFromObj(objv[1], &len);
|
||||
pRet = Tcl_NewByteArrayObj((u8*)bytes, len+1);
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: test_value_overhead <repeat-count> <do-calls>.
|
||||
**
|
||||
** This routine is used to test the overhead of calls to
|
||||
** sqlite3_value_text(), on a value that contains a UTF-8 string. The idea
|
||||
** is to figure out whether or not it is a problem to use sqlite3_value
|
||||
** structures with collation sequence functions.
|
||||
**
|
||||
** If <do-calls> is 0, then the calls to sqlite3_value_text() are not
|
||||
** actually made.
|
||||
*/
|
||||
static int test_value_overhead(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int do_calls;
|
||||
int repeat_count;
|
||||
int i;
|
||||
Mem val;
|
||||
const char *zVal;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " <repeat-count> <do-calls>", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &repeat_count) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &do_calls) ) return TCL_ERROR;
|
||||
|
||||
val.flags = MEM_Str|MEM_Term|MEM_Static;
|
||||
val.z = "hello world";
|
||||
val.type = SQLITE_TEXT;
|
||||
val.enc = SQLITE_UTF8;
|
||||
|
||||
for(i=0; i<repeat_count; i++){
|
||||
if( do_calls ){
|
||||
zVal = (char*)sqlite3_value_text(&val);
|
||||
}
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static u8 name_to_enc(Tcl_Interp *interp, Tcl_Obj *pObj){
|
||||
struct EncName {
|
||||
char *zName;
|
||||
u8 enc;
|
||||
} encnames[] = {
|
||||
{ "UTF8", SQLITE_UTF8 },
|
||||
{ "UTF16LE", SQLITE_UTF16LE },
|
||||
{ "UTF16BE", SQLITE_UTF16BE },
|
||||
{ "UTF16", SQLITE_UTF16NATIVE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
struct EncName *pEnc;
|
||||
char *z = Tcl_GetString(pObj);
|
||||
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
|
||||
if( 0==sqlite3StrICmp(z, pEnc->zName) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !pEnc->enc ){
|
||||
Tcl_AppendResult(interp, "No such encoding: ", z, 0);
|
||||
}
|
||||
return pEnc->enc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: test_translate <string/blob> <from enc> <to enc> ?<transient>?
|
||||
**
|
||||
*/
|
||||
static int test_translate(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
u8 enc_from;
|
||||
u8 enc_to;
|
||||
sqlite3_value *pVal;
|
||||
|
||||
char *z;
|
||||
int len;
|
||||
void (*xDel)(void *p) = SQLITE_STATIC;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0),
|
||||
" <string/blob> <from enc> <to enc>", 0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( objc==5 ){
|
||||
xDel = sqlite3FreeX;
|
||||
}
|
||||
|
||||
enc_from = name_to_enc(interp, objv[2]);
|
||||
if( !enc_from ) return TCL_ERROR;
|
||||
enc_to = name_to_enc(interp, objv[3]);
|
||||
if( !enc_to ) return TCL_ERROR;
|
||||
|
||||
pVal = sqlite3ValueNew();
|
||||
|
||||
if( enc_from==SQLITE_UTF8 ){
|
||||
z = Tcl_GetString(objv[1]);
|
||||
if( objc==5 ){
|
||||
z = sqliteStrDup(z);
|
||||
}
|
||||
sqlite3ValueSetStr(pVal, -1, z, enc_from, xDel);
|
||||
}else{
|
||||
z = (char*)Tcl_GetByteArrayFromObj(objv[1], &len);
|
||||
if( objc==5 ){
|
||||
char *zTmp = z;
|
||||
z = sqliteMalloc(len);
|
||||
memcpy(z, zTmp, len);
|
||||
}
|
||||
sqlite3ValueSetStr(pVal, -1, z, enc_from, xDel);
|
||||
}
|
||||
|
||||
z = (char *)sqlite3ValueText(pVal, enc_to);
|
||||
len = sqlite3ValueBytes(pVal, enc_to) + (enc_to==SQLITE_UTF8?1:2);
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((u8*)z, len));
|
||||
|
||||
sqlite3ValueFree(pVal);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: translate_selftest
|
||||
**
|
||||
** Call sqlite3utfSelfTest() to run the internal tests for unicode
|
||||
** translation. If there is a problem an assert() will fail.
|
||||
**/
|
||||
void sqlite3utfSelfTest();
|
||||
static int test_translate_selftest(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
sqlite3utfSelfTest();
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest5_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "binarize", (Tcl_ObjCmdProc*)binarize },
|
||||
{ "test_value_overhead", (Tcl_ObjCmdProc*)test_value_overhead },
|
||||
{ "test_translate", (Tcl_ObjCmdProc*)test_translate },
|
||||
{ "translate_selftest", (Tcl_ObjCmdProc*)test_translate_selftest},
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
|
@ -1,554 +0,0 @@
|
|||
/*
|
||||
** 2004 May 22
|
||||
**
|
||||
** 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 that modified the OS layer in order to simulate
|
||||
** the effect on the database file of an OS crash or power failure. This
|
||||
** is used to test the ability of SQLite to recover from those situations.
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for the testing only */
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
#include "tcl.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */
|
||||
|
||||
/*
|
||||
** crashFile is a subclass of OsFile that is taylored for the
|
||||
** crash test module.
|
||||
*/
|
||||
typedef struct crashFile crashFile;
|
||||
struct crashFile {
|
||||
IoMethod const *pMethod; /* Must be first */
|
||||
u8 **apBlk; /* Array of blocks that have been written to. */
|
||||
int nBlk; /* Size of apBlock. */
|
||||
i64 offset; /* Next character to be read from the file */
|
||||
int nMaxWrite; /* Largest offset written to. */
|
||||
char *zName; /* File name */
|
||||
OsFile *pBase; /* The real file */
|
||||
crashFile *pNext; /* Next in a list of them all */
|
||||
};
|
||||
|
||||
/*
|
||||
** Size of a simulated disk block
|
||||
*/
|
||||
#define BLOCKSIZE 512
|
||||
#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE)
|
||||
|
||||
|
||||
/*
|
||||
** The following variables control when a simulated crash occurs.
|
||||
**
|
||||
** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of
|
||||
** a file that SQLite will call sqlite3OsSync() on. Each time this happens
|
||||
** iCrashDelay is decremented. If iCrashDelay is zero after being
|
||||
** decremented, a "crash" occurs during the sync() operation.
|
||||
**
|
||||
** In other words, a crash occurs the iCrashDelay'th time zCrashFile is
|
||||
** synced.
|
||||
*/
|
||||
static int iCrashDelay = 0;
|
||||
static char zCrashFile[500];
|
||||
|
||||
/*
|
||||
** Set the value of the two crash parameters.
|
||||
*/
|
||||
static void setCrashParams(int iDelay, char const *zFile){
|
||||
sqlite3OsEnterMutex();
|
||||
assert( strlen(zFile)<sizeof(zCrashFile) );
|
||||
strcpy(zCrashFile, zFile);
|
||||
iCrashDelay = iDelay;
|
||||
sqlite3OsLeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** File zPath is being sync()ed. Return non-zero if this should
|
||||
** cause a crash.
|
||||
*/
|
||||
static int crashRequired(char const *zPath){
|
||||
int r;
|
||||
int n;
|
||||
sqlite3OsEnterMutex();
|
||||
n = strlen(zCrashFile);
|
||||
if( zCrashFile[n-1]=='*' ){
|
||||
n--;
|
||||
}else if( strlen(zPath)>n ){
|
||||
n = strlen(zPath);
|
||||
}
|
||||
r = 0;
|
||||
if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){
|
||||
iCrashDelay--;
|
||||
if( iCrashDelay<=0 ){
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
** A list of all open files.
|
||||
*/
|
||||
static crashFile *pAllFiles = 0;
|
||||
|
||||
/* Forward reference */
|
||||
static void initFile(OsFile **pId, char const *zName, OsFile *pBase);
|
||||
|
||||
/*
|
||||
** Undo the work done by initFile. Delete the OsFile structure
|
||||
** and unlink the structure from the pAllFiles list.
|
||||
*/
|
||||
static void closeFile(crashFile **pId){
|
||||
crashFile *pFile = *pId;
|
||||
if( pFile==pAllFiles ){
|
||||
pAllFiles = pFile->pNext;
|
||||
}else{
|
||||
crashFile *p;
|
||||
for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){
|
||||
assert( p );
|
||||
}
|
||||
p->pNext = pFile->pNext;
|
||||
}
|
||||
sqliteFree(*pId);
|
||||
*pId = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read block 'blk' off of the real disk file and into the cache of pFile.
|
||||
*/
|
||||
static int readBlockIntoCache(crashFile *pFile, int blk){
|
||||
if( blk>=pFile->nBlk ){
|
||||
int n = ((pFile->nBlk * 2) + 100 + blk);
|
||||
/* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */
|
||||
pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
|
||||
if( !pFile->apBlk ) return SQLITE_NOMEM;
|
||||
memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
|
||||
pFile->nBlk = n;
|
||||
}
|
||||
|
||||
if( !pFile->apBlk[blk] ){
|
||||
i64 filesize;
|
||||
int rc;
|
||||
|
||||
u8 *p = sqliteMalloc(BLOCKSIZE);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
pFile->apBlk[blk] = p;
|
||||
|
||||
rc = sqlite3OsFileSize(pFile->pBase, &filesize);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
if( BLOCK_OFFSET(blk)<filesize ){
|
||||
int len = BLOCKSIZE;
|
||||
rc = sqlite3OsSeek(pFile->pBase, blk*BLOCKSIZE);
|
||||
if( BLOCK_OFFSET(blk+1)>filesize ){
|
||||
len = filesize - BLOCK_OFFSET(blk);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = sqlite3OsRead(pFile->pBase, p, len);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write the cache of pFile to disk. If crash is non-zero, randomly
|
||||
** skip blocks when writing. The cache is deleted before returning.
|
||||
*/
|
||||
static int writeCache2(crashFile *pFile, int crash){
|
||||
int i;
|
||||
int nMax = pFile->nMaxWrite;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
for(i=0; i<pFile->nBlk; i++){
|
||||
u8 *p = pFile->apBlk[i];
|
||||
if( p ){
|
||||
int skip = 0;
|
||||
int trash = 0;
|
||||
if( crash ){
|
||||
char random;
|
||||
sqlite3Randomness(1, &random);
|
||||
if( random & 0x01 ){
|
||||
if( random & 0x02 ){
|
||||
trash = 1;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Trashing block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}else{
|
||||
skip = 1;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Skiping block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}
|
||||
}else{
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Writing block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i));
|
||||
}
|
||||
if( rc==SQLITE_OK && !skip ){
|
||||
int len = BLOCKSIZE;
|
||||
if( BLOCK_OFFSET(i+1)>nMax ){
|
||||
len = nMax-BLOCK_OFFSET(i);
|
||||
}
|
||||
if( len>0 ){
|
||||
if( trash ){
|
||||
sqlite3Randomness(len, p);
|
||||
}
|
||||
rc = sqlite3OsWrite(pFile->pBase, p, len);
|
||||
}
|
||||
}
|
||||
sqliteFree(p);
|
||||
}
|
||||
}
|
||||
sqliteFree(pFile->apBlk);
|
||||
pFile->nBlk = 0;
|
||||
pFile->apBlk = 0;
|
||||
pFile->nMaxWrite = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write the cache to disk.
|
||||
*/
|
||||
static int writeCache(crashFile *pFile){
|
||||
if( pFile->apBlk ){
|
||||
int c = crashRequired(pFile->zName);
|
||||
if( c ){
|
||||
crashFile *p;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("\nCrash during sync of %s\n", pFile->zName);
|
||||
#endif
|
||||
for(p=pAllFiles; p; p=p->pNext){
|
||||
writeCache2(p, 1);
|
||||
}
|
||||
exit(-1);
|
||||
}else{
|
||||
return writeCache2(pFile, 0);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the file.
|
||||
*/
|
||||
static int crashClose(OsFile **pId){
|
||||
crashFile *pFile = (crashFile*)*pId;
|
||||
if( pFile ){
|
||||
/* printf("CLOSE %s (%d blocks)\n", pFile->zName, pFile->nBlk); */
|
||||
writeCache(pFile);
|
||||
sqlite3OsClose(&pFile->pBase);
|
||||
}
|
||||
closeFile(&pFile);
|
||||
*pId = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int crashSeek(OsFile *id, i64 offset){
|
||||
((crashFile*)id)->offset = offset;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int crashRead(OsFile *id, void *pBuf, int amt){
|
||||
i64 offset; /* The current offset from the start of the file */
|
||||
i64 end; /* The byte just past the last byte read */
|
||||
int blk; /* Block number the read starts on */
|
||||
int i;
|
||||
u8 *zCsr;
|
||||
int rc = SQLITE_OK;
|
||||
crashFile *pFile = (crashFile*)id;
|
||||
|
||||
offset = pFile->offset;
|
||||
end = offset+amt;
|
||||
blk = (offset/BLOCKSIZE);
|
||||
|
||||
zCsr = (u8 *)pBuf;
|
||||
for(i=blk; i*BLOCKSIZE<end; i++){
|
||||
int off = 0;
|
||||
int len = 0;
|
||||
|
||||
|
||||
if( BLOCK_OFFSET(i) < offset ){
|
||||
off = offset-BLOCK_OFFSET(i);
|
||||
}
|
||||
len = BLOCKSIZE - off;
|
||||
if( BLOCK_OFFSET(i+1) > end ){
|
||||
len = len - (BLOCK_OFFSET(i+1)-end);
|
||||
}
|
||||
|
||||
if( i<pFile->nBlk && pFile->apBlk[i]){
|
||||
u8 *pBlk = pFile->apBlk[i];
|
||||
memcpy(zCsr, &pBlk[off], len);
|
||||
}else{
|
||||
rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i) + off);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = sqlite3OsRead(pFile->pBase, zCsr, len);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
|
||||
zCsr += len;
|
||||
}
|
||||
assert( zCsr==&((u8 *)pBuf)[amt] );
|
||||
|
||||
pFile->offset = end;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int crashWrite(OsFile *id, const void *pBuf, int amt){
|
||||
i64 offset; /* The current offset from the start of the file */
|
||||
i64 end; /* The byte just past the last byte written */
|
||||
int blk; /* Block number the write starts on */
|
||||
int i;
|
||||
const u8 *zCsr;
|
||||
int rc = SQLITE_OK;
|
||||
crashFile *pFile = (crashFile*)id;
|
||||
|
||||
offset = pFile->offset;
|
||||
end = offset+amt;
|
||||
blk = (offset/BLOCKSIZE);
|
||||
|
||||
zCsr = (u8 *)pBuf;
|
||||
for(i=blk; i*BLOCKSIZE<end; i++){
|
||||
u8 *pBlk;
|
||||
int off = 0;
|
||||
int len = 0;
|
||||
|
||||
/* Make sure the block is in the cache */
|
||||
rc = readBlockIntoCache(pFile, i);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Write into the cache */
|
||||
pBlk = pFile->apBlk[i];
|
||||
assert( pBlk );
|
||||
|
||||
if( BLOCK_OFFSET(i) < offset ){
|
||||
off = offset-BLOCK_OFFSET(i);
|
||||
}
|
||||
len = BLOCKSIZE - off;
|
||||
if( BLOCK_OFFSET(i+1) > end ){
|
||||
len = len - (BLOCK_OFFSET(i+1)-end);
|
||||
}
|
||||
memcpy(&pBlk[off], zCsr, len);
|
||||
zCsr += len;
|
||||
}
|
||||
if( pFile->nMaxWrite<end ){
|
||||
pFile->nMaxWrite = end;
|
||||
}
|
||||
assert( zCsr==&((u8 *)pBuf)[amt] );
|
||||
pFile->offset = end;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the file. First flush the write-cache to disk, then call the
|
||||
** real sync() function.
|
||||
*/
|
||||
static int crashSync(OsFile *id, int dataOnly){
|
||||
return writeCache((crashFile*)id);
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new
|
||||
** file size to ensure that nothing in the write-cache past this point
|
||||
** is written to disk.
|
||||
*/
|
||||
static int crashTruncate(OsFile *id, i64 nByte){
|
||||
crashFile *pFile = (crashFile*)id;
|
||||
pFile->nMaxWrite = nByte;
|
||||
return sqlite3OsTruncate(pFile->pBase, nByte);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the file. If the cache contains a write that extended
|
||||
** the file, then return this size instead of the on-disk size.
|
||||
*/
|
||||
static int crashFileSize(OsFile *id, i64 *pSize){
|
||||
crashFile *pFile = (crashFile*)id;
|
||||
int rc = sqlite3OsFileSize(pFile->pBase, pSize);
|
||||
if( rc==SQLITE_OK && pSize && *pSize<pFile->nMaxWrite ){
|
||||
*pSize = pFile->nMaxWrite;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set this global variable to 1 to enable crash testing.
|
||||
*/
|
||||
int sqlite3CrashTestEnable = 0;
|
||||
|
||||
/*
|
||||
** The three functions used to open files. All that is required is to
|
||||
** initialise the os_test.c specific fields and then call the corresponding
|
||||
** os_unix.c function to really open the file.
|
||||
*/
|
||||
int sqlite3CrashOpenReadWrite(const char *zFilename, OsFile **pId,int *pRdonly){
|
||||
OsFile *pBase = 0;
|
||||
int rc;
|
||||
|
||||
sqlite3CrashTestEnable = 0;
|
||||
rc = sqlite3OsOpenReadWrite(zFilename, &pBase, pRdonly);
|
||||
sqlite3CrashTestEnable = 1;
|
||||
if( !rc ){
|
||||
initFile(pId, zFilename, pBase);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3CrashOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
|
||||
OsFile *pBase = 0;
|
||||
int rc;
|
||||
|
||||
sqlite3CrashTestEnable = 0;
|
||||
rc = sqlite3OsOpenExclusive(zFilename, &pBase, delFlag);
|
||||
sqlite3CrashTestEnable = 1;
|
||||
if( !rc ){
|
||||
initFile(pId, zFilename, pBase);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3CrashOpenReadOnly(const char *zFilename, OsFile **pId, int NotUsed){
|
||||
OsFile *pBase = 0;
|
||||
int rc;
|
||||
|
||||
sqlite3CrashTestEnable = 0;
|
||||
rc = sqlite3OsOpenReadOnly(zFilename, &pBase);
|
||||
sqlite3CrashTestEnable = 1;
|
||||
if( !rc ){
|
||||
initFile(pId, zFilename, pBase);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** OpenDirectory is a no-op
|
||||
*/
|
||||
static int crashOpenDir(OsFile *id, const char *zName){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Locking primitives are passed through into the underlying
|
||||
** file descriptor.
|
||||
*/
|
||||
int crashLock(OsFile *id, int lockType){
|
||||
return sqlite3OsLock(((crashFile*)id)->pBase, lockType);
|
||||
}
|
||||
int crashUnlock(OsFile *id, int lockType){
|
||||
return sqlite3OsUnlock(((crashFile*)id)->pBase, lockType);
|
||||
}
|
||||
int crashCheckReservedLock(OsFile *id){
|
||||
return sqlite3OsCheckReservedLock(((crashFile*)id)->pBase);
|
||||
}
|
||||
void crashSetFullSync(OsFile *id, int setting){
|
||||
return; /* This is a no-op */
|
||||
}
|
||||
int crashLockState(OsFile *id){
|
||||
return sqlite3OsLockState(((crashFile*)id)->pBase);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the underlying file handle.
|
||||
*/
|
||||
int crashFileHandle(OsFile *id){
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
||||
return sqlite3OsFileHandle(((crashFile*)id)->pBase);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This vector defines all the methods that can operate on an OsFile
|
||||
** for the crash tester.
|
||||
*/
|
||||
static const IoMethod crashIoMethod = {
|
||||
crashClose,
|
||||
crashOpenDir,
|
||||
crashRead,
|
||||
crashWrite,
|
||||
crashSeek,
|
||||
crashTruncate,
|
||||
crashSync,
|
||||
crashSetFullSync,
|
||||
crashFileHandle,
|
||||
crashFileSize,
|
||||
crashLock,
|
||||
crashUnlock,
|
||||
crashLockState,
|
||||
crashCheckReservedLock,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Initialise the os_test.c specific fields of pFile.
|
||||
*/
|
||||
static void initFile(OsFile **pId, char const *zName, OsFile *pBase){
|
||||
crashFile *pFile = sqliteMalloc(sizeof(crashFile) + strlen(zName)+1);
|
||||
pFile->pMethod = &crashIoMethod;
|
||||
pFile->nMaxWrite = 0;
|
||||
pFile->offset = 0;
|
||||
pFile->nBlk = 0;
|
||||
pFile->apBlk = 0;
|
||||
pFile->zName = (char *)(&pFile[1]);
|
||||
strcpy(pFile->zName, zName);
|
||||
pFile->pBase = pBase;
|
||||
pFile->pNext = pAllFiles;
|
||||
pAllFiles = pFile;
|
||||
*pId = (OsFile*)pFile;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite_crashparams DELAY CRASHFILE
|
||||
**
|
||||
** This procedure implements a TCL command that enables crash testing
|
||||
** in testfixture. Once enabled, crash testing cannot be disabled.
|
||||
*/
|
||||
static int crashParamsObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int delay;
|
||||
const char *zFile;
|
||||
int nFile;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DELAY CRASHFILE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &delay) ) return TCL_ERROR;
|
||||
zFile = Tcl_GetStringFromObj(objv[2], &nFile);
|
||||
if( nFile>=sizeof(zCrashFile)-1 ){
|
||||
Tcl_AppendResult(interp, "crash file name too big", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
setCrashParams(delay, zFile);
|
||||
sqlite3CrashTestEnable = 1;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_DISKIO */
|
||||
|
||||
/*
|
||||
** This procedure registers the TCL procedures defined in this file.
|
||||
*/
|
||||
int Sqlitetest6_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_TEST */
|
|
@ -1,724 +0,0 @@
|
|||
/*
|
||||
** 2006 January 09
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the client/server version of the SQLite library.
|
||||
** Derived from test4.c.
|
||||
**
|
||||
** $Id: test7.c,v 1.4 2006/03/22 22:10:08 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include "os.h"
|
||||
|
||||
/*
|
||||
** This test only works on UNIX with a THREADSAFE build that includes
|
||||
** the SQLITE_SERVER option.
|
||||
*/
|
||||
#if OS_UNIX && defined(THREADSAFE) && THREADSAFE==1 && \
|
||||
defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Interfaces defined in server.c
|
||||
*/
|
||||
int sqlite3_client_open(const char*, sqlite3**);
|
||||
int sqlite3_client_prepare(sqlite3*,const char*,int,
|
||||
sqlite3_stmt**,const char**);
|
||||
int sqlite3_client_step(sqlite3_stmt*);
|
||||
int sqlite3_client_reset(sqlite3_stmt*);
|
||||
int sqlite3_client_finalize(sqlite3_stmt*);
|
||||
int sqlite3_client_close(sqlite3*);
|
||||
int sqlite3_server_start(void);
|
||||
int sqlite3_server_stop(void);
|
||||
|
||||
/*
|
||||
** Each thread is controlled by an instance of the following
|
||||
** structure.
|
||||
*/
|
||||
typedef struct Thread Thread;
|
||||
struct Thread {
|
||||
/* The first group of fields are writable by the supervisor thread
|
||||
** and read-only to the client threads
|
||||
*/
|
||||
char *zFilename; /* Name of database file */
|
||||
void (*xOp)(Thread*); /* next operation to do */
|
||||
char *zArg; /* argument usable by xOp */
|
||||
volatile int opnum; /* Operation number */
|
||||
volatile int busy; /* True if this thread is in use */
|
||||
|
||||
/* The next group of fields are writable by the client threads
|
||||
** but read-only to the superviser thread.
|
||||
*/
|
||||
volatile int completed; /* Number of operations completed */
|
||||
sqlite3 *db; /* Open database */
|
||||
sqlite3_stmt *pStmt; /* Pending operation */
|
||||
char *zErr; /* operation error */
|
||||
char *zStaticErr; /* Static error message */
|
||||
int rc; /* operation return code */
|
||||
int argc; /* number of columns in result */
|
||||
const char *argv[100]; /* result columns */
|
||||
const char *colv[100]; /* result column names */
|
||||
};
|
||||
|
||||
/*
|
||||
** There can be as many as 26 threads running at once. Each is named
|
||||
** by a capital letter: A, B, C, ..., Y, Z.
|
||||
*/
|
||||
#define N_THREAD 26
|
||||
static Thread threadset[N_THREAD];
|
||||
|
||||
/*
|
||||
** The main loop for a thread. Threads use busy waiting.
|
||||
*/
|
||||
static void *client_main(void *pArg){
|
||||
Thread *p = (Thread*)pArg;
|
||||
if( p->db ){
|
||||
sqlite3_client_close(p->db);
|
||||
}
|
||||
sqlite3_client_open(p->zFilename, &p->db);
|
||||
if( SQLITE_OK!=sqlite3_errcode(p->db) ){
|
||||
p->zErr = strdup(sqlite3_errmsg(p->db));
|
||||
sqlite3_client_close(p->db);
|
||||
p->db = 0;
|
||||
}
|
||||
p->pStmt = 0;
|
||||
p->completed = 1;
|
||||
while( p->opnum<=p->completed ) sched_yield();
|
||||
while( p->xOp ){
|
||||
if( p->zErr && p->zErr!=p->zStaticErr ){
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = 0;
|
||||
}
|
||||
(*p->xOp)(p);
|
||||
p->completed++;
|
||||
while( p->opnum<=p->completed ) sched_yield();
|
||||
}
|
||||
if( p->pStmt ){
|
||||
sqlite3_client_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
if( p->db ){
|
||||
sqlite3_client_close(p->db);
|
||||
p->db = 0;
|
||||
}
|
||||
if( p->zErr && p->zErr!=p->zStaticErr ){
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = 0;
|
||||
}
|
||||
p->completed++;
|
||||
sqlite3_thread_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get a thread ID which is an upper case letter. Return the index.
|
||||
** If the argument is not a valid thread ID put an error message in
|
||||
** the interpreter and return -1.
|
||||
*/
|
||||
static int parse_client_id(Tcl_Interp *interp, const char *zArg){
|
||||
if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
|
||||
Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
|
||||
return -1;
|
||||
}
|
||||
return zArg[0] - 'A';
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_create NAME FILENAME
|
||||
**
|
||||
** NAME should be an upper case letter. Start the thread running with
|
||||
** an open connection to the given database.
|
||||
*/
|
||||
static int tcl_client_create(
|
||||
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 i;
|
||||
pthread_t x;
|
||||
int rc;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID FILENAME", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
threadset[i].busy = 1;
|
||||
sqliteFree(threadset[i].zFilename);
|
||||
threadset[i].zFilename = sqliteStrDup(argv[2]);
|
||||
threadset[i].opnum = 1;
|
||||
threadset[i].completed = 0;
|
||||
rc = pthread_create(&x, 0, client_main, &threadset[i]);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "failed to create the thread", 0);
|
||||
sqliteFree(threadset[i].zFilename);
|
||||
threadset[i].busy = 0;
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pthread_detach(x);
|
||||
sqlite3_server_start();
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Wait for a thread to reach its idle state.
|
||||
*/
|
||||
static void client_wait(Thread *p){
|
||||
while( p->opnum>p->completed ) sched_yield();
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_wait ID
|
||||
**
|
||||
** Wait on thread ID to reach its idle state.
|
||||
*/
|
||||
static int tcl_client_wait(
|
||||
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 i;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Stop a thread.
|
||||
*/
|
||||
static void stop_thread(Thread *p){
|
||||
client_wait(p);
|
||||
p->xOp = 0;
|
||||
p->opnum++;
|
||||
client_wait(p);
|
||||
sqliteFree(p->zArg);
|
||||
p->zArg = 0;
|
||||
sqliteFree(p->zFilename);
|
||||
p->zFilename = 0;
|
||||
p->busy = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_halt ID
|
||||
**
|
||||
** Cause a client thread to shut itself down. Wait for the shutdown to be
|
||||
** completed. If ID is "*" then stop all client threads.
|
||||
*/
|
||||
static int tcl_client_halt(
|
||||
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 i;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( argv[1][0]=='*' && argv[1][1]==0 ){
|
||||
for(i=0; i<N_THREAD; i++){
|
||||
if( threadset[i].busy ){
|
||||
stop_thread(&threadset[i]);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
stop_thread(&threadset[i]);
|
||||
}
|
||||
|
||||
/* If no client threads are still running, also stop the server */
|
||||
for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
|
||||
if( i>=N_THREAD ){
|
||||
sqlite3_server_stop();
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_argc ID
|
||||
**
|
||||
** Wait on the most recent client_step to complete, then return the
|
||||
** number of columns in the result set.
|
||||
*/
|
||||
static int tcl_client_argc(
|
||||
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 i;
|
||||
char zBuf[100];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
sprintf(zBuf, "%d", threadset[i].argc);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_argv ID N
|
||||
**
|
||||
** Wait on the most recent client_step to complete, then return the
|
||||
** value of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_client_argv(
|
||||
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 i;
|
||||
int n;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID N", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
|
||||
client_wait(&threadset[i]);
|
||||
if( n<0 || n>=threadset[i].argc ){
|
||||
Tcl_AppendResult(interp, "column number out of range", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_AppendResult(interp, threadset[i].argv[n], 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_colname ID N
|
||||
**
|
||||
** Wait on the most recent client_step to complete, then return the
|
||||
** name of the N-th columns in the result set.
|
||||
*/
|
||||
static int tcl_client_colname(
|
||||
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 i;
|
||||
int n;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID N", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
|
||||
client_wait(&threadset[i]);
|
||||
if( n<0 || n>=threadset[i].argc ){
|
||||
Tcl_AppendResult(interp, "column number out of range", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_AppendResult(interp, threadset[i].colv[n], 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_result ID
|
||||
**
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** result code from that operation.
|
||||
*/
|
||||
static int tcl_client_result(
|
||||
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 i;
|
||||
const char *zName;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
switch( threadset[i].rc ){
|
||||
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;
|
||||
default: zName = "SQLITE_Unknown"; break;
|
||||
}
|
||||
Tcl_AppendResult(interp, zName, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_error ID
|
||||
**
|
||||
** Wait on the most recent operation to complete, then return the
|
||||
** error string.
|
||||
*/
|
||||
static int tcl_client_error(
|
||||
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 i;
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
Tcl_AppendResult(interp, threadset[i].zErr, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to compile an SQL statement.
|
||||
*/
|
||||
static void do_compile(Thread *p){
|
||||
if( p->db==0 ){
|
||||
p->zErr = p->zStaticErr = "no database is open";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
if( p->pStmt ){
|
||||
sqlite3_client_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
p->rc = sqlite3_client_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_compile ID SQL
|
||||
**
|
||||
** Compile a new virtual machine.
|
||||
*/
|
||||
static int tcl_client_compile(
|
||||
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 i;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID SQL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_compile;
|
||||
sqliteFree(threadset[i].zArg);
|
||||
threadset[i].zArg = sqliteStrDup(argv[2]);
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to step the virtual machine.
|
||||
*/
|
||||
static void do_step(Thread *p){
|
||||
int i;
|
||||
if( p->pStmt==0 ){
|
||||
p->zErr = p->zStaticErr = "no virtual machine available";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
p->rc = sqlite3_client_step(p->pStmt);
|
||||
if( p->rc==SQLITE_ROW ){
|
||||
p->argc = sqlite3_column_count(p->pStmt);
|
||||
for(i=0; i<sqlite3_data_count(p->pStmt); i++){
|
||||
p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
|
||||
}
|
||||
for(i=0; i<p->argc; i++){
|
||||
p->colv[i] = sqlite3_column_name(p->pStmt, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_step ID
|
||||
**
|
||||
** Advance the virtual machine by one step
|
||||
*/
|
||||
static int tcl_client_step(
|
||||
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 i;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" IDL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_step;
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to finalize a virtual machine.
|
||||
*/
|
||||
static void do_finalize(Thread *p){
|
||||
if( p->pStmt==0 ){
|
||||
p->zErr = p->zStaticErr = "no virtual machine available";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
p->rc = sqlite3_client_finalize(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_finalize ID
|
||||
**
|
||||
** Finalize the virtual machine.
|
||||
*/
|
||||
static int tcl_client_finalize(
|
||||
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 i;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" IDL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_finalize;
|
||||
sqliteFree(threadset[i].zArg);
|
||||
threadset[i].zArg = 0;
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This procedure runs in the thread to reset a virtual machine.
|
||||
*/
|
||||
static void do_reset(Thread *p){
|
||||
if( p->pStmt==0 ){
|
||||
p->zErr = p->zStaticErr = "no virtual machine available";
|
||||
p->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
p->rc = sqlite3_client_reset(p->pStmt);
|
||||
p->pStmt = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_reset ID
|
||||
**
|
||||
** Finalize the virtual machine.
|
||||
*/
|
||||
static int tcl_client_reset(
|
||||
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 i;
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" IDL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_reset;
|
||||
sqliteFree(threadset[i].zArg);
|
||||
threadset[i].zArg = 0;
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: client_swap ID ID
|
||||
**
|
||||
** Interchange the sqlite* pointer between two threads.
|
||||
*/
|
||||
static int tcl_client_swap(
|
||||
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 i, j;
|
||||
sqlite3 *temp;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" ID1 ID2", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i = parse_client_id(interp, argv[1]);
|
||||
if( i<0 ) return TCL_ERROR;
|
||||
if( !threadset[i].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[i]);
|
||||
j = parse_client_id(interp, argv[2]);
|
||||
if( j<0 ) return TCL_ERROR;
|
||||
if( !threadset[j].busy ){
|
||||
Tcl_AppendResult(interp, "no such thread", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
client_wait(&threadset[j]);
|
||||
temp = threadset[i].db;
|
||||
threadset[i].db = threadset[j].db;
|
||||
threadset[j].db = temp;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest7_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_CmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "client_create", (Tcl_CmdProc*)tcl_client_create },
|
||||
{ "client_wait", (Tcl_CmdProc*)tcl_client_wait },
|
||||
{ "client_halt", (Tcl_CmdProc*)tcl_client_halt },
|
||||
{ "client_argc", (Tcl_CmdProc*)tcl_client_argc },
|
||||
{ "client_argv", (Tcl_CmdProc*)tcl_client_argv },
|
||||
{ "client_colname", (Tcl_CmdProc*)tcl_client_colname },
|
||||
{ "client_result", (Tcl_CmdProc*)tcl_client_result },
|
||||
{ "client_error", (Tcl_CmdProc*)tcl_client_error },
|
||||
{ "client_compile", (Tcl_CmdProc*)tcl_client_compile },
|
||||
{ "client_step", (Tcl_CmdProc*)tcl_client_step },
|
||||
{ "client_reset", (Tcl_CmdProc*)tcl_client_reset },
|
||||
{ "client_finalize", (Tcl_CmdProc*)tcl_client_finalize },
|
||||
{ "client_swap", (Tcl_CmdProc*)tcl_client_swap },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#else
|
||||
int Sqlitetest7_Init(Tcl_Interp *interp){ return TCL_OK; }
|
||||
#endif /* OS_UNIX */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue