Merge branch 'prerelease' of ssh://git.zetetic.net/sqlcipher into prerelease
This commit is contained in:
commit
6157a30cd3
12
Makefile.in
12
Makefile.in
@ -1089,7 +1089,7 @@ parse.h: parse.c
|
||||
|
||||
parse.c: $(TOP)/src/parse.y lemon$(BEXE)
|
||||
cp $(TOP)/src/parse.y .
|
||||
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) -S parse.y
|
||||
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
|
||||
@ -1214,10 +1214,10 @@ FTS5_SRC = \
|
||||
$(TOP)/ext/fts5/fts5_varint.c \
|
||||
$(TOP)/ext/fts5/fts5_vocab.c \
|
||||
|
||||
fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon
|
||||
fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
|
||||
cp $(TOP)/ext/fts5/fts5parse.y .
|
||||
rm -f fts5parse.h
|
||||
./lemon$(BEXE) $(OPTS) fts5parse.y
|
||||
./lemon$(BEXE) $(OPTS) -S fts5parse.y
|
||||
|
||||
fts5parse.h: fts5parse.c
|
||||
|
||||
@ -1279,10 +1279,6 @@ fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuz
|
||||
./fuzzcheck$(TEXE) $(FUZZDATA)
|
||||
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
|
||||
|
||||
fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
|
||||
./fuzzcheck$(TEXE) --limit-mem 100M $(FUZZDATA)
|
||||
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
|
||||
|
||||
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
|
||||
valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
|
||||
valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
|
||||
@ -1300,7 +1296,7 @@ quicktest: ./testfixture$(TEXE)
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
#
|
||||
test: fastfuzztest sourcetest $(TESTPROGS) tcltest
|
||||
test: fuzztest sourcetest $(TESTPROGS) tcltest
|
||||
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
# because valgrind is so much slower than a native machine.
|
||||
|
||||
24
Makefile.msc
24
Makefile.msc
@ -248,6 +248,12 @@ OPTIMIZATIONS = 2
|
||||
SESSION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this to non-0 to enable support for the rbu extension.
|
||||
#
|
||||
!IFNDEF RBU
|
||||
RBU = 0
|
||||
!ENDIF
|
||||
|
||||
# Set the source code file to be used by executables and libraries when
|
||||
# they need the amalgamation.
|
||||
#
|
||||
@ -364,6 +370,13 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
|
||||
!ENDIF
|
||||
|
||||
# Should the rbu extension be enabled? If so, add compilation options
|
||||
# to enable it.
|
||||
#
|
||||
!IF $(RBU)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
# the Windows 10 platform.
|
||||
#
|
||||
@ -1750,7 +1763,7 @@ $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
|
||||
sqlite3.def: libsqlite3.lib
|
||||
echo EXPORTS > sqlite3.def
|
||||
dumpbin /all libsqlite3.lib \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@]*)(?:@\d+)?$$" \1 \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser|rbu)?_[^@]*)(?:@\d+)?$$" \1 \
|
||||
| sort >> sqlite3.def
|
||||
# <</block2>>
|
||||
|
||||
@ -2149,7 +2162,7 @@ parse.h: parse.c
|
||||
parse.c: $(TOP)\src\parse.y lemon.exe
|
||||
del /Q parse.y parse.h parse.h.temp 2>NUL
|
||||
copy $(TOP)\src\parse.y .
|
||||
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S parse.y
|
||||
|
||||
$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS)
|
||||
@ -2309,7 +2322,7 @@ LSM1_SRC = \
|
||||
fts5parse.c: $(TOP)\ext\fts5\fts5parse.y lemon.exe
|
||||
copy $(TOP)\ext\fts5\fts5parse.y .
|
||||
del /Q fts5parse.h 2>NUL
|
||||
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) fts5parse.y
|
||||
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S fts5parse.y
|
||||
|
||||
fts5parse.h: fts5parse.c
|
||||
|
||||
@ -2412,9 +2425,6 @@ queryplantest: testfixture.exe shell
|
||||
fuzztest: fuzzcheck.exe
|
||||
.\fuzzcheck.exe $(FUZZDATA)
|
||||
|
||||
fastfuzztest: fuzzcheck.exe
|
||||
.\fuzzcheck.exe --limit-mem 100M $(FUZZDATA)
|
||||
|
||||
# Minimal testing that runs in less than 3 minutes (on a fast machine)
|
||||
#
|
||||
quicktest: testfixture.exe sourcetest
|
||||
@ -2424,7 +2434,7 @@ quicktest: testfixture.exe sourcetest
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
#
|
||||
test: $(TESTPROGS) sourcetest fastfuzztest
|
||||
test: $(TESTPROGS) sourcetest fuzztest
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\veryquick.test $(TESTOPTS)
|
||||
|
||||
|
||||
@ -210,6 +210,12 @@ OPTIMIZATIONS = 2
|
||||
SESSION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this to non-0 to enable support for the rbu extension.
|
||||
#
|
||||
!IFNDEF RBU
|
||||
RBU = 0
|
||||
!ENDIF
|
||||
|
||||
# Set the source code file to be used by executables and libraries when
|
||||
# they need the amalgamation.
|
||||
#
|
||||
@ -282,7 +288,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_INTROSPECTION_PRAGMAS=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
|
||||
!ENDIF
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
@ -296,6 +301,13 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
|
||||
!ENDIF
|
||||
|
||||
# Should the rbu extension be enabled? If so, add compilation options
|
||||
# to enable it.
|
||||
#
|
||||
!IF $(RBU)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
# the Windows 10 platform.
|
||||
#
|
||||
@ -978,7 +990,7 @@ Replace.exe:
|
||||
sqlite3.def: Replace.exe $(LIBOBJ)
|
||||
echo EXPORTS > sqlite3.def
|
||||
dumpbin /all $(LIBOBJ) \
|
||||
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
||||
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser|rbu)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
||||
| sort >> sqlite3.def
|
||||
|
||||
$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
|
||||
|
||||
@ -161,7 +161,7 @@ AC_ARG_ENABLE(rtree, [AS_HELP_STRING(
|
||||
[--enable-rtree], [include rtree support [default=yes]])],
|
||||
[], [enable_rtree=yes])
|
||||
if test x"$enable_rtree" = "xyes"; then
|
||||
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE"
|
||||
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_GEOPOLY"
|
||||
fi
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
|
||||
# so you can encode the package version directly into the source files.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
AC_INIT([sqlite], [3.7.4])
|
||||
AC_INIT([sqlite], [3.31.0])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
||||
@ -153,7 +153,7 @@ Please `cd` to its location first.
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
PROJECT = sqlite3
|
||||
PROJECT = tclsqlite3
|
||||
!include "rules.vc"
|
||||
|
||||
# nmakehelp -V <file> <tag> will search the file for tag, skips until a
|
||||
@ -162,18 +162,15 @@ PROJECT = sqlite3
|
||||
|
||||
!if [echo REM = This file is generated from Makefile.vc > versions.vc]
|
||||
!endif
|
||||
# get project version from row "AC_INIT([sqlite], [3.7.14])"
|
||||
# get project version from row "AC_INIT([sqlite], [3.x.y])"
|
||||
!if [echo DOTVERSION = \>> versions.vc] \
|
||||
&& [nmakehlp -V ..\configure.in AC_INIT >> versions.vc]
|
||||
&& [nmakehlp -V ..\configure.ac AC_INIT >> versions.vc]
|
||||
!endif
|
||||
!include "versions.vc"
|
||||
|
||||
VERSION = $(DOTVERSION:.=)
|
||||
STUBPREFIX = $(PROJECT)stub
|
||||
|
||||
DLLOBJS = \
|
||||
$(TMP_DIR)\tclsqlite3.obj
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Target names and paths ( shouldn't need changing )
|
||||
#-------------------------------------------------------------------------
|
||||
@ -182,7 +179,7 @@ BINROOT = .
|
||||
ROOT = ..
|
||||
|
||||
PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
|
||||
PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
|
||||
PRJLIBNAME = $(PROJECT).$(EXT)
|
||||
PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
|
||||
|
||||
PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
|
||||
@ -204,6 +201,17 @@ DOCDIR = $(ROOT)\doc
|
||||
TOOLSDIR = $(ROOT)\tools
|
||||
COMPATDIR = $(ROOT)\compat
|
||||
|
||||
### Figure out where the primary source code file(s) is/are.
|
||||
!if exist("$(ROOT)\..\..\sqlite3.c") && exist("$(ROOT)\..\..\src\tclsqlite.c")
|
||||
SQL_INCLUDES = -I"$(ROOT)\..\.."
|
||||
SQLITE_SRCDIR = $(ROOT)\..\..
|
||||
TCLSQLITE_SRCDIR = $(ROOT)\..\..\src
|
||||
DLLOBJS = $(TMP_DIR)\sqlite3.obj $(TMP_DIR)\tclsqlite.obj
|
||||
!else
|
||||
TCLSQLITE_SRCDIR = $(ROOT)\generic
|
||||
DLLOBJS = $(TMP_DIR)\tclsqlite3.obj
|
||||
!endif
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Compile flags
|
||||
#---------------------------------------------------------------------
|
||||
@ -223,7 +231,7 @@ cdebug = -Z7 -WX -Od -GZ
|
||||
!endif
|
||||
|
||||
### Declarations common to all compiler options
|
||||
cflags = -nologo -c -W3 -YX -Fp$(TMP_DIR)^\
|
||||
cflags = -nologo -c -W3 -D_CRT_SECURE_NO_WARNINGS -YX -Fp$(TMP_DIR)^\
|
||||
|
||||
!if $(MSVCRT)
|
||||
!if $(DEBUG)
|
||||
@ -239,8 +247,8 @@ crt = -MT
|
||||
!endif
|
||||
!endif
|
||||
|
||||
INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" \
|
||||
-I"$(ROOT)\.."
|
||||
INCLUDES = $(SQL_INCLUDES) $(TCL_INCLUDES) -I"$(WINDIR)" \
|
||||
-I"$(GENERICDIR)" -I"$(ROOT)\.."
|
||||
BASE_CLFAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) \
|
||||
-DSQLITE_3_SUFFIX_ONLY=1 -DSQLITE_ENABLE_RTREE=1 \
|
||||
-DSQLITE_ENABLE_FTS3=1 -DSQLITE_OMIT_DEPRECATED=1
|
||||
@ -341,20 +349,17 @@ $(PRJSTUBLIB): $(PRJSTUBOBJS)
|
||||
# Implicit rules
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
{$(WINDIR)}.c{$(TMP_DIR)}.obj::
|
||||
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
|
||||
$<
|
||||
<<
|
||||
$(TMP_DIR)\sqlite3.obj: $(SQLITE_SRCDIR)\sqlite3.c
|
||||
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ \
|
||||
-c $(SQLITE_SRCDIR)\sqlite3.c
|
||||
|
||||
{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
|
||||
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
|
||||
$<
|
||||
<<
|
||||
$(TMP_DIR)\tclsqlite.obj: $(TCLSQLITE_SRCDIR)\tclsqlite.c
|
||||
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ \
|
||||
-c $(TCLSQLITE_SRCDIR)\tclsqlite.c
|
||||
|
||||
{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
|
||||
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
|
||||
$<
|
||||
<<
|
||||
$(TMP_DIR)\tclsqlite3.obj: $(TCLSQLITE_SRCDIR)\tclsqlite3.c
|
||||
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ \
|
||||
-c $(TCLSQLITE_SRCDIR)\tclsqlite3.c
|
||||
|
||||
{$(WINDIR)}.rc{$(TMP_DIR)}.res:
|
||||
$(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \
|
||||
|
||||
39
configure
vendored
39
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.30.1.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.31.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@ -587,8 +587,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlcipher'
|
||||
PACKAGE_TARNAME='sqlcipher'
|
||||
PACKAGE_VERSION='3.30.1'
|
||||
PACKAGE_STRING='sqlcipher 3.30.1'
|
||||
PACKAGE_VERSION='3.31.0'
|
||||
PACKAGE_STRING='sqlcipher 3.31.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -776,6 +776,7 @@ enable_amalgamation
|
||||
enable_load_extension
|
||||
enable_memsys5
|
||||
enable_memsys3
|
||||
enable_all
|
||||
enable_fts3
|
||||
enable_fts4
|
||||
enable_fts5
|
||||
@ -1337,7 +1338,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlcipher 3.30.1 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlcipher 3.31.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1402,7 +1403,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.30.1:";;
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.31.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1432,6 +1433,7 @@ Optional Features:
|
||||
Disable loading of external extensions
|
||||
--enable-memsys5 Enable MEMSYS5
|
||||
--enable-memsys3 Enable MEMSYS3
|
||||
--enable-all Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions
|
||||
--enable-fts3 Enable the FTS3 extension
|
||||
--enable-fts4 Enable the FTS4 extension
|
||||
--enable-fts5 Enable the FTS5 extension
|
||||
@ -1538,7 +1540,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlcipher configure 3.30.1
|
||||
sqlcipher configure 3.31.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@ -1957,7 +1959,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlcipher $as_me 3.30.1, which was
|
||||
It was created by sqlcipher $as_me 3.31.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -13026,6 +13028,15 @@ else
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
|
||||
########
|
||||
# The --enable-extensions argument is short-hand to enable
|
||||
# multiple extensions.
|
||||
# Check whether --enable-all was given.
|
||||
if test "${enable_all+set}" = set; then :
|
||||
enableval=$enable_all;
|
||||
fi
|
||||
|
||||
|
||||
#########
|
||||
# See whether we should enable Full Text Search extensions
|
||||
# Check whether --enable-fts3 was given.
|
||||
@ -13041,7 +13052,7 @@ if test "${enable_fts4+set}" = set; then :
|
||||
enableval=$enable_fts4;
|
||||
fi
|
||||
|
||||
if test "${enable_fts4}" = "yes" ; then
|
||||
if test "${enable_fts4}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
|
||||
$as_echo_n "checking for library containing log... " >&6; }
|
||||
@ -13105,7 +13116,7 @@ if test "${enable_fts5+set}" = set; then :
|
||||
enableval=$enable_fts5;
|
||||
fi
|
||||
|
||||
if test "${enable_fts5}" = "yes" ; then
|
||||
if test "${enable_fts5}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
|
||||
$as_echo_n "checking for library containing log... " >&6; }
|
||||
@ -13172,7 +13183,7 @@ if test "${enable_json1+set}" = set; then :
|
||||
enableval=$enable_json1;
|
||||
fi
|
||||
|
||||
if test "${enable_json1}" = "yes" ; then
|
||||
if test "${enable_json1}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1"
|
||||
fi
|
||||
|
||||
@ -13197,7 +13208,7 @@ else
|
||||
enable_geopoly=no
|
||||
fi
|
||||
|
||||
if test "${enable_geopoly}" = "yes" ; then
|
||||
if test "${enable_geopoly}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
|
||||
enable_rtree=yes
|
||||
fi
|
||||
@ -13220,7 +13231,7 @@ if test "${enable_session+set}" = set; then :
|
||||
enableval=$enable_session;
|
||||
fi
|
||||
|
||||
if test "${enable_session}" = "yes" ; then
|
||||
if test "${enable_session}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
fi
|
||||
@ -13808,7 +13819,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlcipher $as_me 3.30.1, which was
|
||||
This file was extended by sqlcipher $as_me 3.31.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -13874,7 +13885,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlcipher config.status 3.30.1
|
||||
sqlcipher config.status 3.31.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
16
configure.ac
16
configure.ac
@ -668,6 +668,12 @@ else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
########
|
||||
# The --enable-extensions argument is short-hand to enable
|
||||
# multiple extensions.
|
||||
AC_ARG_ENABLE(all, AC_HELP_STRING([--enable-all],
|
||||
[Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions]))
|
||||
|
||||
#########
|
||||
# See whether we should enable Full Text Search extensions
|
||||
AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3],
|
||||
@ -677,13 +683,13 @@ if test "${enable_fts3}" = "yes" ; then
|
||||
fi
|
||||
AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4],
|
||||
[Enable the FTS4 extension]))
|
||||
if test "${enable_fts4}" = "yes" ; then
|
||||
if test "${enable_fts4}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
|
||||
AC_SEARCH_LIBS([log],[m])
|
||||
fi
|
||||
AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5],
|
||||
[Enable the FTS5 extension]))
|
||||
if test "${enable_fts5}" = "yes" ; then
|
||||
if test "${enable_fts5}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
|
||||
AC_SEARCH_LIBS([log],[m])
|
||||
fi
|
||||
@ -691,7 +697,7 @@ fi
|
||||
#########
|
||||
# See whether we should enable JSON1
|
||||
AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1],[Enable the JSON1 extension]))
|
||||
if test "${enable_json1}" = "yes" ; then
|
||||
if test "${enable_json1}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1"
|
||||
fi
|
||||
|
||||
@ -709,7 +715,7 @@ fi
|
||||
AC_ARG_ENABLE(geopoly, AC_HELP_STRING([--enable-geopoly],
|
||||
[Enable the GEOPOLY extension]),
|
||||
[enable_geopoly=yes],[enable_geopoly=no])
|
||||
if test "${enable_geopoly}" = "yes" ; then
|
||||
if test "${enable_geopoly}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
|
||||
enable_rtree=yes
|
||||
fi
|
||||
@ -726,7 +732,7 @@ fi
|
||||
# See whether we should enable the SESSION extension
|
||||
AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session],
|
||||
[Enable the SESSION extension]))
|
||||
if test "${enable_session}" = "yes" ; then
|
||||
if test "${enable_session}" = "yes" -o "${enable_all}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
fi
|
||||
|
||||
142
doc/trusted-schema.md
Normal file
142
doc/trusted-schema.md
Normal file
@ -0,0 +1,142 @@
|
||||
# The new-security-options branch
|
||||
|
||||
## The problem that the [new-security-options](/timeline?r=new-security-options) branch tries to solve
|
||||
|
||||
An attacker might modify the schema of an SQLite database by adding
|
||||
structures that cause code to run when some other application opens and
|
||||
reads the database. For example, the attacker might replace a table
|
||||
definition with a view. Or the attacker might add triggers to tables
|
||||
or views, or add new CHECK constraints or generated columns or indexes
|
||||
with expressions in the index list or in the WHERE clause. If the
|
||||
added features invoke SQL functions or virtual tables with side effects,
|
||||
that might cause harm to the system if run by a high-privilege victim.
|
||||
Or, the added features might exfiltrate information if the database is
|
||||
read by a high-privilege victim.
|
||||
|
||||
The changes in this branch strive to make it easier for high-privilege
|
||||
applications to safely read SQLite database files that might have been
|
||||
maliciously corrupted by an attacker.
|
||||
|
||||
## Overview of changes in [new-security-options](/timeline?r=new-security-options)
|
||||
|
||||
The basic idea is to tag every SQL function and virtual table with one
|
||||
of three risk levels:
|
||||
|
||||
1. Innocuous
|
||||
2. Normal
|
||||
3. Direct-Only
|
||||
|
||||
Innocuous functions/vtabs are safe and can be used at any time.
|
||||
Direct-only elements, in contrast, might have cause side-effects and
|
||||
should only be used from top-level SQL, not from within triggers or views nor
|
||||
in elements of the schema such as CHECK constraint, DEFAULT values,
|
||||
generated columns, index expressions, or in the WHERE clause of a
|
||||
partial index that are potentially under the control of an attacker.
|
||||
Normal elements behave like Innocuous if TRUSTED\_SCHEMA=on
|
||||
and behave like direct-only if TRUSTED\_SCHEMA=off.
|
||||
|
||||
Application-defined functions and virtual tables go in as Normal unless
|
||||
the application takes deliberate steps to change the risk level.
|
||||
|
||||
For backwards compatibility, the default is TRUSTED\_SCHEMA=on. Documentation
|
||||
will be updated to recommend applications turn TRUSTED\_SCHEMA to off.
|
||||
|
||||
An innocuous function or virtual table is one that can only read content
|
||||
from the database file in which it resides, and can only alter the database
|
||||
in which it resides. Most SQL functions are innocuous. For example, there
|
||||
is no harm in an attacker running the abs() function.
|
||||
|
||||
Direct-only elements that have side-effects that go outside the database file
|
||||
in which it lives, or return information from outside of the database file.
|
||||
Examples of direct-only elements include:
|
||||
|
||||
1. The fts3\_tokenizer() function
|
||||
2. The writefile() function
|
||||
3. The readfile() function
|
||||
4. The zipvfs virtual table
|
||||
5. The csv virtual table
|
||||
|
||||
We do not want an attacker to be able to add these kinds of things to
|
||||
the database schema and possibly trick a high-privilege application
|
||||
from performing any of these actions. Therefore, functions and vtabs
|
||||
with side-effects are marked as Direct-Only.
|
||||
|
||||
Legacy applications might add other risky functions or vtabs. Those will
|
||||
go in as "Normal" by default. For optimal security, we want those risky
|
||||
app-defined functions and vtabs to be direct-only, but making that the
|
||||
default might break some legacy applications. Hence, all app-defined
|
||||
functions and vtabs go in as Normal, but the application can switch them
|
||||
over to "Direct-Only" behavior using a single pragma.
|
||||
|
||||
The restrictions on the use of functions and virtual tables do not apply
|
||||
to TEMP. A TEMP VIEW or a TEMP TRIGGER can use any valid SQL function
|
||||
or virtual table. The idea is that TEMP views and triggers must be
|
||||
directly created by the application and are thus under the control of the
|
||||
application. TEMP views and triggers cannot be created by an attacker who
|
||||
corrupts the schema of a persistent database file. Hence TEMP views and
|
||||
triggers are safe.
|
||||
|
||||
## Specific changes
|
||||
|
||||
1. New sqlite3\_db\_config() option SQLITE\_DBCONFIG\_TRUSTED\_SCHEMA for
|
||||
turning TRUSTED\_SCHEMA on and off. It defaults to ON.
|
||||
|
||||
2. Compile-time option -DSQLITE\_TRUSTED\_SCHEMA=0 causes the default
|
||||
TRUSTED\_SCHEMA setting to be off.
|
||||
|
||||
3. New pragma "PRAGMA trusted\_schema=(ON\|OFF);". This provides access
|
||||
to the TRUSTED_SCHEMA setting for application coded using scripting
|
||||
languages or other secondary languages where they are unable to make
|
||||
calls to sqlite3\_db\_config().
|
||||
|
||||
4. New options for the "enc" parameter to sqlite3\_create\_function() and
|
||||
its kin:
|
||||
<ol type="a">
|
||||
<li> _SQLITE\_INNOCUOUS_ → tags the new functions as Innocuous
|
||||
<li> _SQLITE\_DIRECTONLY_ → tags the new functions as Direct-Only
|
||||
</ol>
|
||||
|
||||
5. New options to sqlite3\_vtab\_config():
|
||||
<ol type="a">
|
||||
<li> _SQLITE\_VTAB\_INNOCUOUS_ → tags the vtab as Innocuous
|
||||
<li> _SQLITE\_VTAB\_DIRECTONLY_ → tags the vtab as Direct-Only
|
||||
</ol>
|
||||
|
||||
6. Change many of the functions and virtual tables in the SQLite source
|
||||
tree to use one of the tags above.
|
||||
|
||||
7. Enhanced PRAGMA function\_list and virtual-table "pragma\_function\_list"
|
||||
with additional columns. The columns now are:
|
||||
<ul>
|
||||
<li> _name_ → Name of the function
|
||||
<li> _builtin_ → 1 for built-in functions. 0 otherwise.
|
||||
<li> _type_ → 's'=Scalar, 'a'=Aggregate, 'w'=Window
|
||||
<li> _enc_ → 'utf8', 'utf16le', or 'utf16be'
|
||||
<li> _narg_ → number of argument
|
||||
<li> _flags_ → Bitmask of SQLITE\_INNOCUOUS, SQLITE\_DIRECTONLY,
|
||||
SQLITE\_DETERMINISTIC, SQLITE\_SUBTYPE, and
|
||||
SQLITE\_FUNC\_INTERNAL flags.
|
||||
</ul>
|
||||
<p>The last four columns are new.
|
||||
|
||||
8. The function\_list PRAGMA now also shows all entries for each function.
|
||||
So, for example, if a function can take either 2 or 3 arguments,
|
||||
there are separate rows for the 2-argument and 3-argument versions of
|
||||
the function.
|
||||
|
||||
## Additional Notes
|
||||
|
||||
The function_list enhancements allow the application to query the set
|
||||
of SQL functions that meet various criteria. For example, to see all
|
||||
SQL functions that are never allowed to be used in the schema or in
|
||||
trigger or views:
|
||||
|
||||
~~~
|
||||
SELECT DISTINCT name FROM pragma_function_list
|
||||
WHERE (flags & 0x80000)!=0
|
||||
ORDER BY name;
|
||||
~~~
|
||||
|
||||
Doing the same is not possible for virtual tables, as a virtual table
|
||||
might be Innocuous, Normal, or Direct-Only depending on the arguments
|
||||
passed into the xConnect method.
|
||||
@ -10,8 +10,8 @@
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(SQLITEEXPERT_H)
|
||||
#define SQLITEEXPERT_H 1
|
||||
#include "sqlite3.h"
|
||||
|
||||
typedef struct sqlite3expert sqlite3expert;
|
||||
@ -165,4 +165,4 @@ const char *sqlite3_expert_report(sqlite3expert*, int iStmt, int eReport);
|
||||
*/
|
||||
void sqlite3_expert_destroy(sqlite3expert*);
|
||||
|
||||
|
||||
#endif /* !defined(SQLITEEXPERT_H) */
|
||||
|
||||
137
ext/fts3/fts3.c
137
ext/fts3/fts3.c
@ -308,18 +308,6 @@
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following are copied from sqliteInt.h.
|
||||
**
|
||||
** Constants for the largest and smallest possible 64-bit signed integers.
|
||||
** These macros are designed to work correctly on both 32-bit and 64-bit
|
||||
** compilers.
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
static int fts3EvalNext(Fts3Cursor *pCsr);
|
||||
static int fts3EvalStart(Fts3Cursor *pCsr);
|
||||
static int fts3TermSegReaderCursor(
|
||||
@ -364,12 +352,7 @@ int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){
|
||||
v = (*ptr++); \
|
||||
if( (v & mask2)==0 ){ var = v; return ret; }
|
||||
|
||||
/*
|
||||
** Read a 64-bit variable-length integer from memory starting at p[0].
|
||||
** Return the number of bytes read, or 0 on error.
|
||||
** The value is stored in *v.
|
||||
*/
|
||||
int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
|
||||
int sqlite3Fts3GetVarintU(const char *pBuf, sqlite_uint64 *v){
|
||||
const unsigned char *p = (const unsigned char*)pBuf;
|
||||
const unsigned char *pStart = p;
|
||||
u32 a;
|
||||
@ -391,6 +374,41 @@ int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
|
||||
return (int)(p - pStart);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a 64-bit variable-length integer from memory starting at p[0].
|
||||
** Return the number of bytes read, or 0 on error.
|
||||
** The value is stored in *v.
|
||||
*/
|
||||
int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
|
||||
return sqlite3Fts3GetVarintU(pBuf, (sqlite3_uint64*)v);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a 64-bit variable-length integer from memory starting at p[0] and
|
||||
** not extending past pEnd[-1].
|
||||
** Return the number of bytes read, or 0 on error.
|
||||
** The value is stored in *v.
|
||||
*/
|
||||
int sqlite3Fts3GetVarintBounded(
|
||||
const char *pBuf,
|
||||
const char *pEnd,
|
||||
sqlite_int64 *v
|
||||
){
|
||||
const unsigned char *p = (const unsigned char*)pBuf;
|
||||
const unsigned char *pStart = p;
|
||||
const unsigned char *pX = (const unsigned char*)pEnd;
|
||||
u64 b = 0;
|
||||
int shift;
|
||||
for(shift=0; shift<=63; shift+=7){
|
||||
u64 c = p<pX ? *p : 0;
|
||||
p++;
|
||||
b += (c&0x7F) << shift;
|
||||
if( (c & 0x80)==0 ) break;
|
||||
}
|
||||
*v = b;
|
||||
return (int)(p - pStart);
|
||||
}
|
||||
|
||||
/*
|
||||
** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to
|
||||
** a non-negative 32-bit integer before it is returned.
|
||||
@ -1486,6 +1504,10 @@ static int fts3InitVtab(
|
||||
fts3DatabasePageSize(&rc, p);
|
||||
p->nNodeSize = p->nPgsz-35;
|
||||
|
||||
#if defined(SQLITE_DEBUG)||defined(SQLITE_TEST)
|
||||
p->nMergeCount = FTS3_MERGE_COUNT;
|
||||
#endif
|
||||
|
||||
/* Declare the table schema to SQLite. */
|
||||
fts3DeclareVtab(&rc, p);
|
||||
|
||||
@ -1581,6 +1603,10 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
int iDocidLe = -1; /* Index of docid<=x constraint, if present */
|
||||
int iIdx;
|
||||
|
||||
if( p->bLock ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* By default use a full table scan. This is an expensive option,
|
||||
** so search through the constraints to see if a more efficient
|
||||
** strategy is possible.
|
||||
@ -1779,7 +1805,11 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
|
||||
}else{
|
||||
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
|
||||
if( !zSql ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v3(p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
|
||||
p->bLock++;
|
||||
rc = sqlite3_prepare_v3(
|
||||
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
|
||||
);
|
||||
p->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1;
|
||||
@ -1797,11 +1827,15 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
|
||||
if( pCsr->isRequireSeek ){
|
||||
rc = fts3CursorSeekStmt(pCsr);
|
||||
if( rc==SQLITE_OK ){
|
||||
Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab;
|
||||
pTab->bLock++;
|
||||
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
|
||||
pCsr->isRequireSeek = 0;
|
||||
if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
|
||||
pTab->bLock--;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
pTab->bLock--;
|
||||
rc = sqlite3_reset(pCsr->pStmt);
|
||||
if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){
|
||||
/* If no row was found and no error has occurred, then the %_content
|
||||
@ -1973,7 +2007,7 @@ static int fts3SelectLeaf(
|
||||
|
||||
fts3GetVarint32(zNode, &iHeight);
|
||||
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
|
||||
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
|
||||
assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
|
||||
|
||||
if( rc==SQLITE_OK && iHeight>1 ){
|
||||
char *zBlob = 0; /* Blob read from %_segments table */
|
||||
@ -1993,7 +2027,13 @@ static int fts3SelectLeaf(
|
||||
rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
|
||||
int iNewHeight = 0;
|
||||
fts3GetVarint32(zBlob, &iNewHeight);
|
||||
if( iNewHeight>=iHeight ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
}else{
|
||||
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zBlob);
|
||||
}
|
||||
@ -2448,12 +2488,12 @@ static void fts3GetDeltaVarint3(
|
||||
if( *pp>=pEnd ){
|
||||
*pp = 0;
|
||||
}else{
|
||||
sqlite3_int64 iVal;
|
||||
*pp += sqlite3Fts3GetVarint(*pp, &iVal);
|
||||
u64 iVal;
|
||||
*pp += sqlite3Fts3GetVarintU(*pp, &iVal);
|
||||
if( bDescIdx ){
|
||||
*pVal -= iVal;
|
||||
*pVal = (i64)((u64)*pVal - iVal);
|
||||
}else{
|
||||
*pVal += iVal;
|
||||
*pVal = (i64)((u64)*pVal + iVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2480,15 +2520,16 @@ static void fts3PutDeltaVarint3(
|
||||
int *pbFirst, /* IN/OUT: True after first int written */
|
||||
sqlite3_int64 iVal /* Write this value to the list */
|
||||
){
|
||||
sqlite3_int64 iWrite;
|
||||
sqlite3_uint64 iWrite;
|
||||
if( bDescIdx==0 || *pbFirst==0 ){
|
||||
iWrite = iVal - *piPrev;
|
||||
assert_fts3_nc( *pbFirst==0 || iVal>=*piPrev );
|
||||
iWrite = (u64)iVal - (u64)*piPrev;
|
||||
}else{
|
||||
iWrite = *piPrev - iVal;
|
||||
assert_fts3_nc( *piPrev>=iVal );
|
||||
iWrite = (u64)*piPrev - (u64)iVal;
|
||||
}
|
||||
assert( *pbFirst || *piPrev==0 );
|
||||
assert_fts3_nc( *pbFirst==0 || iWrite>0 );
|
||||
assert( *pbFirst==0 || iWrite>=0 );
|
||||
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
|
||||
*piPrev = iVal;
|
||||
*pbFirst = 1;
|
||||
@ -2504,7 +2545,8 @@ static void fts3PutDeltaVarint3(
|
||||
** Using this makes it easier to write code that can merge doclists that are
|
||||
** sorted in either ascending or descending order.
|
||||
*/
|
||||
#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1-i2))
|
||||
/* #define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i64)((u64)i1-i2)) */
|
||||
#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1>i2?1:((i1==i2)?0:-1)))
|
||||
|
||||
/*
|
||||
** This function does an "OR" merge of two doclists (output contains all
|
||||
@ -2918,7 +2960,7 @@ static int fts3SegReaderCursor(
|
||||
** Fts3SegReaderPending might segfault, as the data structures used by
|
||||
** fts4aux are not completely populated. So it's easiest to filter these
|
||||
** calls out here. */
|
||||
if( iLevel<0 && p->aIndex ){
|
||||
if( iLevel<0 && p->aIndex && p->iPrevLangid==iLangid ){
|
||||
Fts3SegReader *pSeg = 0;
|
||||
rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg);
|
||||
if( rc==SQLITE_OK && pSeg ){
|
||||
@ -3181,6 +3223,8 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
int rc;
|
||||
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
|
||||
if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){
|
||||
Fts3Table *pTab = (Fts3Table*)pCursor->pVtab;
|
||||
pTab->bLock++;
|
||||
if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){
|
||||
pCsr->isEof = 1;
|
||||
rc = sqlite3_reset(pCsr->pStmt);
|
||||
@ -3188,6 +3232,7 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
pTab->bLock--;
|
||||
}else{
|
||||
rc = fts3EvalNext((Fts3Cursor *)pCursor);
|
||||
}
|
||||
@ -3248,6 +3293,10 @@ static int fts3FilterMethod(
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
UNUSED_PARAMETER(nVal);
|
||||
|
||||
if( p->bLock ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
eSearch = (idxNum & 0x0000FFFF);
|
||||
assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
|
||||
assert( p->pSegments==0 );
|
||||
@ -3319,7 +3368,11 @@ static int fts3FilterMethod(
|
||||
);
|
||||
}
|
||||
if( zSql ){
|
||||
rc = sqlite3_prepare_v3(p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
|
||||
p->bLock++;
|
||||
rc = sqlite3_prepare_v3(
|
||||
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
|
||||
);
|
||||
p->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
@ -4336,7 +4389,7 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
|
||||
int bIncrOk = (bOptOk
|
||||
&& pCsr->bDesc==pTab->bDescIdx
|
||||
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
&& pTab->bNoIncrDoclist==0
|
||||
#endif
|
||||
);
|
||||
@ -4478,15 +4531,16 @@ static void fts3EvalDlPhraseNext(
|
||||
u8 *pbEof
|
||||
){
|
||||
char *pIter; /* Used to iterate through aAll */
|
||||
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
|
||||
char *pEnd; /* 1 byte past end of aAll */
|
||||
|
||||
if( pDL->pNextDocid ){
|
||||
pIter = pDL->pNextDocid;
|
||||
assert( pDL->aAll!=0 || pIter==0 );
|
||||
}else{
|
||||
pIter = pDL->aAll;
|
||||
}
|
||||
|
||||
if( pIter>=pEnd ){
|
||||
if( pIter==0 || pIter>=(pEnd = pDL->aAll + pDL->nAll) ){
|
||||
/* We have already reached the end of this doclist. EOF. */
|
||||
*pbEof = 1;
|
||||
}else{
|
||||
@ -4858,12 +4912,13 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
|
||||
rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
a = sqlite3_column_blob(pStmt, 0);
|
||||
assert( a );
|
||||
|
||||
pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
|
||||
a += sqlite3Fts3GetVarint(a, &nDoc);
|
||||
while( a<pEnd ){
|
||||
a += sqlite3Fts3GetVarint(a, &nByte);
|
||||
testcase( a==0 ); /* If %_stat.value set to X'' */
|
||||
if( a ){
|
||||
pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
|
||||
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
|
||||
while( a<pEnd ){
|
||||
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nByte);
|
||||
}
|
||||
}
|
||||
if( nDoc==0 || nByte==0 ){
|
||||
sqlite3_reset(pStmt);
|
||||
|
||||
@ -196,6 +196,9 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
# define TESTONLY(X)
|
||||
#endif
|
||||
|
||||
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@ -239,6 +242,7 @@ struct Fts3Table {
|
||||
char *zLanguageid; /* languageid=xxx option, or NULL */
|
||||
int nAutoincrmerge; /* Value configured by 'automerge' */
|
||||
u32 nLeafAdd; /* Number of leaf blocks added this trans */
|
||||
int bLock; /* Used to prevent recursive content= tbls */
|
||||
|
||||
/* Precompiled statements used by the implementation. Each of these
|
||||
** statements is run and reset within a single virtual table API call.
|
||||
@ -297,13 +301,23 @@ struct Fts3Table {
|
||||
int mxSavepoint; /* Largest valid xSavepoint integer */
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
/* True to disable the incremental doclist optimization. This is controled
|
||||
** by special insert command 'test-no-incr-doclist'. */
|
||||
int bNoIncrDoclist;
|
||||
|
||||
/* Number of segments in a level */
|
||||
int nMergeCount;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Macro to find the number of segments to merge */
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
# define MergeCount(P) ((P)->nMergeCount)
|
||||
#else
|
||||
# define MergeCount(P) FTS3_MERGE_COUNT
|
||||
#endif
|
||||
|
||||
/*
|
||||
** When the core wants to read from the virtual table, it creates a
|
||||
** virtual table cursor (an instance of the following structure) using
|
||||
@ -567,6 +581,8 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
|
||||
void sqlite3Fts3ErrMsg(char**,const char*,...);
|
||||
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
|
||||
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
|
||||
int sqlite3Fts3GetVarintU(const char *, sqlite_uint64 *);
|
||||
int sqlite3Fts3GetVarintBounded(const char*,const char*,sqlite3_int64*);
|
||||
int sqlite3Fts3GetVarint32(const char *, int *);
|
||||
int sqlite3Fts3VarintLen(sqlite3_uint64);
|
||||
void sqlite3Fts3Dequote(char *);
|
||||
|
||||
@ -560,7 +560,7 @@ static int fts3BestSnippet(
|
||||
/* Set the *pmSeen output variable. */
|
||||
for(i=0; i<nList; i++){
|
||||
if( sIter.aPhrase[i].pHead ){
|
||||
*pmSeen |= (u64)1 << i;
|
||||
*pmSeen |= (u64)1 << (i%64);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1038,11 +1038,15 @@ static int fts3MatchinfoSelectDoctotal(
|
||||
Fts3Table *pTab,
|
||||
sqlite3_stmt **ppStmt,
|
||||
sqlite3_int64 *pnDoc,
|
||||
const char **paLen
|
||||
const char **paLen,
|
||||
const char **ppEnd
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
const char *a;
|
||||
const char *pEnd;
|
||||
sqlite3_int64 nDoc;
|
||||
int n;
|
||||
|
||||
|
||||
if( !*ppStmt ){
|
||||
int rc = sqlite3Fts3SelectDoctotal(pTab, ppStmt);
|
||||
@ -1051,12 +1055,20 @@ static int fts3MatchinfoSelectDoctotal(
|
||||
pStmt = *ppStmt;
|
||||
assert( sqlite3_data_count(pStmt)==1 );
|
||||
|
||||
n = sqlite3_column_bytes(pStmt, 0);
|
||||
a = sqlite3_column_blob(pStmt, 0);
|
||||
a += sqlite3Fts3GetVarint(a, &nDoc);
|
||||
if( nDoc==0 ) return FTS_CORRUPT_VTAB;
|
||||
*pnDoc = (u32)nDoc;
|
||||
if( a==0 ){
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
pEnd = a + n;
|
||||
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
|
||||
if( nDoc<=0 || a>pEnd ){
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
*pnDoc = nDoc;
|
||||
|
||||
if( paLen ) *paLen = a;
|
||||
if( ppEnd ) *ppEnd = pEnd;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -1237,7 +1249,7 @@ static int fts3MatchinfoValues(
|
||||
case FTS3_MATCHINFO_NDOC:
|
||||
if( bGlobal ){
|
||||
sqlite3_int64 nDoc = 0;
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0, 0);
|
||||
pInfo->aMatchinfo[0] = (u32)nDoc;
|
||||
}
|
||||
break;
|
||||
@ -1246,14 +1258,19 @@ static int fts3MatchinfoValues(
|
||||
if( bGlobal ){
|
||||
sqlite3_int64 nDoc; /* Number of rows in table */
|
||||
const char *a; /* Aggregate column length array */
|
||||
const char *pEnd; /* First byte past end of length array */
|
||||
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a);
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a, &pEnd);
|
||||
if( rc==SQLITE_OK ){
|
||||
int iCol;
|
||||
for(iCol=0; iCol<pInfo->nCol; iCol++){
|
||||
u32 iVal;
|
||||
sqlite3_int64 nToken;
|
||||
a += sqlite3Fts3GetVarint(a, &nToken);
|
||||
if( a>pEnd ){
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
break;
|
||||
}
|
||||
iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
|
||||
pInfo->aMatchinfo[iCol] = iVal;
|
||||
}
|
||||
@ -1267,9 +1284,14 @@ static int fts3MatchinfoValues(
|
||||
if( rc==SQLITE_OK ){
|
||||
int iCol;
|
||||
const char *a = sqlite3_column_blob(pSelectDocsize, 0);
|
||||
const char *pEnd = a + sqlite3_column_bytes(pSelectDocsize, 0);
|
||||
for(iCol=0; iCol<pInfo->nCol; iCol++){
|
||||
sqlite3_int64 nToken;
|
||||
a += sqlite3Fts3GetVarint(a, &nToken);
|
||||
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nToken);
|
||||
if( a>pEnd ){
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
break;
|
||||
}
|
||||
pInfo->aMatchinfo[iCol] = (u32)nToken;
|
||||
}
|
||||
}
|
||||
@ -1300,7 +1322,7 @@ static int fts3MatchinfoValues(
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
if( bGlobal ){
|
||||
if( pCsr->pDeferred ){
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc, 0);
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
|
||||
|
||||
@ -390,7 +390,9 @@ int queryTokenizer(
|
||||
|
||||
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
|
||||
if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB
|
||||
&& sqlite3_column_bytes(pStmt, 0)==sizeof(*pp)
|
||||
){
|
||||
memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
|
||||
}
|
||||
}
|
||||
@ -479,7 +481,7 @@ int sqlite3Fts3InitHashTable(
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
void *p = (void *)pHash;
|
||||
const int any = SQLITE_ANY;
|
||||
const int any = SQLITE_UTF8|SQLITE_DIRECTONLY;
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
char *zTest = 0;
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define FTS_MAX_APPENDABLE_HEIGHT 16
|
||||
|
||||
@ -67,7 +67,7 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The two values that may be meaningfully bound to the :1 parameter in
|
||||
** The values that may be meaningfully bound to the :1 parameter in
|
||||
** statements SQL_REPLACE_STAT and SQL_SELECT_STAT.
|
||||
*/
|
||||
#define FTS_STAT_DOCTOTAL 0
|
||||
@ -335,7 +335,7 @@ static int fts3SqlStmt(
|
||||
** returns zero rows. */
|
||||
/* 28 */ "SELECT level, count(*) AS cnt FROM %Q.'%q_segdir' "
|
||||
" GROUP BY level HAVING cnt>=?"
|
||||
" ORDER BY (level %% 1024) ASC LIMIT 1",
|
||||
" ORDER BY (level %% 1024) ASC, 2 DESC LIMIT 1",
|
||||
|
||||
/* Estimate the upper limit on the number of leaf nodes in a new segment
|
||||
** created by merging the oldest :2 segments from absolute level :1. See
|
||||
@ -696,7 +696,7 @@ static int fts3PendingListAppend(
|
||||
assert( !p || p->iLastDocid<=iDocid );
|
||||
|
||||
if( !p || p->iLastDocid!=iDocid ){
|
||||
sqlite3_int64 iDelta = iDocid - (p ? p->iLastDocid : 0);
|
||||
u64 iDelta = (u64)iDocid - (u64)(p ? p->iLastDocid : 0);
|
||||
if( p ){
|
||||
assert( p->nData<p->nSpace );
|
||||
assert( p->aData[p->nData]==0 );
|
||||
@ -1153,7 +1153,7 @@ static int fts3AllocateSegdirIdx(
|
||||
** segment and allocate (newly freed) index 0 at level iLevel. Otherwise,
|
||||
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
|
||||
*/
|
||||
if( iNext>=FTS3_MERGE_COUNT ){
|
||||
if( iNext>=MergeCount(p) ){
|
||||
fts3LogMerge(16, getAbsoluteLevel(p, iLangid, iIndex, iLevel));
|
||||
rc = fts3SegmentMerge(p, iLangid, iIndex, iLevel);
|
||||
*piIdx = 0;
|
||||
@ -1237,6 +1237,8 @@ int sqlite3Fts3ReadBlock(
|
||||
}
|
||||
*paBlob = aByte;
|
||||
}
|
||||
}else if( rc==SQLITE_ERROR ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -1379,7 +1381,7 @@ static int fts3SegReaderNext(
|
||||
pNext += fts3GetVarint32(pNext, &nSuffix);
|
||||
if( nSuffix<=0
|
||||
|| (&pReader->aNode[pReader->nNode] - pNext)<nSuffix
|
||||
|| nPrefix>pReader->nTermAlloc
|
||||
|| nPrefix>pReader->nTerm
|
||||
){
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
@ -1529,18 +1531,18 @@ static int fts3SegReaderNextDocid(
|
||||
}else{
|
||||
rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_int64 iDelta;
|
||||
pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta);
|
||||
u64 iDelta;
|
||||
pReader->pOffsetList = p + sqlite3Fts3GetVarintU(p, &iDelta);
|
||||
if( pTab->bDescIdx ){
|
||||
pReader->iDocid -= iDelta;
|
||||
pReader->iDocid = (i64)((u64)pReader->iDocid - iDelta);
|
||||
}else{
|
||||
pReader->iDocid += iDelta;
|
||||
pReader->iDocid = (i64)((u64)pReader->iDocid + iDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -2030,6 +2032,11 @@ static int fts3NodeAddTerm(
|
||||
nPrefix = fts3PrefixCompress(pTree->zTerm, pTree->nTerm, zTerm, nTerm);
|
||||
nSuffix = nTerm-nPrefix;
|
||||
|
||||
/* If nSuffix is zero or less, then zTerm/nTerm must be a prefix of
|
||||
** pWriter->zTerm/pWriter->nTerm. i.e. must be equal to or less than when
|
||||
** compared with BINARY collation. This indicates corruption. */
|
||||
if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
|
||||
|
||||
nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix;
|
||||
if( nReq<=p->nNodeSize || !pTree->zTerm ){
|
||||
|
||||
@ -2274,6 +2281,7 @@ static int fts3SegWriterAdd(
|
||||
int rc;
|
||||
|
||||
/* The current leaf node is full. Write it out to the database. */
|
||||
if( pWriter->iFree==LARGEST_INT64 ) return FTS_CORRUPT_VTAB;
|
||||
rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
p->nLeafAdd++;
|
||||
@ -2323,9 +2331,11 @@ static int fts3SegWriterAdd(
|
||||
/* Append the prefix-compressed term and doclist to the buffer. */
|
||||
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nPrefix);
|
||||
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nSuffix);
|
||||
assert( nSuffix>0 );
|
||||
memcpy(&pWriter->aData[nData], &zTerm[nPrefix], nSuffix);
|
||||
nData += nSuffix;
|
||||
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nDoclist);
|
||||
assert( nDoclist>0 );
|
||||
memcpy(&pWriter->aData[nData], aDoclist, nDoclist);
|
||||
pWriter->nData = nData + nDoclist;
|
||||
|
||||
@ -2345,6 +2355,7 @@ static int fts3SegWriterAdd(
|
||||
pWriter->zTerm = zNew;
|
||||
}
|
||||
assert( pWriter->zTerm==pWriter->zMalloc );
|
||||
assert( nTerm>0 );
|
||||
memcpy(pWriter->zTerm, zTerm, nTerm);
|
||||
}else{
|
||||
pWriter->zTerm = (char *)zTerm;
|
||||
@ -2653,6 +2664,7 @@ static int fts3MsrBufferData(
|
||||
pMsr->aBuffer = pNew;
|
||||
}
|
||||
|
||||
assert( nList>0 );
|
||||
memcpy(pMsr->aBuffer, pList, nList);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -2966,14 +2978,12 @@ int sqlite3Fts3SegReaderStep(
|
||||
** doclist. */
|
||||
sqlite3_int64 iDelta;
|
||||
if( p->bDescIdx && nDoclist>0 ){
|
||||
iDelta = iPrev - iDocid;
|
||||
if( iPrev<=iDocid ) return FTS_CORRUPT_VTAB;
|
||||
iDelta = (i64)((u64)iPrev - (u64)iDocid);
|
||||
}else{
|
||||
iDelta = iDocid - iPrev;
|
||||
if( nDoclist>0 && iPrev>=iDocid ) return FTS_CORRUPT_VTAB;
|
||||
iDelta = (i64)((u64)iDocid - (u64)iPrev);
|
||||
}
|
||||
if( iDelta<=0 && (nDoclist>0 || iDelta!=iDocid) ){
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
assert( nDoclist>0 || iDelta==iDocid );
|
||||
|
||||
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
|
||||
if( nDoclist+nByte>pCsr->nBuffer ){
|
||||
@ -3255,7 +3265,7 @@ static int fts3SegmentMerge(
|
||||
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
assert( pWriter || bIgnoreEmpty );
|
||||
assert_fts3_nc( pWriter || bIgnoreEmpty );
|
||||
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
|
||||
rc = fts3DeleteSegdir(
|
||||
@ -3482,7 +3492,10 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
|
||||
int rc;
|
||||
sqlite3_stmt *pAllLangid = 0;
|
||||
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
|
||||
rc = sqlite3Fts3PendingTermsFlush(p);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid);
|
||||
@ -3503,7 +3516,6 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
|
||||
}
|
||||
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
|
||||
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
|
||||
}
|
||||
@ -3841,6 +3853,7 @@ static int fts3IncrmergePush(
|
||||
** be added to. */
|
||||
nPrefix = fts3PrefixCompress(pNode->key.a, pNode->key.n, zTerm, nTerm);
|
||||
nSuffix = nTerm - nPrefix;
|
||||
if(nSuffix<=0 ) return FTS_CORRUPT_VTAB;
|
||||
nSpace = sqlite3Fts3VarintLen(nPrefix);
|
||||
nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
|
||||
|
||||
@ -4235,6 +4248,10 @@ static int fts3IncrmergeLoad(
|
||||
pWriter->bNoLeafData = (pWriter->nLeafData==0);
|
||||
nRoot = sqlite3_column_bytes(pSelect, 4);
|
||||
aRoot = sqlite3_column_blob(pSelect, 4);
|
||||
if( aRoot==0 ){
|
||||
sqlite3_reset(pSelect);
|
||||
return nRoot ? SQLITE_NOMEM : FTS_CORRUPT_VTAB;
|
||||
}
|
||||
}else{
|
||||
return sqlite3_reset(pSelect);
|
||||
}
|
||||
@ -4270,6 +4287,10 @@ static int fts3IncrmergeLoad(
|
||||
int i;
|
||||
int nHeight = (int)aRoot[0];
|
||||
NodeWriter *pNode;
|
||||
if( nHeight<1 || nHeight>FTS_MAX_APPENDABLE_HEIGHT ){
|
||||
sqlite3_reset(pSelect);
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
|
||||
pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT;
|
||||
pWriter->iStart = iStart;
|
||||
@ -4830,13 +4851,17 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
|
||||
const int nHint = pHint->n;
|
||||
int i;
|
||||
|
||||
i = pHint->n-2;
|
||||
i = pHint->n-1;
|
||||
if( (pHint->a[i] & 0x80) ) return FTS_CORRUPT_VTAB;
|
||||
while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
|
||||
if( i==0 ) return FTS_CORRUPT_VTAB;
|
||||
i--;
|
||||
while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
|
||||
|
||||
pHint->n = i;
|
||||
i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel);
|
||||
i += fts3GetVarint32(&pHint->a[i], pnInput);
|
||||
assert( i<=nHint );
|
||||
if( i!=nHint ) return FTS_CORRUPT_VTAB;
|
||||
|
||||
return SQLITE_OK;
|
||||
@ -4906,8 +4931,14 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
|
||||
rc = fts3IncrmergeHintPop(&hint, &iHintAbsLevel, &nHintSeg);
|
||||
if( nSeg<0 || (iAbsLevel % nMod) >= (iHintAbsLevel % nMod) ){
|
||||
/* Based on the scan in the block above, it is known that there
|
||||
** are no levels with a relative level smaller than that of
|
||||
** iAbsLevel with more than nSeg segments, or if nSeg is -1,
|
||||
** no levels with more than nMin segments. Use this to limit the
|
||||
** value of nHintSeg to avoid a large memory allocation in case the
|
||||
** merge-hint is corrupt*/
|
||||
iAbsLevel = iHintAbsLevel;
|
||||
nSeg = nHintSeg;
|
||||
nSeg = MIN(MAX(nMin,nSeg), nHintSeg);
|
||||
bUseHint = 1;
|
||||
bDirtyHint = 1;
|
||||
}else{
|
||||
@ -4920,7 +4951,7 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
/* If nSeg is less that zero, then there is no level with at least
|
||||
** nMin segments and no hint in the %_stat table. No work to do.
|
||||
** Exit early in this case. */
|
||||
if( nSeg<0 ) break;
|
||||
if( nSeg<=0 ) break;
|
||||
|
||||
/* Open a cursor to iterate through the contents of the oldest nSeg
|
||||
** indexes of absolute level iAbsLevel. If this cursor is opened using
|
||||
@ -4948,8 +4979,15 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
}
|
||||
if( SQLITE_OK==rc && pCsr->nSegment==nSeg
|
||||
&& SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
|
||||
&& SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
|
||||
){
|
||||
int bEmpty = 0;
|
||||
rc = sqlite3Fts3SegReaderStep(p, pCsr);
|
||||
if( rc==SQLITE_OK ){
|
||||
bEmpty = 1;
|
||||
}else if( rc!=SQLITE_ROW ){
|
||||
sqlite3Fts3SegReaderFinish(pCsr);
|
||||
break;
|
||||
}
|
||||
if( bUseHint && iIdx>0 ){
|
||||
const char *zKey = pCsr->zTerm;
|
||||
int nKey = pCsr->nTerm;
|
||||
@ -4960,11 +4998,13 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
|
||||
if( rc==SQLITE_OK && pWriter->nLeafEst ){
|
||||
fts3LogMerge(nSeg, iAbsLevel);
|
||||
do {
|
||||
rc = fts3IncrmergeAppend(p, pWriter, pCsr);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
|
||||
if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
|
||||
}while( rc==SQLITE_ROW );
|
||||
if( bEmpty==0 ){
|
||||
do {
|
||||
rc = fts3IncrmergeAppend(p, pWriter, pCsr);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
|
||||
if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
|
||||
}while( rc==SQLITE_ROW );
|
||||
}
|
||||
|
||||
/* Update or delete the input segments */
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -5029,7 +5069,7 @@ static int fts3DoIncrmerge(
|
||||
const char *zParam /* Nul-terminated string containing "A,B" */
|
||||
){
|
||||
int rc;
|
||||
int nMin = (FTS3_MERGE_COUNT / 2);
|
||||
int nMin = (MergeCount(p) / 2);
|
||||
int nMerge = 0;
|
||||
const char *z = zParam;
|
||||
|
||||
@ -5074,7 +5114,7 @@ static int fts3DoAutoincrmerge(
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
p->nAutoincrmerge = fts3Getint(&zParam);
|
||||
if( p->nAutoincrmerge==1 || p->nAutoincrmerge>FTS3_MERGE_COUNT ){
|
||||
if( p->nAutoincrmerge==1 || p->nAutoincrmerge>MergeCount(p) ){
|
||||
p->nAutoincrmerge = 8;
|
||||
}
|
||||
if( !p->bHasStat ){
|
||||
@ -5157,12 +5197,12 @@ static u64 fts3ChecksumIndex(
|
||||
|
||||
i64 iDocid = 0;
|
||||
i64 iCol = 0;
|
||||
i64 iPos = 0;
|
||||
u64 iPos = 0;
|
||||
|
||||
pCsr += sqlite3Fts3GetVarint(pCsr, &iDocid);
|
||||
while( pCsr<pEnd ){
|
||||
i64 iVal = 0;
|
||||
pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
|
||||
u64 iVal = 0;
|
||||
pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal);
|
||||
if( pCsr<pEnd ){
|
||||
if( iVal==0 || iVal==1 ){
|
||||
iCol = 0;
|
||||
@ -5170,8 +5210,12 @@ static u64 fts3ChecksumIndex(
|
||||
if( iVal ){
|
||||
pCsr += sqlite3Fts3GetVarint(pCsr, &iCol);
|
||||
}else{
|
||||
pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
|
||||
iDocid += iVal;
|
||||
pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal);
|
||||
if( p->bDescIdx ){
|
||||
iDocid = (i64)((u64)iDocid - iVal);
|
||||
}else{
|
||||
iDocid = (i64)((u64)iDocid + iVal);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
iPos += (iVal - 2);
|
||||
@ -5244,10 +5288,9 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
|
||||
if( p->abNotindexed[iCol]==0 ){
|
||||
const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
|
||||
int nText = sqlite3_column_bytes(pStmt, iCol+1);
|
||||
sqlite3_tokenizer_cursor *pT = 0;
|
||||
|
||||
rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText,&pT);
|
||||
rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, -1, &pT);
|
||||
while( rc==SQLITE_OK ){
|
||||
char const *zToken; /* Buffer containing token */
|
||||
int nToken = 0; /* Number of bytes in token */
|
||||
@ -5332,7 +5375,7 @@ static int fts3DoIntegrityCheck(
|
||||
** meaningful value to insert is the text 'optimize'.
|
||||
*/
|
||||
static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
int rc; /* Return Code */
|
||||
int rc = SQLITE_ERROR; /* Return Code */
|
||||
const char *zVal = (const char *)sqlite3_value_text(pVal);
|
||||
int nVal = sqlite3_value_bytes(pVal);
|
||||
|
||||
@ -5348,21 +5391,27 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
rc = fts3DoIncrmerge(p, &zVal[6]);
|
||||
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
|
||||
rc = fts3DoAutoincrmerge(p, &zVal[10]);
|
||||
#ifdef SQLITE_TEST
|
||||
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
|
||||
p->nNodeSize = atoi(&zVal[9]);
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
|
||||
p->nMaxPendingData = atoi(&zVal[11]);
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
|
||||
p->bNoIncrDoclist = atoi(&zVal[21]);
|
||||
rc = SQLITE_OK;
|
||||
#endif
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
int v;
|
||||
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
|
||||
v = atoi(&zVal[9]);
|
||||
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
|
||||
v = atoi(&zVal[11]);
|
||||
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal,"test-no-incr-doclist=",21) ){
|
||||
p->bNoIncrDoclist = atoi(&zVal[21]);
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal,"mergecount=",11) ){
|
||||
v = atoi(&zVal[11]);
|
||||
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +159,7 @@ struct Fts5PhraseIter {
|
||||
**
|
||||
** xSetAuxdata(pFts5, pAux, xDelete)
|
||||
**
|
||||
** Save the pointer passed as the second argument as the extension functions
|
||||
** Save the pointer passed as the second argument as the extension function's
|
||||
** "auxiliary data". The pointer may then be retrieved by the current or any
|
||||
** future invocation of the same fts5 extension function made as part of
|
||||
** the same MATCH query using the xGetAuxdata() API.
|
||||
@ -401,8 +401,8 @@ struct Fts5ExtensionApi {
|
||||
**
|
||||
** There are several ways to approach this in FTS5:
|
||||
**
|
||||
** <ol><li> By mapping all synonyms to a single token. In this case, the
|
||||
** In the above example, this means that the tokenizer returns the
|
||||
** <ol><li> By mapping all synonyms to a single token. In this case, using
|
||||
** the above example, this means that the tokenizer returns the
|
||||
** same token for inputs "first" and "1st". Say that token is in
|
||||
** fact "first", so that when the user inserts the document "I won
|
||||
** 1st place" entries are added to the index for tokens "i", "won",
|
||||
|
||||
@ -61,6 +61,11 @@ typedef sqlite3_uint64 u64;
|
||||
*/
|
||||
#define FTS5_MAX_PREFIX_INDEXES 31
|
||||
|
||||
/*
|
||||
** Maximum segments permitted in a single index
|
||||
*/
|
||||
#define FTS5_MAX_SEGMENT 2000
|
||||
|
||||
#define FTS5_DEFAULT_NEARDIST 10
|
||||
#define FTS5_DEFAULT_RANK "bm25"
|
||||
|
||||
@ -417,6 +422,11 @@ int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
|
||||
*/
|
||||
void sqlite3Fts5IterClose(Fts5IndexIter*);
|
||||
|
||||
/*
|
||||
** Close the reader blob handle, if it is open.
|
||||
*/
|
||||
void sqlite3Fts5IndexCloseReader(Fts5Index*);
|
||||
|
||||
/*
|
||||
** This interface is used by the fts5vocab module.
|
||||
*/
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
#define FTS5_DEFAULT_HASHSIZE (1024*1024)
|
||||
|
||||
/* Maximum allowed page size */
|
||||
#define FTS5_MAX_PAGE_SIZE (128*1024)
|
||||
#define FTS5_MAX_PAGE_SIZE (64*1024)
|
||||
|
||||
static int fts5_iswhitespace(char x){
|
||||
return (x==' ');
|
||||
@ -150,7 +150,7 @@ static int fts5Dequote(char *z){
|
||||
assert( q=='[' || q=='\'' || q=='"' || q=='`' );
|
||||
if( q=='[' ) q = ']';
|
||||
|
||||
while( ALWAYS(z[iIn]) ){
|
||||
while( z[iIn] ){
|
||||
if( z[iIn]==q ){
|
||||
if( z[iIn+1]!=q ){
|
||||
/* Character iIn was the close quote. */
|
||||
@ -828,7 +828,7 @@ int sqlite3Fts5ConfigSetValue(
|
||||
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
|
||||
pgsz = sqlite3_value_int(pVal);
|
||||
}
|
||||
if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){
|
||||
if( pgsz<32 || pgsz>FTS5_MAX_PAGE_SIZE ){
|
||||
*pbBadkey = 1;
|
||||
}else{
|
||||
pConfig->pgsz = pgsz;
|
||||
@ -881,6 +881,7 @@ int sqlite3Fts5ConfigSetValue(
|
||||
*pbBadkey = 1;
|
||||
}else{
|
||||
if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
|
||||
if( nCrisisMerge>=FTS5_MAX_SEGMENT ) nCrisisMerge = FTS5_MAX_SEGMENT-1;
|
||||
pConfig->nCrisisMerge = nCrisisMerge;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2516,10 +2516,12 @@ static void fts5ExprFunction(
|
||||
azConfig[1] = "main";
|
||||
azConfig[2] = "tbl";
|
||||
for(i=3; iArg<nArg; iArg++){
|
||||
azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]);
|
||||
const char *z = (const char*)sqlite3_value_text(apVal[iArg]);
|
||||
azConfig[i++] = (z ? z : "");
|
||||
}
|
||||
|
||||
zExpr = (const char*)sqlite3_value_text(apVal[0]);
|
||||
if( zExpr==0 ) zExpr = "";
|
||||
|
||||
rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
@ -239,11 +239,6 @@
|
||||
#define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno)
|
||||
#define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno)
|
||||
|
||||
/*
|
||||
** Maximum segments permitted in a single index
|
||||
*/
|
||||
#define FTS5_MAX_SEGMENT 2000
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; }
|
||||
#endif
|
||||
@ -619,7 +614,7 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
|
||||
/*
|
||||
** Close the read-only blob handle, if it is open.
|
||||
*/
|
||||
static void fts5CloseReader(Fts5Index *p){
|
||||
void sqlite3Fts5IndexCloseReader(Fts5Index *p){
|
||||
if( p->pReader ){
|
||||
sqlite3_blob *pReader = p->pReader;
|
||||
p->pReader = 0;
|
||||
@ -648,7 +643,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
assert( p->pReader==0 );
|
||||
p->pReader = pBlob;
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5CloseReader(p);
|
||||
sqlite3Fts5IndexCloseReader(p);
|
||||
}
|
||||
if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
|
||||
}
|
||||
@ -5209,7 +5204,7 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
|
||||
int sqlite3Fts5IndexSync(Fts5Index *p){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
fts5IndexFlush(p);
|
||||
fts5CloseReader(p);
|
||||
sqlite3Fts5IndexCloseReader(p);
|
||||
return fts5IndexReturn(p);
|
||||
}
|
||||
|
||||
@ -5220,7 +5215,7 @@ int sqlite3Fts5IndexSync(Fts5Index *p){
|
||||
** records must be invalidated.
|
||||
*/
|
||||
int sqlite3Fts5IndexRollback(Fts5Index *p){
|
||||
fts5CloseReader(p);
|
||||
sqlite3Fts5IndexCloseReader(p);
|
||||
fts5IndexDiscardData(p);
|
||||
fts5StructureInvalidate(p);
|
||||
/* assert( p->rc==SQLITE_OK ); */
|
||||
@ -5235,6 +5230,7 @@ int sqlite3Fts5IndexRollback(Fts5Index *p){
|
||||
int sqlite3Fts5IndexReinit(Fts5Index *p){
|
||||
Fts5Structure s;
|
||||
fts5StructureInvalidate(p);
|
||||
fts5IndexDiscardData(p);
|
||||
memset(&s, 0, sizeof(Fts5Structure));
|
||||
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
|
||||
fts5StructureWrite(p, &s);
|
||||
@ -5322,9 +5318,13 @@ int sqlite3Fts5IndexCharlenToBytelen(
|
||||
for(i=0; i<nChar; i++){
|
||||
if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */
|
||||
if( (unsigned char)p[n++]>=0xc0 ){
|
||||
if( n>=nByte ) return 0;
|
||||
while( (p[n] & 0xc0)==0x80 ){
|
||||
n++;
|
||||
if( n>=nByte ) break;
|
||||
if( n>=nByte ){
|
||||
if( i+1==nChar ) break;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5460,7 +5460,7 @@ int sqlite3Fts5IndexQuery(
|
||||
if( p->rc ){
|
||||
sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
|
||||
pRet = 0;
|
||||
fts5CloseReader(p);
|
||||
sqlite3Fts5IndexCloseReader(p);
|
||||
}
|
||||
|
||||
*ppIter = (Fts5IndexIter*)pRet;
|
||||
@ -5533,7 +5533,7 @@ void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
|
||||
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
|
||||
Fts5Index *pIndex = pIter->pIndex;
|
||||
fts5MultiIterFree(pIter);
|
||||
fts5CloseReader(pIndex);
|
||||
sqlite3Fts5IndexCloseReader(pIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5726,6 +5726,37 @@ static int fts5QueryCksum(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if buffer z[], size n bytes, contains as series of valid utf-8
|
||||
** encoded codepoints. If so, return 0. Otherwise, if the buffer does not
|
||||
** contain valid utf-8, return non-zero.
|
||||
*/
|
||||
static int fts5TestUtf8(const char *z, int n){
|
||||
assert_nc( n>0 );
|
||||
int i = 0;
|
||||
while( i<n ){
|
||||
if( (z[i] & 0x80)==0x00 ){
|
||||
i++;
|
||||
}else
|
||||
if( (z[i] & 0xE0)==0xC0 ){
|
||||
if( i+1>=n || (z[i+1] & 0xC0)!=0x80 ) return 1;
|
||||
i += 2;
|
||||
}else
|
||||
if( (z[i] & 0xF0)==0xE0 ){
|
||||
if( i+2>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1;
|
||||
i += 3;
|
||||
}else
|
||||
if( (z[i] & 0xF8)==0xF0 ){
|
||||
if( i+3>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1;
|
||||
if( (z[i+2] & 0xC0)!=0x80 ) return 1;
|
||||
i += 3;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is also purely an internal test. It does not contribute to
|
||||
@ -5766,8 +5797,14 @@ static void fts5TestTerm(
|
||||
** This check may only be performed if the hash table is empty. This
|
||||
** is because the hash table only supports a single scan query at
|
||||
** a time, and the multi-iter loop from which this function is called
|
||||
** is already performing such a scan. */
|
||||
if( p->nPendingData==0 ){
|
||||
** is already performing such a scan.
|
||||
**
|
||||
** Also only do this if buffer zTerm contains nTerm bytes of valid
|
||||
** utf-8. Otherwise, the last part of the buffer contents might contain
|
||||
** a non-utf-8 sequence that happens to be a prefix of a valid utf-8
|
||||
** character stored in the main fts index, which will cause the
|
||||
** test to fail. */
|
||||
if( p->nPendingData==0 && 0==fts5TestUtf8(zTerm, nTerm) ){
|
||||
if( iIdx>0 && rc==SQLITE_OK ){
|
||||
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
|
||||
ck2 = 0;
|
||||
@ -5890,7 +5927,8 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
if( pSeg->pgnoFirst==0 ) return;
|
||||
|
||||
fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
|
||||
"SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d",
|
||||
"SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d "
|
||||
"ORDER BY 1, 2",
|
||||
pConfig->zDb, pConfig->zName, pSeg->iSegid
|
||||
));
|
||||
|
||||
@ -5899,8 +5937,8 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
i64 iRow; /* Rowid for this leaf */
|
||||
Fts5Data *pLeaf; /* Data for this leaf */
|
||||
|
||||
const char *zIdxTerm = (const char*)sqlite3_column_blob(pStmt, 1);
|
||||
int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
|
||||
const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
|
||||
int iIdxLeaf = sqlite3_column_int(pStmt, 2);
|
||||
int bIdxDlidx = sqlite3_column_int(pStmt, 3);
|
||||
|
||||
|
||||
@ -289,7 +289,10 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
|
||||
case FTS5_ROLLBACKTO:
|
||||
assert( p->ts.eState==1 );
|
||||
assert( iSavepoint>=-1 );
|
||||
assert( iSavepoint<=p->ts.iSavepoint );
|
||||
/* The following assert() can fail if another vtab strikes an error
|
||||
** within an xSavepoint() call then SQLite calls xRollbackTo() - without
|
||||
** having called xSavepoint() on this vtab. */
|
||||
/* assert( iSavepoint<=p->ts.iSavepoint ); */
|
||||
p->ts.iSavepoint = iSavepoint;
|
||||
break;
|
||||
}
|
||||
@ -744,6 +747,7 @@ static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
|
||||
sqlite3_free(pCsr->zRankArgs);
|
||||
}
|
||||
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr));
|
||||
}
|
||||
|
||||
@ -894,15 +898,24 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
default: {
|
||||
Fts5Config *pConfig = ((Fts5Table*)pCursor->pVtab)->pConfig;
|
||||
pConfig->bLock++;
|
||||
rc = sqlite3_step(pCsr->pStmt);
|
||||
pConfig->bLock--;
|
||||
if( rc!=SQLITE_ROW ){
|
||||
CsrFlagSet(pCsr, FTS5CSR_EOF);
|
||||
rc = sqlite3_reset(pCsr->pStmt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pCursor->pVtab->zErrMsg = sqlite3_mprintf(
|
||||
"%s", sqlite3_errmsg(pConfig->db)
|
||||
);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1187,6 +1200,13 @@ static int fts5FilterMethod(
|
||||
int iIdxStr = 0;
|
||||
Fts5Expr *pExpr = 0;
|
||||
|
||||
if( pConfig->bLock ){
|
||||
pTab->p.base.zErrMsg = sqlite3_mprintf(
|
||||
"recursively defined fts5 content table"
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( pCsr->ePlan ){
|
||||
fts5FreeCursorComponents(pCsr);
|
||||
memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr));
|
||||
@ -1407,10 +1427,13 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
|
||||
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
|
||||
assert( pCsr->pExpr );
|
||||
sqlite3_reset(pCsr->pStmt);
|
||||
sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
|
||||
pTab->pConfig->bLock++;
|
||||
rc = sqlite3_step(pCsr->pStmt);
|
||||
pTab->pConfig->bLock--;
|
||||
if( rc==SQLITE_ROW ){
|
||||
rc = SQLITE_OK;
|
||||
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
|
||||
@ -1418,6 +1441,10 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
|
||||
rc = sqlite3_reset(pCsr->pStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}else if( pTab->pConfig->pzErrmsg ){
|
||||
*pTab->pConfig->pzErrmsg = sqlite3_mprintf(
|
||||
"%s", sqlite3_errmsg(pTab->pConfig->db)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2433,10 +2460,12 @@ static int fts5ColumnMethod(
|
||||
}
|
||||
}
|
||||
}else if( !fts5IsContentless(pTab) ){
|
||||
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
|
||||
rc = fts5SeekCursor(pCsr, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
|
||||
}
|
||||
pConfig->pzErrmsg = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -560,6 +560,8 @@ int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc;
|
||||
|
||||
p->bTotalsValid = 0;
|
||||
|
||||
/* Delete the contents of the %_data and %_docsize tables. */
|
||||
rc = fts5ExecPrintf(pConfig->db, 0,
|
||||
"DELETE FROM %Q.'%q_data';"
|
||||
@ -611,10 +613,11 @@ int sqlite3Fts5StorageRebuild(Fts5Storage *p){
|
||||
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->abUnindexed[ctx.iCol]==0 ){
|
||||
const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1);
|
||||
int nText = sqlite3_column_bytes(pScan, ctx.iCol+1);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_column_text(pScan, ctx.iCol+1),
|
||||
sqlite3_column_bytes(pScan, ctx.iCol+1),
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
@ -736,10 +739,11 @@ int sqlite3Fts5StorageIndexInsert(
|
||||
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->abUnindexed[ctx.iCol]==0 ){
|
||||
const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]);
|
||||
int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
|
||||
sqlite3_value_bytes(apVal[ctx.iCol+2]),
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
@ -908,10 +912,11 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zText = (const char*)sqlite3_column_text(pScan, i+1);
|
||||
int nText = sqlite3_column_bytes(pScan, i+1);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_column_text(pScan, i+1),
|
||||
sqlite3_column_bytes(pScan, i+1),
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
|
||||
@ -257,16 +257,41 @@ do_execsql_test 6.2 {
|
||||
# Check that an fts5 table cannot be its own content table.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.1 {
|
||||
do_execsql_test 7.1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, c=t1 );
|
||||
INSERT INTO t1( a ) VALUES('abc');
|
||||
}
|
||||
do_catchsql_test 7.2 {
|
||||
do_catchsql_test 7.1.2 {
|
||||
SELECT * FROM t1;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.3 {
|
||||
do_catchsql_test 7.1.3 {
|
||||
SELECT * FROM t1('abc');
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.1.4 {
|
||||
SELECT count(*) FROM t1;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.1.5 {
|
||||
SELECT * FROM t1('abc') ORDER BY rank;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 7.2.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, c=t2 );
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, c=t1 );
|
||||
INSERT INTO t1( a ) VALUES('abc');
|
||||
}
|
||||
do_catchsql_test 7.2.2 {
|
||||
SELECT * FROM t1;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.2.3 {
|
||||
SELECT * FROM t1('abc');
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.2.4 {
|
||||
SELECT count(*) FROM t1;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.2.5 {
|
||||
SELECT * FROM t1('abc') ORDER BY rank;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@ -4484,7 +4484,7 @@ do_test 36.0 {
|
||||
do_catchsql_test 36.1 {
|
||||
INSERT INTO t1(b) VALUES(
|
||||
x'78de3fa24af3733ca8769291a0fee3669f9fddefc5cba913e4225d4b6ce2b04f26b87fad3ee6f9b7d90a1ea62a169bf41e5d32707a6ca5c3d05e4bde05c9d89eaaa8c50e74333d2e9fcd7dfe95528a3a016aac1102d825c5cd70cf99d8a88e0ea7f798d4334386518b7ad359beb168b93aba059a2a3bd93112d65b44c12b9904ea786b204d80531cdf0504bf9b203dbe927061974caf7b9f30cbc3397b61f802e732012a6663d41c3607d6f1c0dbcfd489adac05ca500c0b04439d894cd93a840159225ef73b627e178b9f84b3ffe66cf22a963a8368813ff7961fc47f573211ccec95e0220dcbb3bf429f4a50ba54d7a53784ac51bfef346e6a');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@ -9774,6 +9774,339 @@ do_catchsql_test 66.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_test 67.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename crash-43ed0ad79c0194.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 00 .....@ ........
|
||||
| 96: 00 00 00 00 0d 00 00 00 06 0d e2 00 0f c4 0f 6a ...............j
|
||||
| 112: 0e fc 0e 9d 0e 3d 0d e2 01 00 00 00 00 00 00 00 .....=..........
|
||||
| 3552: 00 00 59 06 06 17 21 21 01 7f 74 61 62 6c 65 74 ..Y...!!..tablet
|
||||
| 3568: 74 74 5f 63 6f 6e 66 69 67 74 74 74 5f 63 6f 6e tt_configttt_con
|
||||
| 3584: 66 69 67 06 43 52 45 41 54 45 20 54 41 42 4c 45 fig.CREATE TABLE
|
||||
| 3600: 20 27 74 74 74 5f 63 6f 6e 66 69 67 27 28 6b 20 'ttt_config'(k
|
||||
| 3616: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 PRIMARY KEY, v)
|
||||
| 3632: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5e 05 07 WITHOUT ROWID^..
|
||||
| 3648: 17 23 23 01 81 03 74 61 62 6c 65 74 74 74 5f 64 .##...tablettt_d
|
||||
| 3664: 6f 63 73 69 7a 65 74 74 74 5f 64 6f 63 73 69 7a ocsizettt_docsiz
|
||||
| 3680: 65 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 e.CREATE TABLE '
|
||||
| 3696: 74 74 74 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 ttt_docsize'(id
|
||||
| 3712: 49 4e 54 45 47 45 52 20 51 52 49 4d 41 52 59 20 INTEGER QRIMARY
|
||||
| 3728: 4b 45 59 2c 20 73 7a 20 42 4c 4f 42 29 5d 04 07 KEY, sz BLOB)]..
|
||||
| 3744: 17 23 23 01 81 01 74 61 62 6c 65 74 74 74 5f 63 .##...tablettt_c
|
||||
| 3760: 6f 6e 74 65 6e 74 74 74 74 5f 63 6f 6e 74 65 6e ontentttt_conten
|
||||
| 3776: 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 t.CREATE TABLE '
|
||||
| 3792: 74 74 74 5f 63 6f 6e 74 65 6e 74 27 28 69 64 20 ttt_content'(id
|
||||
| 3808: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 f1 59 20 INTEGER PRIMA.Y
|
||||
| 3824: 4b 45 59 2c 20 63 30 2c 20 63 31 29 6c 03 07 17 KEY, c0, c1)l...
|
||||
| 3840: 1b 1b 01 81 2f 74 61 62 6c 65 74 74 74 5f 69 64 ..../tablettt_id
|
||||
| 3856: 78 74 74 74 5f 69 64 78 03 43 52 45 41 54 45 20 xttt_idx.CREATE
|
||||
| 3872: 54 41 42 4c 45 20 27 74 74 74 5f 69 64 78 27 28 TABLE 'ttt_idx'(
|
||||
| 3888: 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e segid, term, pgn
|
||||
| 3904: 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 28 73 o, PRIMARY KEY(s
|
||||
| 3920: 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 49 54 egid, term)) WIT
|
||||
| 3936: 48 4f 55 54 20 52 4f 57 49 44 58 02 07 17 1d 1d HOUT ROWIDX.....
|
||||
| 3952: 01 81 03 74 61 62 6c 65 74 74 74 5f 64 61 74 61 ...tablettt_data
|
||||
| 3968: 74 74 74 5f 64 61 74 61 02 43 52 45 41 54 45 20 ttt_data.CREATE
|
||||
| 3984: 54 41 42 4c 45 20 27 74 74 74 5f 64 61 74 61 27 TABLE 'ttt_data'
|
||||
| 4000: 28 69 64 20 49 4e 54 45 47 55 52 20 50 52 49 4d (id INTEGUR PRIM
|
||||
| 4016: 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARY KEY, block B
|
||||
| 4032: 4c 50 42 29 3a 02 06 17 13 13 08 5f 74 61 62 6c LPB):......_tabl
|
||||
| 4048: 65 74 74 74 74 74 74 43 52 45 41 54 45 20 56 49 ettttttCREATE VI
|
||||
| 4064: 52 54 55 41 4c 20 54 41 42 4c 45 20 74 74 74 20 RTUAL TABLE ttt
|
||||
| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 0f 44 00 05 0e 71 00 0f e7 0e 81 0f af 0f 58 ..D...q........X
|
||||
| 16: 0e 98 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3712: 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 03 01 .....0..........
|
||||
| 3728: 01 01 02 01 01 03 01 01 81 24 8c 80 80 80 80 01 .........$......
|
||||
| 3744: 04 00 82 4c 00 00 00 9b 02 30 65 03 1a 12 05 05 ...L.....0e.....
|
||||
| 3760: 07 05 01 01 04 03 03 08 04 03 01 2e 02 05 f7 07 ................
|
||||
| 3776: 01 e6 f5 07 05 01 01 04 03 03 01 22 03 18 03 03 ................
|
||||
| 3792: 08 03 03 02 01 65 03 1e 03 05 05 04 05 05 01 01 .....e..........
|
||||
| 3808: 03 06 03 f4 06 04 03 00 36 03 ff 05 04 05 05 04 ........6.......
|
||||
| 3824: 05 05 04 05 04 f1 01 03 06 04 04 06 04 04 06 04 ................
|
||||
| 3840: 04 07 04 03 03 01 65 03 14 04 05 07 05 05 01 01 ......e.........
|
||||
| 3856: 02 08 a5 01 20 04 05 01 94 f7 05 07 05 05 01 01 .... ...........
|
||||
| 3872: 02 08 0a 0a 0a 04 01 65 03 02 0a 00 06 0a 0a 0a .......e........
|
||||
| 3888: 05 01 65 03 06 a7 01 0a 01 0a 01 01 0a 0a 0a 04 ..e.............
|
||||
| 3904: 2b 31 21 0b 0f ef 00 14 2a 00 00 00 00 01 02 02 +1!.....*.......
|
||||
| 3920: 00 02 01 01 01 02 11 01 50 88 80 80 80 80 01 04 ........P.......
|
||||
| 3936: 00 81 24 00 00 00 47 02 30 65 02 1a 02 05 05 07 ..$...G.0e......
|
||||
| 3952: 05 e6 01 07 aa e3 08 03 03 02 01 65 02 1e 03 05 ...........e....
|
||||
| 3968: 05 05 04 f5 01 01 03 06 04 04 06 04 13 03 01 65 ...............e
|
||||
| 3984: 02 14 04 05 07 05 05 01 f7 f2 08 0a 04 01 65 02 ..............e.
|
||||
| 4000: 02 0a 05 01 65 02 06 00 f1 0a 04 12 14 0f 06 31 ....e..........1
|
||||
| 4016: 84 80 80 80 80 01 03 00 68 00 00 00 2b 02 30 65 ........h...+.0e
|
||||
| 4032: 01 10 02 05 05 00 01 04 03 03 02 01 65 01 12 03 ............e...
|
||||
| 4048: 05 05 01 01 03 06 04 03 03 01 65 01 0e 04 05 04 ..........e.....
|
||||
| 4064: 01 01 02 08 04 0d 0e 06 01 03 00 12 04 4c 4c 00 .............LL.
|
||||
| 4080: 00 00 11 24 00 00 00 00 01 01 01 00 01 01 01 02 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| page 4 offset 12288
|
||||
| 3600: 00 00 00 00 00 00 00 00 00 00 81 52 04 06 00 81 ...........R....
|
||||
| 3616: 5d 81 55 65 20 65 65 20 65 65 65 20 65 20 65 65 ].Ue ee eee e ee
|
||||
| 3632: 20 65 65 65 28 15 20 65 65 20 65 65 65 65 20 65 eee(. ee eeee e
|
||||
| 3648: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
|
||||
| 3664: 20 65 65 20 65 65 65 65 20 65 66 20 65 65 55 20 ee eeee ef eeU
|
||||
| 3680: 65 20 65 55 20 65 65 65 20 65 20 65 65 20 65 65 e eU eee e ee ee
|
||||
| 3696: 65 64 20 65 61 c0 65 65 65 20 65 20 65 65 20 65 ed ea.eee e ee e
|
||||
| 3712: 65 65 20 79 20 65 65 20 65 65 65 65 65 65 20 65 ee y ee eeeeee e
|
||||
| 3728: 65 1f 65 20 65 20 65 20 65 65 20 65 65 65 20 65 e.e e e ee eee e
|
||||
| 3744: 65 20 65 65 65 65 65 20 65 65 20 65 20 65 20 65 e eeeee ee e e e
|
||||
| 3760: 20 65 65 20 65 65 65 20 6b 85 20 65 65 65 66 65 ee eee k. eeefe
|
||||
| 3776: 20 65 65 10 65 20 65 20 65 20 65 65 20 65 65 65 ee.e e e ee eee
|
||||
| 3792: 20 65 65 20 65 65 65 65 65 20 65 65 20 65 20 65 ee eeeee ee e e
|
||||
| 3808: 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 6a e ee eee ee eej
|
||||
| 3824: 03 04 00 75 71 65 20 65 65 20 65 65 65 20 65 30 ...uqe ee eee e0
|
||||
| 3840: 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 65 ee eee e ee eeee
|
||||
| 3856: 20 65 65 20 65 65 65 20 65 1f 65 65 20 65 65 65 ee eee e.ee eee
|
||||
| 3872: 20 65 20 65 65 20 65 65 65 65 65 66 20 65 65 20 e ee eeeeef ee
|
||||
| 3888: 65 21 27 20 65 20 55 65 20 66 65 64 20 65 65 00 e!' e Ue fed ee.
|
||||
| page 5 offset 16384
|
||||
| 4064: 00 00 00 00 05 04 03 00 10 11 20 05 03 03 00 10 .......... .....
|
||||
| 4080: 11 11 05 02 03 00 00 11 11 05 01 03 00 10 09 09 ................
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 01 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| end crash-43ed0ad79c0194.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 67.1 {
|
||||
SELECT snippet(ttt, null,null,
|
||||
EXISTS(SELECT 1 FROM ttt('e NuOT ee*e*ÏNuOY ee*') ) , '',
|
||||
(SELECT 1 FROM ttt('eu NuOT ee*e* NuOY ee*'))
|
||||
), * FROM ttt('e')
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_test 68.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 32768 pagesize 4096 filename crash-41234e232809e7.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 08 .....@ ........
|
||||
| 32: 00 00 00 02 00 00 00 01 00 00 00 09 00 00 00 04 ................
|
||||
| 96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6
|
||||
| 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 0d 92 00 00 00 00 ...k............
|
||||
| 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
|
||||
| 3488: 32 74 32 08 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
|
||||
| 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
|
||||
| 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
|
||||
| 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
|
||||
| 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
|
||||
| 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[.
|
||||
| 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
|
||||
| 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
|
||||
| 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
|
||||
| 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
|
||||
| 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...!
|
||||
| 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont
|
||||
| 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR
|
||||
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c
|
||||
| 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG
|
||||
| 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
|
||||
| 3776: 63 30 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c0, c1, c2)i....
|
||||
| 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt
|
||||
| 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB
|
||||
| 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi
|
||||
| 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P
|
||||
| 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid
|
||||
| 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT
|
||||
| 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t
|
||||
| 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da
|
||||
| 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE
|
||||
| 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT
|
||||
| 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
|
||||
| 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8..
|
||||
| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR
|
||||
| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB
|
||||
| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5
|
||||
| 4032: 28 61 2c 62 2c 63 29 00 00 00 00 00 00 00 00 00 (a,b,c).........
|
||||
| page 3 offset 8192
|
||||
| 0: 0d 00 00 00 03 0c 94 00 0f e6 0f ef 0c 94 00 00 ................
|
||||
| 16: 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3216: 00 00 00 00 86 4a 84 80 80 80 80 01 04 00 8d 18 .....J..........
|
||||
| 3232: 00 00 03 2b 02 30 30 01 02 06 01 02 06 01 02 06 ...+.00.........
|
||||
| 3248: 1f 02 03 01 02 03 01 02 03 01 08 32 30 31 36 30 ...........20160
|
||||
| 3264: 36 30 39 01 02 07 01 02 07 01 02 07 00 01 34 01 609...........4.
|
||||
| 3280: 02 05 01 02 05 01 02 05 01 01 35 01 02 04 01 02 ..........5.....
|
||||
| 3296: 04 01 02 04 02 07 30 30 30 30 30 30 30 1c 02 04 ......0000000...
|
||||
| 3312: 01 02 04 01 02 03 f1 06 62 69 6e 62 72 79 03 06 ........binbry..
|
||||
| 3328: 01 02 02 03 06 01 02 02 03 06 01 02 01 03 16 01 ................
|
||||
| 3344: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 3360: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 3376: 03 04 71 02 02 03 06 11 02 02 01 08 63 6f 6d 70 ..q.........comp
|
||||
| 3392: 69 6c 65 72 01 02 02 01 02 02 01 02 02 01 06 64 iler...........d
|
||||
| 3408: 62 73 74 61 74 07 02 03 01 02 03 01 02 03 02 04 bstat...........
|
||||
| 3424: 65 62 75 67 04 02 02 01 02 02 01 02 02 01 06 65 ebug...........e
|
||||
| 3440: 6e 61 62 6c 65 07 02 02 01 02 02 01 02 02 01 02 nable...........
|
||||
| 3456: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 03 02 ................
|
||||
| 3472: 00 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 3488: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 3504: 02 01 02 02 02 08 78 74 65 6e 73 69 6f 6e 1f 02 ......xtension..
|
||||
| 3520: 04 01 02 04 01 02 04 01 04 66 74 73 34 0a 02 03 .........fts4...
|
||||
| 3536: 01 02 03 01 02 03 04 01 35 0d 02 03 01 02 03 01 ........5.......
|
||||
| 3552: 02 03 01 03 66 63 63 01 02 03 01 02 03 01 02 03 ....fcc.........
|
||||
| 3568: 02 06 65 6f 70 6f 6c 79 10 02 03 01 02 03 01 02 ..eopoly........
|
||||
| 3584: 03 01 05 6a 73 6f 5e 31 13 02 03 01 02 03 01 02 ...jso^1........
|
||||
| 3600: 03 01 04 6c 6f 61 64 1f 02 03 01 02 03 01 02 03 ...load.........
|
||||
| 3616: 01 03 6d 61 78 1c 02 02 01 02 02 01 02 02 02 05 ..max...........
|
||||
| 3632: 65 6d 6f 72 79 1c 02 03 01 02 03 01 02 03 04 04 emory...........
|
||||
| 3648: 73 79 73 35 16 02 03 01 02 03 01 02 03 01 06 6e sys5...........n
|
||||
| 3664: 6f 63 61 73 65 02 06 01 02 02 03 06 01 02 02 03 ocase...........
|
||||
| 3680: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 13 06 ................
|
||||
| 3696: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 3712: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 3728: 02 01 04 6f 6d 69 74 1f 02 02 01 02 02 01 02 02 ...omit.........
|
||||
| 3744: 01 05 72 74 72 65 65 19 02 03 01 02 03 01 02 03 ..rtree.........
|
||||
| 3760: 04 02 69 6d 01 06 01 02 02 03 06 01 12 02 03 06 ..im............
|
||||
| 3776: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 3792: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 3808: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 3824: 01 0a 74 68 72 65 61 64 73 61 66 65 22 02 02 01 ..threadsafe....
|
||||
| 3840: 02 02 01 02 02 01 04 76 74 61 62 07 02 04 01 02 .......vtab.....
|
||||
| 3856: 04 01 02 04 01 01 78 01 06 01 01 02 01 06 01 01 ......x.........
|
||||
| 3872: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3888: 01 06 01 01 02 01 06 01 01 02 01 05 f1 01 02 01 ................
|
||||
| 3904: 06 01 01 02 01 06 01 5b 02 01 06 01 01 02 01 06 .......[........
|
||||
| 3920: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3936: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3952: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3968: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3984: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 4000: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 4016: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 4032: 02 01 06 01 01 02 01 06 01 01 02 04 15 13 0c 0c ................
|
||||
| 4048: 12 44 13 11 0f 47 13 0f 0c 0e 11 10 0f 0e 10 0f .D...G..........
|
||||
| 4064: 44 0f 10 40 15 0f 07 01 03 00 14 24 5a 24 24 0f D..@.......$Z$$.
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0b 01 02 ................
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 24 0c 0a 00 0f d8 0f af 0f 86 0f 74 ....$..........t
|
||||
| 16: 0f 61 0f 4e 0f 2f 0f 0f 0e ef 0e d7 0e be 0e a5 .a.N./..........
|
||||
| 32: 0e 8d 0e 74 0e 5b 0e 40 0e 24 0e 08 0d ef 0d d5 ...t.[.@.$......
|
||||
| 48: 0d bb 0d a0 0d 84 0d 68 0d 4f 0d 35 0d 1b 0c fb .......h.O.5....
|
||||
| 64: 0c da 0c b9 0c 99 0c 78 0c 57 0c 3e 0c 24 0c 0a .......x.W.>.$..
|
||||
| 3072: 00 00 00 00 00 00 00 00 00 00 18 24 05 00 25 0f ...........$..%.
|
||||
| 3088: 19 54 48 52 45 41 44 53 41 46 45 3d 30 58 42 49 .THREADSAFE=0XBI
|
||||
| 3104: 4f 41 52 59 18 23 05 00 25 0f 19 54 48 52 45 41 OARY.#..%..THREA
|
||||
| 3120: 44 53 41 46 45 3d 30 58 4e 4f 43 41 53 45 17 22 DSAFE=0XNOCASE..
|
||||
| 3136: 05 00 25 0f 17 54 48 52 45 41 44 53 41 46 45 3d ..%..THREADSAFE=
|
||||
| 3152: 30 58 52 54 52 49 4d 1f 21 05 00 33 0f 19 4f 4d 0XRTRIM.!..3..OM
|
||||
| 3168: 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 4f IT LOAD EXTENSIO
|
||||
| 3184: 4e 58 42 49 4e 41 52 59 1f 20 05 00 33 0f 19 4f NXBINARY. ..3..O
|
||||
| 3200: 4d 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 MIT LOAD EXTENSI
|
||||
| 3216: 4f 4e 58 4e 4f 43 41 53 45 1e 1f 05 00 33 0f 17 ONXNOCASE....3..
|
||||
| 3232: 4f 4d 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 OMIT LOAD EXTENS
|
||||
| 3248: 49 4f 4e 58 52 54 52 49 4d 1f 1e 05 00 33 0f 19 IONXRTRIM....3..
|
||||
| 3264: 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 30 30 MAX MEMORY=50000
|
||||
| 3280: 30 30 30 58 42 49 4e 41 52 59 1f 1d 05 00 33 0f 000XBINARY....3.
|
||||
| 3296: 19 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 30 .MAX MEMORY=5000
|
||||
| 3312: 30 30 30 30 58 4e 4f 43 41 53 45 1e 1c 05 00 33 0000XNOCASE....3
|
||||
| 3328: 0f 17 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 ..MAX MEMORY=500
|
||||
| 3344: 30 30 30 30 30 58 52 54 52 49 4d 18 1b 05 00 25 00000XRTRIM....%
|
||||
| 3360: 0f 19 45 4e 41 42 4c 45 20 52 54 52 45 45 58 42 ..ENABLE RTREEXB
|
||||
| 3376: 49 4e 41 52 59 18 1a 05 00 25 0f 19 45 4e 41 42 INARY....%..ENAB
|
||||
| 3392: 4c 45 20 52 54 52 46 45 58 4e 4f 43 41 53 45 17 LE RTRFEXNOCASE.
|
||||
| 3408: 19 05 00 25 0f 17 45 4e 41 42 4c 45 20 52 54 52 ...%..ENABLE RTR
|
||||
| 3424: 45 45 58 52 54 52 49 4d 1a 18 05 00 29 0f 19 45 EEXRTRIM....)..E
|
||||
| 3440: 49 41 42 4c 45 20 4d 45 4d 53 59 53 35 58 42 49 IABLE MEMSYS5XBI
|
||||
| 3456: 4e 41 52 59 1a 17 05 00 29 0f 19 45 4e 41 42 4c NARY....)..ENABL
|
||||
| 3472: 45 20 4d 45 4d 53 59 53 35 58 4e 4f 43 41 53 45 E MEMSYS5XNOCASE
|
||||
| 3488: 19 16 05 00 29 0f 17 45 4e 41 42 4c 45 20 4d 45 ....)..ENABLE ME
|
||||
| 3504: 4d 53 59 53 35 58 52 54 52 49 4d 18 15 05 00 25 MSYS5XRTRIM....%
|
||||
| 3520: 0f 19 45 4e 41 42 4c 45 20 4a 53 4f 4e 31 58 42 ..ENABLE JSON1XB
|
||||
| 3536: 49 4e 41 52 59 18 14 05 00 25 0f 19 45 4e 41 42 INARY....%..ENAB
|
||||
| 3552: 4c 45 20 4a 53 4f 4e 31 58 4e 4f 43 41 53 45 17 LE JSON1XNOCASE.
|
||||
| 3568: 13 05 00 25 0f 17 45 4e 41 42 4c 45 20 4a 53 4f ...%..ENABLE JSO
|
||||
| 3584: 4e 31 58 52 54 52 49 4d 1a 12 05 00 29 0f 19 45 N1XRTRIM....)..E
|
||||
| 3600: 4e 41 42 4c 45 20 47 45 4f 50 4f 4c 59 57 42 49 NABLE GEOPOLYWBI
|
||||
| 3616: 4e 41 52 59 1a 11 05 00 29 0f 19 45 4e 41 42 4c NARY....)..ENABL
|
||||
| 3632: 45 20 47 45 4f 50 4f 4c 59 58 4e 4f 42 41 53 45 E GEOPOLYXNOBASE
|
||||
| 3648: 19 10 05 00 29 0f 17 45 4e 41 42 4c 45 20 47 45 ....)..ENABLE GE
|
||||
| 3664: 4f 50 4f 4c 59 58 52 54 52 49 4d 17 0f 05 00 23 OPOLYXRTRIM....#
|
||||
| 3680: 0f 19 45 4e 41 42 4c 45 20 46 54 53 35 58 42 49 ..ENABLE FTS5XBI
|
||||
| 3696: 4e 41 52 59 17 0e 05 00 23 0f 19 45 4e 41 42 4c NARY....#..ENABL
|
||||
| 3712: 45 20 46 54 53 35 58 4e 4f 43 41 53 45 16 0d 05 E FTS5XNOCASE...
|
||||
| 3728: 00 23 0f 17 45 4e 41 42 4c 45 20 46 54 53 35 58 .#..ENABLE FTS5X
|
||||
| 3744: 52 54 52 49 4d 17 0c 05 00 23 0f 19 45 4e 41 42 RTRIM....#..ENAB
|
||||
| 3760: 4c 45 20 46 54 53 34 58 42 49 4e 41 52 59 17 0b LE FTS4XBINARY..
|
||||
| 3776: 05 00 23 0f 19 45 4e 41 42 4c 45 20 46 54 53 34 ..#..ENABLE FTS4
|
||||
| 3792: 58 4e 4f 43 41 53 45 16 0a 05 00 23 0f 17 45 4e XNOCASE....#..EN
|
||||
| 3808: 41 42 4c 45 20 46 54 53 34 58 52 54 52 49 4d 1e ABLE FTS4XRTRIM.
|
||||
| 3824: 09 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS
|
||||
| 3840: 54 41 54 20 56 54 41 42 58 42 49 4e 41 52 59 1e TAT VTABXBINARY.
|
||||
| 3856: 08 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS
|
||||
| 3872: 54 41 54 20 56 54 41 42 58 4e 4f 43 41 53 45 1d TAT VTABXNOCASE.
|
||||
| 3888: 07 05 00 31 0f 17 b7 4e 41 42 4c 45 20 44 42 53 ...1...NABLE DBS
|
||||
| 3904: 54 41 54 20 66 54 41 42 58 52 54 52 49 4d 11 06 TAT fTABXRTRIM..
|
||||
| 3920: 05 00 17 0f 19 44 45 42 55 47 58 42 49 4e 41 52 .....DEBUGXBINAR
|
||||
| 3936: 59 11 05 05 00 17 0f 19 44 45 42 55 47 58 4e 4f Y.......DEBUGXNO
|
||||
| 3952: 43 41 53 45 10 04 05 00 17 0f 17 44 45 42 55 47 CASE.......DEBUG
|
||||
| 3968: 58 62 54 52 49 4d 27 03 05 00 43 0f 19 43 4f 4d XbTRIM'...C..COM
|
||||
| 3984: 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e 30 20 PILER=gcc-5.4.0
|
||||
| 4000: 32 30 31 36 30 36 30 39 52 02 4a 4e 41 52 59 27 20160609R.JNARY'
|
||||
| 4016: 02 05 00 43 0f 19 43 4f 4d 50 49 4c 45 52 3d 67 ...C..COMPILER=g
|
||||
| 4032: 63 63 2d 35 2e 34 2e 30 20 32 30 31 36 30 36 30 cc-5.4.0 2016060
|
||||
| 4048: 39 58 4e 4f 43 41 53 45 26 01 05 00 43 0f 17 43 9XNOCASE&...C..C
|
||||
| 4064: 4f 4d 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e OMPILER=gcc-5.4.
|
||||
| 4080: 30 20 32 30 31 36 30 36 30 39 58 52 54 52 49 4d 0 20160609XRTRIM
|
||||
| page 6 offset 20480
|
||||
| 0: 0d 00 00 00 24 0e e0 00 0f f8 0f f0 0f e8 0f e0 ....$...........
|
||||
| 16: 0f d8 0f d0 0f c8 0f c0 0f b8 0f b0 0f a8 0f a0 ................
|
||||
| 32: 0f 98 0f 90 0f 88 0f 80 0f 78 0f 70 0f 68 0f 60 .........x.p.h.`
|
||||
| 48: 0f 58 0f 50 0f 48 0f 40 0f 38 0f 30 0f 28 0f 20 .X.P.H.@.8.0.(.
|
||||
| 64: 0f 18 0f 10 0f 08 0f 00 0e f8 0e f0 0e e8 0e e0 ................
|
||||
| 3808: 06 24 03 00 12 02 01 01 06 23 03 00 12 02 01 01 .$.......#......
|
||||
| 3824: 06 22 03 00 12 02 01 01 06 21 03 00 12 03 01 01 .........!......
|
||||
| 3840: 06 20 03 00 12 03 01 01 06 1f 03 00 12 03 01 01 . ..............
|
||||
| 3856: 06 1e 03 00 12 03 01 01 06 1d 03 00 12 03 01 01 ................
|
||||
| 3872: 06 1c 03 00 12 03 01 01 06 1b 03 00 12 02 01 01 ................
|
||||
| 3888: 06 1a 03 00 12 02 01 01 06 19 03 00 12 02 01 01 ................
|
||||
| 3904: 06 18 03 00 12 02 01 01 06 17 03 00 12 02 01 01 ................
|
||||
| 3920: 06 16 03 00 12 02 01 01 06 15 03 00 12 02 01 01 ................
|
||||
| 3936: 06 14 03 00 12 02 01 01 06 13 03 00 12 02 01 01 ................
|
||||
| 3952: 06 12 03 00 12 02 01 01 06 11 03 00 12 02 01 01 ................
|
||||
| 3968: 06 10 03 00 12 02 01 01 06 0f 03 00 12 02 01 01 ................
|
||||
| 3984: 06 0e 03 00 12 02 01 01 06 0d 03 00 12 02 01 01 ................
|
||||
| 4000: 06 0c 03 00 12 02 01 01 06 0b 03 00 12 02 01 01 ................
|
||||
| 4016: 06 0a 03 00 12 02 01 01 06 09 03 00 12 03 01 01 ................
|
||||
| 4032: 06 08 03 00 12 03 01 01 06 07 03 00 12 03 01 01 ................
|
||||
| 4048: 06 06 03 00 12 01 01 01 06 05 03 00 12 01 01 01 ................
|
||||
| 4064: 06 04 03 00 12 01 01 01 06 03 03 00 12 06 01 01 ................
|
||||
| 4080: 06 02 03 00 12 06 01 01 06 01 03 00 12 06 01 01 ................
|
||||
| page 7 offset 24576
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| page 8 offset 28672
|
||||
| 0: 0d 00 00 00 03 0f d6 00 0f f4 0f e9 0f d6 00 00 ................
|
||||
| 4048: 00 00 00 00 00 00 11 04 02 2b 69 6e 74 65 67 72 .........+integr
|
||||
| 4064: 69 74 79 2d 63 68 65 63 6b 09 02 02 1b 72 65 62 ity-check....reb
|
||||
| 4080: 75 69 6c 64 0a 01 02 1d 6f 70 74 69 5d 69 71 a5 uild....opti]iq.
|
||||
| end crash-41234e232809e7.db
|
||||
.testctrl prng_seed 1 db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 68.1 {
|
||||
PRAGMA reverse_unordered_selects=ON;
|
||||
INSERT INTO t1(t1) SELECT x FROM t2;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
|
||||
@ -59,10 +59,27 @@ do_catchsql_test 2.1 {
|
||||
SELECT fts5_expr()
|
||||
} {1 {wrong number of arguments to function fts5_expr}}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT fts5_expr_tcl()
|
||||
} {1 {wrong number of arguments to function fts5_expr_tcl}}
|
||||
|
||||
do_catchsql_test 2.3 {
|
||||
SELECT fts5_expr('')
|
||||
} {1 {fts5: syntax error near ""}}
|
||||
|
||||
do_catchsql_test 2.4 {
|
||||
SELECT fts5_expr(NULL)
|
||||
} {1 {fts5: syntax error near ""}}
|
||||
|
||||
do_catchsql_test 2.5 {
|
||||
SELECT fts5_expr(NULL, NULL)
|
||||
} {1 {parse error in ""}}
|
||||
|
||||
for {set i 0} {$i < 255} {incr i} {
|
||||
do_test 2.6.$i {
|
||||
lindex [catchsql {sELECT fts5_expr(NULL, char($i));}] 0
|
||||
} 1
|
||||
}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE e1 USING fts5(text, tokenize = 'porter unicode61');
|
||||
|
||||
@ -36,7 +36,7 @@ do_test 1.1 {
|
||||
execsql { INSERT INTO x8 VALUES( rnddoc(5) ); }
|
||||
}
|
||||
} msg] $msg
|
||||
} {1 {database or disk is full}}
|
||||
} {0 {}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@ -210,4 +210,76 @@ foreach {tn pgsz} {
|
||||
} {1000}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
PRAGMA encoding = 'UTF-16';
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
INSERT INTO vt0 VALUES (x'46f0');
|
||||
SELECT quote(c0) FROM vt0;
|
||||
} {X'46F0'}
|
||||
do_execsql_test 7.1 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
do_execsql_test 7.2 {
|
||||
INSERT INTO vt0(vt0) VALUES('rebuild');
|
||||
}
|
||||
do_execsql_test 7.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
do_execsql_test 7.4 {
|
||||
UPDATE vt0 SET c0='';
|
||||
}
|
||||
do_execsql_test 7.5 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket 7a458c2a5f4
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
PRAGMA locking_mode = EXCLUSIVE;
|
||||
PRAGMA journal_mode = PERSIST;
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
} {exclusive persist}
|
||||
do_execsql_test 8.1 {
|
||||
PRAGMA data_version
|
||||
} {1}
|
||||
do_execsql_test 8.2 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
PRAGMA data_version;
|
||||
} {1}
|
||||
do_execsql_test 8.1 {
|
||||
INSERT INTO vt0(vt0, rank) VALUES('usermerge', 2);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket [771fe617]
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
PRAGMA encoding = 'UTF16';
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
}
|
||||
|
||||
#explain_i { SELECT quote(SUBSTR(x'37', 0)); }
|
||||
#execsql { PRAGMA vdbe_trace = 1 }
|
||||
do_execsql_test 9.1.1 {
|
||||
SELECT quote(SUBSTR(x'37', 0));
|
||||
} {X'37'}
|
||||
do_execsql_test 9.1.2 {
|
||||
SELECT quote(x'37');
|
||||
} {X'37'}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 9.2 {
|
||||
INSERT INTO vt0 VALUES (SUBSTR(x'37', 0));
|
||||
-- INSERT INTO vt0 VALUES (x'37');
|
||||
}
|
||||
do_execsql_test 9.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -491,4 +491,30 @@ do_catchsql_test 14.2 {
|
||||
SELECT matchinfo(x1, 'd') FROM x1('a b c');
|
||||
} {1 {unrecognized matchinfo flag: d}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test using matchinfo() and similar on a non-full-text query
|
||||
#
|
||||
do_execsql_test 15.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y);
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
INSERT INTO t1 VALUES('c', 'd');
|
||||
}
|
||||
|
||||
do_execsql_test 15.1 {
|
||||
SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
|
||||
} {X'02000000'}
|
||||
|
||||
do_execsql_test 15.2 {
|
||||
DELETE FROM t1_content WHERE rowid=1;
|
||||
SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
|
||||
} {X'02000000'}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 15.3 {
|
||||
SELECT fts5_test_all(t1) FROM t1 LIMIT 1;
|
||||
} {
|
||||
{columnsize {0 0} columntext {c d} columntotalsize {2 2} poslist {} tokenize {c d} rowcount 2}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@ -106,6 +106,222 @@ do_execsql_test 2.2.5 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(a);
|
||||
PRAGMA reverse_unordered_selects = true;
|
||||
INSERT INTO vt0 VALUES('365062398'), (0), (0);
|
||||
INSERT INTO vt0(vt0, rank) VALUES('pgsz', '38');
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
UPDATE vt0 SET a = 399905135; -- unexpected: database disk image is malformed
|
||||
}
|
||||
do_execsql_test 3.2 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
INSERT INTO vt0(c0) VALUES ('xyz');
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
BEGIN;
|
||||
INSERT INTO vt0(c0) VALUES ('abc');
|
||||
INSERT INTO vt0(vt0) VALUES('rebuild');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
BEGIN;
|
||||
INSERT INTO vt0(vt0) VALUES('rebuild');
|
||||
INSERT INTO vt0(vt0) VALUES('rebuild');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket [81a7f7b9].
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0, c1);
|
||||
INSERT INTO vt0(vt0, rank) VALUES('pgsz', '65536');
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1236
|
||||
)
|
||||
INSERT INTO vt0(c0) SELECT '0' FROM s;
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
UPDATE vt0 SET c1 = 'T,D&p^y/7#3*v<b<4j7|f';
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_catchsql_test 5.3 {
|
||||
INSERT INTO vt0(vt0, rank) VALUES('pgsz', '65537');
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket [d392017c].
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
|
||||
)
|
||||
INSERT INTO vt0(c0) SELECT '0' FROM s;
|
||||
INSERT INTO vt0(vt0, rank) VALUES('crisismerge', 2000);
|
||||
INSERT INTO vt0(vt0, rank) VALUES('automerge', 0);
|
||||
} {}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
INSERT INTO vt0(vt0) VALUES('rebuild');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(rowid, x) VALUES(1, 'hello world');
|
||||
INSERT INTO t1(rowid, x) VALUES(2, 'well said');
|
||||
INSERT INTO t1(rowid, x) VALUES(3, 'hello said');
|
||||
INSERT INTO t1(rowid, x) VALUES(4, 'well world');
|
||||
|
||||
CREATE TABLE t2 (a, b);
|
||||
INSERT INTO t2 VALUES(1, 'hello');
|
||||
INSERT INTO t2 VALUES(2, 'world');
|
||||
INSERT INTO t2 VALUES(3, 'said');
|
||||
INSERT INTO t2 VALUES(4, 'hello');
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT rowid FROM t1 WHERE (rowid, x) IN (SELECT a, b FROM t2);
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
SELECT rowid FROM t1 WHERE rowid=2 AND t1 = 'hello';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0, tokenize = "ascii", prefix = 1);
|
||||
INSERT INTO vt0(c0) VALUES (x'd1');
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
CREATE VIRTUAL TABLE t1 using FTS5(mailcontent);
|
||||
insert into t1(rowid, mailcontent) values
|
||||
(-4764623217061966105, 'we are going to upgrade'),
|
||||
(8324454597464624651, 'we are going to upgrade');
|
||||
}
|
||||
|
||||
do_execsql_test 9.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 9.2 {
|
||||
SELECT rowid FROM t1('upgrade');
|
||||
} {
|
||||
-4764623217061966105 8324454597464624651
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE vt1 USING fts5(c1, c2, prefix = 1, tokenize = "ascii");
|
||||
INSERT INTO vt1 VALUES (x'e4', '䔬');
|
||||
}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
SELECT quote(CAST(c1 AS blob)), quote(CAST(c2 AS blob)) FROM vt1
|
||||
} {X'E4' X'E494AC'}
|
||||
|
||||
do_execsql_test 10.2 {
|
||||
INSERT INTO vt1(vt1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 11.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(
|
||||
c0, prefix = 71, tokenize = "porter ascii", prefix = 9
|
||||
);
|
||||
} {}
|
||||
do_execsql_test 11.1 {
|
||||
BEGIN;
|
||||
INSERT INTO vt0(c0) VALUES (x'e8');
|
||||
}
|
||||
do_execsql_test 11.2 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket [752fdbf6]
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 11.0 {
|
||||
PRAGMA encoding = 'UTF-16';
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0, c1);
|
||||
INSERT INTO vt0(vt0, rank) VALUES('pgsz', '37');
|
||||
INSERT INTO vt0(c0, c1) VALUES (0.66077, 1957391816);
|
||||
}
|
||||
do_execsql_test 11.1 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ticket [7c0e06b16]
|
||||
#
|
||||
do_execsql_test 12.0 {
|
||||
CREATE TABLE t1(a, b, rank);
|
||||
INSERT INTO t1 VALUES('a', 'hello', '');
|
||||
INSERT INTO t1 VALUES('b', 'world', '');
|
||||
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a);
|
||||
INSERT INTO ft VALUES('b');
|
||||
INSERT INTO ft VALUES('y');
|
||||
|
||||
CREATE TABLE t2(x, y, ft);
|
||||
INSERT INTO t2 VALUES(1, 2, 'x');
|
||||
INSERT INTO t2 VALUES(3, 4, 'b');
|
||||
}
|
||||
|
||||
do_execsql_test 12.1 {
|
||||
SELECT * FROM t1 NATURAL JOIN ft WHERE ft MATCH('b')
|
||||
} {b world {}}
|
||||
do_execsql_test 12.2 {
|
||||
SELECT * FROM ft NATURAL JOIN t1 WHERE ft MATCH('b')
|
||||
} {b world {}}
|
||||
do_execsql_test 12.3 {
|
||||
SELECT * FROM t2 JOIN ft USING (ft)
|
||||
} {3 4 b b}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
85
ext/fts5/test/fts5savepoint.test
Normal file
85
ext/fts5/test/fts5savepoint.test
Normal file
@ -0,0 +1,85 @@
|
||||
# 2019 Dec 26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5savepoint
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(c);
|
||||
BEGIN;
|
||||
SAVEPOINT one;
|
||||
INSERT INTO ft VALUES('a');
|
||||
SAVEPOINT two;
|
||||
INSERT INTO ft VALUES('b');
|
||||
RELEASE two;
|
||||
SAVEPOINT four;
|
||||
INSERT INTO ft VALUES('c');
|
||||
RELEASE four;
|
||||
SAVEPOINT three;
|
||||
INSERT INTO ft VALUES('d');
|
||||
ROLLBACK TO three;
|
||||
COMMIT;
|
||||
SELECT * FROM ft
|
||||
} {a b c}
|
||||
|
||||
reset_db
|
||||
do_catchsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(c);
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(c);
|
||||
DROP TABLE ft2_idx;
|
||||
BEGIN;
|
||||
INSERT INTO ft2 VALUES('a');
|
||||
INSERT INTO ft1 VALUES('a');
|
||||
SAVEPOINT two;
|
||||
INSERT INTO ft1 VALUES('b');
|
||||
COMMIT;
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
reset_db
|
||||
ifcapable fts3 {
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
CREATE VIRTUAL TABLE vt1 USING fts4(c0);
|
||||
INSERT INTO vt1(c0) VALUES(0);
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
BEGIN;
|
||||
UPDATE vt1 SET c0 = 0;
|
||||
INSERT INTO vt1(c0) VALUES (0), (0);
|
||||
UPDATE vt0 SET c0 = 0;
|
||||
INSERT INTO vt1(c0) VALUES (0);
|
||||
UPDATE vt1 SET c0 = 0;
|
||||
INSERT INTO vt1(vt1) VALUES('automerge=1');
|
||||
UPDATE vt1 SET c0 = 0;
|
||||
}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
DROP TABLE vt1;
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
SAVEPOINT x;
|
||||
INSERT INTO vt0 VALUES('x');
|
||||
COMMIT;
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -499,26 +499,27 @@ static void icuLoadCollation(
|
||||
** Register the ICU extension functions with database db.
|
||||
*/
|
||||
int sqlite3IcuInit(sqlite3 *db){
|
||||
# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
|
||||
static const struct IcuScalar {
|
||||
const char *zName; /* Function name */
|
||||
unsigned char nArg; /* Number of arguments */
|
||||
unsigned short enc; /* Optimal text encoding */
|
||||
unsigned int enc; /* Optimal text encoding */
|
||||
unsigned char iContext; /* sqlite3_user_data() context */
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} scalars[] = {
|
||||
{"icu_load_collation", 2, SQLITE_UTF8, 1, icuLoadCollation},
|
||||
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
||||
{"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
|
||||
{"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
|
||||
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
@ -900,6 +900,7 @@ static int amatchConnect(
|
||||
rc = amatchLoadRules(db, pNew, pzErr);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(word,distance,language,"
|
||||
"command HIDDEN,nword HIDDEN)"
|
||||
|
||||
@ -118,6 +118,7 @@ static int completionConnect(
|
||||
#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */
|
||||
#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */
|
||||
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x("
|
||||
" candidate TEXT,"
|
||||
|
||||
@ -119,11 +119,13 @@ int sqlite3_compress_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
|
||||
compressFunc, 0, 0);
|
||||
rc = sqlite3_create_function(db, "compress", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS,
|
||||
0, compressFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
|
||||
uncompressFunc, 0, 0);
|
||||
rc = sqlite3_create_function(db, "uncompress", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, uncompressFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -632,6 +632,15 @@ static int csvtabConnect(
|
||||
for(i=0; i<sizeof(azPValue)/sizeof(azPValue[0]); i++){
|
||||
sqlite3_free(azPValue[i]);
|
||||
}
|
||||
/* Rationale for DIRECTONLY:
|
||||
** An attacker who controls a database schema could use this vtab
|
||||
** to exfiltrate sensitive data from other files in the filesystem.
|
||||
** And, recommended practice is to put all CSV virtual tables in the
|
||||
** TEMP namespace, so they should still be usable from within TEMP
|
||||
** views, so there shouldn't be a serious loss of functionality by
|
||||
** prohibiting the use of this vtab from persistent triggers and views.
|
||||
*/
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
return SQLITE_OK;
|
||||
|
||||
csvtab_connect_oom:
|
||||
|
||||
@ -113,10 +113,12 @@ int sqlite3_eval_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "eval", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "eval", 2,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
|
||||
@ -585,6 +585,7 @@ static int fsdirConnect(
|
||||
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
return rc;
|
||||
@ -978,10 +979,12 @@ int sqlite3_fileio_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "readfile", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
readfileFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "writefile", -1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
writefileFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
@ -822,6 +822,7 @@ static int deltaparsevtabConnect(
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -1070,17 +1071,18 @@ int sqlite3_fossildelta_init(
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
static const int enc = SQLITE_UTF8|SQLITE_INNOCUOUS;
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "delta_create", 2, enc, 0,
|
||||
deltaCreateFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "delta_apply", 2, enc, 0,
|
||||
deltaApplyFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "delta_output_size", 1, enc, 0,
|
||||
deltaOutputSizeFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
@ -540,6 +540,8 @@ static int fuzzerConnect(
|
||||
if( rc!=SQLITE_OK ){
|
||||
fuzzerDisconnect((sqlite3_vtab *)pNew);
|
||||
pNew = 0;
|
||||
}else{
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,10 +121,12 @@ int sqlite3_ieee_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "ieee754", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
ieee754func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "ieee754", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
ieee754func, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
|
||||
115
ext/misc/json1.c
115
ext/misc/json1.c
@ -522,6 +522,37 @@ static void jsonReturnJson(
|
||||
sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate a single byte of Hex into an integer.
|
||||
** This routine only works if h really is a valid hexadecimal
|
||||
** character: 0..9a..fA..F
|
||||
*/
|
||||
static u8 jsonHexToInt(int h){
|
||||
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
|
||||
#ifdef SQLITE_EBCDIC
|
||||
h += 9*(1&~(h>>4));
|
||||
#else
|
||||
h += 9*(1&(h>>6));
|
||||
#endif
|
||||
return (u8)(h & 0xf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a 4-byte hex string into an integer
|
||||
*/
|
||||
static u32 jsonHexToInt4(const char *z){
|
||||
u32 v;
|
||||
assert( safe_isxdigit(z[0]) );
|
||||
assert( safe_isxdigit(z[1]) );
|
||||
assert( safe_isxdigit(z[2]) );
|
||||
assert( safe_isxdigit(z[3]) );
|
||||
v = (jsonHexToInt(z[0])<<12)
|
||||
+ (jsonHexToInt(z[1])<<8)
|
||||
+ (jsonHexToInt(z[2])<<4)
|
||||
+ jsonHexToInt(z[3]);
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make the JsonNode the return value of the function.
|
||||
*/
|
||||
@ -615,15 +646,8 @@ static void jsonReturn(
|
||||
}else{
|
||||
c = z[++i];
|
||||
if( c=='u' ){
|
||||
u32 v = 0, k;
|
||||
for(k=0; k<4; i++, k++){
|
||||
assert( i<n-2 );
|
||||
c = z[i+1];
|
||||
assert( safe_isxdigit(c) );
|
||||
if( c<='9' ) v = v*16 + c - '0';
|
||||
else if( c<='F' ) v = v*16 + c - 'A' + 10;
|
||||
else v = v*16 + c - 'a' + 10;
|
||||
}
|
||||
u32 v = jsonHexToInt4(z+i+1);
|
||||
i += 4;
|
||||
if( v==0 ) break;
|
||||
if( v<=0x7f ){
|
||||
zOut[j++] = (char)v;
|
||||
@ -631,9 +655,25 @@ static void jsonReturn(
|
||||
zOut[j++] = (char)(0xc0 | (v>>6));
|
||||
zOut[j++] = 0x80 | (v&0x3f);
|
||||
}else{
|
||||
zOut[j++] = (char)(0xe0 | (v>>12));
|
||||
zOut[j++] = 0x80 | ((v>>6)&0x3f);
|
||||
zOut[j++] = 0x80 | (v&0x3f);
|
||||
u32 vlo;
|
||||
if( (v&0xfc00)==0xd800
|
||||
&& i<n-6
|
||||
&& z[i+1]=='\\'
|
||||
&& z[i+2]=='u'
|
||||
&& ((vlo = jsonHexToInt4(z+i+3))&0xfc00)==0xdc00
|
||||
){
|
||||
/* We have a surrogate pair */
|
||||
v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
|
||||
i += 6;
|
||||
zOut[j++] = 0xf0 | (v>>18);
|
||||
zOut[j++] = 0x80 | ((v>>12)&0x3f);
|
||||
zOut[j++] = 0x80 | ((v>>6)&0x3f);
|
||||
zOut[j++] = 0x80 | (v&0x3f);
|
||||
}else{
|
||||
zOut[j++] = 0xe0 | (v>>12);
|
||||
zOut[j++] = 0x80 | ((v>>6)&0x3f);
|
||||
zOut[j++] = 0x80 | (v&0x3f);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( c=='b' ){
|
||||
@ -1136,18 +1176,49 @@ static JsonNode *jsonLookupStep(
|
||||
}
|
||||
return pNode;
|
||||
}
|
||||
}else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
|
||||
if( pRoot->eType!=JSON_ARRAY ) return 0;
|
||||
}else if( zPath[0]=='[' ){
|
||||
i = 0;
|
||||
j = 1;
|
||||
while( safe_isdigit(zPath[j]) ){
|
||||
i = i*10 + zPath[j] - '0';
|
||||
j++;
|
||||
}
|
||||
if( zPath[j]!=']' ){
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
if( j<2 || zPath[j]!=']' ){
|
||||
if( zPath[1]=='#' ){
|
||||
JsonNode *pBase = pRoot;
|
||||
int iBase = iRoot;
|
||||
if( pRoot->eType!=JSON_ARRAY ) return 0;
|
||||
for(;;){
|
||||
while( j<=pBase->n ){
|
||||
if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++;
|
||||
j += jsonNodeSize(&pBase[j]);
|
||||
}
|
||||
if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
|
||||
iBase += pBase->u.iAppend;
|
||||
pBase = &pParse->aNode[iBase];
|
||||
j = 1;
|
||||
}
|
||||
j = 2;
|
||||
if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){
|
||||
unsigned int x = 0;
|
||||
j = 3;
|
||||
do{
|
||||
x = x*10 + zPath[j] - '0';
|
||||
j++;
|
||||
}while( safe_isdigit(zPath[j]) );
|
||||
if( x>i ) return 0;
|
||||
i -= x;
|
||||
}
|
||||
if( zPath[j]!=']' ){
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
}
|
||||
}else{
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if( pRoot->eType!=JSON_ARRAY ) return 0;
|
||||
zPath += j + 1;
|
||||
j = 1;
|
||||
for(;;){
|
||||
@ -2020,6 +2091,7 @@ static int jsonEachConnect(
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -2510,16 +2582,19 @@ int sqlite3Json1Init(sqlite3 *db){
|
||||
{ "json_tree", &jsonTreeModule },
|
||||
};
|
||||
#endif
|
||||
static const int enc =
|
||||
SQLITE_UTF8 |
|
||||
SQLITE_DETERMINISTIC |
|
||||
SQLITE_INNOCUOUS;
|
||||
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
||||
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg, enc,
|
||||
(void*)&aFunc[i].flag,
|
||||
aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
|
||||
SQLITE_SUBTYPE | SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
|
||||
SQLITE_SUBTYPE | enc, 0,
|
||||
aAgg[i].xStep, aAgg[i].xFinal,
|
||||
aAgg[i].xValue, jsonGroupInverse, 0);
|
||||
}
|
||||
|
||||
@ -297,14 +297,17 @@ int sqlite3_nextchar_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "next_char", 3,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "next_char", 4,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "next_char", 5,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
|
||||
68
ext/misc/noop.c
Normal file
68
ext/misc/noop.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
** 2020-01-08
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This SQLite extension implements a noop() function used for testing.
|
||||
**
|
||||
** Variants:
|
||||
**
|
||||
** noop(X) The default. Deterministic.
|
||||
** noop_i(X) Deterministic and innocuous.
|
||||
** noop_do(X) Deterministic and direct-only.
|
||||
** noop_nd(X) Non-deterministic.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Implementation of the noop() function.
|
||||
**
|
||||
** The function returns its argument, unchanged.
|
||||
*/
|
||||
static void noopfunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_noop_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "noop", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "noop_i", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "noop_do", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "noop_nd", 1,
|
||||
SQLITE_UTF8,
|
||||
0, noopfunc, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
@ -213,7 +213,8 @@ int sqlite3_percentile_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "percentile", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
0, percentStep, percentFinal);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -79,6 +79,7 @@ static int prefixesConnect(
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ static unsigned re_next_char(ReInput *p){
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 ){
|
||||
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
||||
p->i += 2;
|
||||
if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||||
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||||
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
||||
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
||||
@ -754,7 +754,7 @@ int sqlite3_regexp_init(
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
||||
re_sql_func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
|
||||
0, re_sql_func, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -105,8 +105,9 @@ int sqlite3_rot_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "rot13", 1, SQLITE_UTF8, 0,
|
||||
rot13func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "rot13", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, rot13func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
|
||||
}
|
||||
|
||||
@ -126,6 +126,7 @@ static int seriesConnect(
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -39,25 +39,9 @@ struct SHA1Context {
|
||||
unsigned char buffer[64];
|
||||
};
|
||||
|
||||
|
||||
#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
|
||||
/*
|
||||
* GCC by itself only generates left rotates. Use right rotates if
|
||||
* possible to be kinder to dinky implementations with iterative rotate
|
||||
* instructions.
|
||||
*/
|
||||
#define SHA_ROT(op, x, k) \
|
||||
({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
|
||||
#define rol(x,k) SHA_ROT("roll", x, k)
|
||||
#define ror(x,k) SHA_ROT("rorl", x, k)
|
||||
|
||||
#else
|
||||
/* Generic C equivalent */
|
||||
#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r))
|
||||
#define rol(x,k) SHA_ROT(x,k,32-(k))
|
||||
#define ror(x,k) SHA_ROT(x,32-(k),k)
|
||||
#endif
|
||||
|
||||
|
||||
#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \
|
||||
|(rol(block[i],8)&0x00FF00FF))
|
||||
@ -397,10 +381,11 @@ int sqlite3_sha_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
sha1Func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "sha1_query", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
sha1QueryFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
|
||||
@ -696,19 +696,23 @@ int sqlite3_shathree_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sha3", 1, SQLITE_UTF8, 0,
|
||||
sha3Func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "sha3", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, sha3Func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha3", 2, SQLITE_UTF8, 0,
|
||||
sha3Func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "sha3", 2,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, sha3Func, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8, 0,
|
||||
sha3QueryFunc, 0, 0);
|
||||
rc = sqlite3_create_function(db, "sha3_query", 1,
|
||||
SQLITE_UTF8 | SQLITE_DIRECTONLY,
|
||||
0, sha3QueryFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha3_query", 2, SQLITE_UTF8, 0,
|
||||
sha3QueryFunc, 0, 0);
|
||||
rc = sqlite3_create_function(db, "sha3_query", 2,
|
||||
SQLITE_UTF8 | SQLITE_DIRECTONLY,
|
||||
0, sha3QueryFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2069,6 +2069,7 @@ static int spellfix1Init(
|
||||
if( pNew->zTableName==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(word,rank,distance,langid, "
|
||||
"score, matchlen, phonehash HIDDEN, "
|
||||
|
||||
@ -111,10 +111,12 @@ int sqlite3_sqlar_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sqlar_compress", 1, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "sqlar_compress", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
sqlarCompressFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sqlar_uncompress", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "sqlar_uncompress", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
sqlarUncompressFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
|
||||
@ -502,11 +502,13 @@ int sqlite3_totype_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
|
||||
tointegerFunc, 0, 0);
|
||||
rc = sqlite3_create_function(db, "tointeger", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS, 0,
|
||||
tointegerFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
|
||||
torealFunc, 0, 0);
|
||||
rc = sqlite3_create_function(db, "toreal", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS, 0,
|
||||
torealFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
209
ext/misc/urifuncs.c
Normal file
209
ext/misc/urifuncs.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
** 2020-01-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 SQLite extension implements various SQL functions used to access
|
||||
** the following SQLite C-language APIs:
|
||||
**
|
||||
** sqlite3_uri_parameter()
|
||||
** sqlite3_uri_boolean()
|
||||
** sqlite3_uri_int64()
|
||||
** sqlite3_uri_key()
|
||||
** sqlite3_filename_database()
|
||||
** sqlite3_filename_journal()
|
||||
** sqlite3_filename_wal()
|
||||
** sqlite3_db_filename()
|
||||
**
|
||||
** These SQL functions are for testing and demonstration purposes only.
|
||||
**
|
||||
**
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_db_filename(SCHEMA)
|
||||
**
|
||||
** Return the filename corresponding to SCHEMA.
|
||||
*/
|
||||
static void func_db_filename(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
sqlite3_result_text(context, zFile, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_parameter(SCHEMA,NAME)
|
||||
**
|
||||
** Return the value of the NAME query parameter to the database for SCHEMA
|
||||
*/
|
||||
static void func_uri_parameter(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = sqlite3_uri_parameter(zFile, zName);
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_boolean(SCHEMA,NAME,DEFAULT)
|
||||
**
|
||||
** Return the boolean value of the NAME query parameter to
|
||||
** the database for SCHEMA
|
||||
*/
|
||||
static void func_uri_boolean(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
int iDflt = sqlite3_value_int(argv[2]);
|
||||
int iRes = sqlite3_uri_boolean(zFile, zName, iDflt);
|
||||
sqlite3_result_int(context, iRes);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_key(SCHEMA,N)
|
||||
**
|
||||
** Return the name of the Nth query parameter
|
||||
*/
|
||||
static void func_uri_key(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int N = sqlite3_value_int(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = sqlite3_uri_key(zFile, N);
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_int64(SCHEMA,NAME,DEFAULT)
|
||||
**
|
||||
** Return the int64 value of the NAME query parameter to
|
||||
** the database for SCHEMA
|
||||
*/
|
||||
static void func_uri_int64(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
sqlite3_int64 iDflt = sqlite3_value_int64(argv[2]);
|
||||
sqlite3_int64 iRes = sqlite3_uri_int64(zFile, zName, iDflt);
|
||||
sqlite3_result_int64(context, iRes);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_filename_database(SCHEMA)
|
||||
**
|
||||
** Return the database filename for SCHEMA
|
||||
*/
|
||||
static void func_filename_database(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = zFile ? sqlite3_filename_database(zFile) : 0;
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_filename_journal(SCHEMA)
|
||||
**
|
||||
** Return the rollback journal filename for SCHEMA
|
||||
*/
|
||||
static void func_filename_journal(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = zFile ? sqlite3_filename_journal(zFile) : 0;
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_filename_wal(SCHEMA)
|
||||
**
|
||||
** Return the WAL filename for SCHEMA
|
||||
*/
|
||||
static void func_filename_wal(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = zFile ? sqlite3_filename_wal(zFile) : 0;
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_urifuncs_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
static const struct {
|
||||
const char *zFuncName;
|
||||
int nArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "sqlite3_db_filename", 1, func_db_filename },
|
||||
{ "sqlite3_uri_parameter", 2, func_uri_parameter },
|
||||
{ "sqlite3_uri_boolean", 3, func_uri_boolean },
|
||||
{ "sqlite3_uri_int64", 3, func_uri_int64 },
|
||||
{ "sqlite3_uri_key", 2, func_uri_key },
|
||||
{ "sqlite3_filename_database", 1, func_filename_database },
|
||||
{ "sqlite3_filename_journal", 1, func_filename_journal },
|
||||
{ "sqlite3_filename_wal", 1, func_filename_wal },
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
for(i=0; rc==SQLITE_OK && i<sizeof(aFunc)/sizeof(aFunc[0]); i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
|
||||
SQLITE_UTF8, 0,
|
||||
aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
233
ext/misc/uuid.c
Normal file
233
ext/misc/uuid.c
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
** 2019-10-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 SQLite extension implements functions that handling RFC-4122 UUIDs
|
||||
** Three SQL functions are implemented:
|
||||
**
|
||||
** uuid() - generate a version 4 UUID as a string
|
||||
** uuid_str(X) - convert a UUID X into a well-formed UUID string
|
||||
** uuid_blob(X) - convert a UUID X into a 16-byte blob
|
||||
**
|
||||
** The output from uuid() and uuid_str(X) are always well-formed RFC-4122
|
||||
** UUID strings in this format:
|
||||
**
|
||||
** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
|
||||
**
|
||||
** All of the 'x', 'M', and 'N' values are lower-case hexadecimal digits.
|
||||
** The M digit indicates the "version". For uuid()-generated UUIDs, the
|
||||
** version is always "4" (a random UUID). The upper three bits of N digit
|
||||
** are the "variant". This library only supports variant 1 (indicated
|
||||
** by values of N between '8' and 'b') as those are overwhelming the most
|
||||
** common. Other variants are for legacy compatibility only.
|
||||
**
|
||||
** The output of uuid_blob(X) is always a 16-byte blob. The UUID input
|
||||
** string is converted in network byte order (big-endian) in accordance
|
||||
** with RFC-4122 specifications for variant-1 UUIDs. Note that network
|
||||
** byte order is *always* used, even if the input self-identifies as a
|
||||
** variant-2 UUID.
|
||||
**
|
||||
** The input X to the uuid_str() and uuid_blob() functions can be either
|
||||
** a string or a BLOB. If it is a BLOB it must be exactly 16 bytes in
|
||||
** length or else a NULL is returned. If the input is a string it must
|
||||
** consist of 32 hexadecimal digits, upper or lower case, optionally
|
||||
** surrounded by {...} and with optional "-" characters interposed in the
|
||||
** middle. The flexibility of input is inspired by the PostgreSQL
|
||||
** implementation of UUID functions that accept in all of the following
|
||||
** formats:
|
||||
**
|
||||
** A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
|
||||
** {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
|
||||
** a0eebc999c0b4ef8bb6d6bb9bd380a11
|
||||
** a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
|
||||
** {a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}
|
||||
**
|
||||
** If any of the above inputs are passed into uuid_str(), the output will
|
||||
** always be in the canonical RFC-4122 format:
|
||||
**
|
||||
** a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
|
||||
**
|
||||
** If the X input string has too few or too many digits or contains
|
||||
** stray characters other than {, }, or -, then NULL is returned.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if !defined(SQLITE_ASCII) && !defined(SQLITE_EBCDIC)
|
||||
# define SQLITE_ASCII 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Translate a single byte of Hex into an integer.
|
||||
** This routine only works if h really is a valid hexadecimal
|
||||
** character: 0..9a..fA..F
|
||||
*/
|
||||
static unsigned char sqlite3UuidHexToInt(int h){
|
||||
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
|
||||
#ifdef SQLITE_ASCII
|
||||
h += 9*(1&(h>>6));
|
||||
#endif
|
||||
#ifdef SQLITE_EBCDIC
|
||||
h += 9*(1&~(h>>4));
|
||||
#endif
|
||||
return (unsigned char)(h & 0xf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a 16-byte BLOB into a well-formed RFC-4122 UUID. The output
|
||||
** buffer zStr should be at least 37 bytes in length. The output will
|
||||
** be zero-terminated.
|
||||
*/
|
||||
static void sqlite3UuidBlobToStr(
|
||||
const unsigned char *aBlob, /* Input blob */
|
||||
unsigned char *zStr /* Write the answer here */
|
||||
){
|
||||
static const char zDigits[] = "0123456789abcdef";
|
||||
int i, k;
|
||||
unsigned char x;
|
||||
k = 0;
|
||||
for(i=0, k=0x550; i<16; i++, k=k>>1){
|
||||
if( k&1 ){
|
||||
zStr[0] = '-';
|
||||
zStr++;
|
||||
}
|
||||
x = aBlob[i];
|
||||
zStr[0] = zDigits[x>>4];
|
||||
zStr[1] = zDigits[x&0xf];
|
||||
zStr += 2;
|
||||
}
|
||||
*zStr = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to parse a zero-terminated input string zStr into a binary
|
||||
** UUID. Return 0 on success, or non-zero if the input string is not
|
||||
** parsable.
|
||||
*/
|
||||
static int sqlite3UuidStrToBlob(
|
||||
const unsigned char *zStr, /* Input string */
|
||||
unsigned char *aBlob /* Write results here */
|
||||
){
|
||||
int i;
|
||||
if( zStr[0]=='{' ) zStr++;
|
||||
for(i=0; i<16; i++){
|
||||
if( zStr[0]=='-' ) zStr++;
|
||||
if( isxdigit(zStr[0]) && isxdigit(zStr[1]) ){
|
||||
aBlob[i] = (sqlite3UuidHexToInt(zStr[0])<<4)
|
||||
+ sqlite3UuidHexToInt(zStr[1]);
|
||||
zStr += 2;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( zStr[0]=='}' ) zStr++;
|
||||
return zStr[0]!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Render sqlite3_value pIn as a 16-byte UUID blob. Return a pointer
|
||||
** to the blob, or NULL if the input is not well-formed.
|
||||
*/
|
||||
static const unsigned char *sqlite3UuidInputToBlob(
|
||||
sqlite3_value *pIn, /* Input text */
|
||||
unsigned char *pBuf /* output buffer */
|
||||
){
|
||||
switch( sqlite3_value_type(pIn) ){
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *z = sqlite3_value_text(pIn);
|
||||
if( sqlite3UuidStrToBlob(z, pBuf) ) return 0;
|
||||
return pBuf;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_value_bytes(pIn);
|
||||
return n==16 ? sqlite3_value_blob(pIn) : 0;
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of uuid() */
|
||||
static void sqlite3UuidFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char aBlob[16];
|
||||
unsigned char zStr[37];
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
sqlite3_randomness(16, aBlob);
|
||||
aBlob[6] = (aBlob[6]&0x0f) + 0x40;
|
||||
aBlob[8] = (aBlob[8]&0x3f) + 0x80;
|
||||
sqlite3UuidBlobToStr(aBlob, zStr);
|
||||
sqlite3_result_text(context, (char*)zStr, 36, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/* Implementation of uuid_str() */
|
||||
static void sqlite3UuidStrFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char aBlob[16];
|
||||
unsigned char zStr[37];
|
||||
const unsigned char *pBlob;
|
||||
(void)argc;
|
||||
pBlob = sqlite3UuidInputToBlob(argv[0], aBlob);
|
||||
if( pBlob==0 ) return;
|
||||
sqlite3UuidBlobToStr(pBlob, zStr);
|
||||
sqlite3_result_text(context, (char*)zStr, 36, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/* Implementation of uuid_blob() */
|
||||
static void sqlite3UuidBlobFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char aBlob[16];
|
||||
const unsigned char *pBlob;
|
||||
(void)argc;
|
||||
pBlob = sqlite3UuidInputToBlob(argv[0], aBlob);
|
||||
if( pBlob==0 ) return;
|
||||
sqlite3_result_blob(context, pBlob, 16, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_uuid_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "uuid", 0, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
sqlite3UuidFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uuid_str", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, sqlite3UuidStrFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uuid_blob", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, sqlite3UuidBlobFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -50,6 +50,7 @@ static int wholenumberConnect(
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -369,6 +369,7 @@ static int zipfileConnect(
|
||||
zipfileDequote(pNew->zFile);
|
||||
}
|
||||
}
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
return rc;
|
||||
}
|
||||
@ -981,25 +982,25 @@ static int zipfileDeflate(
|
||||
u8 **ppOut, int *pnOut, /* Output */
|
||||
char **pzErr /* OUT: Error message */
|
||||
){
|
||||
sqlite3_int64 nAlloc = compressBound(nIn);
|
||||
u8 *aOut;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_int64 nAlloc;
|
||||
z_stream str;
|
||||
u8 *aOut;
|
||||
|
||||
memset(&str, 0, sizeof(str));
|
||||
str.next_in = (Bytef*)aIn;
|
||||
str.avail_in = nIn;
|
||||
deflateInit2(&str, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
|
||||
|
||||
nAlloc = deflateBound(&str, nIn);
|
||||
aOut = (u8*)sqlite3_malloc64(nAlloc);
|
||||
if( aOut==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int res;
|
||||
z_stream str;
|
||||
memset(&str, 0, sizeof(str));
|
||||
str.next_in = (Bytef*)aIn;
|
||||
str.avail_in = nIn;
|
||||
str.next_out = aOut;
|
||||
str.avail_out = nAlloc;
|
||||
|
||||
deflateInit2(&str, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
|
||||
res = deflate(&str, Z_FINISH);
|
||||
|
||||
if( res==Z_STREAM_END ){
|
||||
*ppOut = aOut;
|
||||
*pnOut = (int)str.total_out;
|
||||
@ -1308,10 +1309,10 @@ static int zipfileBestIndex(
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
pIdxInfo->estimatedCost = 1000.0;
|
||||
if( idx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idx].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[idx].omit = 1;
|
||||
pIdxInfo->estimatedCost = 1000.0;
|
||||
pIdxInfo->idxNum = 1;
|
||||
}else if( unusable ){
|
||||
return SQLITE_CONSTRAINT;
|
||||
@ -1433,8 +1434,8 @@ static int zipfileGetMode(
|
||||
** identical, ignoring any trailing '/' character in either path. */
|
||||
static int zipfileComparePath(const char *zA, const char *zB, int nB){
|
||||
int nA = (int)strlen(zA);
|
||||
if( zA[nA-1]=='/' ) nA--;
|
||||
if( zB[nB-1]=='/' ) nB--;
|
||||
if( nA>0 && zA[nA-1]=='/' ) nA--;
|
||||
if( nB>0 && zB[nB-1]=='/' ) nB--;
|
||||
if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -1444,6 +1445,10 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( pTab->pWriteFd==0 );
|
||||
if( pTab->zFile==0 || pTab->zFile[0]==0 ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Open a write fd on the file. Also load the entire central directory
|
||||
** structure into memory. During the transaction any new file data is
|
||||
@ -1618,6 +1623,7 @@ static int zipfileUpdate(
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
zPath = (const char*)sqlite3_value_text(apVal[2]);
|
||||
if( zPath==0 ) zPath = "";
|
||||
nPath = (int)strlen(zPath);
|
||||
mTime = zipfileGetTime(apVal[4]);
|
||||
}
|
||||
@ -1627,11 +1633,15 @@ static int zipfileUpdate(
|
||||
** '/'. This appears to be required for compatibility with info-zip
|
||||
** (the unzip command on unix). It does not create directories
|
||||
** otherwise. */
|
||||
if( zPath[nPath-1]!='/' ){
|
||||
if( nPath<=0 || zPath[nPath-1]!='/' ){
|
||||
zFree = sqlite3_mprintf("%s/", zPath);
|
||||
if( zFree==0 ){ rc = SQLITE_NOMEM; }
|
||||
zPath = (const char*)zFree;
|
||||
nPath++;
|
||||
if( zFree==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
nPath = 0;
|
||||
}else{
|
||||
nPath = (int)strlen(zPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2024,19 +2034,19 @@ void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
|
||||
** at the end of the path. Or, if this is not a directory and the path
|
||||
** ends in '/' it is an error. */
|
||||
if( bIsDir==0 ){
|
||||
if( zName[nName-1]=='/' ){
|
||||
if( nName>0 && zName[nName-1]=='/' ){
|
||||
zErr = sqlite3_mprintf("non-directory name must not end with /");
|
||||
rc = SQLITE_ERROR;
|
||||
goto zipfile_step_out;
|
||||
}
|
||||
}else{
|
||||
if( zName[nName-1]!='/' ){
|
||||
if( nName==0 || zName[nName-1]!='/' ){
|
||||
zName = zFree = sqlite3_mprintf("%s/", zName);
|
||||
nName++;
|
||||
if( zName==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto zipfile_step_out;
|
||||
}
|
||||
nName = (int)strlen(zName);
|
||||
}else{
|
||||
while( nName>1 && zName[nName-2]=='/' ) nName--;
|
||||
}
|
||||
|
||||
@ -4936,33 +4936,6 @@ static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** A main database named zName has just been opened. The following
|
||||
** function returns a pointer to a buffer owned by SQLite that contains
|
||||
** the name of the *-wal file this db connection will use. SQLite
|
||||
** happens to pass a pointer to this buffer when using xAccess()
|
||||
** or xOpen() to operate on the *-wal file.
|
||||
*/
|
||||
static const char *rbuMainToWal(const char *zName, int flags){
|
||||
int n = (int)strlen(zName);
|
||||
const char *z = &zName[n];
|
||||
if( flags & SQLITE_OPEN_URI ){
|
||||
int odd = 0;
|
||||
while( 1 ){
|
||||
if( z[0]==0 ){
|
||||
odd = 1 - odd;
|
||||
if( odd && z[1]==0 ) break;
|
||||
}
|
||||
z++;
|
||||
}
|
||||
z += 2;
|
||||
}else{
|
||||
while( *z==0 ) z++;
|
||||
}
|
||||
z += (n + 8 + 1);
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an rbu file handle.
|
||||
*/
|
||||
@ -5011,7 +4984,7 @@ static int rbuVfsOpen(
|
||||
** the name of the *-wal file this db connection will use. SQLite
|
||||
** happens to pass a pointer to this buffer when using xAccess()
|
||||
** or xOpen() to operate on the *-wal file. */
|
||||
pFd->zWal = rbuMainToWal(zName, flags);
|
||||
pFd->zWal = sqlite3_filename_wal(zName);
|
||||
}
|
||||
else if( flags & SQLITE_OPEN_WAL ){
|
||||
rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0);
|
||||
@ -5026,7 +4999,7 @@ static int rbuVfsOpen(
|
||||
char *zCopy;
|
||||
if( rbuIsVacuum(pDb->pRbu) ){
|
||||
zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main");
|
||||
zBase = rbuMainToWal(zBase, SQLITE_OPEN_URI);
|
||||
zBase = sqlite3_filename_wal(zBase);
|
||||
}
|
||||
nCopy = strlen(zBase);
|
||||
zCopy = sqlite3_malloc64(nCopy+2);
|
||||
|
||||
@ -1345,17 +1345,11 @@ static int geopolyFilter(
|
||||
RtreeNode *pRoot = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int iCell = 0;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
rtreeReference(pRtree);
|
||||
|
||||
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
|
||||
freeCursorConstraints(pCsr);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
pStmt = pCsr->pReadAux;
|
||||
memset(pCsr, 0, sizeof(RtreeCursor));
|
||||
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
|
||||
pCsr->pReadAux = pStmt;
|
||||
resetCursor(pCsr);
|
||||
|
||||
pCsr->iStrategy = idxNum;
|
||||
if( idxNum==1 ){
|
||||
@ -1792,14 +1786,20 @@ static int sqlite3_geopoly_init(sqlite3 *db){
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
|
||||
int enc = aFunc[i].bPure ? SQLITE_UTF8|SQLITE_DETERMINISTIC : SQLITE_UTF8;
|
||||
int enc;
|
||||
if( aFunc[i].bPure ){
|
||||
enc = SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS;
|
||||
}else{
|
||||
enc = SQLITE_UTF8|SQLITE_DIRECTONLY;
|
||||
}
|
||||
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
|
||||
enc, 0,
|
||||
aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aAgg[i].zName, 1, SQLITE_UTF8, 0,
|
||||
0, aAgg[i].xStep, aAgg[i].xFinal);
|
||||
rc = sqlite3_create_function(db, aAgg[i].zName, 1,
|
||||
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS, 0,
|
||||
0, aAgg[i].xStep, aAgg[i].xFinal);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
|
||||
|
||||
@ -62,6 +62,7 @@
|
||||
#else
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
#include "sqlite3rtree.h"
|
||||
@ -325,6 +326,12 @@ struct RtreeConstraint {
|
||||
#define RTREE_MATCH 0x46 /* F: Old-style sqlite3_rtree_geometry_callback() */
|
||||
#define RTREE_QUERY 0x47 /* G: New-style sqlite3_rtree_query_callback() */
|
||||
|
||||
/* Special operators available only on cursors. Needs to be consecutive
|
||||
** with the normal values above, but must be less than RTREE_MATCH. These
|
||||
** are used in the cursor for contraints such as x=NULL (RTREE_FALSE) or
|
||||
** x<'xyz' (RTREE_TRUE) */
|
||||
#define RTREE_TRUE 0x3f /* ? */
|
||||
#define RTREE_FALSE 0x40 /* @ */
|
||||
|
||||
/*
|
||||
** An rtree structure node.
|
||||
@ -1058,9 +1065,12 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
|
||||
|
||||
/*
|
||||
** Free the RtreeCursor.aConstraint[] array and its contents.
|
||||
** Reset a cursor back to its initial state.
|
||||
*/
|
||||
static void freeCursorConstraints(RtreeCursor *pCsr){
|
||||
static void resetCursor(RtreeCursor *pCsr){
|
||||
Rtree *pRtree = (Rtree *)(pCsr->base.pVtab);
|
||||
int ii;
|
||||
sqlite3_stmt *pStmt;
|
||||
if( pCsr->aConstraint ){
|
||||
int i; /* Used to iterate through constraint array */
|
||||
for(i=0; i<pCsr->nConstraint; i++){
|
||||
@ -1073,6 +1083,13 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
|
||||
sqlite3_free(pCsr->aConstraint);
|
||||
pCsr->aConstraint = 0;
|
||||
}
|
||||
for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
pStmt = pCsr->pReadAux;
|
||||
memset(pCsr, 0, sizeof(RtreeCursor));
|
||||
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
|
||||
pCsr->pReadAux = pStmt;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1080,13 +1097,10 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
|
||||
*/
|
||||
static int rtreeClose(sqlite3_vtab_cursor *cur){
|
||||
Rtree *pRtree = (Rtree *)(cur->pVtab);
|
||||
int ii;
|
||||
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
||||
assert( pRtree->nCursor>0 );
|
||||
freeCursorConstraints(pCsr);
|
||||
resetCursor(pCsr);
|
||||
sqlite3_finalize(pCsr->pReadAux);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
|
||||
sqlite3_free(pCsr);
|
||||
pRtree->nCursor--;
|
||||
nodeBlobReset(pRtree);
|
||||
@ -1244,9 +1258,12 @@ static void rtreeNonleafConstraint(
|
||||
pCellData += 8 + 4*(p->iCoord&0xfe);
|
||||
|
||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ );
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
|
||||
|| p->op==RTREE_FALSE );
|
||||
assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
|
||||
switch( p->op ){
|
||||
case RTREE_TRUE: return; /* Always satisfied */
|
||||
case RTREE_FALSE: break; /* Never satisfied */
|
||||
case RTREE_LE:
|
||||
case RTREE_LT:
|
||||
case RTREE_EQ:
|
||||
@ -1284,16 +1301,19 @@ static void rtreeLeafConstraint(
|
||||
RtreeDValue xN; /* Coordinate value converted to a double */
|
||||
|
||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ );
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
|
||||
|| p->op==RTREE_FALSE );
|
||||
pCellData += 8 + p->iCoord*4;
|
||||
assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
|
||||
RTREE_DECODE_COORD(eInt, pCellData, xN);
|
||||
switch( p->op ){
|
||||
case RTREE_LE: if( xN <= p->u.rValue ) return; break;
|
||||
case RTREE_LT: if( xN < p->u.rValue ) return; break;
|
||||
case RTREE_GE: if( xN >= p->u.rValue ) return; break;
|
||||
case RTREE_GT: if( xN > p->u.rValue ) return; break;
|
||||
default: if( xN == p->u.rValue ) return; break;
|
||||
case RTREE_TRUE: return; /* Always satisfied */
|
||||
case RTREE_FALSE: break; /* Never satisfied */
|
||||
case RTREE_LE: if( xN <= p->u.rValue ) return; break;
|
||||
case RTREE_LT: if( xN < p->u.rValue ) return; break;
|
||||
case RTREE_GE: if( xN >= p->u.rValue ) return; break;
|
||||
case RTREE_GT: if( xN > p->u.rValue ) return; break;
|
||||
default: if( xN == p->u.rValue ) return; break;
|
||||
}
|
||||
*peWithin = NOT_WITHIN;
|
||||
}
|
||||
@ -1786,17 +1806,11 @@ static int rtreeFilter(
|
||||
int ii;
|
||||
int rc = SQLITE_OK;
|
||||
int iCell = 0;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
rtreeReference(pRtree);
|
||||
|
||||
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
|
||||
freeCursorConstraints(pCsr);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
pStmt = pCsr->pReadAux;
|
||||
memset(pCsr, 0, sizeof(RtreeCursor));
|
||||
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
|
||||
pCsr->pReadAux = pStmt;
|
||||
resetCursor(pCsr);
|
||||
|
||||
pCsr->iStrategy = idxNum;
|
||||
if( idxNum==1 ){
|
||||
@ -1805,7 +1819,15 @@ static int rtreeFilter(
|
||||
RtreeSearchPoint *p; /* Search point for the leaf */
|
||||
i64 iRowid = sqlite3_value_int64(argv[0]);
|
||||
i64 iNode = 0;
|
||||
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
||||
int eType = sqlite3_value_numeric_type(argv[0]);
|
||||
if( eType==SQLITE_INTEGER
|
||||
|| (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid)
|
||||
){
|
||||
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
pLeaf = 0;
|
||||
}
|
||||
if( rc==SQLITE_OK && pLeaf!=0 ){
|
||||
p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
|
||||
assert( p!=0 ); /* Always returns pCsr->sPoint */
|
||||
@ -1835,6 +1857,7 @@ static int rtreeFilter(
|
||||
|| (idxStr && (int)strlen(idxStr)==argc*2) );
|
||||
for(ii=0; ii<argc; ii++){
|
||||
RtreeConstraint *p = &pCsr->aConstraint[ii];
|
||||
int eType = sqlite3_value_numeric_type(argv[ii]);
|
||||
p->op = idxStr[ii*2];
|
||||
p->iCoord = idxStr[ii*2+1]-'0';
|
||||
if( p->op>=RTREE_MATCH ){
|
||||
@ -1849,12 +1872,21 @@ static int rtreeFilter(
|
||||
p->pInfo->nCoord = pRtree->nDim2;
|
||||
p->pInfo->anQueue = pCsr->anQueue;
|
||||
p->pInfo->mxLevel = pRtree->iDepth + 1;
|
||||
}else{
|
||||
}else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
p->u.rValue = sqlite3_value_int64(argv[ii]);
|
||||
#else
|
||||
p->u.rValue = sqlite3_value_double(argv[ii]);
|
||||
#endif
|
||||
}else{
|
||||
p->u.rValue = RTREE_ZERO;
|
||||
if( eType==SQLITE_NULL ){
|
||||
p->op = RTREE_FALSE;
|
||||
}else if( p->op==RTREE_LT || p->op==RTREE_LE ){
|
||||
p->op = RTREE_TRUE;
|
||||
}else{
|
||||
p->op = RTREE_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3631,6 +3663,14 @@ static int getNodeSize(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the length of a token
|
||||
*/
|
||||
static int rtreeTokenLength(const char *z){
|
||||
int dummy = 0;
|
||||
return sqlite3GetToken((const unsigned char*)z,&dummy);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the implementation of both the xConnect and xCreate
|
||||
** methods of the r-tree virtual table.
|
||||
@ -3667,8 +3707,8 @@ static int rtreeInit(
|
||||
};
|
||||
|
||||
assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */
|
||||
if( argc>RTREE_MAX_AUX_COLUMN+3 ){
|
||||
*pzErr = sqlite3_mprintf("%s", aErrMsg[3]);
|
||||
if( argc<6 || argc>RTREE_MAX_AUX_COLUMN+3 ){
|
||||
*pzErr = sqlite3_mprintf("%s", aErrMsg[2 + (argc>=6)]);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@ -3696,16 +3736,18 @@ static int rtreeInit(
|
||||
** the r-tree table schema.
|
||||
*/
|
||||
pSql = sqlite3_str_new(db);
|
||||
sqlite3_str_appendf(pSql, "CREATE TABLE x(%s", argv[3]);
|
||||
sqlite3_str_appendf(pSql, "CREATE TABLE x(%.*s INT",
|
||||
rtreeTokenLength(argv[3]), argv[3]);
|
||||
for(ii=4; ii<argc; ii++){
|
||||
if( argv[ii][0]=='+' ){
|
||||
const char *zArg = argv[ii];
|
||||
if( zArg[0]=='+' ){
|
||||
pRtree->nAux++;
|
||||
sqlite3_str_appendf(pSql, ",%s", argv[ii]+1);
|
||||
sqlite3_str_appendf(pSql, ",%.*s", rtreeTokenLength(zArg+1), zArg+1);
|
||||
}else if( pRtree->nAux>0 ){
|
||||
break;
|
||||
}else{
|
||||
pRtree->nDim2++;
|
||||
sqlite3_str_appendf(pSql, ",%s", argv[ii]);
|
||||
sqlite3_str_appendf(pSql, ",%.*s NUM", rtreeTokenLength(zArg), zArg);
|
||||
}
|
||||
}
|
||||
sqlite3_str_appendf(pSql, ");");
|
||||
|
||||
@ -112,6 +112,9 @@ for {set nCol 1} {$nCol<[llength $cols]} {incr nCol} {
|
||||
|
||||
catchsql { DROP TABLE t1 }
|
||||
}
|
||||
do_catchsql_test rtree-1.3.1000 {
|
||||
CREATE VIRTUAL TABLE t1000 USING rtree;
|
||||
} {1 {Too few columns for an rtree table}}
|
||||
|
||||
# Like execsql except display output as integer where that can be
|
||||
# done without loss of information.
|
||||
@ -374,13 +377,43 @@ do_test rtree-8.1.1 {
|
||||
INSERT INTO t6 VALUES(2, 4, 6);
|
||||
}
|
||||
} {}
|
||||
do_test rtree-8.1.2 { execsql { SELECT ii FROM t6 WHERE x1>2 } } {1 2}
|
||||
do_test rtree-8.1.3 { execsql { SELECT ii FROM t6 WHERE x1>3 } } {2}
|
||||
do_test rtree-8.1.4 { execsql { SELECT ii FROM t6 WHERE x1>4 } } {}
|
||||
do_test rtree-8.1.5 { execsql { SELECT ii FROM t6 WHERE x1>5 } } {}
|
||||
do_test rtree-8.1.6 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
|
||||
do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
|
||||
do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
|
||||
do_test rtree-8.1.2 { execsql { SELECT ii FROM t6 WHERE x1>2 } } {1 2}
|
||||
do_test rtree-8.1.3 { execsql { SELECT ii FROM t6 WHERE x1>3 } } {2}
|
||||
do_test rtree-8.1.4 { execsql { SELECT ii FROM t6 WHERE x1>4 } } {}
|
||||
do_test rtree-8.1.5 { execsql { SELECT ii FROM t6 WHERE x1>5 } } {}
|
||||
do_test rtree-8.1.6 { execsql { SELECT ii FROM t6 WHERE x1>''} } {}
|
||||
do_test rtree-8.1.7 { execsql { SELECT ii FROM t6 WHERE x1>null}} {}
|
||||
do_test rtree-8.1.8 { execsql { SELECT ii FROM t6 WHERE x1>'2'} } {1 2}
|
||||
do_test rtree-8.1.9 { execsql { SELECT ii FROM t6 WHERE x1>'3'} } {2}
|
||||
do_test rtree-8.2.2 { execsql { SELECT ii FROM t6 WHERE x1>=2 } } {1 2}
|
||||
do_test rtree-8.2.3 { execsql { SELECT ii FROM t6 WHERE x1>=3 } } {1 2}
|
||||
do_test rtree-8.2.4 { execsql { SELECT ii FROM t6 WHERE x1>=4 } } {2}
|
||||
do_test rtree-8.2.5 { execsql { SELECT ii FROM t6 WHERE x1>=5 } } {}
|
||||
do_test rtree-8.2.6 { execsql { SELECT ii FROM t6 WHERE x1>=''} } {}
|
||||
do_test rtree-8.2.7 { execsql { SELECT ii FROM t6 WHERE x1>=null}} {}
|
||||
do_test rtree-8.2.8 { execsql { SELECT ii FROM t6 WHERE x1>='4'} } {2}
|
||||
do_test rtree-8.2.9 { execsql { SELECT ii FROM t6 WHERE x1>='5'} } {}
|
||||
do_test rtree-8.3.2 { execsql { SELECT ii FROM t6 WHERE x1<2 } } {}
|
||||
do_test rtree-8.3.3 { execsql { SELECT ii FROM t6 WHERE x1<3 } } {}
|
||||
do_test rtree-8.3.4 { execsql { SELECT ii FROM t6 WHERE x1<4 } } {1}
|
||||
do_test rtree-8.3.5 { execsql { SELECT ii FROM t6 WHERE x1<5 } } {1 2}
|
||||
do_test rtree-8.3.6 { execsql { SELECT ii FROM t6 WHERE x1<''} } {1 2}
|
||||
do_test rtree-8.3.7 { execsql { SELECT ii FROM t6 WHERE x1<null}} {}
|
||||
do_test rtree-8.3.8 { execsql { SELECT ii FROM t6 WHERE x1<'3'} } {}
|
||||
do_test rtree-8.3.9 { execsql { SELECT ii FROM t6 WHERE x1<'4'} } {1}
|
||||
do_test rtree-8.4.2 { execsql { SELECT ii FROM t6 WHERE x1<=2 } } {}
|
||||
do_test rtree-8.4.3 { execsql { SELECT ii FROM t6 WHERE x1<=3 } } {1}
|
||||
do_test rtree-8.4.4 { execsql { SELECT ii FROM t6 WHERE x1<=4 } } {1 2}
|
||||
do_test rtree-8.4.5 { execsql { SELECT ii FROM t6 WHERE x1<=5 } } {1 2}
|
||||
do_test rtree-8.4.6 { execsql { SELECT ii FROM t6 WHERE x1<=''} } {1 2}
|
||||
do_test rtree-8.4.7 { execsql { SELECT ii FROM t6 WHERE x1<=null}} {}
|
||||
do_test rtree-8.5.2 { execsql { SELECT ii FROM t6 WHERE x1=2 } } {}
|
||||
do_test rtree-8.5.3 { execsql { SELECT ii FROM t6 WHERE x1=3 } } {1}
|
||||
do_test rtree-8.5.4 { execsql { SELECT ii FROM t6 WHERE x1=4 } } {2}
|
||||
do_test rtree-8.5.5 { execsql { SELECT ii FROM t6 WHERE x1=5 } } {}
|
||||
do_test rtree-8.5.6 { execsql { SELECT ii FROM t6 WHERE x1=''} } {}
|
||||
do_test rtree-8.5.7 { execsql { SELECT ii FROM t6 WHERE x1=null}} {}
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test cases rtree-9.*
|
||||
@ -578,15 +611,29 @@ do_execsql_test 14.5 {
|
||||
1 0.0 0.0
|
||||
2 52.0 81.0
|
||||
}
|
||||
do_execsql_test 14.6 {
|
||||
INSERT INTO t10 VALUES(0,10,20);
|
||||
SELECT * FROM t10 WHERE ii=NULL;
|
||||
} {}
|
||||
do_execsql_test 14.7 {
|
||||
SELECT * FROM t10 WHERE ii='xyz';
|
||||
} {}
|
||||
do_execsql_test 14.8 {
|
||||
SELECT * FROM t10 WHERE ii='0.0';
|
||||
} {0 10.0 20.0}
|
||||
do_execsql_test 14.9 {
|
||||
SELECT * FROM t10 WHERE ii=0.0;
|
||||
} {0 10.0 20.0}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
|
||||
do_execsql_test 14.104 {
|
||||
DROP TABLE t10;
|
||||
CREATE VIRTUAL TABLE t10 USING rtree_i32(ii, x1, x2);
|
||||
INSERT INTO t10 VALUES(1, 'one', 'two');
|
||||
INSERT INTO t10 VALUES(2, '52xyz', '81...');
|
||||
INSERT INTO t10 VALUES(3, 42.3, 49.9);
|
||||
}
|
||||
do_execsql_test 14.5 {
|
||||
do_execsql_test 14.105 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 0 0
|
||||
@ -661,5 +708,14 @@ do_execsql_test 17.2 {
|
||||
REINDEX;
|
||||
} {}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 18.0 {
|
||||
CREATE VIRTUAL TABLE rt0 USING rtree(c0, c1, c2);
|
||||
INSERT INTO rt0(c0,c1,c2) VALUES(9,2,3);
|
||||
SELECT c0 FROM rt0 WHERE rt0.c1 > '-1';
|
||||
SELECT rt0.c1 > '-1' FROM rt0;
|
||||
} {9 1}
|
||||
|
||||
|
||||
expand_all_sql db
|
||||
finish_test
|
||||
|
||||
@ -33,12 +33,13 @@ if {[info exists G(isquick)] && $G(isquick)} {
|
||||
}
|
||||
|
||||
foreach module {rtree_i32 rtree} {
|
||||
if {$module=="rtree_i32"} {set etype INT} {set etype REAL}
|
||||
for {set nDim 1} {$nDim <= 5} {incr nDim} {
|
||||
|
||||
do_test rtree2-$module.$nDim.1 {
|
||||
set cols [list]
|
||||
foreach c [list c0 c1 c2 c3 c4 c5 c6 c7 c8 c9] {
|
||||
lappend cols "$c REAL"
|
||||
lappend cols "$c $etype"
|
||||
}
|
||||
set cols [join [lrange $cols 0 [expr {$nDim*2-1}]] ", "]
|
||||
execsql "
|
||||
|
||||
@ -177,7 +177,7 @@ do_execsql_test 4.3 {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t1(x PRIMARY KEY, y);
|
||||
CREATE TABLE t1(x INT PRIMARY KEY, y);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, +d1);
|
||||
|
||||
INSERT INTO t1(x) VALUES(1);
|
||||
|
||||
@ -43,10 +43,33 @@ do_execsql_test rtreeH-101 {
|
||||
do_execsql_test rtreeH-102 {
|
||||
SELECT * FROM t1 WHERE rowid=5;
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102b {
|
||||
SELECT * FROM t1 WHERE rowid=5.0;
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102c {
|
||||
SELECT * FROM t1 WHERE rowid='5';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102d {
|
||||
SELECT * FROM t1 WHERE rowid='0005';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-102e {
|
||||
SELECT * FROM t1 WHERE rowid='+5.0e+0';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-103 {
|
||||
SELECT * FROM t1 WHERE label='center';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
|
||||
do_execsql_test rtreeH-104 {
|
||||
SELECT * FROM t1 WHERE rowid='+5.0e+0x';
|
||||
} {}
|
||||
do_execsql_test rtreeH-105 {
|
||||
SELECT * FROM t1 WHERE rowid=x'35';
|
||||
} {}
|
||||
do_execsql_test rtreeH-106 {
|
||||
SELECT * FROM t1 WHERE rowid=null;
|
||||
} {}
|
||||
|
||||
|
||||
do_rtree_integrity_test rtreeH-110 t1
|
||||
|
||||
do_execsql_test rtreeH-120 {
|
||||
|
||||
74
ext/rtree/rtreeI.test
Normal file
74
ext/rtree/rtreeI.test
Normal file
@ -0,0 +1,74 @@
|
||||
# 2019-12-05
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Additional test cases
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
# The following is a test of rowvalue handling on virtual tables that
|
||||
# deal with inequalities and that set the OMIT flag on terms of the
|
||||
# WHERE clause. This is not specific to rtree. We just use rtree because
|
||||
# it is a convenient test platform since it has all the right
|
||||
# characteristics.
|
||||
#
|
||||
do_execsql_test rtreeI-1.10 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
CREATE VIRTUAL TABLE t2 USING rtree(id,x0,x1);
|
||||
INSERT INTO t2(id,x0,x1) VALUES(1,2,3);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.20 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)>(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.21 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0.1)>(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.22 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)>=(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.23 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)<=(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.24 {
|
||||
SELECT 123 FROM t1, t2 WHERE (a,0)<(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.30 {
|
||||
SELECT 123 FROM t1, t2 WHERE (x0,0)<(a,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.31 {
|
||||
SELECT 123 FROM t1, t2 WHERE (x0,0)<(a,0.1);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.40 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0)>(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.41 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0.5)>(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.42 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0)>=(x0,0);
|
||||
} {123}
|
||||
do_execsql_test rtreeI-1.43 {
|
||||
SELECT 123 FROM t1, t2 WHERE x1<5 AND id<99 AND (a,0)<(x0,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.50 {
|
||||
SELECT 123 FROM t1, t2 WHERE 5>x1 AND 99>id AND (x0,0)<(a,0);
|
||||
} {}
|
||||
do_execsql_test rtreeI-1.51 {
|
||||
SELECT 123 FROM t1, t2 WHERE 5>x1 AND 99>id AND (x0,0)<(a,0.5);
|
||||
} {123}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@ -200,7 +200,7 @@ int sqlite3session_attach(
|
||||
** The second argument (xFilter) is the "filter callback". For changes to rows
|
||||
** in tables that are not attached to the Session object, the filter is called
|
||||
** to determine whether changes to the table's rows should be tracked or not.
|
||||
** If xFilter returns 0, changes is not tracked. Note that once a table is
|
||||
** If xFilter returns 0, changes are not tracked. Note that once a table is
|
||||
** attached, xFilter will not be called again.
|
||||
*/
|
||||
void sqlite3session_table_filter(
|
||||
@ -374,7 +374,7 @@ int sqlite3session_changeset(
|
||||
** It an error if database zFrom does not exist or does not contain the
|
||||
** required compatible table.
|
||||
**
|
||||
** If the operation successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||
** may be set to point to a buffer containing an English language error
|
||||
** message. It is the responsibility of the caller to free this buffer using
|
||||
@ -511,7 +511,7 @@ int sqlite3changeset_start_v2(
|
||||
** CAPI3REF: Advance A Changeset Iterator
|
||||
** METHOD: sqlite3_changeset_iter
|
||||
**
|
||||
** This function may only be used with iterators created by function
|
||||
** This function may only be used with iterators created by the function
|
||||
** [sqlite3changeset_start()]. If it is called on an iterator passed to
|
||||
** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE
|
||||
** is returned and the call has no effect.
|
||||
@ -927,8 +927,8 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
** case, this function fails with SQLITE_SCHEMA. If the input changeset
|
||||
** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
|
||||
** returned. Or, if an out-of-memory condition occurs during processing, this
|
||||
** function returns SQLITE_NOMEM. In all cases, if an error occurs the
|
||||
** final contents of the changegroup is undefined.
|
||||
** function returns SQLITE_NOMEM. In all cases, if an error occurs the state
|
||||
** of the final contents of the changegroup is undefined.
|
||||
**
|
||||
** If no error occurs, SQLITE_OK is returned.
|
||||
*/
|
||||
@ -1103,7 +1103,7 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
**
|
||||
** It is safe to execute SQL statements, including those that write to the
|
||||
** table that the callback related to, from within the xConflict callback.
|
||||
** This can be used to further customize the applications conflict
|
||||
** This can be used to further customize the application's conflict
|
||||
** resolution strategy.
|
||||
**
|
||||
** All changes made by these functions are enclosed in a savepoint transaction.
|
||||
@ -1413,7 +1413,7 @@ int sqlite3rebaser_configure(
|
||||
**
|
||||
** Argument pIn must point to a buffer containing a changeset nIn bytes
|
||||
** in size. This function allocates and populates a buffer with a copy
|
||||
** of the changeset rebased rebased according to the configuration of the
|
||||
** of the changeset rebased according to the configuration of the
|
||||
** rebaser object passed as the first argument. If successful, (*ppOut)
|
||||
** is set to point to the new buffer containing the rebased changeset and
|
||||
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
|
||||
|
||||
@ -40,7 +40,7 @@ static sqlite3_stmt *sqlite3UserAuthPrepare(
|
||||
char *zSql;
|
||||
int rc;
|
||||
va_list ap;
|
||||
int savedFlags = db->flags;
|
||||
u64 savedFlags = db->flags;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
|
||||
6
main.mk
6
main.mk
@ -934,10 +934,6 @@ fuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-
|
||||
./fuzzcheck$(EXE) $(FUZZDATA)
|
||||
./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
|
||||
|
||||
fastfuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db
|
||||
./fuzzcheck$(EXE) --limit-mem 100M $(FUZZDATA)
|
||||
./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
|
||||
|
||||
valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db
|
||||
valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
|
||||
valgrind ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
|
||||
@ -955,7 +951,7 @@ quicktest: ./testfixture$(EXE)
|
||||
|
||||
# The default test case. Runs most of the faster standard TCL tests,
|
||||
# and fuzz tests, and sqlite3_analyzer and sqldiff tests.
|
||||
test: fastfuzztest sourcetest $(TESTPROGS) tcltest
|
||||
test: fuzztest sourcetest $(TESTPROGS) tcltest
|
||||
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
# because valgrind is so much slower than a native machine.
|
||||
|
||||
@ -1 +1 @@
|
||||
18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b
|
||||
f6affdd41608946fcfcea914ece149038a8b25a62bbe719ed2561c649b86d824
|
||||
|
||||
183
src/alter.c
183
src/alter.c
@ -31,9 +31,8 @@
|
||||
static int isAlterableTable(Parse *pParse, Table *pTab){
|
||||
if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|| ( (pTab->tabFlags & TF_Shadow)
|
||||
&& (pParse->db->flags & SQLITE_Defensive)
|
||||
&& pParse->db->nVdbeExec==0
|
||||
|| ( (pTab->tabFlags & TF_Shadow)!=0
|
||||
&& sqlite3ReadOnlyShadowTables(pParse->db)
|
||||
)
|
||||
#endif
|
||||
){
|
||||
@ -298,14 +297,6 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
}
|
||||
#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.
|
||||
*/
|
||||
assert( pDflt==0 || pDflt->op==TK_SPAN );
|
||||
if( pDflt && pDflt->pLeft->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
|
||||
@ -319,35 +310,49 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
|
||||
return;
|
||||
}
|
||||
if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"Cannot add a REFERENCES column with non-NULL default value");
|
||||
return;
|
||||
}
|
||||
if( pCol->notNull && !pDflt ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"Cannot add a NOT NULL column with default value NULL");
|
||||
if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
|
||||
/* 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.
|
||||
*/
|
||||
assert( pDflt==0 || pDflt->op==TK_SPAN );
|
||||
if( pDflt && pDflt->pLeft->op==TK_NULL ){
|
||||
pDflt = 0;
|
||||
}
|
||||
if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"Cannot add a REFERENCES column with non-NULL default value");
|
||||
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 = 0;
|
||||
int rc;
|
||||
rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( db->mallocFailed == 1 );
|
||||
return;
|
||||
}
|
||||
if( !pVal ){
|
||||
sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default");
|
||||
return;
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
}
|
||||
}else if( pCol->colFlags & COLFLAG_STORED ){
|
||||
sqlite3ErrorMsg(pParse, "cannot add a STORED column");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure the default expression is something that sqlite3ValueFromExpr()
|
||||
** can handle (i.e. not CURRENT_TIME etc.)
|
||||
*/
|
||||
if( pDflt ){
|
||||
sqlite3_value *pVal = 0;
|
||||
int rc;
|
||||
rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( db->mallocFailed == 1 );
|
||||
return;
|
||||
}
|
||||
if( !pVal ){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default");
|
||||
return;
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
}
|
||||
|
||||
/* Modify the CREATE TABLE statement. */
|
||||
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
|
||||
@ -692,12 +697,14 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
|
||||
RenameToken *pNew;
|
||||
assert( pPtr || pParse->db->mallocFailed );
|
||||
renameTokenCheckAll(pParse, pPtr);
|
||||
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
|
||||
if( pNew ){
|
||||
pNew->p = pPtr;
|
||||
pNew->t = *pToken;
|
||||
pNew->pNext = pParse->pRename;
|
||||
pParse->pRename = pNew;
|
||||
if( pParse->eParseMode!=PARSE_MODE_UNMAP ){
|
||||
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
|
||||
if( pNew ){
|
||||
pNew->p = pPtr;
|
||||
pNew->t = *pToken;
|
||||
pNew->pNext = pParse->pRename;
|
||||
pParse->pRename = pNew;
|
||||
}
|
||||
}
|
||||
|
||||
return pPtr;
|
||||
@ -728,17 +735,39 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterate through the Select objects that are part of WITH clauses attached
|
||||
** to select statement pSelect.
|
||||
*/
|
||||
static void renameWalkWith(Walker *pWalker, Select *pSelect){
|
||||
With *pWith = pSelect->pWith;
|
||||
if( pWith ){
|
||||
int i;
|
||||
for(i=0; i<pWith->nCte; i++){
|
||||
Select *p = pWith->a[i].pSelect;
|
||||
NameContext sNC;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pWalker->pParse;
|
||||
sqlite3SelectPrep(sNC.pParse, p, &sNC);
|
||||
sqlite3WalkSelect(pWalker, p);
|
||||
sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Walker callback used by sqlite3RenameExprUnmap().
|
||||
*/
|
||||
static int renameUnmapSelectCb(Walker *pWalker, Select *p){
|
||||
Parse *pParse = pWalker->pParse;
|
||||
int i;
|
||||
if( pParse->nErr ) return WRC_Abort;
|
||||
if( NEVER(p->selFlags & SF_View) ) return WRC_Prune;
|
||||
if( ALWAYS(p->pEList) ){
|
||||
ExprList *pList = p->pEList;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( pList->a[i].zName ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zName);
|
||||
if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -746,8 +775,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
|
||||
SrcList *pSrc = p->pSrc;
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
|
||||
if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
|
||||
}
|
||||
}
|
||||
|
||||
renameWalkWith(pWalker, p);
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
@ -755,12 +787,15 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
|
||||
** Remove all nodes that are part of expression pExpr from the rename list.
|
||||
*/
|
||||
void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
|
||||
u8 eMode = pParse->eParseMode;
|
||||
Walker sWalker;
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
sWalker.pParse = pParse;
|
||||
sWalker.xExprCallback = renameUnmapExprCb;
|
||||
sWalker.xSelectCallback = renameUnmapSelectCb;
|
||||
pParse->eParseMode = PARSE_MODE_UNMAP;
|
||||
sqlite3WalkExpr(&sWalker, pExpr);
|
||||
pParse->eParseMode = eMode;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -776,7 +811,9 @@ void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){
|
||||
sWalker.xExprCallback = renameUnmapExprCb;
|
||||
sqlite3WalkExprList(&sWalker, pEList);
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zName);
|
||||
if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -814,30 +851,13 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterate through the Select objects that are part of WITH clauses attached
|
||||
** to select statement pSelect.
|
||||
*/
|
||||
static void renameWalkWith(Walker *pWalker, Select *pSelect){
|
||||
if( pSelect->pWith ){
|
||||
int i;
|
||||
for(i=0; i<pSelect->pWith->nCte; i++){
|
||||
Select *p = pSelect->pWith->a[i].pSelect;
|
||||
NameContext sNC;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pWalker->pParse;
|
||||
sqlite3SelectPrep(sNC.pParse, p, &sNC);
|
||||
sqlite3WalkSelect(pWalker, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a Walker select callback. It does nothing. It is only required
|
||||
** because without a dummy callback, sqlite3WalkExpr() and similar do not
|
||||
** descend into sub-select statements.
|
||||
*/
|
||||
static int renameColumnSelectCb(Walker *pWalker, Select *p){
|
||||
if( p->selFlags & SF_View ) return WRC_Prune;
|
||||
renameWalkWith(pWalker, p);
|
||||
return WRC_Continue;
|
||||
}
|
||||
@ -931,8 +951,11 @@ static void renameColumnElistNames(
|
||||
if( pEList ){
|
||||
int i;
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
char *zName = pEList->a[i].zName;
|
||||
if( 0==sqlite3_stricmp(zName, zOld) ){
|
||||
char *zName = pEList->a[i].zEName;
|
||||
if( ALWAYS(pEList->a[i].eEName==ENAME_NAME)
|
||||
&& ALWAYS(zName!=0)
|
||||
&& 0==sqlite3_stricmp(zName, zOld)
|
||||
){
|
||||
renameTokenFind(pParse, pCtx, (void*)zName);
|
||||
}
|
||||
}
|
||||
@ -968,7 +991,6 @@ static void renameColumnIdlistNames(
|
||||
static int renameParseSql(
|
||||
Parse *p, /* Memory to use for Parse object */
|
||||
const char *zDb, /* Name of schema SQL belongs to */
|
||||
int bTable, /* 1 -> RENAME TABLE, 0 -> RENAME COLUMN */
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zSql, /* SQL to parse */
|
||||
int bTemp /* True if SQL is from temp schema */
|
||||
@ -982,7 +1004,7 @@ static int renameParseSql(
|
||||
** occurs and the parse does not result in a new table, index or
|
||||
** trigger object, the database must be corrupt. */
|
||||
memset(p, 0, sizeof(Parse));
|
||||
p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN);
|
||||
p->eParseMode = PARSE_MODE_RENAME;
|
||||
p->db = db;
|
||||
p->nQueryLoop = 1;
|
||||
rc = sqlite3RunParser(p, zSql, &zErr);
|
||||
@ -1289,7 +1311,7 @@ static void renameColumnFunc(
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
db->xAuth = 0;
|
||||
#endif
|
||||
rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp);
|
||||
rc = renameParseSql(&sParse, zDb, db, zSql, bTemp);
|
||||
|
||||
/* Find tokens that need to be replaced. */
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
@ -1303,8 +1325,9 @@ static void renameColumnFunc(
|
||||
if( sParse.pNewTable ){
|
||||
Select *pSelect = sParse.pNewTable->pSelect;
|
||||
if( pSelect ){
|
||||
pSelect->selFlags &= ~SF_View;
|
||||
sParse.rc = SQLITE_OK;
|
||||
sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
|
||||
sqlite3SelectPrep(&sParse, pSelect, 0);
|
||||
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3WalkSelect(&sWalker, pSelect);
|
||||
@ -1331,6 +1354,11 @@ static void renameColumnFunc(
|
||||
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
for(i=0; i<sParse.pNewTable->nCol; i++){
|
||||
sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
|
||||
for(i=0; i<pFKey->nCol; i++){
|
||||
@ -1416,6 +1444,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
|
||||
int i;
|
||||
RenameCtx *p = pWalker->u.pRename;
|
||||
SrcList *pSrc = pSelect->pSrc;
|
||||
if( pSelect->selFlags & SF_View ) return WRC_Prune;
|
||||
if( pSrc==0 ){
|
||||
assert( pWalker->pParse->db->mallocFailed );
|
||||
return WRC_Abort;
|
||||
@ -1486,7 +1515,7 @@ static void renameTableFunc(
|
||||
sWalker.xSelectCallback = renameTableSelectCb;
|
||||
sWalker.u.pRename = &sCtx;
|
||||
|
||||
rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
|
||||
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
int isLegacy = (db->flags & SQLITE_LegacyAlter);
|
||||
@ -1495,13 +1524,19 @@ static void renameTableFunc(
|
||||
|
||||
if( pTab->pSelect ){
|
||||
if( isLegacy==0 ){
|
||||
Select *pSelect = pTab->pSelect;
|
||||
NameContext sNC;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = &sParse;
|
||||
|
||||
assert( pSelect->selFlags & SF_View );
|
||||
pSelect->selFlags &= ~SF_View;
|
||||
sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
|
||||
if( sParse.nErr ) rc = sParse.rc;
|
||||
sqlite3WalkSelect(&sWalker, pTab->pSelect);
|
||||
if( sParse.nErr ){
|
||||
rc = sParse.rc;
|
||||
}else{
|
||||
sqlite3WalkSelect(&sWalker, pTab->pSelect);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* Modify any FK definitions to point to the new table. */
|
||||
@ -1622,7 +1657,7 @@ static void renameTableTest(
|
||||
if( zDb && zInput ){
|
||||
int rc;
|
||||
Parse sParse;
|
||||
rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
|
||||
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
|
||||
NameContext sNC;
|
||||
|
||||
@ -916,18 +916,17 @@ static const FuncDef statGetFuncdef = {
|
||||
{0}
|
||||
};
|
||||
|
||||
static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
|
||||
assert( regOut!=regStat4 && regOut!=regStat4+1 );
|
||||
static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1);
|
||||
#elif SQLITE_DEBUG
|
||||
assert( iParam==STAT_GET_STAT1 );
|
||||
#else
|
||||
UNUSED_PARAMETER( iParam );
|
||||
#endif
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
|
||||
(char*)&statGetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 1 + IsStat4);
|
||||
assert( regOut!=regStat4 && regOut!=regStat4+1 );
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4,
|
||||
&statGetFuncdef, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1095,9 +1094,8 @@ static void analyzeOneTable(
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
|
||||
(char*)&statInitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat4);
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4,
|
||||
&statInitFuncdef, 0);
|
||||
|
||||
/* Implementation of the following:
|
||||
**
|
||||
@ -1182,7 +1180,7 @@ static void analyzeOneTable(
|
||||
int j, k, regKey;
|
||||
regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
|
||||
k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
|
||||
assert( k>=0 && k<pIdx->nColumn );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
|
||||
VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
|
||||
@ -1192,13 +1190,12 @@ static void analyzeOneTable(
|
||||
}
|
||||
#endif
|
||||
assert( regChng==(regStat4+1) );
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
|
||||
(char*)&statPushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat4);
|
||||
sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4,
|
||||
&statPushFuncdef, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
|
||||
|
||||
/* Add the entry to the stat1 table. */
|
||||
callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
|
||||
callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1);
|
||||
assert( "BBB"[0]==SQLITE_AFF_TEXT );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
@ -1224,12 +1221,12 @@ static void analyzeOneTable(
|
||||
pParse->nMem = MAX(pParse->nMem, regCol+nCol);
|
||||
|
||||
addrNext = sqlite3VdbeCurrentAddr(v);
|
||||
callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid);
|
||||
callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid);
|
||||
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
|
||||
VdbeCoverage(v);
|
||||
callStatGet(v, regStat4, STAT_GET_NEQ, regEq);
|
||||
callStatGet(v, regStat4, STAT_GET_NLT, regLt);
|
||||
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
|
||||
callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq);
|
||||
callStatGet(pParse, regStat4, STAT_GET_NLT, regLt);
|
||||
callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt);
|
||||
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
|
||||
VdbeCoverage(v);
|
||||
for(i=0; i<nCol; i++){
|
||||
@ -1854,9 +1851,9 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
/* Load the statistics from the sqlite_stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( rc==SQLITE_OK ){
|
||||
db->lookaside.bDisable++;
|
||||
DisableLookaside;
|
||||
rc = loadStat4(db, sInfo.zDatabase);
|
||||
db->lookaside.bDisable--;
|
||||
EnableLookaside;
|
||||
}
|
||||
for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
|
||||
14
src/attach.c
14
src/attach.c
@ -401,11 +401,8 @@ static void codeAttach(
|
||||
|
||||
assert( v || db->mallocFailed );
|
||||
if( v ){
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regArgs+3-pFunc->nArg, regArgs+3,
|
||||
(char *)pFunc, P4_FUNCDEF);
|
||||
assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg );
|
||||
sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg));
|
||||
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regArgs+3-pFunc->nArg, regArgs+3,
|
||||
pFunc->nArg, pFunc, 0);
|
||||
/* 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).
|
||||
@ -480,7 +477,7 @@ void sqlite3FixInit(
|
||||
pFix->pSchema = db->aDb[iDb].pSchema;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
pFix->bVarOnly = (iDb==1);
|
||||
pFix->bTemp = (iDb==1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -508,7 +505,7 @@ int sqlite3FixSrcList(
|
||||
if( NEVER(pList==0) ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pFix->bVarOnly==0 ){
|
||||
if( pFix->bTemp==0 ){
|
||||
if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
@ -518,6 +515,7 @@ int sqlite3FixSrcList(
|
||||
sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
pItem->fg.fromDDL = 1;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
|
||||
@ -573,7 +571,7 @@ int sqlite3FixExpr(
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
ExprSetProperty(pExpr, EP_Indirect);
|
||||
if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
|
||||
if( pExpr->op==TK_VARIABLE ){
|
||||
if( pFix->pParse->db->init.busy ){
|
||||
pExpr->op = TK_NULL;
|
||||
|
||||
135
src/btree.c
135
src/btree.c
@ -699,6 +699,9 @@ static int saveCursorPosition(BtCursor *pCur){
|
||||
assert( 0==pCur->pKey );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
|
||||
if( pCur->curFlags & BTCF_Pinned ){
|
||||
return SQLITE_CONSTRAINT_PINNED;
|
||||
}
|
||||
if( pCur->eState==CURSOR_SKIPNEXT ){
|
||||
pCur->eState = CURSOR_VALID;
|
||||
}else{
|
||||
@ -1446,7 +1449,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
|
||||
int sz2 = 0;
|
||||
int sz = get2byte(&data[iFree+2]);
|
||||
int top = get2byte(&data[hdr+5]);
|
||||
if( top>=iFree ){
|
||||
if( NEVER(top>=iFree) ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
if( iFree2 ){
|
||||
@ -1455,7 +1458,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
|
||||
if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
|
||||
sz += sz2;
|
||||
}else if( iFree+sz>usableSize ){
|
||||
}else if( NEVER(iFree+sz>usableSize) ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
|
||||
@ -1647,8 +1650,10 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
|
||||
u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
|
||||
if( pSpace ){
|
||||
int g2;
|
||||
assert( pSpace+nByte<=data+pPage->pBt->usableSize );
|
||||
if( (*pIdx = (int)(pSpace-data))<=gap ){
|
||||
*pIdx = g2 = (int)(pSpace-data);
|
||||
if( NEVER(g2<=gap) ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}else{
|
||||
return SQLITE_OK;
|
||||
@ -1726,12 +1731,12 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
}else{
|
||||
while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
|
||||
if( iFreeBlk<iPtr+4 ){
|
||||
if( iFreeBlk==0 ) break;
|
||||
if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
iPtr = iFreeBlk;
|
||||
}
|
||||
if( iFreeBlk>pPage->pBt->usableSize-4 ){
|
||||
if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
assert( iFreeBlk>iPtr || iFreeBlk==0 );
|
||||
@ -1746,7 +1751,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
nFrag = iFreeBlk - iEnd;
|
||||
if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
|
||||
if( iEnd > pPage->pBt->usableSize ){
|
||||
if( NEVER(iEnd > pPage->pBt->usableSize) ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
iSize = iEnd - iStart;
|
||||
@ -1774,7 +1779,8 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
/* The new freeblock is at the beginning of the cell content area,
|
||||
** so just extend the cell content area rather than create another
|
||||
** freelist entry */
|
||||
if( iStart<x || iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
if( NEVER(iPtr!=hdr+1) ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
put2byte(&data[hdr+1], iFreeBlk);
|
||||
put2byte(&data[hdr+5], iEnd);
|
||||
}else{
|
||||
@ -1894,7 +1900,7 @@ static int btreeComputeFreeSpace(MemPage *pPage){
|
||||
nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */
|
||||
if( pc>0 ){
|
||||
u32 next, size;
|
||||
if( pc<iCellFirst ){
|
||||
if( pc<top ){
|
||||
/* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will
|
||||
** always be at least one cell before the first freeblock.
|
||||
*/
|
||||
@ -2131,12 +2137,12 @@ static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){
|
||||
** error, return ((unsigned int)-1).
|
||||
*/
|
||||
static Pgno btreePagecount(BtShared *pBt){
|
||||
assert( (pBt->nPage & 0x80000000)==0 || CORRUPT_DB );
|
||||
return pBt->nPage;
|
||||
}
|
||||
u32 sqlite3BtreeLastPage(Btree *p){
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( ((p->pBt->nPage)&0x80000000)==0 );
|
||||
return btreePagecount(p->pBt);
|
||||
return btreePagecount(p->pBt) & 0x7fffffff;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2403,9 +2409,13 @@ int sqlite3BtreeOpen(
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename,
|
||||
nFullPathname, zFullPathname);
|
||||
if( rc ){
|
||||
sqlite3_free(zFullPathname);
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
if( rc==SQLITE_OK_SYMLINK ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
sqlite3_free(zFullPathname);
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if SQLITE_THREADSAFE
|
||||
@ -4365,8 +4375,9 @@ static int btreeCursor(
|
||||
/* The following assert statements verify that if this is a sharable
|
||||
** b-tree database, the connection is holding the required table locks,
|
||||
** and that no other connection has any open cursor that conflicts with
|
||||
** this lock. */
|
||||
assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1)) );
|
||||
** this lock. The iTable<1 term disables the check for corrupt schemas. */
|
||||
assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1))
|
||||
|| iTable<1 );
|
||||
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
|
||||
|
||||
/* Assert that the caller has opened the required transaction. */
|
||||
@ -4379,9 +4390,13 @@ static int btreeCursor(
|
||||
allocateTempSpace(pBt);
|
||||
if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
if( iTable==1 && btreePagecount(pBt)==0 ){
|
||||
assert( wrFlag==0 );
|
||||
iTable = 0;
|
||||
if( iTable<=1 ){
|
||||
if( iTable<1 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}else if( btreePagecount(pBt)==0 ){
|
||||
assert( wrFlag==0 );
|
||||
iTable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that no other errors can occur, finish filling in the BtCursor
|
||||
@ -4406,6 +4421,19 @@ static int btreeCursor(
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int btreeCursorWithLock(
|
||||
Btree *p, /* The btree */
|
||||
int iTable, /* Root page of table to open */
|
||||
int wrFlag, /* 1 to write. 0 read-only */
|
||||
struct KeyInfo *pKeyInfo, /* First arg to comparison function */
|
||||
BtCursor *pCur /* Space for new cursor */
|
||||
){
|
||||
int rc;
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
int sqlite3BtreeCursor(
|
||||
Btree *p, /* The btree */
|
||||
int iTable, /* Root page of table to open */
|
||||
@ -4413,15 +4441,11 @@ int sqlite3BtreeCursor(
|
||||
struct KeyInfo *pKeyInfo, /* First arg to xCompare() */
|
||||
BtCursor *pCur /* Write new cursor here */
|
||||
){
|
||||
int rc;
|
||||
if( iTable<1 ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
if( p->sharable ){
|
||||
return btreeCursorWithLock(p, iTable, wrFlag, pKeyInfo, pCur);
|
||||
}else{
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
|
||||
sqlite3BtreeLeave(p);
|
||||
return btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4544,6 +4568,18 @@ i64 sqlite3BtreeIntegerKey(BtCursor *pCur){
|
||||
return pCur->info.nKey;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pin or unpin a cursor.
|
||||
*/
|
||||
void sqlite3BtreeCursorPin(BtCursor *pCur){
|
||||
assert( (pCur->curFlags & BTCF_Pinned)==0 );
|
||||
pCur->curFlags |= BTCF_Pinned;
|
||||
}
|
||||
void sqlite3BtreeCursorUnpin(BtCursor *pCur){
|
||||
assert( (pCur->curFlags & BTCF_Pinned)!=0 );
|
||||
pCur->curFlags &= ~BTCF_Pinned;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
/*
|
||||
** Return the offset into the database file for the start of the
|
||||
@ -5700,8 +5736,11 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){
|
||||
** to be invalid here. This can only occur if a second cursor modifies
|
||||
** the page while cursor pCur is holding a reference to it. Which can
|
||||
** only happen if the database is corrupt in such a way as to link the
|
||||
** page into more than one b-tree structure. */
|
||||
testcase( idx>pPage->nCell );
|
||||
** page into more than one b-tree structure.
|
||||
**
|
||||
** Update 2019-12-23: appears to long longer be possible after the
|
||||
** addition of anotherValidCursor() condition on balance_deeper(). */
|
||||
harmless( idx>pPage->nCell );
|
||||
|
||||
if( idx>=pPage->nCell ){
|
||||
if( !pPage->leaf ){
|
||||
@ -6900,7 +6939,7 @@ static int rebuildPage(
|
||||
|
||||
assert( i<iEnd );
|
||||
j = get2byte(&aData[hdr+5]);
|
||||
if( j>(u32)usableSize ){ j = 0; }
|
||||
if( NEVER(j>(u32)usableSize) ){ j = 0; }
|
||||
memcpy(&pTmp[j], &aData[j], usableSize - j);
|
||||
|
||||
for(k=0; pCArray->ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
|
||||
@ -6926,7 +6965,7 @@ static int rebuildPage(
|
||||
if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT;
|
||||
memcpy(pData, pCell, sz);
|
||||
assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
|
||||
testcase( sz!=pPg->xCellSize(pPg,pCell) );
|
||||
testcase( sz!=pPg->xCellSize(pPg,pCell) )
|
||||
i++;
|
||||
if( i>=iEnd ) break;
|
||||
if( pCArray->ixNx[k]<=i ){
|
||||
@ -8290,6 +8329,30 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid
|
||||
** on the same B-tree as pCur.
|
||||
**
|
||||
** This can if a database is corrupt with two or more SQL tables
|
||||
** pointing to the same b-tree. If an insert occurs on one SQL table
|
||||
** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL
|
||||
** table linked to the same b-tree. If the secondary insert causes a
|
||||
** rebalance, that can change content out from under the cursor on the
|
||||
** first SQL table, violating invariants on the first insert.
|
||||
*/
|
||||
static int anotherValidCursor(BtCursor *pCur){
|
||||
BtCursor *pOther;
|
||||
for(pOther=pCur->pBt->pCursor; pOther; pOther=pOther->pNext){
|
||||
if( pOther!=pCur
|
||||
&& pOther->eState==CURSOR_VALID
|
||||
&& pOther->pPage==pCur->pPage
|
||||
){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The page that pCur currently points to has just been modified in
|
||||
** some way. This function figures out if this modification means the
|
||||
@ -8317,7 +8380,7 @@ static int balance(BtCursor *pCur){
|
||||
if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
|
||||
break;
|
||||
}else if( (iPage = pCur->iPage)==0 ){
|
||||
if( pPage->nOverflow ){
|
||||
if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){
|
||||
/* The root page of the b-tree is overfull. In this case call the
|
||||
** balance_deeper() function to create a new child for the root-page
|
||||
** and copy the current contents of the root-page to it. The
|
||||
@ -8613,7 +8676,6 @@ int sqlite3BtreeInsert(
|
||||
if( flags & BTREE_SAVEPOSITION ){
|
||||
assert( pCur->curFlags & BTCF_ValidNKey );
|
||||
assert( pX->nKey==pCur->info.nKey );
|
||||
assert( pCur->info.nSize!=0 );
|
||||
assert( loc==0 );
|
||||
}
|
||||
#endif
|
||||
@ -8688,7 +8750,9 @@ int sqlite3BtreeInsert(
|
||||
}
|
||||
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
|
||||
assert( pCur->eState==CURSOR_VALID
|
||||
|| (pCur->eState==CURSOR_INVALID && loc)
|
||||
|| CORRUPT_DB );
|
||||
|
||||
pPage = pCur->pPage;
|
||||
assert( pPage->intKey || pX->nKey>=0 );
|
||||
@ -9459,7 +9523,7 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
|
||||
** Otherwise, if an error is encountered (i.e. an IO error or database
|
||||
** corruption) an SQLite error code is returned.
|
||||
*/
|
||||
int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
|
||||
int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){
|
||||
i64 nEntry = 0; /* Value to return in *pnEntry */
|
||||
int rc; /* Return code */
|
||||
|
||||
@ -9472,7 +9536,7 @@ int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
|
||||
/* Unless an error occurs, the following loop runs one iteration for each
|
||||
** page in the B-Tree structure (not including overflow pages).
|
||||
*/
|
||||
while( rc==SQLITE_OK ){
|
||||
while( rc==SQLITE_OK && !db->u1.isInterrupted ){
|
||||
int iIdx; /* Index of child node in parent */
|
||||
MemPage *pPage; /* Current page of the b-tree */
|
||||
|
||||
@ -9598,6 +9662,7 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage){
|
||||
checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
|
||||
return 1;
|
||||
}
|
||||
if( pCheck->db->u1.isInterrupted ) return 1;
|
||||
setPageReferenced(pCheck, iPage);
|
||||
return 0;
|
||||
}
|
||||
@ -10041,6 +10106,7 @@ end_of_check:
|
||||
** returned. If a memory allocation error occurs, NULL is returned.
|
||||
*/
|
||||
char *sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
Btree *p, /* The btree to be checked */
|
||||
int *aRoot, /* An array of root pages numbers for individual trees */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
@ -10058,6 +10124,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
|
||||
VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
|
||||
assert( nRef>=0 );
|
||||
sCheck.db = db;
|
||||
sCheck.pBt = pBt;
|
||||
sCheck.pPager = pBt->pPager;
|
||||
sCheck.nPage = btreePagecount(sCheck.pBt);
|
||||
|
||||
@ -306,6 +306,8 @@ int sqlite3BtreeNext(BtCursor*, int flags);
|
||||
int sqlite3BtreeEof(BtCursor*);
|
||||
int sqlite3BtreePrevious(BtCursor*, int flags);
|
||||
i64 sqlite3BtreeIntegerKey(BtCursor*);
|
||||
void sqlite3BtreeCursorPin(BtCursor*);
|
||||
void sqlite3BtreeCursorUnpin(BtCursor*);
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
i64 sqlite3BtreeOffset(BtCursor*);
|
||||
#endif
|
||||
@ -314,7 +316,7 @@ const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
|
||||
u32 sqlite3BtreePayloadSize(BtCursor*);
|
||||
sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,int*aRoot,int nRoot,int,int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
i64 sqlite3BtreeRowCountEst(BtCursor*);
|
||||
|
||||
@ -335,7 +337,7 @@ int sqlite3BtreeCursorIsValid(BtCursor*);
|
||||
int sqlite3BtreeCursorIsValidNN(BtCursor*);
|
||||
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
int sqlite3BtreeCount(BtCursor *, i64 *);
|
||||
int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
@ -542,6 +542,7 @@ struct BtCursor {
|
||||
#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
|
||||
#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
|
||||
#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */
|
||||
#define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */
|
||||
|
||||
/*
|
||||
** Potential values for BtCursor.eState.
|
||||
@ -685,6 +686,7 @@ struct IntegrityCk {
|
||||
int v1, v2; /* Values for up to two %d fields in zPfx */
|
||||
StrAccum errMsg; /* Accumulate the error message text here */
|
||||
u32 *heap; /* Min-heap used for analyzing cell coverage */
|
||||
sqlite3 *db; /* Database connection running the check */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
386
src/build.c
386
src/build.c
@ -856,13 +856,14 @@ int sqlite3CheckObjectName(
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( pParse->nested==0
|
||||
&& 0==sqlite3StrNICmp(zName, "sqlite_", 7)
|
||||
if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7))
|
||||
|| (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName))
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s",
|
||||
zName);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -877,10 +878,12 @@ Index *sqlite3PrimaryKeyIndex(Table *pTab){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the column of index pIdx that corresponds to table
|
||||
** column iCol. Return -1 if not found.
|
||||
** Convert an table column number into a index column number. That is,
|
||||
** for the column iCol in the table (as defined by the CREATE TABLE statement)
|
||||
** find the (first) offset of that column in index pIdx. Or return -1
|
||||
** if column iCol is not used in index pIdx.
|
||||
*/
|
||||
i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
|
||||
i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){
|
||||
int i;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
if( iCol==pIdx->aiColumn[i] ) return i;
|
||||
@ -888,6 +891,84 @@ i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
/* Convert a storage column number into a table column number.
|
||||
**
|
||||
** The storage column number (0,1,2,....) is the index of the value
|
||||
** as it appears in the record on disk. The true column number
|
||||
** is the index (0,1,2,...) of the column in the CREATE TABLE statement.
|
||||
**
|
||||
** The storage column number is less than the table column number if
|
||||
** and only there are VIRTUAL columns to the left.
|
||||
**
|
||||
** If SQLITE_OMIT_GENERATED_COLUMNS, this routine is a no-op macro.
|
||||
*/
|
||||
i16 sqlite3StorageColumnToTable(Table *pTab, i16 iCol){
|
||||
if( pTab->tabFlags & TF_HasVirtual ){
|
||||
int i;
|
||||
for(i=0; i<=iCol; i++){
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) iCol++;
|
||||
}
|
||||
}
|
||||
return iCol;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
/* Convert a table column number into a storage column number.
|
||||
**
|
||||
** The storage column number (0,1,2,....) is the index of the value
|
||||
** as it appears in the record on disk. Or, if the input column is
|
||||
** the N-th virtual column (zero-based) then the storage number is
|
||||
** the number of non-virtual columns in the table plus N.
|
||||
**
|
||||
** The true column number is the index (0,1,2,...) of the column in
|
||||
** the CREATE TABLE statement.
|
||||
**
|
||||
** If the input column is a VIRTUAL column, then it should not appear
|
||||
** in storage. But the value sometimes is cached in registers that
|
||||
** follow the range of registers used to construct storage. This
|
||||
** avoids computing the same VIRTUAL column multiple times, and provides
|
||||
** values for use by OP_Param opcodes in triggers. Hence, if the
|
||||
** input column is a VIRTUAL table, put it after all the other columns.
|
||||
**
|
||||
** In the following, N means "normal column", S means STORED, and
|
||||
** V means VIRTUAL. Suppose the CREATE TABLE has columns like this:
|
||||
**
|
||||
** CREATE TABLE ex(N,S,V,N,S,V,N,S,V);
|
||||
** -- 0 1 2 3 4 5 6 7 8
|
||||
**
|
||||
** Then the mapping from this function is as follows:
|
||||
**
|
||||
** INPUTS: 0 1 2 3 4 5 6 7 8
|
||||
** OUTPUTS: 0 1 6 2 3 7 4 5 8
|
||||
**
|
||||
** So, in other words, this routine shifts all the virtual columns to
|
||||
** the end.
|
||||
**
|
||||
** If SQLITE_OMIT_GENERATED_COLUMNS then there are no virtual columns and
|
||||
** this routine is a no-op macro. If the pTab does not have any virtual
|
||||
** columns, then this routine is no-op that always return iCol. If iCol
|
||||
** is negative (indicating the ROWID column) then this routine return iCol.
|
||||
*/
|
||||
i16 sqlite3TableColumnToStorage(Table *pTab, i16 iCol){
|
||||
int i;
|
||||
i16 n;
|
||||
assert( iCol<pTab->nCol );
|
||||
if( (pTab->tabFlags & TF_HasVirtual)==0 || iCol<0 ) return iCol;
|
||||
for(i=0, n=0; i<iCol; i++){
|
||||
if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) n++;
|
||||
}
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ){
|
||||
/* iCol is a virtual column itself */
|
||||
return pTab->nNVCol + i - n;
|
||||
}else{
|
||||
/* iCol is a normal or stored column */
|
||||
return n;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Begin constructing a new table representation in memory. This is
|
||||
** the first of several action routines that get called in response
|
||||
@ -1178,6 +1259,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
|
||||
pCol->colFlags |= COLFLAG_HASTYPE;
|
||||
}
|
||||
p->nCol++;
|
||||
p->nNVCol++;
|
||||
pParse->constraintName.n = 0;
|
||||
}
|
||||
|
||||
@ -1322,10 +1404,17 @@ void sqlite3AddDefaultValue(
|
||||
sqlite3 *db = pParse->db;
|
||||
p = pParse->pNewTable;
|
||||
if( p!=0 ){
|
||||
int isInit = db->init.busy && db->init.iDb!=1;
|
||||
pCol = &(p->aCol[p->nCol-1]);
|
||||
if( !sqlite3ExprIsConstantOrFunction(pExpr, db->init.busy) ){
|
||||
if( !sqlite3ExprIsConstantOrFunction(pExpr, isInit) ){
|
||||
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
|
||||
pCol->zName);
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
}else if( pCol->colFlags & COLFLAG_GENERATED ){
|
||||
testcase( pCol->colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pCol->colFlags & COLFLAG_STORED );
|
||||
sqlite3ErrorMsg(pParse, "cannot use DEFAULT on a generated column");
|
||||
#endif
|
||||
}else{
|
||||
/* A copy of pExpr is used instead of the original, as pExpr contains
|
||||
** tokens that point to volatile memory.
|
||||
@ -1371,6 +1460,21 @@ static void sqlite3StringToId(Expr *p){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Tag the given column as being part of the PRIMARY KEY
|
||||
*/
|
||||
static void makeColumnPartOfPrimaryKey(Parse *pParse, Column *pCol){
|
||||
pCol->colFlags |= COLFLAG_PRIMKEY;
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
if( pCol->colFlags & COLFLAG_GENERATED ){
|
||||
testcase( pCol->colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pCol->colFlags & COLFLAG_STORED );
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"generated columns cannot be part of the PRIMARY KEY");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Designate the PRIMARY KEY for the table. pList is a list of names
|
||||
** of columns that form the primary key. If pList is NULL, then the
|
||||
@ -1410,7 +1514,7 @@ void sqlite3AddPrimaryKey(
|
||||
if( pList==0 ){
|
||||
iCol = pTab->nCol - 1;
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pCol->colFlags |= COLFLAG_PRIMKEY;
|
||||
makeColumnPartOfPrimaryKey(pParse, pCol);
|
||||
nTerm = 1;
|
||||
}else{
|
||||
nTerm = pList->nExpr;
|
||||
@ -1423,7 +1527,7 @@ void sqlite3AddPrimaryKey(
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pCol->colFlags |= COLFLAG_PRIMKEY;
|
||||
makeColumnPartOfPrimaryKey(pParse, pCol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1444,6 +1548,7 @@ void sqlite3AddPrimaryKey(
|
||||
assert( autoInc==0 || autoInc==1 );
|
||||
pTab->tabFlags |= autoInc*TF_Autoincrement;
|
||||
if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags;
|
||||
(void)sqlite3HasExplicitNulls(pParse, pList);
|
||||
}else if( autoInc ){
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
|
||||
@ -1520,41 +1625,58 @@ void sqlite3AddCollateType(Parse *pParse, Token *pToken){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function returns the collation sequence for database native text
|
||||
** encoding identified by the string zName, length nName.
|
||||
**
|
||||
** If the requested collation sequence is not available, or not available
|
||||
** in the database native encoding, the collation factory is invoked to
|
||||
** request it. If the collation factory does not supply such a sequence,
|
||||
** and the sequence is available in another text encoding, then that is
|
||||
** returned instead.
|
||||
**
|
||||
** If no versions of the requested collations sequence are available, or
|
||||
** another error occurs, NULL is returned and an error message written into
|
||||
** pParse.
|
||||
**
|
||||
** This routine is a wrapper around sqlite3FindCollSeq(). This routine
|
||||
** invokes the collation factory if the named collation cannot be found
|
||||
** and generates an error message.
|
||||
**
|
||||
** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
|
||||
/* Change the most recently parsed column to be a GENERATED ALWAYS AS
|
||||
** column.
|
||||
*/
|
||||
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
|
||||
sqlite3 *db = pParse->db;
|
||||
u8 enc = ENC(db);
|
||||
u8 initbusy = db->init.busy;
|
||||
CollSeq *pColl;
|
||||
|
||||
pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
|
||||
if( !initbusy && (!pColl || !pColl->xCmp) ){
|
||||
pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName);
|
||||
void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType){
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
u8 eType = COLFLAG_VIRTUAL;
|
||||
Table *pTab = pParse->pNewTable;
|
||||
Column *pCol;
|
||||
if( pTab==0 ){
|
||||
/* generated column in an CREATE TABLE IF NOT EXISTS that already exists */
|
||||
goto generated_done;
|
||||
}
|
||||
pCol = &(pTab->aCol[pTab->nCol-1]);
|
||||
if( IN_DECLARE_VTAB ){
|
||||
sqlite3ErrorMsg(pParse, "virtual tables cannot use computed columns");
|
||||
goto generated_done;
|
||||
}
|
||||
if( pCol->pDflt ) goto generated_error;
|
||||
if( pType ){
|
||||
if( pType->n==7 && sqlite3StrNICmp("virtual",pType->z,7)==0 ){
|
||||
/* no-op */
|
||||
}else if( pType->n==6 && sqlite3StrNICmp("stored",pType->z,6)==0 ){
|
||||
eType = COLFLAG_STORED;
|
||||
}else{
|
||||
goto generated_error;
|
||||
}
|
||||
}
|
||||
if( eType==COLFLAG_VIRTUAL ) pTab->nNVCol--;
|
||||
pCol->colFlags |= eType;
|
||||
assert( TF_HasVirtual==COLFLAG_VIRTUAL );
|
||||
assert( TF_HasStored==COLFLAG_STORED );
|
||||
pTab->tabFlags |= eType;
|
||||
if( pCol->colFlags & COLFLAG_PRIMKEY ){
|
||||
makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */
|
||||
}
|
||||
pCol->pDflt = pExpr;
|
||||
pExpr = 0;
|
||||
goto generated_done;
|
||||
|
||||
return pColl;
|
||||
generated_error:
|
||||
sqlite3ErrorMsg(pParse, "error in generated column \"%s\"",
|
||||
pCol->zName);
|
||||
generated_done:
|
||||
sqlite3ExprDelete(pParse->db, pExpr);
|
||||
#else
|
||||
/* Throw and error for the GENERATED ALWAYS AS clause if the
|
||||
** SQLITE_OMIT_GENERATED_COLUMNS compile-time option is used. */
|
||||
sqlite3ErrorMsg(pParse, "generated columns not supported");
|
||||
sqlite3ExprDelete(pParse->db, pExpr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code that will increment the schema cookie.
|
||||
**
|
||||
@ -1812,15 +1934,24 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){
|
||||
** high-order bit of colNotIdxed is always 1. All unindexed columns
|
||||
** of the table have a 1.
|
||||
**
|
||||
** 2019-10-24: For the purpose of this computation, virtual columns are
|
||||
** not considered to be covered by the index, even if they are in the
|
||||
** index, because we do not trust the logic in whereIndexExprTrans() to be
|
||||
** able to find all instances of a reference to the indexed table column
|
||||
** and convert them into references to the index. Hence we always want
|
||||
** the actual table at hand in order to recompute the virtual column, if
|
||||
** necessary.
|
||||
**
|
||||
** The colNotIdxed mask is AND-ed with the SrcList.a[].colUsed mask
|
||||
** to determine if the index is covering index.
|
||||
*/
|
||||
static void recomputeColumnsNotIndexed(Index *pIdx){
|
||||
Bitmask m = 0;
|
||||
int j;
|
||||
Table *pTab = pIdx->pTable;
|
||||
for(j=pIdx->nColumn-1; j>=0; j--){
|
||||
int x = pIdx->aiColumn[j];
|
||||
if( x>=0 ){
|
||||
if( x>=0 && (pTab->aCol[x].colFlags & COLFLAG_VIRTUAL)==0 ){
|
||||
testcase( x==BMS-1 );
|
||||
testcase( x==BMS-2 );
|
||||
if( x<BMS-1 ) m |= MASKBIT(x);
|
||||
@ -1871,6 +2002,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
pTab->aCol[i].notNull = OE_Abort;
|
||||
}
|
||||
}
|
||||
pTab->tabFlags |= TF_HasNotNull;
|
||||
}
|
||||
|
||||
/* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
|
||||
@ -1978,11 +2110,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
*/
|
||||
nExtra = 0;
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( !hasColumn(pPk->aiColumn, nPk, i) ) nExtra++;
|
||||
if( !hasColumn(pPk->aiColumn, nPk, i)
|
||||
&& (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++;
|
||||
}
|
||||
if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
|
||||
for(i=0, j=nPk; i<pTab->nCol; i++){
|
||||
if( !hasColumn(pPk->aiColumn, j, i) ){
|
||||
if( !hasColumn(pPk->aiColumn, j, i)
|
||||
&& (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
|
||||
){
|
||||
assert( j<pPk->nColumn );
|
||||
pPk->aiColumn[j] = i;
|
||||
pPk->azColl[j] = sqlite3StrBINARY;
|
||||
@ -1990,7 +2125,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
}
|
||||
}
|
||||
assert( pPk->nColumn==j );
|
||||
assert( pTab->nCol<=j );
|
||||
assert( pTab->nNVCol<=j );
|
||||
recomputeColumnsNotIndexed(pPk);
|
||||
}
|
||||
|
||||
@ -2002,7 +2137,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
** zName is temporarily modified while this routine is running, but is
|
||||
** restored to its original value prior to this routine returning.
|
||||
*/
|
||||
static int isShadowTableName(sqlite3 *db, char *zName){
|
||||
int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
|
||||
char *zTail; /* Pointer to the last "_" in zName */
|
||||
Table *pTab; /* Table that zName is a shadow of */
|
||||
Module *pMod; /* Module for the virtual table */
|
||||
@ -2020,8 +2155,6 @@ static int isShadowTableName(sqlite3 *db, char *zName){
|
||||
if( pMod->pModule->xShadowName==0 ) return 0;
|
||||
return pMod->pModule->xShadowName(zTail+1);
|
||||
}
|
||||
#else
|
||||
# define isShadowTableName(x,y) 0
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/*
|
||||
@ -2063,7 +2196,7 @@ void sqlite3EndTable(
|
||||
p = pParse->pNewTable;
|
||||
if( p==0 ) return;
|
||||
|
||||
if( pSelect==0 && isShadowTableName(db, p->zName) ){
|
||||
if( pSelect==0 && sqlite3ShadowTableName(db, p->zName) ){
|
||||
p->tabFlags |= TF_Shadow;
|
||||
}
|
||||
|
||||
@ -2099,12 +2232,11 @@ void sqlite3EndTable(
|
||||
}
|
||||
if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName);
|
||||
}else{
|
||||
p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
|
||||
convertToWithoutRowidTable(pParse, p);
|
||||
return;
|
||||
}
|
||||
p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
|
||||
convertToWithoutRowidTable(pParse, p);
|
||||
}
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, p->pSchema);
|
||||
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
@ -2112,8 +2244,45 @@ void sqlite3EndTable(
|
||||
*/
|
||||
if( p->pCheck ){
|
||||
sqlite3ResolveSelfReference(pParse, p, NC_IsCheck, 0, p->pCheck);
|
||||
if( pParse->nErr ){
|
||||
/* If errors are seen, delete the CHECK constraints now, else they might
|
||||
** actually be used if PRAGMA writable_schema=ON is set. */
|
||||
sqlite3ExprListDelete(db, p->pCheck);
|
||||
p->pCheck = 0;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
if( p->tabFlags & TF_HasGenerated ){
|
||||
int ii, nNG = 0;
|
||||
testcase( p->tabFlags & TF_HasVirtual );
|
||||
testcase( p->tabFlags & TF_HasStored );
|
||||
for(ii=0; ii<p->nCol; ii++){
|
||||
u32 colFlags = p->aCol[ii].colFlags;
|
||||
if( (colFlags & COLFLAG_GENERATED)!=0 ){
|
||||
Expr *pX = p->aCol[ii].pDflt;
|
||||
testcase( colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( colFlags & COLFLAG_STORED );
|
||||
if( sqlite3ResolveSelfReference(pParse, p, NC_GenCol, pX, 0) ){
|
||||
/* If there are errors in resolving the expression, change the
|
||||
** expression to a NULL. This prevents code generators that operate
|
||||
** on the expression from inserting extra parts into the expression
|
||||
** tree that have been allocated from lookaside memory, which is
|
||||
** illegal in a schema and will lead to errors or heap corruption
|
||||
** when the database connection closes. */
|
||||
sqlite3ExprDelete(db, pX);
|
||||
p->aCol[ii].pDflt = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
|
||||
}
|
||||
}else{
|
||||
nNG++;
|
||||
}
|
||||
}
|
||||
if( nNG==0 ){
|
||||
sqlite3ErrorMsg(pParse, "must have at least one non-generated column");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Estimate the average row size for the table and for all implied indices */
|
||||
estimateTableWidth(p);
|
||||
@ -2190,7 +2359,7 @@ void sqlite3EndTable(
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect, SQLITE_AFF_BLOB);
|
||||
if( pSelTab==0 ) return;
|
||||
assert( p->aCol==0 );
|
||||
p->nCol = pSelTab->nCol;
|
||||
p->nCol = p->nNVCol = pSelTab->nCol;
|
||||
p->aCol = pSelTab->aCol;
|
||||
pSelTab->nCol = 0;
|
||||
pSelTab->aCol = 0;
|
||||
@ -2263,7 +2432,6 @@ void sqlite3EndTable(
|
||||
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
|
||||
}
|
||||
|
||||
|
||||
/* Add the table to the in-memory representation of the database.
|
||||
*/
|
||||
if( db->init.busy ){
|
||||
@ -2334,6 +2502,7 @@ void sqlite3CreateView(
|
||||
** allocated rather than point to the input string - which means that
|
||||
** they will persist after the current sqlite3_exec() call returns.
|
||||
*/
|
||||
pSelect->selFlags |= SF_View;
|
||||
if( IN_RENAME_OBJECT ){
|
||||
p->pSelect = pSelect;
|
||||
pSelect = 0;
|
||||
@ -2447,7 +2616,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
db->lookaside.bDisable++;
|
||||
DisableLookaside;
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
@ -2457,7 +2626,10 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
|
||||
#endif
|
||||
pParse->nTab = n;
|
||||
if( pTable->pCheck ){
|
||||
if( pSelTab==0 ){
|
||||
pTable->nCol = 0;
|
||||
nErr++;
|
||||
}else if( pTable->pCheck ){
|
||||
/* CREATE VIEW name(arglist) AS ...
|
||||
** The names of the columns in the table are taken from
|
||||
** arglist which is stored in pTable->pCheck. The pCheck field
|
||||
@ -2473,7 +2645,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel,
|
||||
SQLITE_AFF_NONE);
|
||||
}
|
||||
}else if( pSelTab ){
|
||||
}else{
|
||||
/* CREATE VIEW name AS... without an argument list. Construct
|
||||
** the column names from the SELECT statement that defines the view.
|
||||
*/
|
||||
@ -2483,13 +2655,11 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
pSelTab->nCol = 0;
|
||||
pSelTab->aCol = 0;
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
|
||||
}else{
|
||||
pTable->nCol = 0;
|
||||
nErr++;
|
||||
}
|
||||
pTable->nNVCol = pTable->nCol;
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
db->lookaside.bDisable--;
|
||||
EnableLookaside;
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
pParse->eParseMode = eParseMode;
|
||||
#endif
|
||||
@ -2746,6 +2916,37 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
sqliteViewResetAll(db, iDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if shadow tables should be read-only in the current
|
||||
** context.
|
||||
*/
|
||||
int sqlite3ReadOnlyShadowTables(sqlite3 *db){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( (db->flags & SQLITE_Defensive)!=0
|
||||
&& db->pVtabCtx==0
|
||||
&& db->nVdbeExec==0
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if it is not allowed to drop the given table
|
||||
*/
|
||||
static int tableMayNotBeDropped(sqlite3 *db, Table *pTab){
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
|
||||
if( sqlite3StrNICmp(pTab->zName+7, "stat", 4)==0 ) return 0;
|
||||
if( sqlite3StrNICmp(pTab->zName+7, "parameters", 10)==0 ) return 0;
|
||||
return 1;
|
||||
}
|
||||
if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to do the work of a DROP TABLE statement.
|
||||
** pName is the name of the table to be dropped.
|
||||
@ -2815,9 +3016,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
|
||||
&& sqlite3StrNICmp(pTab->zName+7, "stat", 4)!=0
|
||||
&& sqlite3StrNICmp(pTab->zName+7, "parameters", 10)!=0 ){
|
||||
if( tableMayNotBeDropped(db, pTab) ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
|
||||
goto exit_drop_table;
|
||||
}
|
||||
@ -2909,7 +3108,7 @@ void sqlite3CreateForeignKey(
|
||||
nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
|
||||
if( pToCol ){
|
||||
for(i=0; i<pToCol->nExpr; i++){
|
||||
nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
|
||||
nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
|
||||
}
|
||||
}
|
||||
pFKey = sqlite3DbMallocZero(db, nByte );
|
||||
@ -2934,7 +3133,7 @@ void sqlite3CreateForeignKey(
|
||||
for(i=0; i<nCol; i++){
|
||||
int j;
|
||||
for(j=0; j<p->nCol; j++){
|
||||
if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){
|
||||
if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zEName)==0 ){
|
||||
pFKey->aCol[i].iFrom = j;
|
||||
break;
|
||||
}
|
||||
@ -2942,22 +3141,22 @@ void sqlite3CreateForeignKey(
|
||||
if( j>=p->nCol ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"unknown column \"%s\" in foreign key definition",
|
||||
pFromCol->a[i].zName);
|
||||
pFromCol->a[i].zEName);
|
||||
goto fk_end;
|
||||
}
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName);
|
||||
sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zEName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pToCol ){
|
||||
for(i=0; i<nCol; i++){
|
||||
int n = sqlite3Strlen30(pToCol->a[i].zName);
|
||||
int n = sqlite3Strlen30(pToCol->a[i].zEName);
|
||||
pFKey->aCol[i].zCol = z;
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName);
|
||||
sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zEName);
|
||||
}
|
||||
memcpy(z, pToCol->a[i].zName, n);
|
||||
memcpy(z, pToCol->a[i].zEName, n);
|
||||
z[n] = 0;
|
||||
z += n+1;
|
||||
}
|
||||
@ -3488,8 +3687,13 @@ void sqlite3CreateIndex(
|
||||
assert( j<=0x7fff );
|
||||
if( j<0 ){
|
||||
j = pTab->iPKey;
|
||||
}else if( pTab->aCol[j].notNull==0 ){
|
||||
pIndex->uniqNotNull = 0;
|
||||
}else{
|
||||
if( pTab->aCol[j].notNull==0 ){
|
||||
pIndex->uniqNotNull = 0;
|
||||
}
|
||||
if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){
|
||||
pIndex->bHasVCol = 1;
|
||||
}
|
||||
}
|
||||
pIndex->aiColumn[i] = (i16)j;
|
||||
}
|
||||
@ -3544,13 +3748,13 @@ void sqlite3CreateIndex(
|
||||
/* If this index contains every column of its table, then mark
|
||||
** it as a covering index */
|
||||
assert( HasRowid(pTab)
|
||||
|| pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 );
|
||||
|| pTab->iPKey<0 || sqlite3TableColumnToIndex(pIndex, pTab->iPKey)>=0 );
|
||||
recomputeColumnsNotIndexed(pIndex);
|
||||
if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
|
||||
pIndex->isCovering = 1;
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( j==pTab->iPKey ) continue;
|
||||
if( sqlite3ColumnOfIndex(pIndex,j)>=0 ) continue;
|
||||
if( sqlite3TableColumnToIndex(pIndex,j)>=0 ) continue;
|
||||
pIndex->isCovering = 0;
|
||||
break;
|
||||
}
|
||||
@ -3725,26 +3929,9 @@ void sqlite3CreateIndex(
|
||||
sqlite3VdbeJumpHere(v, pIndex->tnum);
|
||||
}
|
||||
}
|
||||
|
||||
/* When adding an index to the list of indices for a table, make
|
||||
** sure all indices labeled OE_Replace come after all those labeled
|
||||
** OE_Ignore. This is necessary for the correct constraint check
|
||||
** processing (in sqlite3GenerateConstraintChecks()) as part of
|
||||
** UPDATE and INSERT statements.
|
||||
*/
|
||||
if( db->init.busy || pTblName==0 ){
|
||||
if( onError!=OE_Replace || pTab->pIndex==0
|
||||
|| pTab->pIndex->onError==OE_Replace){
|
||||
pIndex->pNext = pTab->pIndex;
|
||||
pTab->pIndex = pIndex;
|
||||
}else{
|
||||
Index *pOther = pTab->pIndex;
|
||||
while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){
|
||||
pOther = pOther->pNext;
|
||||
}
|
||||
pIndex->pNext = pOther->pNext;
|
||||
pOther->pNext = pIndex;
|
||||
}
|
||||
pIndex->pNext = pTab->pIndex;
|
||||
pTab->pIndex = pIndex;
|
||||
pIndex = 0;
|
||||
}
|
||||
else if( IN_RENAME_OBJECT ){
|
||||
@ -3756,6 +3943,21 @@ void sqlite3CreateIndex(
|
||||
/* Clean up before exiting */
|
||||
exit_create_index:
|
||||
if( pIndex ) sqlite3FreeIndex(db, pIndex);
|
||||
if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */
|
||||
Index **ppFrom = &pTab->pIndex;
|
||||
Index *pThis;
|
||||
for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){
|
||||
Index *pNext;
|
||||
if( pThis->onError!=OE_Replace ) continue;
|
||||
while( (pNext = pThis->pNext)!=0 && pNext->onError!=OE_Replace ){
|
||||
*ppFrom = pNext;
|
||||
pThis->pNext = pNext->pNext;
|
||||
pNext->pNext = pThis;
|
||||
ppFrom = &pNext->pNext;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3ExprDelete(db, pPIWhere);
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
sqlite3SrcListDelete(db, pTblName);
|
||||
|
||||
141
src/callback.c
141
src/callback.c
@ -65,51 +65,6 @@ static int synthCollSeq(sqlite3 *db, CollSeq *pColl){
|
||||
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 desired 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. If no collation is found, leave an error message.
|
||||
**
|
||||
** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
|
||||
*/
|
||||
CollSeq *sqlite3GetCollSeq(
|
||||
Parse *pParse, /* Parsing context */
|
||||
u8 enc, /* The desired encoding for the collating sequence */
|
||||
CollSeq *pColl, /* Collating sequence with native encoding, or NULL */
|
||||
const char *zName /* Collating sequence name */
|
||||
){
|
||||
CollSeq *p;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
p = pColl;
|
||||
if( !p ){
|
||||
p = sqlite3FindCollSeq(db, enc, zName, 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, enc, zName);
|
||||
p = sqlite3FindCollSeq(db, enc, zName, 0);
|
||||
}
|
||||
if( p && !p->xCmp && synthCollSeq(db, p) ){
|
||||
p = 0;
|
||||
}
|
||||
assert( !p || p->xCmp );
|
||||
if( p==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
||||
pParse->rc = SQLITE_ERROR_MISSING_COLLSEQ;
|
||||
}
|
||||
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
|
||||
@ -202,10 +157,10 @@ static CollSeq *findCollSeqEntry(
|
||||
** See also: sqlite3LocateCollSeq(), sqlite3GetCollSeq()
|
||||
*/
|
||||
CollSeq *sqlite3FindCollSeq(
|
||||
sqlite3 *db,
|
||||
u8 enc,
|
||||
const char *zName,
|
||||
int create
|
||||
sqlite3 *db, /* Database connection to search */
|
||||
u8 enc, /* Desired text encoding */
|
||||
const char *zName, /* Name of the collating sequence. Might be NULL */
|
||||
int create /* True to create CollSeq if doesn't already exist */
|
||||
){
|
||||
CollSeq *pColl;
|
||||
if( zName ){
|
||||
@ -219,6 +174,85 @@ CollSeq *sqlite3FindCollSeq(
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 desired 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. If no collation is found, leave an error message.
|
||||
**
|
||||
** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
|
||||
*/
|
||||
CollSeq *sqlite3GetCollSeq(
|
||||
Parse *pParse, /* Parsing context */
|
||||
u8 enc, /* The desired encoding for the collating sequence */
|
||||
CollSeq *pColl, /* Collating sequence with native encoding, or NULL */
|
||||
const char *zName /* Collating sequence name */
|
||||
){
|
||||
CollSeq *p;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
p = pColl;
|
||||
if( !p ){
|
||||
p = sqlite3FindCollSeq(db, enc, zName, 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, enc, zName);
|
||||
p = sqlite3FindCollSeq(db, enc, zName, 0);
|
||||
}
|
||||
if( p && !p->xCmp && synthCollSeq(db, p) ){
|
||||
p = 0;
|
||||
}
|
||||
assert( !p || p->xCmp );
|
||||
if( p==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
||||
pParse->rc = SQLITE_ERROR_MISSING_COLLSEQ;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function returns the collation sequence for database native text
|
||||
** encoding identified by the string zName.
|
||||
**
|
||||
** If the requested collation sequence is not available, or not available
|
||||
** in the database native encoding, the collation factory is invoked to
|
||||
** request it. If the collation factory does not supply such a sequence,
|
||||
** and the sequence is available in another text encoding, then that is
|
||||
** returned instead.
|
||||
**
|
||||
** If no versions of the requested collations sequence are available, or
|
||||
** another error occurs, NULL is returned and an error message written into
|
||||
** pParse.
|
||||
**
|
||||
** This routine is a wrapper around sqlite3FindCollSeq(). This routine
|
||||
** invokes the collation factory if the named collation cannot be found
|
||||
** and generates an error message.
|
||||
**
|
||||
** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
|
||||
*/
|
||||
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
|
||||
sqlite3 *db = pParse->db;
|
||||
u8 enc = ENC(db);
|
||||
u8 initbusy = db->init.busy;
|
||||
CollSeq *pColl;
|
||||
|
||||
pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
|
||||
if( !initbusy && (!pColl || !pColl->xCmp) ){
|
||||
pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName);
|
||||
}
|
||||
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/* During the search for the best function definition, this procedure
|
||||
** is called to test how well the function passed as the first argument
|
||||
** matches the request for a function with nArg arguments in a system
|
||||
@ -254,12 +288,13 @@ static int matchQuality(
|
||||
u8 enc /* Desired text encoding */
|
||||
){
|
||||
int match;
|
||||
|
||||
/* nArg of -2 is a special case */
|
||||
if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
|
||||
assert( p->nArg>=-1 );
|
||||
|
||||
/* Wrong number of arguments means "no match" */
|
||||
if( p->nArg!=nArg && p->nArg>=0 ) return 0;
|
||||
if( p->nArg!=nArg ){
|
||||
if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
|
||||
if( p->nArg>=0 ) return 0;
|
||||
}
|
||||
|
||||
/* Give a better score to a function with a specific number of arguments
|
||||
** than to function that accepts any number of arguments. */
|
||||
|
||||
@ -688,7 +688,7 @@ static int parseModifier(
|
||||
r = p->s*1000.0 + 210866760000000.0;
|
||||
if( r>=0.0 && r<464269060800000.0 ){
|
||||
clearYMD_HMS_TZ(p);
|
||||
p->iJD = (sqlite3_int64)r;
|
||||
p->iJD = (sqlite3_int64)(r + 0.5);
|
||||
p->validJD = 1;
|
||||
p->rawS = 0;
|
||||
rc = 0;
|
||||
|
||||
@ -73,6 +73,7 @@ static int dbpageConnect(
|
||||
DbpageTable *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
348
src/dbstat.c
348
src/dbstat.c
@ -12,7 +12,7 @@
|
||||
**
|
||||
** This file contains an implementation of the "dbstat" virtual table.
|
||||
**
|
||||
** The dbstat virtual table is used to extract low-level formatting
|
||||
** The dbstat virtual table is used to extract low-level storage
|
||||
** information from an SQLite database in order to implement the
|
||||
** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script
|
||||
** for an example implementation.
|
||||
@ -56,27 +56,30 @@
|
||||
**
|
||||
** '/1c2/000/' // Left-most child of 451st child of root
|
||||
*/
|
||||
#define VTAB_SCHEMA \
|
||||
"CREATE TABLE xx( " \
|
||||
" name TEXT, /* Name of table or index */" \
|
||||
" path TEXT, /* Path to page from root */" \
|
||||
" pageno INTEGER, /* Page number */" \
|
||||
" pagetype TEXT, /* 'internal', 'leaf' or 'overflow' */" \
|
||||
" ncell INTEGER, /* Cells on page (0 for overflow) */" \
|
||||
" payload INTEGER, /* Bytes of payload on this page */" \
|
||||
" unused INTEGER, /* Bytes of unused space on this page */" \
|
||||
" mx_payload INTEGER, /* Largest payload size of all cells */" \
|
||||
" pgoffset INTEGER, /* Offset of page in file */" \
|
||||
" pgsize INTEGER, /* Size of the page */" \
|
||||
" schema TEXT HIDDEN /* Database schema being analyzed */" \
|
||||
");"
|
||||
|
||||
static const char zDbstatSchema[] =
|
||||
"CREATE TABLE x("
|
||||
" name TEXT," /* 0 Name of table or index */
|
||||
" path TEXT," /* 1 Path to page from root (NULL for agg) */
|
||||
" pageno INTEGER," /* 2 Page number (page count for aggregates) */
|
||||
" pagetype TEXT," /* 3 'internal', 'leaf', 'overflow', or NULL */
|
||||
" ncell INTEGER," /* 4 Cells on page (0 for overflow) */
|
||||
" payload INTEGER," /* 5 Bytes of payload on this page */
|
||||
" unused INTEGER," /* 6 Bytes of unused space on this page */
|
||||
" mx_payload INTEGER," /* 7 Largest payload size of all cells */
|
||||
" pgoffset INTEGER," /* 8 Offset of page in file (NULL for agg) */
|
||||
" pgsize INTEGER," /* 9 Size of the page (sum for aggregate) */
|
||||
" schema TEXT HIDDEN," /* 10 Database schema being analyzed */
|
||||
" aggregate BOOLEAN HIDDEN" /* 11 aggregate info for each table */
|
||||
")"
|
||||
;
|
||||
|
||||
/* Forward reference to data structured used in this module */
|
||||
typedef struct StatTable StatTable;
|
||||
typedef struct StatCursor StatCursor;
|
||||
typedef struct StatPage StatPage;
|
||||
typedef struct StatCell StatCell;
|
||||
|
||||
/* Size information for a single cell within a btree page */
|
||||
struct StatCell {
|
||||
int nLocal; /* Bytes of local payload */
|
||||
u32 iChildPg; /* Child node (or 0 if this is a leaf) */
|
||||
@ -86,10 +89,11 @@ struct StatCell {
|
||||
int iOvfl; /* Iterates through aOvfl[] */
|
||||
};
|
||||
|
||||
/* Size information for a single btree page */
|
||||
struct StatPage {
|
||||
u32 iPgno;
|
||||
DbPage *pPg;
|
||||
int iCell;
|
||||
u32 iPgno; /* Page number */
|
||||
DbPage *pPg; /* Page content */
|
||||
int iCell; /* Current cell */
|
||||
|
||||
char *zPath; /* Path to this page */
|
||||
|
||||
@ -99,34 +103,38 @@ struct StatPage {
|
||||
int nUnused; /* Number of unused bytes on page */
|
||||
StatCell *aCell; /* Array of parsed cells */
|
||||
u32 iRightChildPg; /* Right-child page number (or 0) */
|
||||
int nMxPayload; /* Largest payload of any cell on this page */
|
||||
int nMxPayload; /* Largest payload of any cell on the page */
|
||||
};
|
||||
|
||||
/* The cursor for scanning the dbstat virtual table */
|
||||
struct StatCursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_vtab_cursor base; /* base class. MUST BE FIRST! */
|
||||
sqlite3_stmt *pStmt; /* Iterates through set of root pages */
|
||||
int isEof; /* After pStmt has returned SQLITE_DONE */
|
||||
u8 isEof; /* After pStmt has returned SQLITE_DONE */
|
||||
u8 isAgg; /* Aggregate results for each table */
|
||||
int iDb; /* Schema used for this query */
|
||||
|
||||
StatPage aPage[32];
|
||||
StatPage aPage[32]; /* Pages in path to current page */
|
||||
int iPage; /* Current entry in aPage[] */
|
||||
|
||||
/* Values to return. */
|
||||
u32 iPageno; /* Value of 'pageno' column */
|
||||
char *zName; /* Value of 'name' column */
|
||||
char *zPath; /* Value of 'path' column */
|
||||
u32 iPageno; /* Value of 'pageno' column */
|
||||
char *zPagetype; /* Value of 'pagetype' column */
|
||||
int nPage; /* Number of pages in current btree */
|
||||
int nCell; /* Value of 'ncell' column */
|
||||
int nPayload; /* Value of 'payload' column */
|
||||
int nUnused; /* Value of 'unused' column */
|
||||
int nMxPayload; /* Value of 'mx_payload' column */
|
||||
i64 nUnused; /* Value of 'unused' column */
|
||||
i64 nPayload; /* Value of 'payload' column */
|
||||
i64 iOffset; /* Value of 'pgOffset' column */
|
||||
int szPage; /* Value of 'pgSize' column */
|
||||
i64 szPage; /* Value of 'pgSize' column */
|
||||
};
|
||||
|
||||
/* An instance of the DBSTAT virtual table */
|
||||
struct StatTable {
|
||||
sqlite3_vtab base;
|
||||
sqlite3 *db;
|
||||
sqlite3_vtab base; /* base class. MUST BE FIRST! */
|
||||
sqlite3 *db; /* Database connection that owns this vtab */
|
||||
int iDb; /* Index of database to analyze */
|
||||
};
|
||||
|
||||
@ -135,7 +143,7 @@ struct StatTable {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Connect to or create a statvfs virtual table.
|
||||
** Connect to or create a new DBSTAT virtual table.
|
||||
*/
|
||||
static int statConnect(
|
||||
sqlite3 *db,
|
||||
@ -159,7 +167,8 @@ static int statConnect(
|
||||
}else{
|
||||
iDb = 0;
|
||||
}
|
||||
rc = sqlite3_declare_vtab(db, VTAB_SCHEMA);
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
rc = sqlite3_declare_vtab(db, zDbstatSchema);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable));
|
||||
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
@ -177,7 +186,7 @@ static int statConnect(
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect from or destroy a statvfs virtual table.
|
||||
** Disconnect from or destroy the DBSTAT virtual table.
|
||||
*/
|
||||
static int statDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
@ -185,14 +194,20 @@ static int statDisconnect(sqlite3_vtab *pVtab){
|
||||
}
|
||||
|
||||
/*
|
||||
** There is no "best-index". This virtual table always does a linear
|
||||
** scan. However, a schema=? constraint should cause this table to
|
||||
** operate on a different database schema, so check for it.
|
||||
** Compute the best query strategy and return the result in idxNum.
|
||||
**
|
||||
** idxNum is normally 0, but will be 1 if a schema=? constraint exists.
|
||||
** idxNum-Bit Meaning
|
||||
** ---------- ----------------------------------------------
|
||||
** 0x01 There is a schema=? term in the WHERE clause
|
||||
** 0x02 There is a name=? term in the WHERE clause
|
||||
** 0x04 There is an aggregate=? term in the WHERE clause
|
||||
** 0x08 Output should be ordered by name and path
|
||||
*/
|
||||
static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int i;
|
||||
int iSchema = -1;
|
||||
int iName = -1;
|
||||
int iAgg = -1;
|
||||
|
||||
/* Look for a valid schema=? constraint. If found, change the idxNum to
|
||||
** 1 and request the value of that constraint be sent to xFilter. And
|
||||
@ -200,16 +215,40 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
** used.
|
||||
*/
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
|
||||
if( pIdxInfo->aConstraint[i].usable==0 ) return SQLITE_CONSTRAINT;
|
||||
if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 1.0;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
break;
|
||||
if( pIdxInfo->aConstraint[i].usable==0 ){
|
||||
/* Force DBSTAT table should always be the right-most table in a join */
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
switch( pIdxInfo->aConstraint[i].iColumn ){
|
||||
case 0: { /* name */
|
||||
iName = i;
|
||||
break;
|
||||
}
|
||||
case 10: { /* schema */
|
||||
iSchema = i;
|
||||
break;
|
||||
}
|
||||
case 11: { /* aggregate */
|
||||
iAgg = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if( iSchema>=0 ){
|
||||
pIdxInfo->aConstraintUsage[iSchema].argvIndex = ++i;
|
||||
pIdxInfo->idxNum |= 0x01;
|
||||
}
|
||||
if( iName>=0 ){
|
||||
pIdxInfo->aConstraintUsage[iName].argvIndex = ++i;
|
||||
pIdxInfo->idxNum |= 0x02;
|
||||
}
|
||||
if( iAgg>=0 ){
|
||||
pIdxInfo->aConstraintUsage[iAgg].argvIndex = ++i;
|
||||
pIdxInfo->idxNum |= 0x04;
|
||||
}
|
||||
pIdxInfo->estimatedCost = 1.0;
|
||||
|
||||
/* Records are always returned in ascending order of (name, path).
|
||||
** If this will satisfy the client, set the orderByConsumed flag so that
|
||||
@ -227,13 +266,14 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
)
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
pIdxInfo->idxNum |= 0x08;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new statvfs cursor.
|
||||
** Open a new DBSTAT cursor.
|
||||
*/
|
||||
static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
StatTable *pTab = (StatTable *)pVTab;
|
||||
@ -283,8 +323,18 @@ static void statResetCsr(StatCursor *pCsr){
|
||||
pCsr->isEof = 0;
|
||||
}
|
||||
|
||||
/* Resize the space-used counters inside of the cursor */
|
||||
static void statResetCounts(StatCursor *pCsr){
|
||||
pCsr->nCell = 0;
|
||||
pCsr->nMxPayload = 0;
|
||||
pCsr->nUnused = 0;
|
||||
pCsr->nPayload = 0;
|
||||
pCsr->szPage = 0;
|
||||
pCsr->nPage = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a statvfs cursor.
|
||||
** Close a DBSTAT cursor.
|
||||
*/
|
||||
static int statClose(sqlite3_vtab_cursor *pCursor){
|
||||
StatCursor *pCsr = (StatCursor *)pCursor;
|
||||
@ -294,11 +344,15 @@ static int statClose(sqlite3_vtab_cursor *pCursor){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void getLocalPayload(
|
||||
/*
|
||||
** For a single cell on a btree page, compute the number of bytes of
|
||||
** content (payload) stored on that page. That is to say, compute the
|
||||
** number of bytes of content not found on overflow pages.
|
||||
*/
|
||||
static int getLocalPayload(
|
||||
int nUsable, /* Usable bytes per page */
|
||||
u8 flags, /* Page flags */
|
||||
int nTotal, /* Total record (payload) size */
|
||||
int *pnLocal /* OUT: Bytes stored locally */
|
||||
int nTotal /* Total record (payload) size */
|
||||
){
|
||||
int nLocal;
|
||||
int nMinLocal;
|
||||
@ -314,9 +368,12 @@ static void getLocalPayload(
|
||||
|
||||
nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4);
|
||||
if( nLocal>nMaxLocal ) nLocal = nMinLocal;
|
||||
*pnLocal = nLocal;
|
||||
return nLocal;
|
||||
}
|
||||
|
||||
/* Populate the StatPage object with information about the all
|
||||
** cells found on the page currently under analysis.
|
||||
*/
|
||||
static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
int nUnused;
|
||||
int iOff;
|
||||
@ -387,7 +444,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
iOff += sqlite3GetVarint(&aData[iOff], &dummy);
|
||||
}
|
||||
if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
|
||||
getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
|
||||
nLocal = getLocalPayload(nUsable, p->flags, nPayload);
|
||||
if( nLocal<0 ) goto statPageIsCorrupt;
|
||||
pCell->nLocal = nLocal;
|
||||
assert( nPayload>=(u32)nLocal );
|
||||
@ -437,23 +494,25 @@ static void statSizeAndOffset(StatCursor *pCsr){
|
||||
sqlite3_file *fd;
|
||||
sqlite3_int64 x[2];
|
||||
|
||||
/* The default page size and offset */
|
||||
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
|
||||
|
||||
/* If connected to a ZIPVFS backend, override the page size and
|
||||
** offset with actual values obtained from ZIPVFS.
|
||||
/* If connected to a ZIPVFS backend, find the page size and
|
||||
** offset from ZIPVFS.
|
||||
*/
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
x[0] = pCsr->iPageno;
|
||||
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
||||
pCsr->iOffset = x[0];
|
||||
pCsr->szPage = (int)x[1];
|
||||
pCsr->szPage += x[1];
|
||||
}else{
|
||||
/* Not ZIPVFS: The default page size and offset */
|
||||
pCsr->szPage += sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a statvfs cursor to the next entry in the file.
|
||||
** Move a DBSTAT cursor to the next entry. Normally, the next
|
||||
** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0),
|
||||
** the next entry is the next btree.
|
||||
*/
|
||||
static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
int rc;
|
||||
@ -469,6 +528,8 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
|
||||
statNextRestart:
|
||||
if( pCsr->aPage[0].pPg==0 ){
|
||||
/* Start measuring space on the next btree */
|
||||
statResetCounts(pCsr);
|
||||
rc = sqlite3_step(pCsr->pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
int nPage;
|
||||
@ -481,44 +542,47 @@ statNextRestart:
|
||||
rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0);
|
||||
pCsr->aPage[0].iPgno = iRoot;
|
||||
pCsr->aPage[0].iCell = 0;
|
||||
pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
|
||||
if( !pCsr->isAgg ){
|
||||
pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pCsr->iPage = 0;
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
pCsr->nPage = 1;
|
||||
}else{
|
||||
pCsr->isEof = 1;
|
||||
return sqlite3_reset(pCsr->pStmt);
|
||||
}
|
||||
}else{
|
||||
|
||||
/* Page p itself has already been visited. */
|
||||
/* Continue analyzing the btree previously started */
|
||||
StatPage *p = &pCsr->aPage[pCsr->iPage];
|
||||
|
||||
if( !pCsr->isAgg ) statResetCounts(pCsr);
|
||||
while( p->iCell<p->nCell ){
|
||||
StatCell *pCell = &p->aCell[p->iCell];
|
||||
if( pCell->iOvfl<pCell->nOvfl ){
|
||||
int nUsable;
|
||||
while( pCell->iOvfl<pCell->nOvfl ){
|
||||
int nUsable, iOvfl;
|
||||
sqlite3BtreeEnter(pBt);
|
||||
nUsable = sqlite3BtreeGetPageSize(pBt) -
|
||||
sqlite3BtreeGetReserveNoMutex(pBt);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
|
||||
pCsr->iPageno = pCell->aOvfl[pCell->iOvfl];
|
||||
pCsr->zPagetype = "overflow";
|
||||
pCsr->nCell = 0;
|
||||
pCsr->nMxPayload = 0;
|
||||
pCsr->zPath = z = sqlite3_mprintf(
|
||||
"%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl
|
||||
);
|
||||
if( pCell->iOvfl<pCell->nOvfl-1 ){
|
||||
pCsr->nUnused = 0;
|
||||
pCsr->nPayload = nUsable - 4;
|
||||
}else{
|
||||
pCsr->nPayload = pCell->nLastOvfl;
|
||||
pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
|
||||
}
|
||||
pCell->iOvfl++;
|
||||
pCsr->nPage++;
|
||||
statSizeAndOffset(pCsr);
|
||||
return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
|
||||
if( pCell->iOvfl<pCell->nOvfl-1 ){
|
||||
pCsr->nPayload += nUsable - 4;
|
||||
}else{
|
||||
pCsr->nPayload += pCell->nLastOvfl;
|
||||
pCsr->nUnused += nUsable - 4 - pCell->nLastOvfl;
|
||||
}
|
||||
iOvfl = pCell->iOvfl;
|
||||
pCell->iOvfl++;
|
||||
if( !pCsr->isAgg ){
|
||||
pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
|
||||
pCsr->iPageno = pCell->aOvfl[iOvfl];
|
||||
pCsr->zPagetype = "overflow";
|
||||
pCsr->zPath = z = sqlite3_mprintf(
|
||||
"%s%.3x+%.6x", p->zPath, p->iCell, iOvfl
|
||||
);
|
||||
return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
|
||||
}
|
||||
}
|
||||
if( p->iRightChildPg ) break;
|
||||
p->iCell++;
|
||||
@ -526,8 +590,13 @@ statNextRestart:
|
||||
|
||||
if( !p->iRightChildPg || p->iCell>p->nCell ){
|
||||
statClearPage(p);
|
||||
if( pCsr->iPage==0 ) return statNext(pCursor);
|
||||
pCsr->iPage--;
|
||||
if( pCsr->iPage>0 ){
|
||||
pCsr->iPage--;
|
||||
}else if( pCsr->isAgg ){
|
||||
/* label-statNext-done: When computing aggregate space usage over
|
||||
** an entire btree, this is the exit point from this function */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
goto statNextRestart; /* Tail recursion */
|
||||
}
|
||||
pCsr->iPage++;
|
||||
@ -543,10 +612,13 @@ statNextRestart:
|
||||
p[1].iPgno = p->aCell[p->iCell].iChildPg;
|
||||
}
|
||||
rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0);
|
||||
pCsr->nPage++;
|
||||
p[1].iCell = 0;
|
||||
p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
|
||||
if( !pCsr->isAgg ){
|
||||
p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
p->iCell++;
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
|
||||
@ -576,16 +648,23 @@ statNextRestart:
|
||||
pCsr->zPagetype = "corrupted";
|
||||
break;
|
||||
}
|
||||
pCsr->nCell = p->nCell;
|
||||
pCsr->nUnused = p->nUnused;
|
||||
pCsr->nMxPayload = p->nMxPayload;
|
||||
pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
pCsr->nCell += p->nCell;
|
||||
pCsr->nUnused += p->nUnused;
|
||||
if( p->nMxPayload>pCsr->nMxPayload ) pCsr->nMxPayload = p->nMxPayload;
|
||||
if( !pCsr->isAgg ){
|
||||
pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
|
||||
if( z==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
nPayload = 0;
|
||||
for(i=0; i<p->nCell; i++){
|
||||
nPayload += p->aCell[i].nLocal;
|
||||
}
|
||||
pCsr->nPayload = nPayload;
|
||||
pCsr->nPayload += nPayload;
|
||||
|
||||
/* If computing aggregate space usage by btree, continue with the
|
||||
** next page. The loop will exit via the return at label-statNext-done
|
||||
*/
|
||||
if( pCsr->isAgg ) goto statNextRestart;
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +676,10 @@ static int statEof(sqlite3_vtab_cursor *pCursor){
|
||||
return pCsr->isEof;
|
||||
}
|
||||
|
||||
/* Initialize a cursor according to the query plan idxNum using the
|
||||
** arguments in argv[0]. See statBestIndex() for a description of the
|
||||
** meaning of the bits in idxNum.
|
||||
*/
|
||||
static int statFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
@ -604,29 +687,52 @@ static int statFilter(
|
||||
){
|
||||
StatCursor *pCsr = (StatCursor *)pCursor;
|
||||
StatTable *pTab = (StatTable*)(pCursor->pVtab);
|
||||
char *zSql;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_str *pSql; /* Query of btrees to analyze */
|
||||
char *zSql; /* String value of pSql */
|
||||
int iArg = 0; /* Count of argv[] parameters used so far */
|
||||
int rc = SQLITE_OK; /* Result of this operation */
|
||||
const char *zName = 0; /* Only provide analysis of this table */
|
||||
|
||||
if( idxNum==1 ){
|
||||
const char *zDbase = (const char*)sqlite3_value_text(argv[0]);
|
||||
statResetCsr(pCsr);
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
if( idxNum & 0x01 ){
|
||||
/* schema=? constraint is present. Get its value */
|
||||
const char *zDbase = (const char*)sqlite3_value_text(argv[iArg++]);
|
||||
pCsr->iDb = sqlite3FindDbName(pTab->db, zDbase);
|
||||
if( pCsr->iDb<0 ){
|
||||
sqlite3_free(pCursor->pVtab->zErrMsg);
|
||||
pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase);
|
||||
return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM_BKPT;
|
||||
pCsr->iDb = 0;
|
||||
pCsr->isEof = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}else{
|
||||
pCsr->iDb = pTab->iDb;
|
||||
}
|
||||
statResetCsr(pCsr);
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
|
||||
" UNION ALL "
|
||||
"SELECT name, rootpage, type"
|
||||
" FROM \"%w\".sqlite_master WHERE rootpage!=0"
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName);
|
||||
if( idxNum & 0x02 ){
|
||||
/* name=? constraint is present */
|
||||
zName = (const char*)sqlite3_value_text(argv[iArg++]);
|
||||
}
|
||||
if( idxNum & 0x04 ){
|
||||
/* aggregate=? constraint is present */
|
||||
pCsr->isAgg = sqlite3_value_double(argv[iArg++])!=0.0;
|
||||
}else{
|
||||
pCsr->isAgg = 0;
|
||||
}
|
||||
pSql = sqlite3_str_new(pTab->db);
|
||||
sqlite3_str_appendf(pSql,
|
||||
"SELECT * FROM ("
|
||||
"SELECT 'sqlite_master' AS name,1 AS rootpage,'table' AS type"
|
||||
" UNION ALL "
|
||||
"SELECT name,rootpage,type"
|
||||
" FROM \"%w\".sqlite_master WHERE rootpage!=0)",
|
||||
pTab->db->aDb[pCsr->iDb].zDbSName);
|
||||
if( zName ){
|
||||
sqlite3_str_appendf(pSql, "WHERE name=%Q", zName);
|
||||
}
|
||||
if( idxNum & 0x08 ){
|
||||
sqlite3_str_appendf(pSql, " ORDER BY name");
|
||||
}
|
||||
zSql = sqlite3_str_finish(pSql);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
@ -651,13 +757,21 @@ static int statColumn(
|
||||
sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 1: /* path */
|
||||
sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT);
|
||||
if( !pCsr->isAgg ){
|
||||
sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
case 2: /* pageno */
|
||||
sqlite3_result_int64(ctx, pCsr->iPageno);
|
||||
if( pCsr->isAgg ){
|
||||
sqlite3_result_int64(ctx, pCsr->nPage);
|
||||
}else{
|
||||
sqlite3_result_int64(ctx, pCsr->iPageno);
|
||||
}
|
||||
break;
|
||||
case 3: /* pagetype */
|
||||
sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC);
|
||||
if( !pCsr->isAgg ){
|
||||
sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC);
|
||||
}
|
||||
break;
|
||||
case 4: /* ncell */
|
||||
sqlite3_result_int(ctx, pCsr->nCell);
|
||||
@ -672,17 +786,23 @@ static int statColumn(
|
||||
sqlite3_result_int(ctx, pCsr->nMxPayload);
|
||||
break;
|
||||
case 8: /* pgoffset */
|
||||
sqlite3_result_int64(ctx, pCsr->iOffset);
|
||||
if( !pCsr->isAgg ){
|
||||
sqlite3_result_int64(ctx, pCsr->iOffset);
|
||||
}
|
||||
break;
|
||||
case 9: /* pgsize */
|
||||
sqlite3_result_int(ctx, pCsr->szPage);
|
||||
break;
|
||||
default: { /* schema */
|
||||
case 10: { /* schema */
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
int iDb = pCsr->iDb;
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
default: { /* aggregate */
|
||||
sqlite3_result_int(ctx, pCsr->isAgg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
11
src/delete.c
11
src/delete.c
@ -70,11 +70,7 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){
|
||||
return sqlite3WritableSchema(db)==0 && pParse->nested==0;
|
||||
}
|
||||
assert( pTab->tabFlags & TF_Shadow );
|
||||
return (db->flags & SQLITE_Defensive)!=0
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
&& db->pVtabCtx==0
|
||||
#endif
|
||||
&& db->nVdbeExec==0;
|
||||
return sqlite3ReadOnlyShadowTables(db);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -737,7 +733,8 @@ void sqlite3GenerateRowDelete(
|
||||
testcase( mask!=0xffffffff && iCol==31 );
|
||||
testcase( mask!=0xffffffff && iCol==32 );
|
||||
if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
|
||||
int kk = sqlite3TableColumnToStorage(pTab, iCol);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+kk+1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,6 +914,8 @@ int sqlite3GenerateIndexKey(
|
||||
sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
|
||||
SQLITE_JUMPIFNULL);
|
||||
pParse->iSelfTab = 0;
|
||||
pPrior = 0; /* Ticket a9efb42811fa41ee 2019-11-02;
|
||||
** pPartIdxWhere may have corrupted regPrior registers */
|
||||
}else{
|
||||
*piPartIdxLabel = 0;
|
||||
}
|
||||
|
||||
666
src/expr.c
666
src/expr.c
File diff suppressed because it is too large
Load Diff
36
src/fkey.c
36
src/fkey.c
@ -349,7 +349,7 @@ static void fkLookupParent(
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
for(i=0; i<pFKey->nCol; i++){
|
||||
int iReg = aiCol[i] + regData + 1;
|
||||
int iReg = sqlite3TableColumnToStorage(pFKey->pFrom,aiCol[i]) + regData + 1;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
@ -365,7 +365,8 @@ static void fkLookupParent(
|
||||
** is no matching parent key. Before using MustBeInt, make a copy of
|
||||
** the value. Otherwise, the value inserted into the child key column
|
||||
** will have INTEGER affinity applied to it, which may not be correct. */
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy,
|
||||
sqlite3TableColumnToStorage(pFKey->pFrom,aiCol[0])+1+regData, regTemp);
|
||||
iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
|
||||
VdbeCoverage(v);
|
||||
|
||||
@ -392,7 +393,9 @@ static void fkLookupParent(
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy,
|
||||
sqlite3TableColumnToStorage(pFKey->pFrom, aiCol[i])+1+regData,
|
||||
regTemp+i);
|
||||
}
|
||||
|
||||
/* If the parent table is the same as the child table, and we are about
|
||||
@ -408,8 +411,11 @@ static void fkLookupParent(
|
||||
if( pTab==pFKey->pFrom && nIncr==1 ){
|
||||
int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
|
||||
for(i=0; i<nCol; i++){
|
||||
int iChild = aiCol[i]+1+regData;
|
||||
int iParent = pIdx->aiColumn[i]+1+regData;
|
||||
int iChild = sqlite3TableColumnToStorage(pFKey->pFrom,aiCol[i])
|
||||
+1+regData;
|
||||
int iParent = 1+regData;
|
||||
iParent += sqlite3TableColumnToStorage(pIdx->pTable,
|
||||
pIdx->aiColumn[i]);
|
||||
assert( pIdx->aiColumn[i]>=0 );
|
||||
assert( aiCol[i]!=pTab->iPKey );
|
||||
if( pIdx->aiColumn[i]==pTab->iPKey ){
|
||||
@ -477,7 +483,7 @@ static Expr *exprTableRegister(
|
||||
if( pExpr ){
|
||||
if( iCol>=0 && iCol!=pTab->iPKey ){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pExpr->iTable = regBase + iCol + 1;
|
||||
pExpr->iTable = regBase + sqlite3TableColumnToStorage(pTab,iCol) + 1;
|
||||
pExpr->affExpr = pCol->affinity;
|
||||
zColl = pCol->zColl;
|
||||
if( zColl==0 ) zColl = db->pDfltColl->zName;
|
||||
@ -926,7 +932,9 @@ void sqlite3FkCheck(
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
|
||||
for(i=0; i<pFKey->nCol; i++){
|
||||
int iReg = pFKey->aCol[i].iFrom + regOld + 1;
|
||||
int iFromCol, iReg;
|
||||
iFromCol = pFKey->aCol[i].iFrom;
|
||||
iReg = sqlite3TableColumnToStorage(pFKey->pFrom,iFromCol) + regOld+1;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1);
|
||||
@ -1261,7 +1269,15 @@ static Trigger *fkActionTrigger(
|
||||
sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0));
|
||||
}else if( action==OE_SetDflt ){
|
||||
Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt;
|
||||
Column *pCol = pFKey->pFrom->aCol + iFromCol;
|
||||
Expr *pDflt;
|
||||
if( pCol->colFlags & COLFLAG_GENERATED ){
|
||||
testcase( pCol->colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pCol->colFlags & COLFLAG_STORED );
|
||||
pDflt = 0;
|
||||
}else{
|
||||
pDflt = pCol->pDflt;
|
||||
}
|
||||
if( pDflt ){
|
||||
pNew = sqlite3ExprDup(db, pDflt, 0);
|
||||
}else{
|
||||
@ -1299,7 +1315,7 @@ static Trigger *fkActionTrigger(
|
||||
}
|
||||
|
||||
/* Disable lookaside memory allocation */
|
||||
db->lookaside.bDisable++;
|
||||
DisableLookaside;
|
||||
|
||||
pTrigger = (Trigger *)sqlite3DbMallocZero(db,
|
||||
sizeof(Trigger) + /* struct Trigger */
|
||||
@ -1321,7 +1337,7 @@ static Trigger *fkActionTrigger(
|
||||
}
|
||||
|
||||
/* Re-enable the lookaside buffer, if it was disabled earlier. */
|
||||
db->lookaside.bDisable--;
|
||||
EnableLookaside;
|
||||
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
sqlite3ExprDelete(db, pWhen);
|
||||
|
||||
27
src/func.c
27
src/func.c
@ -16,7 +16,9 @@
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
#include <math.h>
|
||||
#endif
|
||||
#include "vdbeInt.h"
|
||||
|
||||
/*
|
||||
@ -1920,12 +1922,20 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
** For peak efficiency, put the most frequently used function last.
|
||||
*/
|
||||
static FuncDef aBuiltinFunc[] = {
|
||||
/***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/
|
||||
TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0),
|
||||
TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0),
|
||||
TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0),
|
||||
#ifdef SQLITE_DEBUG
|
||||
TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0),
|
||||
#endif
|
||||
/***** Regular functions *****/
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
VFUNCTION(load_extension, 1, 0, 0, loadExt ),
|
||||
VFUNCTION(load_extension, 2, 0, 0, loadExt ),
|
||||
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
|
||||
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
|
||||
#endif
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
|
||||
@ -1934,12 +1944,9 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
|
||||
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
|
||||
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
|
||||
FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
#ifdef SQLITE_DEBUG
|
||||
FUNCTION2(affinity, 1, 0, 0, noopFunc, SQLITE_FUNC_AFFINITY),
|
||||
#endif
|
||||
INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
||||
INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
||||
INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
FUNCTION2(sqlite_offset, 1, 0, 0, noopFunc, SQLITE_FUNC_OFFSET|
|
||||
SQLITE_FUNC_TYPEOF),
|
||||
@ -1972,7 +1979,7 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION(upper, 1, 0, 0, upperFunc ),
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE),
|
||||
VFUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
@ -2012,7 +2019,7 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
#endif
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE),
|
||||
};
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
|
||||
14
src/global.c
14
src/global.c
@ -87,7 +87,6 @@ const unsigned char sqlite3UpperToLower[] = {
|
||||
** non-ASCII UTF character. Hence the test for whether or not a character is
|
||||
** part of an identifier is 0x46.
|
||||
*/
|
||||
#ifdef SQLITE_ASCII
|
||||
const unsigned char sqlite3CtypeMap[256] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
|
||||
@ -125,7 +124,6 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
|
||||
};
|
||||
#endif
|
||||
|
||||
/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards
|
||||
** compatibility for legacy applications, the URI filename capability is
|
||||
@ -190,9 +188,18 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE)
|
||||
** or at run-time for an individual database connection using
|
||||
** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE);
|
||||
**
|
||||
** With the two-size-lookaside enhancement, less lookaside is required.
|
||||
** The default configuration of 1200,40 actually provides 30 1200-byte slots
|
||||
** and 93 128-byte slots, which is more lookaside than is available
|
||||
** using the older 1200,100 configuration without two-size-lookaside.
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_LOOKASIDE
|
||||
# define SQLITE_DEFAULT_LOOKASIDE 1200,100
|
||||
# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */
|
||||
# else
|
||||
# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -258,7 +265,6 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* xTestCallback */
|
||||
#endif
|
||||
0, /* bLocaltimeFault */
|
||||
0, /* bInternalFunctions */
|
||||
0x7ffffffe, /* iOnceResetThreshold */
|
||||
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
|
||||
0, /* iPrngSeed */
|
||||
|
||||
24
src/hwtime.h
24
src/hwtime.h
@ -11,7 +11,7 @@
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains inline asm code for retrieving "high-performance"
|
||||
** counters for x86 class CPUs.
|
||||
** counters for x86 and x86_64 class CPUs.
|
||||
*/
|
||||
#ifndef SQLITE_HWTIME_H
|
||||
#define SQLITE_HWTIME_H
|
||||
@ -22,8 +22,9 @@
|
||||
** processor and returns that value. This can be used for high-res
|
||||
** profiling.
|
||||
*/
|
||||
#if (defined(__GNUC__) || defined(_MSC_VER)) && \
|
||||
(defined(i386) || defined(__i386__) || defined(_M_IX86))
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(defined(__GNUC__) || defined(_MSC_VER)) && \
|
||||
(defined(i386) || defined(__i386__) || defined(_M_IX86))
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
@ -44,7 +45,7 @@
|
||||
|
||||
#endif
|
||||
|
||||
#elif (defined(__GNUC__) && defined(__x86_64__))
|
||||
#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
|
||||
|
||||
__inline__ sqlite_uint64 sqlite3Hwtime(void){
|
||||
unsigned long val;
|
||||
@ -52,7 +53,7 @@
|
||||
return val;
|
||||
}
|
||||
|
||||
#elif (defined(__GNUC__) && defined(__ppc__))
|
||||
#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))
|
||||
|
||||
__inline__ sqlite_uint64 sqlite3Hwtime(void){
|
||||
unsigned long long retval;
|
||||
@ -69,14 +70,13 @@
|
||||
|
||||
#else
|
||||
|
||||
#error Need implementation of sqlite3Hwtime() for your platform.
|
||||
|
||||
/*
|
||||
** To compile without implementing sqlite3Hwtime() for your platform,
|
||||
** you can remove the above #error and use the following
|
||||
** stub function. You will lose timing support for many
|
||||
** of the debugging and testing utilities, but it should at
|
||||
** least compile and run.
|
||||
** asm() is needed for hardware timing support. Without asm(),
|
||||
** disable the sqlite3Hwtime() routine.
|
||||
**
|
||||
** sqlite3Hwtime() is only used for some obscure debugging
|
||||
** and analysis configurations, not in any deliverable, so this
|
||||
** should not be a great loss.
|
||||
*/
|
||||
sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
|
||||
|
||||
|
||||
713
src/insert.c
713
src/insert.c
@ -37,7 +37,7 @@ void sqlite3OpenTable(
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum,
|
||||
(opcode==OP_OpenWrite)?1:0, pTab->zName);
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nCol);
|
||||
sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nNVCol);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
@ -129,7 +129,7 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
|
||||
** 'E' REAL
|
||||
*/
|
||||
void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
|
||||
int i;
|
||||
int i, j;
|
||||
char *zColAff = pTab->zColAff;
|
||||
if( zColAff==0 ){
|
||||
sqlite3 *db = sqlite3VdbeDb(v);
|
||||
@ -139,13 +139,15 @@ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
|
||||
return;
|
||||
}
|
||||
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
for(i=j=0; i<pTab->nCol; i++){
|
||||
assert( pTab->aCol[i].affinity!=0 );
|
||||
zColAff[i] = pTab->aCol[i].affinity;
|
||||
if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
|
||||
zColAff[j++] = pTab->aCol[i].affinity;
|
||||
}
|
||||
}
|
||||
do{
|
||||
zColAff[i--] = 0;
|
||||
}while( i>=0 && zColAff[i]<=SQLITE_AFF_BLOB );
|
||||
zColAff[j--] = 0;
|
||||
}while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB );
|
||||
pTab->zColAff = zColAff;
|
||||
}
|
||||
assert( zColAff!=0 );
|
||||
@ -199,6 +201,119 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This walker callback will compute the union of colFlags flags for all
|
||||
** referenced columns in a CHECK constraint or generated column expression.
|
||||
*/
|
||||
static int exprColumnFlagUnion(Walker *pWalker, Expr *pExpr){
|
||||
if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 ){
|
||||
assert( pExpr->iColumn < pWalker->u.pTab->nCol );
|
||||
pWalker->eCode |= pWalker->u.pTab->aCol[pExpr->iColumn].colFlags;
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
/*
|
||||
** All regular columns for table pTab have been puts into registers
|
||||
** starting with iRegStore. The registers that correspond to STORED
|
||||
** or VIRTUAL columns have not yet been initialized. This routine goes
|
||||
** back and computes the values for those columns based on the previously
|
||||
** computed normal columns.
|
||||
*/
|
||||
void sqlite3ComputeGeneratedColumns(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int iRegStore, /* Register holding the first column */
|
||||
Table *pTab /* The table */
|
||||
){
|
||||
int i;
|
||||
Walker w;
|
||||
Column *pRedo;
|
||||
int eProgress;
|
||||
VdbeOp *pOp;
|
||||
|
||||
assert( pTab->tabFlags & TF_HasGenerated );
|
||||
testcase( pTab->tabFlags & TF_HasVirtual );
|
||||
testcase( pTab->tabFlags & TF_HasStored );
|
||||
|
||||
/* Before computing generated columns, first go through and make sure
|
||||
** that appropriate affinity has been applied to the regular columns
|
||||
*/
|
||||
sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore);
|
||||
if( (pTab->tabFlags & TF_HasStored)!=0
|
||||
&& (pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1))->opcode==OP_Affinity
|
||||
){
|
||||
/* Change the OP_Affinity argument to '@' (NONE) for all stored
|
||||
** columns. '@' is the no-op affinity and those columns have not
|
||||
** yet been computed. */
|
||||
int ii, jj;
|
||||
char *zP4 = pOp->p4.z;
|
||||
assert( zP4!=0 );
|
||||
assert( pOp->p4type==P4_DYNAMIC );
|
||||
for(ii=jj=0; zP4[jj]; ii++){
|
||||
if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){
|
||||
continue;
|
||||
}
|
||||
if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){
|
||||
zP4[jj] = SQLITE_AFF_NONE;
|
||||
}
|
||||
jj++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Because there can be multiple generated columns that refer to one another,
|
||||
** this is a two-pass algorithm. On the first pass, mark all generated
|
||||
** columns as "not available".
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
|
||||
testcase( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pTab->aCol[i].colFlags & COLFLAG_STORED );
|
||||
pTab->aCol[i].colFlags |= COLFLAG_NOTAVAIL;
|
||||
}
|
||||
}
|
||||
|
||||
w.u.pTab = pTab;
|
||||
w.xExprCallback = exprColumnFlagUnion;
|
||||
w.xSelectCallback = 0;
|
||||
w.xSelectCallback2 = 0;
|
||||
|
||||
/* On the second pass, compute the value of each NOT-AVAILABLE column.
|
||||
** Companion code in the TK_COLUMN case of sqlite3ExprCodeTarget() will
|
||||
** compute dependencies and mark remove the COLSPAN_NOTAVAIL mark, as
|
||||
** they are needed.
|
||||
*/
|
||||
pParse->iSelfTab = -iRegStore;
|
||||
do{
|
||||
eProgress = 0;
|
||||
pRedo = 0;
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
Column *pCol = pTab->aCol + i;
|
||||
if( (pCol->colFlags & COLFLAG_NOTAVAIL)!=0 ){
|
||||
int x;
|
||||
pCol->colFlags |= COLFLAG_BUSY;
|
||||
w.eCode = 0;
|
||||
sqlite3WalkExpr(&w, pCol->pDflt);
|
||||
pCol->colFlags &= ~COLFLAG_BUSY;
|
||||
if( w.eCode & COLFLAG_NOTAVAIL ){
|
||||
pRedo = pCol;
|
||||
continue;
|
||||
}
|
||||
eProgress = 1;
|
||||
assert( pCol->colFlags & COLFLAG_GENERATED );
|
||||
x = sqlite3TableColumnToStorage(pTab, i) + iRegStore;
|
||||
sqlite3ExprCodeGeneratedColumn(pParse, pCol, x);
|
||||
pCol->colFlags &= ~COLFLAG_NOTAVAIL;
|
||||
}
|
||||
}
|
||||
}while( pRedo && eProgress );
|
||||
if( pRedo ){
|
||||
sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zName);
|
||||
}
|
||||
pParse->iSelfTab = 0;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
/*
|
||||
** Locate or create an AutoincInfo structure associated with table pTab
|
||||
@ -506,7 +621,7 @@ void sqlite3Insert(
|
||||
Parse *pParse, /* Parser context */
|
||||
SrcList *pTabList, /* Name of table into which we are inserting */
|
||||
Select *pSelect, /* A SELECT statement to use as the data source */
|
||||
IdList *pColumn, /* Column names corresponding to IDLIST. */
|
||||
IdList *pColumn, /* Column names corresponding to IDLIST, or NULL. */
|
||||
int onError, /* How to handle constraint errors */
|
||||
Upsert *pUpsert /* ON CONFLICT clauses for upsert, or NULL */
|
||||
){
|
||||
@ -531,6 +646,7 @@ void sqlite3Insert(
|
||||
u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
|
||||
u8 bIdListInOrder; /* True if IDLIST is in table order */
|
||||
ExprList *pList = 0; /* List of VALUES() to be inserted */
|
||||
int iRegStore; /* Register in which to store next column */
|
||||
|
||||
/* Register allocations */
|
||||
int regFromSelect = 0;/* Base register for data coming from SELECT */
|
||||
@ -638,8 +754,8 @@ void sqlite3Insert(
|
||||
*/
|
||||
regAutoinc = autoIncBegin(pParse, iDb, pTab);
|
||||
|
||||
/* Allocate registers for holding the rowid of the new row,
|
||||
** the content of the new row, and the assembled row record.
|
||||
/* Allocate a block registers to hold the rowid and the values
|
||||
** for all columns of the new row.
|
||||
*/
|
||||
regRowid = regIns = pParse->nMem+1;
|
||||
pParse->nMem += pTab->nCol + 1;
|
||||
@ -658,9 +774,17 @@ void sqlite3Insert(
|
||||
** the index into IDLIST of the primary key column. ipkColumn is
|
||||
** the index of the primary key as it appears in IDLIST, not as
|
||||
** is appears in the original table. (The index of the INTEGER
|
||||
** PRIMARY KEY in the original table is pTab->iPKey.)
|
||||
** PRIMARY KEY in the original table is pTab->iPKey.) After this
|
||||
** loop, if ipkColumn==(-1), that means that integer primary key
|
||||
** is unspecified, and hence the table is either WITHOUT ROWID or
|
||||
** it will automatically generated an integer primary key.
|
||||
**
|
||||
** bIdListInOrder is true if the columns in IDLIST are in storage
|
||||
** order. This enables an optimization that avoids shuffling the
|
||||
** columns into storage order. False negatives are harmless,
|
||||
** but false positives will cause database corruption.
|
||||
*/
|
||||
bIdListInOrder = (pTab->tabFlags & TF_OOOHidden)==0;
|
||||
bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0;
|
||||
if( pColumn ){
|
||||
for(i=0; i<pColumn->nId; i++){
|
||||
pColumn->a[i].idx = -1;
|
||||
@ -673,6 +797,14 @@ void sqlite3Insert(
|
||||
if( j==pTab->iPKey ){
|
||||
ipkColumn = i; assert( !withoutRowid );
|
||||
}
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"cannot INSERT into generated column \"%s\"",
|
||||
pTab->aCol[j].zName);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -782,13 +914,26 @@ void sqlite3Insert(
|
||||
*/
|
||||
if( pColumn==0 && nColumn>0 ){
|
||||
ipkColumn = pTab->iPKey;
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
if( ipkColumn>=0 && (pTab->tabFlags & TF_HasGenerated)!=0 ){
|
||||
testcase( pTab->tabFlags & TF_HasVirtual );
|
||||
testcase( pTab->tabFlags & TF_HasStored );
|
||||
for(i=ipkColumn-1; i>=0; i--){
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
|
||||
testcase( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pTab->aCol[i].colFlags & COLFLAG_STORED );
|
||||
ipkColumn--;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Make sure the number of columns in the source data matches the number
|
||||
** of columns to be inserted into the table.
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0);
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
|
||||
}
|
||||
if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
@ -834,6 +979,10 @@ void sqlite3Insert(
|
||||
pTab->zName);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( pTab->pSelect ){
|
||||
sqlite3ErrorMsg(pParse, "cannot UPSERT a view");
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
@ -871,10 +1020,91 @@ void sqlite3Insert(
|
||||
** goto C
|
||||
** D: ...
|
||||
*/
|
||||
sqlite3VdbeReleaseRegisters(pParse, regData, pTab->nCol, 0, 0);
|
||||
addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
|
||||
VdbeCoverage(v);
|
||||
if( ipkColumn>=0 ){
|
||||
/* tag-20191021-001: If the INTEGER PRIMARY KEY is being generated by the
|
||||
** SELECT, go ahead and copy the value into the rowid slot now, so that
|
||||
** the value does not get overwritten by a NULL at tag-20191021-002. */
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute data for ordinary columns of the new entry. Values
|
||||
** are written in storage order into registers starting with regData.
|
||||
** Only ordinary columns are computed in this loop. The rowid
|
||||
** (if there is one) is computed later and generated columns are
|
||||
** computed after the rowid since they might depend on the value
|
||||
** of the rowid.
|
||||
*/
|
||||
nHidden = 0;
|
||||
iRegStore = regData; assert( regData==regRowid+1 );
|
||||
for(i=0; i<pTab->nCol; i++, iRegStore++){
|
||||
int k;
|
||||
u32 colFlags;
|
||||
assert( i>=nHidden );
|
||||
if( i==pTab->iPKey ){
|
||||
/* tag-20191021-002: References to the INTEGER PRIMARY KEY are filled
|
||||
** using the rowid. So put a NULL in the IPK slot of the record to avoid
|
||||
** using excess space. The file format definition requires this extra
|
||||
** NULL - we cannot optimize further by skipping the column completely */
|
||||
sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
|
||||
continue;
|
||||
}
|
||||
if( ((colFlags = pTab->aCol[i].colFlags) & COLFLAG_NOINSERT)!=0 ){
|
||||
nHidden++;
|
||||
if( (colFlags & COLFLAG_VIRTUAL)!=0 ){
|
||||
/* Virtual columns do not participate in OP_MakeRecord. So back up
|
||||
** iRegStore by one slot to compensate for the iRegStore++ in the
|
||||
** outer for() loop */
|
||||
iRegStore--;
|
||||
continue;
|
||||
}else if( (colFlags & COLFLAG_STORED)!=0 ){
|
||||
/* Stored columns are computed later. But if there are BEFORE
|
||||
** triggers, the slots used for stored columns will be OP_Copy-ed
|
||||
** to a second block of registers, so the register needs to be
|
||||
** initialized to NULL to avoid an uninitialized register read */
|
||||
if( tmask & TRIGGER_BEFORE ){
|
||||
sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
|
||||
}
|
||||
continue;
|
||||
}else if( pColumn==0 ){
|
||||
/* Hidden columns that are not explicitly named in the INSERT
|
||||
** get there default value */
|
||||
sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if( pColumn ){
|
||||
for(j=0; j<pColumn->nId && pColumn->a[j].idx!=i; j++){}
|
||||
if( j>=pColumn->nId ){
|
||||
/* A column not named in the insert column list gets its
|
||||
** default value */
|
||||
sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
|
||||
continue;
|
||||
}
|
||||
k = j;
|
||||
}else if( nColumn==0 ){
|
||||
/* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */
|
||||
sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
|
||||
continue;
|
||||
}else{
|
||||
k = i - nHidden;
|
||||
}
|
||||
|
||||
if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, k, iRegStore);
|
||||
}else if( pSelect ){
|
||||
if( regFromSelect!=regData ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+k, iRegStore);
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pList->a[k].pExpr, iRegStore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Run the BEFORE and INSTEAD OF triggers, if there are any
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(pParse);
|
||||
@ -909,25 +1139,21 @@ void sqlite3Insert(
|
||||
*/
|
||||
assert( !IsVirtual(pTab) );
|
||||
|
||||
/* Create the new column data
|
||||
*/
|
||||
for(i=j=0; i<pTab->nCol; i++){
|
||||
if( pColumn ){
|
||||
for(j=0; j<pColumn->nId; j++){
|
||||
if( pColumn->a[j].idx==i ) break;
|
||||
}
|
||||
}
|
||||
if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId)
|
||||
|| (pColumn==0 && IsOrdinaryHiddenColumn(&pTab->aCol[i])) ){
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1);
|
||||
}else if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1);
|
||||
}else{
|
||||
assert( pSelect==0 ); /* Otherwise useTempTable is true */
|
||||
sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1);
|
||||
}
|
||||
if( pColumn==0 && !IsOrdinaryHiddenColumn(&pTab->aCol[i]) ) j++;
|
||||
/* Copy the new data already generated. */
|
||||
assert( pTab->nNVCol>0 );
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1);
|
||||
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
/* Compute the new value for generated columns after all other
|
||||
** columns have already been computed. This must be done after
|
||||
** computing the ROWID in case one of the generated columns
|
||||
** refers to the ROWID. */
|
||||
if( pTab->tabFlags & TF_HasGenerated ){
|
||||
testcase( pTab->tabFlags & TF_HasVirtual );
|
||||
testcase( pTab->tabFlags & TF_HasStored );
|
||||
sqlite3ComputeGeneratedColumns(pParse, regCols+1, pTab);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
|
||||
** do not attempt any conversions before assembling the record.
|
||||
@ -945,19 +1171,17 @@ void sqlite3Insert(
|
||||
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1);
|
||||
}
|
||||
|
||||
/* Compute the content of the next row to insert into a range of
|
||||
** registers beginning at regIns.
|
||||
*/
|
||||
if( !isView ){
|
||||
if( IsVirtual(pTab) ){
|
||||
/* The row that the VUpdate opcode will delete: none */
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regIns);
|
||||
}
|
||||
if( ipkColumn>=0 ){
|
||||
/* Compute the new rowid */
|
||||
if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid);
|
||||
}else if( pSelect ){
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
|
||||
/* Rowid already initialized at tag-20191021-001 */
|
||||
}else{
|
||||
Expr *pIpk = pList->a[ipkColumn].pExpr;
|
||||
if( pIpk->op==TK_NULL && !IsVirtual(pTab) ){
|
||||
@ -990,45 +1214,15 @@ void sqlite3Insert(
|
||||
}
|
||||
autoIncStep(pParse, regAutoinc, regRowid);
|
||||
|
||||
/* Compute data for all columns of the new entry, beginning
|
||||
** with the first column.
|
||||
*/
|
||||
nHidden = 0;
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
int iRegStore = regRowid+1+i;
|
||||
if( i==pTab->iPKey ){
|
||||
/* The value of the INTEGER PRIMARY KEY column is always a NULL.
|
||||
** Whenever this column is read, the rowid will be substituted
|
||||
** in its place. Hence, fill this column with a NULL to avoid
|
||||
** taking up data space with information that will never be used.
|
||||
** As there may be shallow copies of this value, make it a soft-NULL */
|
||||
sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
|
||||
continue;
|
||||
}
|
||||
if( pColumn==0 ){
|
||||
if( IsHiddenColumn(&pTab->aCol[i]) ){
|
||||
j = -1;
|
||||
nHidden++;
|
||||
}else{
|
||||
j = i - nHidden;
|
||||
}
|
||||
}else{
|
||||
for(j=0; j<pColumn->nId; j++){
|
||||
if( pColumn->a[j].idx==i ) break;
|
||||
}
|
||||
}
|
||||
if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){
|
||||
sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
|
||||
}else if( useTempTable ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore);
|
||||
}else if( pSelect ){
|
||||
if( regFromSelect!=regData ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
/* Compute the new value for generated columns after all other
|
||||
** columns have already been computed. This must be done after
|
||||
** computing the ROWID in case one of the generated columns
|
||||
** is derived from the INTEGER PRIMARY KEY. */
|
||||
if( pTab->tabFlags & TF_HasGenerated ){
|
||||
sqlite3ComputeGeneratedColumns(pParse, regRowid+1, pTab);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code to check constraints and generate index keys and
|
||||
** do the insertion.
|
||||
@ -1058,9 +1252,7 @@ void sqlite3Insert(
|
||||
** cursor that is disturbed. And these instructions both clear the
|
||||
** VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT
|
||||
** functionality. */
|
||||
bUseSeek = (isReplace==0 || (pTrigger==0 &&
|
||||
((db->flags & SQLITE_ForeignKeys)==0 || sqlite3FkReferences(pTab)==0)
|
||||
));
|
||||
bUseSeek = (isReplace==0 || !sqlite3VdbeHasSubProgram(v));
|
||||
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
|
||||
regIns, aRegIdx, 0, appendFlag, bUseSeek
|
||||
);
|
||||
@ -1089,6 +1281,15 @@ void sqlite3Insert(
|
||||
sqlite3VdbeAddOp1(v, OP_Close, srcTab);
|
||||
}else if( pSelect ){
|
||||
sqlite3VdbeGoto(v, addrCont);
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* If we are jumping back to an OP_Yield that is preceded by an
|
||||
** OP_ReleaseReg, set the p5 flag on the OP_Goto so that the
|
||||
** OP_ReleaseReg will be included in the loop. */
|
||||
if( sqlite3VdbeGetOp(v, addrCont-1)->opcode==OP_ReleaseReg ){
|
||||
assert( sqlite3VdbeGetOp(v, addrCont)->opcode==OP_Yield );
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
}
|
||||
#endif
|
||||
sqlite3VdbeJumpHere(v, addrInsTop);
|
||||
}
|
||||
|
||||
@ -1311,7 +1512,6 @@ void sqlite3GenerateConstraintChecks(
|
||||
int ix; /* Index loop counter */
|
||||
int nCol; /* Number of columns */
|
||||
int onError; /* Conflict resolution strategy */
|
||||
int addr1; /* Address of jump instruction */
|
||||
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
|
||||
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
|
||||
Index *pUpIdx = 0; /* Index to which to apply the upsert */
|
||||
@ -1321,6 +1521,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */
|
||||
int ipkTop = 0; /* Top of the IPK uniqueness check */
|
||||
int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */
|
||||
/* Variables associated with retesting uniqueness constraints after
|
||||
** replace triggers fire have run */
|
||||
int regTrigCnt; /* Register used to count replace trigger invocations */
|
||||
int addrRecheck = 0; /* Jump here to recheck all uniqueness constraints */
|
||||
int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
|
||||
Trigger *pTrigger; /* List of DELETE triggers on the table pTab */
|
||||
int nReplaceTrig = 0; /* Number of replace triggers coded */
|
||||
|
||||
isUpdate = regOldData!=0;
|
||||
db = pParse->db;
|
||||
@ -1347,63 +1554,103 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
/* Test all NOT NULL constraints.
|
||||
*/
|
||||
for(i=0; i<nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
continue; /* ROWID is never NULL */
|
||||
}
|
||||
if( aiChng && aiChng[i]<0 ){
|
||||
/* Don't bother checking for NOT NULL on columns that do not change */
|
||||
continue;
|
||||
}
|
||||
onError = pTab->aCol[i].notNull;
|
||||
if( onError==OE_None ) continue; /* This column is allowed to be NULL */
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
|| onError==OE_Ignore || onError==OE_Replace );
|
||||
addr1 = 0;
|
||||
switch( onError ){
|
||||
case OE_Replace: {
|
||||
assert( onError==OE_Replace );
|
||||
addr1 = sqlite3VdbeMakeLabel(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_NotNull, regNewData+1+i, addr1);
|
||||
VdbeCoverage(v);
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
|
||||
sqlite3VdbeAddOp2(v, OP_NotNull, regNewData+1+i, addr1);
|
||||
VdbeCoverage(v);
|
||||
onError = OE_Abort;
|
||||
/* Fall through into the OE_Abort case to generate code that runs
|
||||
** if both the input and the default value are NULL */
|
||||
}
|
||||
case OE_Abort:
|
||||
sqlite3MayAbort(pParse);
|
||||
/* Fall through */
|
||||
case OE_Rollback:
|
||||
case OE_Fail: {
|
||||
char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
|
||||
pTab->aCol[i].zName);
|
||||
sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError,
|
||||
regNewData+1+i);
|
||||
sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
|
||||
sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
|
||||
VdbeCoverage(v);
|
||||
if( addr1 ) sqlite3VdbeResolveLabel(v, addr1);
|
||||
if( pTab->tabFlags & TF_HasNotNull ){
|
||||
int b2ndPass = 0; /* True if currently running 2nd pass */
|
||||
int nSeenReplace = 0; /* Number of ON CONFLICT REPLACE operations */
|
||||
int nGenerated = 0; /* Number of generated columns with NOT NULL */
|
||||
while(1){ /* Make 2 passes over columns. Exit loop via "break" */
|
||||
for(i=0; i<nCol; i++){
|
||||
int iReg; /* Register holding column value */
|
||||
Column *pCol = &pTab->aCol[i]; /* The column to check for NOT NULL */
|
||||
int isGenerated; /* non-zero if column is generated */
|
||||
onError = pCol->notNull;
|
||||
if( onError==OE_None ) continue; /* No NOT NULL on this column */
|
||||
if( i==pTab->iPKey ){
|
||||
continue; /* ROWID is never NULL */
|
||||
}
|
||||
isGenerated = pCol->colFlags & COLFLAG_GENERATED;
|
||||
if( isGenerated && !b2ndPass ){
|
||||
nGenerated++;
|
||||
continue; /* Generated columns processed on 2nd pass */
|
||||
}
|
||||
if( aiChng && aiChng[i]<0 && !isGenerated ){
|
||||
/* Do not check NOT NULL on columns that do not change */
|
||||
continue;
|
||||
}
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( onError==OE_Replace ){
|
||||
if( b2ndPass /* REPLACE becomes ABORT on the 2nd pass */
|
||||
|| pCol->pDflt==0 /* REPLACE is ABORT if no DEFAULT value */
|
||||
){
|
||||
testcase( pCol->colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pCol->colFlags & COLFLAG_STORED );
|
||||
testcase( pCol->colFlags & COLFLAG_GENERATED );
|
||||
onError = OE_Abort;
|
||||
}else{
|
||||
assert( !isGenerated );
|
||||
}
|
||||
}else if( b2ndPass && !isGenerated ){
|
||||
continue;
|
||||
}
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
|| onError==OE_Ignore || onError==OE_Replace );
|
||||
testcase( i!=sqlite3TableColumnToStorage(pTab, i) );
|
||||
iReg = sqlite3TableColumnToStorage(pTab, i) + regNewData + 1;
|
||||
switch( onError ){
|
||||
case OE_Replace: {
|
||||
int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, iReg);
|
||||
VdbeCoverage(v);
|
||||
assert( (pCol->colFlags & COLFLAG_GENERATED)==0 );
|
||||
nSeenReplace++;
|
||||
sqlite3ExprCode(pParse, pCol->pDflt, iReg);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
break;
|
||||
}
|
||||
case OE_Abort:
|
||||
sqlite3MayAbort(pParse);
|
||||
/* Fall through */
|
||||
case OE_Rollback:
|
||||
case OE_Fail: {
|
||||
char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
|
||||
pCol->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL,
|
||||
onError, iReg);
|
||||
sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
|
||||
sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
|
||||
VdbeCoverage(v);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( onError==OE_Ignore );
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, ignoreDest);
|
||||
VdbeCoverage(v);
|
||||
break;
|
||||
}
|
||||
} /* end switch(onError) */
|
||||
} /* end loop i over columns */
|
||||
if( nGenerated==0 && nSeenReplace==0 ){
|
||||
/* If there are no generated columns with NOT NULL constraints
|
||||
** and no NOT NULL ON CONFLICT REPLACE constraints, then a single
|
||||
** pass is sufficient */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( onError==OE_Ignore );
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest);
|
||||
VdbeCoverage(v);
|
||||
break;
|
||||
if( b2ndPass ) break; /* Never need more than 2 passes */
|
||||
b2ndPass = 1;
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
if( nSeenReplace>0 && (pTab->tabFlags & TF_HasGenerated)!=0 ){
|
||||
/* If any NOT NULL ON CONFLICT REPLACE constraints fired on the
|
||||
** first pass, recomputed values for all generated columns, as
|
||||
** those values might depend on columns affected by the REPLACE.
|
||||
*/
|
||||
sqlite3ComputeGeneratedColumns(pParse, regNewData+1, pTab);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} /* end of 2-pass loop */
|
||||
} /* end if( has-not-null-constraints ) */
|
||||
|
||||
/* Test all CHECK constraints
|
||||
*/
|
||||
@ -1428,7 +1675,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
if( onError==OE_Ignore ){
|
||||
sqlite3VdbeGoto(v, ignoreDest);
|
||||
}else{
|
||||
char *zName = pCheck->a[i].zName;
|
||||
char *zName = pCheck->a[i].zEName;
|
||||
if( zName==0 ) zName = pTab->zName;
|
||||
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-26383-51744 */
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
|
||||
@ -1485,6 +1732,50 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if it is possible that triggers (either explicitly coded
|
||||
** triggers or FK resolution actions) might run as a result of deletes
|
||||
** that happen when OE_Replace conflict resolution occurs. (Call these
|
||||
** "replace triggers".) If any replace triggers run, we will need to
|
||||
** recheck all of the uniqueness constraints after they have all run.
|
||||
** But on the recheck, the resolution is OE_Abort instead of OE_Replace.
|
||||
**
|
||||
** If replace triggers are a possibility, then
|
||||
**
|
||||
** (1) Allocate register regTrigCnt and initialize it to zero.
|
||||
** That register will count the number of replace triggers that
|
||||
** fire. Constraint recheck only occurs if the number is positive.
|
||||
** (2) Initialize pTrigger to the list of all DELETE triggers on pTab.
|
||||
** (3) Initialize addrRecheck and lblRecheckOk
|
||||
**
|
||||
** The uniqueness rechecking code will create a series of tests to run
|
||||
** in a second pass. The addrRecheck and lblRecheckOk variables are
|
||||
** used to link together these tests which are separated from each other
|
||||
** in the generate bytecode.
|
||||
*/
|
||||
if( (db->flags & (SQLITE_RecTriggers|SQLITE_ForeignKeys))==0 ){
|
||||
/* There are not DELETE triggers nor FK constraints. No constraint
|
||||
** rechecks are needed. */
|
||||
pTrigger = 0;
|
||||
regTrigCnt = 0;
|
||||
}else{
|
||||
if( db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
regTrigCnt = pTrigger!=0 || sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||
}else{
|
||||
pTrigger = 0;
|
||||
regTrigCnt = sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||
}
|
||||
if( regTrigCnt ){
|
||||
/* Replace triggers might exist. Allocate the counter and
|
||||
** initialize it to zero. */
|
||||
regTrigCnt = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regTrigCnt);
|
||||
VdbeComment((v, "trigger count"));
|
||||
lblRecheckOk = sqlite3VdbeMakeLabel(pParse);
|
||||
addrRecheck = lblRecheckOk;
|
||||
}
|
||||
}
|
||||
|
||||
/* If rowid is changing, make sure the new rowid does not previously
|
||||
** exist in the table.
|
||||
*/
|
||||
@ -1574,14 +1865,12 @@ void sqlite3GenerateConstraintChecks(
|
||||
** to run without a statement journal if there are no indexes on the
|
||||
** table.
|
||||
*/
|
||||
Trigger *pTrigger = 0;
|
||||
if( db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
if( regTrigCnt ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regNewData, 1, 0, OE_Replace, 1, -1);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTrigCnt, 1); /* incr trigger cnt */
|
||||
nReplaceTrig++;
|
||||
}else{
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
assert( HasRowid(pTab) );
|
||||
@ -1631,6 +1920,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
int regR; /* Range of registers holding conflicting PK */
|
||||
int iThisCur; /* Cursor for this UNIQUE index */
|
||||
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
|
||||
int addrConflictCk; /* First opcode in the conflict check logic */
|
||||
|
||||
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
|
||||
if( pUpIdx==pIdx ){
|
||||
@ -1670,14 +1960,15 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
|
||||
pParse->iSelfTab = 0;
|
||||
VdbeComment((v, "%s column %d", pIdx->zName, i));
|
||||
}else if( iField==XN_ROWID || iField==pTab->iPKey ){
|
||||
x = regNewData;
|
||||
sqlite3VdbeAddOp2(v, OP_IntCopy, x, regIdx+i);
|
||||
VdbeComment((v, "rowid"));
|
||||
}else{
|
||||
if( iField==XN_ROWID || iField==pTab->iPKey ){
|
||||
x = regNewData;
|
||||
}else{
|
||||
x = iField + regNewData + 1;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, iField<0 ? OP_IntCopy : OP_SCopy, x, regIdx+i);
|
||||
VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
|
||||
testcase( sqlite3TableColumnToStorage(pTab, iField)!=iField );
|
||||
x = sqlite3TableColumnToStorage(pTab, iField) + regNewData + 1;
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
|
||||
VdbeComment((v, "%s", pTab->aCol[iField].zName));
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
|
||||
@ -1687,6 +1978,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3SetMakeRecordP5(v, pIdx->pTable);
|
||||
}
|
||||
#endif
|
||||
sqlite3VdbeReleaseRegisters(pParse, regIdx, pIdx->nColumn, 0, 0);
|
||||
|
||||
/* In an UPDATE operation, if this index is the PRIMARY KEY index
|
||||
** of a WITHOUT ROWID table and there has been no change the
|
||||
@ -1744,8 +2036,9 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
|
||||
regIdx, pIdx->nKeyCol); VdbeCoverage(v);
|
||||
addrConflictCk =
|
||||
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
|
||||
regIdx, pIdx->nKeyCol); VdbeCoverage(v);
|
||||
|
||||
/* Generate code to handle collisions */
|
||||
regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField);
|
||||
@ -1766,7 +2059,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
if( pIdx!=pPk ){
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
assert( pPk->aiColumn[i]>=0 );
|
||||
x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
|
||||
x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i);
|
||||
VdbeComment((v, "%s.%s", pTab->zName,
|
||||
pTab->aCol[pPk->aiColumn[i]].zName));
|
||||
@ -1792,6 +2085,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
addrJump = addrUniqueOk;
|
||||
op = OP_Eq;
|
||||
}
|
||||
x = sqlite3TableColumnToStorage(pTab, x);
|
||||
sqlite3VdbeAddOp4(v, op,
|
||||
regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ
|
||||
);
|
||||
@ -1828,17 +2122,71 @@ void sqlite3GenerateConstraintChecks(
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Trigger *pTrigger = 0;
|
||||
int nConflictCk; /* Number of opcodes in conflict check logic */
|
||||
|
||||
assert( onError==OE_Replace );
|
||||
if( db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
nConflictCk = sqlite3VdbeCurrentAddr(v) - addrConflictCk;
|
||||
assert( nConflictCk>0 );
|
||||
testcase( nConflictCk>1 );
|
||||
if( regTrigCnt ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
nReplaceTrig++;
|
||||
}
|
||||
if( pTrigger && isUpdate ){
|
||||
sqlite3VdbeAddOp1(v, OP_CursorLock, iDataCur);
|
||||
}
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regR, nPkField, 0, OE_Replace,
|
||||
(pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur);
|
||||
if( pTrigger && isUpdate ){
|
||||
sqlite3VdbeAddOp1(v, OP_CursorUnlock, iDataCur);
|
||||
}
|
||||
if( regTrigCnt ){
|
||||
int addrBypass; /* Jump destination to bypass recheck logic */
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTrigCnt, 1); /* incr trigger cnt */
|
||||
addrBypass = sqlite3VdbeAddOp0(v, OP_Goto); /* Bypass recheck */
|
||||
VdbeComment((v, "bypass recheck"));
|
||||
|
||||
/* Here we insert code that will be invoked after all constraint
|
||||
** checks have run, if and only if one or more replace triggers
|
||||
** fired. */
|
||||
sqlite3VdbeResolveLabel(v, lblRecheckOk);
|
||||
lblRecheckOk = sqlite3VdbeMakeLabel(pParse);
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
/* Bypass the recheck if this partial index is not defined
|
||||
** for the current row */
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regIdx-1, lblRecheckOk);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
/* Copy the constraint check code from above, except change
|
||||
** the constraint-ok jump destination to be the address of
|
||||
** the next retest block */
|
||||
while( nConflictCk>0 ){
|
||||
VdbeOp x; /* Conflict check opcode to copy */
|
||||
/* The sqlite3VdbeAddOp4() call might reallocate the opcode array.
|
||||
** Hence, make a complete copy of the opcode, rather than using
|
||||
** a pointer to the opcode. */
|
||||
x = *sqlite3VdbeGetOp(v, addrConflictCk);
|
||||
if( x.opcode!=OP_IdxRowid ){
|
||||
int p2; /* New P2 value for copied conflict check opcode */
|
||||
if( sqlite3OpcodeProperty[x.opcode]&OPFLG_JUMP ){
|
||||
p2 = lblRecheckOk;
|
||||
}else{
|
||||
p2 = x.p2;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, x.opcode, x.p1, p2, x.p3, x.p4.z, x.p4type);
|
||||
sqlite3VdbeChangeP5(v, x.p5);
|
||||
VdbeCoverageIf(v, p2!=x.p2);
|
||||
}
|
||||
nConflictCk--;
|
||||
addrConflictCk++;
|
||||
}
|
||||
/* If the retest fails, issue an abort */
|
||||
sqlite3UniqueConstraint(pParse, OE_Abort, pIdx);
|
||||
|
||||
sqlite3VdbeJumpHere(v, addrBypass); /* Terminate the recheck bypass */
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
@ -1859,10 +2207,30 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeJumpHere(v, ipkBottom);
|
||||
}
|
||||
|
||||
/* Recheck all uniqueness constraints after replace triggers have run */
|
||||
testcase( regTrigCnt!=0 && nReplaceTrig==0 );
|
||||
assert( regTrigCnt!=0 || nReplaceTrig==0 );
|
||||
if( nReplaceTrig ){
|
||||
sqlite3VdbeAddOp2(v, OP_IfNot, regTrigCnt, lblRecheckOk);VdbeCoverage(v);
|
||||
if( !pPk ){
|
||||
if( isUpdate ){
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRecheck, regOldData);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRecheck, regNewData);
|
||||
VdbeCoverage(v);
|
||||
sqlite3RowidConstraint(pParse, OE_Abort, pTab);
|
||||
}else{
|
||||
sqlite3VdbeGoto(v, addrRecheck);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, lblRecheckOk);
|
||||
}
|
||||
|
||||
/* Generate the table record */
|
||||
if( HasRowid(pTab) ){
|
||||
int regRec = aRegIdx[ix];
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData+1, pTab->nCol, regRec);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData+1, pTab->nNVCol, regRec);
|
||||
sqlite3SetMakeRecordP5(v, pTab);
|
||||
if( !bAffinityDone ){
|
||||
sqlite3TableAffinity(v, pTab, 0);
|
||||
@ -1929,6 +2297,10 @@ void sqlite3CompleteInsertion(
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
/* All REPLACE indexes are at the end of the list */
|
||||
assert( pIdx->onError!=OE_Replace
|
||||
|| pIdx->pNext==0
|
||||
|| pIdx->pNext->onError==OE_Replace );
|
||||
if( aRegIdx[i]==0 ) continue;
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
|
||||
@ -2079,7 +2451,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
|
||||
int i;
|
||||
assert( pDest && pSrc );
|
||||
assert( pDest->pTable!=pSrc->pTable );
|
||||
if( pDest->nKeyCol!=pSrc->nKeyCol ){
|
||||
if( pDest->nKeyCol!=pSrc->nKeyCol || pDest->nColumn!=pSrc->nColumn ){
|
||||
return 0; /* Different number of columns */
|
||||
}
|
||||
if( pDest->onError!=pSrc->onError ){
|
||||
@ -2256,6 +2628,39 @@ static int xferOptimization(
|
||||
){
|
||||
return 0; /* Neither table may have __hidden__ columns */
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
/* Even if tables t1 and t2 have identical schemas, if they contain
|
||||
** generated columns, then this statement is semantically incorrect:
|
||||
**
|
||||
** INSERT INTO t2 SELECT * FROM t1;
|
||||
**
|
||||
** The reason is that generated column values are returned by the
|
||||
** the SELECT statement on the right but the INSERT statement on the
|
||||
** left wants them to be omitted.
|
||||
**
|
||||
** Nevertheless, this is a useful notational shorthand to tell SQLite
|
||||
** to do a bulk transfer all of the content from t1 over to t2.
|
||||
**
|
||||
** We could, in theory, disable this (except for internal use by the
|
||||
** VACUUM command where it is actually needed). But why do that? It
|
||||
** seems harmless enough, and provides a useful service.
|
||||
*/
|
||||
if( (pDestCol->colFlags & COLFLAG_GENERATED) !=
|
||||
(pSrcCol->colFlags & COLFLAG_GENERATED) ){
|
||||
return 0; /* Both columns have the same generated-column type */
|
||||
}
|
||||
/* But the transfer is only allowed if both the source and destination
|
||||
** tables have the exact same expressions for generated columns.
|
||||
** This requirement could be relaxed for VIRTUAL columns, I suppose.
|
||||
*/
|
||||
if( (pDestCol->colFlags & COLFLAG_GENERATED)!=0 ){
|
||||
if( sqlite3ExprCompare(0, pSrcCol->pDflt, pDestCol->pDflt, -1)!=0 ){
|
||||
testcase( pDestCol->colFlags & COLFLAG_VIRTUAL );
|
||||
testcase( pDestCol->colFlags & COLFLAG_STORED );
|
||||
return 0; /* Different generator expressions */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( pDestCol->affinity!=pSrcCol->affinity ){
|
||||
return 0; /* Affinity must be the same on all columns */
|
||||
@ -2267,7 +2672,7 @@ static int xferOptimization(
|
||||
return 0; /* tab2 must be NOT NULL if tab1 is */
|
||||
}
|
||||
/* Default values for second and subsequent columns need to match. */
|
||||
if( i>0 ){
|
||||
if( (pDestCol->colFlags & COLFLAG_GENERATED)==0 && i>0 ){
|
||||
assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN );
|
||||
assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN );
|
||||
if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0)
|
||||
|
||||
@ -468,6 +468,12 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
/* Version 3.31.0 and later */
|
||||
sqlite3_hard_heap_limit64,
|
||||
sqlite3_uri_key,
|
||||
sqlite3_filename_database,
|
||||
sqlite3_filename_journal,
|
||||
sqlite3_filename_wal,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
143
src/main.c
143
src/main.c
@ -683,6 +683,9 @@ int sqlite3_config(int op, ...){
|
||||
static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
void *pStart;
|
||||
sqlite3_int64 szAlloc = sz*(sqlite3_int64)cnt;
|
||||
int nBig; /* Number of full-size slots */
|
||||
int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */
|
||||
|
||||
if( sqlite3LookasideUsed(db,0)>0 ){
|
||||
return SQLITE_BUSY;
|
||||
@ -705,37 +708,71 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
pStart = 0;
|
||||
}else if( pBuf==0 ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
pStart = sqlite3Malloc( sz*(sqlite3_int64)cnt ); /* IMP: R-61949-35727 */
|
||||
pStart = sqlite3Malloc( szAlloc ); /* IMP: R-61949-35727 */
|
||||
sqlite3EndBenignMalloc();
|
||||
if( pStart ) cnt = sqlite3MallocSize(pStart)/sz;
|
||||
if( pStart ) szAlloc = sqlite3MallocSize(pStart);
|
||||
}else{
|
||||
pStart = pBuf;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
if( sz>=LOOKASIDE_SMALL*3 ){
|
||||
nBig = szAlloc/(3*LOOKASIDE_SMALL+sz);
|
||||
nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL;
|
||||
}else if( sz>=LOOKASIDE_SMALL*2 ){
|
||||
nBig = szAlloc/(LOOKASIDE_SMALL+sz);
|
||||
nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL;
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
|
||||
if( sz>0 ){
|
||||
nBig = szAlloc/sz;
|
||||
nSm = 0;
|
||||
}else{
|
||||
nBig = nSm = 0;
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
db->lookaside.pInit = 0;
|
||||
db->lookaside.pFree = 0;
|
||||
db->lookaside.sz = (u16)sz;
|
||||
db->lookaside.szTrue = (u16)sz;
|
||||
if( pStart ){
|
||||
int i;
|
||||
LookasideSlot *p;
|
||||
assert( sz > (int)sizeof(LookasideSlot*) );
|
||||
db->lookaside.nSlot = cnt;
|
||||
p = (LookasideSlot*)pStart;
|
||||
for(i=cnt-1; i>=0; i--){
|
||||
for(i=0; i<nBig; i++){
|
||||
p->pNext = db->lookaside.pInit;
|
||||
db->lookaside.pInit = p;
|
||||
p = (LookasideSlot*)&((u8*)p)[sz];
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
db->lookaside.pSmallInit = 0;
|
||||
db->lookaside.pSmallFree = 0;
|
||||
db->lookaside.pMiddle = p;
|
||||
for(i=0; i<nSm; i++){
|
||||
p->pNext = db->lookaside.pSmallInit;
|
||||
db->lookaside.pSmallInit = p;
|
||||
p = (LookasideSlot*)&((u8*)p)[LOOKASIDE_SMALL];
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
|
||||
assert( ((uptr)p)<=szAlloc + (uptr)pStart );
|
||||
db->lookaside.pEnd = p;
|
||||
db->lookaside.bDisable = 0;
|
||||
db->lookaside.bMalloced = pBuf==0 ?1:0;
|
||||
db->lookaside.nSlot = nBig+nSm;
|
||||
}else{
|
||||
db->lookaside.pStart = db;
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
db->lookaside.pSmallInit = 0;
|
||||
db->lookaside.pSmallFree = 0;
|
||||
db->lookaside.pMiddle = db;
|
||||
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
|
||||
db->lookaside.pEnd = db;
|
||||
db->lookaside.bDisable = 1;
|
||||
db->lookaside.sz = 0;
|
||||
db->lookaside.bMalloced = 0;
|
||||
db->lookaside.nSlot = 0;
|
||||
}
|
||||
assert( sqlite3LookasideUsed(db,0)==0 );
|
||||
#endif /* SQLITE_OMIT_LOOKASIDE */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -849,6 +886,8 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
{ SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, SQLITE_LegacyAlter },
|
||||
{ SQLITE_DBCONFIG_DQS_DDL, SQLITE_DqsDDL },
|
||||
{ SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML },
|
||||
{ SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt },
|
||||
{ SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema },
|
||||
};
|
||||
unsigned int i;
|
||||
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
||||
@ -1387,6 +1426,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break;
|
||||
case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break;
|
||||
case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break;
|
||||
case SQLITE_CANTOPEN_SYMLINK: zName = "SQLITE_CANTOPEN_SYMLINK"; break;
|
||||
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
|
||||
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
|
||||
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
|
||||
@ -1719,8 +1759,15 @@ int sqlite3CreateFunc(
|
||||
|
||||
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
|
||||
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
|
||||
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|SQLITE_SUBTYPE);
|
||||
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
|
||||
SQLITE_SUBTYPE|SQLITE_INNOCUOUS);
|
||||
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
|
||||
|
||||
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
|
||||
** the meaning is inverted. So flip the bit. */
|
||||
assert( SQLITE_FUNC_UNSAFE==SQLITE_INNOCUOUS );
|
||||
extraFlags ^= SQLITE_FUNC_UNSAFE;
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/* If SQLITE_UTF16 is specified as the encoding type, transform this
|
||||
@ -1734,11 +1781,13 @@ int sqlite3CreateFunc(
|
||||
enc = SQLITE_UTF16NATIVE;
|
||||
}else if( enc==SQLITE_ANY ){
|
||||
int rc;
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg,
|
||||
(SQLITE_UTF8|extraFlags)^SQLITE_FUNC_UNSAFE,
|
||||
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
|
||||
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg,
|
||||
(SQLITE_UTF16LE|extraFlags)^SQLITE_FUNC_UNSAFE,
|
||||
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
@ -3064,6 +3113,7 @@ static int openDatabase(
|
||||
db->magic = SQLITE_MAGIC_BUSY;
|
||||
db->aDb = db->aDbStatic;
|
||||
db->lookaside.bDisable = 1;
|
||||
db->lookaside.sz = 0;
|
||||
|
||||
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
|
||||
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
|
||||
@ -3077,7 +3127,9 @@ static int openDatabase(
|
||||
| SQLITE_EnableTrigger
|
||||
| SQLITE_EnableView
|
||||
| SQLITE_CacheSpill
|
||||
|
||||
#if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0
|
||||
| SQLITE_TrustedSchema
|
||||
#endif
|
||||
/* The SQLITE_DQS compile-time option determines the default settings
|
||||
** for SQLITE_DBCONFIG_DQS_DDL and SQLITE_DBCONFIG_DQS_DML.
|
||||
**
|
||||
@ -3314,6 +3366,13 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS
|
||||
/* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time
|
||||
** option gives access to internal functions by default.
|
||||
** Testing use only!!! */
|
||||
db->mDbFlags |= DBFLAG_InternalFunc;
|
||||
#endif
|
||||
|
||||
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
|
||||
** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
|
||||
** mode. Doing nothing at all also makes NORMAL the default.
|
||||
@ -3852,6 +3911,7 @@ int sqlite3_test_control(int op, ...){
|
||||
** This test-control also resets the PRNG so that the new seed will
|
||||
** be used for the next call to sqlite3_randomness().
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
case SQLITE_TESTCTRL_PRNG_SEED: {
|
||||
int x = va_arg(ap, int);
|
||||
int y;
|
||||
@ -3862,6 +3922,7 @@ int sqlite3_test_control(int op, ...){
|
||||
sqlite3_randomness(0,0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** sqlite3_test_control(BITVEC_TEST, size, program)
|
||||
@ -4046,15 +4107,14 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff);
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, sqlite3*);
|
||||
**
|
||||
** If parameter onoff is non-zero, internal-use-only SQL functions
|
||||
** are visible to ordinary SQL. This is useful for testing but is
|
||||
** unsafe because invalid parameters to those internal-use-only functions
|
||||
** can result in crashes or segfaults.
|
||||
** Toggle the ability to use internal functions on or off for
|
||||
** the database connection given in the argument.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: {
|
||||
sqlite3GlobalConfig.bInternalFunctions = va_arg(ap, int);
|
||||
sqlite3 *db = va_arg(ap, sqlite3*);
|
||||
db->mDbFlags ^= DBFLAG_InternalFunc;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4213,6 +4273,19 @@ const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the name of Nth query parameter of the filename.
|
||||
*/
|
||||
const char *sqlite3_uri_key(const char *zFilename, int N){
|
||||
if( zFilename==0 || N<0 ) return 0;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] && (N--)>0 ){
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
}
|
||||
return zFilename[0] ? zFilename : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a boolean value for a query parameter.
|
||||
*/
|
||||
@ -4238,6 +4311,46 @@ sqlite3_int64 sqlite3_uri_int64(
|
||||
return bDflt;
|
||||
}
|
||||
|
||||
/*
|
||||
** The Pager stores the Journal filename, WAL filename, and Database filename
|
||||
** consecutively in memory, in that order, with prefixes \000\001\000,
|
||||
** \002\000, and \003\000, in that order. Thus the three names look like query
|
||||
** parameters if you start at the first prefix.
|
||||
**
|
||||
** This routine backs up a filename to the start of the first prefix.
|
||||
**
|
||||
** This only works if the filenamed passed in was obtained from the Pager.
|
||||
*/
|
||||
static const char *startOfNameList(const char *zName){
|
||||
while( zName[0]!='\001' || zName[1]!=0 ){
|
||||
zName -= 3;
|
||||
while( zName[0]!='\000' ){ zName--; }
|
||||
zName++;
|
||||
}
|
||||
return zName-1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate a filename that was handed to a VFS routine into the corresponding
|
||||
** database, journal, or WAL file.
|
||||
**
|
||||
** It is an error to pass this routine a filename string that was not
|
||||
** passed into the VFS from the SQLite core. Doing so is similar to
|
||||
** passing free() a pointer that was not obtained from malloc() - it is
|
||||
** an error that we cannot easily detect but that will likely cause memory
|
||||
** corruption.
|
||||
*/
|
||||
const char *sqlite3_filename_database(const char *zFilename){
|
||||
return sqlite3_uri_parameter(zFilename - 3, "\003");
|
||||
}
|
||||
const char *sqlite3_filename_journal(const char *zFilename){
|
||||
const char *z = sqlite3_uri_parameter(startOfNameList(zFilename), "\001");
|
||||
return ALWAYS(z) && z[0] ? z : 0;
|
||||
}
|
||||
const char *sqlite3_filename_wal(const char *zFilename){
|
||||
return sqlite3_uri_parameter(startOfNameList(zFilename), "\002");
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the Btree pointer identified by zDbName. Return NULL if not found.
|
||||
*/
|
||||
|
||||
188
src/malloc.c
188
src/malloc.c
@ -32,19 +32,27 @@ int sqlite3_release_memory(int n){
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Default value of the hard heap limit. 0 means "no limit".
|
||||
*/
|
||||
#ifndef SQLITE_MAX_MEMORY
|
||||
# define SQLITE_MAX_MEMORY 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** State information local to the memory allocation subsystem.
|
||||
*/
|
||||
static SQLITE_WSD struct Mem0Global {
|
||||
sqlite3_mutex *mutex; /* Mutex to serialize access */
|
||||
sqlite3_int64 alarmThreshold; /* The soft heap limit */
|
||||
sqlite3_int64 hardLimit; /* The hard upper bound on memory */
|
||||
|
||||
/*
|
||||
** True if heap is nearly "full" where "full" is defined by the
|
||||
** sqlite3_soft_heap_limit() setting.
|
||||
*/
|
||||
int nearlyFull;
|
||||
} mem0 = { 0, 0, 0 };
|
||||
} mem0 = { 0, SQLITE_MAX_MEMORY, SQLITE_MAX_MEMORY, 0 };
|
||||
|
||||
#define mem0 GLOBAL(struct Mem0Global, mem0)
|
||||
|
||||
@ -74,8 +82,15 @@ int sqlite3_memory_alarm(
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Set the soft heap-size limit for the library. Passing a zero or
|
||||
** negative value indicates no limit.
|
||||
** Set the soft heap-size limit for the library. An argument of
|
||||
** zero disables the limit. A negative argument is a no-op used to
|
||||
** obtain the return value.
|
||||
**
|
||||
** The return value is the value of the heap limit just before this
|
||||
** interface was called.
|
||||
**
|
||||
** If the hard heap limit is enabled, then the soft heap limit cannot
|
||||
** be disabled nor raised above the hard heap limit.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
||||
sqlite3_int64 priorLimit;
|
||||
@ -91,6 +106,9 @@ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
return priorLimit;
|
||||
}
|
||||
if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){
|
||||
n = mem0.hardLimit;
|
||||
}
|
||||
mem0.alarmThreshold = n;
|
||||
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
mem0.nearlyFull = (n>0 && n<=nUsed);
|
||||
@ -104,6 +122,37 @@ void sqlite3_soft_heap_limit(int n){
|
||||
sqlite3_soft_heap_limit64(n);
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the hard heap-size limit for the library. An argument of zero
|
||||
** disables the hard heap limit. A negative argument is a no-op used
|
||||
** to obtain the return value without affecting the hard heap limit.
|
||||
**
|
||||
** The return value is the value of the hard heap limit just prior to
|
||||
** calling this interface.
|
||||
**
|
||||
** Setting the hard heap limit will also activate the soft heap limit
|
||||
** and constrain the soft heap limit to be no more than the hard heap
|
||||
** limit.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 n){
|
||||
sqlite3_int64 priorLimit;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
if( rc ) return -1;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
priorLimit = mem0.hardLimit;
|
||||
if( n>=0 ){
|
||||
mem0.hardLimit = n;
|
||||
if( n<mem0.alarmThreshold || mem0.alarmThreshold==0 ){
|
||||
mem0.alarmThreshold = n;
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
return priorLimit;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Initialize the memory allocation subsystem.
|
||||
*/
|
||||
@ -201,19 +250,19 @@ static void mallocWithAlarm(int n, void **pp){
|
||||
** following xRoundup() call. */
|
||||
nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
||||
|
||||
#ifdef SQLITE_MAX_MEMORY
|
||||
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nFull>SQLITE_MAX_MEMORY ){
|
||||
*pp = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
|
||||
if( mem0.alarmThreshold>0 ){
|
||||
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
if( nUsed >= mem0.alarmThreshold - nFull ){
|
||||
mem0.nearlyFull = 1;
|
||||
sqlite3MallocAlarm(nFull);
|
||||
if( mem0.hardLimit ){
|
||||
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
if( nUsed >= mem0.hardLimit - nFull ){
|
||||
*pp = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
mem0.nearlyFull = 0;
|
||||
}
|
||||
@ -294,10 +343,17 @@ int sqlite3MallocSize(void *p){
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
static int lookasideMallocSize(sqlite3 *db, void *p){
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
return p<db->lookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL;
|
||||
#else
|
||||
return db->lookaside.szTrue;
|
||||
#endif
|
||||
}
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
assert( p!=0 );
|
||||
if( db==0 || !isLookaside(db,p) ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( db==0 || !isLookaside(db,p) ){
|
||||
if( db==0 ){
|
||||
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
||||
@ -305,12 +361,23 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
||||
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
||||
}
|
||||
#endif
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}else{
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
return db->lookaside.sz;
|
||||
}
|
||||
#endif
|
||||
if( db ){
|
||||
if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
return LOOKASIDE_SMALL;
|
||||
}
|
||||
#endif
|
||||
if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
return db->lookaside.szTrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
sqlite3_uint64 sqlite3_msize(void *p){
|
||||
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
|
||||
@ -357,15 +424,27 @@ void sqlite3DbFreeNN(sqlite3 *db, void *p){
|
||||
measureAllocationSize(db, p);
|
||||
return;
|
||||
}
|
||||
if( isLookaside(db, p) ){
|
||||
LookasideSlot *pBuf = (LookasideSlot*)p;
|
||||
if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
|
||||
LookasideSlot *pBuf = (LookasideSlot*)p;
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Trash all content in the buffer being freed */
|
||||
memset(p, 0xaa, db->lookaside.sz);
|
||||
memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */
|
||||
#endif
|
||||
pBuf->pNext = db->lookaside.pFree;
|
||||
db->lookaside.pFree = pBuf;
|
||||
return;
|
||||
pBuf->pNext = db->lookaside.pSmallFree;
|
||||
db->lookaside.pSmallFree = pBuf;
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
|
||||
if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
|
||||
LookasideSlot *pBuf = (LookasideSlot*)p;
|
||||
#ifdef SQLITE_DEBUG
|
||||
memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */
|
||||
#endif
|
||||
pBuf->pNext = db->lookaside.pFree;
|
||||
db->lookaside.pFree = pBuf;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
||||
@ -521,23 +600,37 @@ void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( db->pnBytesFreed==0 );
|
||||
if( db->lookaside.bDisable==0 ){
|
||||
assert( db->mallocFailed==0 );
|
||||
if( n>db->lookaside.sz ){
|
||||
db->lookaside.anStat[1]++;
|
||||
}else if( (pBuf = db->lookaside.pFree)!=0 ){
|
||||
db->lookaside.pFree = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}else if( (pBuf = db->lookaside.pInit)!=0 ){
|
||||
db->lookaside.pInit = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}else{
|
||||
db->lookaside.anStat[2]++;
|
||||
if( n>db->lookaside.sz ){
|
||||
if( !db->lookaside.bDisable ){
|
||||
db->lookaside.anStat[1]++;
|
||||
}else if( db->mallocFailed ){
|
||||
return 0;
|
||||
}
|
||||
}else if( db->mallocFailed ){
|
||||
return 0;
|
||||
return dbMallocRawFinish(db, n);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
if( n<=LOOKASIDE_SMALL ){
|
||||
if( (pBuf = db->lookaside.pSmallFree)!=0 ){
|
||||
db->lookaside.pSmallFree = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}else if( (pBuf = db->lookaside.pSmallInit)!=0 ){
|
||||
db->lookaside.pSmallInit = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( (pBuf = db->lookaside.pFree)!=0 ){
|
||||
db->lookaside.pFree = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}else if( (pBuf = db->lookaside.pInit)!=0 ){
|
||||
db->lookaside.pInit = pBuf->pNext;
|
||||
db->lookaside.anStat[0]++;
|
||||
return (void*)pBuf;
|
||||
}else{
|
||||
db->lookaside.anStat[2]++;
|
||||
}
|
||||
#else
|
||||
assert( db!=0 );
|
||||
@ -561,7 +654,16 @@ void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
|
||||
assert( db!=0 );
|
||||
if( p==0 ) return sqlite3DbMallocRawNN(db, n);
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
|
||||
if( ((uptr)p)<(uptr)db->lookaside.pEnd ){
|
||||
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
|
||||
if( ((uptr)p)>=(uptr)db->lookaside.pMiddle ){
|
||||
if( n<=LOOKASIDE_SMALL ) return p;
|
||||
}else
|
||||
#endif
|
||||
if( ((uptr)p)>=(uptr)db->lookaside.pStart ){
|
||||
if( n<=db->lookaside.szTrue ) return p;
|
||||
}
|
||||
}
|
||||
return dbReallocFinish(db, p, n);
|
||||
}
|
||||
static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
|
||||
@ -572,7 +674,7 @@ static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
|
||||
if( isLookaside(db, p) ){
|
||||
pNew = sqlite3DbMallocRawNN(db, n);
|
||||
if( pNew ){
|
||||
memcpy(pNew, p, db->lookaside.sz);
|
||||
memcpy(pNew, p, lookasideMallocSize(db, p));
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
}else{
|
||||
@ -671,7 +773,7 @@ void sqlite3OomFault(sqlite3 *db){
|
||||
if( db->nVdbeExec>0 ){
|
||||
db->u1.isInterrupted = 1;
|
||||
}
|
||||
db->lookaside.bDisable++;
|
||||
DisableLookaside;
|
||||
if( db->pParse ){
|
||||
db->pParse->rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
@ -690,7 +792,7 @@ void sqlite3OomClear(sqlite3 *db){
|
||||
db->mallocFailed = 0;
|
||||
db->u1.isInterrupted = 0;
|
||||
assert( db->lookaside.bDisable>0 );
|
||||
db->lookaside.bDisable--;
|
||||
EnableLookaside;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
src/os.c
2
src/os.c
@ -215,7 +215,7 @@ int sqlite3OsOpen(
|
||||
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
|
||||
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
|
||||
** reaching the VFS. */
|
||||
rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut);
|
||||
rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut);
|
||||
assert( rc==SQLITE_OK || pFile->pMethods==0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -3685,7 +3685,7 @@ static int openDirectory(const char *zFilename, int *pFd){
|
||||
if( zDirname[0]!='/' ) zDirname[0] = '.';
|
||||
zDirname[1] = 0;
|
||||
}
|
||||
fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
||||
fd = robust_open(zDirname, O_RDONLY|O_BINARY|O_NOFOLLOW, 0);
|
||||
if( fd>=0 ){
|
||||
OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
||||
}
|
||||
@ -4576,10 +4576,12 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
|
||||
if( pInode->bProcessLock==0 ){
|
||||
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
||||
pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT,(sStat.st_mode&0777));
|
||||
pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW,
|
||||
(sStat.st_mode&0777));
|
||||
}
|
||||
if( pShmNode->hShm<0 ){
|
||||
pShmNode->hShm = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777));
|
||||
pShmNode->hShm = robust_open(zShm, O_RDONLY|O_NOFOLLOW,
|
||||
(sStat.st_mode&0777));
|
||||
if( pShmNode->hShm<0 ){
|
||||
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm);
|
||||
goto shm_open_err;
|
||||
@ -5929,7 +5931,7 @@ static int unixOpen(
|
||||
unixFile *p = (unixFile *)pFile;
|
||||
int fd = -1; /* File descriptor returned by open() */
|
||||
int openFlags = 0; /* Flags to pass to open() */
|
||||
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
||||
int eType = flags&0x0FFF00; /* Type of file to open */
|
||||
int noLock; /* True to omit locking primitives */
|
||||
int rc = SQLITE_OK; /* Function Return Code */
|
||||
int ctrlFlags = 0; /* UNIXFILE_* flags */
|
||||
@ -6039,7 +6041,7 @@ static int unixOpen(
|
||||
if( isReadWrite ) openFlags |= O_RDWR;
|
||||
if( isCreate ) openFlags |= O_CREAT;
|
||||
if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
|
||||
openFlags |= (O_LARGEFILE|O_BINARY);
|
||||
openFlags |= (O_LARGEFILE|O_BINARY|O_NOFOLLOW);
|
||||
|
||||
if( fd<0 ){
|
||||
mode_t openMode; /* Permissions to create file with */
|
||||
@ -6257,7 +6259,8 @@ static int unixAccess(
|
||||
|
||||
if( flags==SQLITE_ACCESS_EXISTS ){
|
||||
struct stat buf;
|
||||
*pResOut = (0==osStat(zPath, &buf) && buf.st_size>0);
|
||||
*pResOut = 0==osStat(zPath, &buf) &&
|
||||
(!S_ISREG(buf.st_mode) || buf.st_size>0);
|
||||
}else{
|
||||
*pResOut = osAccess(zPath, W_OK|R_OK)==0;
|
||||
}
|
||||
@ -6311,7 +6314,7 @@ static int unixFullPathname(
|
||||
#else
|
||||
int rc = SQLITE_OK;
|
||||
int nByte;
|
||||
int nLink = 1; /* Number of symbolic links followed so far */
|
||||
int nLink = 0; /* Number of symbolic links followed so far */
|
||||
const char *zIn = zPath; /* Input path for each iteration of loop */
|
||||
char *zDel = 0;
|
||||
|
||||
@ -6340,10 +6343,11 @@ static int unixFullPathname(
|
||||
}
|
||||
|
||||
if( bLink ){
|
||||
nLink++;
|
||||
if( zDel==0 ){
|
||||
zDel = sqlite3_malloc(nOut);
|
||||
if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
|
||||
}else if( ++nLink>SQLITE_MAX_SYMLINKS ){
|
||||
}else if( nLink>=SQLITE_MAX_SYMLINKS ){
|
||||
rc = SQLITE_CANTOPEN_BKPT;
|
||||
}
|
||||
|
||||
@ -6379,6 +6383,7 @@ static int unixFullPathname(
|
||||
}while( rc==SQLITE_OK );
|
||||
|
||||
sqlite3_free(zDel);
|
||||
if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK;
|
||||
return rc;
|
||||
#endif /* HAVE_READLINK && HAVE_LSTAT */
|
||||
}
|
||||
@ -6864,7 +6869,7 @@ static int proxyCreateUnixFile(
|
||||
int fd = -1;
|
||||
unixFile *pNew;
|
||||
int rc = SQLITE_OK;
|
||||
int openFlags = O_RDWR | O_CREAT;
|
||||
int openFlags = O_RDWR | O_CREAT | O_NOFOLLOW;
|
||||
sqlite3_vfs dummyVfs;
|
||||
int terrno = 0;
|
||||
UnixUnusedFd *pUnused = NULL;
|
||||
@ -6894,7 +6899,7 @@ static int proxyCreateUnixFile(
|
||||
}
|
||||
}
|
||||
if( fd<0 ){
|
||||
openFlags = O_RDONLY;
|
||||
openFlags = O_RDONLY | O_NOFOLLOW;
|
||||
fd = robust_open(path, openFlags, 0);
|
||||
terrno = errno;
|
||||
}
|
||||
@ -7020,7 +7025,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
|
||||
goto end_breaklock;
|
||||
}
|
||||
/* write it out to the temporary break file */
|
||||
fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
|
||||
fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW), 0);
|
||||
if( fd<0 ){
|
||||
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
|
||||
goto end_breaklock;
|
||||
|
||||
142
src/pager.c
142
src/pager.c
@ -1172,6 +1172,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
|
||||
}
|
||||
IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
|
||||
}
|
||||
pPager->changeCountDone = pPager->tempFile; /* ticket fb3b3024ea238d5c */
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1359,6 +1360,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
|
||||
len = 0;
|
||||
}
|
||||
zMaster[len] = '\0';
|
||||
zMaster[len+1] = '\0';
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -1892,7 +1894,6 @@ static void pager_unlock(Pager *pPager){
|
||||
** code is cleared and the cache reset in the block below.
|
||||
*/
|
||||
assert( pPager->errCode || pPager->eState!=PAGER_ERROR );
|
||||
pPager->changeCountDone = 0;
|
||||
pPager->eState = PAGER_OPEN;
|
||||
}
|
||||
|
||||
@ -2156,7 +2157,6 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
|
||||
){
|
||||
rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
|
||||
pPager->changeCountDone = 0;
|
||||
}
|
||||
pPager->eState = PAGER_READER;
|
||||
pPager->setMaster = 0;
|
||||
@ -2595,15 +2595,16 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
|
||||
rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
nMasterPtr = pVfs->mxPathname+1;
|
||||
zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1);
|
||||
zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 2);
|
||||
if( !zMasterJournal ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto delmaster_out;
|
||||
}
|
||||
zMasterPtr = &zMasterJournal[nMasterJournal+1];
|
||||
zMasterPtr = &zMasterJournal[nMasterJournal+2];
|
||||
rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0);
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
zMasterJournal[nMasterJournal] = 0;
|
||||
zMasterJournal[nMasterJournal+1] = 0;
|
||||
|
||||
zJournal = zMasterJournal;
|
||||
while( (zJournal-zMasterJournal)<nMasterJournal ){
|
||||
@ -4760,7 +4761,8 @@ int sqlite3PagerOpen(
|
||||
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
|
||||
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
|
||||
const char *zUri = 0; /* URI args to copy */
|
||||
int nUri = 0; /* Number of bytes of URI args at *zUri */
|
||||
int nUriByte = 1; /* Number of bytes of URI args at *zUri */
|
||||
int nUri = 0; /* Number of URI parameters */
|
||||
|
||||
/* Figure out how much space is required for each journal file-handle
|
||||
** (there are two of them, the main journal and the sub-journal). */
|
||||
@ -4794,14 +4796,24 @@ int sqlite3PagerOpen(
|
||||
}
|
||||
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_OK_SYMLINK ){
|
||||
if( vfsFlags & SQLITE_OPEN_NOFOLLOW ){
|
||||
rc = SQLITE_CANTOPEN_SYMLINK;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
nPathname = sqlite3Strlen30(zPathname);
|
||||
z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
|
||||
while( *z ){
|
||||
z += sqlite3Strlen30(z)+1;
|
||||
z += sqlite3Strlen30(z)+1;
|
||||
z += strlen(z)+1;
|
||||
z += strlen(z)+1;
|
||||
nUri++;
|
||||
}
|
||||
nUri = (int)(&z[1] - zUri);
|
||||
assert( nUri>=0 );
|
||||
nUriByte = (int)(&z[1] - zUri);
|
||||
assert( nUriByte>=1 );
|
||||
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
|
||||
/* This branch is taken when the journal path required by
|
||||
** the database being opened will be more than pVfs->mxPathname
|
||||
@ -4826,50 +4838,88 @@ int sqlite3PagerOpen(
|
||||
** Database file handle (pVfs->szOsFile bytes)
|
||||
** Sub-journal file handle (journalFileSize bytes)
|
||||
** Main journal file handle (journalFileSize bytes)
|
||||
** \0\1\0 journal prefix (3 bytes)
|
||||
** Journal filename (nPathname+8+1 bytes)
|
||||
** \2\0 WAL prefix (2 bytes)
|
||||
** WAL filename (nPathname+4+1 bytes)
|
||||
** \3\0 database prefix (2 bytes)
|
||||
** Database file name (nPathname+1 bytes)
|
||||
** Journal file name (nPathname+8+1 bytes)
|
||||
** URI query parameters (nUriByte bytes)
|
||||
** \0\0 terminator (2 bytes)
|
||||
*/
|
||||
pPtr = (u8 *)sqlite3MallocZero(
|
||||
ROUND8(sizeof(*pPager)) + /* Pager structure */
|
||||
ROUND8(pcacheSize) + /* PCache object */
|
||||
ROUND8(pVfs->szOsFile) + /* The main db file */
|
||||
journalFileSize * 2 + /* The two journal files */
|
||||
nPathname + 1 + nUri + /* zFilename */
|
||||
nPathname + 8 + 2 /* zJournal */
|
||||
ROUND8(sizeof(*pPager)) + /* Pager structure */
|
||||
ROUND8(pcacheSize) + /* PCache object */
|
||||
ROUND8(pVfs->szOsFile) + /* The main db file */
|
||||
journalFileSize * 2 + /* The two journal files */
|
||||
3 + /* Journal prefix */
|
||||
nPathname + 8 + 1 + /* Journal filename */
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
+ nPathname + 4 + 2 /* zWal */
|
||||
2 + /* WAL prefix */
|
||||
nPathname + 4 + 1 + /* WAL filename */
|
||||
#endif
|
||||
2 + /* Database prefix */
|
||||
nPathname + 1 + /* database filename */
|
||||
nUriByte + /* query parameters */
|
||||
2 /* Terminator */
|
||||
);
|
||||
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
|
||||
if( !pPtr ){
|
||||
sqlite3DbFree(0, zPathname);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pPager = (Pager*)(pPtr);
|
||||
pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager)));
|
||||
pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize));
|
||||
pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile));
|
||||
pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize);
|
||||
pPager->zFilename = (char*)(pPtr += journalFileSize);
|
||||
pPager = (Pager*)pPtr; pPtr += ROUND8(sizeof(*pPager));
|
||||
pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize);
|
||||
pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile);
|
||||
pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
|
||||
pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
|
||||
|
||||
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
|
||||
if( zPathname ){
|
||||
assert( nPathname>0 );
|
||||
pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
|
||||
memcpy(pPager->zFilename, zPathname, nPathname);
|
||||
if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
|
||||
memcpy(pPager->zJournal, zPathname, nPathname);
|
||||
memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+2);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
pPager->zWal = &pPager->zJournal[nPathname+8+1];
|
||||
memcpy(pPager->zWal, zPathname, nPathname);
|
||||
memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
|
||||
|
||||
/* Fill in Pager.zJournal */
|
||||
pPtr[1] = '\001'; pPtr += 3;
|
||||
if( nPathname>0 ){
|
||||
pPager->zJournal = (char*)pPtr;
|
||||
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
|
||||
memcpy(pPtr, "-journal",8); pPtr += 8 + 1;
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
sqlite3FileSuffix3(zFilename,pPager->zJournal);
|
||||
pPtr = (u8*)(pPager->zJournal + sqlite3Strlen30(pPager->zJournal)+1);
|
||||
#endif
|
||||
sqlite3DbFree(0, zPathname);
|
||||
}else{
|
||||
pPager->zJournal = 0;
|
||||
pPtr++;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/* Fill in Pager.zWal */
|
||||
pPtr[0] = '\002'; pPtr[1] = 0; pPtr += 2;
|
||||
if( nPathname>0 ){
|
||||
pPager->zWal = (char*)pPtr;
|
||||
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
|
||||
memcpy(pPtr, "-wal", 4); pPtr += 4 + 1;
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
sqlite3FileSuffix3(zFilename, pPager->zWal);
|
||||
pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1);
|
||||
#endif
|
||||
}else{
|
||||
pPager->zWal = 0;
|
||||
pPtr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fill in the Pager.zFilename and pPager.zQueryParam fields */
|
||||
pPtr[0] = '\003'; pPtr[1] = 0; pPtr += 2;
|
||||
pPager->zFilename = (char*)pPtr;
|
||||
if( nPathname>0 ){
|
||||
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1;
|
||||
if( zUri ){
|
||||
memcpy(pPtr, zUri, nUriByte); /* pPtr += nUriByte; // not needed */
|
||||
}
|
||||
/* Double-zero terminator implied by the sqlite3MallocZero */
|
||||
}
|
||||
|
||||
if( nPathname ) sqlite3DbFree(0, zPathname);
|
||||
pPager->pVfs = pVfs;
|
||||
pPager->vfsFlags = vfsFlags;
|
||||
|
||||
@ -4918,9 +4968,9 @@ int sqlite3PagerOpen(
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0);
|
||||
pPager->noLock = sqlite3_uri_boolean(pPager->zFilename, "nolock", 0);
|
||||
if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
|
||||
|| sqlite3_uri_boolean(zFilename, "immutable", 0) ){
|
||||
|| sqlite3_uri_boolean(pPager->zFilename, "immutable", 0) ){
|
||||
vfsFlags |= SQLITE_OPEN_READONLY;
|
||||
goto act_like_temp_file;
|
||||
}
|
||||
@ -6611,6 +6661,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
|
||||
** But if (due to a coding error elsewhere in the system) it does get
|
||||
** called, just return the same error code without doing anything. */
|
||||
if( NEVER(pPager->errCode) ) return pPager->errCode;
|
||||
pPager->iDataVersion++;
|
||||
|
||||
assert( pPager->eState==PAGER_WRITER_LOCKED
|
||||
|| pPager->eState==PAGER_WRITER_FINISHED
|
||||
@ -6639,7 +6690,6 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
|
||||
}
|
||||
|
||||
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
|
||||
pPager->iDataVersion++;
|
||||
rc = pager_end_transaction(pPager, pPager->setMaster, 1);
|
||||
return pager_error(pPager, rc);
|
||||
}
|
||||
@ -6983,9 +7033,13 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
** behavior. But when the Btree needs to know the filename for matching to
|
||||
** shared cache, it uses nullIfMemDb==0 so that in-memory databases can
|
||||
** participate in shared-cache.
|
||||
**
|
||||
** The return value to this routine is always safe to use with
|
||||
** sqlite3_uri_parameter() and sqlite3_filename_database() and friends.
|
||||
*/
|
||||
const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
|
||||
return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename;
|
||||
const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){
|
||||
static const char zFake[] = { 0x00, 0x01, 0x00, 0x00, 0x00 };
|
||||
return (nullIfMemDb && pPager->memDb) ? &zFake[3] : pPager->zFilename;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -7640,6 +7694,8 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
/*
|
||||
** If this is a WAL database, obtain a snapshot handle for the snapshot
|
||||
|
||||
@ -203,7 +203,7 @@ u32 sqlite3PagerDataVersion(Pager*);
|
||||
int sqlite3PagerRefcount(Pager*);
|
||||
#endif
|
||||
int sqlite3PagerMemUsed(Pager*);
|
||||
const char *sqlite3PagerFilename(Pager*, int);
|
||||
const char *sqlite3PagerFilename(const Pager*, int);
|
||||
sqlite3_vfs *sqlite3PagerVfs(Pager*);
|
||||
sqlite3_file *sqlite3PagerFile(Pager*);
|
||||
sqlite3_file *sqlite3PagerJrnlFile(Pager*);
|
||||
|
||||
15
src/parse.y
15
src/parse.y
@ -106,8 +106,9 @@ struct FrameBound { int eType; Expr *pExpr; };
|
||||
** shared across database connections.
|
||||
*/
|
||||
static void disableLookaside(Parse *pParse){
|
||||
sqlite3 *db = pParse->db;
|
||||
pParse->disableLookaside++;
|
||||
pParse->db->lookaside.bDisable++;
|
||||
DisableLookaside;
|
||||
}
|
||||
|
||||
} // end %include
|
||||
@ -119,7 +120,7 @@ cmdlist ::= ecmd.
|
||||
ecmd ::= SEMI.
|
||||
ecmd ::= cmdx SEMI.
|
||||
%ifndef SQLITE_OMIT_EXPLAIN
|
||||
ecmd ::= explain cmdx.
|
||||
ecmd ::= explain cmdx SEMI. {NEVER-REDUCE}
|
||||
explain ::= EXPLAIN. { pParse->explain = 1; }
|
||||
explain ::= EXPLAIN QUERY PLAN. { pParse->explain = 2; }
|
||||
%endif SQLITE_OMIT_EXPLAIN
|
||||
@ -219,6 +220,9 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
|
||||
CURRENT FOLLOWING PARTITION PRECEDING RANGE UNBOUNDED
|
||||
EXCLUDE GROUPS OTHERS TIES
|
||||
%endif SQLITE_OMIT_WINDOWFUNC
|
||||
%ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
GENERATED ALWAYS
|
||||
%endif
|
||||
REINDEX RENAME CTIME_KW IF
|
||||
.
|
||||
%wildcard ANY.
|
||||
@ -346,6 +350,10 @@ ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R).
|
||||
{sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
|
||||
ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
|
||||
ccons ::= COLLATE ids(C). {sqlite3AddCollateType(pParse, &C);}
|
||||
ccons ::= GENERATED ALWAYS AS generated.
|
||||
ccons ::= AS generated.
|
||||
generated ::= LP expr(E) RP. {sqlite3AddGenerated(pParse,E,0);}
|
||||
generated ::= LP expr(E) RP ID(TYPE). {sqlite3AddGenerated(pParse,E,&TYPE);}
|
||||
|
||||
// The optional AUTOINCREMENT keyword
|
||||
%type autoinc {int}
|
||||
@ -1070,6 +1078,9 @@ expr(A) ::= LP nexprlist(X) COMMA expr(Y) RP. {
|
||||
A = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
||||
if( A ){
|
||||
A->x.pList = pList;
|
||||
if( ALWAYS(pList->nExpr) ){
|
||||
A->flags |= pList->a[0].pExpr->flags & EP_Propagate;
|
||||
}
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, pList);
|
||||
}
|
||||
|
||||
@ -448,13 +448,15 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
|
||||
}
|
||||
#else
|
||||
pPg = pcache1Alloc(pCache->szAlloc);
|
||||
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
|
||||
#endif
|
||||
if( benignMalloc ){ sqlite3EndBenignMalloc(); }
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
#endif
|
||||
if( pPg==0 ) return 0;
|
||||
#ifndef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
|
||||
#endif
|
||||
p->page.pBuf = pPg;
|
||||
p->page.pExtra = &p[1];
|
||||
p->isBulkLocal = 0;
|
||||
|
||||
120
src/pragma.c
120
src/pragma.c
@ -295,6 +295,55 @@ static const PragmaName *pragmaLocate(const char *zName){
|
||||
return lwr>upr ? 0 : &aPragmaName[mid];
|
||||
}
|
||||
|
||||
/*
|
||||
** Create zero or more entries in the output for the SQL functions
|
||||
** defined by FuncDef p.
|
||||
*/
|
||||
static void pragmaFunclistLine(
|
||||
Vdbe *v, /* The prepared statement being created */
|
||||
FuncDef *p, /* A particular function definition */
|
||||
int isBuiltin, /* True if this is a built-in function */
|
||||
int showInternFuncs /* True if showing internal functions */
|
||||
){
|
||||
for(; p; p=p->pNext){
|
||||
const char *zType;
|
||||
static const u32 mask =
|
||||
SQLITE_DETERMINISTIC |
|
||||
SQLITE_DIRECTONLY |
|
||||
SQLITE_SUBTYPE |
|
||||
SQLITE_INNOCUOUS |
|
||||
SQLITE_FUNC_INTERNAL
|
||||
;
|
||||
static const char *azEnc[] = { 0, "utf8", "utf16le", "utf16be" };
|
||||
|
||||
assert( SQLITE_FUNC_ENCMASK==0x3 );
|
||||
assert( strcmp(azEnc[SQLITE_UTF8],"utf8")==0 );
|
||||
assert( strcmp(azEnc[SQLITE_UTF16LE],"utf16le")==0 );
|
||||
assert( strcmp(azEnc[SQLITE_UTF16BE],"utf16be")==0 );
|
||||
|
||||
if( p->xSFunc==0 ) continue;
|
||||
if( (p->funcFlags & SQLITE_FUNC_INTERNAL)!=0
|
||||
&& showInternFuncs==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
if( p->xValue!=0 ){
|
||||
zType = "w";
|
||||
}else if( p->xFinalize!=0 ){
|
||||
zType = "a";
|
||||
}else{
|
||||
zType = "s";
|
||||
}
|
||||
sqlite3VdbeMultiLoad(v, 1, "sissii",
|
||||
p->zName, isBuiltin,
|
||||
zType, azEnc[p->funcFlags&SQLITE_FUNC_ENCMASK],
|
||||
p->nArg,
|
||||
(p->funcFlags & mask) ^ SQLITE_INNOCUOUS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Helper subroutine for PRAGMA integrity_check:
|
||||
**
|
||||
@ -1115,10 +1164,19 @@ void sqlite3Pragma(
|
||||
sqlite3CodeVerifySchema(pParse, iTabDb);
|
||||
sqlite3ViewGetColumnNames(pParse, pTab);
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
int isHidden = IsHiddenColumn(pCol);
|
||||
if( isHidden && pPragma->iArg==0 ){
|
||||
nHidden++;
|
||||
continue;
|
||||
int isHidden = 0;
|
||||
if( pCol->colFlags & COLFLAG_NOINSERT ){
|
||||
if( pPragma->iArg==0 ){
|
||||
nHidden++;
|
||||
continue;
|
||||
}
|
||||
if( pCol->colFlags & COLFLAG_VIRTUAL ){
|
||||
isHidden = 2; /* GENERATED ALWAYS AS ... VIRTUAL */
|
||||
}else if( pCol->colFlags & COLFLAG_STORED ){
|
||||
isHidden = 3; /* GENERATED ALWAYS AS ... STORED */
|
||||
}else{ assert( pCol->colFlags & COLFLAG_HIDDEN );
|
||||
isHidden = 1; /* HIDDEN */
|
||||
}
|
||||
}
|
||||
if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
|
||||
k = 0;
|
||||
@ -1127,13 +1185,13 @@ void sqlite3Pragma(
|
||||
}else{
|
||||
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
|
||||
}
|
||||
assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
|
||||
assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN || isHidden>=2 );
|
||||
sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi",
|
||||
i-nHidden,
|
||||
pCol->zName,
|
||||
sqlite3ColumnType(pCol,""),
|
||||
pCol->notNull ? 1 : 0,
|
||||
pCol->pDflt ? pCol->pDflt->u.zToken : 0,
|
||||
pCol->pDflt && isHidden<2 ? pCol->pDflt->u.zToken : 0,
|
||||
k,
|
||||
isHidden);
|
||||
}
|
||||
@ -1265,16 +1323,16 @@ void sqlite3Pragma(
|
||||
int i;
|
||||
HashElem *j;
|
||||
FuncDef *p;
|
||||
pParse->nMem = 2;
|
||||
int showInternFunc = (db->mDbFlags & DBFLAG_InternalFunc)!=0;
|
||||
pParse->nMem = 6;
|
||||
for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
|
||||
for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash ){
|
||||
if( p->funcFlags & SQLITE_FUNC_INTERNAL ) continue;
|
||||
sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1);
|
||||
pragmaFunclistLine(v, p, 1, showInternFunc);
|
||||
}
|
||||
}
|
||||
for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){
|
||||
p = (FuncDef*)sqliteHashData(j);
|
||||
sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 0);
|
||||
pragmaFunclistLine(v, p, 0, showInternFunc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1592,7 +1650,7 @@ void sqlite3Pragma(
|
||||
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
|
||||
if( !isQuick ){
|
||||
/* Sanity check on record header decoding */
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nCol-1, 3);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
|
||||
}
|
||||
/* Verify that all NOT NULL columns really are NOT NULL */
|
||||
@ -1602,7 +1660,9 @@ void sqlite3Pragma(
|
||||
if( j==pTab->iPKey ) continue;
|
||||
if( pTab->aCol[j].notNull==0 ) continue;
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
|
||||
if( sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
|
||||
}
|
||||
jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v);
|
||||
zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
|
||||
pTab->aCol[j].zName);
|
||||
@ -1779,10 +1839,17 @@ void sqlite3Pragma(
|
||||
** will be overwritten when the schema is next loaded. If it does not
|
||||
** already exists, it will be created to use the new encoding value.
|
||||
*/
|
||||
if(
|
||||
!(DbHasProperty(db, 0, DB_SchemaLoaded)) ||
|
||||
DbHasProperty(db, 0, DB_Empty)
|
||||
){
|
||||
int canChangeEnc = 1; /* True if allowed to change the encoding */
|
||||
int i; /* For looping over all attached databases */
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt!=0
|
||||
&& DbHasProperty(db,i,DB_SchemaLoaded)
|
||||
&& !DbHasProperty(db,i,DB_Empty)
|
||||
){
|
||||
canChangeEnc = 0;
|
||||
}
|
||||
}
|
||||
if( canChangeEnc ){
|
||||
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
|
||||
if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){
|
||||
SCHEMA_ENC(db) = ENC(db) =
|
||||
@ -2095,6 +2162,27 @@ void sqlite3Pragma(
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** PRAGMA hard_heap_limit
|
||||
** PRAGMA hard_heap_limit = N
|
||||
**
|
||||
** Invoke sqlite3_hard_heap_limit64() to query or set the hard heap
|
||||
** limit. The hard heap limit can be activated or lowered by this
|
||||
** pragma, but not raised or deactivated. Only the
|
||||
** sqlite3_hard_heap_limit64() C-language API can raise or deactivate
|
||||
** the hard heap limit. This allows an application to set a heap limit
|
||||
** constraint that cannot be relaxed by an untrusted SQL script.
|
||||
*/
|
||||
case PragTyp_HARD_HEAP_LIMIT: {
|
||||
sqlite3_int64 N;
|
||||
if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK ){
|
||||
sqlite3_int64 iPrior = sqlite3_hard_heap_limit64(-1);
|
||||
if( N>0 && (iPrior==0 || iPrior>N) ) sqlite3_hard_heap_limit64(N);
|
||||
}
|
||||
returnSingleInt(v, sqlite3_hard_heap_limit64(-1));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** PRAGMA threads
|
||||
** PRAGMA threads = N
|
||||
|
||||
154
src/pragma.h
154
src/pragma.h
@ -21,34 +21,35 @@
|
||||
#define PragTyp_FOREIGN_KEY_CHECK 13
|
||||
#define PragTyp_FOREIGN_KEY_LIST 14
|
||||
#define PragTyp_FUNCTION_LIST 15
|
||||
#define PragTyp_INCREMENTAL_VACUUM 16
|
||||
#define PragTyp_INDEX_INFO 17
|
||||
#define PragTyp_INDEX_LIST 18
|
||||
#define PragTyp_INTEGRITY_CHECK 19
|
||||
#define PragTyp_JOURNAL_MODE 20
|
||||
#define PragTyp_JOURNAL_SIZE_LIMIT 21
|
||||
#define PragTyp_LOCK_PROXY_FILE 22
|
||||
#define PragTyp_LOCKING_MODE 23
|
||||
#define PragTyp_PAGE_COUNT 24
|
||||
#define PragTyp_MMAP_SIZE 25
|
||||
#define PragTyp_MODULE_LIST 26
|
||||
#define PragTyp_OPTIMIZE 27
|
||||
#define PragTyp_PAGE_SIZE 28
|
||||
#define PragTyp_PRAGMA_LIST 29
|
||||
#define PragTyp_SECURE_DELETE 30
|
||||
#define PragTyp_SHRINK_MEMORY 31
|
||||
#define PragTyp_SOFT_HEAP_LIMIT 32
|
||||
#define PragTyp_SYNCHRONOUS 33
|
||||
#define PragTyp_TABLE_INFO 34
|
||||
#define PragTyp_TEMP_STORE 35
|
||||
#define PragTyp_TEMP_STORE_DIRECTORY 36
|
||||
#define PragTyp_THREADS 37
|
||||
#define PragTyp_WAL_AUTOCHECKPOINT 38
|
||||
#define PragTyp_WAL_CHECKPOINT 39
|
||||
#define PragTyp_ACTIVATE_EXTENSIONS 40
|
||||
#define PragTyp_KEY 41
|
||||
#define PragTyp_LOCK_STATUS 42
|
||||
#define PragTyp_STATS 43
|
||||
#define PragTyp_HARD_HEAP_LIMIT 16
|
||||
#define PragTyp_INCREMENTAL_VACUUM 17
|
||||
#define PragTyp_INDEX_INFO 18
|
||||
#define PragTyp_INDEX_LIST 19
|
||||
#define PragTyp_INTEGRITY_CHECK 20
|
||||
#define PragTyp_JOURNAL_MODE 21
|
||||
#define PragTyp_JOURNAL_SIZE_LIMIT 22
|
||||
#define PragTyp_LOCK_PROXY_FILE 23
|
||||
#define PragTyp_LOCKING_MODE 24
|
||||
#define PragTyp_PAGE_COUNT 25
|
||||
#define PragTyp_MMAP_SIZE 26
|
||||
#define PragTyp_MODULE_LIST 27
|
||||
#define PragTyp_OPTIMIZE 28
|
||||
#define PragTyp_PAGE_SIZE 29
|
||||
#define PragTyp_PRAGMA_LIST 30
|
||||
#define PragTyp_SECURE_DELETE 31
|
||||
#define PragTyp_SHRINK_MEMORY 32
|
||||
#define PragTyp_SOFT_HEAP_LIMIT 33
|
||||
#define PragTyp_SYNCHRONOUS 34
|
||||
#define PragTyp_TABLE_INFO 35
|
||||
#define PragTyp_TEMP_STORE 36
|
||||
#define PragTyp_TEMP_STORE_DIRECTORY 37
|
||||
#define PragTyp_THREADS 38
|
||||
#define PragTyp_WAL_AUTOCHECKPOINT 39
|
||||
#define PragTyp_WAL_CHECKPOINT 40
|
||||
#define PragTyp_ACTIVATE_EXTENSIONS 41
|
||||
#define PragTyp_KEY 42
|
||||
#define PragTyp_LOCK_STATUS 43
|
||||
#define PragTyp_STATS 44
|
||||
|
||||
/* Property flags associated with various pragma. */
|
||||
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
|
||||
@ -87,35 +88,39 @@ static const char *const pragCName[] = {
|
||||
/* 18 */ "desc",
|
||||
/* 19 */ "coll",
|
||||
/* 20 */ "key",
|
||||
/* 21 */ "tbl", /* Used by: stats */
|
||||
/* 22 */ "idx",
|
||||
/* 23 */ "wdth",
|
||||
/* 24 */ "hght",
|
||||
/* 25 */ "flgs",
|
||||
/* 26 */ "seq", /* Used by: index_list */
|
||||
/* 27 */ "name",
|
||||
/* 28 */ "unique",
|
||||
/* 29 */ "origin",
|
||||
/* 30 */ "partial",
|
||||
/* 31 */ "table", /* Used by: foreign_key_check */
|
||||
/* 32 */ "rowid",
|
||||
/* 33 */ "parent",
|
||||
/* 34 */ "fkid",
|
||||
/* 21 */ "name", /* Used by: function_list */
|
||||
/* 22 */ "builtin",
|
||||
/* 23 */ "type",
|
||||
/* 24 */ "enc",
|
||||
/* 25 */ "narg",
|
||||
/* 26 */ "flags",
|
||||
/* 27 */ "tbl", /* Used by: stats */
|
||||
/* 28 */ "idx",
|
||||
/* 29 */ "wdth",
|
||||
/* 30 */ "hght",
|
||||
/* 31 */ "flgs",
|
||||
/* 32 */ "seq", /* Used by: index_list */
|
||||
/* 33 */ "name",
|
||||
/* 34 */ "unique",
|
||||
/* 35 */ "origin",
|
||||
/* 36 */ "partial",
|
||||
/* 37 */ "table", /* Used by: foreign_key_check */
|
||||
/* 38 */ "rowid",
|
||||
/* 39 */ "parent",
|
||||
/* 40 */ "fkid",
|
||||
/* index_info reuses 15 */
|
||||
/* 35 */ "seq", /* Used by: database_list */
|
||||
/* 36 */ "name",
|
||||
/* 37 */ "file",
|
||||
/* 38 */ "busy", /* Used by: wal_checkpoint */
|
||||
/* 39 */ "log",
|
||||
/* 40 */ "checkpointed",
|
||||
/* 41 */ "name", /* Used by: function_list */
|
||||
/* 42 */ "builtin",
|
||||
/* collation_list reuses 26 */
|
||||
/* 43 */ "database", /* Used by: lock_status */
|
||||
/* 44 */ "status",
|
||||
/* 45 */ "cache_size", /* Used by: default_cache_size */
|
||||
/* 41 */ "seq", /* Used by: database_list */
|
||||
/* 42 */ "name",
|
||||
/* 43 */ "file",
|
||||
/* 44 */ "busy", /* Used by: wal_checkpoint */
|
||||
/* 45 */ "log",
|
||||
/* 46 */ "checkpointed",
|
||||
/* collation_list reuses 32 */
|
||||
/* 47 */ "database", /* Used by: lock_status */
|
||||
/* 48 */ "status",
|
||||
/* 49 */ "cache_size", /* Used by: default_cache_size */
|
||||
/* module_list pragma_list reuses 9 */
|
||||
/* 46 */ "timeout", /* Used by: busy_timeout */
|
||||
/* 50 */ "timeout", /* Used by: busy_timeout */
|
||||
};
|
||||
|
||||
/* Definitions of all built-in pragmas */
|
||||
@ -161,7 +166,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "busy_timeout",
|
||||
/* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 46, 1,
|
||||
/* ColNames: */ 50, 1,
|
||||
/* iArg: */ 0 },
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
{/* zName: */ "cache_size",
|
||||
@ -200,7 +205,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "collation_list",
|
||||
/* ePragTyp: */ PragTyp_COLLATION_LIST,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 26, 2,
|
||||
/* ColNames: */ 32, 2,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
|
||||
@ -235,14 +240,14 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "database_list",
|
||||
/* ePragTyp: */ PragTyp_DATABASE_LIST,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
|
||||
/* ColNames: */ 35, 3,
|
||||
/* ColNames: */ 41, 3,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
|
||||
{/* zName: */ "default_cache_size",
|
||||
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
|
||||
/* ColNames: */ 45, 1,
|
||||
/* ColNames: */ 49, 1,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
@ -272,7 +277,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "foreign_key_check",
|
||||
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
|
||||
/* ColNames: */ 31, 4,
|
||||
/* ColNames: */ 37, 4,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FOREIGN_KEY)
|
||||
@ -315,10 +320,15 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "function_list",
|
||||
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 41, 2,
|
||||
/* ColNames: */ 21, 6,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#endif
|
||||
{/* zName: */ "hard_heap_limit",
|
||||
/* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
{/* zName: */ "hexkey",
|
||||
/* ePragTyp: */ PragTyp_KEY,
|
||||
@ -356,7 +366,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "index_list",
|
||||
/* ePragTyp: */ PragTyp_INDEX_LIST,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
||||
/* ColNames: */ 26, 5,
|
||||
/* ColNames: */ 32, 5,
|
||||
/* iArg: */ 0 },
|
||||
{/* zName: */ "index_xinfo",
|
||||
/* ePragTyp: */ PragTyp_INDEX_INFO,
|
||||
@ -396,11 +406,6 @@ static const PragmaName aPragmaName[] = {
|
||||
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ SQLITE_LegacyAlter },
|
||||
{/* zName: */ "legacy_file_format",
|
||||
/* ePragTyp: */ PragTyp_FLAG,
|
||||
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ SQLITE_LegacyFileFmt },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
|
||||
{/* zName: */ "lock_proxy_file",
|
||||
@ -413,7 +418,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "lock_status",
|
||||
/* ePragTyp: */ PragTyp_LOCK_STATUS,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 43, 2,
|
||||
/* ColNames: */ 47, 2,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
@ -561,7 +566,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "stats",
|
||||
/* ePragTyp: */ PragTyp_STATS,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
|
||||
/* ColNames: */ 21, 5,
|
||||
/* ColNames: */ 27, 5,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
@ -612,6 +617,13 @@ static const PragmaName aPragmaName[] = {
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
{/* zName: */ "trusted_schema",
|
||||
/* ePragTyp: */ PragTyp_FLAG,
|
||||
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ SQLITE_TrustedSchema },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
{/* zName: */ "user_version",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
@ -657,7 +669,7 @@ static const PragmaName aPragmaName[] = {
|
||||
{/* zName: */ "wal_checkpoint",
|
||||
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
|
||||
/* ePragFlg: */ PragFlg_NeedSchema,
|
||||
/* ColNames: */ 38, 3,
|
||||
/* ColNames: */ 44, 3,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
@ -668,4 +680,4 @@ static const PragmaName aPragmaName[] = {
|
||||
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
|
||||
#endif
|
||||
};
|
||||
/* Number of pragmas: 65 on by default, 81 total. */
|
||||
/* Number of pragmas: 66 on by default, 82 total. */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user