76146 lines
2.7 MiB
76146 lines
2.7 MiB
--- contrib/sqlite3/Makefile.am.orig
|
|
+++ contrib/sqlite3/Makefile.am
|
|
@@ -1,6 +1,5 @@
|
|
|
|
-AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @SESSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE
|
|
-
|
|
+AM_CFLAGS = @BUILD_CFLAGS@
|
|
lib_LTLIBRARIES = libsqlite3.la
|
|
libsqlite3_la_SOURCES = sqlite3.c
|
|
libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8
|
|
@@ -10,11 +9,11 @@
|
|
EXTRA_sqlite3_SOURCES = sqlite3.c
|
|
sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
|
|
sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
|
|
-sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
|
|
|
include_HEADERS = sqlite3.h sqlite3ext.h
|
|
|
|
-EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs
|
|
+EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs Makefile.fallback
|
|
pkgconfigdir = ${libdir}/pkgconfig
|
|
pkgconfig_DATA = sqlite3.pc
|
|
|
|
--- contrib/sqlite3/Makefile.fallback.orig
|
|
+++ contrib/sqlite3/Makefile.fallback
|
|
@@ -0,0 +1,19 @@
|
|
+#!/usr/bin/make
|
|
+#
|
|
+# If the configure script does not work, then this Makefile is available
|
|
+# as a backup. Manually configure the variables below.
|
|
+#
|
|
+# Note: This makefile works out-of-the-box on MacOS 10.2 (Jaguar)
|
|
+#
|
|
+CC = gcc
|
|
+CFLAGS = -O0 -I.
|
|
+LIBS = -lz
|
|
+COPTS += -D_BSD_SOURCE
|
|
+COPTS += -DSQLITE_ENABLE_LOCKING_STYLE=0
|
|
+COPTS += -DSQLITE_THREADSAFE=0
|
|
+COPTS += -DSQLITE_OMIT_LOAD_EXTENSION
|
|
+COPTS += -DSQLITE_WITHOUT_ZONEMALLOC
|
|
+COPTS += -DSQLITE_ENABLE_RTREE
|
|
+
|
|
+sqlite3: shell.c sqlite3.c
|
|
+ $(CC) $(CFLAGS) $(COPTS) -o sqlite3 shell.c sqlite3.c $(LIBS)
|
|
--- contrib/sqlite3/Makefile.in.orig
|
|
+++ contrib/sqlite3/Makefile.in
|
|
@@ -260,7 +260,6 @@
|
|
DLLTOOL = @DLLTOOL@
|
|
DSYMUTIL = @DSYMUTIL@
|
|
DUMPBIN = @DUMPBIN@
|
|
-DYNAMIC_EXTENSION_FLAGS = @DYNAMIC_EXTENSION_FLAGS@
|
|
ECHO_C = @ECHO_C@
|
|
ECHO_N = @ECHO_N@
|
|
ECHO_T = @ECHO_T@
|
|
@@ -268,7 +267,6 @@
|
|
EXEEXT = @EXEEXT@
|
|
EXTRA_SHELL_OBJ = @EXTRA_SHELL_OBJ@
|
|
FGREP = @FGREP@
|
|
-FTS5_FLAGS = @FTS5_FLAGS@
|
|
GREP = @GREP@
|
|
INSTALL = @INSTALL@
|
|
INSTALL_DATA = @INSTALL_DATA@
|
|
@@ -275,7 +273,6 @@
|
|
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
|
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
|
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
|
-JSON1_FLAGS = @JSON1_FLAGS@
|
|
LD = @LD@
|
|
LDFLAGS = @LDFLAGS@
|
|
LIBOBJS = @LIBOBJS@
|
|
@@ -305,11 +302,10 @@
|
|
RANLIB = @RANLIB@
|
|
READLINE_LIBS = @READLINE_LIBS@
|
|
SED = @SED@
|
|
-SESSION_FLAGS = @SESSION_FLAGS@
|
|
SET_MAKE = @SET_MAKE@
|
|
SHELL = @SHELL@
|
|
+SHELL_CFLAGS = @SHELL_CFLAGS@
|
|
STRIP = @STRIP@
|
|
-THREADSAFE_FLAGS = @THREADSAFE_FLAGS@
|
|
VERSION = @VERSION@
|
|
abs_builddir = @abs_builddir@
|
|
abs_srcdir = @abs_srcdir@
|
|
@@ -363,7 +359,7 @@
|
|
top_build_prefix = @top_build_prefix@
|
|
top_builddir = @top_builddir@
|
|
top_srcdir = @top_srcdir@
|
|
-AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @SESSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE
|
|
+AM_CFLAGS = @BUILD_CFLAGS@
|
|
lib_LTLIBRARIES = libsqlite3.la
|
|
libsqlite3_la_SOURCES = sqlite3.c
|
|
libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8
|
|
@@ -371,9 +367,9 @@
|
|
EXTRA_sqlite3_SOURCES = sqlite3.c
|
|
sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
|
|
sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
|
|
-sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
+sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
|
include_HEADERS = sqlite3.h sqlite3ext.h
|
|
-EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs
|
|
+EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs Makefile.fallback
|
|
pkgconfigdir = ${libdir}/pkgconfig
|
|
pkgconfig_DATA = sqlite3.pc
|
|
man_MANS = sqlite3.1
|
|
--- contrib/sqlite3/Makefile.msc.orig
|
|
+++ contrib/sqlite3/Makefile.msc
|
|
@@ -277,6 +277,12 @@
|
|
!IF $(MINIMAL_AMALGAMATION)==0
|
|
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
|
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
|
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
|
|
+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
|
|
!ENDIF
|
|
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
|
!ENDIF
|
|
@@ -561,6 +567,7 @@
|
|
!ENDIF
|
|
!ENDIF
|
|
|
|
+
|
|
# This is the core library that the shell executable should link with.
|
|
#
|
|
!IFNDEF SHELL_CORE_LIB
|
|
@@ -808,7 +815,7 @@
|
|
# If requested, link to the RPCRT4 library.
|
|
#
|
|
!IF $(USE_RPCRT4_LIB)!=0
|
|
-LTLINK = $(LTLINK) rpcrt4.lib
|
|
+LTLIBS = $(LTLIBS) rpcrt4.lib
|
|
!ENDIF
|
|
|
|
# If a platform was set, force the linker to target that.
|
|
@@ -927,7 +934,9 @@
|
|
# when the shell is not being dynamically linked.
|
|
#
|
|
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
|
|
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTVTAB
|
|
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
|
|
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
|
|
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
|
!ENDIF
|
|
|
|
|
|
@@ -934,8 +943,16 @@
|
|
# This is the default Makefile target. The objects listed here
|
|
# are what get build when you type just "make" with no arguments.
|
|
#
|
|
-all: dll shell
|
|
+core: dll shell
|
|
|
|
+# Targets that require the Tcl library.
|
|
+#
|
|
+tcl: $(ALL_TCL_TARGETS)
|
|
+
|
|
+# This Makefile target builds all of the standard binaries.
|
|
+#
|
|
+all: core tcl
|
|
+
|
|
# Dynamic link library section.
|
|
#
|
|
dll: $(SQLITE3DLL)
|
|
@@ -954,11 +971,11 @@
|
|
sqlite3.def: Replace.exe $(LIBOBJ)
|
|
echo EXPORTS > sqlite3.def
|
|
dumpbin /all $(LIBOBJ) \
|
|
- | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
|
+ | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
|
| sort >> sqlite3.def
|
|
|
|
-$(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
|
|
- $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\shell.c $(SHELL_CORE_SRC) \
|
|
+$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
|
|
+ $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \
|
|
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
|
|
|
|
|
@@ -973,7 +990,7 @@
|
|
!IF $(USE_RC)!=0
|
|
_HASHCHAR=^#
|
|
!IF ![echo !IFNDEF VERSION > rcver.vc] && \
|
|
- ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| find "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
|
|
+ ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| "%SystemRoot%\System32\find.exe" "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
|
|
![echo !ENDIF >> rcver.vc]
|
|
!INCLUDE rcver.vc
|
|
!ENDIF
|
|
--- contrib/sqlite3/configure.orig
|
|
+++ contrib/sqlite3/configure
|
|
@@ -1,6 +1,6 @@
|
|
#! /bin/sh
|
|
# Guess values for system-dependent variables and create Makefiles.
|
|
-# Generated by GNU Autoconf 2.69 for sqlite 3.20.0.
|
|
+# Generated by GNU Autoconf 2.69 for sqlite 3.26.0.
|
|
#
|
|
# Report bugs to <http://www.sqlite.org>.
|
|
#
|
|
@@ -590,8 +590,8 @@
|
|
# Identity of this package.
|
|
PACKAGE_NAME='sqlite'
|
|
PACKAGE_TARNAME='sqlite'
|
|
-PACKAGE_VERSION='3.20.0'
|
|
-PACKAGE_STRING='sqlite 3.20.0'
|
|
+PACKAGE_VERSION='3.26.0'
|
|
+PACKAGE_STRING='sqlite 3.26.0'
|
|
PACKAGE_BUGREPORT='http://www.sqlite.org'
|
|
PACKAGE_URL=''
|
|
|
|
@@ -636,12 +636,8 @@
|
|
am__EXEEXT_TRUE
|
|
LTLIBOBJS
|
|
LIBOBJS
|
|
+SHELL_CFLAGS
|
|
EXTRA_SHELL_OBJ
|
|
-SESSION_FLAGS
|
|
-JSON1_FLAGS
|
|
-FTS5_FLAGS
|
|
-DYNAMIC_EXTENSION_FLAGS
|
|
-THREADSAFE_FLAGS
|
|
READLINE_LIBS
|
|
BUILD_CFLAGS
|
|
CPP
|
|
@@ -775,9 +771,13 @@
|
|
enable_readline
|
|
enable_threadsafe
|
|
enable_dynamic_extensions
|
|
+enable_fts4
|
|
+enable_fts3
|
|
enable_fts5
|
|
enable_json1
|
|
+enable_rtree
|
|
enable_session
|
|
+enable_debug
|
|
enable_static_shell
|
|
'
|
|
ac_precious_vars='build_alias
|
|
@@ -1330,7 +1330,7 @@
|
|
# Omit some internal or obsolete options to make the list less imposing.
|
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
|
cat <<_ACEOF
|
|
-\`configure' configures sqlite 3.20.0 to adapt to many kinds of systems.
|
|
+\`configure' configures sqlite 3.26.0 to adapt to many kinds of systems.
|
|
|
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
|
|
|
@@ -1400,7 +1400,7 @@
|
|
|
|
if test -n "$ac_init_help"; then
|
|
case $ac_init_help in
|
|
- short | recursive ) echo "Configuration of sqlite 3.20.0:";;
|
|
+ short | recursive ) echo "Configuration of sqlite 3.26.0:";;
|
|
esac
|
|
cat <<\_ACEOF
|
|
|
|
@@ -1425,9 +1425,13 @@
|
|
--enable-threadsafe build a thread-safe library [default=yes]
|
|
--enable-dynamic-extensions
|
|
support loadable extensions [default=yes]
|
|
- --enable-fts5 include fts5 support [default=no]
|
|
- --enable-json1 include json1 support [default=no]
|
|
+ --enable-fts4 include fts4 support [default=yes]
|
|
+ --enable-fts3 include fts3 support [default=no]
|
|
+ --enable-fts5 include fts5 support [default=yes]
|
|
+ --enable-json1 include json1 support [default=yes]
|
|
+ --enable-rtree include rtree support [default=yes]
|
|
--enable-session enable the session extension [default=no]
|
|
+ --enable-debug build with debugging features enabled [default=no]
|
|
--enable-static-shell statically link libsqlite3 into shell tool
|
|
[default=yes]
|
|
|
|
@@ -1521,7 +1525,7 @@
|
|
test -n "$ac_init_help" && exit $ac_status
|
|
if $ac_init_version; then
|
|
cat <<\_ACEOF
|
|
-sqlite configure 3.20.0
|
|
+sqlite configure 3.26.0
|
|
generated by GNU Autoconf 2.69
|
|
|
|
Copyright (C) 2012 Free Software Foundation, Inc.
|
|
@@ -1936,7 +1940,7 @@
|
|
This file contains any messages produced by compilers while
|
|
running configure, to aid debugging if configure makes a mistake.
|
|
|
|
-It was created by sqlite $as_me 3.20.0, which was
|
|
+It was created by sqlite $as_me 3.26.0, which was
|
|
generated by GNU Autoconf 2.69. Invocation command line was
|
|
|
|
$ $0 $@
|
|
@@ -2285,12 +2289,8 @@
|
|
|
|
|
|
|
|
-
|
|
-# Use automake.
|
|
-am__api_version='1.15'
|
|
-
|
|
ac_aux_dir=
|
|
-for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
|
|
+for ac_dir in . "$srcdir"/.; do
|
|
if test -f "$ac_dir/install-sh"; then
|
|
ac_aux_dir=$ac_dir
|
|
ac_install_sh="$ac_aux_dir/install-sh -c"
|
|
@@ -2306,7 +2306,7 @@
|
|
fi
|
|
done
|
|
if test -z "$ac_aux_dir"; then
|
|
- as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
|
|
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5
|
|
fi
|
|
|
|
# These three variables are undocumented and unsupported,
|
|
@@ -2318,6 +2318,10 @@
|
|
ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
|
|
|
|
|
|
+
|
|
+# Use automake.
|
|
+am__api_version='1.15'
|
|
+
|
|
# Find a good install program. We prefer a C program (faster),
|
|
# so one script is as good as another. But avoid the broken or
|
|
# incompatible versions:
|
|
@@ -2802,7 +2806,7 @@
|
|
|
|
# Define the identity of the package.
|
|
PACKAGE='sqlite'
|
|
- VERSION='3.20.0'
|
|
+ VERSION='3.26.0'
|
|
|
|
|
|
cat >>confdefs.h <<_ACEOF
|
|
@@ -13038,6 +13042,7 @@
|
|
|
|
ac_config_files="$ac_config_files Makefile sqlite3.pc"
|
|
|
|
+BUILD_CFLAGS=
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
@@ -13302,9 +13307,8 @@
|
|
enable_threadsafe=yes
|
|
fi
|
|
|
|
-THREADSAFE_FLAGS=-DSQLITE_THREADSAFE=0
|
|
if test x"$enable_threadsafe" != "xno"; then
|
|
- THREADSAFE_FLAGS="-D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
|
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5
|
|
$as_echo_n "checking for library containing pthread_create... " >&6; }
|
|
if ${ac_cv_search_pthread_create+:} false; then :
|
|
@@ -13418,7 +13422,6 @@
|
|
fi
|
|
|
|
fi
|
|
-
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -13489,16 +13492,43 @@
|
|
fi
|
|
|
|
else
|
|
- DYNAMIC_EXTENSION_FLAGS=-DSQLITE_OMIT_LOAD_EXTENSION=1
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_OMIT_LOAD_EXTENSION=1"
|
|
fi
|
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for whether to support dynamic extensions" >&5
|
|
$as_echo_n "checking for whether to support dynamic extensions... " >&6; }
|
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_dynamic_extensions" >&5
|
|
$as_echo "$enable_dynamic_extensions" >&6; }
|
|
+#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
+# --enable-fts4
|
|
+#
|
|
+# Check whether --enable-fts4 was given.
|
|
+if test "${enable_fts4+set}" = set; then :
|
|
+ enableval=$enable_fts4;
|
|
+else
|
|
+ enable_fts4=yes
|
|
+fi
|
|
|
|
+if test x"$enable_fts4" = "xyes"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS4"
|
|
+fi
|
|
#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
+# --enable-fts3
|
|
+#
|
|
+# Check whether --enable-fts3 was given.
|
|
+if test "${enable_fts3+set}" = set; then :
|
|
+ enableval=$enable_fts3;
|
|
+fi
|
|
+
|
|
+if test x"$enable_fts3" = "xyes" -a x"$enable_fts4" = "xno"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS3"
|
|
+fi
|
|
+#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
# --enable-fts5
|
|
#
|
|
# Check whether --enable-fts5 was given.
|
|
@@ -13505,7 +13535,7 @@
|
|
if test "${enable_fts5+set}" = set; then :
|
|
enableval=$enable_fts5;
|
|
else
|
|
- enable_fts5=no
|
|
+ enable_fts5=yes
|
|
fi
|
|
|
|
if test x"$enable_fts5" = "xyes"; then
|
|
@@ -13565,9 +13595,8 @@
|
|
|
|
fi
|
|
|
|
- FTS5_FLAGS=-DSQLITE_ENABLE_FTS5
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS5"
|
|
fi
|
|
-
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -13577,32 +13606,57 @@
|
|
if test "${enable_json1+set}" = set; then :
|
|
enableval=$enable_json1;
|
|
else
|
|
- enable_json1=no
|
|
+ enable_json1=yes
|
|
fi
|
|
|
|
if test x"$enable_json1" = "xyes"; then
|
|
- JSON1_FLAGS=-DSQLITE_ENABLE_JSON1
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_JSON1"
|
|
fi
|
|
+#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
+# --enable-rtree
|
|
+#
|
|
+# Check whether --enable-rtree was given.
|
|
+if test "${enable_rtree+set}" = set; then :
|
|
+ enableval=$enable_rtree;
|
|
+else
|
|
+ enable_rtree=yes
|
|
+fi
|
|
|
|
+if test x"$enable_rtree" = "xyes"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE"
|
|
+fi
|
|
#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
# --enable-session
|
|
#
|
|
# Check whether --enable-session was given.
|
|
if test "${enable_session+set}" = set; then :
|
|
enableval=$enable_session;
|
|
-else
|
|
- enable_session=no
|
|
fi
|
|
|
|
if test x"$enable_session" = "xyes"; then
|
|
- SESSION_FLAGS="-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
|
fi
|
|
+#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
+# --enable-debug
|
|
+#
|
|
+# Check whether --enable-debug was given.
|
|
+if test "${enable_debug+set}" = set; then :
|
|
+ enableval=$enable_debug;
|
|
+fi
|
|
|
|
+if test x"$enable_debug" = "xyes"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
|
|
+ CFLAGS="-g -O0"
|
|
+fi
|
|
#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
# --enable-static-shell
|
|
#
|
|
# Check whether --enable-static-shell was given.
|
|
@@ -13631,7 +13685,136 @@
|
|
fi
|
|
done
|
|
|
|
+for ac_header in zlib.h
|
|
+do :
|
|
+ ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default"
|
|
+if test "x$ac_cv_header_zlib_h" = xyes; then :
|
|
+ cat >>confdefs.h <<_ACEOF
|
|
+#define HAVE_ZLIB_H 1
|
|
+_ACEOF
|
|
|
|
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing deflate" >&5
|
|
+$as_echo_n "checking for library containing deflate... " >&6; }
|
|
+if ${ac_cv_search_deflate+:} false; then :
|
|
+ $as_echo_n "(cached) " >&6
|
|
+else
|
|
+ ac_func_search_save_LIBS=$LIBS
|
|
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
|
+/* end confdefs.h. */
|
|
+
|
|
+/* Override any GCC internal prototype to avoid an error.
|
|
+ Use char because int might match the return type of a GCC
|
|
+ builtin and then its argument prototype would still apply. */
|
|
+#ifdef __cplusplus
|
|
+extern "C"
|
|
+#endif
|
|
+char deflate ();
|
|
+int
|
|
+main ()
|
|
+{
|
|
+return deflate ();
|
|
+ ;
|
|
+ return 0;
|
|
+}
|
|
+_ACEOF
|
|
+for ac_lib in '' z; do
|
|
+ if test -z "$ac_lib"; then
|
|
+ ac_res="none required"
|
|
+ else
|
|
+ ac_res=-l$ac_lib
|
|
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
|
|
+ fi
|
|
+ if ac_fn_c_try_link "$LINENO"; then :
|
|
+ ac_cv_search_deflate=$ac_res
|
|
+fi
|
|
+rm -f core conftest.err conftest.$ac_objext \
|
|
+ conftest$ac_exeext
|
|
+ if ${ac_cv_search_deflate+:} false; then :
|
|
+ break
|
|
+fi
|
|
+done
|
|
+if ${ac_cv_search_deflate+:} false; then :
|
|
+
|
|
+else
|
|
+ ac_cv_search_deflate=no
|
|
+fi
|
|
+rm conftest.$ac_ext
|
|
+LIBS=$ac_func_search_save_LIBS
|
|
+fi
|
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_deflate" >&5
|
|
+$as_echo "$ac_cv_search_deflate" >&6; }
|
|
+ac_res=$ac_cv_search_deflate
|
|
+if test "$ac_res" != no; then :
|
|
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_HAVE_ZLIB"
|
|
+fi
|
|
+
|
|
+
|
|
+fi
|
|
+
|
|
+done
|
|
+
|
|
+
|
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing system" >&5
|
|
+$as_echo_n "checking for library containing system... " >&6; }
|
|
+if ${ac_cv_search_system+:} false; then :
|
|
+ $as_echo_n "(cached) " >&6
|
|
+else
|
|
+ ac_func_search_save_LIBS=$LIBS
|
|
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
|
+/* end confdefs.h. */
|
|
+
|
|
+/* Override any GCC internal prototype to avoid an error.
|
|
+ Use char because int might match the return type of a GCC
|
|
+ builtin and then its argument prototype would still apply. */
|
|
+#ifdef __cplusplus
|
|
+extern "C"
|
|
+#endif
|
|
+char system ();
|
|
+int
|
|
+main ()
|
|
+{
|
|
+return system ();
|
|
+ ;
|
|
+ return 0;
|
|
+}
|
|
+_ACEOF
|
|
+for ac_lib in '' ; do
|
|
+ if test -z "$ac_lib"; then
|
|
+ ac_res="none required"
|
|
+ else
|
|
+ ac_res=-l$ac_lib
|
|
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
|
|
+ fi
|
|
+ if ac_fn_c_try_link "$LINENO"; then :
|
|
+ ac_cv_search_system=$ac_res
|
|
+fi
|
|
+rm -f core conftest.err conftest.$ac_objext \
|
|
+ conftest$ac_exeext
|
|
+ if ${ac_cv_search_system+:} false; then :
|
|
+ break
|
|
+fi
|
|
+done
|
|
+if ${ac_cv_search_system+:} false; then :
|
|
+
|
|
+else
|
|
+ ac_cv_search_system=no
|
|
+fi
|
|
+rm conftest.$ac_ext
|
|
+LIBS=$ac_func_search_save_LIBS
|
|
+fi
|
|
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_system" >&5
|
|
+$as_echo "$ac_cv_search_system" >&6; }
|
|
+ac_res=$ac_cv_search_system
|
|
+if test "$ac_res" != no; then :
|
|
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
|
|
+
|
|
+else
|
|
+ SHELL_CFLAGS="-DSQLITE_NOHAVE_SYSTEM"
|
|
+fi
|
|
+
|
|
+
|
|
+
|
|
#-----------------------------------------------------------------------
|
|
# UPDATE: Maybe it's better if users just set CFLAGS before invoking
|
|
# configure. This option doesn't really add much...
|
|
@@ -14227,7 +14410,7 @@
|
|
# report actual input values of CONFIG_FILES etc. instead of their
|
|
# values after options handling.
|
|
ac_log="
|
|
-This file was extended by sqlite $as_me 3.20.0, which was
|
|
+This file was extended by sqlite $as_me 3.26.0, which was
|
|
generated by GNU Autoconf 2.69. Invocation command line was
|
|
|
|
CONFIG_FILES = $CONFIG_FILES
|
|
@@ -14284,7 +14467,7 @@
|
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
|
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
|
ac_cs_version="\\
|
|
-sqlite config.status 3.20.0
|
|
+sqlite config.status 3.26.0
|
|
configured by $0, generated by GNU Autoconf 2.69,
|
|
with options \\"\$ac_cs_config\\"
|
|
|
|
--- contrib/sqlite3/configure.ac.orig
|
|
+++ contrib/sqlite3/configure.ac
|
|
@@ -10,8 +10,9 @@
|
|
#
|
|
|
|
AC_PREREQ(2.61)
|
|
-AC_INIT(sqlite, 3.20.0, http://www.sqlite.org)
|
|
+AC_INIT(sqlite, 3.26.0, http://www.sqlite.org)
|
|
AC_CONFIG_SRCDIR([sqlite3.c])
|
|
+AC_CONFIG_AUX_DIR([.])
|
|
|
|
# Use automake.
|
|
AM_INIT_AUTOMAKE([foreign])
|
|
@@ -28,6 +29,7 @@
|
|
AC_FUNC_STRERROR_R
|
|
|
|
AC_CONFIG_FILES([Makefile sqlite3.pc])
|
|
+BUILD_CFLAGS=
|
|
AC_SUBST(BUILD_CFLAGS)
|
|
|
|
#-------------------------------------------------------------------------
|
|
@@ -85,13 +87,11 @@
|
|
AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
|
|
[--enable-threadsafe], [build a thread-safe library [default=yes]])],
|
|
[], [enable_threadsafe=yes])
|
|
-THREADSAFE_FLAGS=-DSQLITE_THREADSAFE=0
|
|
if test x"$enable_threadsafe" != "xno"; then
|
|
- THREADSAFE_FLAGS="-D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
|
|
AC_SEARCH_LIBS(pthread_create, pthread)
|
|
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
|
|
fi
|
|
-AC_SUBST(THREADSAFE_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -103,24 +103,44 @@
|
|
if test x"$enable_dynamic_extensions" != "xno"; then
|
|
AC_SEARCH_LIBS(dlopen, dl)
|
|
else
|
|
- DYNAMIC_EXTENSION_FLAGS=-DSQLITE_OMIT_LOAD_EXTENSION=1
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_OMIT_LOAD_EXTENSION=1"
|
|
fi
|
|
AC_MSG_CHECKING([for whether to support dynamic extensions])
|
|
AC_MSG_RESULT($enable_dynamic_extensions)
|
|
-AC_SUBST(DYNAMIC_EXTENSION_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
+# --enable-fts4
|
|
+#
|
|
+AC_ARG_ENABLE(fts4, [AS_HELP_STRING(
|
|
+ [--enable-fts4], [include fts4 support [default=yes]])],
|
|
+ [], [enable_fts4=yes])
|
|
+if test x"$enable_fts4" = "xyes"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS4"
|
|
+fi
|
|
+#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
+# --enable-fts3
|
|
+#
|
|
+AC_ARG_ENABLE(fts3, [AS_HELP_STRING(
|
|
+ [--enable-fts3], [include fts3 support [default=no]])],
|
|
+ [], [])
|
|
+if test x"$enable_fts3" = "xyes" -a x"$enable_fts4" = "xno"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS3"
|
|
+fi
|
|
+#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
# --enable-fts5
|
|
#
|
|
AC_ARG_ENABLE(fts5, [AS_HELP_STRING(
|
|
- [--enable-fts5], [include fts5 support [default=no]])],
|
|
- [], [enable_fts5=no])
|
|
+ [--enable-fts5], [include fts5 support [default=yes]])],
|
|
+ [], [enable_fts5=yes])
|
|
if test x"$enable_fts5" = "xyes"; then
|
|
AC_SEARCH_LIBS(log, m)
|
|
- FTS5_FLAGS=-DSQLITE_ENABLE_FTS5
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS5"
|
|
fi
|
|
-AC_SUBST(FTS5_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
@@ -127,27 +147,48 @@
|
|
# --enable-json1
|
|
#
|
|
AC_ARG_ENABLE(json1, [AS_HELP_STRING(
|
|
- [--enable-json1], [include json1 support [default=no]])],
|
|
- [], [enable_json1=no])
|
|
+ [--enable-json1], [include json1 support [default=yes]])],
|
|
+ [],[enable_json1=yes])
|
|
if test x"$enable_json1" = "xyes"; then
|
|
- JSON1_FLAGS=-DSQLITE_ENABLE_JSON1
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_JSON1"
|
|
fi
|
|
-AC_SUBST(JSON1_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
+# --enable-rtree
|
|
+#
|
|
+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"
|
|
+fi
|
|
+#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
# --enable-session
|
|
#
|
|
AC_ARG_ENABLE(session, [AS_HELP_STRING(
|
|
[--enable-session], [enable the session extension [default=no]])],
|
|
- [], [enable_session=no])
|
|
+ [], [])
|
|
if test x"$enable_session" = "xyes"; then
|
|
- SESSION_FLAGS="-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
|
fi
|
|
-AC_SUBST(SESSION_FLAGS)
|
|
#-----------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------
|
|
+# --enable-debug
|
|
+#
|
|
+AC_ARG_ENABLE(debug, [AS_HELP_STRING(
|
|
+ [--enable-debug], [build with debugging features enabled [default=no]])],
|
|
+ [], [])
|
|
+if test x"$enable_debug" = "xyes"; then
|
|
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
|
|
+ CFLAGS="-g -O0"
|
|
+fi
|
|
+#-----------------------------------------------------------------------
|
|
+
|
|
+#-----------------------------------------------------------------------
|
|
# --enable-static-shell
|
|
#
|
|
AC_ARG_ENABLE(static-shell, [AS_HELP_STRING(
|
|
@@ -163,7 +204,13 @@
|
|
#-----------------------------------------------------------------------
|
|
|
|
AC_CHECK_FUNCS(posix_fallocate)
|
|
+AC_CHECK_HEADERS(zlib.h,[
|
|
+ AC_SEARCH_LIBS(deflate,z,[BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_HAVE_ZLIB"])
|
|
+])
|
|
|
|
+AC_SEARCH_LIBS(system,,,[SHELL_CFLAGS="-DSQLITE_NOHAVE_SYSTEM"])
|
|
+AC_SUBST(SHELL_CFLAGS)
|
|
+
|
|
#-----------------------------------------------------------------------
|
|
# UPDATE: Maybe it's better if users just set CFLAGS before invoking
|
|
# configure. This option doesn't really add much...
|
|
--- contrib/sqlite3/shell.c.orig
|
|
+++ contrib/sqlite3/shell.c
|
|
@@ -79,6 +79,9 @@
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include "sqlite3.h"
|
|
+typedef sqlite3_int64 i64;
|
|
+typedef sqlite3_uint64 u64;
|
|
+typedef unsigned char u8;
|
|
#if SQLITE_USER_AUTHENTICATION
|
|
# include "sqlite3userauth.h"
|
|
#endif
|
|
@@ -90,9 +93,22 @@
|
|
# if !defined(__RTP__) && !defined(_WRS_KERNEL)
|
|
# include <pwd.h>
|
|
# endif
|
|
+#endif
|
|
+#if (!defined(_WIN32) && !defined(WIN32)) || defined(__MINGW32__)
|
|
# include <unistd.h>
|
|
-# include <sys/types.h>
|
|
+# include <dirent.h>
|
|
+# define GETPID getpid
|
|
+# if defined(__MINGW32__)
|
|
+# define DIRENT dirent
|
|
+# ifndef S_ISLNK
|
|
+# define S_ISLNK(mode) (0)
|
|
+# endif
|
|
+# endif
|
|
+#else
|
|
+# define GETPID (int)GetCurrentProcessId
|
|
#endif
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
|
|
#if HAVE_READLINE
|
|
# include <readline/readline.h>
|
|
@@ -137,6 +153,9 @@
|
|
# ifndef access
|
|
# define access(f,m) _access((f),(m))
|
|
# endif
|
|
+# ifndef unlink
|
|
+# define unlink _unlink
|
|
+# endif
|
|
# undef popen
|
|
# define popen _popen
|
|
# undef pclose
|
|
@@ -358,6 +377,11 @@
|
|
#define UNUSED_PARAMETER(x) (void)(x)
|
|
|
|
/*
|
|
+** Number of elements in an array
|
|
+*/
|
|
+#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
|
|
+
|
|
+/*
|
|
** If the following flag is set, then command execution stops
|
|
** at an error if we are not interactive.
|
|
*/
|
|
@@ -433,6 +457,12 @@
|
|
# define raw_printf fprintf
|
|
#endif
|
|
|
|
+/* Indicate out-of-memory and exit. */
|
|
+static void shell_out_of_memory(void){
|
|
+ raw_printf(stderr,"Error: out of memory\n");
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
/*
|
|
** Write I/O traces to the following stream.
|
|
*/
|
|
@@ -556,7 +586,7 @@
|
|
if( n+100>nLine ){
|
|
nLine = nLine*2 + 100;
|
|
zLine = realloc(zLine, nLine);
|
|
- if( zLine==0 ) return 0;
|
|
+ if( zLine==0 ) shell_out_of_memory();
|
|
}
|
|
if( fgets(&zLine[n], nLine - n, in)==0 ){
|
|
if( n==0 ){
|
|
@@ -583,10 +613,7 @@
|
|
int nTrans = strlen30(zTrans)+1;
|
|
if( nTrans>nLine ){
|
|
zLine = realloc(zLine, nTrans);
|
|
- if( zLine==0 ){
|
|
- sqlite3_free(zTrans);
|
|
- return 0;
|
|
- }
|
|
+ if( zLine==0 ) shell_out_of_memory();
|
|
}
|
|
memcpy(zLine, zTrans, nTrans);
|
|
sqlite3_free(zTrans);
|
|
@@ -629,7 +656,66 @@
|
|
}
|
|
return zResult;
|
|
}
|
|
+
|
|
+
|
|
/*
|
|
+** Return the value of a hexadecimal digit. Return -1 if the input
|
|
+** is not a hex digit.
|
|
+*/
|
|
+static int hexDigitValue(char c){
|
|
+ if( c>='0' && c<='9' ) return c - '0';
|
|
+ if( c>='a' && c<='f' ) return c - 'a' + 10;
|
|
+ if( c>='A' && c<='F' ) return c - 'A' + 10;
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Interpret zArg as an integer value, possibly with suffixes.
|
|
+*/
|
|
+static sqlite3_int64 integerValue(const char *zArg){
|
|
+ sqlite3_int64 v = 0;
|
|
+ static const struct { char *zSuffix; int iMult; } aMult[] = {
|
|
+ { "KiB", 1024 },
|
|
+ { "MiB", 1024*1024 },
|
|
+ { "GiB", 1024*1024*1024 },
|
|
+ { "KB", 1000 },
|
|
+ { "MB", 1000000 },
|
|
+ { "GB", 1000000000 },
|
|
+ { "K", 1000 },
|
|
+ { "M", 1000000 },
|
|
+ { "G", 1000000000 },
|
|
+ };
|
|
+ int i;
|
|
+ int isNeg = 0;
|
|
+ if( zArg[0]=='-' ){
|
|
+ isNeg = 1;
|
|
+ zArg++;
|
|
+ }else if( zArg[0]=='+' ){
|
|
+ zArg++;
|
|
+ }
|
|
+ if( zArg[0]=='0' && zArg[1]=='x' ){
|
|
+ int x;
|
|
+ zArg += 2;
|
|
+ while( (x = hexDigitValue(zArg[0]))>=0 ){
|
|
+ v = (v<<4) + x;
|
|
+ zArg++;
|
|
+ }
|
|
+ }else{
|
|
+ while( IsDigit(zArg[0]) ){
|
|
+ v = v*10 + zArg[0] - '0';
|
|
+ zArg++;
|
|
+ }
|
|
+ }
|
|
+ for(i=0; i<ArraySize(aMult); i++){
|
|
+ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
|
|
+ v *= aMult[i].iMult;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return isNeg? -v : v;
|
|
+}
|
|
+
|
|
+/*
|
|
** A variable length string to which one can append text.
|
|
*/
|
|
typedef struct ShellText ShellText;
|
|
@@ -674,10 +760,7 @@
|
|
if( p->n+len>=p->nAlloc ){
|
|
p->nAlloc = p->nAlloc*2 + len + 20;
|
|
p->z = realloc(p->z, p->nAlloc);
|
|
- if( p->z==0 ){
|
|
- memset(p, 0, sizeof(*p));
|
|
- return;
|
|
- }
|
|
+ if( p->z==0 ) shell_out_of_memory();
|
|
}
|
|
|
|
if( quote ){
|
|
@@ -706,48 +789,82 @@
|
|
** Return '"' if quoting is required. Return 0 if no quoting is required.
|
|
*/
|
|
static char quoteChar(const char *zName){
|
|
- /* All SQLite keywords, in alphabetical order */
|
|
- static const char *azKeywords[] = {
|
|
- "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
|
|
- "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
|
|
- "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
|
|
- "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
|
|
- "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
|
|
- "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
|
|
- "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
|
|
- "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
|
|
- "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
|
|
- "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
|
|
- "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
|
|
- "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
|
|
- "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
|
|
- "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
|
|
- "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
|
|
- "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
|
|
- "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
|
|
- "WITH", "WITHOUT",
|
|
- };
|
|
- int i, lwr, upr, mid, c;
|
|
+ int i;
|
|
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
|
|
for(i=0; zName[i]; i++){
|
|
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
|
|
}
|
|
- lwr = 0;
|
|
- upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1;
|
|
- while( lwr<=upr ){
|
|
- mid = (lwr+upr)/2;
|
|
- c = sqlite3_stricmp(azKeywords[mid], zName);
|
|
- if( c==0 ) return '"';
|
|
- if( c<0 ){
|
|
- lwr = mid+1;
|
|
- }else{
|
|
- upr = mid-1;
|
|
- }
|
|
+ return sqlite3_keyword_check(zName, i) ? '"' : 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Construct a fake object name and column list to describe the structure
|
|
+** of the view, virtual table, or table valued function zSchema.zName.
|
|
+*/
|
|
+static char *shellFakeSchema(
|
|
+ sqlite3 *db, /* The database connection containing the vtab */
|
|
+ const char *zSchema, /* Schema of the database holding the vtab */
|
|
+ const char *zName /* The name of the virtual table */
|
|
+){
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ char *zSql;
|
|
+ ShellText s;
|
|
+ char cQuote;
|
|
+ char *zDiv = "(";
|
|
+ int nRow = 0;
|
|
+
|
|
+ zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;",
|
|
+ zSchema ? zSchema : "main", zName);
|
|
+ sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ initText(&s);
|
|
+ if( zSchema ){
|
|
+ cQuote = quoteChar(zSchema);
|
|
+ if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0;
|
|
+ appendText(&s, zSchema, cQuote);
|
|
+ appendText(&s, ".", 0);
|
|
}
|
|
- return 0;
|
|
+ cQuote = quoteChar(zName);
|
|
+ appendText(&s, zName, cQuote);
|
|
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
|
+ const char *zCol = (const char*)sqlite3_column_text(pStmt, 1);
|
|
+ nRow++;
|
|
+ appendText(&s, zDiv, 0);
|
|
+ zDiv = ",";
|
|
+ cQuote = quoteChar(zCol);
|
|
+ appendText(&s, zCol, cQuote);
|
|
+ }
|
|
+ appendText(&s, ")", 0);
|
|
+ sqlite3_finalize(pStmt);
|
|
+ if( nRow==0 ){
|
|
+ freeText(&s);
|
|
+ s.z = 0;
|
|
+ }
|
|
+ return s.z;
|
|
}
|
|
|
|
/*
|
|
+** SQL function: shell_module_schema(X)
|
|
+**
|
|
+** Return a fake schema for the table-valued function or eponymous virtual
|
|
+** table X.
|
|
+*/
|
|
+static void shellModuleSchema(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal
|
|
+){
|
|
+ const char *zName = (const char*)sqlite3_value_text(apVal[0]);
|
|
+ char *zFake = shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName);
|
|
+ UNUSED_PARAMETER(nVal);
|
|
+ if( zFake ){
|
|
+ sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
|
|
+ -1, sqlite3_free);
|
|
+ free(zFake);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
** SQL function: shell_add_schema(S,X)
|
|
**
|
|
** Add the schema name X to the CREATE statement in S and return the result.
|
|
@@ -782,20 +899,38 @@
|
|
int i = 0;
|
|
const char *zIn = (const char*)sqlite3_value_text(apVal[0]);
|
|
const char *zSchema = (const char*)sqlite3_value_text(apVal[1]);
|
|
- assert( nVal==2 );
|
|
+ const char *zName = (const char*)sqlite3_value_text(apVal[2]);
|
|
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
|
|
+ UNUSED_PARAMETER(nVal);
|
|
if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){
|
|
for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){
|
|
int n = strlen30(aPrefix[i]);
|
|
if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
|
|
- char cQuote = quoteChar(zSchema);
|
|
- char *z;
|
|
- if( cQuote ){
|
|
- z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8);
|
|
- }else{
|
|
- z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8);
|
|
+ char *z = 0;
|
|
+ char *zFake = 0;
|
|
+ if( zSchema ){
|
|
+ char cQuote = quoteChar(zSchema);
|
|
+ if( cQuote && sqlite3_stricmp(zSchema,"temp")!=0 ){
|
|
+ z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8);
|
|
+ }else{
|
|
+ z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8);
|
|
+ }
|
|
}
|
|
- sqlite3_result_text(pCtx, z, -1, sqlite3_free);
|
|
- return;
|
|
+ if( zName
|
|
+ && aPrefix[i][0]=='V'
|
|
+ && (zFake = shellFakeSchema(db, zSchema, zName))!=0
|
|
+ ){
|
|
+ if( z==0 ){
|
|
+ z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
|
|
+ }else{
|
|
+ z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
|
|
+ }
|
|
+ free(zFake);
|
|
+ }
|
|
+ if( z ){
|
|
+ sqlite3_result_text(pCtx, z, -1, sqlite3_free);
|
|
+ return;
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -811,6 +946,364 @@
|
|
#define SQLITE_EXTENSION_INIT1
|
|
#define SQLITE_EXTENSION_INIT2(X) (void)(X)
|
|
|
|
+#if defined(_WIN32) && defined(_MSC_VER)
|
|
+/************************* Begin test_windirent.h ******************/
|
|
+/*
|
|
+** 2015 November 30
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+** This file contains declarations for most of the opendir() family of
|
|
+** POSIX functions on Win32 using the MSVCRT.
|
|
+*/
|
|
+
|
|
+#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
|
|
+#define SQLITE_WINDIRENT_H
|
|
+
|
|
+/*
|
|
+** We need several data types from the Windows SDK header.
|
|
+*/
|
|
+
|
|
+#ifndef WIN32_LEAN_AND_MEAN
|
|
+#define WIN32_LEAN_AND_MEAN
|
|
+#endif
|
|
+
|
|
+#include "windows.h"
|
|
+
|
|
+/*
|
|
+** We need several support functions from the SQLite core.
|
|
+*/
|
|
+
|
|
+
|
|
+/*
|
|
+** We need several things from the ANSI and MSVCRT headers.
|
|
+*/
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <errno.h>
|
|
+#include <io.h>
|
|
+#include <limits.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+
|
|
+/*
|
|
+** We may need several defines that should have been in "sys/stat.h".
|
|
+*/
|
|
+
|
|
+#ifndef S_ISREG
|
|
+#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
|
+#endif
|
|
+
|
|
+#ifndef S_ISDIR
|
|
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
|
+#endif
|
|
+
|
|
+#ifndef S_ISLNK
|
|
+#define S_ISLNK(mode) (0)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** We may need to provide the "mode_t" type.
|
|
+*/
|
|
+
|
|
+#ifndef MODE_T_DEFINED
|
|
+ #define MODE_T_DEFINED
|
|
+ typedef unsigned short mode_t;
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** We may need to provide the "ino_t" type.
|
|
+*/
|
|
+
|
|
+#ifndef INO_T_DEFINED
|
|
+ #define INO_T_DEFINED
|
|
+ typedef unsigned short ino_t;
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** We need to define "NAME_MAX" if it was not present in "limits.h".
|
|
+*/
|
|
+
|
|
+#ifndef NAME_MAX
|
|
+# ifdef FILENAME_MAX
|
|
+# define NAME_MAX (FILENAME_MAX)
|
|
+# else
|
|
+# define NAME_MAX (260)
|
|
+# endif
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
|
|
+*/
|
|
+
|
|
+#ifndef NULL_INTPTR_T
|
|
+# define NULL_INTPTR_T ((intptr_t)(0))
|
|
+#endif
|
|
+
|
|
+#ifndef BAD_INTPTR_T
|
|
+# define BAD_INTPTR_T ((intptr_t)(-1))
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** We need to provide the necessary structures and related types.
|
|
+*/
|
|
+
|
|
+#ifndef DIRENT_DEFINED
|
|
+#define DIRENT_DEFINED
|
|
+typedef struct DIRENT DIRENT;
|
|
+typedef DIRENT *LPDIRENT;
|
|
+struct DIRENT {
|
|
+ ino_t d_ino; /* Sequence number, do not use. */
|
|
+ unsigned d_attributes; /* Win32 file attributes. */
|
|
+ char d_name[NAME_MAX + 1]; /* Name within the directory. */
|
|
+};
|
|
+#endif
|
|
+
|
|
+#ifndef DIR_DEFINED
|
|
+#define DIR_DEFINED
|
|
+typedef struct DIR DIR;
|
|
+typedef DIR *LPDIR;
|
|
+struct DIR {
|
|
+ intptr_t d_handle; /* Value returned by "_findfirst". */
|
|
+ DIRENT d_first; /* DIRENT constructed based on "_findfirst". */
|
|
+ DIRENT d_next; /* DIRENT constructed based on "_findnext". */
|
|
+};
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Provide a macro, for use by the implementation, to determine if a
|
|
+** particular directory entry should be skipped over when searching for
|
|
+** the next directory entry that should be returned by the readdir() or
|
|
+** readdir_r() functions.
|
|
+*/
|
|
+
|
|
+#ifndef is_filtered
|
|
+# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Provide the function prototype for the POSIX compatiable getenv()
|
|
+** function. This function is not thread-safe.
|
|
+*/
|
|
+
|
|
+extern const char *windirent_getenv(const char *name);
|
|
+
|
|
+/*
|
|
+** Finally, we can provide the function prototypes for the opendir(),
|
|
+** readdir(), readdir_r(), and closedir() POSIX functions.
|
|
+*/
|
|
+
|
|
+extern LPDIR opendir(const char *dirname);
|
|
+extern LPDIRENT readdir(LPDIR dirp);
|
|
+extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result);
|
|
+extern INT closedir(LPDIR dirp);
|
|
+
|
|
+#endif /* defined(WIN32) && defined(_MSC_VER) */
|
|
+
|
|
+/************************* End test_windirent.h ********************/
|
|
+/************************* Begin test_windirent.c ******************/
|
|
+/*
|
|
+** 2015 November 30
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+** This file contains code to implement most of the opendir() family of
|
|
+** POSIX functions on Win32 using the MSVCRT.
|
|
+*/
|
|
+
|
|
+#if defined(_WIN32) && defined(_MSC_VER)
|
|
+/* #include "test_windirent.h" */
|
|
+
|
|
+/*
|
|
+** Implementation of the POSIX getenv() function using the Win32 API.
|
|
+** This function is not thread-safe.
|
|
+*/
|
|
+const char *windirent_getenv(
|
|
+ const char *name
|
|
+){
|
|
+ static char value[32768]; /* Maximum length, per MSDN */
|
|
+ DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */
|
|
+ DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */
|
|
+
|
|
+ memset(value, 0, sizeof(value));
|
|
+ dwRet = GetEnvironmentVariableA(name, value, dwSize);
|
|
+ if( dwRet==0 || dwRet>dwSize ){
|
|
+ /*
|
|
+ ** The function call to GetEnvironmentVariableA() failed -OR-
|
|
+ ** the buffer is not large enough. Either way, return NULL.
|
|
+ */
|
|
+ return 0;
|
|
+ }else{
|
|
+ /*
|
|
+ ** The function call to GetEnvironmentVariableA() succeeded
|
|
+ ** -AND- the buffer contains the entire value.
|
|
+ */
|
|
+ return value;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the POSIX opendir() function using the MSVCRT.
|
|
+*/
|
|
+LPDIR opendir(
|
|
+ const char *dirname
|
|
+){
|
|
+ struct _finddata_t data;
|
|
+ LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
|
|
+ SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
|
|
+
|
|
+ if( dirp==NULL ) return NULL;
|
|
+ memset(dirp, 0, sizeof(DIR));
|
|
+
|
|
+ /* TODO: Remove this if Unix-style root paths are not used. */
|
|
+ if( sqlite3_stricmp(dirname, "/")==0 ){
|
|
+ dirname = windirent_getenv("SystemDrive");
|
|
+ }
|
|
+
|
|
+ memset(&data, 0, sizeof(struct _finddata_t));
|
|
+ _snprintf(data.name, namesize, "%s\\*", dirname);
|
|
+ dirp->d_handle = _findfirst(data.name, &data);
|
|
+
|
|
+ if( dirp->d_handle==BAD_INTPTR_T ){
|
|
+ closedir(dirp);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* TODO: Remove this block to allow hidden and/or system files. */
|
|
+ if( is_filtered(data) ){
|
|
+next:
|
|
+
|
|
+ memset(&data, 0, sizeof(struct _finddata_t));
|
|
+ if( _findnext(dirp->d_handle, &data)==-1 ){
|
|
+ closedir(dirp);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* TODO: Remove this block to allow hidden and/or system files. */
|
|
+ if( is_filtered(data) ) goto next;
|
|
+ }
|
|
+
|
|
+ dirp->d_first.d_attributes = data.attrib;
|
|
+ strncpy(dirp->d_first.d_name, data.name, NAME_MAX);
|
|
+ dirp->d_first.d_name[NAME_MAX] = '\0';
|
|
+
|
|
+ return dirp;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the POSIX readdir() function using the MSVCRT.
|
|
+*/
|
|
+LPDIRENT readdir(
|
|
+ LPDIR dirp
|
|
+){
|
|
+ struct _finddata_t data;
|
|
+
|
|
+ if( dirp==NULL ) return NULL;
|
|
+
|
|
+ if( dirp->d_first.d_ino==0 ){
|
|
+ dirp->d_first.d_ino++;
|
|
+ dirp->d_next.d_ino++;
|
|
+
|
|
+ return &dirp->d_first;
|
|
+ }
|
|
+
|
|
+next:
|
|
+
|
|
+ memset(&data, 0, sizeof(struct _finddata_t));
|
|
+ if( _findnext(dirp->d_handle, &data)==-1 ) return NULL;
|
|
+
|
|
+ /* TODO: Remove this block to allow hidden and/or system files. */
|
|
+ if( is_filtered(data) ) goto next;
|
|
+
|
|
+ dirp->d_next.d_ino++;
|
|
+ dirp->d_next.d_attributes = data.attrib;
|
|
+ strncpy(dirp->d_next.d_name, data.name, NAME_MAX);
|
|
+ dirp->d_next.d_name[NAME_MAX] = '\0';
|
|
+
|
|
+ return &dirp->d_next;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the POSIX readdir_r() function using the MSVCRT.
|
|
+*/
|
|
+INT readdir_r(
|
|
+ LPDIR dirp,
|
|
+ LPDIRENT entry,
|
|
+ LPDIRENT *result
|
|
+){
|
|
+ struct _finddata_t data;
|
|
+
|
|
+ if( dirp==NULL ) return EBADF;
|
|
+
|
|
+ if( dirp->d_first.d_ino==0 ){
|
|
+ dirp->d_first.d_ino++;
|
|
+ dirp->d_next.d_ino++;
|
|
+
|
|
+ entry->d_ino = dirp->d_first.d_ino;
|
|
+ entry->d_attributes = dirp->d_first.d_attributes;
|
|
+ strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX);
|
|
+ entry->d_name[NAME_MAX] = '\0';
|
|
+
|
|
+ *result = entry;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+next:
|
|
+
|
|
+ memset(&data, 0, sizeof(struct _finddata_t));
|
|
+ if( _findnext(dirp->d_handle, &data)==-1 ){
|
|
+ *result = NULL;
|
|
+ return ENOENT;
|
|
+ }
|
|
+
|
|
+ /* TODO: Remove this block to allow hidden and/or system files. */
|
|
+ if( is_filtered(data) ) goto next;
|
|
+
|
|
+ entry->d_ino = (ino_t)-1; /* not available */
|
|
+ entry->d_attributes = data.attrib;
|
|
+ strncpy(entry->d_name, data.name, NAME_MAX);
|
|
+ entry->d_name[NAME_MAX] = '\0';
|
|
+
|
|
+ *result = entry;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the POSIX closedir() function using the MSVCRT.
|
|
+*/
|
|
+INT closedir(
|
|
+ LPDIR dirp
|
|
+){
|
|
+ INT result = 0;
|
|
+
|
|
+ if( dirp==NULL ) return EINVAL;
|
|
+
|
|
+ if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){
|
|
+ result = _findclose(dirp->d_handle);
|
|
+ }
|
|
+
|
|
+ sqlite3_free(dirp);
|
|
+ return result;
|
|
+}
|
|
+
|
|
+#endif /* defined(WIN32) && defined(_MSC_VER) */
|
|
+
|
|
+/************************* End test_windirent.c ********************/
|
|
+#define dirent DIRENT
|
|
+#endif
|
|
/************************* Begin ../ext/misc/shathree.c ******************/
|
|
/*
|
|
** 2017-03-08
|
|
@@ -824,7 +1317,7 @@
|
|
**
|
|
******************************************************************************
|
|
**
|
|
-** This SQLite extension implements a functions that compute SHA1 hashes.
|
|
+** This SQLite extension implements functions that compute SHA3 hashes.
|
|
** Two SQL functions are implemented:
|
|
**
|
|
** sha3(X,SIZE)
|
|
@@ -844,7 +1337,7 @@
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
-typedef sqlite3_uint64 u64;
|
|
+/* typedef sqlite3_uint64 u64; */
|
|
|
|
/******************************************************************************
|
|
** The Hash Engine
|
|
@@ -891,9 +1384,9 @@
|
|
*/
|
|
static void KeccakF1600Step(SHA3Context *p){
|
|
int i;
|
|
- u64 B0, B1, B2, B3, B4;
|
|
- u64 C0, C1, C2, C3, C4;
|
|
- u64 D0, D1, D2, D3, D4;
|
|
+ u64 b0, b1, b2, b3, b4;
|
|
+ u64 c0, c1, c2, c3, c4;
|
|
+ u64 d0, d1, d2, d3, d4;
|
|
static const u64 RC[] = {
|
|
0x0000000000000001ULL, 0x0000000000008082ULL,
|
|
0x800000000000808aULL, 0x8000000080008000ULL,
|
|
@@ -908,301 +1401,301 @@
|
|
0x8000000080008081ULL, 0x8000000000008080ULL,
|
|
0x0000000080000001ULL, 0x8000000080008008ULL
|
|
};
|
|
-# define A00 (p->u.s[0])
|
|
-# define A01 (p->u.s[1])
|
|
-# define A02 (p->u.s[2])
|
|
-# define A03 (p->u.s[3])
|
|
-# define A04 (p->u.s[4])
|
|
-# define A10 (p->u.s[5])
|
|
-# define A11 (p->u.s[6])
|
|
-# define A12 (p->u.s[7])
|
|
-# define A13 (p->u.s[8])
|
|
-# define A14 (p->u.s[9])
|
|
-# define A20 (p->u.s[10])
|
|
-# define A21 (p->u.s[11])
|
|
-# define A22 (p->u.s[12])
|
|
-# define A23 (p->u.s[13])
|
|
-# define A24 (p->u.s[14])
|
|
-# define A30 (p->u.s[15])
|
|
-# define A31 (p->u.s[16])
|
|
-# define A32 (p->u.s[17])
|
|
-# define A33 (p->u.s[18])
|
|
-# define A34 (p->u.s[19])
|
|
-# define A40 (p->u.s[20])
|
|
-# define A41 (p->u.s[21])
|
|
-# define A42 (p->u.s[22])
|
|
-# define A43 (p->u.s[23])
|
|
-# define A44 (p->u.s[24])
|
|
+# define a00 (p->u.s[0])
|
|
+# define a01 (p->u.s[1])
|
|
+# define a02 (p->u.s[2])
|
|
+# define a03 (p->u.s[3])
|
|
+# define a04 (p->u.s[4])
|
|
+# define a10 (p->u.s[5])
|
|
+# define a11 (p->u.s[6])
|
|
+# define a12 (p->u.s[7])
|
|
+# define a13 (p->u.s[8])
|
|
+# define a14 (p->u.s[9])
|
|
+# define a20 (p->u.s[10])
|
|
+# define a21 (p->u.s[11])
|
|
+# define a22 (p->u.s[12])
|
|
+# define a23 (p->u.s[13])
|
|
+# define a24 (p->u.s[14])
|
|
+# define a30 (p->u.s[15])
|
|
+# define a31 (p->u.s[16])
|
|
+# define a32 (p->u.s[17])
|
|
+# define a33 (p->u.s[18])
|
|
+# define a34 (p->u.s[19])
|
|
+# define a40 (p->u.s[20])
|
|
+# define a41 (p->u.s[21])
|
|
+# define a42 (p->u.s[22])
|
|
+# define a43 (p->u.s[23])
|
|
+# define a44 (p->u.s[24])
|
|
# define ROL64(a,x) ((a<<x)|(a>>(64-x)))
|
|
|
|
for(i=0; i<24; i+=4){
|
|
- C0 = A00^A10^A20^A30^A40;
|
|
- C1 = A01^A11^A21^A31^A41;
|
|
- C2 = A02^A12^A22^A32^A42;
|
|
- C3 = A03^A13^A23^A33^A43;
|
|
- C4 = A04^A14^A24^A34^A44;
|
|
- D0 = C4^ROL64(C1, 1);
|
|
- D1 = C0^ROL64(C2, 1);
|
|
- D2 = C1^ROL64(C3, 1);
|
|
- D3 = C2^ROL64(C4, 1);
|
|
- D4 = C3^ROL64(C0, 1);
|
|
+ c0 = a00^a10^a20^a30^a40;
|
|
+ c1 = a01^a11^a21^a31^a41;
|
|
+ c2 = a02^a12^a22^a32^a42;
|
|
+ c3 = a03^a13^a23^a33^a43;
|
|
+ c4 = a04^a14^a24^a34^a44;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
|
|
- B0 = (A00^D0);
|
|
- B1 = ROL64((A11^D1), 44);
|
|
- B2 = ROL64((A22^D2), 43);
|
|
- B3 = ROL64((A33^D3), 21);
|
|
- B4 = ROL64((A44^D4), 14);
|
|
- A00 = B0 ^((~B1)& B2 );
|
|
- A00 ^= RC[i];
|
|
- A11 = B1 ^((~B2)& B3 );
|
|
- A22 = B2 ^((~B3)& B4 );
|
|
- A33 = B3 ^((~B4)& B0 );
|
|
- A44 = B4 ^((~B0)& B1 );
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a11^d1), 44);
|
|
+ b2 = ROL64((a22^d2), 43);
|
|
+ b3 = ROL64((a33^d3), 21);
|
|
+ b4 = ROL64((a44^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i];
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
|
|
- B2 = ROL64((A20^D0), 3);
|
|
- B3 = ROL64((A31^D1), 45);
|
|
- B4 = ROL64((A42^D2), 61);
|
|
- B0 = ROL64((A03^D3), 28);
|
|
- B1 = ROL64((A14^D4), 20);
|
|
- A20 = B0 ^((~B1)& B2 );
|
|
- A31 = B1 ^((~B2)& B3 );
|
|
- A42 = B2 ^((~B3)& B4 );
|
|
- A03 = B3 ^((~B4)& B0 );
|
|
- A14 = B4 ^((~B0)& B1 );
|
|
+ b2 = ROL64((a20^d0), 3);
|
|
+ b3 = ROL64((a31^d1), 45);
|
|
+ b4 = ROL64((a42^d2), 61);
|
|
+ b0 = ROL64((a03^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
|
|
- B4 = ROL64((A40^D0), 18);
|
|
- B0 = ROL64((A01^D1), 1);
|
|
- B1 = ROL64((A12^D2), 6);
|
|
- B2 = ROL64((A23^D3), 25);
|
|
- B3 = ROL64((A34^D4), 8);
|
|
- A40 = B0 ^((~B1)& B2 );
|
|
- A01 = B1 ^((~B2)& B3 );
|
|
- A12 = B2 ^((~B3)& B4 );
|
|
- A23 = B3 ^((~B4)& B0 );
|
|
- A34 = B4 ^((~B0)& B1 );
|
|
+ b4 = ROL64((a40^d0), 18);
|
|
+ b0 = ROL64((a01^d1), 1);
|
|
+ b1 = ROL64((a12^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a34^d4), 8);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
|
|
- B1 = ROL64((A10^D0), 36);
|
|
- B2 = ROL64((A21^D1), 10);
|
|
- B3 = ROL64((A32^D2), 15);
|
|
- B4 = ROL64((A43^D3), 56);
|
|
- B0 = ROL64((A04^D4), 27);
|
|
- A10 = B0 ^((~B1)& B2 );
|
|
- A21 = B1 ^((~B2)& B3 );
|
|
- A32 = B2 ^((~B3)& B4 );
|
|
- A43 = B3 ^((~B4)& B0 );
|
|
- A04 = B4 ^((~B0)& B1 );
|
|
+ b1 = ROL64((a10^d0), 36);
|
|
+ b2 = ROL64((a21^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a43^d3), 56);
|
|
+ b0 = ROL64((a04^d4), 27);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
|
|
- B3 = ROL64((A30^D0), 41);
|
|
- B4 = ROL64((A41^D1), 2);
|
|
- B0 = ROL64((A02^D2), 62);
|
|
- B1 = ROL64((A13^D3), 55);
|
|
- B2 = ROL64((A24^D4), 39);
|
|
- A30 = B0 ^((~B1)& B2 );
|
|
- A41 = B1 ^((~B2)& B3 );
|
|
- A02 = B2 ^((~B3)& B4 );
|
|
- A13 = B3 ^((~B4)& B0 );
|
|
- A24 = B4 ^((~B0)& B1 );
|
|
+ b3 = ROL64((a30^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a02^d2), 62);
|
|
+ b1 = ROL64((a13^d3), 55);
|
|
+ b2 = ROL64((a24^d4), 39);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
|
|
- C0 = A00^A20^A40^A10^A30;
|
|
- C1 = A11^A31^A01^A21^A41;
|
|
- C2 = A22^A42^A12^A32^A02;
|
|
- C3 = A33^A03^A23^A43^A13;
|
|
- C4 = A44^A14^A34^A04^A24;
|
|
- D0 = C4^ROL64(C1, 1);
|
|
- D1 = C0^ROL64(C2, 1);
|
|
- D2 = C1^ROL64(C3, 1);
|
|
- D3 = C2^ROL64(C4, 1);
|
|
- D4 = C3^ROL64(C0, 1);
|
|
+ c0 = a00^a20^a40^a10^a30;
|
|
+ c1 = a11^a31^a01^a21^a41;
|
|
+ c2 = a22^a42^a12^a32^a02;
|
|
+ c3 = a33^a03^a23^a43^a13;
|
|
+ c4 = a44^a14^a34^a04^a24;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
|
|
- B0 = (A00^D0);
|
|
- B1 = ROL64((A31^D1), 44);
|
|
- B2 = ROL64((A12^D2), 43);
|
|
- B3 = ROL64((A43^D3), 21);
|
|
- B4 = ROL64((A24^D4), 14);
|
|
- A00 = B0 ^((~B1)& B2 );
|
|
- A00 ^= RC[i+1];
|
|
- A31 = B1 ^((~B2)& B3 );
|
|
- A12 = B2 ^((~B3)& B4 );
|
|
- A43 = B3 ^((~B4)& B0 );
|
|
- A24 = B4 ^((~B0)& B1 );
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a31^d1), 44);
|
|
+ b2 = ROL64((a12^d2), 43);
|
|
+ b3 = ROL64((a43^d3), 21);
|
|
+ b4 = ROL64((a24^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i+1];
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
|
|
- B2 = ROL64((A40^D0), 3);
|
|
- B3 = ROL64((A21^D1), 45);
|
|
- B4 = ROL64((A02^D2), 61);
|
|
- B0 = ROL64((A33^D3), 28);
|
|
- B1 = ROL64((A14^D4), 20);
|
|
- A40 = B0 ^((~B1)& B2 );
|
|
- A21 = B1 ^((~B2)& B3 );
|
|
- A02 = B2 ^((~B3)& B4 );
|
|
- A33 = B3 ^((~B4)& B0 );
|
|
- A14 = B4 ^((~B0)& B1 );
|
|
+ b2 = ROL64((a40^d0), 3);
|
|
+ b3 = ROL64((a21^d1), 45);
|
|
+ b4 = ROL64((a02^d2), 61);
|
|
+ b0 = ROL64((a33^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
|
|
- B4 = ROL64((A30^D0), 18);
|
|
- B0 = ROL64((A11^D1), 1);
|
|
- B1 = ROL64((A42^D2), 6);
|
|
- B2 = ROL64((A23^D3), 25);
|
|
- B3 = ROL64((A04^D4), 8);
|
|
- A30 = B0 ^((~B1)& B2 );
|
|
- A11 = B1 ^((~B2)& B3 );
|
|
- A42 = B2 ^((~B3)& B4 );
|
|
- A23 = B3 ^((~B4)& B0 );
|
|
- A04 = B4 ^((~B0)& B1 );
|
|
+ b4 = ROL64((a30^d0), 18);
|
|
+ b0 = ROL64((a11^d1), 1);
|
|
+ b1 = ROL64((a42^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a04^d4), 8);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
|
|
- B1 = ROL64((A20^D0), 36);
|
|
- B2 = ROL64((A01^D1), 10);
|
|
- B3 = ROL64((A32^D2), 15);
|
|
- B4 = ROL64((A13^D3), 56);
|
|
- B0 = ROL64((A44^D4), 27);
|
|
- A20 = B0 ^((~B1)& B2 );
|
|
- A01 = B1 ^((~B2)& B3 );
|
|
- A32 = B2 ^((~B3)& B4 );
|
|
- A13 = B3 ^((~B4)& B0 );
|
|
- A44 = B4 ^((~B0)& B1 );
|
|
+ b1 = ROL64((a20^d0), 36);
|
|
+ b2 = ROL64((a01^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a13^d3), 56);
|
|
+ b0 = ROL64((a44^d4), 27);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
|
|
- B3 = ROL64((A10^D0), 41);
|
|
- B4 = ROL64((A41^D1), 2);
|
|
- B0 = ROL64((A22^D2), 62);
|
|
- B1 = ROL64((A03^D3), 55);
|
|
- B2 = ROL64((A34^D4), 39);
|
|
- A10 = B0 ^((~B1)& B2 );
|
|
- A41 = B1 ^((~B2)& B3 );
|
|
- A22 = B2 ^((~B3)& B4 );
|
|
- A03 = B3 ^((~B4)& B0 );
|
|
- A34 = B4 ^((~B0)& B1 );
|
|
+ b3 = ROL64((a10^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a22^d2), 62);
|
|
+ b1 = ROL64((a03^d3), 55);
|
|
+ b2 = ROL64((a34^d4), 39);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
|
|
- C0 = A00^A40^A30^A20^A10;
|
|
- C1 = A31^A21^A11^A01^A41;
|
|
- C2 = A12^A02^A42^A32^A22;
|
|
- C3 = A43^A33^A23^A13^A03;
|
|
- C4 = A24^A14^A04^A44^A34;
|
|
- D0 = C4^ROL64(C1, 1);
|
|
- D1 = C0^ROL64(C2, 1);
|
|
- D2 = C1^ROL64(C3, 1);
|
|
- D3 = C2^ROL64(C4, 1);
|
|
- D4 = C3^ROL64(C0, 1);
|
|
+ c0 = a00^a40^a30^a20^a10;
|
|
+ c1 = a31^a21^a11^a01^a41;
|
|
+ c2 = a12^a02^a42^a32^a22;
|
|
+ c3 = a43^a33^a23^a13^a03;
|
|
+ c4 = a24^a14^a04^a44^a34;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
|
|
- B0 = (A00^D0);
|
|
- B1 = ROL64((A21^D1), 44);
|
|
- B2 = ROL64((A42^D2), 43);
|
|
- B3 = ROL64((A13^D3), 21);
|
|
- B4 = ROL64((A34^D4), 14);
|
|
- A00 = B0 ^((~B1)& B2 );
|
|
- A00 ^= RC[i+2];
|
|
- A21 = B1 ^((~B2)& B3 );
|
|
- A42 = B2 ^((~B3)& B4 );
|
|
- A13 = B3 ^((~B4)& B0 );
|
|
- A34 = B4 ^((~B0)& B1 );
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a21^d1), 44);
|
|
+ b2 = ROL64((a42^d2), 43);
|
|
+ b3 = ROL64((a13^d3), 21);
|
|
+ b4 = ROL64((a34^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i+2];
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
|
|
- B2 = ROL64((A30^D0), 3);
|
|
- B3 = ROL64((A01^D1), 45);
|
|
- B4 = ROL64((A22^D2), 61);
|
|
- B0 = ROL64((A43^D3), 28);
|
|
- B1 = ROL64((A14^D4), 20);
|
|
- A30 = B0 ^((~B1)& B2 );
|
|
- A01 = B1 ^((~B2)& B3 );
|
|
- A22 = B2 ^((~B3)& B4 );
|
|
- A43 = B3 ^((~B4)& B0 );
|
|
- A14 = B4 ^((~B0)& B1 );
|
|
+ b2 = ROL64((a30^d0), 3);
|
|
+ b3 = ROL64((a01^d1), 45);
|
|
+ b4 = ROL64((a22^d2), 61);
|
|
+ b0 = ROL64((a43^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
|
|
- B4 = ROL64((A10^D0), 18);
|
|
- B0 = ROL64((A31^D1), 1);
|
|
- B1 = ROL64((A02^D2), 6);
|
|
- B2 = ROL64((A23^D3), 25);
|
|
- B3 = ROL64((A44^D4), 8);
|
|
- A10 = B0 ^((~B1)& B2 );
|
|
- A31 = B1 ^((~B2)& B3 );
|
|
- A02 = B2 ^((~B3)& B4 );
|
|
- A23 = B3 ^((~B4)& B0 );
|
|
- A44 = B4 ^((~B0)& B1 );
|
|
+ b4 = ROL64((a10^d0), 18);
|
|
+ b0 = ROL64((a31^d1), 1);
|
|
+ b1 = ROL64((a02^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a44^d4), 8);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
|
|
- B1 = ROL64((A40^D0), 36);
|
|
- B2 = ROL64((A11^D1), 10);
|
|
- B3 = ROL64((A32^D2), 15);
|
|
- B4 = ROL64((A03^D3), 56);
|
|
- B0 = ROL64((A24^D4), 27);
|
|
- A40 = B0 ^((~B1)& B2 );
|
|
- A11 = B1 ^((~B2)& B3 );
|
|
- A32 = B2 ^((~B3)& B4 );
|
|
- A03 = B3 ^((~B4)& B0 );
|
|
- A24 = B4 ^((~B0)& B1 );
|
|
+ b1 = ROL64((a40^d0), 36);
|
|
+ b2 = ROL64((a11^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a03^d3), 56);
|
|
+ b0 = ROL64((a24^d4), 27);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
|
|
- B3 = ROL64((A20^D0), 41);
|
|
- B4 = ROL64((A41^D1), 2);
|
|
- B0 = ROL64((A12^D2), 62);
|
|
- B1 = ROL64((A33^D3), 55);
|
|
- B2 = ROL64((A04^D4), 39);
|
|
- A20 = B0 ^((~B1)& B2 );
|
|
- A41 = B1 ^((~B2)& B3 );
|
|
- A12 = B2 ^((~B3)& B4 );
|
|
- A33 = B3 ^((~B4)& B0 );
|
|
- A04 = B4 ^((~B0)& B1 );
|
|
+ b3 = ROL64((a20^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a12^d2), 62);
|
|
+ b1 = ROL64((a33^d3), 55);
|
|
+ b2 = ROL64((a04^d4), 39);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
|
|
- C0 = A00^A30^A10^A40^A20;
|
|
- C1 = A21^A01^A31^A11^A41;
|
|
- C2 = A42^A22^A02^A32^A12;
|
|
- C3 = A13^A43^A23^A03^A33;
|
|
- C4 = A34^A14^A44^A24^A04;
|
|
- D0 = C4^ROL64(C1, 1);
|
|
- D1 = C0^ROL64(C2, 1);
|
|
- D2 = C1^ROL64(C3, 1);
|
|
- D3 = C2^ROL64(C4, 1);
|
|
- D4 = C3^ROL64(C0, 1);
|
|
+ c0 = a00^a30^a10^a40^a20;
|
|
+ c1 = a21^a01^a31^a11^a41;
|
|
+ c2 = a42^a22^a02^a32^a12;
|
|
+ c3 = a13^a43^a23^a03^a33;
|
|
+ c4 = a34^a14^a44^a24^a04;
|
|
+ d0 = c4^ROL64(c1, 1);
|
|
+ d1 = c0^ROL64(c2, 1);
|
|
+ d2 = c1^ROL64(c3, 1);
|
|
+ d3 = c2^ROL64(c4, 1);
|
|
+ d4 = c3^ROL64(c0, 1);
|
|
|
|
- B0 = (A00^D0);
|
|
- B1 = ROL64((A01^D1), 44);
|
|
- B2 = ROL64((A02^D2), 43);
|
|
- B3 = ROL64((A03^D3), 21);
|
|
- B4 = ROL64((A04^D4), 14);
|
|
- A00 = B0 ^((~B1)& B2 );
|
|
- A00 ^= RC[i+3];
|
|
- A01 = B1 ^((~B2)& B3 );
|
|
- A02 = B2 ^((~B3)& B4 );
|
|
- A03 = B3 ^((~B4)& B0 );
|
|
- A04 = B4 ^((~B0)& B1 );
|
|
+ b0 = (a00^d0);
|
|
+ b1 = ROL64((a01^d1), 44);
|
|
+ b2 = ROL64((a02^d2), 43);
|
|
+ b3 = ROL64((a03^d3), 21);
|
|
+ b4 = ROL64((a04^d4), 14);
|
|
+ a00 = b0 ^((~b1)& b2 );
|
|
+ a00 ^= RC[i+3];
|
|
+ a01 = b1 ^((~b2)& b3 );
|
|
+ a02 = b2 ^((~b3)& b4 );
|
|
+ a03 = b3 ^((~b4)& b0 );
|
|
+ a04 = b4 ^((~b0)& b1 );
|
|
|
|
- B2 = ROL64((A10^D0), 3);
|
|
- B3 = ROL64((A11^D1), 45);
|
|
- B4 = ROL64((A12^D2), 61);
|
|
- B0 = ROL64((A13^D3), 28);
|
|
- B1 = ROL64((A14^D4), 20);
|
|
- A10 = B0 ^((~B1)& B2 );
|
|
- A11 = B1 ^((~B2)& B3 );
|
|
- A12 = B2 ^((~B3)& B4 );
|
|
- A13 = B3 ^((~B4)& B0 );
|
|
- A14 = B4 ^((~B0)& B1 );
|
|
+ b2 = ROL64((a10^d0), 3);
|
|
+ b3 = ROL64((a11^d1), 45);
|
|
+ b4 = ROL64((a12^d2), 61);
|
|
+ b0 = ROL64((a13^d3), 28);
|
|
+ b1 = ROL64((a14^d4), 20);
|
|
+ a10 = b0 ^((~b1)& b2 );
|
|
+ a11 = b1 ^((~b2)& b3 );
|
|
+ a12 = b2 ^((~b3)& b4 );
|
|
+ a13 = b3 ^((~b4)& b0 );
|
|
+ a14 = b4 ^((~b0)& b1 );
|
|
|
|
- B4 = ROL64((A20^D0), 18);
|
|
- B0 = ROL64((A21^D1), 1);
|
|
- B1 = ROL64((A22^D2), 6);
|
|
- B2 = ROL64((A23^D3), 25);
|
|
- B3 = ROL64((A24^D4), 8);
|
|
- A20 = B0 ^((~B1)& B2 );
|
|
- A21 = B1 ^((~B2)& B3 );
|
|
- A22 = B2 ^((~B3)& B4 );
|
|
- A23 = B3 ^((~B4)& B0 );
|
|
- A24 = B4 ^((~B0)& B1 );
|
|
+ b4 = ROL64((a20^d0), 18);
|
|
+ b0 = ROL64((a21^d1), 1);
|
|
+ b1 = ROL64((a22^d2), 6);
|
|
+ b2 = ROL64((a23^d3), 25);
|
|
+ b3 = ROL64((a24^d4), 8);
|
|
+ a20 = b0 ^((~b1)& b2 );
|
|
+ a21 = b1 ^((~b2)& b3 );
|
|
+ a22 = b2 ^((~b3)& b4 );
|
|
+ a23 = b3 ^((~b4)& b0 );
|
|
+ a24 = b4 ^((~b0)& b1 );
|
|
|
|
- B1 = ROL64((A30^D0), 36);
|
|
- B2 = ROL64((A31^D1), 10);
|
|
- B3 = ROL64((A32^D2), 15);
|
|
- B4 = ROL64((A33^D3), 56);
|
|
- B0 = ROL64((A34^D4), 27);
|
|
- A30 = B0 ^((~B1)& B2 );
|
|
- A31 = B1 ^((~B2)& B3 );
|
|
- A32 = B2 ^((~B3)& B4 );
|
|
- A33 = B3 ^((~B4)& B0 );
|
|
- A34 = B4 ^((~B0)& B1 );
|
|
+ b1 = ROL64((a30^d0), 36);
|
|
+ b2 = ROL64((a31^d1), 10);
|
|
+ b3 = ROL64((a32^d2), 15);
|
|
+ b4 = ROL64((a33^d3), 56);
|
|
+ b0 = ROL64((a34^d4), 27);
|
|
+ a30 = b0 ^((~b1)& b2 );
|
|
+ a31 = b1 ^((~b2)& b3 );
|
|
+ a32 = b2 ^((~b3)& b4 );
|
|
+ a33 = b3 ^((~b4)& b0 );
|
|
+ a34 = b4 ^((~b0)& b1 );
|
|
|
|
- B3 = ROL64((A40^D0), 41);
|
|
- B4 = ROL64((A41^D1), 2);
|
|
- B0 = ROL64((A42^D2), 62);
|
|
- B1 = ROL64((A43^D3), 55);
|
|
- B2 = ROL64((A44^D4), 39);
|
|
- A40 = B0 ^((~B1)& B2 );
|
|
- A41 = B1 ^((~B2)& B3 );
|
|
- A42 = B2 ^((~B3)& B4 );
|
|
- A43 = B3 ^((~B4)& B0 );
|
|
- A44 = B4 ^((~B0)& B1 );
|
|
+ b3 = ROL64((a40^d0), 41);
|
|
+ b4 = ROL64((a41^d1), 2);
|
|
+ b0 = ROL64((a42^d2), 62);
|
|
+ b1 = ROL64((a43^d3), 55);
|
|
+ b2 = ROL64((a44^d4), 39);
|
|
+ a40 = b0 ^((~b1)& b2 );
|
|
+ a41 = b1 ^((~b2)& b3 );
|
|
+ a42 = b2 ^((~b3)& b4 );
|
|
+ a43 = b3 ^((~b4)& b0 );
|
|
+ a44 = b4 ^((~b0)& b1 );
|
|
}
|
|
}
|
|
|
|
@@ -1499,7 +1992,7 @@
|
|
|
|
|
|
#ifdef _WIN32
|
|
-__declspec(dllexport)
|
|
+
|
|
#endif
|
|
int sqlite3_shathree_init(
|
|
sqlite3 *db,
|
|
@@ -1541,29 +2034,122 @@
|
|
******************************************************************************
|
|
**
|
|
** This SQLite extension implements SQL functions readfile() and
|
|
-** writefile().
|
|
+** writefile(), and eponymous virtual type "fsdir".
|
|
+**
|
|
+** WRITEFILE(FILE, DATA [, MODE [, MTIME]]):
|
|
+**
|
|
+** If neither of the optional arguments is present, then this UDF
|
|
+** function writes blob DATA to file FILE. If successful, the number
|
|
+** of bytes written is returned. If an error occurs, NULL is returned.
|
|
+**
|
|
+** If the first option argument - MODE - is present, then it must
|
|
+** be passed an integer value that corresponds to a POSIX mode
|
|
+** value (file type + permissions, as returned in the stat.st_mode
|
|
+** field by the stat() system call). Three types of files may
|
|
+** be written/created:
|
|
+**
|
|
+** regular files: (mode & 0170000)==0100000
|
|
+** symbolic links: (mode & 0170000)==0120000
|
|
+** directories: (mode & 0170000)==0040000
|
|
+**
|
|
+** For a directory, the DATA is ignored. For a symbolic link, it is
|
|
+** interpreted as text and used as the target of the link. For a
|
|
+** regular file, it is interpreted as a blob and written into the
|
|
+** named file. Regardless of the type of file, its permissions are
|
|
+** set to (mode & 0777) before returning.
|
|
+**
|
|
+** If the optional MTIME argument is present, then it is interpreted
|
|
+** as an integer - the number of seconds since the unix epoch. The
|
|
+** modification-time of the target file is set to this value before
|
|
+** returning.
|
|
+**
|
|
+** If three or more arguments are passed to this function and an
|
|
+** error is encountered, an exception is raised.
|
|
+**
|
|
+** READFILE(FILE):
|
|
+**
|
|
+** Read and return the contents of file FILE (type blob) from disk.
|
|
+**
|
|
+** FSDIR:
|
|
+**
|
|
+** Used as follows:
|
|
+**
|
|
+** SELECT * FROM fsdir($path [, $dir]);
|
|
+**
|
|
+** Parameter $path is an absolute or relative pathname. If the file that it
|
|
+** refers to does not exist, it is an error. If the path refers to a regular
|
|
+** file or symbolic link, it returns a single row. Or, if the path refers
|
|
+** to a directory, it returns one row for the directory, and one row for each
|
|
+** file within the hierarchy rooted at $path.
|
|
+**
|
|
+** Each row has the following columns:
|
|
+**
|
|
+** name: Path to file or directory (text value).
|
|
+** mode: Value of stat.st_mode for directory entry (an integer).
|
|
+** mtime: Value of stat.st_mtime for directory entry (an integer).
|
|
+** data: For a regular file, a blob containing the file data. For a
|
|
+** symlink, a text value containing the text of the link. For a
|
|
+** directory, NULL.
|
|
+**
|
|
+** If a non-NULL value is specified for the optional $dir parameter and
|
|
+** $path is a relative path, then $path is interpreted relative to $dir.
|
|
+** And the paths returned in the "name" column of the table are also
|
|
+** relative to directory $dir.
|
|
*/
|
|
SQLITE_EXTENSION_INIT1
|
|
#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+# include <unistd.h>
|
|
+# include <dirent.h>
|
|
+# include <utime.h>
|
|
+# include <sys/time.h>
|
|
+#else
|
|
+# include "windows.h"
|
|
+# include <io.h>
|
|
+# include <direct.h>
|
|
+/* # include "test_windirent.h" */
|
|
+# define dirent DIRENT
|
|
+# ifndef chmod
|
|
+# define chmod _chmod
|
|
+# endif
|
|
+# ifndef stat
|
|
+# define stat _stat
|
|
+# endif
|
|
+# define mkdir(path,mode) _mkdir(path)
|
|
+# define lstat(path,buf) stat(path,buf)
|
|
+#endif
|
|
+#include <time.h>
|
|
+#include <errno.h>
|
|
+
|
|
+
|
|
/*
|
|
-** Implementation of the "readfile(X)" SQL function. The entire content
|
|
-** of the file named X is read and returned as a BLOB. NULL is returned
|
|
-** if the file does not exist or is unreadable.
|
|
+** Structure of the fsdir() table-valued function
|
|
*/
|
|
-static void readfileFunc(
|
|
- sqlite3_context *context,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- const char *zName;
|
|
+ /* 0 1 2 3 4 5 */
|
|
+#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
|
|
+#define FSDIR_COLUMN_NAME 0 /* Name of the file */
|
|
+#define FSDIR_COLUMN_MODE 1 /* Access mode */
|
|
+#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
|
|
+#define FSDIR_COLUMN_DATA 3 /* File content */
|
|
+#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
|
|
+#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
|
|
+
|
|
+
|
|
+/*
|
|
+** Set the result stored by context ctx to a blob containing the
|
|
+** contents of file zName.
|
|
+*/
|
|
+static void readFileContents(sqlite3_context *ctx, const char *zName){
|
|
FILE *in;
|
|
long nIn;
|
|
void *pBuf;
|
|
|
|
- (void)(argc); /* Unused parameter */
|
|
- zName = (const char*)sqlite3_value_text(argv[0]);
|
|
- if( zName==0 ) return;
|
|
in = fopen(zName, "rb");
|
|
if( in==0 ) return;
|
|
fseek(in, 0, SEEK_END);
|
|
@@ -1571,7 +2157,7 @@
|
|
rewind(in);
|
|
pBuf = sqlite3_malloc( nIn );
|
|
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
|
|
- sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
|
|
+ sqlite3_result_blob(ctx, pBuf, nIn, sqlite3_free);
|
|
}else{
|
|
sqlite3_free(pBuf);
|
|
}
|
|
@@ -1579,39 +2165,807 @@
|
|
}
|
|
|
|
/*
|
|
-** Implementation of the "writefile(X,Y)" SQL function. The argument Y
|
|
-** is written into file X. The number of bytes written is returned. Or
|
|
-** NULL is returned if something goes wrong, such as being unable to open
|
|
-** file X for writing.
|
|
+** Implementation of the "readfile(X)" SQL function. The entire content
|
|
+** of the file named X is read and returned as a BLOB. NULL is returned
|
|
+** if the file does not exist or is unreadable.
|
|
*/
|
|
+static void readfileFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *zName;
|
|
+ (void)(argc); /* Unused parameter */
|
|
+ zName = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zName==0 ) return;
|
|
+ readFileContents(context, zName);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set the error message contained in context ctx to the results of
|
|
+** vprintf(zFmt, ...).
|
|
+*/
|
|
+static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
|
|
+ char *zMsg = 0;
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ zMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ sqlite3_result_error(ctx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+#if defined(_WIN32)
|
|
+/*
|
|
+** This function is designed to convert a Win32 FILETIME structure into the
|
|
+** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC).
|
|
+*/
|
|
+static sqlite3_uint64 fileTimeToUnixTime(
|
|
+ LPFILETIME pFileTime
|
|
+){
|
|
+ SYSTEMTIME epochSystemTime;
|
|
+ ULARGE_INTEGER epochIntervals;
|
|
+ FILETIME epochFileTime;
|
|
+ ULARGE_INTEGER fileIntervals;
|
|
+
|
|
+ memset(&epochSystemTime, 0, sizeof(SYSTEMTIME));
|
|
+ epochSystemTime.wYear = 1970;
|
|
+ epochSystemTime.wMonth = 1;
|
|
+ epochSystemTime.wDay = 1;
|
|
+ SystemTimeToFileTime(&epochSystemTime, &epochFileTime);
|
|
+ epochIntervals.LowPart = epochFileTime.dwLowDateTime;
|
|
+ epochIntervals.HighPart = epochFileTime.dwHighDateTime;
|
|
+
|
|
+ fileIntervals.LowPart = pFileTime->dwLowDateTime;
|
|
+ fileIntervals.HighPart = pFileTime->dwHighDateTime;
|
|
+
|
|
+ return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function attempts to normalize the time values found in the stat()
|
|
+** buffer to UTC. This is necessary on Win32, where the runtime library
|
|
+** appears to return these values as local times.
|
|
+*/
|
|
+static void statTimesToUtc(
|
|
+ const char *zPath,
|
|
+ struct stat *pStatBuf
|
|
+){
|
|
+ HANDLE hFindFile;
|
|
+ WIN32_FIND_DATAW fd;
|
|
+ LPWSTR zUnicodeName;
|
|
+ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
|
|
+ zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
|
|
+ if( zUnicodeName ){
|
|
+ memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
|
|
+ hFindFile = FindFirstFileW(zUnicodeName, &fd);
|
|
+ if( hFindFile!=NULL ){
|
|
+ pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
|
|
+ pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
|
|
+ pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
|
|
+ FindClose(hFindFile);
|
|
+ }
|
|
+ sqlite3_free(zUnicodeName);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** This function is used in place of stat(). On Windows, special handling
|
|
+** is required in order for the included time to be returned as UTC. On all
|
|
+** other systems, this function simply calls stat().
|
|
+*/
|
|
+static int fileStat(
|
|
+ const char *zPath,
|
|
+ struct stat *pStatBuf
|
|
+){
|
|
+#if defined(_WIN32)
|
|
+ int rc = stat(zPath, pStatBuf);
|
|
+ if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
|
+ return rc;
|
|
+#else
|
|
+ return stat(zPath, pStatBuf);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is used in place of lstat(). On Windows, special handling
|
|
+** is required in order for the included time to be returned as UTC. On all
|
|
+** other systems, this function simply calls lstat().
|
|
+*/
|
|
+static int fileLinkStat(
|
|
+ const char *zPath,
|
|
+ struct stat *pStatBuf
|
|
+){
|
|
+#if defined(_WIN32)
|
|
+ int rc = lstat(zPath, pStatBuf);
|
|
+ if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
|
+ return rc;
|
|
+#else
|
|
+ return lstat(zPath, pStatBuf);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** Argument zFile is the name of a file that will be created and/or written
|
|
+** by SQL function writefile(). This function ensures that the directory
|
|
+** zFile will be written to exists, creating it if required. The permissions
|
|
+** for any path components created by this function are set to (mode&0777).
|
|
+**
|
|
+** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
|
|
+** SQLITE_OK is returned if the directory is successfully created, or
|
|
+** SQLITE_ERROR otherwise.
|
|
+*/
|
|
+static int makeDirectory(
|
|
+ const char *zFile,
|
|
+ mode_t mode
|
|
+){
|
|
+ char *zCopy = sqlite3_mprintf("%s", zFile);
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( zCopy==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int nCopy = (int)strlen(zCopy);
|
|
+ int i = 1;
|
|
+
|
|
+ while( rc==SQLITE_OK ){
|
|
+ struct stat sStat;
|
|
+ int rc2;
|
|
+
|
|
+ for(; zCopy[i]!='/' && i<nCopy; i++);
|
|
+ if( i==nCopy ) break;
|
|
+ zCopy[i] = '\0';
|
|
+
|
|
+ rc2 = fileStat(zCopy, &sStat);
|
|
+ if( rc2!=0 ){
|
|
+ if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
|
|
+ }
|
|
+ zCopy[i] = '/';
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zCopy);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function does the work for the writefile() UDF. Refer to
|
|
+** header comments at the top of this file for details.
|
|
+*/
|
|
+static int writeFile(
|
|
+ sqlite3_context *pCtx, /* Context to return bytes written in */
|
|
+ const char *zFile, /* File to write */
|
|
+ sqlite3_value *pData, /* Data to write */
|
|
+ mode_t mode, /* MODE parameter passed to writefile() */
|
|
+ sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
|
|
+){
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+ if( S_ISLNK(mode) ){
|
|
+ const char *zTo = (const char*)sqlite3_value_text(pData);
|
|
+ if( symlink(zTo, zFile)<0 ) return 1;
|
|
+ }else
|
|
+#endif
|
|
+ {
|
|
+ if( S_ISDIR(mode) ){
|
|
+ if( mkdir(zFile, mode) ){
|
|
+ /* The mkdir() call to create the directory failed. This might not
|
|
+ ** be an error though - if there is already a directory at the same
|
|
+ ** path and either the permissions already match or can be changed
|
|
+ ** to do so using chmod(), it is not an error. */
|
|
+ struct stat sStat;
|
|
+ if( errno!=EEXIST
|
|
+ || 0!=fileStat(zFile, &sStat)
|
|
+ || !S_ISDIR(sStat.st_mode)
|
|
+ || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
|
|
+ ){
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_int64 nWrite = 0;
|
|
+ const char *z;
|
|
+ int rc = 0;
|
|
+ FILE *out = fopen(zFile, "wb");
|
|
+ if( out==0 ) return 1;
|
|
+ z = (const char*)sqlite3_value_blob(pData);
|
|
+ if( z ){
|
|
+ sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out);
|
|
+ nWrite = sqlite3_value_bytes(pData);
|
|
+ if( nWrite!=n ){
|
|
+ rc = 1;
|
|
+ }
|
|
+ }
|
|
+ fclose(out);
|
|
+ if( rc==0 && mode && chmod(zFile, mode & 0777) ){
|
|
+ rc = 1;
|
|
+ }
|
|
+ if( rc ) return 2;
|
|
+ sqlite3_result_int64(pCtx, nWrite);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( mtime>=0 ){
|
|
+#if defined(_WIN32)
|
|
+ /* Windows */
|
|
+ FILETIME lastAccess;
|
|
+ FILETIME lastWrite;
|
|
+ SYSTEMTIME currentTime;
|
|
+ LONGLONG intervals;
|
|
+ HANDLE hFile;
|
|
+ LPWSTR zUnicodeName;
|
|
+ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
|
|
+
|
|
+ GetSystemTime(¤tTime);
|
|
+ SystemTimeToFileTime(¤tTime, &lastAccess);
|
|
+ intervals = Int32x32To64(mtime, 10000000) + 116444736000000000;
|
|
+ lastWrite.dwLowDateTime = (DWORD)intervals;
|
|
+ lastWrite.dwHighDateTime = intervals >> 32;
|
|
+ zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
|
|
+ if( zUnicodeName==0 ){
|
|
+ return 1;
|
|
+ }
|
|
+ hFile = CreateFileW(
|
|
+ zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
|
|
+ FILE_FLAG_BACKUP_SEMANTICS, NULL
|
|
+ );
|
|
+ sqlite3_free(zUnicodeName);
|
|
+ if( hFile!=INVALID_HANDLE_VALUE ){
|
|
+ BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite);
|
|
+ CloseHandle(hFile);
|
|
+ return !bResult;
|
|
+ }else{
|
|
+ return 1;
|
|
+ }
|
|
+#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
|
|
+ /* Recent unix */
|
|
+ struct timespec times[2];
|
|
+ times[0].tv_nsec = times[1].tv_nsec = 0;
|
|
+ times[0].tv_sec = time(0);
|
|
+ times[1].tv_sec = mtime;
|
|
+ if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
|
|
+ return 1;
|
|
+ }
|
|
+#else
|
|
+ /* Legacy unix */
|
|
+ struct timeval times[2];
|
|
+ times[0].tv_usec = times[1].tv_usec = 0;
|
|
+ times[0].tv_sec = time(0);
|
|
+ times[1].tv_sec = mtime;
|
|
+ if( utimes(zFile, times) ){
|
|
+ return 1;
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the "writefile(W,X[,Y[,Z]]])" SQL function.
|
|
+** Refer to header comments at the top of this file for details.
|
|
+*/
|
|
static void writefileFunc(
|
|
sqlite3_context *context,
|
|
int argc,
|
|
sqlite3_value **argv
|
|
){
|
|
- FILE *out;
|
|
- const char *z;
|
|
- sqlite3_int64 rc;
|
|
const char *zFile;
|
|
+ mode_t mode = 0;
|
|
+ int res;
|
|
+ sqlite3_int64 mtime = -1;
|
|
|
|
- (void)(argc); /* Unused parameter */
|
|
+ if( argc<2 || argc>4 ){
|
|
+ sqlite3_result_error(context,
|
|
+ "wrong number of arguments to function writefile()", -1
|
|
+ );
|
|
+ return;
|
|
+ }
|
|
+
|
|
zFile = (const char*)sqlite3_value_text(argv[0]);
|
|
if( zFile==0 ) return;
|
|
- out = fopen(zFile, "wb");
|
|
- if( out==0 ) return;
|
|
- z = (const char*)sqlite3_value_blob(argv[1]);
|
|
- if( z==0 ){
|
|
- rc = 0;
|
|
+ if( argc>=3 ){
|
|
+ mode = (mode_t)sqlite3_value_int(argv[2]);
|
|
+ }
|
|
+ if( argc==4 ){
|
|
+ mtime = sqlite3_value_int64(argv[3]);
|
|
+ }
|
|
+
|
|
+ res = writeFile(context, zFile, argv[1], mode, mtime);
|
|
+ if( res==1 && errno==ENOENT ){
|
|
+ if( makeDirectory(zFile, mode)==SQLITE_OK ){
|
|
+ res = writeFile(context, zFile, argv[1], mode, mtime);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( argc>2 && res!=0 ){
|
|
+ if( S_ISLNK(mode) ){
|
|
+ ctxErrorMsg(context, "failed to create symlink: %s", zFile);
|
|
+ }else if( S_ISDIR(mode) ){
|
|
+ ctxErrorMsg(context, "failed to create directory: %s", zFile);
|
|
+ }else{
|
|
+ ctxErrorMsg(context, "failed to write file: %s", zFile);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: lsmode(MODE)
|
|
+**
|
|
+** Given a numberic st_mode from stat(), convert it into a human-readable
|
|
+** text string in the style of "ls -l".
|
|
+*/
|
|
+static void lsModeFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ int iMode = sqlite3_value_int(argv[0]);
|
|
+ char z[16];
|
|
+ (void)argc;
|
|
+ if( S_ISLNK(iMode) ){
|
|
+ z[0] = 'l';
|
|
+ }else if( S_ISREG(iMode) ){
|
|
+ z[0] = '-';
|
|
+ }else if( S_ISDIR(iMode) ){
|
|
+ z[0] = 'd';
|
|
}else{
|
|
- rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
|
|
+ z[0] = '?';
|
|
}
|
|
- fclose(out);
|
|
- sqlite3_result_int64(context, rc);
|
|
+ for(i=0; i<3; i++){
|
|
+ int m = (iMode >> ((2-i)*3));
|
|
+ char *a = &z[1 + i*3];
|
|
+ a[0] = (m & 0x4) ? 'r' : '-';
|
|
+ a[1] = (m & 0x2) ? 'w' : '-';
|
|
+ a[2] = (m & 0x1) ? 'x' : '-';
|
|
+ }
|
|
+ z[10] = '\0';
|
|
+ sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
|
|
}
|
|
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
|
|
+/*
|
|
+** Cursor type for recursively iterating through a directory structure.
|
|
+*/
|
|
+typedef struct fsdir_cursor fsdir_cursor;
|
|
+typedef struct FsdirLevel FsdirLevel;
|
|
+
|
|
+struct FsdirLevel {
|
|
+ DIR *pDir; /* From opendir() */
|
|
+ char *zDir; /* Name of directory (nul-terminated) */
|
|
+};
|
|
+
|
|
+struct fsdir_cursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+
|
|
+ int nLvl; /* Number of entries in aLvl[] array */
|
|
+ int iLvl; /* Index of current entry */
|
|
+ FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
|
|
+
|
|
+ const char *zBase;
|
|
+ int nBase;
|
|
+
|
|
+ struct stat sStat; /* Current lstat() results */
|
|
+ char *zPath; /* Path to current entry */
|
|
+ sqlite3_int64 iRowid; /* Current rowid */
|
|
+};
|
|
+
|
|
+typedef struct fsdir_tab fsdir_tab;
|
|
+struct fsdir_tab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Construct a new fsdir virtual table object.
|
|
+*/
|
|
+static int fsdirConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ fsdir_tab *pNew = 0;
|
|
+ int rc;
|
|
+ (void)pAux;
|
|
+ (void)argc;
|
|
+ (void)argv;
|
|
+ (void)pzErr;
|
|
+ rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for fsdir vtab objects.
|
|
+*/
|
|
+static int fsdirDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new fsdir_cursor object.
|
|
+*/
|
|
+static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ fsdir_cursor *pCur;
|
|
+ (void)p;
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ pCur->iLvl = -1;
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset a cursor back to the state it was in when first returned
|
|
+** by fsdirOpen().
|
|
+*/
|
|
+static void fsdirResetCursor(fsdir_cursor *pCur){
|
|
+ int i;
|
|
+ for(i=0; i<=pCur->iLvl; i++){
|
|
+ FsdirLevel *pLvl = &pCur->aLvl[i];
|
|
+ if( pLvl->pDir ) closedir(pLvl->pDir);
|
|
+ sqlite3_free(pLvl->zDir);
|
|
+ }
|
|
+ sqlite3_free(pCur->zPath);
|
|
+ sqlite3_free(pCur->aLvl);
|
|
+ pCur->aLvl = 0;
|
|
+ pCur->zPath = 0;
|
|
+ pCur->zBase = 0;
|
|
+ pCur->nBase = 0;
|
|
+ pCur->nLvl = 0;
|
|
+ pCur->iLvl = -1;
|
|
+ pCur->iRowid = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for an fsdir_cursor.
|
|
+*/
|
|
+static int fsdirClose(sqlite3_vtab_cursor *cur){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+
|
|
+ fsdirResetCursor(pCur);
|
|
+ sqlite3_free(pCur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set the error message for the virtual table associated with cursor
|
|
+** pCur to the results of vprintf(zFmt, ...).
|
|
+*/
|
|
+static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Advance an fsdir_cursor to its next row of output.
|
|
+*/
|
|
+static int fsdirNext(sqlite3_vtab_cursor *cur){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ mode_t m = pCur->sStat.st_mode;
|
|
+
|
|
+ pCur->iRowid++;
|
|
+ if( S_ISDIR(m) ){
|
|
+ /* Descend into this directory */
|
|
+ int iNew = pCur->iLvl + 1;
|
|
+ FsdirLevel *pLvl;
|
|
+ if( iNew>=pCur->nLvl ){
|
|
+ int nNew = iNew+1;
|
|
+ int nByte = nNew*sizeof(FsdirLevel);
|
|
+ FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc(pCur->aLvl, nByte);
|
|
+ if( aNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl));
|
|
+ pCur->aLvl = aNew;
|
|
+ pCur->nLvl = nNew;
|
|
+ }
|
|
+ pCur->iLvl = iNew;
|
|
+ pLvl = &pCur->aLvl[iNew];
|
|
+
|
|
+ pLvl->zDir = pCur->zPath;
|
|
+ pCur->zPath = 0;
|
|
+ pLvl->pDir = opendir(pLvl->zDir);
|
|
+ if( pLvl->pDir==0 ){
|
|
+ fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while( pCur->iLvl>=0 ){
|
|
+ FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl];
|
|
+ struct dirent *pEntry = readdir(pLvl->pDir);
|
|
+ if( pEntry ){
|
|
+ if( pEntry->d_name[0]=='.' ){
|
|
+ if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue;
|
|
+ if( pEntry->d_name[1]=='\0' ) continue;
|
|
+ }
|
|
+ sqlite3_free(pCur->zPath);
|
|
+ pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name);
|
|
+ if( pCur->zPath==0 ) return SQLITE_NOMEM;
|
|
+ if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
|
|
+ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ closedir(pLvl->pDir);
|
|
+ sqlite3_free(pLvl->zDir);
|
|
+ pLvl->pDir = 0;
|
|
+ pLvl->zDir = 0;
|
|
+ pCur->iLvl--;
|
|
+ }
|
|
+
|
|
+ /* EOF */
|
|
+ sqlite3_free(pCur->zPath);
|
|
+ pCur->zPath = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the series_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int fsdirColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ switch( i ){
|
|
+ case FSDIR_COLUMN_NAME: {
|
|
+ sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case FSDIR_COLUMN_MODE:
|
|
+ sqlite3_result_int64(ctx, pCur->sStat.st_mode);
|
|
+ break;
|
|
+
|
|
+ case FSDIR_COLUMN_MTIME:
|
|
+ sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
|
|
+ break;
|
|
+
|
|
+ case FSDIR_COLUMN_DATA: {
|
|
+ mode_t m = pCur->sStat.st_mode;
|
|
+ if( S_ISDIR(m) ){
|
|
+ sqlite3_result_null(ctx);
|
|
+#if !defined(_WIN32) && !defined(WIN32)
|
|
+ }else if( S_ISLNK(m) ){
|
|
+ char aStatic[64];
|
|
+ char *aBuf = aStatic;
|
|
+ int nBuf = 64;
|
|
+ int n;
|
|
+
|
|
+ while( 1 ){
|
|
+ n = readlink(pCur->zPath, aBuf, nBuf);
|
|
+ if( n<nBuf ) break;
|
|
+ if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
|
+ nBuf = nBuf*2;
|
|
+ aBuf = sqlite3_malloc(nBuf);
|
|
+ if( aBuf==0 ){
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
|
|
+ if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
|
+#endif
|
|
+ }else{
|
|
+ readFileContents(ctx, pCur->zPath);
|
|
+ }
|
|
+ }
|
|
+ case FSDIR_COLUMN_PATH:
|
|
+ default: {
|
|
+ /* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
|
|
+ ** always return their values as NULL */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the rowid for the current row. In this implementation, the
|
|
+** first row returned is assigned rowid value 1, and each subsequent
|
|
+** row a value 1 more than that of the previous.
|
|
+*/
|
|
+static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ *pRowid = pCur->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor has been moved off of the last
|
|
+** row of output.
|
|
+*/
|
|
+static int fsdirEof(sqlite3_vtab_cursor *cur){
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ return (pCur->zPath==0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFilter callback.
|
|
+**
|
|
+** idxNum==1 PATH parameter only
|
|
+** idxNum==2 Both PATH and DIR supplied
|
|
+*/
|
|
+static int fsdirFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ const char *zDir = 0;
|
|
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
|
+ (void)idxStr;
|
|
+ fsdirResetCursor(pCur);
|
|
+
|
|
+ if( idxNum==0 ){
|
|
+ fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ assert( argc==idxNum && (argc==1 || argc==2) );
|
|
+ zDir = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( zDir==0 ){
|
|
+ fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ if( argc==2 ){
|
|
+ pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
|
|
+ }
|
|
+ if( pCur->zBase ){
|
|
+ pCur->nBase = (int)strlen(pCur->zBase)+1;
|
|
+ pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir);
|
|
+ }else{
|
|
+ pCur->zPath = sqlite3_mprintf("%s", zDir);
|
|
+ }
|
|
+
|
|
+ if( pCur->zPath==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
|
|
+ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQLite will invoke this method one or more times while planning a query
|
|
+** that uses the generate_series virtual table. This routine needs to create
|
|
+** a query plan for each invocation and compute an estimated cost for that
|
|
+** plan.
|
|
+**
|
|
+** In this implementation idxNum is used to represent the
|
|
+** query plan. idxStr is unused.
|
|
+**
|
|
+** The query plan is represented by values of idxNum:
|
|
+**
|
|
+** (1) The path value is supplied by argv[0]
|
|
+** (2) Path is in argv[0] and dir is in argv[1]
|
|
+*/
|
|
+static int fsdirBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop over constraints */
|
|
+ int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
|
|
+ int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
|
|
+ int seenPath = 0; /* True if an unusable PATH= constraint is seen */
|
|
+ int seenDir = 0; /* True if an unusable DIR= constraint is seen */
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ (void)tab;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ switch( pConstraint->iColumn ){
|
|
+ case FSDIR_COLUMN_PATH: {
|
|
+ if( pConstraint->usable ){
|
|
+ idxPath = i;
|
|
+ seenPath = 0;
|
|
+ }else if( idxPath<0 ){
|
|
+ seenPath = 1;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case FSDIR_COLUMN_DIR: {
|
|
+ if( pConstraint->usable ){
|
|
+ idxDir = i;
|
|
+ seenDir = 0;
|
|
+ }else if( idxDir<0 ){
|
|
+ seenDir = 1;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( seenPath || seenDir ){
|
|
+ /* If input parameters are unusable, disallow this plan */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+
|
|
+ if( idxPath<0 ){
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ /* The pIdxInfo->estimatedCost should have been initialized to a huge
|
|
+ ** number. Leave it unchanged. */
|
|
+ pIdxInfo->estimatedRows = 0x7fffffff;
|
|
+ }else{
|
|
+ pIdxInfo->aConstraintUsage[idxPath].omit = 1;
|
|
+ pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
|
|
+ if( idxDir>=0 ){
|
|
+ pIdxInfo->aConstraintUsage[idxDir].omit = 1;
|
|
+ pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
|
|
+ pIdxInfo->idxNum = 2;
|
|
+ pIdxInfo->estimatedCost = 10.0;
|
|
+ }else{
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ pIdxInfo->estimatedCost = 100.0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register the "fsdir" virtual table.
|
|
+*/
|
|
+static int fsdirRegister(sqlite3 *db){
|
|
+ static sqlite3_module fsdirModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ fsdirConnect, /* xConnect */
|
|
+ fsdirBestIndex, /* xBestIndex */
|
|
+ fsdirDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ fsdirOpen, /* xOpen - open a cursor */
|
|
+ fsdirClose, /* xClose - close a cursor */
|
|
+ fsdirFilter, /* xFilter - configure scan constraints */
|
|
+ fsdirNext, /* xNext - advance a cursor */
|
|
+ fsdirEof, /* xEof - check for end of scan */
|
|
+ fsdirColumn, /* xColumn - read data */
|
|
+ fsdirRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+ };
|
|
+
|
|
+ int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
|
|
+ return rc;
|
|
+}
|
|
+#else /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+# define fsdirRegister(x) SQLITE_OK
|
|
+#endif
|
|
+
|
|
#ifdef _WIN32
|
|
-__declspec(dllexport)
|
|
+
|
|
#endif
|
|
int sqlite3_fileio_init(
|
|
sqlite3 *db,
|
|
@@ -1624,9 +2978,16 @@
|
|
rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
|
readfileFunc, 0, 0);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
|
+ rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
|
|
writefileFunc, 0, 0);
|
|
}
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0,
|
|
+ lsModeFunc, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = fsdirRegister(db);
|
|
+ }
|
|
return rc;
|
|
}
|
|
|
|
@@ -1695,6 +3056,7 @@
|
|
char *zPrefix; /* The prefix for the word we want to complete */
|
|
char *zLine; /* The whole that we want to complete */
|
|
const char *zCurrentRow; /* Current output row */
|
|
+ int szRow; /* Length of the zCurrentRow string */
|
|
sqlite3_stmt *pStmt; /* Current statement */
|
|
sqlite3_int64 iRowid; /* The rowid */
|
|
int ePhase; /* Current phase */
|
|
@@ -1711,7 +3073,7 @@
|
|
#define COMPLETION_INDEXES 5
|
|
#define COMPLETION_TRIGGERS 6
|
|
#define COMPLETION_DATABASES 7
|
|
-#define COMPLETION_TABLES 8
|
|
+#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */
|
|
#define COMPLETION_COLUMNS 9
|
|
#define COMPLETION_MODULES 10
|
|
#define COMPLETION_EOF 11
|
|
@@ -1808,32 +3170,6 @@
|
|
}
|
|
|
|
/*
|
|
-** All SQL keywords understood by SQLite
|
|
-*/
|
|
-static const char *completionKwrds[] = {
|
|
- "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
|
|
- "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
|
|
- "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
|
|
- "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
|
|
- "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
|
|
- "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
|
|
- "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
|
|
- "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
|
|
- "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
|
|
- "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
|
|
- "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
|
|
- "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
|
|
- "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
|
|
- "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
|
|
- "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
|
|
- "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
|
|
- "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
|
|
- "WITH", "WITHOUT",
|
|
-};
|
|
-#define completionKwCount \
|
|
- (int)(sizeof(completionKwrds)/sizeof(completionKwrds[0]))
|
|
-
|
|
-/*
|
|
** Advance a completion_cursor to its next row of output.
|
|
**
|
|
** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object
|
|
@@ -1855,11 +3191,11 @@
|
|
while( pCur->ePhase!=COMPLETION_EOF ){
|
|
switch( pCur->ePhase ){
|
|
case COMPLETION_KEYWORDS: {
|
|
- if( pCur->j >= completionKwCount ){
|
|
+ if( pCur->j >= sqlite3_keyword_count() ){
|
|
pCur->zCurrentRow = 0;
|
|
pCur->ePhase = COMPLETION_DATABASES;
|
|
}else{
|
|
- pCur->zCurrentRow = completionKwrds[pCur->j++];
|
|
+ sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow);
|
|
}
|
|
iCol = -1;
|
|
break;
|
|
@@ -1883,8 +3219,7 @@
|
|
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
|
|
zSql = sqlite3_mprintf(
|
|
"%z%s"
|
|
- "SELECT name FROM \"%w\".sqlite_master"
|
|
- " WHERE type='table'",
|
|
+ "SELECT name FROM \"%w\".sqlite_master",
|
|
zSql, zSep, zDb
|
|
);
|
|
if( zSql==0 ) return SQLITE_NOMEM;
|
|
@@ -1932,6 +3267,7 @@
|
|
if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){
|
|
/* Extract the next row of content */
|
|
pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol);
|
|
+ pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
|
|
}else{
|
|
/* When all rows are finished, advance to the next phase */
|
|
sqlite3_finalize(pCur->pStmt);
|
|
@@ -1941,7 +3277,9 @@
|
|
}
|
|
}
|
|
if( pCur->nPrefix==0 ) break;
|
|
- if( sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){
|
|
+ if( pCur->nPrefix<=pCur->szRow
|
|
+ && sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0
|
|
+ ){
|
|
break;
|
|
}
|
|
}
|
|
@@ -1961,7 +3299,7 @@
|
|
completion_cursor *pCur = (completion_cursor*)cur;
|
|
switch( i ){
|
|
case COMPLETION_COLUMN_CANDIDATE: {
|
|
- sqlite3_result_text(ctx, pCur->zCurrentRow, -1, SQLITE_TRANSIENT);
|
|
+ sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT);
|
|
break;
|
|
}
|
|
case COMPLETION_COLUMN_PREFIX: {
|
|
@@ -2021,7 +3359,7 @@
|
|
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
|
|
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
|
|
}
|
|
- iArg++;
|
|
+ iArg = 1;
|
|
}
|
|
if( idxNum & 2 ){
|
|
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
|
|
@@ -2029,7 +3367,6 @@
|
|
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
|
|
if( pCur->zLine==0 ) return SQLITE_NOMEM;
|
|
}
|
|
- iArg++;
|
|
}
|
|
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
|
|
int i = pCur->nLine;
|
|
@@ -2125,7 +3462,8 @@
|
|
0, /* xRename */
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
- 0 /* xRollbackTo */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
};
|
|
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
@@ -2139,7 +3477,7 @@
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
-__declspec(dllexport)
|
|
+
|
|
#endif
|
|
int sqlite3_completion_init(
|
|
sqlite3 *db,
|
|
@@ -2156,7 +3494,5004 @@
|
|
}
|
|
|
|
/************************* End ../ext/misc/completion.c ********************/
|
|
+/************************* Begin ../ext/misc/appendvfs.c ******************/
|
|
+/*
|
|
+** 2017-10-20
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements a VFS shim that allows an SQLite database to be
|
|
+** appended onto the end of some other file, such as an executable.
|
|
+**
|
|
+** A special record must appear at the end of the file that identifies the
|
|
+** file as an appended database and provides an offset to page 1. For
|
|
+** best performance page 1 should be located at a disk page boundary, though
|
|
+** that is not required.
|
|
+**
|
|
+** When opening a database using this VFS, the connection might treat
|
|
+** the file as an ordinary SQLite database, or it might treat is as a
|
|
+** database appended onto some other file. Here are the rules:
|
|
+**
|
|
+** (1) When opening a new empty file, that file is treated as an ordinary
|
|
+** database.
|
|
+**
|
|
+** (2) When opening a file that begins with the standard SQLite prefix
|
|
+** string "SQLite format 3", that file is treated as an ordinary
|
|
+** database.
|
|
+**
|
|
+** (3) When opening a file that ends with the appendvfs trailer string
|
|
+** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended
|
|
+** database.
|
|
+**
|
|
+** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
|
|
+** set, then a new database is appended to the already existing file.
|
|
+**
|
|
+** (5) Otherwise, SQLITE_CANTOPEN is returned.
|
|
+**
|
|
+** To avoid unnecessary complications with the PENDING_BYTE, the size of
|
|
+** the file containing the database is limited to 1GB. This VFS will refuse
|
|
+** to read or write past the 1GB mark. This restriction might be lifted in
|
|
+** future versions. For now, if you need a large database, then keep the
|
|
+** database in a separate file.
|
|
+**
|
|
+** If the file being opened is not an appended database, then this shim is
|
|
+** a pass-through into the default underlying VFS.
|
|
+**/
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
|
|
+/* The append mark at the end of the database is:
|
|
+**
|
|
+** Start-Of-SQLite3-NNNNNNNN
|
|
+** 123456789 123456789 12345
|
|
+**
|
|
+** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
|
|
+** the offset to page 1.
|
|
+*/
|
|
+#define APND_MARK_PREFIX "Start-Of-SQLite3-"
|
|
+#define APND_MARK_PREFIX_SZ 17
|
|
+#define APND_MARK_SIZE 25
|
|
+
|
|
+/*
|
|
+** Maximum size of the combined prefix + database + append-mark. This
|
|
+** must be less than 0x40000000 to avoid locking issues on Windows.
|
|
+*/
|
|
+#define APND_MAX_SIZE (65536*15259)
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this utility
|
|
+*/
|
|
+typedef struct sqlite3_vfs ApndVfs;
|
|
+typedef struct ApndFile ApndFile;
|
|
+
|
|
+/* Access to a lower-level VFS that (might) implement dynamic loading,
|
|
+** access to randomness, etc.
|
|
+*/
|
|
+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
|
+#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
|
|
+
|
|
+/* An open file */
|
|
+struct ApndFile {
|
|
+ sqlite3_file base; /* IO methods */
|
|
+ sqlite3_int64 iPgOne; /* File offset to page 1 */
|
|
+ sqlite3_int64 iMark; /* Start of the append-mark */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Methods for ApndFile
|
|
+*/
|
|
+static int apndClose(sqlite3_file*);
|
|
+static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
+static int apndWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
+static int apndTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
+static int apndSync(sqlite3_file*, int flags);
|
|
+static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
+static int apndLock(sqlite3_file*, int);
|
|
+static int apndUnlock(sqlite3_file*, int);
|
|
+static int apndCheckReservedLock(sqlite3_file*, int *pResOut);
|
|
+static int apndFileControl(sqlite3_file*, int op, void *pArg);
|
|
+static int apndSectorSize(sqlite3_file*);
|
|
+static int apndDeviceCharacteristics(sqlite3_file*);
|
|
+static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
|
+static int apndShmLock(sqlite3_file*, int offset, int n, int flags);
|
|
+static void apndShmBarrier(sqlite3_file*);
|
|
+static int apndShmUnmap(sqlite3_file*, int deleteFlag);
|
|
+static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
|
+static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
|
+
|
|
+/*
|
|
+** Methods for ApndVfs
|
|
+*/
|
|
+static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
+static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
+static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
+static int apndFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
+static void *apndDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
+static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
+static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
+static void apndDlClose(sqlite3_vfs*, void*);
|
|
+static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
+static int apndSleep(sqlite3_vfs*, int microseconds);
|
|
+static int apndCurrentTime(sqlite3_vfs*, double*);
|
|
+static int apndGetLastError(sqlite3_vfs*, int, char *);
|
|
+static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
+static int apndSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
|
|
+static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z);
|
|
+static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName);
|
|
+
|
|
+static sqlite3_vfs apnd_vfs = {
|
|
+ 3, /* iVersion (set when registered) */
|
|
+ 0, /* szOsFile (set when registered) */
|
|
+ 1024, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ "apndvfs", /* zName */
|
|
+ 0, /* pAppData (set when registered) */
|
|
+ apndOpen, /* xOpen */
|
|
+ apndDelete, /* xDelete */
|
|
+ apndAccess, /* xAccess */
|
|
+ apndFullPathname, /* xFullPathname */
|
|
+ apndDlOpen, /* xDlOpen */
|
|
+ apndDlError, /* xDlError */
|
|
+ apndDlSym, /* xDlSym */
|
|
+ apndDlClose, /* xDlClose */
|
|
+ apndRandomness, /* xRandomness */
|
|
+ apndSleep, /* xSleep */
|
|
+ apndCurrentTime, /* xCurrentTime */
|
|
+ apndGetLastError, /* xGetLastError */
|
|
+ apndCurrentTimeInt64, /* xCurrentTimeInt64 */
|
|
+ apndSetSystemCall, /* xSetSystemCall */
|
|
+ apndGetSystemCall, /* xGetSystemCall */
|
|
+ apndNextSystemCall /* xNextSystemCall */
|
|
+};
|
|
+
|
|
+static const sqlite3_io_methods apnd_io_methods = {
|
|
+ 3, /* iVersion */
|
|
+ apndClose, /* xClose */
|
|
+ apndRead, /* xRead */
|
|
+ apndWrite, /* xWrite */
|
|
+ apndTruncate, /* xTruncate */
|
|
+ apndSync, /* xSync */
|
|
+ apndFileSize, /* xFileSize */
|
|
+ apndLock, /* xLock */
|
|
+ apndUnlock, /* xUnlock */
|
|
+ apndCheckReservedLock, /* xCheckReservedLock */
|
|
+ apndFileControl, /* xFileControl */
|
|
+ apndSectorSize, /* xSectorSize */
|
|
+ apndDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ apndShmMap, /* xShmMap */
|
|
+ apndShmLock, /* xShmLock */
|
|
+ apndShmBarrier, /* xShmBarrier */
|
|
+ apndShmUnmap, /* xShmUnmap */
|
|
+ apndFetch, /* xFetch */
|
|
+ apndUnfetch /* xUnfetch */
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Close an apnd-file.
|
|
+*/
|
|
+static int apndClose(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xClose(pFile);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read data from an apnd-file.
|
|
+*/
|
|
+static int apndRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add the append-mark onto the end of the file.
|
|
+*/
|
|
+static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){
|
|
+ int i;
|
|
+ unsigned char a[APND_MARK_SIZE];
|
|
+ memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
|
|
+ for(i=0; i<8; i++){
|
|
+ a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff;
|
|
+ }
|
|
+ return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an apnd-file.
|
|
+*/
|
|
+static int apndWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ int rc;
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL;
|
|
+ rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne);
|
|
+ if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){
|
|
+ sqlite3_int64 sz = 0;
|
|
+ rc = pFile->pMethods->xFileSize(pFile, &sz);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->iMark = sz - APND_MARK_SIZE;
|
|
+ if( iOfst + iAmt + p->iPgOne > p->iMark ){
|
|
+ p->iMark = p->iPgOne + iOfst + iAmt;
|
|
+ rc = apndWriteMark(p, pFile);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an apnd-file.
|
|
+*/
|
|
+static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ int rc;
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->iMark = p->iPgOne+size;
|
|
+ rc = apndWriteMark(p, pFile);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an apnd-file.
|
|
+*/
|
|
+static int apndSync(sqlite3_file *pFile, int flags){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xSync(pFile, flags);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an apnd-file.
|
|
+*/
|
|
+static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ int rc;
|
|
+ pFile = ORIGFILE(p);
|
|
+ rc = pFile->pMethods->xFileSize(pFile, pSize);
|
|
+ if( rc==SQLITE_OK && p->iPgOne ){
|
|
+ *pSize -= p->iPgOne + APND_MARK_SIZE;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an apnd-file.
|
|
+*/
|
|
+static int apndLock(sqlite3_file *pFile, int eLock){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xLock(pFile, eLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unlock an apnd-file.
|
|
+*/
|
|
+static int apndUnlock(sqlite3_file *pFile, int eLock){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xUnlock(pFile, eLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an apnd-file.
|
|
+*/
|
|
+static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an apnd-file.
|
|
+*/
|
|
+static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ int rc;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ rc = pFile->pMethods->xFileControl(pFile, op, pArg);
|
|
+ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
|
|
+ *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->iPgOne, *(char**)pArg);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the sector-size in bytes for an apnd-file.
|
|
+*/
|
|
+static int apndSectorSize(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xSectorSize(pFile);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an apnd-file.
|
|
+*/
|
|
+static int apndDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xDeviceCharacteristics(pFile);
|
|
+}
|
|
+
|
|
+/* Create a shared memory file mapping */
|
|
+static int apndShmMap(
|
|
+ sqlite3_file *pFile,
|
|
+ int iPg,
|
|
+ int pgsz,
|
|
+ int bExtend,
|
|
+ void volatile **pp
|
|
+){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
|
|
+}
|
|
+
|
|
+/* Perform locking on a shared-memory segment */
|
|
+static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xShmLock(pFile,offset,n,flags);
|
|
+}
|
|
+
|
|
+/* Memory barrier operation on shared memory */
|
|
+static void apndShmBarrier(sqlite3_file *pFile){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ pFile->pMethods->xShmBarrier(pFile);
|
|
+}
|
|
+
|
|
+/* Unmap a shared memory segment */
|
|
+static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
|
|
+}
|
|
+
|
|
+/* Fetch a page of a memory-mapped file */
|
|
+static int apndFetch(
|
|
+ sqlite3_file *pFile,
|
|
+ sqlite3_int64 iOfst,
|
|
+ int iAmt,
|
|
+ void **pp
|
|
+){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
|
|
+}
|
|
+
|
|
+/* Release a memory-mapped page */
|
|
+static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
|
+ ApndFile *p = (ApndFile *)pFile;
|
|
+ pFile = ORIGFILE(pFile);
|
|
+ return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check to see if the file is an ordinary SQLite database file.
|
|
+*/
|
|
+static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
|
|
+ int rc;
|
|
+ char zHdr[16];
|
|
+ static const char aSqliteHdr[] = "SQLite format 3";
|
|
+ if( sz<512 ) return 0;
|
|
+ rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0);
|
|
+ if( rc ) return 0;
|
|
+ return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Try to read the append-mark off the end of a file. Return the
|
|
+** start of the appended database if the append-mark is present. If
|
|
+** there is no append-mark, return -1;
|
|
+*/
|
|
+static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
|
|
+ int rc, i;
|
|
+ sqlite3_int64 iMark;
|
|
+ unsigned char a[APND_MARK_SIZE];
|
|
+
|
|
+ if( sz<=APND_MARK_SIZE ) return -1;
|
|
+ rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
|
|
+ if( rc ) return -1;
|
|
+ if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
|
|
+ iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ]&0x7f))<<56;
|
|
+ for(i=1; i<8; i++){
|
|
+ iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i);
|
|
+ }
|
|
+ return iMark;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an apnd file handle.
|
|
+*/
|
|
+static int apndOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ ApndFile *p;
|
|
+ sqlite3_file *pSubFile;
|
|
+ sqlite3_vfs *pSubVfs;
|
|
+ int rc;
|
|
+ sqlite3_int64 sz;
|
|
+ pSubVfs = ORIGVFS(pVfs);
|
|
+ if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
|
|
+ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
|
|
+ }
|
|
+ p = (ApndFile*)pFile;
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ pSubFile = ORIGFILE(pFile);
|
|
+ p->base.pMethods = &apnd_io_methods;
|
|
+ rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
|
|
+ if( rc ) goto apnd_open_done;
|
|
+ rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
|
|
+ if( rc ){
|
|
+ pSubFile->pMethods->xClose(pSubFile);
|
|
+ goto apnd_open_done;
|
|
+ }
|
|
+ if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
|
|
+ memmove(pFile, pSubFile, pSubVfs->szOsFile);
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ p->iMark = 0;
|
|
+ p->iPgOne = apndReadMark(sz, pFile);
|
|
+ if( p->iPgOne>0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ if( (flags & SQLITE_OPEN_CREATE)==0 ){
|
|
+ pSubFile->pMethods->xClose(pSubFile);
|
|
+ rc = SQLITE_CANTOPEN;
|
|
+ }
|
|
+ p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff;
|
|
+apnd_open_done:
|
|
+ if( rc ) pFile->pMethods = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** All other VFS methods are pass-thrus.
|
|
+*/
|
|
+static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
|
|
+}
|
|
+static int apndAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
|
|
+}
|
|
+static int apndFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
|
|
+}
|
|
+static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
|
+}
|
|
+static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
|
+}
|
|
+static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
|
+}
|
|
+static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
|
+}
|
|
+static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
|
+}
|
|
+static int apndSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
|
+}
|
|
+static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
|
+}
|
|
+static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
|
+}
|
|
+static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
+ return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
|
+}
|
|
+static int apndSetSystemCall(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_syscall_ptr pCall
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
|
|
+}
|
|
+static sqlite3_syscall_ptr apndGetSystemCall(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName
|
|
+){
|
|
+ return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
|
|
+}
|
|
+static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
|
+ return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+
|
|
+#endif
|
|
+/*
|
|
+** This routine is called when the extension is loaded.
|
|
+** Register the new VFS.
|
|
+*/
|
|
+int sqlite3_appendvfs_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3_vfs *pOrig;
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg;
|
|
+ (void)db;
|
|
+ pOrig = sqlite3_vfs_find(0);
|
|
+ apnd_vfs.iVersion = pOrig->iVersion;
|
|
+ apnd_vfs.pAppData = pOrig;
|
|
+ apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile);
|
|
+ rc = sqlite3_vfs_register(&apnd_vfs, 0);
|
|
+#ifdef APPENDVFS_TEST
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_auto_extension((void(*)(void))apndvfsRegister);
|
|
+ }
|
|
+#endif
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/************************* End ../ext/misc/appendvfs.c ********************/
|
|
+#ifdef SQLITE_HAVE_ZLIB
|
|
+/************************* Begin ../ext/misc/zipfile.c ******************/
|
|
+/*
|
|
+** 2017-12-26
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements a virtual table for reading and writing ZIP archive
|
|
+** files.
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** SELECT name, sz, datetime(mtime,'unixepoch') FROM zipfile($filename);
|
|
+**
|
|
+** Current limitations:
|
|
+**
|
|
+** * No support for encryption
|
|
+** * No support for ZIP archives spanning multiple files
|
|
+** * No support for zip64 extensions
|
|
+** * Only the "inflate/deflate" (zlib) compression method is supported
|
|
+*/
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+
|
|
+#include <zlib.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+#ifndef SQLITE_AMALGAMATION
|
|
+
|
|
+/* typedef sqlite3_int64 i64; */
|
|
+/* typedef unsigned char u8; */
|
|
+typedef unsigned short u16;
|
|
+typedef unsigned long u32;
|
|
+#define MIN(a,b) ((a)<(b) ? (a) : (b))
|
|
+
|
|
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
|
+# define ALWAYS(X) (1)
|
|
+# define NEVER(X) (0)
|
|
+#elif !defined(NDEBUG)
|
|
+# define ALWAYS(X) ((X)?1:(assert(0),0))
|
|
+# define NEVER(X) ((X)?(assert(0),1):0)
|
|
+#else
|
|
+# define ALWAYS(X) (X)
|
|
+# define NEVER(X) (X)
|
|
+#endif
|
|
+
|
|
+#endif /* SQLITE_AMALGAMATION */
|
|
+
|
|
+/*
|
|
+** Definitions for mode bitmasks S_IFDIR, S_IFREG and S_IFLNK.
|
|
+**
|
|
+** In some ways it would be better to obtain these values from system
|
|
+** header files. But, the dependency is undesirable and (a) these
|
|
+** have been stable for decades, (b) the values are part of POSIX and
|
|
+** are also made explicit in [man stat], and (c) are part of the
|
|
+** file format for zip archives.
|
|
+*/
|
|
+#ifndef S_IFDIR
|
|
+# define S_IFDIR 0040000
|
|
+#endif
|
|
+#ifndef S_IFREG
|
|
+# define S_IFREG 0100000
|
|
+#endif
|
|
+#ifndef S_IFLNK
|
|
+# define S_IFLNK 0120000
|
|
+#endif
|
|
+
|
|
+static const char ZIPFILE_SCHEMA[] =
|
|
+ "CREATE TABLE y("
|
|
+ "name PRIMARY KEY," /* 0: Name of file in zip archive */
|
|
+ "mode," /* 1: POSIX mode for file */
|
|
+ "mtime," /* 2: Last modification time (secs since 1970)*/
|
|
+ "sz," /* 3: Size of object */
|
|
+ "rawdata," /* 4: Raw data */
|
|
+ "data," /* 5: Uncompressed data */
|
|
+ "method," /* 6: Compression method (integer) */
|
|
+ "z HIDDEN" /* 7: Name of zip file */
|
|
+ ") WITHOUT ROWID;";
|
|
+
|
|
+#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
|
|
+#define ZIPFILE_BUFFER_SIZE (64*1024)
|
|
+
|
|
+
|
|
+/*
|
|
+** Magic numbers used to read and write zip files.
|
|
+**
|
|
+** ZIPFILE_NEWENTRY_MADEBY:
|
|
+** Use this value for the "version-made-by" field in new zip file
|
|
+** entries. The upper byte indicates "unix", and the lower byte
|
|
+** indicates that the zip file matches pkzip specification 3.0.
|
|
+** This is what info-zip seems to do.
|
|
+**
|
|
+** ZIPFILE_NEWENTRY_REQUIRED:
|
|
+** Value for "version-required-to-extract" field of new entries.
|
|
+** Version 2.0 is required to support folders and deflate compression.
|
|
+**
|
|
+** ZIPFILE_NEWENTRY_FLAGS:
|
|
+** Value for "general-purpose-bit-flags" field of new entries. Bit
|
|
+** 11 means "utf-8 filename and comment".
|
|
+**
|
|
+** ZIPFILE_SIGNATURE_CDS:
|
|
+** First 4 bytes of a valid CDS record.
|
|
+**
|
|
+** ZIPFILE_SIGNATURE_LFH:
|
|
+** First 4 bytes of a valid LFH record.
|
|
+**
|
|
+** ZIPFILE_SIGNATURE_EOCD
|
|
+** First 4 bytes of a valid EOCD record.
|
|
+*/
|
|
+#define ZIPFILE_EXTRA_TIMESTAMP 0x5455
|
|
+#define ZIPFILE_NEWENTRY_MADEBY ((3<<8) + 30)
|
|
+#define ZIPFILE_NEWENTRY_REQUIRED 20
|
|
+#define ZIPFILE_NEWENTRY_FLAGS 0x800
|
|
+#define ZIPFILE_SIGNATURE_CDS 0x02014b50
|
|
+#define ZIPFILE_SIGNATURE_LFH 0x04034b50
|
|
+#define ZIPFILE_SIGNATURE_EOCD 0x06054b50
|
|
+
|
|
+/*
|
|
+** The sizes of the fixed-size part of each of the three main data
|
|
+** structures in a zip archive.
|
|
+*/
|
|
+#define ZIPFILE_LFH_FIXED_SZ 30
|
|
+#define ZIPFILE_EOCD_FIXED_SZ 22
|
|
+#define ZIPFILE_CDS_FIXED_SZ 46
|
|
+
|
|
+/*
|
|
+*** 4.3.16 End of central directory record:
|
|
+***
|
|
+*** end of central dir signature 4 bytes (0x06054b50)
|
|
+*** number of this disk 2 bytes
|
|
+*** number of the disk with the
|
|
+*** start of the central directory 2 bytes
|
|
+*** total number of entries in the
|
|
+*** central directory on this disk 2 bytes
|
|
+*** total number of entries in
|
|
+*** the central directory 2 bytes
|
|
+*** size of the central directory 4 bytes
|
|
+*** offset of start of central
|
|
+*** directory with respect to
|
|
+*** the starting disk number 4 bytes
|
|
+*** .ZIP file comment length 2 bytes
|
|
+*** .ZIP file comment (variable size)
|
|
+*/
|
|
+typedef struct ZipfileEOCD ZipfileEOCD;
|
|
+struct ZipfileEOCD {
|
|
+ u16 iDisk;
|
|
+ u16 iFirstDisk;
|
|
+ u16 nEntry;
|
|
+ u16 nEntryTotal;
|
|
+ u32 nSize;
|
|
+ u32 iOffset;
|
|
+};
|
|
+
|
|
+/*
|
|
+*** 4.3.12 Central directory structure:
|
|
+***
|
|
+*** ...
|
|
+***
|
|
+*** central file header signature 4 bytes (0x02014b50)
|
|
+*** version made by 2 bytes
|
|
+*** version needed to extract 2 bytes
|
|
+*** general purpose bit flag 2 bytes
|
|
+*** compression method 2 bytes
|
|
+*** last mod file time 2 bytes
|
|
+*** last mod file date 2 bytes
|
|
+*** crc-32 4 bytes
|
|
+*** compressed size 4 bytes
|
|
+*** uncompressed size 4 bytes
|
|
+*** file name length 2 bytes
|
|
+*** extra field length 2 bytes
|
|
+*** file comment length 2 bytes
|
|
+*** disk number start 2 bytes
|
|
+*** internal file attributes 2 bytes
|
|
+*** external file attributes 4 bytes
|
|
+*** relative offset of local header 4 bytes
|
|
+*/
|
|
+typedef struct ZipfileCDS ZipfileCDS;
|
|
+struct ZipfileCDS {
|
|
+ u16 iVersionMadeBy;
|
|
+ u16 iVersionExtract;
|
|
+ u16 flags;
|
|
+ u16 iCompression;
|
|
+ u16 mTime;
|
|
+ u16 mDate;
|
|
+ u32 crc32;
|
|
+ u32 szCompressed;
|
|
+ u32 szUncompressed;
|
|
+ u16 nFile;
|
|
+ u16 nExtra;
|
|
+ u16 nComment;
|
|
+ u16 iDiskStart;
|
|
+ u16 iInternalAttr;
|
|
+ u32 iExternalAttr;
|
|
+ u32 iOffset;
|
|
+ char *zFile; /* Filename (sqlite3_malloc()) */
|
|
+};
|
|
+
|
|
+/*
|
|
+*** 4.3.7 Local file header:
|
|
+***
|
|
+*** local file header signature 4 bytes (0x04034b50)
|
|
+*** version needed to extract 2 bytes
|
|
+*** general purpose bit flag 2 bytes
|
|
+*** compression method 2 bytes
|
|
+*** last mod file time 2 bytes
|
|
+*** last mod file date 2 bytes
|
|
+*** crc-32 4 bytes
|
|
+*** compressed size 4 bytes
|
|
+*** uncompressed size 4 bytes
|
|
+*** file name length 2 bytes
|
|
+*** extra field length 2 bytes
|
|
+***
|
|
+*/
|
|
+typedef struct ZipfileLFH ZipfileLFH;
|
|
+struct ZipfileLFH {
|
|
+ u16 iVersionExtract;
|
|
+ u16 flags;
|
|
+ u16 iCompression;
|
|
+ u16 mTime;
|
|
+ u16 mDate;
|
|
+ u32 crc32;
|
|
+ u32 szCompressed;
|
|
+ u32 szUncompressed;
|
|
+ u16 nFile;
|
|
+ u16 nExtra;
|
|
+};
|
|
+
|
|
+typedef struct ZipfileEntry ZipfileEntry;
|
|
+struct ZipfileEntry {
|
|
+ ZipfileCDS cds; /* Parsed CDS record */
|
|
+ u32 mUnixTime; /* Modification time, in UNIX format */
|
|
+ u8 *aExtra; /* cds.nExtra+cds.nComment bytes of extra data */
|
|
+ i64 iDataOff; /* Offset to data in file (if aData==0) */
|
|
+ u8 *aData; /* cds.szCompressed bytes of compressed data */
|
|
+ ZipfileEntry *pNext; /* Next element in in-memory CDS */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Cursor type for zipfile tables.
|
|
+*/
|
|
+typedef struct ZipfileCsr ZipfileCsr;
|
|
+struct ZipfileCsr {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ i64 iId; /* Cursor ID */
|
|
+ u8 bEof; /* True when at EOF */
|
|
+ u8 bNoop; /* If next xNext() call is no-op */
|
|
+
|
|
+ /* Used outside of write transactions */
|
|
+ FILE *pFile; /* Zip file */
|
|
+ i64 iNextOff; /* Offset of next record in central directory */
|
|
+ ZipfileEOCD eocd; /* Parse of central directory record */
|
|
+
|
|
+ ZipfileEntry *pFreeEntry; /* Free this list when cursor is closed or reset */
|
|
+ ZipfileEntry *pCurrent; /* Current entry */
|
|
+ ZipfileCsr *pCsrNext; /* Next cursor on same virtual table */
|
|
+};
|
|
+
|
|
+typedef struct ZipfileTab ZipfileTab;
|
|
+struct ZipfileTab {
|
|
+ sqlite3_vtab base; /* Base class - must be first */
|
|
+ char *zFile; /* Zip file this table accesses (may be NULL) */
|
|
+ sqlite3 *db; /* Host database connection */
|
|
+ u8 *aBuffer; /* Temporary buffer used for various tasks */
|
|
+
|
|
+ ZipfileCsr *pCsrList; /* List of cursors */
|
|
+ i64 iNextCsrid;
|
|
+
|
|
+ /* The following are used by write transactions only */
|
|
+ ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */
|
|
+ ZipfileEntry *pLastEntry; /* Last element in pFirstEntry list */
|
|
+ FILE *pWriteFd; /* File handle open on zip archive */
|
|
+ i64 szCurrent; /* Current size of zip archive */
|
|
+ i64 szOrig; /* Size of archive at start of transaction */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Set the error message contained in context ctx to the results of
|
|
+** vprintf(zFmt, ...).
|
|
+*/
|
|
+static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
|
|
+ char *zMsg = 0;
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ zMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ sqlite3_result_error(ctx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+/*
|
|
+** If string zIn is quoted, dequote it in place. Otherwise, if the string
|
|
+** is not quoted, do nothing.
|
|
+*/
|
|
+static void zipfileDequote(char *zIn){
|
|
+ char q = zIn[0];
|
|
+ if( q=='"' || q=='\'' || q=='`' || q=='[' ){
|
|
+ int iIn = 1;
|
|
+ int iOut = 0;
|
|
+ if( q=='[' ) q = ']';
|
|
+ while( ALWAYS(zIn[iIn]) ){
|
|
+ char c = zIn[iIn++];
|
|
+ if( c==q && zIn[iIn++]!=q ) break;
|
|
+ zIn[iOut++] = c;
|
|
+ }
|
|
+ zIn[iOut] = '\0';
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Construct a new ZipfileTab virtual table object.
|
|
+**
|
|
+** argv[0] -> module name ("zipfile")
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[...] -> "column name" and other module argument fields.
|
|
+*/
|
|
+static int zipfileConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ int nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE;
|
|
+ int nFile = 0;
|
|
+ const char *zFile = 0;
|
|
+ ZipfileTab *pNew = 0;
|
|
+ int rc;
|
|
+
|
|
+ /* If the table name is not "zipfile", require that the argument be
|
|
+ ** specified. This stops zipfile tables from being created as:
|
|
+ **
|
|
+ ** CREATE VIRTUAL TABLE zzz USING zipfile();
|
|
+ **
|
|
+ ** It does not prevent:
|
|
+ **
|
|
+ ** CREATE VIRTUAL TABLE zipfile USING zipfile();
|
|
+ */
|
|
+ assert( 0==sqlite3_stricmp(argv[0], "zipfile") );
|
|
+ if( (0!=sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){
|
|
+ *pzErr = sqlite3_mprintf("zipfile constructor requires one argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ if( argc>3 ){
|
|
+ zFile = argv[3];
|
|
+ nFile = (int)strlen(zFile)+1;
|
|
+ }
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = (ZipfileTab*)sqlite3_malloc(nByte+nFile);
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, nByte+nFile);
|
|
+ pNew->db = db;
|
|
+ pNew->aBuffer = (u8*)&pNew[1];
|
|
+ if( zFile ){
|
|
+ pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE];
|
|
+ memcpy(pNew->zFile, zFile, nFile);
|
|
+ zipfileDequote(pNew->zFile);
|
|
+ }
|
|
+ }
|
|
+ *ppVtab = (sqlite3_vtab*)pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the ZipfileEntry structure indicated by the only argument.
|
|
+*/
|
|
+static void zipfileEntryFree(ZipfileEntry *p){
|
|
+ if( p ){
|
|
+ sqlite3_free(p->cds.zFile);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Release resources that should be freed at the end of a write
|
|
+** transaction.
|
|
+*/
|
|
+static void zipfileCleanupTransaction(ZipfileTab *pTab){
|
|
+ ZipfileEntry *pEntry;
|
|
+ ZipfileEntry *pNext;
|
|
+
|
|
+ if( pTab->pWriteFd ){
|
|
+ fclose(pTab->pWriteFd);
|
|
+ pTab->pWriteFd = 0;
|
|
+ }
|
|
+ for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){
|
|
+ pNext = pEntry->pNext;
|
|
+ zipfileEntryFree(pEntry);
|
|
+ }
|
|
+ pTab->pFirstEntry = 0;
|
|
+ pTab->pLastEntry = 0;
|
|
+ pTab->szCurrent = 0;
|
|
+ pTab->szOrig = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This method is the destructor for zipfile vtab objects.
|
|
+*/
|
|
+static int zipfileDisconnect(sqlite3_vtab *pVtab){
|
|
+ zipfileCleanupTransaction((ZipfileTab*)pVtab);
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Constructor for a new ZipfileCsr object.
|
|
+*/
|
|
+static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)p;
|
|
+ ZipfileCsr *pCsr;
|
|
+ pCsr = sqlite3_malloc(sizeof(*pCsr));
|
|
+ *ppCsr = (sqlite3_vtab_cursor*)pCsr;
|
|
+ if( pCsr==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ memset(pCsr, 0, sizeof(*pCsr));
|
|
+ pCsr->iId = ++pTab->iNextCsrid;
|
|
+ pCsr->pCsrNext = pTab->pCsrList;
|
|
+ pTab->pCsrList = pCsr;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset a cursor back to the state it was in when first returned
|
|
+** by zipfileOpen().
|
|
+*/
|
|
+static void zipfileResetCursor(ZipfileCsr *pCsr){
|
|
+ ZipfileEntry *p;
|
|
+ ZipfileEntry *pNext;
|
|
+
|
|
+ pCsr->bEof = 0;
|
|
+ if( pCsr->pFile ){
|
|
+ fclose(pCsr->pFile);
|
|
+ pCsr->pFile = 0;
|
|
+ zipfileEntryFree(pCsr->pCurrent);
|
|
+ pCsr->pCurrent = 0;
|
|
+ }
|
|
+
|
|
+ for(p=pCsr->pFreeEntry; p; p=pNext){
|
|
+ pNext = p->pNext;
|
|
+ zipfileEntryFree(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Destructor for an ZipfileCsr.
|
|
+*/
|
|
+static int zipfileClose(sqlite3_vtab_cursor *cur){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab);
|
|
+ ZipfileCsr **pp;
|
|
+ zipfileResetCursor(pCsr);
|
|
+
|
|
+ /* Remove this cursor from the ZipfileTab.pCsrList list. */
|
|
+ for(pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext));
|
|
+ *pp = pCsr->pCsrNext;
|
|
+
|
|
+ sqlite3_free(pCsr);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Set the error message for the virtual table associated with cursor
|
|
+** pCsr to the results of vprintf(zFmt, ...).
|
|
+*/
|
|
+static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ sqlite3_free(pTab->base.zErrMsg);
|
|
+ pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ sqlite3_free(pCsr->base.pVtab->zErrMsg);
|
|
+ pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read nRead bytes of data from offset iOff of file pFile into buffer
|
|
+** aRead[]. Return SQLITE_OK if successful, or an SQLite error code
|
|
+** otherwise.
|
|
+**
|
|
+** If an error does occur, output variable (*pzErrmsg) may be set to point
|
|
+** to an English language error message. It is the responsibility of the
|
|
+** caller to eventually free this buffer using
|
|
+** sqlite3_free().
|
|
+*/
|
|
+static int zipfileReadData(
|
|
+ FILE *pFile, /* Read from this file */
|
|
+ u8 *aRead, /* Read into this buffer */
|
|
+ int nRead, /* Number of bytes to read */
|
|
+ i64 iOff, /* Offset to read from */
|
|
+ char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
|
|
+){
|
|
+ size_t n;
|
|
+ fseek(pFile, (long)iOff, SEEK_SET);
|
|
+ n = fread(aRead, 1, nRead, pFile);
|
|
+ if( (int)n!=nRead ){
|
|
+ *pzErrmsg = sqlite3_mprintf("error in fread()");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static int zipfileAppendData(
|
|
+ ZipfileTab *pTab,
|
|
+ const u8 *aWrite,
|
|
+ int nWrite
|
|
+){
|
|
+ size_t n;
|
|
+ fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
|
|
+ n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
|
|
+ if( (int)n!=nWrite ){
|
|
+ pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ pTab->szCurrent += nWrite;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read and return a 16-bit little-endian unsigned integer from buffer aBuf.
|
|
+*/
|
|
+static u16 zipfileGetU16(const u8 *aBuf){
|
|
+ return (aBuf[1] << 8) + aBuf[0];
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
|
|
+*/
|
|
+static u32 zipfileGetU32(const u8 *aBuf){
|
|
+ return ((u32)(aBuf[3]) << 24)
|
|
+ + ((u32)(aBuf[2]) << 16)
|
|
+ + ((u32)(aBuf[1]) << 8)
|
|
+ + ((u32)(aBuf[0]) << 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write a 16-bit little endiate integer into buffer aBuf.
|
|
+*/
|
|
+static void zipfilePutU16(u8 *aBuf, u16 val){
|
|
+ aBuf[0] = val & 0xFF;
|
|
+ aBuf[1] = (val>>8) & 0xFF;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write a 32-bit little endiate integer into buffer aBuf.
|
|
+*/
|
|
+static void zipfilePutU32(u8 *aBuf, u32 val){
|
|
+ aBuf[0] = val & 0xFF;
|
|
+ aBuf[1] = (val>>8) & 0xFF;
|
|
+ aBuf[2] = (val>>16) & 0xFF;
|
|
+ aBuf[3] = (val>>24) & 0xFF;
|
|
+}
|
|
+
|
|
+#define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) )
|
|
+#define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) )
|
|
+
|
|
+#define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; }
|
|
+#define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; }
|
|
+
|
|
+/*
|
|
+** Magic numbers used to read CDS records.
|
|
+*/
|
|
+#define ZIPFILE_CDS_NFILE_OFF 28
|
|
+#define ZIPFILE_CDS_SZCOMPRESSED_OFF 20
|
|
+
|
|
+/*
|
|
+** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR
|
|
+** if the record is not well-formed, or SQLITE_OK otherwise.
|
|
+*/
|
|
+static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){
|
|
+ u8 *aRead = aBuf;
|
|
+ u32 sig = zipfileRead32(aRead);
|
|
+ int rc = SQLITE_OK;
|
|
+ if( sig!=ZIPFILE_SIGNATURE_CDS ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ pCDS->iVersionMadeBy = zipfileRead16(aRead);
|
|
+ pCDS->iVersionExtract = zipfileRead16(aRead);
|
|
+ pCDS->flags = zipfileRead16(aRead);
|
|
+ pCDS->iCompression = zipfileRead16(aRead);
|
|
+ pCDS->mTime = zipfileRead16(aRead);
|
|
+ pCDS->mDate = zipfileRead16(aRead);
|
|
+ pCDS->crc32 = zipfileRead32(aRead);
|
|
+ pCDS->szCompressed = zipfileRead32(aRead);
|
|
+ pCDS->szUncompressed = zipfileRead32(aRead);
|
|
+ assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
|
|
+ pCDS->nFile = zipfileRead16(aRead);
|
|
+ pCDS->nExtra = zipfileRead16(aRead);
|
|
+ pCDS->nComment = zipfileRead16(aRead);
|
|
+ pCDS->iDiskStart = zipfileRead16(aRead);
|
|
+ pCDS->iInternalAttr = zipfileRead16(aRead);
|
|
+ pCDS->iExternalAttr = zipfileRead32(aRead);
|
|
+ pCDS->iOffset = zipfileRead32(aRead);
|
|
+ assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] );
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Decode the LFH record in buffer aBuf into (*pLFH). Return SQLITE_ERROR
|
|
+** if the record is not well-formed, or SQLITE_OK otherwise.
|
|
+*/
|
|
+static int zipfileReadLFH(
|
|
+ u8 *aBuffer,
|
|
+ ZipfileLFH *pLFH
|
|
+){
|
|
+ u8 *aRead = aBuffer;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ u32 sig = zipfileRead32(aRead);
|
|
+ if( sig!=ZIPFILE_SIGNATURE_LFH ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ pLFH->iVersionExtract = zipfileRead16(aRead);
|
|
+ pLFH->flags = zipfileRead16(aRead);
|
|
+ pLFH->iCompression = zipfileRead16(aRead);
|
|
+ pLFH->mTime = zipfileRead16(aRead);
|
|
+ pLFH->mDate = zipfileRead16(aRead);
|
|
+ pLFH->crc32 = zipfileRead32(aRead);
|
|
+ pLFH->szCompressed = zipfileRead32(aRead);
|
|
+ pLFH->szUncompressed = zipfileRead32(aRead);
|
|
+ pLFH->nFile = zipfileRead16(aRead);
|
|
+ pLFH->nExtra = zipfileRead16(aRead);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields.
|
|
+** Scan through this buffer to find an "extra-timestamp" field. If one
|
|
+** exists, extract the 32-bit modification-timestamp from it and store
|
|
+** the value in output parameter *pmTime.
|
|
+**
|
|
+** Zero is returned if no extra-timestamp record could be found (and so
|
|
+** *pmTime is left unchanged), or non-zero otherwise.
|
|
+**
|
|
+** The general format of an extra field is:
|
|
+**
|
|
+** Header ID 2 bytes
|
|
+** Data Size 2 bytes
|
|
+** Data N bytes
|
|
+*/
|
|
+static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
|
|
+ int ret = 0;
|
|
+ u8 *p = aExtra;
|
|
+ u8 *pEnd = &aExtra[nExtra];
|
|
+
|
|
+ while( p<pEnd ){
|
|
+ u16 id = zipfileRead16(p);
|
|
+ u16 nByte = zipfileRead16(p);
|
|
+
|
|
+ switch( id ){
|
|
+ case ZIPFILE_EXTRA_TIMESTAMP: {
|
|
+ u8 b = p[0];
|
|
+ if( b & 0x01 ){ /* 0x01 -> modtime is present */
|
|
+ *pmTime = zipfileGetU32(&p[1]);
|
|
+ ret = 1;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ p += nByte;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert the standard MS-DOS timestamp stored in the mTime and mDate
|
|
+** fields of the CDS structure passed as the only argument to a 32-bit
|
|
+** UNIX seconds-since-the-epoch timestamp. Return the result.
|
|
+**
|
|
+** "Standard" MS-DOS time format:
|
|
+**
|
|
+** File modification time:
|
|
+** Bits 00-04: seconds divided by 2
|
|
+** Bits 05-10: minute
|
|
+** Bits 11-15: hour
|
|
+** File modification date:
|
|
+** Bits 00-04: day
|
|
+** Bits 05-08: month (1-12)
|
|
+** Bits 09-15: years from 1980
|
|
+**
|
|
+** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx
|
|
+*/
|
|
+static u32 zipfileMtime(ZipfileCDS *pCDS){
|
|
+ int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F));
|
|
+ int M = ((pCDS->mDate >> 5) & 0x0F);
|
|
+ int D = (pCDS->mDate & 0x1F);
|
|
+ int B = -13;
|
|
+
|
|
+ int sec = (pCDS->mTime & 0x1F)*2;
|
|
+ int min = (pCDS->mTime >> 5) & 0x3F;
|
|
+ int hr = (pCDS->mTime >> 11) & 0x1F;
|
|
+ i64 JD;
|
|
+
|
|
+ /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */
|
|
+
|
|
+ /* Calculate the JD in seconds for noon on the day in question */
|
|
+ if( M<3 ){
|
|
+ Y = Y-1;
|
|
+ M = M+12;
|
|
+ }
|
|
+ JD = (i64)(24*60*60) * (
|
|
+ (int)(365.25 * (Y + 4716))
|
|
+ + (int)(30.6001 * (M + 1))
|
|
+ + D + B - 1524
|
|
+ );
|
|
+
|
|
+ /* Correct the JD for the time within the day */
|
|
+ JD += (hr-12) * 3600 + min * 60 + sec;
|
|
+
|
|
+ /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */
|
|
+ return (u32)(JD - (i64)(24405875) * 24*60*6);
|
|
+}
|
|
+
|
|
+/*
|
|
+** The opposite of zipfileMtime(). This function populates the mTime and
|
|
+** mDate fields of the CDS structure passed as the first argument according
|
|
+** to the UNIX timestamp value passed as the second.
|
|
+*/
|
|
+static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
|
|
+ /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */
|
|
+ i64 JD = (i64)2440588 + mUnixTime / (24*60*60);
|
|
+
|
|
+ int A, B, C, D, E;
|
|
+ int yr, mon, day;
|
|
+ int hr, min, sec;
|
|
+
|
|
+ A = (int)((JD - 1867216.25)/36524.25);
|
|
+ A = (int)(JD + 1 + A - (A/4));
|
|
+ B = A + 1524;
|
|
+ C = (int)((B - 122.1)/365.25);
|
|
+ D = (36525*(C&32767))/100;
|
|
+ E = (int)((B-D)/30.6001);
|
|
+
|
|
+ day = B - D - (int)(30.6001*E);
|
|
+ mon = (E<14 ? E-1 : E-13);
|
|
+ yr = mon>2 ? C-4716 : C-4715;
|
|
+
|
|
+ hr = (mUnixTime % (24*60*60)) / (60*60);
|
|
+ min = (mUnixTime % (60*60)) / 60;
|
|
+ sec = (mUnixTime % 60);
|
|
+
|
|
+ if( yr>=1980 ){
|
|
+ pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9));
|
|
+ pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11));
|
|
+ }else{
|
|
+ pCds->mDate = pCds->mTime = 0;
|
|
+ }
|
|
+
|
|
+ assert( mUnixTime<315507600
|
|
+ || mUnixTime==zipfileMtime(pCds)
|
|
+ || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
|
|
+ /* || (mUnixTime % 2) */
|
|
+ );
|
|
+}
|
|
+
|
|
+/*
|
|
+** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
|
|
+** size) containing an entire zip archive image. Or, if aBlob is NULL,
|
|
+** then pFile is a file-handle open on a zip file. In either case, this
|
|
+** function creates a ZipfileEntry object based on the zip archive entry
|
|
+** for which the CDS record is at offset iOff.
|
|
+**
|
|
+** If successful, SQLITE_OK is returned and (*ppEntry) set to point to
|
|
+** the new object. Otherwise, an SQLite error code is returned and the
|
|
+** final value of (*ppEntry) undefined.
|
|
+*/
|
|
+static int zipfileGetEntry(
|
|
+ ZipfileTab *pTab, /* Store any error message here */
|
|
+ const u8 *aBlob, /* Pointer to in-memory file image */
|
|
+ int nBlob, /* Size of aBlob[] in bytes */
|
|
+ FILE *pFile, /* If aBlob==0, read from this file */
|
|
+ i64 iOff, /* Offset of CDS record */
|
|
+ ZipfileEntry **ppEntry /* OUT: Pointer to new object */
|
|
+){
|
|
+ u8 *aRead;
|
|
+ char **pzErr = &pTab->base.zErrMsg;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( aBlob==0 ){
|
|
+ aRead = pTab->aBuffer;
|
|
+ rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
|
|
+ }else{
|
|
+ aRead = (u8*)&aBlob[iOff];
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int nAlloc;
|
|
+ ZipfileEntry *pNew;
|
|
+
|
|
+ int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]);
|
|
+ int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]);
|
|
+ nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]);
|
|
+
|
|
+ nAlloc = sizeof(ZipfileEntry) + nExtra;
|
|
+ if( aBlob ){
|
|
+ nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]);
|
|
+ }
|
|
+
|
|
+ pNew = (ZipfileEntry*)sqlite3_malloc(nAlloc);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pNew, 0, sizeof(ZipfileEntry));
|
|
+ rc = zipfileReadCDS(aRead, &pNew->cds);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
|
|
+ }else if( aBlob==0 ){
|
|
+ rc = zipfileReadData(
|
|
+ pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
|
|
+ );
|
|
+ }else{
|
|
+ aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ u32 *pt = &pNew->mUnixTime;
|
|
+ pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead);
|
|
+ pNew->aExtra = (u8*)&pNew[1];
|
|
+ memcpy(pNew->aExtra, &aRead[nFile], nExtra);
|
|
+ if( pNew->cds.zFile==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else if( 0==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){
|
|
+ pNew->mUnixTime = zipfileMtime(&pNew->cds);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ static const int szFix = ZIPFILE_LFH_FIXED_SZ;
|
|
+ ZipfileLFH lfh;
|
|
+ if( pFile ){
|
|
+ rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
|
|
+ }else{
|
|
+ aRead = (u8*)&aBlob[pNew->cds.iOffset];
|
|
+ }
|
|
+
|
|
+ rc = zipfileReadLFH(aRead, &lfh);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
|
|
+ pNew->iDataOff += lfh.nFile + lfh.nExtra;
|
|
+ if( aBlob && pNew->cds.szCompressed ){
|
|
+ pNew->aData = &pNew->aExtra[nExtra];
|
|
+ memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
|
|
+ }
|
|
+ }else{
|
|
+ *pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
|
|
+ (int)pNew->cds.iOffset
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ zipfileEntryFree(pNew);
|
|
+ }else{
|
|
+ *ppEntry = pNew;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Advance an ZipfileCsr to its next row of output.
|
|
+*/
|
|
+static int zipfileNext(sqlite3_vtab_cursor *cur){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( pCsr->pFile ){
|
|
+ i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
|
|
+ zipfileEntryFree(pCsr->pCurrent);
|
|
+ pCsr->pCurrent = 0;
|
|
+ if( pCsr->iNextOff>=iEof ){
|
|
+ pCsr->bEof = 1;
|
|
+ }else{
|
|
+ ZipfileEntry *p = 0;
|
|
+ ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab);
|
|
+ rc = zipfileGetEntry(pTab, 0, 0, pCsr->pFile, pCsr->iNextOff, &p);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
|
|
+ pCsr->iNextOff += (int)p->cds.nExtra + p->cds.nFile + p->cds.nComment;
|
|
+ }
|
|
+ pCsr->pCurrent = p;
|
|
+ }
|
|
+ }else{
|
|
+ if( !pCsr->bNoop ){
|
|
+ pCsr->pCurrent = pCsr->pCurrent->pNext;
|
|
+ }
|
|
+ if( pCsr->pCurrent==0 ){
|
|
+ pCsr->bEof = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pCsr->bNoop = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void zipfileFree(void *p) {
|
|
+ sqlite3_free(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Buffer aIn (size nIn bytes) contains compressed data. Uncompressed, the
|
|
+** size is nOut bytes. This function uncompresses the data and sets the
|
|
+** return value in context pCtx to the result (a blob).
|
|
+**
|
|
+** If an error occurs, an error code is left in pCtx instead.
|
|
+*/
|
|
+static void zipfileInflate(
|
|
+ sqlite3_context *pCtx, /* Store result here */
|
|
+ const u8 *aIn, /* Compressed data */
|
|
+ int nIn, /* Size of buffer aIn[] in bytes */
|
|
+ int nOut /* Expected output size */
|
|
+){
|
|
+ u8 *aRes = sqlite3_malloc(nOut);
|
|
+ if( aRes==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ int err;
|
|
+ z_stream str;
|
|
+ memset(&str, 0, sizeof(str));
|
|
+
|
|
+ str.next_in = (Byte*)aIn;
|
|
+ str.avail_in = nIn;
|
|
+ str.next_out = (Byte*)aRes;
|
|
+ str.avail_out = nOut;
|
|
+
|
|
+ err = inflateInit2(&str, -15);
|
|
+ if( err!=Z_OK ){
|
|
+ zipfileCtxErrorMsg(pCtx, "inflateInit2() failed (%d)", err);
|
|
+ }else{
|
|
+ err = inflate(&str, Z_NO_FLUSH);
|
|
+ if( err!=Z_STREAM_END ){
|
|
+ zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
|
|
+ }else{
|
|
+ sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
|
|
+ aRes = 0;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(aRes);
|
|
+ inflateEnd(&str);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Buffer aIn (size nIn bytes) contains uncompressed data. This function
|
|
+** compresses it and sets (*ppOut) to point to a buffer containing the
|
|
+** compressed data. The caller is responsible for eventually calling
|
|
+** sqlite3_free() to release buffer (*ppOut). Before returning, (*pnOut)
|
|
+** is set to the size of buffer (*ppOut) in bytes.
|
|
+**
|
|
+** If no error occurs, SQLITE_OK is returned. Otherwise, an SQLite error
|
|
+** code is returned and an error message left in virtual-table handle
|
|
+** pTab. The values of (*ppOut) and (*pnOut) are left unchanged in this
|
|
+** case.
|
|
+*/
|
|
+static int zipfileDeflate(
|
|
+ const u8 *aIn, int nIn, /* Input */
|
|
+ u8 **ppOut, int *pnOut, /* Output */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ int nAlloc = (int)compressBound(nIn);
|
|
+ u8 *aOut;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ aOut = (u8*)sqlite3_malloc(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;
|
|
+ }else{
|
|
+ sqlite3_free(aOut);
|
|
+ *pzErr = sqlite3_mprintf("zipfile: deflate() error");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ deflateEnd(&str);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Return values of columns for the row at which the series_cursor
|
|
+** is currently pointing.
|
|
+*/
|
|
+static int zipfileColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ ZipfileCDS *pCDS = &pCsr->pCurrent->cds;
|
|
+ int rc = SQLITE_OK;
|
|
+ switch( i ){
|
|
+ case 0: /* name */
|
|
+ sqlite3_result_text(ctx, pCDS->zFile, -1, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ case 1: /* mode */
|
|
+ /* TODO: Whether or not the following is correct surely depends on
|
|
+ ** the platform on which the archive was created. */
|
|
+ sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16);
|
|
+ break;
|
|
+ case 2: { /* mtime */
|
|
+ sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime);
|
|
+ break;
|
|
+ }
|
|
+ case 3: { /* sz */
|
|
+ if( sqlite3_vtab_nochange(ctx)==0 ){
|
|
+ sqlite3_result_int64(ctx, pCDS->szUncompressed);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case 4: /* rawdata */
|
|
+ if( sqlite3_vtab_nochange(ctx) ) break;
|
|
+ case 5: { /* data */
|
|
+ if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){
|
|
+ int sz = pCDS->szCompressed;
|
|
+ int szFinal = pCDS->szUncompressed;
|
|
+ if( szFinal>0 ){
|
|
+ u8 *aBuf;
|
|
+ u8 *aFree = 0;
|
|
+ if( pCsr->pCurrent->aData ){
|
|
+ aBuf = pCsr->pCurrent->aData;
|
|
+ }else{
|
|
+ aBuf = aFree = sqlite3_malloc(sz);
|
|
+ if( aBuf==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ FILE *pFile = pCsr->pFile;
|
|
+ if( pFile==0 ){
|
|
+ pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
|
|
+ }
|
|
+ rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff,
|
|
+ &pCsr->base.pVtab->zErrMsg
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( i==5 && pCDS->iCompression ){
|
|
+ zipfileInflate(ctx, aBuf, sz, szFinal);
|
|
+ }else{
|
|
+ sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(aFree);
|
|
+ }else{
|
|
+ /* Figure out if this is a directory or a zero-sized file. Consider
|
|
+ ** it to be a directory either if the mode suggests so, or if
|
|
+ ** the final character in the name is '/'. */
|
|
+ u32 mode = pCDS->iExternalAttr >> 16;
|
|
+ if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){
|
|
+ sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case 6: /* method */
|
|
+ sqlite3_result_int(ctx, pCDS->iCompression);
|
|
+ break;
|
|
+ default: /* z */
|
|
+ assert( i==7 );
|
|
+ sqlite3_result_int64(ctx, pCsr->iId);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the cursor is at EOF.
|
|
+*/
|
|
+static int zipfileEof(sqlite3_vtab_cursor *cur){
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ return pCsr->bEof;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If aBlob is not NULL, then it points to a buffer nBlob bytes in size
|
|
+** containing an entire zip archive image. Or, if aBlob is NULL, then pFile
|
|
+** is guaranteed to be a file-handle open on a zip file.
|
|
+**
|
|
+** This function attempts to locate the EOCD record within the zip archive
|
|
+** and populate *pEOCD with the results of decoding it. SQLITE_OK is
|
|
+** returned if successful. Otherwise, an SQLite error code is returned and
|
|
+** an English language error message may be left in virtual-table pTab.
|
|
+*/
|
|
+static int zipfileReadEOCD(
|
|
+ ZipfileTab *pTab, /* Return errors here */
|
|
+ const u8 *aBlob, /* Pointer to in-memory file image */
|
|
+ int nBlob, /* Size of aBlob[] in bytes */
|
|
+ FILE *pFile, /* Read from this file if aBlob==0 */
|
|
+ ZipfileEOCD *pEOCD /* Object to populate */
|
|
+){
|
|
+ u8 *aRead = pTab->aBuffer; /* Temporary buffer */
|
|
+ int nRead; /* Bytes to read from file */
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( aBlob==0 ){
|
|
+ i64 iOff; /* Offset to read from */
|
|
+ i64 szFile; /* Total size of file in bytes */
|
|
+ fseek(pFile, 0, SEEK_END);
|
|
+ szFile = (i64)ftell(pFile);
|
|
+ if( szFile==0 ){
|
|
+ memset(pEOCD, 0, sizeof(ZipfileEOCD));
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
|
|
+ iOff = szFile - nRead;
|
|
+ rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg);
|
|
+ }else{
|
|
+ nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE));
|
|
+ aRead = (u8*)&aBlob[nBlob-nRead];
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int i;
|
|
+
|
|
+ /* Scan backwards looking for the signature bytes */
|
|
+ for(i=nRead-20; i>=0; i--){
|
|
+ if( aRead[i]==0x50 && aRead[i+1]==0x4b
|
|
+ && aRead[i+2]==0x05 && aRead[i+3]==0x06
|
|
+ ){
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( i<0 ){
|
|
+ pTab->base.zErrMsg = sqlite3_mprintf(
|
|
+ "cannot find end of central directory record"
|
|
+ );
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+
|
|
+ aRead += i+4;
|
|
+ pEOCD->iDisk = zipfileRead16(aRead);
|
|
+ pEOCD->iFirstDisk = zipfileRead16(aRead);
|
|
+ pEOCD->nEntry = zipfileRead16(aRead);
|
|
+ pEOCD->nEntryTotal = zipfileRead16(aRead);
|
|
+ pEOCD->nSize = zipfileRead32(aRead);
|
|
+ pEOCD->iOffset = zipfileRead32(aRead);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add object pNew to the linked list that begins at ZipfileTab.pFirstEntry
|
|
+** and ends with pLastEntry. If argument pBefore is NULL, then pNew is added
|
|
+** to the end of the list. Otherwise, it is added to the list immediately
|
|
+** before pBefore (which is guaranteed to be a part of said list).
|
|
+*/
|
|
+static void zipfileAddEntry(
|
|
+ ZipfileTab *pTab,
|
|
+ ZipfileEntry *pBefore,
|
|
+ ZipfileEntry *pNew
|
|
+){
|
|
+ assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) );
|
|
+ assert( pNew->pNext==0 );
|
|
+ if( pBefore==0 ){
|
|
+ if( pTab->pFirstEntry==0 ){
|
|
+ pTab->pFirstEntry = pTab->pLastEntry = pNew;
|
|
+ }else{
|
|
+ assert( pTab->pLastEntry->pNext==0 );
|
|
+ pTab->pLastEntry->pNext = pNew;
|
|
+ pTab->pLastEntry = pNew;
|
|
+ }
|
|
+ }else{
|
|
+ ZipfileEntry **pp;
|
|
+ for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext));
|
|
+ pNew->pNext = pBefore;
|
|
+ *pp = pNew;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
|
|
+ ZipfileEOCD eocd;
|
|
+ int rc;
|
|
+ int i;
|
|
+ i64 iOff;
|
|
+
|
|
+ rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd);
|
|
+ iOff = eocd.iOffset;
|
|
+ for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){
|
|
+ ZipfileEntry *pNew = 0;
|
|
+ rc = zipfileGetEntry(pTab, aBlob, nBlob, pTab->pWriteFd, iOff, &pNew);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ zipfileAddEntry(pTab, 0, pNew);
|
|
+ iOff += ZIPFILE_CDS_FIXED_SZ;
|
|
+ iOff += (int)pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment;
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFilter callback.
|
|
+*/
|
|
+static int zipfileFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
|
|
+ ZipfileCsr *pCsr = (ZipfileCsr*)cur;
|
|
+ const char *zFile = 0; /* Zip file to scan */
|
|
+ int rc = SQLITE_OK; /* Return Code */
|
|
+ int bInMemory = 0; /* True for an in-memory zipfile */
|
|
+
|
|
+ zipfileResetCursor(pCsr);
|
|
+
|
|
+ if( pTab->zFile ){
|
|
+ zFile = pTab->zFile;
|
|
+ }else if( idxNum==0 ){
|
|
+ zipfileCursorErr(pCsr, "zipfile() function requires an argument");
|
|
+ return SQLITE_ERROR;
|
|
+ }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
|
+ const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
|
|
+ int nBlob = sqlite3_value_bytes(argv[0]);
|
|
+ assert( pTab->pFirstEntry==0 );
|
|
+ rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
|
|
+ pCsr->pFreeEntry = pTab->pFirstEntry;
|
|
+ pTab->pFirstEntry = pTab->pLastEntry = 0;
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ bInMemory = 1;
|
|
+ }else{
|
|
+ zFile = (const char*)sqlite3_value_text(argv[0]);
|
|
+ }
|
|
+
|
|
+ if( 0==pTab->pWriteFd && 0==bInMemory ){
|
|
+ pCsr->pFile = fopen(zFile, "rb");
|
|
+ if( pCsr->pFile==0 ){
|
|
+ zipfileCursorErr(pCsr, "cannot open file: %s", zFile);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pCsr->eocd.nEntry==0 ){
|
|
+ pCsr->bEof = 1;
|
|
+ }else{
|
|
+ pCsr->iNextOff = pCsr->eocd.iOffset;
|
|
+ rc = zipfileNext(cur);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ pCsr->bNoop = 1;
|
|
+ pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry;
|
|
+ rc = zipfileNext(cur);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xBestIndex callback.
|
|
+*/
|
|
+static int zipfileBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i;
|
|
+ int idx = -1;
|
|
+ int unusable = 0;
|
|
+
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
|
|
+ if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue;
|
|
+ if( pCons->usable==0 ){
|
|
+ unusable = 1;
|
|
+ }else if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ idx = i;
|
|
+ }
|
|
+ }
|
|
+ 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;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static ZipfileEntry *zipfileNewEntry(const char *zPath){
|
|
+ ZipfileEntry *pNew;
|
|
+ pNew = sqlite3_malloc(sizeof(ZipfileEntry));
|
|
+ if( pNew ){
|
|
+ memset(pNew, 0, sizeof(ZipfileEntry));
|
|
+ pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
|
|
+ if( pNew->cds.zFile==0 ){
|
|
+ sqlite3_free(pNew);
|
|
+ pNew = 0;
|
|
+ }
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){
|
|
+ ZipfileCDS *pCds = &pEntry->cds;
|
|
+ u8 *a = aBuf;
|
|
+
|
|
+ pCds->nExtra = 9;
|
|
+
|
|
+ /* Write the LFH itself */
|
|
+ zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH);
|
|
+ zipfileWrite16(a, pCds->iVersionExtract);
|
|
+ zipfileWrite16(a, pCds->flags);
|
|
+ zipfileWrite16(a, pCds->iCompression);
|
|
+ zipfileWrite16(a, pCds->mTime);
|
|
+ zipfileWrite16(a, pCds->mDate);
|
|
+ zipfileWrite32(a, pCds->crc32);
|
|
+ zipfileWrite32(a, pCds->szCompressed);
|
|
+ zipfileWrite32(a, pCds->szUncompressed);
|
|
+ zipfileWrite16(a, (u16)pCds->nFile);
|
|
+ zipfileWrite16(a, pCds->nExtra);
|
|
+ assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] );
|
|
+
|
|
+ /* Add the file name */
|
|
+ memcpy(a, pCds->zFile, (int)pCds->nFile);
|
|
+ a += (int)pCds->nFile;
|
|
+
|
|
+ /* The "extra" data */
|
|
+ zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
|
|
+ zipfileWrite16(a, 5);
|
|
+ *a++ = 0x01;
|
|
+ zipfileWrite32(a, pEntry->mUnixTime);
|
|
+
|
|
+ return a-aBuf;
|
|
+}
|
|
+
|
|
+static int zipfileAppendEntry(
|
|
+ ZipfileTab *pTab,
|
|
+ ZipfileEntry *pEntry,
|
|
+ const u8 *pData,
|
|
+ int nData
|
|
+){
|
|
+ u8 *aBuf = pTab->aBuffer;
|
|
+ int nBuf;
|
|
+ int rc;
|
|
+
|
|
+ nBuf = zipfileSerializeLFH(pEntry, aBuf);
|
|
+ rc = zipfileAppendData(pTab, aBuf, nBuf);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pEntry->iDataOff = pTab->szCurrent;
|
|
+ rc = zipfileAppendData(pTab, pData, nData);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int zipfileGetMode(
|
|
+ sqlite3_value *pVal,
|
|
+ int bIsDir, /* If true, default to directory */
|
|
+ u32 *pMode, /* OUT: Mode value */
|
|
+ char **pzErr /* OUT: Error message */
|
|
+){
|
|
+ const char *z = (const char*)sqlite3_value_text(pVal);
|
|
+ u32 mode = 0;
|
|
+ if( z==0 ){
|
|
+ mode = (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644));
|
|
+ }else if( z[0]>='0' && z[0]<='9' ){
|
|
+ mode = (unsigned int)sqlite3_value_int(pVal);
|
|
+ }else{
|
|
+ const char zTemplate[11] = "-rwxrwxrwx";
|
|
+ int i;
|
|
+ if( strlen(z)!=10 ) goto parse_error;
|
|
+ switch( z[0] ){
|
|
+ case '-': mode |= S_IFREG; break;
|
|
+ case 'd': mode |= S_IFDIR; break;
|
|
+ case 'l': mode |= S_IFLNK; break;
|
|
+ default: goto parse_error;
|
|
+ }
|
|
+ for(i=1; i<10; i++){
|
|
+ if( z[i]==zTemplate[i] ) mode |= 1 << (9-i);
|
|
+ else if( z[i]!='-' ) goto parse_error;
|
|
+ }
|
|
+ }
|
|
+ if( ((mode & S_IFDIR)==0)==bIsDir ){
|
|
+ /* The "mode" attribute is a directory, but data has been specified.
|
|
+ ** Or vice-versa - no data but "mode" is a file or symlink. */
|
|
+ *pzErr = sqlite3_mprintf("zipfile: mode does not match data");
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ *pMode = mode;
|
|
+ return SQLITE_OK;
|
|
+
|
|
+ parse_error:
|
|
+ *pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s", z);
|
|
+ return SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Both (const char*) arguments point to nul-terminated strings. Argument
|
|
+** nB is the value of strlen(zB). This function returns 0 if the strings are
|
|
+** 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==nB && memcmp(zA, zB, nA)==0 ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int zipfileBegin(sqlite3_vtab *pVtab){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)pVtab;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ assert( pTab->pWriteFd==0 );
|
|
+
|
|
+ /* Open a write fd on the file. Also load the entire central directory
|
|
+ ** structure into memory. During the transaction any new file data is
|
|
+ ** appended to the archive file, but the central directory is accumulated
|
|
+ ** in main-memory until the transaction is committed. */
|
|
+ pTab->pWriteFd = fopen(pTab->zFile, "ab+");
|
|
+ if( pTab->pWriteFd==0 ){
|
|
+ pTab->base.zErrMsg = sqlite3_mprintf(
|
|
+ "zipfile: failed to open file %s for writing", pTab->zFile
|
|
+ );
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ fseek(pTab->pWriteFd, 0, SEEK_END);
|
|
+ pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
|
|
+ rc = zipfileLoadDirectory(pTab, 0, 0);
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ zipfileCleanupTransaction(pTab);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current time as a 32-bit timestamp in UNIX epoch format (like
|
|
+** time(2)).
|
|
+*/
|
|
+static u32 zipfileTime(void){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
|
|
+ u32 ret;
|
|
+ if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
|
|
+ i64 ms;
|
|
+ pVfs->xCurrentTimeInt64(pVfs, &ms);
|
|
+ ret = (u32)((ms/1000) - ((i64)24405875 * 8640));
|
|
+ }else{
|
|
+ double day;
|
|
+ pVfs->xCurrentTime(pVfs, &day);
|
|
+ ret = (u32)((day - 2440587.5) * 86400);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a 32-bit timestamp in UNIX epoch format.
|
|
+**
|
|
+** If the value passed as the only argument is either NULL or an SQL NULL,
|
|
+** return the current time. Otherwise, return the value stored in (*pVal)
|
|
+** cast to a 32-bit unsigned integer.
|
|
+*/
|
|
+static u32 zipfileGetTime(sqlite3_value *pVal){
|
|
+ if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
|
|
+ return zipfileTime();
|
|
+ }
|
|
+ return (u32)sqlite3_value_int64(pVal);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
|
|
+** linked list. Remove it from the list and free the object.
|
|
+*/
|
|
+static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
|
|
+ if( pOld ){
|
|
+ ZipfileEntry **pp;
|
|
+ for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext));
|
|
+ *pp = (*pp)->pNext;
|
|
+ zipfileEntryFree(pOld);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xUpdate method.
|
|
+*/
|
|
+static int zipfileUpdate(
|
|
+ sqlite3_vtab *pVtab,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)pVtab;
|
|
+ int rc = SQLITE_OK; /* Return Code */
|
|
+ ZipfileEntry *pNew = 0; /* New in-memory CDS entry */
|
|
+
|
|
+ u32 mode = 0; /* Mode for new entry */
|
|
+ u32 mTime = 0; /* Modification time for new entry */
|
|
+ i64 sz = 0; /* Uncompressed size */
|
|
+ const char *zPath = 0; /* Path for new entry */
|
|
+ int nPath = 0; /* strlen(zPath) */
|
|
+ const u8 *pData = 0; /* Pointer to buffer containing content */
|
|
+ int nData = 0; /* Size of pData buffer in bytes */
|
|
+ int iMethod = 0; /* Compression method for new entry */
|
|
+ u8 *pFree = 0; /* Free this */
|
|
+ char *zFree = 0; /* Also free this */
|
|
+ ZipfileEntry *pOld = 0;
|
|
+ ZipfileEntry *pOld2 = 0;
|
|
+ int bUpdate = 0; /* True for an update that modifies "name" */
|
|
+ int bIsDir = 0;
|
|
+ u32 iCrc32 = 0;
|
|
+
|
|
+ if( pTab->pWriteFd==0 ){
|
|
+ rc = zipfileBegin(pVtab);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ }
|
|
+
|
|
+ /* If this is a DELETE or UPDATE, find the archive entry to delete. */
|
|
+ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
|
|
+ const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
|
|
+ int nDelete = (int)strlen(zDelete);
|
|
+ if( nVal>1 ){
|
|
+ const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]);
|
|
+ if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){
|
|
+ bUpdate = 1;
|
|
+ }
|
|
+ }
|
|
+ for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){
|
|
+ if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){
|
|
+ break;
|
|
+ }
|
|
+ assert( pOld->pNext );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( nVal>1 ){
|
|
+ /* Check that "sz" and "rawdata" are both NULL: */
|
|
+ if( sqlite3_value_type(apVal[5])!=SQLITE_NULL ){
|
|
+ zipfileTableErr(pTab, "sz must be NULL");
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ if( sqlite3_value_type(apVal[6])!=SQLITE_NULL ){
|
|
+ zipfileTableErr(pTab, "rawdata must be NULL");
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){
|
|
+ /* data=NULL. A directory */
|
|
+ bIsDir = 1;
|
|
+ }else{
|
|
+ /* Value specified for "data", and possibly "method". This must be
|
|
+ ** a regular file or a symlink. */
|
|
+ const u8 *aIn = sqlite3_value_blob(apVal[7]);
|
|
+ int nIn = sqlite3_value_bytes(apVal[7]);
|
|
+ int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL;
|
|
+
|
|
+ iMethod = sqlite3_value_int(apVal[8]);
|
|
+ sz = nIn;
|
|
+ pData = aIn;
|
|
+ nData = nIn;
|
|
+ if( iMethod!=0 && iMethod!=8 ){
|
|
+ zipfileTableErr(pTab, "unknown compression method: %d", iMethod);
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ }else{
|
|
+ if( bAuto || iMethod ){
|
|
+ int nCmp;
|
|
+ rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( iMethod || nCmp<nIn ){
|
|
+ iMethod = 8;
|
|
+ pData = pFree;
|
|
+ nData = nCmp;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ iCrc32 = crc32(0, aIn, nIn);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ zPath = (const char*)sqlite3_value_text(apVal[2]);
|
|
+ nPath = (int)strlen(zPath);
|
|
+ mTime = zipfileGetTime(apVal[4]);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK && bIsDir ){
|
|
+ /* For a directory, check that the last character in the path is a
|
|
+ ** '/'. 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]!='/' ){
|
|
+ zFree = sqlite3_mprintf("%s/", zPath);
|
|
+ if( zFree==0 ){ rc = SQLITE_NOMEM; }
|
|
+ zPath = (const char*)zFree;
|
|
+ nPath++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check that we're not inserting a duplicate entry -OR- updating an
|
|
+ ** entry with a path, thereby making it into a duplicate. */
|
|
+ if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){
|
|
+ ZipfileEntry *p;
|
|
+ for(p=pTab->pFirstEntry; p; p=p->pNext){
|
|
+ if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){
|
|
+ switch( sqlite3_vtab_on_conflict(pTab->db) ){
|
|
+ case SQLITE_IGNORE: {
|
|
+ goto zipfile_update_done;
|
|
+ }
|
|
+ case SQLITE_REPLACE: {
|
|
+ pOld2 = p;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ zipfileTableErr(pTab, "duplicate name: \"%s\"", zPath);
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ /* Create the new CDS record. */
|
|
+ pNew = zipfileNewEntry(zPath);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
|
|
+ pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
|
|
+ pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS;
|
|
+ pNew->cds.iCompression = (u16)iMethod;
|
|
+ zipfileMtimeToDos(&pNew->cds, mTime);
|
|
+ pNew->cds.crc32 = iCrc32;
|
|
+ pNew->cds.szCompressed = nData;
|
|
+ pNew->cds.szUncompressed = (u32)sz;
|
|
+ pNew->cds.iExternalAttr = (mode<<16);
|
|
+ pNew->cds.iOffset = (u32)pTab->szCurrent;
|
|
+ pNew->cds.nFile = (u16)nPath;
|
|
+ pNew->mUnixTime = (u32)mTime;
|
|
+ rc = zipfileAppendEntry(pTab, pNew, pData, nData);
|
|
+ zipfileAddEntry(pTab, pOld, pNew);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK && (pOld || pOld2) ){
|
|
+ ZipfileCsr *pCsr;
|
|
+ for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
|
|
+ if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){
|
|
+ pCsr->pCurrent = pCsr->pCurrent->pNext;
|
|
+ pCsr->bNoop = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ zipfileRemoveEntryFromList(pTab, pOld);
|
|
+ zipfileRemoveEntryFromList(pTab, pOld2);
|
|
+ }
|
|
+
|
|
+zipfile_update_done:
|
|
+ sqlite3_free(pFree);
|
|
+ sqlite3_free(zFree);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){
|
|
+ u8 *a = aBuf;
|
|
+ zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD);
|
|
+ zipfileWrite16(a, p->iDisk);
|
|
+ zipfileWrite16(a, p->iFirstDisk);
|
|
+ zipfileWrite16(a, p->nEntry);
|
|
+ zipfileWrite16(a, p->nEntryTotal);
|
|
+ zipfileWrite32(a, p->nSize);
|
|
+ zipfileWrite32(a, p->iOffset);
|
|
+ zipfileWrite16(a, 0); /* Size of trailing comment in bytes*/
|
|
+
|
|
+ return a-aBuf;
|
|
+}
|
|
+
|
|
+static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
|
|
+ int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer);
|
|
+ assert( nBuf==ZIPFILE_EOCD_FIXED_SZ );
|
|
+ return zipfileAppendData(pTab, pTab->aBuffer, nBuf);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Serialize the CDS structure into buffer aBuf[]. Return the number
|
|
+** of bytes written.
|
|
+*/
|
|
+static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){
|
|
+ u8 *a = aBuf;
|
|
+ ZipfileCDS *pCDS = &pEntry->cds;
|
|
+
|
|
+ if( pEntry->aExtra==0 ){
|
|
+ pCDS->nExtra = 9;
|
|
+ }
|
|
+
|
|
+ zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS);
|
|
+ zipfileWrite16(a, pCDS->iVersionMadeBy);
|
|
+ zipfileWrite16(a, pCDS->iVersionExtract);
|
|
+ zipfileWrite16(a, pCDS->flags);
|
|
+ zipfileWrite16(a, pCDS->iCompression);
|
|
+ zipfileWrite16(a, pCDS->mTime);
|
|
+ zipfileWrite16(a, pCDS->mDate);
|
|
+ zipfileWrite32(a, pCDS->crc32);
|
|
+ zipfileWrite32(a, pCDS->szCompressed);
|
|
+ zipfileWrite32(a, pCDS->szUncompressed);
|
|
+ assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
|
|
+ zipfileWrite16(a, pCDS->nFile);
|
|
+ zipfileWrite16(a, pCDS->nExtra);
|
|
+ zipfileWrite16(a, pCDS->nComment);
|
|
+ zipfileWrite16(a, pCDS->iDiskStart);
|
|
+ zipfileWrite16(a, pCDS->iInternalAttr);
|
|
+ zipfileWrite32(a, pCDS->iExternalAttr);
|
|
+ zipfileWrite32(a, pCDS->iOffset);
|
|
+
|
|
+ memcpy(a, pCDS->zFile, pCDS->nFile);
|
|
+ a += pCDS->nFile;
|
|
+
|
|
+ if( pEntry->aExtra ){
|
|
+ int n = (int)pCDS->nExtra + (int)pCDS->nComment;
|
|
+ memcpy(a, pEntry->aExtra, n);
|
|
+ a += n;
|
|
+ }else{
|
|
+ assert( pCDS->nExtra==9 );
|
|
+ zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
|
|
+ zipfileWrite16(a, 5);
|
|
+ *a++ = 0x01;
|
|
+ zipfileWrite32(a, pEntry->mUnixTime);
|
|
+ }
|
|
+
|
|
+ return a-aBuf;
|
|
+}
|
|
+
|
|
+static int zipfileCommit(sqlite3_vtab *pVtab){
|
|
+ ZipfileTab *pTab = (ZipfileTab*)pVtab;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( pTab->pWriteFd ){
|
|
+ i64 iOffset = pTab->szCurrent;
|
|
+ ZipfileEntry *p;
|
|
+ ZipfileEOCD eocd;
|
|
+ int nEntry = 0;
|
|
+
|
|
+ /* Write out all entries */
|
|
+ for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){
|
|
+ int n = zipfileSerializeCDS(p, pTab->aBuffer);
|
|
+ rc = zipfileAppendData(pTab, pTab->aBuffer, n);
|
|
+ nEntry++;
|
|
+ }
|
|
+
|
|
+ /* Write out the EOCD record */
|
|
+ eocd.iDisk = 0;
|
|
+ eocd.iFirstDisk = 0;
|
|
+ eocd.nEntry = (u16)nEntry;
|
|
+ eocd.nEntryTotal = (u16)nEntry;
|
|
+ eocd.nSize = (u32)(pTab->szCurrent - iOffset);
|
|
+ eocd.iOffset = (u32)iOffset;
|
|
+ rc = zipfileAppendEOCD(pTab, &eocd);
|
|
+
|
|
+ zipfileCleanupTransaction(pTab);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int zipfileRollback(sqlite3_vtab *pVtab){
|
|
+ return zipfileCommit(pVtab);
|
|
+}
|
|
+
|
|
+static ZipfileCsr *zipfileFindCursor(ZipfileTab *pTab, i64 iId){
|
|
+ ZipfileCsr *pCsr;
|
|
+ for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
|
|
+ if( iId==pCsr->iId ) break;
|
|
+ }
|
|
+ return pCsr;
|
|
+}
|
|
+
|
|
+static void zipfileFunctionCds(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ ZipfileCsr *pCsr;
|
|
+ ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context);
|
|
+ assert( argc>0 );
|
|
+
|
|
+ pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0]));
|
|
+ if( pCsr ){
|
|
+ ZipfileCDS *p = &pCsr->pCurrent->cds;
|
|
+ char *zRes = sqlite3_mprintf("{"
|
|
+ "\"version-made-by\" : %u, "
|
|
+ "\"version-to-extract\" : %u, "
|
|
+ "\"flags\" : %u, "
|
|
+ "\"compression\" : %u, "
|
|
+ "\"time\" : %u, "
|
|
+ "\"date\" : %u, "
|
|
+ "\"crc32\" : %u, "
|
|
+ "\"compressed-size\" : %u, "
|
|
+ "\"uncompressed-size\" : %u, "
|
|
+ "\"file-name-length\" : %u, "
|
|
+ "\"extra-field-length\" : %u, "
|
|
+ "\"file-comment-length\" : %u, "
|
|
+ "\"disk-number-start\" : %u, "
|
|
+ "\"internal-attr\" : %u, "
|
|
+ "\"external-attr\" : %u, "
|
|
+ "\"offset\" : %u }",
|
|
+ (u32)p->iVersionMadeBy, (u32)p->iVersionExtract,
|
|
+ (u32)p->flags, (u32)p->iCompression,
|
|
+ (u32)p->mTime, (u32)p->mDate,
|
|
+ (u32)p->crc32, (u32)p->szCompressed,
|
|
+ (u32)p->szUncompressed, (u32)p->nFile,
|
|
+ (u32)p->nExtra, (u32)p->nComment,
|
|
+ (u32)p->iDiskStart, (u32)p->iInternalAttr,
|
|
+ (u32)p->iExternalAttr, (u32)p->iOffset
|
|
+ );
|
|
+
|
|
+ if( zRes==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(zRes);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFindFunction method.
|
|
+*/
|
|
+static int zipfileFindFunction(
|
|
+ sqlite3_vtab *pVtab, /* Virtual table handle */
|
|
+ int nArg, /* Number of SQL function arguments */
|
|
+ const char *zName, /* Name of SQL function */
|
|
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
|
|
+ void **ppArg /* OUT: User data for *pxFunc */
|
|
+){
|
|
+ if( sqlite3_stricmp("zipfile_cds", zName)==0 ){
|
|
+ *pxFunc = zipfileFunctionCds;
|
|
+ *ppArg = (void*)pVtab;
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+typedef struct ZipfileBuffer ZipfileBuffer;
|
|
+struct ZipfileBuffer {
|
|
+ u8 *a; /* Pointer to buffer */
|
|
+ int n; /* Size of buffer in bytes */
|
|
+ int nAlloc; /* Byte allocated at a[] */
|
|
+};
|
|
+
|
|
+typedef struct ZipfileCtx ZipfileCtx;
|
|
+struct ZipfileCtx {
|
|
+ int nEntry;
|
|
+ ZipfileBuffer body;
|
|
+ ZipfileBuffer cds;
|
|
+};
|
|
+
|
|
+static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
|
|
+ if( pBuf->n+nByte>pBuf->nAlloc ){
|
|
+ u8 *aNew;
|
|
+ int nNew = pBuf->n ? pBuf->n*2 : 512;
|
|
+ int nReq = pBuf->n + nByte;
|
|
+
|
|
+ while( nNew<nReq ) nNew = nNew*2;
|
|
+ aNew = sqlite3_realloc(pBuf->a, nNew);
|
|
+ if( aNew==0 ) return SQLITE_NOMEM;
|
|
+ pBuf->a = aNew;
|
|
+ pBuf->nAlloc = nNew;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** xStep() callback for the zipfile() aggregate. This can be called in
|
|
+** any of the following ways:
|
|
+**
|
|
+** SELECT zipfile(name,data) ...
|
|
+** SELECT zipfile(name,mode,mtime,data) ...
|
|
+** SELECT zipfile(name,mode,mtime,data,method) ...
|
|
+*/
|
|
+void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
|
|
+ ZipfileCtx *p; /* Aggregate function context */
|
|
+ ZipfileEntry e; /* New entry to add to zip archive */
|
|
+
|
|
+ sqlite3_value *pName = 0;
|
|
+ sqlite3_value *pMode = 0;
|
|
+ sqlite3_value *pMtime = 0;
|
|
+ sqlite3_value *pData = 0;
|
|
+ sqlite3_value *pMethod = 0;
|
|
+
|
|
+ int bIsDir = 0;
|
|
+ u32 mode;
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zErr = 0;
|
|
+
|
|
+ int iMethod = -1; /* Compression method to use (0 or 8) */
|
|
+
|
|
+ const u8 *aData = 0; /* Possibly compressed data for new entry */
|
|
+ int nData = 0; /* Size of aData[] in bytes */
|
|
+ int szUncompressed = 0; /* Size of data before compression */
|
|
+ u8 *aFree = 0; /* Free this before returning */
|
|
+ u32 iCrc32 = 0; /* crc32 of uncompressed data */
|
|
+
|
|
+ char *zName = 0; /* Path (name) of new entry */
|
|
+ int nName = 0; /* Size of zName in bytes */
|
|
+ char *zFree = 0; /* Free this before returning */
|
|
+ int nByte;
|
|
+
|
|
+ memset(&e, 0, sizeof(e));
|
|
+ p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
|
+ if( p==0 ) return;
|
|
+
|
|
+ /* Martial the arguments into stack variables */
|
|
+ if( nVal!=2 && nVal!=4 && nVal!=5 ){
|
|
+ zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ pName = apVal[0];
|
|
+ if( nVal==2 ){
|
|
+ pData = apVal[1];
|
|
+ }else{
|
|
+ pMode = apVal[1];
|
|
+ pMtime = apVal[2];
|
|
+ pData = apVal[3];
|
|
+ if( nVal==5 ){
|
|
+ pMethod = apVal[4];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check that the 'name' parameter looks ok. */
|
|
+ zName = (char*)sqlite3_value_text(pName);
|
|
+ nName = sqlite3_value_bytes(pName);
|
|
+ if( zName==0 ){
|
|
+ zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+
|
|
+ /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
|
|
+ ** deflate compression) or NULL (choose automatically). */
|
|
+ if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
|
|
+ iMethod = (int)sqlite3_value_int64(pMethod);
|
|
+ if( iMethod!=0 && iMethod!=8 ){
|
|
+ zErr = sqlite3_mprintf("illegal method value: %d", iMethod);
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Now inspect the data. If this is NULL, then the new entry must be a
|
|
+ ** directory. Otherwise, figure out whether or not the data should
|
|
+ ** be deflated or simply stored in the zip archive. */
|
|
+ if( sqlite3_value_type(pData)==SQLITE_NULL ){
|
|
+ bIsDir = 1;
|
|
+ iMethod = 0;
|
|
+ }else{
|
|
+ aData = sqlite3_value_blob(pData);
|
|
+ szUncompressed = nData = sqlite3_value_bytes(pData);
|
|
+ iCrc32 = crc32(0, aData, nData);
|
|
+ if( iMethod<0 || iMethod==8 ){
|
|
+ int nOut = 0;
|
|
+ rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ if( iMethod==8 || nOut<nData ){
|
|
+ aData = aFree;
|
|
+ nData = nOut;
|
|
+ iMethod = 8;
|
|
+ }else{
|
|
+ iMethod = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Decode the "mode" argument. */
|
|
+ rc = zipfileGetMode(pMode, bIsDir, &mode, &zErr);
|
|
+ if( rc ) goto zipfile_step_out;
|
|
+
|
|
+ /* Decode the "mtime" argument. */
|
|
+ e.mUnixTime = zipfileGetTime(pMtime);
|
|
+
|
|
+ /* If this is a directory entry, ensure that there is exactly one '/'
|
|
+ ** 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]=='/' ){
|
|
+ zErr = sqlite3_mprintf("non-directory name must not end with /");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ }else{
|
|
+ if( zName[nName-1]!='/' ){
|
|
+ zName = zFree = sqlite3_mprintf("%s/", zName);
|
|
+ nName++;
|
|
+ if( zName==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ goto zipfile_step_out;
|
|
+ }
|
|
+ }else{
|
|
+ while( nName>1 && zName[nName-2]=='/' ) nName--;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Assemble the ZipfileEntry object for the new zip archive entry */
|
|
+ e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
|
|
+ e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
|
|
+ e.cds.flags = ZIPFILE_NEWENTRY_FLAGS;
|
|
+ e.cds.iCompression = (u16)iMethod;
|
|
+ zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime);
|
|
+ e.cds.crc32 = iCrc32;
|
|
+ e.cds.szCompressed = nData;
|
|
+ e.cds.szUncompressed = szUncompressed;
|
|
+ e.cds.iExternalAttr = (mode<<16);
|
|
+ e.cds.iOffset = p->body.n;
|
|
+ e.cds.nFile = (u16)nName;
|
|
+ e.cds.zFile = zName;
|
|
+
|
|
+ /* Append the LFH to the body of the new archive */
|
|
+ nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9;
|
|
+ if( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out;
|
|
+ p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]);
|
|
+
|
|
+ /* Append the data to the body of the new archive */
|
|
+ if( nData>0 ){
|
|
+ if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out;
|
|
+ memcpy(&p->body.a[p->body.n], aData, nData);
|
|
+ p->body.n += nData;
|
|
+ }
|
|
+
|
|
+ /* Append the CDS record to the directory of the new archive */
|
|
+ nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9;
|
|
+ if( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out;
|
|
+ p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]);
|
|
+
|
|
+ /* Increment the count of entries in the archive */
|
|
+ p->nEntry++;
|
|
+
|
|
+ zipfile_step_out:
|
|
+ sqlite3_free(aFree);
|
|
+ sqlite3_free(zFree);
|
|
+ if( rc ){
|
|
+ if( zErr ){
|
|
+ sqlite3_result_error(pCtx, zErr, -1);
|
|
+ }else{
|
|
+ sqlite3_result_error_code(pCtx, rc);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** xFinalize() callback for zipfile aggregate function.
|
|
+*/
|
|
+void zipfileFinal(sqlite3_context *pCtx){
|
|
+ ZipfileCtx *p;
|
|
+ ZipfileEOCD eocd;
|
|
+ int nZip;
|
|
+ u8 *aZip;
|
|
+
|
|
+ p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
|
+ if( p==0 ) return;
|
|
+ if( p->nEntry>0 ){
|
|
+ memset(&eocd, 0, sizeof(eocd));
|
|
+ eocd.nEntry = (u16)p->nEntry;
|
|
+ eocd.nEntryTotal = (u16)p->nEntry;
|
|
+ eocd.nSize = p->cds.n;
|
|
+ eocd.iOffset = p->body.n;
|
|
+
|
|
+ nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ;
|
|
+ aZip = (u8*)sqlite3_malloc(nZip);
|
|
+ if( aZip==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ memcpy(aZip, p->body.a, p->body.n);
|
|
+ memcpy(&aZip[p->body.n], p->cds.a, p->cds.n);
|
|
+ zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]);
|
|
+ sqlite3_result_blob(pCtx, aZip, nZip, zipfileFree);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_free(p->body.a);
|
|
+ sqlite3_free(p->cds.a);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Register the "zipfile" virtual table.
|
|
+*/
|
|
+static int zipfileRegister(sqlite3 *db){
|
|
+ static sqlite3_module zipfileModule = {
|
|
+ 1, /* iVersion */
|
|
+ zipfileConnect, /* xCreate */
|
|
+ zipfileConnect, /* xConnect */
|
|
+ zipfileBestIndex, /* xBestIndex */
|
|
+ zipfileDisconnect, /* xDisconnect */
|
|
+ zipfileDisconnect, /* xDestroy */
|
|
+ zipfileOpen, /* xOpen - open a cursor */
|
|
+ zipfileClose, /* xClose - close a cursor */
|
|
+ zipfileFilter, /* xFilter - configure scan constraints */
|
|
+ zipfileNext, /* xNext - advance a cursor */
|
|
+ zipfileEof, /* xEof - check for end of scan */
|
|
+ zipfileColumn, /* xColumn - read data */
|
|
+ 0, /* xRowid - read data */
|
|
+ zipfileUpdate, /* xUpdate */
|
|
+ zipfileBegin, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ zipfileCommit, /* xCommit */
|
|
+ zipfileRollback, /* xRollback */
|
|
+ zipfileFindFunction, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ };
|
|
+
|
|
+ int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0);
|
|
+ if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0,
|
|
+ zipfileStep, zipfileFinal
|
|
+ );
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#else /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+# define zipfileRegister(x) SQLITE_OK
|
|
+#endif
|
|
+
|
|
+#ifdef _WIN32
|
|
+
|
|
+#endif
|
|
+int sqlite3_zipfile_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ return zipfileRegister(db);
|
|
+}
|
|
+
|
|
+/************************* End ../ext/misc/zipfile.c ********************/
|
|
+/************************* Begin ../ext/misc/sqlar.c ******************/
|
|
+/*
|
|
+** 2017-12-17
|
|
+**
|
|
+** 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.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** Utility functions sqlar_compress() and sqlar_uncompress(). Useful
|
|
+** for working with sqlar archives and used by the shell tool's built-in
|
|
+** sqlar support.
|
|
+*/
|
|
+SQLITE_EXTENSION_INIT1
|
|
+#include <zlib.h>
|
|
+
|
|
+/*
|
|
+** Implementation of the "sqlar_compress(X)" SQL function.
|
|
+**
|
|
+** If the type of X is SQLITE_BLOB, and compressing that blob using
|
|
+** zlib utility function compress() yields a smaller blob, return the
|
|
+** compressed blob. Otherwise, return a copy of X.
|
|
+**
|
|
+** SQLar uses the "zlib format" for compressed content. The zlib format
|
|
+** contains a two-byte identification header and a four-byte checksum at
|
|
+** the end. This is different from ZIP which uses the raw deflate format.
|
|
+**
|
|
+** Future enhancements to SQLar might add support for new compression formats.
|
|
+** If so, those new formats will be identified by alternative headers in the
|
|
+** compressed data.
|
|
+*/
|
|
+static void sqlarCompressFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ assert( argc==1 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
|
+ const Bytef *pData = sqlite3_value_blob(argv[0]);
|
|
+ uLong nData = sqlite3_value_bytes(argv[0]);
|
|
+ uLongf nOut = compressBound(nData);
|
|
+ Bytef *pOut;
|
|
+
|
|
+ pOut = (Bytef*)sqlite3_malloc(nOut);
|
|
+ if( pOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }else{
|
|
+ if( Z_OK!=compress(pOut, &nOut, pData, nData) ){
|
|
+ sqlite3_result_error(context, "error in compress()", -1);
|
|
+ }else if( nOut<nData ){
|
|
+ sqlite3_result_blob(context, pOut, nOut, SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ sqlite3_result_value(context, argv[0]);
|
|
+ }
|
|
+ sqlite3_free(pOut);
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_value(context, argv[0]);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the "sqlar_uncompress(X,SZ)" SQL function
|
|
+**
|
|
+** Parameter SZ is interpreted as an integer. If it is less than or
|
|
+** equal to zero, then this function returns a copy of X. Or, if
|
|
+** SZ is equal to the size of X when interpreted as a blob, also
|
|
+** return a copy of X. Otherwise, decompress blob X using zlib
|
|
+** utility function uncompress() and return the results (another
|
|
+** blob).
|
|
+*/
|
|
+static void sqlarUncompressFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ uLong nData;
|
|
+ uLongf sz;
|
|
+
|
|
+ assert( argc==2 );
|
|
+ sz = sqlite3_value_int(argv[1]);
|
|
+
|
|
+ if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
|
|
+ sqlite3_result_value(context, argv[0]);
|
|
+ }else{
|
|
+ const Bytef *pData= sqlite3_value_blob(argv[0]);
|
|
+ Bytef *pOut = sqlite3_malloc(sz);
|
|
+ if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){
|
|
+ sqlite3_result_error(context, "error in uncompress()", -1);
|
|
+ }else{
|
|
+ sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT);
|
|
+ }
|
|
+ sqlite3_free(pOut);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef _WIN32
|
|
+
|
|
+#endif
|
|
+int sqlite3_sqlar_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, "sqlar_compress", 1, SQLITE_UTF8, 0,
|
|
+ sqlarCompressFunc, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "sqlar_uncompress", 2, SQLITE_UTF8, 0,
|
|
+ sqlarUncompressFunc, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/************************* End ../ext/misc/sqlar.c ********************/
|
|
+#endif
|
|
+/************************* Begin ../ext/expert/sqlite3expert.h ******************/
|
|
+/*
|
|
+** 2017 April 07
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+*/
|
|
+
|
|
+
|
|
+
|
|
+typedef struct sqlite3expert sqlite3expert;
|
|
+
|
|
+/*
|
|
+** Create a new sqlite3expert object.
|
|
+**
|
|
+** If successful, a pointer to the new object is returned and (*pzErr) set
|
|
+** to NULL. Or, if an error occurs, NULL is returned and (*pzErr) set to
|
|
+** an English-language error message. In this case it is the responsibility
|
|
+** of the caller to eventually free the error message buffer using
|
|
+** sqlite3_free().
|
|
+*/
|
|
+sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErr);
|
|
+
|
|
+/*
|
|
+** Configure an sqlite3expert object.
|
|
+**
|
|
+** EXPERT_CONFIG_SAMPLE:
|
|
+** By default, sqlite3_expert_analyze() generates sqlite_stat1 data for
|
|
+** each candidate index. This involves scanning and sorting the entire
|
|
+** contents of each user database table once for each candidate index
|
|
+** associated with the table. For large databases, this can be
|
|
+** prohibitively slow. This option allows the sqlite3expert object to
|
|
+** be configured so that sqlite_stat1 data is instead generated based on a
|
|
+** subset of each table, or so that no sqlite_stat1 data is used at all.
|
|
+**
|
|
+** A single integer argument is passed to this option. If the value is less
|
|
+** than or equal to zero, then no sqlite_stat1 data is generated or used by
|
|
+** the analysis - indexes are recommended based on the database schema only.
|
|
+** Or, if the value is 100 or greater, complete sqlite_stat1 data is
|
|
+** generated for each candidate index (this is the default). Finally, if the
|
|
+** value falls between 0 and 100, then it represents the percentage of user
|
|
+** table rows that should be considered when generating sqlite_stat1 data.
|
|
+**
|
|
+** Examples:
|
|
+**
|
|
+** // Do not generate any sqlite_stat1 data
|
|
+** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 0);
|
|
+**
|
|
+** // Generate sqlite_stat1 data based on 10% of the rows in each table.
|
|
+** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 10);
|
|
+*/
|
|
+int sqlite3_expert_config(sqlite3expert *p, int op, ...);
|
|
+
|
|
+#define EXPERT_CONFIG_SAMPLE 1 /* int */
|
|
+
|
|
+/*
|
|
+** Specify zero or more SQL statements to be included in the analysis.
|
|
+**
|
|
+** Buffer zSql must contain zero or more complete SQL statements. This
|
|
+** function parses all statements contained in the buffer and adds them
|
|
+** to the internal list of statements to analyze. If successful, SQLITE_OK
|
|
+** is returned and (*pzErr) set to NULL. Or, if an error occurs - for example
|
|
+** due to a error in the SQL - an SQLite error code is returned and (*pzErr)
|
|
+** may be set to point to an English language error message. In this case
|
|
+** the caller is responsible for eventually freeing the error message buffer
|
|
+** using sqlite3_free().
|
|
+**
|
|
+** If an error does occur while processing one of the statements in the
|
|
+** buffer passed as the second argument, none of the statements in the
|
|
+** buffer are added to the analysis.
|
|
+**
|
|
+** This function must be called before sqlite3_expert_analyze(). If a call
|
|
+** to this function is made on an sqlite3expert object that has already
|
|
+** been passed to sqlite3_expert_analyze() SQLITE_MISUSE is returned
|
|
+** immediately and no statements are added to the analysis.
|
|
+*/
|
|
+int sqlite3_expert_sql(
|
|
+ sqlite3expert *p, /* From a successful sqlite3_expert_new() */
|
|
+ const char *zSql, /* SQL statement(s) to add */
|
|
+ char **pzErr /* OUT: Error message (if any) */
|
|
+);
|
|
+
|
|
+
|
|
+/*
|
|
+** This function is called after the sqlite3expert object has been configured
|
|
+** with all SQL statements using sqlite3_expert_sql() to actually perform
|
|
+** the analysis. Once this function has been called, it is not possible to
|
|
+** add further SQL statements to the analysis.
|
|
+**
|
|
+** If successful, SQLITE_OK is returned and (*pzErr) is set to NULL. Or, if
|
|
+** an error occurs, an SQLite error code is returned and (*pzErr) set to
|
|
+** point to a buffer containing an English language error message. In this
|
|
+** case it is the responsibility of the caller to eventually free the buffer
|
|
+** using sqlite3_free().
|
|
+**
|
|
+** If an error does occur within this function, the sqlite3expert object
|
|
+** is no longer useful for any purpose. At that point it is no longer
|
|
+** possible to add further SQL statements to the object or to re-attempt
|
|
+** the analysis. The sqlite3expert object must still be freed using a call
|
|
+** sqlite3_expert_destroy().
|
|
+*/
|
|
+int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr);
|
|
+
|
|
+/*
|
|
+** Return the total number of statements loaded using sqlite3_expert_sql().
|
|
+** The total number of SQL statements may be different from the total number
|
|
+** to calls to sqlite3_expert_sql().
|
|
+*/
|
|
+int sqlite3_expert_count(sqlite3expert*);
|
|
+
|
|
+/*
|
|
+** Return a component of the report.
|
|
+**
|
|
+** This function is called after sqlite3_expert_analyze() to extract the
|
|
+** results of the analysis. Each call to this function returns either a
|
|
+** NULL pointer or a pointer to a buffer containing a nul-terminated string.
|
|
+** The value passed as the third argument must be one of the EXPERT_REPORT_*
|
|
+** #define constants defined below.
|
|
+**
|
|
+** For some EXPERT_REPORT_* parameters, the buffer returned contains
|
|
+** information relating to a specific SQL statement. In these cases that
|
|
+** SQL statement is identified by the value passed as the second argument.
|
|
+** SQL statements are numbered from 0 in the order in which they are parsed.
|
|
+** If an out-of-range value (less than zero or equal to or greater than the
|
|
+** value returned by sqlite3_expert_count()) is passed as the second argument
|
|
+** along with such an EXPERT_REPORT_* parameter, NULL is always returned.
|
|
+**
|
|
+** EXPERT_REPORT_SQL:
|
|
+** Return the text of SQL statement iStmt.
|
|
+**
|
|
+** EXPERT_REPORT_INDEXES:
|
|
+** Return a buffer containing the CREATE INDEX statements for all recommended
|
|
+** indexes for statement iStmt. If there are no new recommeded indexes, NULL
|
|
+** is returned.
|
|
+**
|
|
+** EXPERT_REPORT_PLAN:
|
|
+** Return a buffer containing the EXPLAIN QUERY PLAN output for SQL query
|
|
+** iStmt after the proposed indexes have been added to the database schema.
|
|
+**
|
|
+** EXPERT_REPORT_CANDIDATES:
|
|
+** Return a pointer to a buffer containing the CREATE INDEX statements
|
|
+** for all indexes that were tested (for all SQL statements). The iStmt
|
|
+** parameter is ignored for EXPERT_REPORT_CANDIDATES calls.
|
|
+*/
|
|
+const char *sqlite3_expert_report(sqlite3expert*, int iStmt, int eReport);
|
|
+
|
|
+/*
|
|
+** Values for the third argument passed to sqlite3_expert_report().
|
|
+*/
|
|
+#define EXPERT_REPORT_SQL 1
|
|
+#define EXPERT_REPORT_INDEXES 2
|
|
+#define EXPERT_REPORT_PLAN 3
|
|
+#define EXPERT_REPORT_CANDIDATES 4
|
|
+
|
|
+/*
|
|
+** Free an (sqlite3expert*) handle and all associated resources. There
|
|
+** should be one call to this function for each successful call to
|
|
+** sqlite3-expert_new().
|
|
+*/
|
|
+void sqlite3_expert_destroy(sqlite3expert*);
|
|
+
|
|
+
|
|
+
|
|
+/************************* End ../ext/expert/sqlite3expert.h ********************/
|
|
+/************************* Begin ../ext/expert/sqlite3expert.c ******************/
|
|
+/*
|
|
+** 2017 April 09
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+*/
|
|
+#include <assert.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+
|
|
+/* typedef sqlite3_int64 i64; */
|
|
+/* typedef sqlite3_uint64 u64; */
|
|
+
|
|
+typedef struct IdxColumn IdxColumn;
|
|
+typedef struct IdxConstraint IdxConstraint;
|
|
+typedef struct IdxScan IdxScan;
|
|
+typedef struct IdxStatement IdxStatement;
|
|
+typedef struct IdxTable IdxTable;
|
|
+typedef struct IdxWrite IdxWrite;
|
|
+
|
|
+#define STRLEN (int)strlen
|
|
+
|
|
+/*
|
|
+** A temp table name that we assume no user database will actually use.
|
|
+** If this assumption proves incorrect triggers on the table with the
|
|
+** conflicting name will be ignored.
|
|
+*/
|
|
+#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776"
|
|
+
|
|
+/*
|
|
+** A single constraint. Equivalent to either "col = ?" or "col < ?" (or
|
|
+** any other type of single-ended range constraint on a column).
|
|
+**
|
|
+** pLink:
|
|
+** Used to temporarily link IdxConstraint objects into lists while
|
|
+** creating candidate indexes.
|
|
+*/
|
|
+struct IdxConstraint {
|
|
+ char *zColl; /* Collation sequence */
|
|
+ int bRange; /* True for range, false for eq */
|
|
+ int iCol; /* Constrained table column */
|
|
+ int bFlag; /* Used by idxFindCompatible() */
|
|
+ int bDesc; /* True if ORDER BY <expr> DESC */
|
|
+ IdxConstraint *pNext; /* Next constraint in pEq or pRange list */
|
|
+ IdxConstraint *pLink; /* See above */
|
|
+};
|
|
+
|
|
+/*
|
|
+** A single scan of a single table.
|
|
+*/
|
|
+struct IdxScan {
|
|
+ IdxTable *pTab; /* Associated table object */
|
|
+ int iDb; /* Database containing table zTable */
|
|
+ i64 covering; /* Mask of columns required for cov. index */
|
|
+ IdxConstraint *pOrder; /* ORDER BY columns */
|
|
+ IdxConstraint *pEq; /* List of == constraints */
|
|
+ IdxConstraint *pRange; /* List of < constraints */
|
|
+ IdxScan *pNextScan; /* Next IdxScan object for same analysis */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Information regarding a single database table. Extracted from
|
|
+** "PRAGMA table_info" by function idxGetTableInfo().
|
|
+*/
|
|
+struct IdxColumn {
|
|
+ char *zName;
|
|
+ char *zColl;
|
|
+ int iPk;
|
|
+};
|
|
+struct IdxTable {
|
|
+ int nCol;
|
|
+ char *zName; /* Table name */
|
|
+ IdxColumn *aCol;
|
|
+ IdxTable *pNext; /* Next table in linked list of all tables */
|
|
+};
|
|
+
|
|
+/*
|
|
+** An object of the following type is created for each unique table/write-op
|
|
+** seen. The objects are stored in a singly-linked list beginning at
|
|
+** sqlite3expert.pWrite.
|
|
+*/
|
|
+struct IdxWrite {
|
|
+ IdxTable *pTab;
|
|
+ int eOp; /* SQLITE_UPDATE, DELETE or INSERT */
|
|
+ IdxWrite *pNext;
|
|
+};
|
|
+
|
|
+/*
|
|
+** Each statement being analyzed is represented by an instance of this
|
|
+** structure.
|
|
+*/
|
|
+struct IdxStatement {
|
|
+ int iId; /* Statement number */
|
|
+ char *zSql; /* SQL statement */
|
|
+ char *zIdx; /* Indexes */
|
|
+ char *zEQP; /* Plan */
|
|
+ IdxStatement *pNext;
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+** A hash table for storing strings. With space for a payload string
|
|
+** with each entry. Methods are:
|
|
+**
|
|
+** idxHashInit()
|
|
+** idxHashClear()
|
|
+** idxHashAdd()
|
|
+** idxHashSearch()
|
|
+*/
|
|
+#define IDX_HASH_SIZE 1023
|
|
+typedef struct IdxHashEntry IdxHashEntry;
|
|
+typedef struct IdxHash IdxHash;
|
|
+struct IdxHashEntry {
|
|
+ char *zKey; /* nul-terminated key */
|
|
+ char *zVal; /* nul-terminated value string */
|
|
+ char *zVal2; /* nul-terminated value string 2 */
|
|
+ IdxHashEntry *pHashNext; /* Next entry in same hash bucket */
|
|
+ IdxHashEntry *pNext; /* Next entry in hash */
|
|
+};
|
|
+struct IdxHash {
|
|
+ IdxHashEntry *pFirst;
|
|
+ IdxHashEntry *aHash[IDX_HASH_SIZE];
|
|
+};
|
|
+
|
|
+/*
|
|
+** sqlite3expert object.
|
|
+*/
|
|
+struct sqlite3expert {
|
|
+ int iSample; /* Percentage of tables to sample for stat1 */
|
|
+ sqlite3 *db; /* User database */
|
|
+ sqlite3 *dbm; /* In-memory db for this analysis */
|
|
+ sqlite3 *dbv; /* Vtab schema for this analysis */
|
|
+ IdxTable *pTable; /* List of all IdxTable objects */
|
|
+ IdxScan *pScan; /* List of scan objects */
|
|
+ IdxWrite *pWrite; /* List of write objects */
|
|
+ IdxStatement *pStatement; /* List of IdxStatement objects */
|
|
+ int bRun; /* True once analysis has run */
|
|
+ char **pzErrmsg;
|
|
+ int rc; /* Error code from whereinfo hook */
|
|
+ IdxHash hIdx; /* Hash containing all candidate indexes */
|
|
+ char *zCandidates; /* For EXPERT_REPORT_CANDIDATES */
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc().
|
|
+** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL.
|
|
+*/
|
|
+static void *idxMalloc(int *pRc, int nByte){
|
|
+ void *pRet;
|
|
+ assert( *pRc==SQLITE_OK );
|
|
+ assert( nByte>0 );
|
|
+ pRet = sqlite3_malloc(nByte);
|
|
+ if( pRet ){
|
|
+ memset(pRet, 0, nByte);
|
|
+ }else{
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Initialize an IdxHash hash table.
|
|
+*/
|
|
+static void idxHashInit(IdxHash *pHash){
|
|
+ memset(pHash, 0, sizeof(IdxHash));
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reset an IdxHash hash table.
|
|
+*/
|
|
+static void idxHashClear(IdxHash *pHash){
|
|
+ int i;
|
|
+ for(i=0; i<IDX_HASH_SIZE; i++){
|
|
+ IdxHashEntry *pEntry;
|
|
+ IdxHashEntry *pNext;
|
|
+ for(pEntry=pHash->aHash[i]; pEntry; pEntry=pNext){
|
|
+ pNext = pEntry->pHashNext;
|
|
+ sqlite3_free(pEntry->zVal2);
|
|
+ sqlite3_free(pEntry);
|
|
+ }
|
|
+ }
|
|
+ memset(pHash, 0, sizeof(IdxHash));
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the index of the hash bucket that the string specified by the
|
|
+** arguments to this function belongs.
|
|
+*/
|
|
+static int idxHashString(const char *z, int n){
|
|
+ unsigned int ret = 0;
|
|
+ int i;
|
|
+ for(i=0; i<n; i++){
|
|
+ ret += (ret<<3) + (unsigned char)(z[i]);
|
|
+ }
|
|
+ return (int)(ret % IDX_HASH_SIZE);
|
|
+}
|
|
+
|
|
+/*
|
|
+** If zKey is already present in the hash table, return non-zero and do
|
|
+** nothing. Otherwise, add an entry with key zKey and payload string zVal to
|
|
+** the hash table passed as the second argument.
|
|
+*/
|
|
+static int idxHashAdd(
|
|
+ int *pRc,
|
|
+ IdxHash *pHash,
|
|
+ const char *zKey,
|
|
+ const char *zVal
|
|
+){
|
|
+ int nKey = STRLEN(zKey);
|
|
+ int iHash = idxHashString(zKey, nKey);
|
|
+ int nVal = (zVal ? STRLEN(zVal) : 0);
|
|
+ IdxHashEntry *pEntry;
|
|
+ assert( iHash>=0 );
|
|
+ for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
|
|
+ if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + nKey+1 + nVal+1);
|
|
+ if( pEntry ){
|
|
+ pEntry->zKey = (char*)&pEntry[1];
|
|
+ memcpy(pEntry->zKey, zKey, nKey);
|
|
+ if( zVal ){
|
|
+ pEntry->zVal = &pEntry->zKey[nKey+1];
|
|
+ memcpy(pEntry->zVal, zVal, nVal);
|
|
+ }
|
|
+ pEntry->pHashNext = pHash->aHash[iHash];
|
|
+ pHash->aHash[iHash] = pEntry;
|
|
+
|
|
+ pEntry->pNext = pHash->pFirst;
|
|
+ pHash->pFirst = pEntry;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If zKey/nKey is present in the hash table, return a pointer to the
|
|
+** hash-entry object.
|
|
+*/
|
|
+static IdxHashEntry *idxHashFind(IdxHash *pHash, const char *zKey, int nKey){
|
|
+ int iHash;
|
|
+ IdxHashEntry *pEntry;
|
|
+ if( nKey<0 ) nKey = STRLEN(zKey);
|
|
+ iHash = idxHashString(zKey, nKey);
|
|
+ assert( iHash>=0 );
|
|
+ for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
|
|
+ if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){
|
|
+ return pEntry;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If the hash table contains an entry with a key equal to the string
|
|
+** passed as the final two arguments to this function, return a pointer
|
|
+** to the payload string. Otherwise, if zKey/nKey is not present in the
|
|
+** hash table, return NULL.
|
|
+*/
|
|
+static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
|
|
+ IdxHashEntry *pEntry = idxHashFind(pHash, zKey, nKey);
|
|
+ if( pEntry ) return pEntry->zVal;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl
|
|
+** variable to point to a copy of nul-terminated string zColl.
|
|
+*/
|
|
+static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){
|
|
+ IdxConstraint *pNew;
|
|
+ int nColl = STRLEN(zColl);
|
|
+
|
|
+ assert( *pRc==SQLITE_OK );
|
|
+ pNew = (IdxConstraint*)idxMalloc(pRc, sizeof(IdxConstraint) * nColl + 1);
|
|
+ if( pNew ){
|
|
+ pNew->zColl = (char*)&pNew[1];
|
|
+ memcpy(pNew->zColl, zColl, nColl+1);
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+/*
|
|
+** An error associated with database handle db has just occurred. Pass
|
|
+** the error message to callback function xOut.
|
|
+*/
|
|
+static void idxDatabaseError(
|
|
+ sqlite3 *db, /* Database handle */
|
|
+ char **pzErrmsg /* Write error here */
|
|
+){
|
|
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+}
|
|
+
|
|
+/*
|
|
+** Prepare an SQL statement.
|
|
+*/
|
|
+static int idxPrepareStmt(
|
|
+ sqlite3 *db, /* Database handle to compile against */
|
|
+ sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
|
|
+ char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */
|
|
+ const char *zSql /* SQL statement to compile */
|
|
+){
|
|
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ *ppStmt = 0;
|
|
+ idxDatabaseError(db, pzErrmsg);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Prepare an SQL statement using the results of a printf() formatting.
|
|
+*/
|
|
+static int idxPrintfPrepareStmt(
|
|
+ sqlite3 *db, /* Database handle to compile against */
|
|
+ sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
|
|
+ char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */
|
|
+ const char *zFmt, /* printf() format of SQL statement */
|
|
+ ... /* Trailing printf() arguments */
|
|
+){
|
|
+ va_list ap;
|
|
+ int rc;
|
|
+ char *zSql;
|
|
+ va_start(ap, zFmt);
|
|
+ zSql = sqlite3_vmprintf(zFmt, ap);
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = idxPrepareStmt(db, ppStmt, pzErrmsg, zSql);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ va_end(ap);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*************************************************************************
|
|
+** Beginning of virtual table implementation.
|
|
+*/
|
|
+typedef struct ExpertVtab ExpertVtab;
|
|
+struct ExpertVtab {
|
|
+ sqlite3_vtab base;
|
|
+ IdxTable *pTab;
|
|
+ sqlite3expert *pExpert;
|
|
+};
|
|
+
|
|
+typedef struct ExpertCsr ExpertCsr;
|
|
+struct ExpertCsr {
|
|
+ sqlite3_vtab_cursor base;
|
|
+ sqlite3_stmt *pData;
|
|
+};
|
|
+
|
|
+static char *expertDequote(const char *zIn){
|
|
+ int n = STRLEN(zIn);
|
|
+ char *zRet = sqlite3_malloc(n);
|
|
+
|
|
+ assert( zIn[0]=='\'' );
|
|
+ assert( zIn[n-1]=='\'' );
|
|
+
|
|
+ if( zRet ){
|
|
+ int iOut = 0;
|
|
+ int iIn = 0;
|
|
+ for(iIn=1; iIn<(n-1); iIn++){
|
|
+ if( zIn[iIn]=='\'' ){
|
|
+ assert( zIn[iIn+1]=='\'' );
|
|
+ iIn++;
|
|
+ }
|
|
+ zRet[iOut++] = zIn[iIn];
|
|
+ }
|
|
+ zRet[iOut] = '\0';
|
|
+ }
|
|
+
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is the implementation of both the xConnect and xCreate
|
|
+** methods of the r-tree virtual table.
|
|
+**
|
|
+** argv[0] -> module name
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[...] -> column names...
|
|
+*/
|
|
+static int expertConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3expert *pExpert = (sqlite3expert*)pAux;
|
|
+ ExpertVtab *p = 0;
|
|
+ int rc;
|
|
+
|
|
+ if( argc!=4 ){
|
|
+ *pzErr = sqlite3_mprintf("internal error!");
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ char *zCreateTable = expertDequote(argv[3]);
|
|
+ if( zCreateTable ){
|
|
+ rc = sqlite3_declare_vtab(db, zCreateTable);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p = idxMalloc(&rc, sizeof(ExpertVtab));
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->pExpert = pExpert;
|
|
+ p->pTab = pExpert->pTable;
|
|
+ assert( sqlite3_stricmp(p->pTab->zName, argv[2])==0 );
|
|
+ }
|
|
+ sqlite3_free(zCreateTable);
|
|
+ }else{
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *ppVtab = (sqlite3_vtab*)p;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int expertDisconnect(sqlite3_vtab *pVtab){
|
|
+ ExpertVtab *p = (ExpertVtab*)pVtab;
|
|
+ sqlite3_free(p);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static int expertBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pIdxInfo){
|
|
+ ExpertVtab *p = (ExpertVtab*)pVtab;
|
|
+ int rc = SQLITE_OK;
|
|
+ int n = 0;
|
|
+ IdxScan *pScan;
|
|
+ const int opmask =
|
|
+ SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_GT |
|
|
+ SQLITE_INDEX_CONSTRAINT_LT | SQLITE_INDEX_CONSTRAINT_GE |
|
|
+ SQLITE_INDEX_CONSTRAINT_LE;
|
|
+
|
|
+ pScan = idxMalloc(&rc, sizeof(IdxScan));
|
|
+ if( pScan ){
|
|
+ int i;
|
|
+
|
|
+ /* Link the new scan object into the list */
|
|
+ pScan->pTab = p->pTab;
|
|
+ pScan->pNextScan = p->pExpert->pScan;
|
|
+ p->pExpert->pScan = pScan;
|
|
+
|
|
+ /* Add the constraints to the IdxScan object */
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
|
|
+ if( pCons->usable
|
|
+ && pCons->iColumn>=0
|
|
+ && p->pTab->aCol[pCons->iColumn].iPk==0
|
|
+ && (pCons->op & opmask)
|
|
+ ){
|
|
+ IdxConstraint *pNew;
|
|
+ const char *zColl = sqlite3_vtab_collation(pIdxInfo, i);
|
|
+ pNew = idxNewConstraint(&rc, zColl);
|
|
+ if( pNew ){
|
|
+ pNew->iCol = pCons->iColumn;
|
|
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ pNew->pNext = pScan->pEq;
|
|
+ pScan->pEq = pNew;
|
|
+ }else{
|
|
+ pNew->bRange = 1;
|
|
+ pNew->pNext = pScan->pRange;
|
|
+ pScan->pRange = pNew;
|
|
+ }
|
|
+ }
|
|
+ n++;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = n;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Add the ORDER BY to the IdxScan object */
|
|
+ for(i=pIdxInfo->nOrderBy-1; i>=0; i--){
|
|
+ int iCol = pIdxInfo->aOrderBy[i].iColumn;
|
|
+ if( iCol>=0 ){
|
|
+ IdxConstraint *pNew = idxNewConstraint(&rc, p->pTab->aCol[iCol].zColl);
|
|
+ if( pNew ){
|
|
+ pNew->iCol = iCol;
|
|
+ pNew->bDesc = pIdxInfo->aOrderBy[i].desc;
|
|
+ pNew->pNext = pScan->pOrder;
|
|
+ pNew->pLink = pScan->pOrder;
|
|
+ pScan->pOrder = pNew;
|
|
+ n++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pIdxInfo->estimatedCost = 1000000.0 / (n+1);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int expertUpdate(
|
|
+ sqlite3_vtab *pVtab,
|
|
+ int nData,
|
|
+ sqlite3_value **azData,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ (void)pVtab;
|
|
+ (void)nData;
|
|
+ (void)azData;
|
|
+ (void)pRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xOpen method.
|
|
+*/
|
|
+static int expertOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ int rc = SQLITE_OK;
|
|
+ ExpertCsr *pCsr;
|
|
+ (void)pVTab;
|
|
+ pCsr = idxMalloc(&rc, sizeof(ExpertCsr));
|
|
+ *ppCursor = (sqlite3_vtab_cursor*)pCsr;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xClose method.
|
|
+*/
|
|
+static int expertClose(sqlite3_vtab_cursor *cur){
|
|
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
|
|
+ sqlite3_finalize(pCsr->pData);
|
|
+ sqlite3_free(pCsr);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xEof method.
|
|
+**
|
|
+** Return non-zero if the cursor does not currently point to a valid
|
|
+** record (i.e if the scan has finished), or zero otherwise.
|
|
+*/
|
|
+static int expertEof(sqlite3_vtab_cursor *cur){
|
|
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
|
|
+ return pCsr->pData==0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xNext method.
|
|
+*/
|
|
+static int expertNext(sqlite3_vtab_cursor *cur){
|
|
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ assert( pCsr->pData );
|
|
+ rc = sqlite3_step(pCsr->pData);
|
|
+ if( rc!=SQLITE_ROW ){
|
|
+ rc = sqlite3_finalize(pCsr->pData);
|
|
+ pCsr->pData = 0;
|
|
+ }else{
|
|
+ rc = SQLITE_OK;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xRowid method.
|
|
+*/
|
|
+static int expertRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ (void)cur;
|
|
+ *pRowid = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xColumn method.
|
|
+*/
|
|
+static int expertColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
|
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
|
|
+ sqlite3_value *pVal;
|
|
+ pVal = sqlite3_column_value(pCsr->pData, i);
|
|
+ if( pVal ){
|
|
+ sqlite3_result_value(ctx, pVal);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Virtual table module xFilter method.
|
|
+*/
|
|
+static int expertFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
|
|
+ ExpertVtab *pVtab = (ExpertVtab*)(cur->pVtab);
|
|
+ sqlite3expert *pExpert = pVtab->pExpert;
|
|
+ int rc;
|
|
+
|
|
+ (void)idxNum;
|
|
+ (void)idxStr;
|
|
+ (void)argc;
|
|
+ (void)argv;
|
|
+ rc = sqlite3_finalize(pCsr->pData);
|
|
+ pCsr->pData = 0;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
|
|
+ "SELECT * FROM main.%Q WHERE sample()", pVtab->pTab->zName
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = expertNext(cur);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int idxRegisterVtab(sqlite3expert *p){
|
|
+ static sqlite3_module expertModule = {
|
|
+ 2, /* iVersion */
|
|
+ expertConnect, /* xCreate - create a table */
|
|
+ expertConnect, /* xConnect - connect to an existing table */
|
|
+ expertBestIndex, /* xBestIndex - Determine search strategy */
|
|
+ expertDisconnect, /* xDisconnect - Disconnect from a table */
|
|
+ expertDisconnect, /* xDestroy - Drop a table */
|
|
+ expertOpen, /* xOpen - open a cursor */
|
|
+ expertClose, /* xClose - close a cursor */
|
|
+ expertFilter, /* xFilter - configure scan constraints */
|
|
+ expertNext, /* xNext - advance a cursor */
|
|
+ expertEof, /* xEof */
|
|
+ expertColumn, /* xColumn - read data */
|
|
+ expertRowid, /* xRowid - read data */
|
|
+ expertUpdate, /* xUpdate - write data */
|
|
+ 0, /* xBegin - begin transaction */
|
|
+ 0, /* xSync - sync transaction */
|
|
+ 0, /* xCommit - commit transaction */
|
|
+ 0, /* xRollback - rollback transaction */
|
|
+ 0, /* xFindFunction - function overloading */
|
|
+ 0, /* xRename - rename the table */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
+ };
|
|
+
|
|
+ return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
|
|
+}
|
|
+/*
|
|
+** End of virtual table implementation.
|
|
+*************************************************************************/
|
|
+/*
|
|
+** Finalize SQL statement pStmt. If (*pRc) is SQLITE_OK when this function
|
|
+** is called, set it to the return value of sqlite3_finalize() before
|
|
+** returning. Otherwise, discard the sqlite3_finalize() return value.
|
|
+*/
|
|
+static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){
|
|
+ int rc = sqlite3_finalize(pStmt);
|
|
+ if( *pRc==SQLITE_OK ) *pRc = rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Attempt to allocate an IdxTable structure corresponding to table zTab
|
|
+** in the main database of connection db. If successful, set (*ppOut) to
|
|
+** point to the new object and return SQLITE_OK. Otherwise, return an
|
|
+** SQLite error code and set (*ppOut) to NULL. In this case *pzErrmsg may be
|
|
+** set to point to an error string.
|
|
+**
|
|
+** It is the responsibility of the caller to eventually free either the
|
|
+** IdxTable object or error message using sqlite3_free().
|
|
+*/
|
|
+static int idxGetTableInfo(
|
|
+ sqlite3 *db, /* Database connection to read details from */
|
|
+ const char *zTab, /* Table name */
|
|
+ IdxTable **ppOut, /* OUT: New object (if successful) */
|
|
+ char **pzErrmsg /* OUT: Error message (if not) */
|
|
+){
|
|
+ sqlite3_stmt *p1 = 0;
|
|
+ int nCol = 0;
|
|
+ int nTab = STRLEN(zTab);
|
|
+ int nByte = sizeof(IdxTable) + nTab + 1;
|
|
+ IdxTable *pNew = 0;
|
|
+ int rc, rc2;
|
|
+ char *pCsr = 0;
|
|
+
|
|
+ rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
|
|
+ const char *zCol = (const char*)sqlite3_column_text(p1, 1);
|
|
+ nByte += 1 + STRLEN(zCol);
|
|
+ rc = sqlite3_table_column_metadata(
|
|
+ db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
|
|
+ );
|
|
+ nByte += 1 + STRLEN(zCol);
|
|
+ nCol++;
|
|
+ }
|
|
+ rc2 = sqlite3_reset(p1);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
+
|
|
+ nByte += sizeof(IdxColumn) * nCol;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = idxMalloc(&rc, nByte);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew->aCol = (IdxColumn*)&pNew[1];
|
|
+ pNew->nCol = nCol;
|
|
+ pCsr = (char*)&pNew->aCol[nCol];
|
|
+ }
|
|
+
|
|
+ nCol = 0;
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
|
|
+ const char *zCol = (const char*)sqlite3_column_text(p1, 1);
|
|
+ int nCopy = STRLEN(zCol) + 1;
|
|
+ pNew->aCol[nCol].zName = pCsr;
|
|
+ pNew->aCol[nCol].iPk = sqlite3_column_int(p1, 5);
|
|
+ memcpy(pCsr, zCol, nCopy);
|
|
+ pCsr += nCopy;
|
|
+
|
|
+ rc = sqlite3_table_column_metadata(
|
|
+ db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
|
|
+ );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ nCopy = STRLEN(zCol) + 1;
|
|
+ pNew->aCol[nCol].zColl = pCsr;
|
|
+ memcpy(pCsr, zCol, nCopy);
|
|
+ pCsr += nCopy;
|
|
+ }
|
|
+
|
|
+ nCol++;
|
|
+ }
|
|
+ idxFinalize(&rc, p1);
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_free(pNew);
|
|
+ pNew = 0;
|
|
+ }else{
|
|
+ pNew->zName = pCsr;
|
|
+ memcpy(pNew->zName, zTab, nTab+1);
|
|
+ }
|
|
+
|
|
+ *ppOut = pNew;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is a no-op if *pRc is set to anything other than
|
|
+** SQLITE_OK when it is called.
|
|
+**
|
|
+** If *pRc is initially set to SQLITE_OK, then the text specified by
|
|
+** the printf() style arguments is appended to zIn and the result returned
|
|
+** in a buffer allocated by sqlite3_malloc(). sqlite3_free() is called on
|
|
+** zIn before returning.
|
|
+*/
|
|
+static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ char *zAppend = 0;
|
|
+ char *zRet = 0;
|
|
+ int nIn = zIn ? STRLEN(zIn) : 0;
|
|
+ int nAppend = 0;
|
|
+ va_start(ap, zFmt);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ zAppend = sqlite3_vmprintf(zFmt, ap);
|
|
+ if( zAppend ){
|
|
+ nAppend = STRLEN(zAppend);
|
|
+ zRet = (char*)sqlite3_malloc(nIn + nAppend + 1);
|
|
+ }
|
|
+ if( zAppend && zRet ){
|
|
+ if( nIn ) memcpy(zRet, zIn, nIn);
|
|
+ memcpy(&zRet[nIn], zAppend, nAppend+1);
|
|
+ }else{
|
|
+ sqlite3_free(zRet);
|
|
+ zRet = 0;
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }
|
|
+ sqlite3_free(zAppend);
|
|
+ sqlite3_free(zIn);
|
|
+ }
|
|
+ va_end(ap);
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true if zId must be quoted in order to use it as an SQL
|
|
+** identifier, or false otherwise.
|
|
+*/
|
|
+static int idxIdentifierRequiresQuotes(const char *zId){
|
|
+ int i;
|
|
+ for(i=0; zId[i]; i++){
|
|
+ if( !(zId[i]=='_')
|
|
+ && !(zId[i]>='0' && zId[i]<='9')
|
|
+ && !(zId[i]>='a' && zId[i]<='z')
|
|
+ && !(zId[i]>='A' && zId[i]<='Z')
|
|
+ ){
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function appends an index column definition suitable for constraint
|
|
+** pCons to the string passed as zIn and returns the result.
|
|
+*/
|
|
+static char *idxAppendColDefn(
|
|
+ int *pRc, /* IN/OUT: Error code */
|
|
+ char *zIn, /* Column defn accumulated so far */
|
|
+ IdxTable *pTab, /* Table index will be created on */
|
|
+ IdxConstraint *pCons
|
|
+){
|
|
+ char *zRet = zIn;
|
|
+ IdxColumn *p = &pTab->aCol[pCons->iCol];
|
|
+ if( zRet ) zRet = idxAppendText(pRc, zRet, ", ");
|
|
+
|
|
+ if( idxIdentifierRequiresQuotes(p->zName) ){
|
|
+ zRet = idxAppendText(pRc, zRet, "%Q", p->zName);
|
|
+ }else{
|
|
+ zRet = idxAppendText(pRc, zRet, "%s", p->zName);
|
|
+ }
|
|
+
|
|
+ if( sqlite3_stricmp(p->zColl, pCons->zColl) ){
|
|
+ if( idxIdentifierRequiresQuotes(pCons->zColl) ){
|
|
+ zRet = idxAppendText(pRc, zRet, " COLLATE %Q", pCons->zColl);
|
|
+ }else{
|
|
+ zRet = idxAppendText(pRc, zRet, " COLLATE %s", pCons->zColl);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( pCons->bDesc ){
|
|
+ zRet = idxAppendText(pRc, zRet, " DESC");
|
|
+ }
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Search database dbm for an index compatible with the one idxCreateFromCons()
|
|
+** would create from arguments pScan, pEq and pTail. If no error occurs and
|
|
+** such an index is found, return non-zero. Or, if no such index is found,
|
|
+** return zero.
|
|
+**
|
|
+** If an error occurs, set *pRc to an SQLite error code and return zero.
|
|
+*/
|
|
+static int idxFindCompatible(
|
|
+ int *pRc, /* OUT: Error code */
|
|
+ sqlite3* dbm, /* Database to search */
|
|
+ IdxScan *pScan, /* Scan for table to search for index on */
|
|
+ IdxConstraint *pEq, /* List of == constraints */
|
|
+ IdxConstraint *pTail /* List of range constraints */
|
|
+){
|
|
+ const char *zTbl = pScan->pTab->zName;
|
|
+ sqlite3_stmt *pIdxList = 0;
|
|
+ IdxConstraint *pIter;
|
|
+ int nEq = 0; /* Number of elements in pEq */
|
|
+ int rc;
|
|
+
|
|
+ /* Count the elements in list pEq */
|
|
+ for(pIter=pEq; pIter; pIter=pIter->pLink) nEq++;
|
|
+
|
|
+ rc = idxPrintfPrepareStmt(dbm, &pIdxList, 0, "PRAGMA index_list=%Q", zTbl);
|
|
+ while( rc==SQLITE_OK && sqlite3_step(pIdxList)==SQLITE_ROW ){
|
|
+ int bMatch = 1;
|
|
+ IdxConstraint *pT = pTail;
|
|
+ sqlite3_stmt *pInfo = 0;
|
|
+ const char *zIdx = (const char*)sqlite3_column_text(pIdxList, 1);
|
|
+
|
|
+ /* Zero the IdxConstraint.bFlag values in the pEq list */
|
|
+ for(pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0;
|
|
+
|
|
+ rc = idxPrintfPrepareStmt(dbm, &pInfo, 0, "PRAGMA index_xInfo=%Q", zIdx);
|
|
+ while( rc==SQLITE_OK && sqlite3_step(pInfo)==SQLITE_ROW ){
|
|
+ int iIdx = sqlite3_column_int(pInfo, 0);
|
|
+ int iCol = sqlite3_column_int(pInfo, 1);
|
|
+ const char *zColl = (const char*)sqlite3_column_text(pInfo, 4);
|
|
+
|
|
+ if( iIdx<nEq ){
|
|
+ for(pIter=pEq; pIter; pIter=pIter->pLink){
|
|
+ if( pIter->bFlag ) continue;
|
|
+ if( pIter->iCol!=iCol ) continue;
|
|
+ if( sqlite3_stricmp(pIter->zColl, zColl) ) continue;
|
|
+ pIter->bFlag = 1;
|
|
+ break;
|
|
+ }
|
|
+ if( pIter==0 ){
|
|
+ bMatch = 0;
|
|
+ break;
|
|
+ }
|
|
+ }else{
|
|
+ if( pT ){
|
|
+ if( pT->iCol!=iCol || sqlite3_stricmp(pT->zColl, zColl) ){
|
|
+ bMatch = 0;
|
|
+ break;
|
|
+ }
|
|
+ pT = pT->pLink;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ idxFinalize(&rc, pInfo);
|
|
+
|
|
+ if( rc==SQLITE_OK && bMatch ){
|
|
+ sqlite3_finalize(pIdxList);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ idxFinalize(&rc, pIdxList);
|
|
+
|
|
+ *pRc = rc;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int idxCreateFromCons(
|
|
+ sqlite3expert *p,
|
|
+ IdxScan *pScan,
|
|
+ IdxConstraint *pEq,
|
|
+ IdxConstraint *pTail
|
|
+){
|
|
+ sqlite3 *dbm = p->dbm;
|
|
+ int rc = SQLITE_OK;
|
|
+ if( (pEq || pTail) && 0==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){
|
|
+ IdxTable *pTab = pScan->pTab;
|
|
+ char *zCols = 0;
|
|
+ char *zIdx = 0;
|
|
+ IdxConstraint *pCons;
|
|
+ unsigned int h = 0;
|
|
+ const char *zFmt;
|
|
+
|
|
+ for(pCons=pEq; pCons; pCons=pCons->pLink){
|
|
+ zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
|
|
+ }
|
|
+ for(pCons=pTail; pCons; pCons=pCons->pLink){
|
|
+ zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ /* Hash the list of columns to come up with a name for the index */
|
|
+ const char *zTable = pScan->pTab->zName;
|
|
+ char *zName; /* Index name */
|
|
+ int i;
|
|
+ for(i=0; zCols[i]; i++){
|
|
+ h += ((h<<3) + zCols[i]);
|
|
+ }
|
|
+ zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
|
|
+ if( zName==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ if( idxIdentifierRequiresQuotes(zTable) ){
|
|
+ zFmt = "CREATE INDEX '%q' ON %Q(%s)";
|
|
+ }else{
|
|
+ zFmt = "CREATE INDEX %s ON %s(%s)";
|
|
+ }
|
|
+ zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols);
|
|
+ if( !zIdx ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg);
|
|
+ idxHashAdd(&rc, &p->hIdx, zName, zIdx);
|
|
+ }
|
|
+ sqlite3_free(zName);
|
|
+ sqlite3_free(zIdx);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zCols);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true if list pList (linked by IdxConstraint.pLink) contains
|
|
+** a constraint compatible with *p. Otherwise return false.
|
|
+*/
|
|
+static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){
|
|
+ IdxConstraint *pCmp;
|
|
+ for(pCmp=pList; pCmp; pCmp=pCmp->pLink){
|
|
+ if( p->iCol==pCmp->iCol ) return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int idxCreateFromWhere(
|
|
+ sqlite3expert *p,
|
|
+ IdxScan *pScan, /* Create indexes for this scan */
|
|
+ IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */
|
|
+){
|
|
+ IdxConstraint *p1 = 0;
|
|
+ IdxConstraint *pCon;
|
|
+ int rc;
|
|
+
|
|
+ /* Gather up all the == constraints. */
|
|
+ for(pCon=pScan->pEq; pCon; pCon=pCon->pNext){
|
|
+ if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
|
|
+ pCon->pLink = p1;
|
|
+ p1 = pCon;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Create an index using the == constraints collected above. And the
|
|
+ ** range constraint/ORDER BY terms passed in by the caller, if any. */
|
|
+ rc = idxCreateFromCons(p, pScan, p1, pTail);
|
|
+
|
|
+ /* If no range/ORDER BY passed by the caller, create a version of the
|
|
+ ** index for each range constraint. */
|
|
+ if( pTail==0 ){
|
|
+ for(pCon=pScan->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){
|
|
+ assert( pCon->pLink==0 );
|
|
+ if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
|
|
+ rc = idxCreateFromCons(p, pScan, p1, pCon);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create candidate indexes in database [dbm] based on the data in
|
|
+** linked-list pScan.
|
|
+*/
|
|
+static int idxCreateCandidates(sqlite3expert *p){
|
|
+ int rc = SQLITE_OK;
|
|
+ IdxScan *pIter;
|
|
+
|
|
+ for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
|
|
+ rc = idxCreateFromWhere(p, pIter, 0);
|
|
+ if( rc==SQLITE_OK && pIter->pOrder ){
|
|
+ rc = idxCreateFromWhere(p, pIter, pIter->pOrder);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free all elements of the linked list starting at pConstraint.
|
|
+*/
|
|
+static void idxConstraintFree(IdxConstraint *pConstraint){
|
|
+ IdxConstraint *pNext;
|
|
+ IdxConstraint *p;
|
|
+
|
|
+ for(p=pConstraint; p; p=pNext){
|
|
+ pNext = p->pNext;
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free all elements of the linked list starting from pScan up until pLast
|
|
+** (pLast is not freed).
|
|
+*/
|
|
+static void idxScanFree(IdxScan *pScan, IdxScan *pLast){
|
|
+ IdxScan *p;
|
|
+ IdxScan *pNext;
|
|
+ for(p=pScan; p!=pLast; p=pNext){
|
|
+ pNext = p->pNextScan;
|
|
+ idxConstraintFree(p->pOrder);
|
|
+ idxConstraintFree(p->pEq);
|
|
+ idxConstraintFree(p->pRange);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free all elements of the linked list starting from pStatement up
|
|
+** until pLast (pLast is not freed).
|
|
+*/
|
|
+static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){
|
|
+ IdxStatement *p;
|
|
+ IdxStatement *pNext;
|
|
+ for(p=pStatement; p!=pLast; p=pNext){
|
|
+ pNext = p->pNext;
|
|
+ sqlite3_free(p->zEQP);
|
|
+ sqlite3_free(p->zIdx);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the linked list of IdxTable objects starting at pTab.
|
|
+*/
|
|
+static void idxTableFree(IdxTable *pTab){
|
|
+ IdxTable *pIter;
|
|
+ IdxTable *pNext;
|
|
+ for(pIter=pTab; pIter; pIter=pNext){
|
|
+ pNext = pIter->pNext;
|
|
+ sqlite3_free(pIter);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the linked list of IdxWrite objects starting at pTab.
|
|
+*/
|
|
+static void idxWriteFree(IdxWrite *pTab){
|
|
+ IdxWrite *pIter;
|
|
+ IdxWrite *pNext;
|
|
+ for(pIter=pTab; pIter; pIter=pNext){
|
|
+ pNext = pIter->pNext;
|
|
+ sqlite3_free(pIter);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** This function is called after candidate indexes have been created. It
|
|
+** runs all the queries to see which indexes they prefer, and populates
|
|
+** IdxStatement.zIdx and IdxStatement.zEQP with the results.
|
|
+*/
|
|
+int idxFindIndexes(
|
|
+ sqlite3expert *p,
|
|
+ char **pzErr /* OUT: Error message (sqlite3_malloc) */
|
|
+){
|
|
+ IdxStatement *pStmt;
|
|
+ sqlite3 *dbm = p->dbm;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ IdxHash hIdx;
|
|
+ idxHashInit(&hIdx);
|
|
+
|
|
+ for(pStmt=p->pStatement; rc==SQLITE_OK && pStmt; pStmt=pStmt->pNext){
|
|
+ IdxHashEntry *pEntry;
|
|
+ sqlite3_stmt *pExplain = 0;
|
|
+ idxHashClear(&hIdx);
|
|
+ rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr,
|
|
+ "EXPLAIN QUERY PLAN %s", pStmt->zSql
|
|
+ );
|
|
+ while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){
|
|
+ /* int iId = sqlite3_column_int(pExplain, 0); */
|
|
+ /* int iParent = sqlite3_column_int(pExplain, 1); */
|
|
+ /* int iNotUsed = sqlite3_column_int(pExplain, 2); */
|
|
+ const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
|
|
+ int nDetail = STRLEN(zDetail);
|
|
+ int i;
|
|
+
|
|
+ for(i=0; i<nDetail; i++){
|
|
+ const char *zIdx = 0;
|
|
+ if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
|
|
+ zIdx = &zDetail[i+13];
|
|
+ }else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){
|
|
+ zIdx = &zDetail[i+22];
|
|
+ }
|
|
+ if( zIdx ){
|
|
+ const char *zSql;
|
|
+ int nIdx = 0;
|
|
+ while( zIdx[nIdx]!='\0' && (zIdx[nIdx]!=' ' || zIdx[nIdx+1]!='(') ){
|
|
+ nIdx++;
|
|
+ }
|
|
+ zSql = idxHashSearch(&p->hIdx, zIdx, nIdx);
|
|
+ if( zSql ){
|
|
+ idxHashAdd(&rc, &hIdx, zSql, 0);
|
|
+ if( rc ) goto find_indexes_out;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( zDetail[0]!='-' ){
|
|
+ pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n", zDetail);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for(pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
|
|
+ pStmt->zIdx = idxAppendText(&rc, pStmt->zIdx, "%s;\n", pEntry->zKey);
|
|
+ }
|
|
+
|
|
+ idxFinalize(&rc, pExplain);
|
|
+ }
|
|
+
|
|
+ find_indexes_out:
|
|
+ idxHashClear(&hIdx);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int idxAuthCallback(
|
|
+ void *pCtx,
|
|
+ int eOp,
|
|
+ const char *z3,
|
|
+ const char *z4,
|
|
+ const char *zDb,
|
|
+ const char *zTrigger
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ (void)z4;
|
|
+ (void)zTrigger;
|
|
+ if( eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE || eOp==SQLITE_DELETE ){
|
|
+ if( sqlite3_stricmp(zDb, "main")==0 ){
|
|
+ sqlite3expert *p = (sqlite3expert*)pCtx;
|
|
+ IdxTable *pTab;
|
|
+ for(pTab=p->pTable; pTab; pTab=pTab->pNext){
|
|
+ if( 0==sqlite3_stricmp(z3, pTab->zName) ) break;
|
|
+ }
|
|
+ if( pTab ){
|
|
+ IdxWrite *pWrite;
|
|
+ for(pWrite=p->pWrite; pWrite; pWrite=pWrite->pNext){
|
|
+ if( pWrite->pTab==pTab && pWrite->eOp==eOp ) break;
|
|
+ }
|
|
+ if( pWrite==0 ){
|
|
+ pWrite = idxMalloc(&rc, sizeof(IdxWrite));
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pWrite->pTab = pTab;
|
|
+ pWrite->eOp = eOp;
|
|
+ pWrite->pNext = p->pWrite;
|
|
+ p->pWrite = pWrite;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int idxProcessOneTrigger(
|
|
+ sqlite3expert *p,
|
|
+ IdxWrite *pWrite,
|
|
+ char **pzErr
|
|
+){
|
|
+ static const char *zInt = UNIQUE_TABLE_NAME;
|
|
+ static const char *zDrop = "DROP TABLE " UNIQUE_TABLE_NAME;
|
|
+ IdxTable *pTab = pWrite->pTab;
|
|
+ const char *zTab = pTab->zName;
|
|
+ const char *zSql =
|
|
+ "SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_master "
|
|
+ "WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
|
|
+ "ORDER BY type;";
|
|
+ sqlite3_stmt *pSelect = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zWrite = 0;
|
|
+
|
|
+ /* Create the table and its triggers in the temp schema */
|
|
+ rc = idxPrintfPrepareStmt(p->db, &pSelect, pzErr, zSql, zTab, zTab);
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSelect) ){
|
|
+ const char *zCreate = (const char*)sqlite3_column_text(pSelect, 0);
|
|
+ rc = sqlite3_exec(p->dbv, zCreate, 0, 0, pzErr);
|
|
+ }
|
|
+ idxFinalize(&rc, pSelect);
|
|
+
|
|
+ /* Rename the table in the temp schema to zInt */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ char *z = sqlite3_mprintf("ALTER TABLE temp.%Q RENAME TO %Q", zTab, zInt);
|
|
+ if( z==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_exec(p->dbv, z, 0, 0, pzErr);
|
|
+ sqlite3_free(z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ switch( pWrite->eOp ){
|
|
+ case SQLITE_INSERT: {
|
|
+ int i;
|
|
+ zWrite = idxAppendText(&rc, zWrite, "INSERT INTO %Q VALUES(", zInt);
|
|
+ for(i=0; i<pTab->nCol; i++){
|
|
+ zWrite = idxAppendText(&rc, zWrite, "%s?", i==0 ? "" : ", ");
|
|
+ }
|
|
+ zWrite = idxAppendText(&rc, zWrite, ")");
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_UPDATE: {
|
|
+ int i;
|
|
+ zWrite = idxAppendText(&rc, zWrite, "UPDATE %Q SET ", zInt);
|
|
+ for(i=0; i<pTab->nCol; i++){
|
|
+ zWrite = idxAppendText(&rc, zWrite, "%s%Q=?", i==0 ? "" : ", ",
|
|
+ pTab->aCol[i].zName
|
|
+ );
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ assert( pWrite->eOp==SQLITE_DELETE );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ zWrite = sqlite3_mprintf("DELETE FROM %Q", zInt);
|
|
+ if( zWrite==0 ) rc = SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_stmt *pX = 0;
|
|
+ rc = sqlite3_prepare_v2(p->dbv, zWrite, -1, &pX, 0);
|
|
+ idxFinalize(&rc, pX);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ idxDatabaseError(p->dbv, pzErr);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zWrite);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_exec(p->dbv, zDrop, 0, 0, pzErr);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int idxProcessTriggers(sqlite3expert *p, char **pzErr){
|
|
+ int rc = SQLITE_OK;
|
|
+ IdxWrite *pEnd = 0;
|
|
+ IdxWrite *pFirst = p->pWrite;
|
|
+
|
|
+ while( rc==SQLITE_OK && pFirst!=pEnd ){
|
|
+ IdxWrite *pIter;
|
|
+ for(pIter=pFirst; rc==SQLITE_OK && pIter!=pEnd; pIter=pIter->pNext){
|
|
+ rc = idxProcessOneTrigger(p, pIter, pzErr);
|
|
+ }
|
|
+ pEnd = pFirst;
|
|
+ pFirst = p->pWrite;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
|
|
+ int rc = idxRegisterVtab(p);
|
|
+ sqlite3_stmt *pSchema = 0;
|
|
+
|
|
+ /* For each table in the main db schema:
|
|
+ **
|
|
+ ** 1) Add an entry to the p->pTable list, and
|
|
+ ** 2) Create the equivalent virtual table in dbv.
|
|
+ */
|
|
+ rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
|
|
+ "SELECT type, name, sql, 1 FROM sqlite_master "
|
|
+ "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
|
|
+ " UNION ALL "
|
|
+ "SELECT type, name, sql, 2 FROM sqlite_master "
|
|
+ "WHERE type = 'trigger'"
|
|
+ " AND tbl_name IN(SELECT name FROM sqlite_master WHERE type = 'view') "
|
|
+ "ORDER BY 4, 1"
|
|
+ );
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
|
|
+ const char *zType = (const char*)sqlite3_column_text(pSchema, 0);
|
|
+ const char *zName = (const char*)sqlite3_column_text(pSchema, 1);
|
|
+ const char *zSql = (const char*)sqlite3_column_text(pSchema, 2);
|
|
+
|
|
+ if( zType[0]=='v' || zType[1]=='r' ){
|
|
+ rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg);
|
|
+ }else{
|
|
+ IdxTable *pTab;
|
|
+ rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int i;
|
|
+ char *zInner = 0;
|
|
+ char *zOuter = 0;
|
|
+ pTab->pNext = p->pTable;
|
|
+ p->pTable = pTab;
|
|
+
|
|
+ /* The statement the vtab will pass to sqlite3_declare_vtab() */
|
|
+ zInner = idxAppendText(&rc, 0, "CREATE TABLE x(");
|
|
+ for(i=0; i<pTab->nCol; i++){
|
|
+ zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s",
|
|
+ (i==0 ? "" : ", "), pTab->aCol[i].zName, pTab->aCol[i].zColl
|
|
+ );
|
|
+ }
|
|
+ zInner = idxAppendText(&rc, zInner, ")");
|
|
+
|
|
+ /* The CVT statement to create the vtab */
|
|
+ zOuter = idxAppendText(&rc, 0,
|
|
+ "CREATE VIRTUAL TABLE %Q USING expert(%Q)", zName, zInner
|
|
+ );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_exec(p->dbv, zOuter, 0, 0, pzErrmsg);
|
|
+ }
|
|
+ sqlite3_free(zInner);
|
|
+ sqlite3_free(zOuter);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ idxFinalize(&rc, pSchema);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+struct IdxSampleCtx {
|
|
+ int iTarget;
|
|
+ double target; /* Target nRet/nRow value */
|
|
+ double nRow; /* Number of rows seen */
|
|
+ double nRet; /* Number of rows returned */
|
|
+};
|
|
+
|
|
+static void idxSampleFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ struct IdxSampleCtx *p = (struct IdxSampleCtx*)sqlite3_user_data(pCtx);
|
|
+ int bRet;
|
|
+
|
|
+ (void)argv;
|
|
+ assert( argc==0 );
|
|
+ if( p->nRow==0.0 ){
|
|
+ bRet = 1;
|
|
+ }else{
|
|
+ bRet = (p->nRet / p->nRow) <= p->target;
|
|
+ if( bRet==0 ){
|
|
+ unsigned short rnd;
|
|
+ sqlite3_randomness(2, (void*)&rnd);
|
|
+ bRet = ((int)rnd % 100) <= p->iTarget;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_result_int(pCtx, bRet);
|
|
+ p->nRow += 1.0;
|
|
+ p->nRet += (double)bRet;
|
|
+}
|
|
+
|
|
+struct IdxRemCtx {
|
|
+ int nSlot;
|
|
+ struct IdxRemSlot {
|
|
+ int eType; /* SQLITE_NULL, INTEGER, REAL, TEXT, BLOB */
|
|
+ i64 iVal; /* SQLITE_INTEGER value */
|
|
+ double rVal; /* SQLITE_FLOAT value */
|
|
+ int nByte; /* Bytes of space allocated at z */
|
|
+ int n; /* Size of buffer z */
|
|
+ char *z; /* SQLITE_TEXT/BLOB value */
|
|
+ } aSlot[1];
|
|
+};
|
|
+
|
|
+/*
|
|
+** Implementation of scalar function rem().
|
|
+*/
|
|
+static void idxRemFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ struct IdxRemCtx *p = (struct IdxRemCtx*)sqlite3_user_data(pCtx);
|
|
+ struct IdxRemSlot *pSlot;
|
|
+ int iSlot;
|
|
+ assert( argc==2 );
|
|
+
|
|
+ iSlot = sqlite3_value_int(argv[0]);
|
|
+ assert( iSlot<=p->nSlot );
|
|
+ pSlot = &p->aSlot[iSlot];
|
|
+
|
|
+ switch( pSlot->eType ){
|
|
+ case SQLITE_NULL:
|
|
+ /* no-op */
|
|
+ break;
|
|
+
|
|
+ case SQLITE_INTEGER:
|
|
+ sqlite3_result_int64(pCtx, pSlot->iVal);
|
|
+ break;
|
|
+
|
|
+ case SQLITE_FLOAT:
|
|
+ sqlite3_result_double(pCtx, pSlot->rVal);
|
|
+ break;
|
|
+
|
|
+ case SQLITE_BLOB:
|
|
+ sqlite3_result_blob(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+
|
|
+ case SQLITE_TEXT:
|
|
+ sqlite3_result_text(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ pSlot->eType = sqlite3_value_type(argv[1]);
|
|
+ switch( pSlot->eType ){
|
|
+ case SQLITE_NULL:
|
|
+ /* no-op */
|
|
+ break;
|
|
+
|
|
+ case SQLITE_INTEGER:
|
|
+ pSlot->iVal = sqlite3_value_int64(argv[1]);
|
|
+ break;
|
|
+
|
|
+ case SQLITE_FLOAT:
|
|
+ pSlot->rVal = sqlite3_value_double(argv[1]);
|
|
+ break;
|
|
+
|
|
+ case SQLITE_BLOB:
|
|
+ case SQLITE_TEXT: {
|
|
+ int nByte = sqlite3_value_bytes(argv[1]);
|
|
+ if( nByte>pSlot->nByte ){
|
|
+ char *zNew = (char*)sqlite3_realloc(pSlot->z, nByte*2);
|
|
+ if( zNew==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ return;
|
|
+ }
|
|
+ pSlot->nByte = nByte*2;
|
|
+ pSlot->z = zNew;
|
|
+ }
|
|
+ pSlot->n = nByte;
|
|
+ if( pSlot->eType==SQLITE_BLOB ){
|
|
+ memcpy(pSlot->z, sqlite3_value_blob(argv[1]), nByte);
|
|
+ }else{
|
|
+ memcpy(pSlot->z, sqlite3_value_text(argv[1]), nByte);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
|
|
+ int rc = SQLITE_OK;
|
|
+ const char *zMax =
|
|
+ "SELECT max(i.seqno) FROM "
|
|
+ " sqlite_master AS s, "
|
|
+ " pragma_index_list(s.name) AS l, "
|
|
+ " pragma_index_info(l.name) AS i "
|
|
+ "WHERE s.type = 'table'";
|
|
+ sqlite3_stmt *pMax = 0;
|
|
+
|
|
+ *pnMax = 0;
|
|
+ rc = idxPrepareStmt(db, &pMax, pzErr, zMax);
|
|
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
|
|
+ *pnMax = sqlite3_column_int(pMax, 0) + 1;
|
|
+ }
|
|
+ idxFinalize(&rc, pMax);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int idxPopulateOneStat1(
|
|
+ sqlite3expert *p,
|
|
+ sqlite3_stmt *pIndexXInfo,
|
|
+ sqlite3_stmt *pWriteStat,
|
|
+ const char *zTab,
|
|
+ const char *zIdx,
|
|
+ char **pzErr
|
|
+){
|
|
+ char *zCols = 0;
|
|
+ char *zOrder = 0;
|
|
+ char *zQuery = 0;
|
|
+ int nCol = 0;
|
|
+ int i;
|
|
+ sqlite3_stmt *pQuery = 0;
|
|
+ int *aStat = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ assert( p->iSample>0 );
|
|
+
|
|
+ /* Formulate the query text */
|
|
+ sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
|
|
+ while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
|
|
+ const char *zComma = zCols==0 ? "" : ", ";
|
|
+ const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
|
|
+ const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
|
|
+ zCols = idxAppendText(&rc, zCols,
|
|
+ "%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl
|
|
+ );
|
|
+ zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
|
|
+ }
|
|
+ sqlite3_reset(pIndexXInfo);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( p->iSample==100 ){
|
|
+ zQuery = sqlite3_mprintf(
|
|
+ "SELECT %s FROM %Q x ORDER BY %s", zCols, zTab, zOrder
|
|
+ );
|
|
+ }else{
|
|
+ zQuery = sqlite3_mprintf(
|
|
+ "SELECT %s FROM temp."UNIQUE_TABLE_NAME" x ORDER BY %s", zCols, zOrder
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zCols);
|
|
+ sqlite3_free(zOrder);
|
|
+
|
|
+ /* Formulate the query text */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
|
|
+ rc = idxPrepareStmt(dbrem, &pQuery, pzErr, zQuery);
|
|
+ }
|
|
+ sqlite3_free(zQuery);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ aStat = (int*)idxMalloc(&rc, sizeof(int)*(nCol+1));
|
|
+ }
|
|
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
|
|
+ IdxHashEntry *pEntry;
|
|
+ char *zStat = 0;
|
|
+ for(i=0; i<=nCol; i++) aStat[i] = 1;
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
|
|
+ aStat[0]++;
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( sqlite3_column_int(pQuery, i)==0 ) break;
|
|
+ }
|
|
+ for(/*no-op*/; i<nCol; i++){
|
|
+ aStat[i+1]++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int s0 = aStat[0];
|
|
+ zStat = sqlite3_mprintf("%d", s0);
|
|
+ if( zStat==0 ) rc = SQLITE_NOMEM;
|
|
+ for(i=1; rc==SQLITE_OK && i<=nCol; i++){
|
|
+ zStat = idxAppendText(&rc, zStat, " %d", (s0+aStat[i]/2) / aStat[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_bind_text(pWriteStat, 1, zTab, -1, SQLITE_STATIC);
|
|
+ sqlite3_bind_text(pWriteStat, 2, zIdx, -1, SQLITE_STATIC);
|
|
+ sqlite3_bind_text(pWriteStat, 3, zStat, -1, SQLITE_STATIC);
|
|
+ sqlite3_step(pWriteStat);
|
|
+ rc = sqlite3_reset(pWriteStat);
|
|
+ }
|
|
+
|
|
+ pEntry = idxHashFind(&p->hIdx, zIdx, STRLEN(zIdx));
|
|
+ if( pEntry ){
|
|
+ assert( pEntry->zVal2==0 );
|
|
+ pEntry->zVal2 = zStat;
|
|
+ }else{
|
|
+ sqlite3_free(zStat);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(aStat);
|
|
+ idxFinalize(&rc, pQuery);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int idxBuildSampleTable(sqlite3expert *p, const char *zTab){
|
|
+ int rc;
|
|
+ char *zSql;
|
|
+
|
|
+ rc = sqlite3_exec(p->dbv,"DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "CREATE TABLE temp." UNIQUE_TABLE_NAME " AS SELECT * FROM %Q", zTab
|
|
+ );
|
|
+ if( zSql==0 ) return SQLITE_NOMEM;
|
|
+ rc = sqlite3_exec(p->dbv, zSql, 0, 0, 0);
|
|
+ sqlite3_free(zSql);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called as part of sqlite3_expert_analyze(). Candidate
|
|
+** indexes have already been created in database sqlite3expert.dbm, this
|
|
+** function populates sqlite_stat1 table in the same database.
|
|
+**
|
|
+** The stat1 data is generated by querying the
|
|
+*/
|
|
+static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
|
+ int rc = SQLITE_OK;
|
|
+ int nMax =0;
|
|
+ struct IdxRemCtx *pCtx = 0;
|
|
+ struct IdxSampleCtx samplectx;
|
|
+ int i;
|
|
+ i64 iPrev = -100000;
|
|
+ sqlite3_stmt *pAllIndex = 0;
|
|
+ sqlite3_stmt *pIndexXInfo = 0;
|
|
+ sqlite3_stmt *pWrite = 0;
|
|
+
|
|
+ const char *zAllIndex =
|
|
+ "SELECT s.rowid, s.name, l.name FROM "
|
|
+ " sqlite_master AS s, "
|
|
+ " pragma_index_list(s.name) AS l "
|
|
+ "WHERE s.type = 'table'";
|
|
+ const char *zIndexXInfo =
|
|
+ "SELECT name, coll FROM pragma_index_xinfo(?) WHERE key";
|
|
+ const char *zWrite = "INSERT INTO sqlite_stat1 VALUES(?, ?, ?)";
|
|
+
|
|
+ /* If iSample==0, no sqlite_stat1 data is required. */
|
|
+ if( p->iSample==0 ) return SQLITE_OK;
|
|
+
|
|
+ rc = idxLargestIndex(p->dbm, &nMax, pzErr);
|
|
+ if( nMax<=0 || rc!=SQLITE_OK ) return rc;
|
|
+
|
|
+ rc = sqlite3_exec(p->dbm, "ANALYZE; PRAGMA writable_schema=1", 0, 0, 0);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
|
|
+ pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
|
|
+ rc = sqlite3_create_function(
|
|
+ dbrem, "rem", 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
|
|
+ );
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(
|
|
+ p->db, "sample", 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pCtx->nSlot = nMax+1;
|
|
+ rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxPrepareStmt(p->dbm, &pIndexXInfo, pzErr, zIndexXInfo);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxPrepareStmt(p->dbm, &pWrite, pzErr, zWrite);
|
|
+ }
|
|
+
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pAllIndex) ){
|
|
+ i64 iRowid = sqlite3_column_int64(pAllIndex, 0);
|
|
+ const char *zTab = (const char*)sqlite3_column_text(pAllIndex, 1);
|
|
+ const char *zIdx = (const char*)sqlite3_column_text(pAllIndex, 2);
|
|
+ if( p->iSample<100 && iPrev!=iRowid ){
|
|
+ samplectx.target = (double)p->iSample / 100.0;
|
|
+ samplectx.iTarget = p->iSample;
|
|
+ samplectx.nRow = 0.0;
|
|
+ samplectx.nRet = 0.0;
|
|
+ rc = idxBuildSampleTable(p, zTab);
|
|
+ if( rc!=SQLITE_OK ) break;
|
|
+ }
|
|
+ rc = idxPopulateOneStat1(p, pIndexXInfo, pWrite, zTab, zIdx, pzErr);
|
|
+ iPrev = iRowid;
|
|
+ }
|
|
+ if( rc==SQLITE_OK && p->iSample<100 ){
|
|
+ rc = sqlite3_exec(p->dbv,
|
|
+ "DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME, 0,0,0
|
|
+ );
|
|
+ }
|
|
+
|
|
+ idxFinalize(&rc, pAllIndex);
|
|
+ idxFinalize(&rc, pIndexXInfo);
|
|
+ idxFinalize(&rc, pWrite);
|
|
+
|
|
+ for(i=0; i<pCtx->nSlot; i++){
|
|
+ sqlite3_free(pCtx->aSlot[i].z);
|
|
+ }
|
|
+ sqlite3_free(pCtx);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_master", 0, 0, 0);
|
|
+ }
|
|
+
|
|
+ sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate a new sqlite3expert object.
|
|
+*/
|
|
+sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3expert *pNew;
|
|
+
|
|
+ pNew = (sqlite3expert*)idxMalloc(&rc, sizeof(sqlite3expert));
|
|
+
|
|
+ /* Open two in-memory databases to work with. The "vtab database" (dbv)
|
|
+ ** will contain a virtual table corresponding to each real table in
|
|
+ ** the user database schema, and a copy of each view. It is used to
|
|
+ ** collect information regarding the WHERE, ORDER BY and other clauses
|
|
+ ** of the user's query.
|
|
+ */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew->db = db;
|
|
+ pNew->iSample = 100;
|
|
+ rc = sqlite3_open(":memory:", &pNew->dbv);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_open(":memory:", &pNew->dbm);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Copy the entire schema of database [db] into [dbm]. */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_stmt *pSql;
|
|
+ rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
|
|
+ "SELECT sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%%'"
|
|
+ " AND sql NOT LIKE 'CREATE VIRTUAL %%'"
|
|
+ );
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
|
+ const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
|
|
+ rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
|
|
+ }
|
|
+ idxFinalize(&rc, pSql);
|
|
+ }
|
|
+
|
|
+ /* Create the vtab schema */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxCreateVtabSchema(pNew, pzErrmsg);
|
|
+ }
|
|
+
|
|
+ /* Register the auth callback with dbv */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
|
|
+ }
|
|
+
|
|
+ /* If an error has occurred, free the new object and reutrn NULL. Otherwise,
|
|
+ ** return the new sqlite3expert handle. */
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_expert_destroy(pNew);
|
|
+ pNew = 0;
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Configure an sqlite3expert object.
|
|
+*/
|
|
+int sqlite3_expert_config(sqlite3expert *p, int op, ...){
|
|
+ int rc = SQLITE_OK;
|
|
+ va_list ap;
|
|
+ va_start(ap, op);
|
|
+ switch( op ){
|
|
+ case EXPERT_CONFIG_SAMPLE: {
|
|
+ int iVal = va_arg(ap, int);
|
|
+ if( iVal<0 ) iVal = 0;
|
|
+ if( iVal>100 ) iVal = 100;
|
|
+ p->iSample = iVal;
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ rc = SQLITE_NOTFOUND;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ va_end(ap);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add an SQL statement to the analysis.
|
|
+*/
|
|
+int sqlite3_expert_sql(
|
|
+ sqlite3expert *p, /* From sqlite3_expert_new() */
|
|
+ const char *zSql, /* SQL statement to add */
|
|
+ char **pzErr /* OUT: Error message (if any) */
|
|
+){
|
|
+ IdxScan *pScanOrig = p->pScan;
|
|
+ IdxStatement *pStmtOrig = p->pStatement;
|
|
+ int rc = SQLITE_OK;
|
|
+ const char *zStmt = zSql;
|
|
+
|
|
+ if( p->bRun ) return SQLITE_MISUSE;
|
|
+
|
|
+ while( rc==SQLITE_OK && zStmt && zStmt[0] ){
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pStmt ){
|
|
+ IdxStatement *pNew;
|
|
+ const char *z = sqlite3_sql(pStmt);
|
|
+ int n = STRLEN(z);
|
|
+ pNew = (IdxStatement*)idxMalloc(&rc, sizeof(IdxStatement) + n+1);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew->zSql = (char*)&pNew[1];
|
|
+ memcpy(pNew->zSql, z, n+1);
|
|
+ pNew->pNext = p->pStatement;
|
|
+ if( p->pStatement ) pNew->iId = p->pStatement->iId+1;
|
|
+ p->pStatement = pNew;
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ }
|
|
+ }else{
|
|
+ idxDatabaseError(p->dbv, pzErr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ idxScanFree(p->pScan, pScanOrig);
|
|
+ idxStatementFree(p->pStatement, pStmtOrig);
|
|
+ p->pScan = pScanOrig;
|
|
+ p->pStatement = pStmtOrig;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){
|
|
+ int rc;
|
|
+ IdxHashEntry *pEntry;
|
|
+
|
|
+ /* Do trigger processing to collect any extra IdxScan structures */
|
|
+ rc = idxProcessTriggers(p, pzErr);
|
|
+
|
|
+ /* Create candidate indexes within the in-memory database file */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxCreateCandidates(p);
|
|
+ }
|
|
+
|
|
+ /* Generate the stat1 data */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxPopulateStat1(p, pzErr);
|
|
+ }
|
|
+
|
|
+ /* Formulate the EXPERT_REPORT_CANDIDATES text */
|
|
+ for(pEntry=p->hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
|
|
+ p->zCandidates = idxAppendText(&rc, p->zCandidates,
|
|
+ "%s;%s%s\n", pEntry->zVal,
|
|
+ pEntry->zVal2 ? " -- stat1: " : "", pEntry->zVal2
|
|
+ );
|
|
+ }
|
|
+
|
|
+ /* Figure out which of the candidate indexes are preferred by the query
|
|
+ ** planner and report the results to the user. */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = idxFindIndexes(p, pzErr);
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ p->bRun = 1;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the total number of statements that have been added to this
|
|
+** sqlite3expert using sqlite3_expert_sql().
|
|
+*/
|
|
+int sqlite3_expert_count(sqlite3expert *p){
|
|
+ int nRet = 0;
|
|
+ if( p->pStatement ) nRet = p->pStatement->iId+1;
|
|
+ return nRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a component of the report.
|
|
+*/
|
|
+const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){
|
|
+ const char *zRet = 0;
|
|
+ IdxStatement *pStmt;
|
|
+
|
|
+ if( p->bRun==0 ) return 0;
|
|
+ for(pStmt=p->pStatement; pStmt && pStmt->iId!=iStmt; pStmt=pStmt->pNext);
|
|
+ switch( eReport ){
|
|
+ case EXPERT_REPORT_SQL:
|
|
+ if( pStmt ) zRet = pStmt->zSql;
|
|
+ break;
|
|
+ case EXPERT_REPORT_INDEXES:
|
|
+ if( pStmt ) zRet = pStmt->zIdx;
|
|
+ break;
|
|
+ case EXPERT_REPORT_PLAN:
|
|
+ if( pStmt ) zRet = pStmt->zEQP;
|
|
+ break;
|
|
+ case EXPERT_REPORT_CANDIDATES:
|
|
+ zRet = p->zCandidates;
|
|
+ break;
|
|
+ }
|
|
+ return zRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free an sqlite3expert object.
|
|
+*/
|
|
+void sqlite3_expert_destroy(sqlite3expert *p){
|
|
+ if( p ){
|
|
+ sqlite3_close(p->dbm);
|
|
+ sqlite3_close(p->dbv);
|
|
+ idxScanFree(p->pScan, 0);
|
|
+ idxStatementFree(p->pStatement, 0);
|
|
+ idxTableFree(p->pTable);
|
|
+ idxWriteFree(p->pWrite);
|
|
+ idxHashClear(&p->hIdx);
|
|
+ sqlite3_free(p->zCandidates);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */
|
|
+
|
|
+/************************* End ../ext/expert/sqlite3expert.c ********************/
|
|
+
|
|
#if defined(SQLITE_ENABLE_SESSION)
|
|
/*
|
|
** State information for a single open session
|
|
@@ -2182,6 +8517,29 @@
|
|
int colWidth[100]; /* Column widths prior to ".explain on" */
|
|
};
|
|
|
|
+typedef struct ExpertInfo ExpertInfo;
|
|
+struct ExpertInfo {
|
|
+ sqlite3expert *pExpert;
|
|
+ int bVerbose;
|
|
+};
|
|
+
|
|
+/* A single line in the EQP output */
|
|
+typedef struct EQPGraphRow EQPGraphRow;
|
|
+struct EQPGraphRow {
|
|
+ int iEqpId; /* ID for this row */
|
|
+ int iParentId; /* ID of the parent row */
|
|
+ EQPGraphRow *pNext; /* Next row in sequence */
|
|
+ char zText[1]; /* Text to display for this row */
|
|
+};
|
|
+
|
|
+/* All EQP output is collected into an instance of the following */
|
|
+typedef struct EQPGraph EQPGraph;
|
|
+struct EQPGraph {
|
|
+ EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */
|
|
+ EQPGraphRow *pLast; /* Last element of the pRow list */
|
|
+ char zPrefix[100]; /* Graph prefix */
|
|
+};
|
|
+
|
|
/*
|
|
** State information about the database connection is contained in an
|
|
** instance of the following structure.
|
|
@@ -2189,10 +8547,15 @@
|
|
typedef struct ShellState ShellState;
|
|
struct ShellState {
|
|
sqlite3 *db; /* The database */
|
|
- int autoExplain; /* Automatically turn on .explain mode */
|
|
- int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
|
|
- int statsOn; /* True to display memory stats before each finalize */
|
|
- int scanstatsOn; /* True to display scan stats before each finalize */
|
|
+ u8 autoExplain; /* Automatically turn on .explain mode */
|
|
+ u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
|
|
+ u8 autoEQPtest; /* autoEQP is in test mode */
|
|
+ u8 statsOn; /* True to display memory stats before each finalize */
|
|
+ u8 scanstatsOn; /* True to display scan stats before each finalize */
|
|
+ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
|
|
+ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
|
|
+ u8 nEqpLevel; /* Depth of the EQP output graph */
|
|
+ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
|
|
int outCount; /* Revert to stdout when reaching zero */
|
|
int cnt; /* Number of records displayed so far */
|
|
FILE *out; /* Write results here */
|
|
@@ -2199,6 +8562,7 @@
|
|
FILE *traceOut; /* Output for sqlite3_trace() */
|
|
int nErr; /* Number of errors seen */
|
|
int mode; /* An output mode setting */
|
|
+ int modePrior; /* Saved mode */
|
|
int cMode; /* temporary output mode for the current query */
|
|
int normalMode; /* Output mode before ".explain on" */
|
|
int writableSchema; /* True if PRAGMA writable_schema=ON */
|
|
@@ -2206,9 +8570,12 @@
|
|
int nCheck; /* Number of ".check" commands run */
|
|
unsigned shellFlgs; /* Various flags */
|
|
char *zDestTable; /* Name of destination table when MODE_Insert */
|
|
+ char *zTempFile; /* Temporary file that might need deleting */
|
|
char zTestcase[30]; /* Name of current test case */
|
|
char colSeparator[20]; /* Column separator character for several modes */
|
|
char rowSeparator[20]; /* Row separator character for MODE_Ascii */
|
|
+ char colSepPrior[20]; /* Saved column separator */
|
|
+ char rowSepPrior[20]; /* Saved row separator */
|
|
int colWidth[100]; /* Requested width of each column when in column mode*/
|
|
int actualWidth[100]; /* Actual width of each column */
|
|
char nullValue[20]; /* The text to print when a NULL comes back from
|
|
@@ -2222,23 +8589,41 @@
|
|
int *aiIndent; /* Array of indents used in MODE_Explain */
|
|
int nIndent; /* Size of array aiIndent[] */
|
|
int iIndent; /* Index of current op in aiIndent[] */
|
|
+ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
|
|
#if defined(SQLITE_ENABLE_SESSION)
|
|
int nSession; /* Number of active sessions */
|
|
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
|
|
#endif
|
|
+ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
|
|
};
|
|
|
|
+
|
|
+/* Allowed values for ShellState.autoEQP
|
|
+*/
|
|
+#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */
|
|
+#define AUTOEQP_on 1 /* Automatic EQP is on */
|
|
+#define AUTOEQP_trigger 2 /* On and also show plans for triggers */
|
|
+#define AUTOEQP_full 3 /* Show full EXPLAIN */
|
|
+
|
|
+/* Allowed values for ShellState.openMode
|
|
+*/
|
|
+#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
|
|
+#define SHELL_OPEN_NORMAL 1 /* Normal database file */
|
|
+#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
|
|
+#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
|
|
+#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
|
|
+#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
|
|
+
|
|
/*
|
|
** These are the allowed shellFlgs values
|
|
*/
|
|
-#define SHFLG_Scratch 0x00000001 /* The --scratch option is used */
|
|
-#define SHFLG_Pagecache 0x00000002 /* The --pagecache option is used */
|
|
-#define SHFLG_Lookaside 0x00000004 /* Lookaside memory is used */
|
|
-#define SHFLG_Backslash 0x00000008 /* The --backslash option is used */
|
|
-#define SHFLG_PreserveRowid 0x00000010 /* .dump preserves rowid values */
|
|
-#define SHFLG_Newlines 0x00000020 /* .dump --newline flag */
|
|
-#define SHFLG_CountChanges 0x00000040 /* .changes setting */
|
|
-#define SHFLG_Echo 0x00000080 /* .echo or --echo setting */
|
|
+#define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */
|
|
+#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */
|
|
+#define SHFLG_Backslash 0x00000004 /* The --backslash option is used */
|
|
+#define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */
|
|
+#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
|
|
+#define SHFLG_CountChanges 0x00000020 /* .changes setting */
|
|
+#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
|
|
|
|
/*
|
|
** Macros for testing and setting shellFlgs
|
|
@@ -2262,6 +8647,7 @@
|
|
#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */
|
|
#define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */
|
|
#define MODE_Pretty 11 /* Pretty-print schemas */
|
|
+#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */
|
|
|
|
static const char *modeDescr[] = {
|
|
"line",
|
|
@@ -2276,6 +8662,7 @@
|
|
"explain",
|
|
"ascii",
|
|
"prettyprint",
|
|
+ "eqp"
|
|
};
|
|
|
|
/*
|
|
@@ -2292,11 +8679,6 @@
|
|
#define SEP_Record "\x1E"
|
|
|
|
/*
|
|
-** Number of elements in an array
|
|
-*/
|
|
-#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
|
|
-
|
|
-/*
|
|
** A callback for the sqlite3_log() interface.
|
|
*/
|
|
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
|
|
@@ -2307,6 +8689,181 @@
|
|
}
|
|
|
|
/*
|
|
+** SQL function: shell_putsnl(X)
|
|
+**
|
|
+** Write the text X to the screen (or whatever output is being directed)
|
|
+** adding a newline at the end, and then return X.
|
|
+*/
|
|
+static void shellPutsFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nVal,
|
|
+ sqlite3_value **apVal
|
|
+){
|
|
+ ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
|
|
+ (void)nVal;
|
|
+ utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
|
|
+ sqlite3_result_value(pCtx, apVal[0]);
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: edit(VALUE)
|
|
+** edit(VALUE,EDITOR)
|
|
+**
|
|
+** These steps:
|
|
+**
|
|
+** (1) Write VALUE into a temporary file.
|
|
+** (2) Run program EDITOR on that temporary file.
|
|
+** (3) Read the temporary file back and return its content as the result.
|
|
+** (4) Delete the temporary file
|
|
+**
|
|
+** If the EDITOR argument is omitted, use the value in the VISUAL
|
|
+** environment variable. If still there is no EDITOR, through an error.
|
|
+**
|
|
+** Also throw an error if the EDITOR program returns a non-zero exit code.
|
|
+*/
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
+static void editFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ const char *zEditor;
|
|
+ char *zTempFile = 0;
|
|
+ sqlite3 *db;
|
|
+ char *zCmd = 0;
|
|
+ int bBin;
|
|
+ int rc;
|
|
+ int hasCRNL = 0;
|
|
+ FILE *f = 0;
|
|
+ sqlite3_int64 sz;
|
|
+ sqlite3_int64 x;
|
|
+ unsigned char *p = 0;
|
|
+
|
|
+ if( argc==2 ){
|
|
+ zEditor = (const char*)sqlite3_value_text(argv[1]);
|
|
+ }else{
|
|
+ zEditor = getenv("VISUAL");
|
|
+ }
|
|
+ if( zEditor==0 ){
|
|
+ sqlite3_result_error(context, "no editor for edit()", -1);
|
|
+ return;
|
|
+ }
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
|
+ sqlite3_result_error(context, "NULL input to edit()", -1);
|
|
+ return;
|
|
+ }
|
|
+ db = sqlite3_context_db_handle(context);
|
|
+ zTempFile = 0;
|
|
+ sqlite3_file_control(db, 0, SQLITE_FCNTL_TEMPFILENAME, &zTempFile);
|
|
+ if( zTempFile==0 ){
|
|
+ sqlite3_uint64 r = 0;
|
|
+ sqlite3_randomness(sizeof(r), &r);
|
|
+ zTempFile = sqlite3_mprintf("temp%llx", r);
|
|
+ if( zTempFile==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB;
|
|
+ /* When writing the file to be edited, do \n to \r\n conversions on systems
|
|
+ ** that want \r\n line endings */
|
|
+ f = fopen(zTempFile, bBin ? "wb" : "w");
|
|
+ if( f==0 ){
|
|
+ sqlite3_result_error(context, "edit() cannot open temp file", -1);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ sz = sqlite3_value_bytes(argv[0]);
|
|
+ if( bBin ){
|
|
+ x = fwrite(sqlite3_value_blob(argv[0]), 1, sz, f);
|
|
+ }else{
|
|
+ const char *z = (const char*)sqlite3_value_text(argv[0]);
|
|
+ /* Remember whether or not the value originally contained \r\n */
|
|
+ if( z && strstr(z,"\r\n")!=0 ) hasCRNL = 1;
|
|
+ x = fwrite(sqlite3_value_text(argv[0]), 1, sz, f);
|
|
+ }
|
|
+ fclose(f);
|
|
+ f = 0;
|
|
+ if( x!=sz ){
|
|
+ sqlite3_result_error(context, "edit() could not write the whole file", -1);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile);
|
|
+ if( zCmd==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ rc = system(zCmd);
|
|
+ sqlite3_free(zCmd);
|
|
+ if( rc ){
|
|
+ sqlite3_result_error(context, "EDITOR returned non-zero", -1);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ f = fopen(zTempFile, "rb");
|
|
+ if( f==0 ){
|
|
+ sqlite3_result_error(context,
|
|
+ "edit() cannot reopen temp file after edit", -1);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ fseek(f, 0, SEEK_END);
|
|
+ sz = ftell(f);
|
|
+ rewind(f);
|
|
+ p = sqlite3_malloc64( sz+(bBin==0) );
|
|
+ if( p==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ x = fread(p, 1, sz, f);
|
|
+ fclose(f);
|
|
+ f = 0;
|
|
+ if( x!=sz ){
|
|
+ sqlite3_result_error(context, "could not read back the whole file", -1);
|
|
+ goto edit_func_end;
|
|
+ }
|
|
+ if( bBin ){
|
|
+ sqlite3_result_blob64(context, p, sz, sqlite3_free);
|
|
+ }else{
|
|
+ sqlite3_int64 i, j;
|
|
+ if( hasCRNL ){
|
|
+ /* If the original contains \r\n then do no conversions back to \n */
|
|
+ j = sz;
|
|
+ }else{
|
|
+ /* If the file did not originally contain \r\n then convert any new
|
|
+ ** \r\n back into \n */
|
|
+ for(i=j=0; i<sz; i++){
|
|
+ if( p[i]=='\r' && p[i+1]=='\n' ) i++;
|
|
+ p[j++] = p[i];
|
|
+ }
|
|
+ sz = j;
|
|
+ p[sz] = 0;
|
|
+ }
|
|
+ sqlite3_result_text64(context, (const char*)p, sz,
|
|
+ sqlite3_free, SQLITE_UTF8);
|
|
+ }
|
|
+ p = 0;
|
|
+
|
|
+edit_func_end:
|
|
+ if( f ) fclose(f);
|
|
+ unlink(zTempFile);
|
|
+ sqlite3_free(zTempFile);
|
|
+ sqlite3_free(p);
|
|
+}
|
|
+#endif /* SQLITE_NOHAVE_SYSTEM */
|
|
+
|
|
+/*
|
|
+** Save or restore the current output mode
|
|
+*/
|
|
+static void outputModePush(ShellState *p){
|
|
+ p->modePrior = p->mode;
|
|
+ memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
|
|
+ memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
|
|
+}
|
|
+static void outputModePop(ShellState *p){
|
|
+ p->mode = p->modePrior;
|
|
+ memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
|
|
+ memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
|
|
+}
|
|
+
|
|
+/*
|
|
** Output the given string as a hex-encoded blob (eg. X'1234' )
|
|
*/
|
|
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
|
|
@@ -2551,12 +9108,9 @@
|
|
}
|
|
}
|
|
if( i==0 ){
|
|
- putc('"', out);
|
|
- for(i=0; z[i]; i++){
|
|
- if( z[i]=='"' ) putc('"', out);
|
|
- putc(z[i], out);
|
|
- }
|
|
- putc('"', out);
|
|
+ char *zQuoted = sqlite3_mprintf("\"%w\"", z);
|
|
+ utf8_printf(out, "%s", zQuoted);
|
|
+ sqlite3_free(zQuoted);
|
|
}else{
|
|
utf8_printf(out, "%s", z);
|
|
}
|
|
@@ -2566,7 +9120,6 @@
|
|
}
|
|
}
|
|
|
|
-#ifdef SIGINT
|
|
/*
|
|
** This routine runs when the user presses Ctrl-C
|
|
*/
|
|
@@ -2576,6 +9129,20 @@
|
|
if( seenInterrupt>2 ) exit(1);
|
|
if( globalDb ) sqlite3_interrupt(globalDb);
|
|
}
|
|
+
|
|
+#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
|
|
+/*
|
|
+** This routine runs for console events (e.g. Ctrl-C) on Win32
|
|
+*/
|
|
+static BOOL WINAPI ConsoleCtrlHandler(
|
|
+ DWORD dwCtrlType /* One of the CTRL_*_EVENT constants */
|
|
+){
|
|
+ if( dwCtrlType==CTRL_C_EVENT ){
|
|
+ interrupt_handler(0);
|
|
+ return TRUE;
|
|
+ }
|
|
+ return FALSE;
|
|
+}
|
|
#endif
|
|
|
|
#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
@@ -2646,6 +9213,108 @@
|
|
}
|
|
|
|
/*
|
|
+** Return true if string z[] has nothing but whitespace and comments to the
|
|
+** end of the first line.
|
|
+*/
|
|
+static int wsToEol(const char *z){
|
|
+ int i;
|
|
+ for(i=0; z[i]; i++){
|
|
+ if( z[i]=='\n' ) return 1;
|
|
+ if( IsSpace(z[i]) ) continue;
|
|
+ if( z[i]=='-' && z[i+1]=='-' ) return 1;
|
|
+ return 0;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add a new entry to the EXPLAIN QUERY PLAN data
|
|
+*/
|
|
+static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
|
|
+ EQPGraphRow *pNew;
|
|
+ int nText = strlen30(zText);
|
|
+ if( p->autoEQPtest ){
|
|
+ utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
|
|
+ }
|
|
+ pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
|
|
+ if( pNew==0 ) shell_out_of_memory();
|
|
+ pNew->iEqpId = iEqpId;
|
|
+ pNew->iParentId = p2;
|
|
+ memcpy(pNew->zText, zText, nText+1);
|
|
+ pNew->pNext = 0;
|
|
+ if( p->sGraph.pLast ){
|
|
+ p->sGraph.pLast->pNext = pNew;
|
|
+ }else{
|
|
+ p->sGraph.pRow = pNew;
|
|
+ }
|
|
+ p->sGraph.pLast = pNew;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free and reset the EXPLAIN QUERY PLAN data that has been collected
|
|
+** in p->sGraph.
|
|
+*/
|
|
+static void eqp_reset(ShellState *p){
|
|
+ EQPGraphRow *pRow, *pNext;
|
|
+ for(pRow = p->sGraph.pRow; pRow; pRow = pNext){
|
|
+ pNext = pRow->pNext;
|
|
+ sqlite3_free(pRow);
|
|
+ }
|
|
+ memset(&p->sGraph, 0, sizeof(p->sGraph));
|
|
+}
|
|
+
|
|
+/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
|
|
+** pOld, or return the first such line if pOld is NULL
|
|
+*/
|
|
+static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){
|
|
+ EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow;
|
|
+ while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
|
|
+ return pRow;
|
|
+}
|
|
+
|
|
+/* Render a single level of the graph that has iEqpId as its parent. Called
|
|
+** recursively to render sublevels.
|
|
+*/
|
|
+static void eqp_render_level(ShellState *p, int iEqpId){
|
|
+ EQPGraphRow *pRow, *pNext;
|
|
+ int n = strlen30(p->sGraph.zPrefix);
|
|
+ char *z;
|
|
+ for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
|
|
+ pNext = eqp_next_row(p, iEqpId, pRow);
|
|
+ z = pRow->zText;
|
|
+ utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
|
|
+ if( n<(int)sizeof(p->sGraph.zPrefix)-7 ){
|
|
+ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
|
|
+ eqp_render_level(p, pRow->iEqpId);
|
|
+ p->sGraph.zPrefix[n] = 0;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Display and reset the EXPLAIN QUERY PLAN data
|
|
+*/
|
|
+static void eqp_render(ShellState *p){
|
|
+ EQPGraphRow *pRow = p->sGraph.pRow;
|
|
+ if( pRow ){
|
|
+ if( pRow->zText[0]=='-' ){
|
|
+ if( pRow->pNext==0 ){
|
|
+ eqp_reset(p);
|
|
+ return;
|
|
+ }
|
|
+ utf8_printf(p->out, "%s\n", pRow->zText+3);
|
|
+ p->sGraph.pRow = pRow->pNext;
|
|
+ sqlite3_free(pRow);
|
|
+ }else{
|
|
+ utf8_printf(p->out, "QUERY PLAN\n");
|
|
+ }
|
|
+ p->sGraph.zPrefix[0] = 0;
|
|
+ eqp_render_level(p, 0);
|
|
+ eqp_reset(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
** This is the callback routine that the shell
|
|
** invokes for each row of a query result.
|
|
*/
|
|
@@ -2659,6 +9328,7 @@
|
|
int i;
|
|
ShellState *p = (ShellState*)pArg;
|
|
|
|
+ if( azArg==0 ) return 0;
|
|
switch( p->cMode ){
|
|
case MODE_Line: {
|
|
int w = 5;
|
|
@@ -2773,6 +9443,7 @@
|
|
for(i=0; IsSpace(z[i]); i++){}
|
|
for(; (c = z[i])!=0; i++){
|
|
if( IsSpace(c) ){
|
|
+ if( z[j-1]=='\r' ) z[j-1] = '\n';
|
|
if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue;
|
|
}else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){
|
|
j--;
|
|
@@ -2782,7 +9453,7 @@
|
|
while( j>0 && IsSpace(z[j-1]) ){ j--; }
|
|
z[j] = 0;
|
|
if( strlen30(z)>=79 ){
|
|
- for(i=j=0; (c = z[i])!=0; i++){
|
|
+ for(i=j=0; (c = z[i])!=0; i++){ /* Copy changes from z[i] back to z[j] */
|
|
if( c==cEnd ){
|
|
cEnd = 0;
|
|
}else if( c=='"' || c=='\'' || c=='`' ){
|
|
@@ -2789,6 +9460,8 @@
|
|
cEnd = c;
|
|
}else if( c=='[' ){
|
|
cEnd = ']';
|
|
+ }else if( c=='-' && z[i+1]=='-' ){
|
|
+ cEnd = '\n';
|
|
}else if( c=='(' ){
|
|
nParen++;
|
|
}else if( c==')' ){
|
|
@@ -2799,7 +9472,9 @@
|
|
}
|
|
}
|
|
z[j++] = c;
|
|
- if( nParen==1 && (c=='(' || c==',' || c=='\n') ){
|
|
+ if( nParen==1 && cEnd==0
|
|
+ && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
|
|
+ ){
|
|
if( c=='\n' ) j--;
|
|
printSchemaLineN(p->out, z, j, "\n ");
|
|
j = 0;
|
|
@@ -2919,8 +9594,16 @@
|
|
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
|
|
char z[50];
|
|
double r = sqlite3_column_double(p->pStmt, i);
|
|
- sqlite3_snprintf(50,z,"%!.20g", r);
|
|
- raw_printf(p->out, "%s", z);
|
|
+ sqlite3_uint64 ur;
|
|
+ memcpy(&ur,&r,sizeof(r));
|
|
+ if( ur==0x7ff0000000000000LL ){
|
|
+ raw_printf(p->out, "1e999");
|
|
+ }else if( ur==0xfff0000000000000LL ){
|
|
+ raw_printf(p->out, "-1e999");
|
|
+ }else{
|
|
+ sqlite3_snprintf(50,z,"%!.20g", r);
|
|
+ raw_printf(p->out, "%s", z);
|
|
+ }
|
|
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
|
|
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
|
|
int nBlob = sqlite3_column_bytes(p->pStmt, i);
|
|
@@ -2988,6 +9671,10 @@
|
|
utf8_printf(p->out, "%s", p->rowSeparator);
|
|
break;
|
|
}
|
|
+ case MODE_EQP: {
|
|
+ eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
|
|
+ break;
|
|
+ }
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -3009,6 +9696,7 @@
|
|
ShellText *p = (ShellText*)pArg;
|
|
int i;
|
|
UNUSED_PARAMETER(az);
|
|
+ if( azArg==0 ) return 0;
|
|
if( p->n ) appendText(p, "|", 0);
|
|
for(i=0; i<nArg; i++){
|
|
if( i ) appendText(p, ",", 0);
|
|
@@ -3073,7 +9761,7 @@
|
|
*/
|
|
static void set_table_name(ShellState *p, const char *zName){
|
|
int i, n;
|
|
- int cQuote;
|
|
+ char cQuote;
|
|
char *z;
|
|
|
|
if( p->zDestTable ){
|
|
@@ -3085,10 +9773,7 @@
|
|
n = strlen30(zName);
|
|
if( cQuote ) n += n+2;
|
|
z = p->zDestTable = malloc( n+1 );
|
|
- if( z==0 ){
|
|
- raw_printf(stderr,"Error: out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ if( z==0 ) shell_out_of_memory();
|
|
n = 0;
|
|
if( cQuote ) z[n++] = cQuote;
|
|
for(i=0; zName[i]; i++){
|
|
@@ -3196,7 +9881,7 @@
|
|
};
|
|
int i;
|
|
for(i=0; i<ArraySize(aTrans); i++){
|
|
- int n = (int)strlen(aTrans[i].zPattern);
|
|
+ int n = strlen30(aTrans[i].zPattern);
|
|
if( strncmp(aTrans[i].zPattern, z, n)==0 ){
|
|
utf8_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
|
|
break;
|
|
@@ -3243,37 +9928,54 @@
|
|
){
|
|
int iCur;
|
|
int iHiwtr;
|
|
+ FILE *out;
|
|
+ if( pArg==0 || pArg->out==0 ) return 0;
|
|
+ out = pArg->out;
|
|
|
|
- if( pArg && pArg->out ){
|
|
- displayStatLine(pArg, "Memory Used:",
|
|
- "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
|
|
- displayStatLine(pArg, "Number of Outstanding Allocations:",
|
|
- "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset);
|
|
- if( pArg->shellFlgs & SHFLG_Pagecache ){
|
|
- displayStatLine(pArg, "Number of Pcache Pages Used:",
|
|
- "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset);
|
|
+ if( pArg->pStmt && (pArg->statsOn & 2) ){
|
|
+ int nCol, i, x;
|
|
+ sqlite3_stmt *pStmt = pArg->pStmt;
|
|
+ char z[100];
|
|
+ nCol = sqlite3_column_count(pStmt);
|
|
+ raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);
|
|
+ utf8_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i));
|
|
+#ifndef SQLITE_OMIT_DECLTYPE
|
|
+ sqlite3_snprintf(30, z+x, "declared type:");
|
|
+ utf8_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
|
|
+#endif
|
|
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
|
+ sqlite3_snprintf(30, z+x, "database name:");
|
|
+ utf8_printf(out, "%-36s %s\n", z, sqlite3_column_database_name(pStmt,i));
|
|
+ sqlite3_snprintf(30, z+x, "table name:");
|
|
+ utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
|
|
+ sqlite3_snprintf(30, z+x, "origin name:");
|
|
+ utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i));
|
|
+#endif
|
|
}
|
|
- displayStatLine(pArg, "Number of Pcache Overflow Bytes:",
|
|
- "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset);
|
|
- if( pArg->shellFlgs & SHFLG_Scratch ){
|
|
- displayStatLine(pArg, "Number of Scratch Allocations Used:",
|
|
- "%lld (max %lld)", SQLITE_STATUS_SCRATCH_USED, bReset);
|
|
- }
|
|
- displayStatLine(pArg, "Number of Scratch Overflow Bytes:",
|
|
- "%lld (max %lld) bytes", SQLITE_STATUS_SCRATCH_OVERFLOW, bReset);
|
|
- displayStatLine(pArg, "Largest Allocation:",
|
|
- "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset);
|
|
- displayStatLine(pArg, "Largest Pcache Allocation:",
|
|
- "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset);
|
|
- displayStatLine(pArg, "Largest Scratch Allocation:",
|
|
- "%lld bytes", SQLITE_STATUS_SCRATCH_SIZE, bReset);
|
|
+ }
|
|
+
|
|
+ displayStatLine(pArg, "Memory Used:",
|
|
+ "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
|
|
+ displayStatLine(pArg, "Number of Outstanding Allocations:",
|
|
+ "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset);
|
|
+ if( pArg->shellFlgs & SHFLG_Pagecache ){
|
|
+ displayStatLine(pArg, "Number of Pcache Pages Used:",
|
|
+ "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset);
|
|
+ }
|
|
+ displayStatLine(pArg, "Number of Pcache Overflow Bytes:",
|
|
+ "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset);
|
|
+ displayStatLine(pArg, "Largest Allocation:",
|
|
+ "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset);
|
|
+ displayStatLine(pArg, "Largest Pcache Allocation:",
|
|
+ "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset);
|
|
#ifdef YYTRACKMAXSTACKDEPTH
|
|
- displayStatLine(pArg, "Deepest Parser Stack:",
|
|
- "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset);
|
|
+ displayStatLine(pArg, "Deepest Parser Stack:",
|
|
+ "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset);
|
|
#endif
|
|
- }
|
|
|
|
- if( pArg && pArg->out && db ){
|
|
+ if( db ){
|
|
if( pArg->shellFlgs & SHFLG_Lookaside ){
|
|
iHiwtr = iCur = -1;
|
|
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED,
|
|
@@ -3308,6 +10010,9 @@
|
|
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
|
|
raw_printf(pArg->out, "Page cache writes: %d\n", iCur);
|
|
iHiwtr = iCur = -1;
|
|
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
|
|
+ raw_printf(pArg->out, "Page cache spills: %d\n", iCur);
|
|
+ iHiwtr = iCur = -1;
|
|
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
|
|
raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n",
|
|
iCur);
|
|
@@ -3317,7 +10022,7 @@
|
|
iCur);
|
|
}
|
|
|
|
- if( pArg && pArg->out && db && pArg->pStmt ){
|
|
+ if( pArg->pStmt ){
|
|
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
|
|
bReset);
|
|
raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur);
|
|
@@ -3327,6 +10032,12 @@
|
|
raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur);
|
|
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
|
|
raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
|
|
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE, bReset);
|
|
+ raw_printf(pArg->out, "Reprepare operations: %d\n", iCur);
|
|
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset);
|
|
+ raw_printf(pArg->out, "Number of times run: %d\n", iCur);
|
|
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset);
|
|
+ raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur);
|
|
}
|
|
|
|
#ifdef __linux__
|
|
@@ -3425,8 +10136,7 @@
|
|
int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
|
|
int iOp; /* Index of operation in p->aiIndent[] */
|
|
|
|
- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
|
|
- "NextIfOpen", "PrevIfOpen", 0 };
|
|
+ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 };
|
|
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
|
|
"Rewind", 0 };
|
|
const char *azGoto[] = { "Goto", 0 };
|
|
@@ -3476,7 +10186,9 @@
|
|
}
|
|
nAlloc += 100;
|
|
p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int));
|
|
+ if( p->aiIndent==0 ) shell_out_of_memory();
|
|
abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
|
|
+ if( abYield==0 ) shell_out_of_memory();
|
|
}
|
|
abYield[iOp] = str_in_array(zOp, azYield);
|
|
p->aiIndent[iOp] = 0;
|
|
@@ -3542,8 +10254,7 @@
|
|
*/
|
|
static void exec_prepared_stmt(
|
|
ShellState *pArg, /* Pointer to ShellState */
|
|
- sqlite3_stmt *pStmt, /* Statment to run */
|
|
- int (*xCallback)(void*,int,char**,char**,int*) /* Callback function */
|
|
+ sqlite3_stmt *pStmt /* Statment to run */
|
|
){
|
|
int rc;
|
|
|
|
@@ -3553,57 +10264,181 @@
|
|
rc = sqlite3_step(pStmt);
|
|
/* if we have a result set... */
|
|
if( SQLITE_ROW == rc ){
|
|
- /* if we have a callback... */
|
|
- if( xCallback ){
|
|
- /* allocate space for col name ptr, value ptr, and type */
|
|
- int nCol = sqlite3_column_count(pStmt);
|
|
- void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
|
|
- if( !pData ){
|
|
- rc = SQLITE_NOMEM;
|
|
- }else{
|
|
- char **azCols = (char **)pData; /* Names of result columns */
|
|
- char **azVals = &azCols[nCol]; /* Results */
|
|
- int *aiTypes = (int *)&azVals[nCol]; /* Result types */
|
|
- int i, x;
|
|
- assert(sizeof(int) <= sizeof(char *));
|
|
- /* save off ptrs to column names */
|
|
+ /* allocate space for col name ptr, value ptr, and type */
|
|
+ int nCol = sqlite3_column_count(pStmt);
|
|
+ void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
|
|
+ if( !pData ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ char **azCols = (char **)pData; /* Names of result columns */
|
|
+ char **azVals = &azCols[nCol]; /* Results */
|
|
+ int *aiTypes = (int *)&azVals[nCol]; /* Result types */
|
|
+ int i, x;
|
|
+ assert(sizeof(int) <= sizeof(char *));
|
|
+ /* save off ptrs to column names */
|
|
+ for(i=0; i<nCol; i++){
|
|
+ azCols[i] = (char *)sqlite3_column_name(pStmt, i);
|
|
+ }
|
|
+ do{
|
|
+ /* extract the data and data types */
|
|
for(i=0; i<nCol; i++){
|
|
- azCols[i] = (char *)sqlite3_column_name(pStmt, i);
|
|
+ aiTypes[i] = x = sqlite3_column_type(pStmt, i);
|
|
+ if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){
|
|
+ azVals[i] = "";
|
|
+ }else{
|
|
+ azVals[i] = (char*)sqlite3_column_text(pStmt, i);
|
|
+ }
|
|
+ if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ break; /* from for */
|
|
+ }
|
|
+ } /* end for */
|
|
+
|
|
+ /* if data and types extracted successfully... */
|
|
+ if( SQLITE_ROW == rc ){
|
|
+ /* call the supplied callback with the result row data */
|
|
+ if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){
|
|
+ rc = SQLITE_ABORT;
|
|
+ }else{
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ }
|
|
}
|
|
- do{
|
|
- /* extract the data and data types */
|
|
- for(i=0; i<nCol; i++){
|
|
- aiTypes[i] = x = sqlite3_column_type(pStmt, i);
|
|
- if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){
|
|
- azVals[i] = "";
|
|
- }else{
|
|
- azVals[i] = (char*)sqlite3_column_text(pStmt, i);
|
|
- }
|
|
- if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
|
|
- rc = SQLITE_NOMEM;
|
|
- break; /* from for */
|
|
- }
|
|
- } /* end for */
|
|
+ } while( SQLITE_ROW == rc );
|
|
+ sqlite3_free(pData);
|
|
+ }
|
|
+ }
|
|
+}
|
|
|
|
- /* if data and types extracted successfully... */
|
|
- if( SQLITE_ROW == rc ){
|
|
- /* call the supplied callback with the result row data */
|
|
- if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){
|
|
- rc = SQLITE_ABORT;
|
|
- }else{
|
|
- rc = sqlite3_step(pStmt);
|
|
- }
|
|
- }
|
|
- } while( SQLITE_ROW == rc );
|
|
- sqlite3_free(pData);
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+/*
|
|
+** This function is called to process SQL if the previous shell command
|
|
+** was ".expert". It passes the SQL in the second argument directly to
|
|
+** the sqlite3expert object.
|
|
+**
|
|
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error
|
|
+** code. In this case, (*pzErr) may be set to point to a buffer containing
|
|
+** an English language error message. It is the responsibility of the
|
|
+** caller to eventually free this buffer using sqlite3_free().
|
|
+*/
|
|
+static int expertHandleSQL(
|
|
+ ShellState *pState,
|
|
+ const char *zSql,
|
|
+ char **pzErr
|
|
+){
|
|
+ assert( pState->expert.pExpert );
|
|
+ assert( pzErr==0 || *pzErr==0 );
|
|
+ return sqlite3_expert_sql(pState->expert.pExpert, zSql, pzErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called either to silently clean up the object
|
|
+** created by the ".expert" command (if bCancel==1), or to generate a
|
|
+** report from it and then clean it up (if bCancel==0).
|
|
+**
|
|
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error
|
|
+** code. In this case, (*pzErr) may be set to point to a buffer containing
|
|
+** an English language error message. It is the responsibility of the
|
|
+** caller to eventually free this buffer using sqlite3_free().
|
|
+*/
|
|
+static int expertFinish(
|
|
+ ShellState *pState,
|
|
+ int bCancel,
|
|
+ char **pzErr
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3expert *p = pState->expert.pExpert;
|
|
+ assert( p );
|
|
+ assert( bCancel || pzErr==0 || *pzErr==0 );
|
|
+ if( bCancel==0 ){
|
|
+ FILE *out = pState->out;
|
|
+ int bVerbose = pState->expert.bVerbose;
|
|
+
|
|
+ rc = sqlite3_expert_analyze(p, pzErr);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int nQuery = sqlite3_expert_count(p);
|
|
+ int i;
|
|
+
|
|
+ if( bVerbose ){
|
|
+ const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
|
|
+ raw_printf(out, "-- Candidates -----------------------------\n");
|
|
+ raw_printf(out, "%s\n", zCand);
|
|
}
|
|
+ for(i=0; i<nQuery; i++){
|
|
+ const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL);
|
|
+ const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES);
|
|
+ const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN);
|
|
+ if( zIdx==0 ) zIdx = "(no new indexes)\n";
|
|
+ if( bVerbose ){
|
|
+ raw_printf(out, "-- Query %d --------------------------------\n",i+1);
|
|
+ raw_printf(out, "%s\n\n", zSql);
|
|
+ }
|
|
+ raw_printf(out, "%s\n", zIdx);
|
|
+ raw_printf(out, "%s\n", zEQP);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_expert_destroy(p);
|
|
+ pState->expert.pExpert = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of ".expert" dot command.
|
|
+*/
|
|
+static int expertDotCommand(
|
|
+ ShellState *pState, /* Current shell tool state */
|
|
+ char **azArg, /* Array of arguments passed to dot command */
|
|
+ int nArg /* Number of entries in azArg[] */
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zErr = 0;
|
|
+ int i;
|
|
+ int iSample = 0;
|
|
+
|
|
+ assert( pState->expert.pExpert==0 );
|
|
+ memset(&pState->expert, 0, sizeof(ExpertInfo));
|
|
+
|
|
+ for(i=1; rc==SQLITE_OK && i<nArg; i++){
|
|
+ char *z = azArg[i];
|
|
+ int n;
|
|
+ if( z[0]=='-' && z[1]=='-' ) z++;
|
|
+ n = strlen30(z);
|
|
+ if( n>=2 && 0==strncmp(z, "-verbose", n) ){
|
|
+ pState->expert.bVerbose = 1;
|
|
+ }
|
|
+ else if( n>=2 && 0==strncmp(z, "-sample", n) ){
|
|
+ if( i==(nArg-1) ){
|
|
+ raw_printf(stderr, "option requires an argument: %s\n", z);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ iSample = (int)integerValue(azArg[++i]);
|
|
+ if( iSample<0 || iSample>100 ){
|
|
+ raw_printf(stderr, "value out of range: %s\n", azArg[i]);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else{
|
|
+ raw_printf(stderr, "unknown option: %s\n", z);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
|
|
+ if( pState->expert.pExpert==0 ){
|
|
+ raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr);
|
|
+ rc = SQLITE_ERROR;
|
|
}else{
|
|
- do{
|
|
- rc = sqlite3_step(pStmt);
|
|
- } while( rc == SQLITE_ROW );
|
|
+ sqlite3_expert_config(
|
|
+ pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample
|
|
+ );
|
|
}
|
|
}
|
|
+
|
|
+ return rc;
|
|
}
|
|
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
|
|
|
/*
|
|
** Execute a statement or set of statements. Print
|
|
@@ -3615,11 +10450,8 @@
|
|
** and callback data argument.
|
|
*/
|
|
static int shell_exec(
|
|
- sqlite3 *db, /* An open database */
|
|
+ ShellState *pArg, /* Pointer to ShellState */
|
|
const char *zSql, /* SQL to be evaluated */
|
|
- int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */
|
|
- /* (not the same as sqlite3_exec) */
|
|
- ShellState *pArg, /* Pointer to ShellState */
|
|
char **pzErrMsg /* Error msg written here */
|
|
){
|
|
sqlite3_stmt *pStmt = NULL; /* Statement to execute. */
|
|
@@ -3626,11 +10458,19 @@
|
|
int rc = SQLITE_OK; /* Return Code */
|
|
int rc2;
|
|
const char *zLeftover; /* Tail of unprocessed SQL */
|
|
+ sqlite3 *db = pArg->db;
|
|
|
|
if( pzErrMsg ){
|
|
*pzErrMsg = NULL;
|
|
}
|
|
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( pArg->expert.pExpert ){
|
|
+ rc = expertHandleSQL(pArg, zSql, pzErrMsg);
|
|
+ return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
|
|
+ }
|
|
+#endif
|
|
+
|
|
while( zSql[0] && (SQLITE_OK == rc) ){
|
|
static const char *zStmtSql;
|
|
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
|
|
@@ -3664,20 +10504,27 @@
|
|
if( pArg && pArg->autoEQP && sqlite3_strlike("EXPLAIN%",zStmtSql,0)!=0 ){
|
|
sqlite3_stmt *pExplain;
|
|
char *zEQP;
|
|
+ int triggerEQP = 0;
|
|
disable_debug_trace_modes();
|
|
+ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP);
|
|
+ if( pArg->autoEQP>=AUTOEQP_trigger ){
|
|
+ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
|
|
+ }
|
|
zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql);
|
|
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
|
|
if( rc==SQLITE_OK ){
|
|
while( sqlite3_step(pExplain)==SQLITE_ROW ){
|
|
- raw_printf(pArg->out,"--EQP-- %d,",sqlite3_column_int(pExplain, 0));
|
|
- raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1));
|
|
- raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
|
|
- utf8_printf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
|
|
+ const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
|
|
+ int iEqpId = sqlite3_column_int(pExplain, 0);
|
|
+ int iParentId = sqlite3_column_int(pExplain, 1);
|
|
+ if( zEQPLine[0]=='-' ) eqp_render(pArg);
|
|
+ eqp_append(pArg, iEqpId, iParentId, zEQPLine);
|
|
}
|
|
+ eqp_render(pArg);
|
|
}
|
|
sqlite3_finalize(pExplain);
|
|
sqlite3_free(zEQP);
|
|
- if( pArg->autoEQP>=2 ){
|
|
+ if( pArg->autoEQP>=AUTOEQP_full ){
|
|
/* Also do an EXPLAIN for ".eqp full" mode */
|
|
zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql);
|
|
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
|
|
@@ -3684,22 +10531,34 @@
|
|
if( rc==SQLITE_OK ){
|
|
pArg->cMode = MODE_Explain;
|
|
explain_data_prepare(pArg, pExplain);
|
|
- exec_prepared_stmt(pArg, pExplain, xCallback);
|
|
+ exec_prepared_stmt(pArg, pExplain);
|
|
explain_data_delete(pArg);
|
|
}
|
|
sqlite3_finalize(pExplain);
|
|
sqlite3_free(zEQP);
|
|
}
|
|
+ if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
|
|
+ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
|
|
+ /* Reprepare pStmt before reactiving trace modes */
|
|
+ sqlite3_finalize(pStmt);
|
|
+ sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
+ if( pArg ) pArg->pStmt = pStmt;
|
|
+ }
|
|
restore_debug_trace_modes();
|
|
}
|
|
|
|
if( pArg ){
|
|
pArg->cMode = pArg->mode;
|
|
- if( pArg->autoExplain
|
|
- && sqlite3_column_count(pStmt)==8
|
|
- && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
|
|
- ){
|
|
- pArg->cMode = MODE_Explain;
|
|
+ if( pArg->autoExplain ){
|
|
+ if( sqlite3_column_count(pStmt)==8
|
|
+ && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
|
|
+ ){
|
|
+ pArg->cMode = MODE_Explain;
|
|
+ }
|
|
+ if( sqlite3_column_count(pStmt)==4
|
|
+ && sqlite3_strlike("EXPLAIN QUERY PLAN%", zStmtSql,0)==0 ){
|
|
+ pArg->cMode = MODE_EQP;
|
|
+ }
|
|
}
|
|
|
|
/* If the shell is currently in ".explain" mode, gather the extra
|
|
@@ -3709,8 +10568,9 @@
|
|
}
|
|
}
|
|
|
|
- exec_prepared_stmt(pArg, pStmt, xCallback);
|
|
+ exec_prepared_stmt(pArg, pStmt);
|
|
explain_data_delete(pArg);
|
|
+ eqp_render(pArg);
|
|
|
|
/* print usage stats if stats on */
|
|
if( pArg && pArg->statsOn ){
|
|
@@ -3788,10 +10648,7 @@
|
|
if( nCol>=nAlloc-2 ){
|
|
nAlloc = nAlloc*2 + nCol + 10;
|
|
azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
|
|
- if( azCol==0 ){
|
|
- raw_printf(stderr, "Error: out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ if( azCol==0 ) shell_out_of_memory();
|
|
}
|
|
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
|
if( sqlite3_column_int(pStmt, 5) ){
|
|
@@ -3807,6 +10664,7 @@
|
|
}
|
|
}
|
|
sqlite3_finalize(pStmt);
|
|
+ if( azCol==0 ) return 0;
|
|
azCol[0] = 0;
|
|
azCol[nCol+1] = 0;
|
|
|
|
@@ -3890,7 +10748,7 @@
|
|
ShellState *p = (ShellState *)pArg;
|
|
|
|
UNUSED_PARAMETER(azNotUsed);
|
|
- if( nArg!=3 ) return 1;
|
|
+ if( nArg!=3 || azArg==0 ) return 0;
|
|
zTable = azArg[0];
|
|
zType = azArg[1];
|
|
zSql = azArg[2];
|
|
@@ -3971,11 +10829,11 @@
|
|
savedMode = p->mode;
|
|
p->zDestTable = sTable.z;
|
|
p->mode = p->cMode = MODE_Insert;
|
|
- rc = shell_exec(p->db, sSelect.z, shell_callback, p, 0);
|
|
+ rc = shell_exec(p, sSelect.z, 0);
|
|
if( (rc&0xff)==SQLITE_CORRUPT ){
|
|
raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n");
|
|
toggleSelectOrder(p->db);
|
|
- shell_exec(p->db, sSelect.z, shell_callback, p, 0);
|
|
+ shell_exec(p, sSelect.z, 0);
|
|
toggleSelectOrder(p->db);
|
|
}
|
|
p->zDestTable = savedDestTable;
|
|
@@ -4026,124 +10884,239 @@
|
|
}
|
|
|
|
/*
|
|
-** Text of a help message
|
|
+** Text of help messages.
|
|
+**
|
|
+** The help text for each individual command begins with a line that starts
|
|
+** with ".". Subsequent lines are supplimental information.
|
|
+**
|
|
+** There must be two or more spaces between the end of the command and the
|
|
+** start of the description of what that command does.
|
|
*/
|
|
-static char zHelp[] =
|
|
+static const char *(azHelp[]) = {
|
|
+#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
|
|
+ ".archive ... Manage SQL archives",
|
|
+ " Each command must have exactly one of the following options:",
|
|
+ " -c, --create Create a new archive",
|
|
+ " -u, --update Update or add files to an existing archive",
|
|
+ " -t, --list List contents of archive",
|
|
+ " -x, --extract Extract files from archive",
|
|
+ " Optional arguments:",
|
|
+ " -v, --verbose Print each filename as it is processed",
|
|
+ " -f FILE, --file FILE Operate on archive FILE (default is current db)",
|
|
+ " -a FILE, --append FILE Operate on FILE opened using the apndvfs VFS",
|
|
+ " -C DIR, --directory DIR Change to directory DIR to read/extract files",
|
|
+ " -n, --dryrun Show the SQL that would have occurred",
|
|
+ " Examples:",
|
|
+ " .ar -cf archive.sar foo bar # Create archive.sar from files foo and bar",
|
|
+ " .ar -tf archive.sar # List members of archive.sar",
|
|
+ " .ar -xvf archive.sar # Verbosely extract files from archive.sar",
|
|
+ " See also:",
|
|
+ " http://sqlite.org/cli.html#sqlar_archive_support",
|
|
+#endif
|
|
#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
- ".auth ON|OFF Show authorizer callbacks\n"
|
|
+ ".auth ON|OFF Show authorizer callbacks",
|
|
#endif
|
|
- ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
|
|
- ".bail on|off Stop after hitting an error. Default OFF\n"
|
|
- ".binary on|off Turn binary output on or off. Default OFF\n"
|
|
- ".cd DIRECTORY Change the working directory to DIRECTORY\n"
|
|
- ".changes on|off Show number of rows changed by SQL\n"
|
|
- ".check GLOB Fail if output since .testcase does not match\n"
|
|
- ".clone NEWDB Clone data into NEWDB from the existing database\n"
|
|
- ".databases List names and files of attached databases\n"
|
|
- ".dbinfo ?DB? Show status information about the database\n"
|
|
- ".dump ?TABLE? ... Dump the database in an SQL text format\n"
|
|
- " If TABLE specified, only dump tables matching\n"
|
|
- " LIKE pattern TABLE.\n"
|
|
- ".echo on|off Turn command echo on or off\n"
|
|
- ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n"
|
|
- ".exit Exit this program\n"
|
|
+ ".backup ?DB? FILE Backup DB (default \"main\") to FILE",
|
|
+ " --append Use the appendvfs",
|
|
+ ".bail on|off Stop after hitting an error. Default OFF",
|
|
+ ".binary on|off Turn binary output on or off. Default OFF",
|
|
+ ".cd DIRECTORY Change the working directory to DIRECTORY",
|
|
+ ".changes on|off Show number of rows changed by SQL",
|
|
+ ".check GLOB Fail if output since .testcase does not match",
|
|
+ ".clone NEWDB Clone data into NEWDB from the existing database",
|
|
+ ".databases List names and files of attached databases",
|
|
+ ".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
|
|
+ ".dbinfo ?DB? Show status information about the database",
|
|
+ ".dump ?TABLE? ... Render all database content as SQL",
|
|
+ " Options:",
|
|
+ " --preserve-rowids Include ROWID values in the output",
|
|
+ " --newlines Allow unescaped newline characters in output",
|
|
+ " TABLE is LIKE pattern for the tables to dump",
|
|
+ ".echo on|off Turn command echo on or off",
|
|
+ ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN",
|
|
+ ".excel Display the output of next command in a spreadsheet",
|
|
+ ".exit ?CODE? Exit this program with return-code CODE",
|
|
+ ".expert EXPERIMENTAL. Suggest indexes for specified queries",
|
|
/* Because explain mode comes on automatically now, the ".explain" mode
|
|
** is removed from the help screen. It is still supported for legacy, however */
|
|
-/*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n"*/
|
|
- ".fullschema ?--indent? Show schema and the content of sqlite_stat tables\n"
|
|
- ".headers on|off Turn display of headers on or off\n"
|
|
- ".help Show this message\n"
|
|
- ".import FILE TABLE Import data from FILE into TABLE\n"
|
|
+/*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic",*/
|
|
+ ".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
|
|
+ ".headers on|off Turn display of headers on or off",
|
|
+ ".help ?-all? ?PATTERN? Show help text for PATTERN",
|
|
+ ".import FILE TABLE Import data from FILE into TABLE",
|
|
#ifndef SQLITE_OMIT_TEST_CONTROL
|
|
- ".imposter INDEX TABLE Create imposter table TABLE on index INDEX\n"
|
|
+ ".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
|
|
#endif
|
|
- ".indexes ?TABLE? Show names of all indexes\n"
|
|
- " If TABLE specified, only show indexes for tables\n"
|
|
- " matching LIKE pattern TABLE.\n"
|
|
+ ".indexes ?TABLE? Show names of indexes",
|
|
+ " If TABLE is specified, only show indexes for",
|
|
+ " tables matching TABLE using the LIKE operator.",
|
|
#ifdef SQLITE_ENABLE_IOTRACE
|
|
- ".iotrace FILE Enable I/O diagnostic logging to FILE\n"
|
|
+ ".iotrace FILE Enable I/O diagnostic logging to FILE",
|
|
#endif
|
|
- ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT\n"
|
|
- ".lint OPTIONS Report potential schema issues. Options:\n"
|
|
- " fkey-indexes Find missing foreign key indexes\n"
|
|
+ ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT",
|
|
+ ".lint OPTIONS Report potential schema issues.",
|
|
+ " Options:",
|
|
+ " fkey-indexes Find missing foreign key indexes",
|
|
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
- ".load FILE ?ENTRY? Load an extension library\n"
|
|
+ ".load FILE ?ENTRY? Load an extension library",
|
|
#endif
|
|
- ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n"
|
|
- ".mode MODE ?TABLE? Set output mode where MODE is one of:\n"
|
|
- " ascii Columns/rows delimited by 0x1F and 0x1E\n"
|
|
- " csv Comma-separated values\n"
|
|
- " column Left-aligned columns. (See .width)\n"
|
|
- " html HTML <table> code\n"
|
|
- " insert SQL insert statements for TABLE\n"
|
|
- " line One value per line\n"
|
|
- " list Values delimited by \"|\"\n"
|
|
- " quote Escape answers as for SQL\n"
|
|
- " tabs Tab-separated values\n"
|
|
- " tcl TCL list elements\n"
|
|
- ".nullvalue STRING Use STRING in place of NULL values\n"
|
|
- ".once FILENAME Output for the next SQL command only to FILENAME\n"
|
|
- ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE\n"
|
|
- " The --new option starts with an empty file\n"
|
|
- ".output ?FILENAME? Send output to FILENAME or stdout\n"
|
|
- ".print STRING... Print literal STRING\n"
|
|
- ".prompt MAIN CONTINUE Replace the standard prompts\n"
|
|
- ".quit Exit this program\n"
|
|
- ".read FILENAME Execute SQL in FILENAME\n"
|
|
- ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
|
|
- ".save FILE Write in-memory database into FILE\n"
|
|
- ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n"
|
|
- ".schema ?PATTERN? Show the CREATE statements matching PATTERN\n"
|
|
- " Add --indent for pretty-printing\n"
|
|
- ".selftest ?--init? Run tests defined in the SELFTEST table\n"
|
|
- ".separator COL ?ROW? Change the column separator and optionally the row\n"
|
|
- " separator for both the output mode and .import\n"
|
|
+ ".log FILE|off Turn logging on or off. FILE can be stderr/stdout",
|
|
+ ".mode MODE ?TABLE? Set output mode",
|
|
+ " MODE is one of:",
|
|
+ " ascii Columns/rows delimited by 0x1F and 0x1E",
|
|
+ " csv Comma-separated values",
|
|
+ " column Left-aligned columns. (See .width)",
|
|
+ " html HTML <table> code",
|
|
+ " insert SQL insert statements for TABLE",
|
|
+ " line One value per line",
|
|
+ " list Values delimited by \"|\"",
|
|
+ " quote Escape answers as for SQL",
|
|
+ " tabs Tab-separated values",
|
|
+ " tcl TCL list elements",
|
|
+ ".nullvalue STRING Use STRING in place of NULL values",
|
|
+ ".once (-e|-x|FILE) Output for the next SQL command only to FILE",
|
|
+ " If FILE begins with '|' then open as a pipe",
|
|
+ " Other options:",
|
|
+ " -e Invoke system text editor",
|
|
+ " -x Open in a spreadsheet",
|
|
+ ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
|
|
+ " Options:",
|
|
+ " --append Use appendvfs to append database to the end of FILE",
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ " --deserialize Load into memory useing sqlite3_deserialize()",
|
|
+#endif
|
|
+ " --new Initialize FILE to an empty database",
|
|
+ " --readonly Open FILE readonly",
|
|
+ " --zip FILE is a ZIP archive",
|
|
+ ".output ?FILE? Send output to FILE or stdout if FILE is omitted",
|
|
+ " If FILE begins with '|' then open it as a pipe.",
|
|
+ ".print STRING... Print literal STRING",
|
|
+ ".prompt MAIN CONTINUE Replace the standard prompts",
|
|
+ ".quit Exit this program",
|
|
+ ".read FILE Read input from FILE",
|
|
+ ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
|
|
+ ".save FILE Write in-memory database into FILE",
|
|
+ ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
|
|
+ ".schema ?PATTERN? Show the CREATE statements matching PATTERN",
|
|
+ " Options:",
|
|
+ " --indent Try to pretty-print the schema",
|
|
+ ".selftest ?OPTIONS? Run tests defined in the SELFTEST table",
|
|
+ " Options:",
|
|
+ " --init Create a new SELFTEST table",
|
|
+ " -v Verbose output",
|
|
+ ".separator COL ?ROW? Change the column and row separators",
|
|
#if defined(SQLITE_ENABLE_SESSION)
|
|
- ".session CMD ... Create or control sessions\n"
|
|
+ ".session ?NAME? CMD ... Create or control sessions",
|
|
+ " Subcommands:",
|
|
+ " attach TABLE Attach TABLE",
|
|
+ " changeset FILE Write a changeset into FILE",
|
|
+ " close Close one session",
|
|
+ " enable ?BOOLEAN? Set or query the enable bit",
|
|
+ " filter GLOB... Reject tables matching GLOBs",
|
|
+ " indirect ?BOOLEAN? Mark or query the indirect status",
|
|
+ " isempty Query whether the session is empty",
|
|
+ " list List currently open session names",
|
|
+ " open DB NAME Open a new session on DB",
|
|
+ " patchset FILE Write a patchset into FILE",
|
|
+ " If ?NAME? is omitted, the first defined session is used.",
|
|
#endif
|
|
- ".sha3sum ?OPTIONS...? Compute a SHA3 hash of database content\n"
|
|
- ".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
|
|
- ".show Show the current values for various settings\n"
|
|
- ".stats ?on|off? Show stats or turn stats on or off\n"
|
|
- ".system CMD ARGS... Run CMD ARGS... in a system shell\n"
|
|
- ".tables ?TABLE? List names of tables\n"
|
|
- " If TABLE specified, only list tables matching\n"
|
|
- " LIKE pattern TABLE.\n"
|
|
- ".testcase NAME Begin redirecting output to 'testcase-out.txt'\n"
|
|
- ".timeout MS Try opening locked tables for MS milliseconds\n"
|
|
- ".timer on|off Turn SQL timer on or off\n"
|
|
- ".trace FILE|off Output each SQL statement as it is run\n"
|
|
- ".vfsinfo ?AUX? Information about the top-level VFS\n"
|
|
- ".vfslist List all available VFSes\n"
|
|
- ".vfsname ?AUX? Print the name of the VFS stack\n"
|
|
- ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
|
|
- " Negative values right-justify\n"
|
|
-;
|
|
+ ".sha3sum ... Compute a SHA3 hash of database content",
|
|
+ " Options:",
|
|
+ " --schema Also hash the sqlite_master table",
|
|
+ " --sha3-224 Use the sha3-224 algorithm",
|
|
+ " --sha3-256 Use the sha3-256 algorithm. This is the default.",
|
|
+ " --sha3-384 Use the sha3-384 algorithm",
|
|
+ " --sha3-512 Use the sha3-512 algorithm",
|
|
+ " Any other argument is a LIKE pattern for tables to hash",
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
+ ".shell CMD ARGS... Run CMD ARGS... in a system shell",
|
|
+#endif
|
|
+ ".show Show the current values for various settings",
|
|
+ ".stats ?on|off? Show stats or turn stats on or off",
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
+ ".system CMD ARGS... Run CMD ARGS... in a system shell",
|
|
+#endif
|
|
+ ".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
|
|
+ ".testcase NAME Begin redirecting output to 'testcase-out.txt'",
|
|
+ ".timeout MS Try opening locked tables for MS milliseconds",
|
|
+ ".timer on|off Turn SQL timer on or off",
|
|
+ ".trace FILE|off Output each SQL statement as it is run",
|
|
+ ".vfsinfo ?AUX? Information about the top-level VFS",
|
|
+ ".vfslist List all available VFSes",
|
|
+ ".vfsname ?AUX? Print the name of the VFS stack",
|
|
+ ".width NUM1 NUM2 ... Set column widths for \"column\" mode",
|
|
+ " Negative values right-justify",
|
|
+};
|
|
|
|
-#if defined(SQLITE_ENABLE_SESSION)
|
|
/*
|
|
-** Print help information for the ".sessions" command
|
|
+** Output help text.
|
|
+**
|
|
+** zPattern describes the set of commands for which help text is provided.
|
|
+** If zPattern is NULL, then show all commands, but only give a one-line
|
|
+** description of each.
|
|
+**
|
|
+** Return the number of matches.
|
|
*/
|
|
-void session_help(ShellState *p){
|
|
- raw_printf(p->out,
|
|
- ".session ?NAME? SUBCOMMAND ?ARGS...?\n"
|
|
- "If ?NAME? is omitted, the first defined session is used.\n"
|
|
- "Subcommands:\n"
|
|
- " attach TABLE Attach TABLE\n"
|
|
- " changeset FILE Write a changeset into FILE\n"
|
|
- " close Close one session\n"
|
|
- " enable ?BOOLEAN? Set or query the enable bit\n"
|
|
- " filter GLOB... Reject tables matching GLOBs\n"
|
|
- " indirect ?BOOLEAN? Mark or query the indirect status\n"
|
|
- " isempty Query whether the session is empty\n"
|
|
- " list List currently open session names\n"
|
|
- " open DB NAME Open a new session on DB\n"
|
|
- " patchset FILE Write a patchset into FILE\n"
|
|
- );
|
|
+static int showHelp(FILE *out, const char *zPattern){
|
|
+ int i = 0;
|
|
+ int j = 0;
|
|
+ int n = 0;
|
|
+ char *zPat;
|
|
+ if( zPattern==0
|
|
+ || zPattern[0]=='0'
|
|
+ || strcmp(zPattern,"-a")==0
|
|
+ || strcmp(zPattern,"-all")==0
|
|
+ ){
|
|
+ /* Show all commands, but only one line per command */
|
|
+ if( zPattern==0 ) zPattern = "";
|
|
+ for(i=0; i<ArraySize(azHelp); i++){
|
|
+ if( azHelp[i][0]=='.' || zPattern[0] ){
|
|
+ utf8_printf(out, "%s\n", azHelp[i]);
|
|
+ n++;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ /* Look for commands that for which zPattern is an exact prefix */
|
|
+ zPat = sqlite3_mprintf(".%s*", zPattern);
|
|
+ for(i=0; i<ArraySize(azHelp); i++){
|
|
+ if( sqlite3_strglob(zPat, azHelp[i])==0 ){
|
|
+ utf8_printf(out, "%s\n", azHelp[i]);
|
|
+ j = i+1;
|
|
+ n++;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zPat);
|
|
+ if( n ){
|
|
+ if( n==1 ){
|
|
+ /* when zPattern is a prefix of exactly one command, then include the
|
|
+ ** details of that command, which should begin at offset j */
|
|
+ while( j<ArraySize(azHelp)-1 && azHelp[j][0]!='.' ){
|
|
+ utf8_printf(out, "%s\n", azHelp[j]);
|
|
+ j++;
|
|
+ }
|
|
+ }
|
|
+ return n;
|
|
+ }
|
|
+ /* Look for commands that contain zPattern anywhere. Show the complete
|
|
+ ** text of all commands that match. */
|
|
+ zPat = sqlite3_mprintf("%%%s%%", zPattern);
|
|
+ for(i=0; i<ArraySize(azHelp); i++){
|
|
+ if( azHelp[i][0]=='.' ) j = i;
|
|
+ if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
|
|
+ utf8_printf(out, "%s\n", azHelp[j]);
|
|
+ while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]!='.' ){
|
|
+ j++;
|
|
+ utf8_printf(out, "%s\n", azHelp[j]);
|
|
+ }
|
|
+ i = j;
|
|
+ n++;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(zPat);
|
|
+ }
|
|
+ return n;
|
|
}
|
|
-#endif
|
|
|
|
-
|
|
/* Forward reference */
|
|
static int process_input(ShellState *p, FILE *in);
|
|
|
|
@@ -4172,7 +11145,7 @@
|
|
nIn = ftell(in);
|
|
rewind(in);
|
|
pBuf = sqlite3_malloc64( nIn+1 );
|
|
- if( pBuf==0 ) return 0;
|
|
+ if( pBuf==0 ){ fclose(in); return 0; }
|
|
nRead = fread(pBuf, nIn, 1, in);
|
|
fclose(in);
|
|
if( nRead!=1 ){
|
|
@@ -4232,18 +11205,105 @@
|
|
#endif
|
|
|
|
/*
|
|
+** Try to deduce the type of file for zName based on its content. Return
|
|
+** one of the SHELL_OPEN_* constants.
|
|
+**
|
|
+** If the file does not exist or is empty but its name looks like a ZIP
|
|
+** archive and the dfltZip flag is true, then assume it is a ZIP archive.
|
|
+** Otherwise, assume an ordinary database regardless of the filename if
|
|
+** the type cannot be determined from content.
|
|
+*/
|
|
+int deduceDatabaseType(const char *zName, int dfltZip){
|
|
+ FILE *f = fopen(zName, "rb");
|
|
+ size_t n;
|
|
+ int rc = SHELL_OPEN_UNSPEC;
|
|
+ char zBuf[100];
|
|
+ if( f==0 ){
|
|
+ if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
|
|
+ return SHELL_OPEN_ZIPFILE;
|
|
+ }else{
|
|
+ return SHELL_OPEN_NORMAL;
|
|
+ }
|
|
+ }
|
|
+ n = fread(zBuf, 16, 1, f);
|
|
+ if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){
|
|
+ fclose(f);
|
|
+ return SHELL_OPEN_NORMAL;
|
|
+ }
|
|
+ fseek(f, -25, SEEK_END);
|
|
+ n = fread(zBuf, 25, 1, f);
|
|
+ if( n==1 && memcmp(zBuf, "Start-Of-SQLite3-", 17)==0 ){
|
|
+ rc = SHELL_OPEN_APPENDVFS;
|
|
+ }else{
|
|
+ fseek(f, -22, SEEK_END);
|
|
+ n = fread(zBuf, 22, 1, f);
|
|
+ if( n==1 && zBuf[0]==0x50 && zBuf[1]==0x4b && zBuf[2]==0x05
|
|
+ && zBuf[3]==0x06 ){
|
|
+ rc = SHELL_OPEN_ZIPFILE;
|
|
+ }else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
|
|
+ rc = SHELL_OPEN_ZIPFILE;
|
|
+ }
|
|
+ }
|
|
+ fclose(f);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Flags for open_db().
|
|
+**
|
|
+** The default behavior of open_db() is to exit(1) if the database fails to
|
|
+** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
|
|
+** but still returns without calling exit.
|
|
+**
|
|
+** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a
|
|
+** ZIP archive if the file does not exist or is empty and its name matches
|
|
+** the *.zip pattern.
|
|
+*/
|
|
+#define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */
|
|
+#define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */
|
|
+
|
|
+/*
|
|
** Make sure the database is open. If it is not, then open it. If
|
|
** the database fails to open, print an error message and exit.
|
|
*/
|
|
-static void open_db(ShellState *p, int keepAlive){
|
|
+static void open_db(ShellState *p, int openFlags){
|
|
if( p->db==0 ){
|
|
- sqlite3_initialize();
|
|
- sqlite3_open(p->zDbFilename, &p->db);
|
|
+ if( p->openMode==SHELL_OPEN_UNSPEC ){
|
|
+ if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){
|
|
+ p->openMode = SHELL_OPEN_NORMAL;
|
|
+ }else{
|
|
+ p->openMode = (u8)deduceDatabaseType(p->zDbFilename,
|
|
+ (openFlags & OPEN_DB_ZIPFILE)!=0);
|
|
+ }
|
|
+ }
|
|
+ switch( p->openMode ){
|
|
+ case SHELL_OPEN_APPENDVFS: {
|
|
+ sqlite3_open_v2(p->zDbFilename, &p->db,
|
|
+ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, "apndvfs");
|
|
+ break;
|
|
+ }
|
|
+ case SHELL_OPEN_DESERIALIZE: {
|
|
+ sqlite3_open(0, &p->db);
|
|
+ break;
|
|
+ }
|
|
+ case SHELL_OPEN_ZIPFILE: {
|
|
+ sqlite3_open(":memory:", &p->db);
|
|
+ break;
|
|
+ }
|
|
+ case SHELL_OPEN_READONLY: {
|
|
+ sqlite3_open_v2(p->zDbFilename, &p->db, SQLITE_OPEN_READONLY, 0);
|
|
+ break;
|
|
+ }
|
|
+ case SHELL_OPEN_UNSPEC:
|
|
+ case SHELL_OPEN_NORMAL: {
|
|
+ sqlite3_open(p->zDbFilename, &p->db);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
globalDb = p->db;
|
|
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
|
|
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
|
|
p->zDbFilename, sqlite3_errmsg(p->db));
|
|
- if( keepAlive ) return;
|
|
+ if( openFlags & OPEN_DB_KEEPALIVE ) return;
|
|
exit(1);
|
|
}
|
|
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
@@ -4252,11 +11312,54 @@
|
|
sqlite3_fileio_init(p->db, 0, 0);
|
|
sqlite3_shathree_init(p->db, 0, 0);
|
|
sqlite3_completion_init(p->db, 0, 0);
|
|
- sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0,
|
|
+#ifdef SQLITE_HAVE_ZLIB
|
|
+ sqlite3_zipfile_init(p->db, 0, 0);
|
|
+ sqlite3_sqlar_init(p->db, 0, 0);
|
|
+#endif
|
|
+ sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
|
|
shellAddSchemaName, 0, 0);
|
|
+ sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
|
|
+ shellModuleSchema, 0, 0);
|
|
+ sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
|
|
+ shellPutsFunc, 0, 0);
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
+ sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0,
|
|
+ editFunc, 0, 0);
|
|
+ sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
|
|
+ editFunc, 0, 0);
|
|
+#endif
|
|
+ if( p->openMode==SHELL_OPEN_ZIPFILE ){
|
|
+ char *zSql = sqlite3_mprintf(
|
|
+ "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
|
|
+ sqlite3_exec(p->db, zSql, 0, 0, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ else if( p->openMode==SHELL_OPEN_DESERIALIZE ){
|
|
+ int nData = 0;
|
|
+ unsigned char *aData = (unsigned char*)readFile(p->zDbFilename, &nData);
|
|
+ int rc = sqlite3_deserialize(p->db, "main", aData, nData, nData,
|
|
+ SQLITE_DESERIALIZE_RESIZEABLE |
|
|
+ SQLITE_DESERIALIZE_FREEONCLOSE);
|
|
+ if( rc ){
|
|
+ utf8_printf(stderr, "Error: sqlite3_deserialize() returns %d\n", rc);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+** Attempt to close the databaes connection. Report errors.
|
|
+*/
|
|
+void close_db(sqlite3 *db){
|
|
+ int rc = sqlite3_close(db);
|
|
+ if( rc ){
|
|
+ utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n",
|
|
+ rc, sqlite3_errmsg(db));
|
|
+ }
|
|
+}
|
|
+
|
|
#if HAVE_READLINE || HAVE_EDITLINE
|
|
/*
|
|
** Readline completion callbacks
|
|
@@ -4291,7 +11394,7 @@
|
|
** Linenoise completion callback
|
|
*/
|
|
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
|
|
- int nLine = (int)strlen(zLine);
|
|
+ int nLine = strlen30(zLine);
|
|
int i, iStart;
|
|
sqlite3_stmt *pStmt = 0;
|
|
char *zSql;
|
|
@@ -4298,7 +11401,7 @@
|
|
char zBuf[1000];
|
|
|
|
if( nLine>sizeof(zBuf)-30 ) return;
|
|
- if( zLine[0]=='.' ) return;
|
|
+ if( zLine[0]=='.' || zLine[0]=='#') return;
|
|
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
|
|
if( i==nLine-1 ) return;
|
|
iStart = i+1;
|
|
@@ -4382,63 +11485,6 @@
|
|
}
|
|
|
|
/*
|
|
-** Return the value of a hexadecimal digit. Return -1 if the input
|
|
-** is not a hex digit.
|
|
-*/
|
|
-static int hexDigitValue(char c){
|
|
- if( c>='0' && c<='9' ) return c - '0';
|
|
- if( c>='a' && c<='f' ) return c - 'a' + 10;
|
|
- if( c>='A' && c<='F' ) return c - 'A' + 10;
|
|
- return -1;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Interpret zArg as an integer value, possibly with suffixes.
|
|
-*/
|
|
-static sqlite3_int64 integerValue(const char *zArg){
|
|
- sqlite3_int64 v = 0;
|
|
- static const struct { char *zSuffix; int iMult; } aMult[] = {
|
|
- { "KiB", 1024 },
|
|
- { "MiB", 1024*1024 },
|
|
- { "GiB", 1024*1024*1024 },
|
|
- { "KB", 1000 },
|
|
- { "MB", 1000000 },
|
|
- { "GB", 1000000000 },
|
|
- { "K", 1000 },
|
|
- { "M", 1000000 },
|
|
- { "G", 1000000000 },
|
|
- };
|
|
- int i;
|
|
- int isNeg = 0;
|
|
- if( zArg[0]=='-' ){
|
|
- isNeg = 1;
|
|
- zArg++;
|
|
- }else if( zArg[0]=='+' ){
|
|
- zArg++;
|
|
- }
|
|
- if( zArg[0]=='0' && zArg[1]=='x' ){
|
|
- int x;
|
|
- zArg += 2;
|
|
- while( (x = hexDigitValue(zArg[0]))>=0 ){
|
|
- v = (v<<4) + x;
|
|
- zArg++;
|
|
- }
|
|
- }else{
|
|
- while( IsDigit(zArg[0]) ){
|
|
- v = v*10 + zArg[0] - '0';
|
|
- zArg++;
|
|
- }
|
|
- }
|
|
- for(i=0; i<ArraySize(aMult); i++){
|
|
- if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
|
|
- v *= aMult[i].iMult;
|
|
- break;
|
|
- }
|
|
- }
|
|
- return isNeg? -v : v;
|
|
-}
|
|
-
|
|
-/*
|
|
** Interpret zArg as either an integer or a boolean value. Return 1 or 0
|
|
** for TRUE and FALSE. Return the integer value if appropriate.
|
|
*/
|
|
@@ -4484,7 +11530,7 @@
|
|
** recognized and do the right thing. NULL is returned if the output
|
|
** filename is "off".
|
|
*/
|
|
-static FILE *output_file_open(const char *zFile){
|
|
+static FILE *output_file_open(const char *zFile, int bTextMode){
|
|
FILE *f;
|
|
if( strcmp(zFile,"stdout")==0 ){
|
|
f = stdout;
|
|
@@ -4493,7 +11539,7 @@
|
|
}else if( strcmp(zFile, "off")==0 ){
|
|
f = 0;
|
|
}else{
|
|
- f = fopen(zFile, "wb");
|
|
+ f = fopen(zFile, bTextMode ? "w" : "wb");
|
|
if( f==0 ){
|
|
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
|
|
}
|
|
@@ -4501,7 +11547,6 @@
|
|
return f;
|
|
}
|
|
|
|
-#if !defined(SQLITE_UNTESTABLE)
|
|
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
|
/*
|
|
** A routine for handling output from sqlite3_trace().
|
|
@@ -4517,7 +11562,7 @@
|
|
UNUSED_PARAMETER(pP);
|
|
if( f ){
|
|
const char *z = (const char*)pX;
|
|
- int i = (int)strlen(z);
|
|
+ int i = strlen30(z);
|
|
while( i>0 && z[i-1]==';' ){ i--; }
|
|
utf8_printf(f, "%.*s;\n", i, z);
|
|
}
|
|
@@ -4524,7 +11569,6 @@
|
|
return 0;
|
|
}
|
|
#endif
|
|
-#endif
|
|
|
|
/*
|
|
** A no-op routine that runs with the ".breakpoint" doc-command. This is
|
|
@@ -4557,10 +11601,7 @@
|
|
if( p->n+1>=p->nAlloc ){
|
|
p->nAlloc += p->nAlloc + 100;
|
|
p->z = sqlite3_realloc64(p->z, p->nAlloc);
|
|
- if( p->z==0 ){
|
|
- raw_printf(stderr, "out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ if( p->z==0 ) shell_out_of_memory();
|
|
}
|
|
p->z[p->n++] = (char)c;
|
|
}
|
|
@@ -4706,7 +11747,7 @@
|
|
char *zInsert = 0;
|
|
int rc;
|
|
int i, j, n;
|
|
- int nTable = (int)strlen(zTable);
|
|
+ int nTable = strlen30(zTable);
|
|
int k = 0;
|
|
int cnt = 0;
|
|
const int spinRate = 10000;
|
|
@@ -4721,13 +11762,10 @@
|
|
}
|
|
n = sqlite3_column_count(pQuery);
|
|
zInsert = sqlite3_malloc64(200 + nTable + n*3);
|
|
- if( zInsert==0 ){
|
|
- raw_printf(stderr, "out of memory\n");
|
|
- goto end_data_xfer;
|
|
- }
|
|
+ if( zInsert==0 ) shell_out_of_memory();
|
|
sqlite3_snprintf(200+nTable,zInsert,
|
|
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
|
|
- i = (int)strlen(zInsert);
|
|
+ i = strlen30(zInsert);
|
|
for(j=1; j<n; j++){
|
|
memcpy(zInsert+i, ",?", 2);
|
|
i += 2;
|
|
@@ -4902,11 +11940,15 @@
|
|
sqlite3_exec(newDb, "COMMIT;", 0, 0, 0);
|
|
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
|
|
}
|
|
- sqlite3_close(newDb);
|
|
+ close_db(newDb);
|
|
}
|
|
|
|
/*
|
|
-** Change the output file back to stdout
|
|
+** Change the output file back to stdout.
|
|
+**
|
|
+** If the p->doXdgOpen flag is set, that means the output was being
|
|
+** redirected to a temporary file named by p->zTempFile. In that case,
|
|
+** launch start/open/xdg-open on that temporary file.
|
|
*/
|
|
static void output_reset(ShellState *p){
|
|
if( p->outfile[0]=='|' ){
|
|
@@ -4915,6 +11957,26 @@
|
|
#endif
|
|
}else{
|
|
output_file_close(p->out);
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
+ if( p->doXdgOpen ){
|
|
+ const char *zXdgOpenCmd =
|
|
+#if defined(_WIN32)
|
|
+ "start";
|
|
+#elif defined(__APPLE__)
|
|
+ "open";
|
|
+#else
|
|
+ "xdg-open";
|
|
+#endif
|
|
+ char *zCmd;
|
|
+ zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
|
|
+ if( system(zCmd) ){
|
|
+ utf8_printf(stderr, "Failed: [%s]\n", zCmd);
|
|
+ }
|
|
+ sqlite3_free(zCmd);
|
|
+ outputModePop(p);
|
|
+ p->doXdgOpen = 0;
|
|
+ }
|
|
+#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
|
|
}
|
|
p->outfile[0] = 0;
|
|
p->out = stdout;
|
|
@@ -4976,20 +12038,25 @@
|
|
{ "schema size:",
|
|
"SELECT total(length(sql)) FROM %s" },
|
|
};
|
|
- sqlite3_file *pFile = 0;
|
|
int i;
|
|
+ unsigned iDataVersion;
|
|
char *zSchemaTab;
|
|
char *zDb = nArg>=2 ? azArg[1] : "main";
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
unsigned char aHdr[100];
|
|
open_db(p, 0);
|
|
if( p->db==0 ) return 1;
|
|
- sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile);
|
|
- if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){
|
|
- return 1;
|
|
- }
|
|
- i = pFile->pMethods->xRead(pFile, aHdr, 100, 0);
|
|
- if( i!=SQLITE_OK ){
|
|
+ sqlite3_prepare_v2(p->db,"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
|
|
+ -1, &pStmt, 0);
|
|
+ sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
|
|
+ if( sqlite3_step(pStmt)==SQLITE_ROW
|
|
+ && sqlite3_column_bytes(pStmt,0)>100
|
|
+ ){
|
|
+ memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100);
|
|
+ sqlite3_finalize(pStmt);
|
|
+ }else{
|
|
raw_printf(stderr, "unable to read database header\n");
|
|
+ sqlite3_finalize(pStmt);
|
|
return 1;
|
|
}
|
|
i = get2byteInt(aHdr+16);
|
|
@@ -5025,6 +12092,8 @@
|
|
utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val);
|
|
}
|
|
sqlite3_free(zSchemaTab);
|
|
+ sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
|
|
+ utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
|
|
return 0;
|
|
}
|
|
|
|
@@ -5038,14 +12107,6 @@
|
|
}
|
|
|
|
/*
|
|
-** Print an out-of-memory message to stderr and return 1.
|
|
-*/
|
|
-static int shellNomemError(void){
|
|
- raw_printf(stderr, "Error: out of memory\n");
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/*
|
|
** Compare the pattern in zGlob[] against the text in z[]. Return TRUE
|
|
** if they match and FALSE (0) if they do not match.
|
|
**
|
|
@@ -5169,8 +12230,43 @@
|
|
return rc;
|
|
}
|
|
|
|
+/*
|
|
+** Try to delete the temporary file (if there is one) and free the
|
|
+** memory used to hold the name of the temp file.
|
|
+*/
|
|
+static void clearTempFile(ShellState *p){
|
|
+ if( p->zTempFile==0 ) return;
|
|
+ if( p->doXdgOpen ) return;
|
|
+ if( shellDeleteFile(p->zTempFile) ) return;
|
|
+ sqlite3_free(p->zTempFile);
|
|
+ p->zTempFile = 0;
|
|
+}
|
|
|
|
/*
|
|
+** Create a new temp file name with the given suffix.
|
|
+*/
|
|
+static void newTempFile(ShellState *p, const char *zSuffix){
|
|
+ clearTempFile(p);
|
|
+ sqlite3_free(p->zTempFile);
|
|
+ p->zTempFile = 0;
|
|
+ if( p->db ){
|
|
+ sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile);
|
|
+ }
|
|
+ if( p->zTempFile==0 ){
|
|
+ sqlite3_uint64 r;
|
|
+ sqlite3_randomness(sizeof(r), &r);
|
|
+ p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix);
|
|
+ }else{
|
|
+ p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
|
|
+ }
|
|
+ if( p->zTempFile==0 ){
|
|
+ raw_printf(stderr, "out of memory\n");
|
|
+ exit(1);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
** The implementation of SQL scalar function fkey_collate_clause(), used
|
|
** by the ".lint fkey-indexes" command. This scalar function is always
|
|
** called with four arguments - the parent table name, the parent column name,
|
|
@@ -5246,10 +12342,10 @@
|
|
**
|
|
** 0. The text of an SQL statement similar to:
|
|
**
|
|
- ** "EXPLAIN QUERY PLAN SELECT rowid FROM child_table WHERE child_key=?"
|
|
+ ** "EXPLAIN QUERY PLAN SELECT 1 FROM child_table WHERE child_key=?"
|
|
**
|
|
- ** This is the same SELECT that the foreign keys implementation needs
|
|
- ** to run internally on child tables. If there is an index that can
|
|
+ ** This SELECT is similar to the one that the foreign keys implementation
|
|
+ ** needs to run internally on child tables. If there is an index that can
|
|
** be used to optimize this query, then it can also be used by the FK
|
|
** implementation to optimize DELETE or UPDATE statements on the parent
|
|
** table.
|
|
@@ -5277,7 +12373,7 @@
|
|
*/
|
|
const char *zSql =
|
|
"SELECT "
|
|
- " 'EXPLAIN QUERY PLAN SELECT rowid FROM ' || quote(s.name) || ' WHERE '"
|
|
+ " 'EXPLAIN QUERY PLAN SELECT 1 FROM ' || quote(s.name) || ' WHERE '"
|
|
" || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' "
|
|
" || fkey_collate_clause("
|
|
" f.[table], COALESCE(f.[to], p.[name]), s.name, f.[from]),' AND ')"
|
|
@@ -5305,7 +12401,7 @@
|
|
const char *zGlobIPK = "SEARCH TABLE * USING INTEGER PRIMARY KEY (rowid=?)";
|
|
|
|
for(i=2; i<nArg; i++){
|
|
- int n = (int)strlen(azArg[i]);
|
|
+ int n = strlen30(azArg[i]);
|
|
if( n>1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){
|
|
bVerbose = 1;
|
|
}
|
|
@@ -5408,7 +12504,7 @@
|
|
int nArg /* Number of entries in azArg[] */
|
|
){
|
|
int n;
|
|
- n = (nArg>=2 ? (int)strlen(azArg[1]) : 0);
|
|
+ n = (nArg>=2 ? strlen30(azArg[1]) : 0);
|
|
if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage;
|
|
return lintFkeyIndexes(pState, azArg, nArg);
|
|
|
|
@@ -5419,8 +12515,741 @@
|
|
return SQLITE_ERROR;
|
|
}
|
|
|
|
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
|
+/*********************************************************************************
|
|
+** The ".archive" or ".ar" command.
|
|
+*/
|
|
+static void shellPrepare(
|
|
+ sqlite3 *db,
|
|
+ int *pRc,
|
|
+ const char *zSql,
|
|
+ sqlite3_stmt **ppStmt
|
|
+){
|
|
+ *ppStmt = 0;
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ raw_printf(stderr, "sql error: %s (%d)\n",
|
|
+ sqlite3_errmsg(db), sqlite3_errcode(db)
|
|
+ );
|
|
+ *pRc = rc;
|
|
+ }
|
|
+ }
|
|
+}
|
|
|
|
+static void shellPreparePrintf(
|
|
+ sqlite3 *db,
|
|
+ int *pRc,
|
|
+ sqlite3_stmt **ppStmt,
|
|
+ const char *zFmt,
|
|
+ ...
|
|
+){
|
|
+ *ppStmt = 0;
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ va_list ap;
|
|
+ char *z;
|
|
+ va_start(ap, zFmt);
|
|
+ z = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+ if( z==0 ){
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ shellPrepare(db, pRc, z, ppStmt);
|
|
+ sqlite3_free(z);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void shellFinalize(
|
|
+ int *pRc,
|
|
+ sqlite3_stmt *pStmt
|
|
+){
|
|
+ if( pStmt ){
|
|
+ sqlite3 *db = sqlite3_db_handle(pStmt);
|
|
+ int rc = sqlite3_finalize(pStmt);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
|
+ }
|
|
+ *pRc = rc;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void shellReset(
|
|
+ int *pRc,
|
|
+ sqlite3_stmt *pStmt
|
|
+){
|
|
+ int rc = sqlite3_reset(pStmt);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3 *db = sqlite3_db_handle(pStmt);
|
|
+ raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
|
+ }
|
|
+ *pRc = rc;
|
|
+ }
|
|
+}
|
|
/*
|
|
+** Structure representing a single ".ar" command.
|
|
+*/
|
|
+typedef struct ArCommand ArCommand;
|
|
+struct ArCommand {
|
|
+ u8 eCmd; /* An AR_CMD_* value */
|
|
+ u8 bVerbose; /* True if --verbose */
|
|
+ u8 bZip; /* True if the archive is a ZIP */
|
|
+ u8 bDryRun; /* True if --dry-run */
|
|
+ u8 bAppend; /* True if --append */
|
|
+ u8 fromCmdLine; /* Run from -A instead of .archive */
|
|
+ int nArg; /* Number of command arguments */
|
|
+ char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
|
|
+ const char *zFile; /* --file argument, or NULL */
|
|
+ const char *zDir; /* --directory argument, or NULL */
|
|
+ char **azArg; /* Array of command arguments */
|
|
+ ShellState *p; /* Shell state */
|
|
+ sqlite3 *db; /* Database containing the archive */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
|
|
+*/
|
|
+static int arUsage(FILE *f){
|
|
+ showHelp(f,"archive");
|
|
+ return SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Print an error message for the .ar command to stderr and return
|
|
+** SQLITE_ERROR.
|
|
+*/
|
|
+static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ char *z;
|
|
+ va_start(ap, zFmt);
|
|
+ z = sqlite3_vmprintf(zFmt, ap);
|
|
+ va_end(ap);
|
|
+ utf8_printf(stderr, "Error: %s\n", z);
|
|
+ if( pAr->fromCmdLine ){
|
|
+ utf8_printf(stderr, "Use \"-A\" for more help\n");
|
|
+ }else{
|
|
+ utf8_printf(stderr, "Use \".archive --help\" for more help\n");
|
|
+ }
|
|
+ sqlite3_free(z);
|
|
+ return SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Values for ArCommand.eCmd.
|
|
+*/
|
|
+#define AR_CMD_CREATE 1
|
|
+#define AR_CMD_EXTRACT 2
|
|
+#define AR_CMD_LIST 3
|
|
+#define AR_CMD_UPDATE 4
|
|
+#define AR_CMD_HELP 5
|
|
+
|
|
+/*
|
|
+** Other (non-command) switches.
|
|
+*/
|
|
+#define AR_SWITCH_VERBOSE 6
|
|
+#define AR_SWITCH_FILE 7
|
|
+#define AR_SWITCH_DIRECTORY 8
|
|
+#define AR_SWITCH_APPEND 9
|
|
+#define AR_SWITCH_DRYRUN 10
|
|
+
|
|
+static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
|
|
+ switch( eSwitch ){
|
|
+ case AR_CMD_CREATE:
|
|
+ case AR_CMD_EXTRACT:
|
|
+ case AR_CMD_LIST:
|
|
+ case AR_CMD_UPDATE:
|
|
+ case AR_CMD_HELP:
|
|
+ if( pAr->eCmd ){
|
|
+ return arErrorMsg(pAr, "multiple command options");
|
|
+ }
|
|
+ pAr->eCmd = eSwitch;
|
|
+ break;
|
|
+
|
|
+ case AR_SWITCH_DRYRUN:
|
|
+ pAr->bDryRun = 1;
|
|
+ break;
|
|
+ case AR_SWITCH_VERBOSE:
|
|
+ pAr->bVerbose = 1;
|
|
+ break;
|
|
+ case AR_SWITCH_APPEND:
|
|
+ pAr->bAppend = 1;
|
|
+ /* Fall thru into --file */
|
|
+ case AR_SWITCH_FILE:
|
|
+ pAr->zFile = zArg;
|
|
+ break;
|
|
+ case AR_SWITCH_DIRECTORY:
|
|
+ pAr->zDir = zArg;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parse the command line for an ".ar" command. The results are written into
|
|
+** structure (*pAr). SQLITE_OK is returned if the command line is parsed
|
|
+** successfully, otherwise an error message is written to stderr and
|
|
+** SQLITE_ERROR returned.
|
|
+*/
|
|
+static int arParseCommand(
|
|
+ char **azArg, /* Array of arguments passed to dot command */
|
|
+ int nArg, /* Number of entries in azArg[] */
|
|
+ ArCommand *pAr /* Populate this object */
|
|
+){
|
|
+ struct ArSwitch {
|
|
+ const char *zLong;
|
|
+ char cShort;
|
|
+ u8 eSwitch;
|
|
+ u8 bArg;
|
|
+ } aSwitch[] = {
|
|
+ { "create", 'c', AR_CMD_CREATE, 0 },
|
|
+ { "extract", 'x', AR_CMD_EXTRACT, 0 },
|
|
+ { "list", 't', AR_CMD_LIST, 0 },
|
|
+ { "update", 'u', AR_CMD_UPDATE, 0 },
|
|
+ { "help", 'h', AR_CMD_HELP, 0 },
|
|
+ { "verbose", 'v', AR_SWITCH_VERBOSE, 0 },
|
|
+ { "file", 'f', AR_SWITCH_FILE, 1 },
|
|
+ { "append", 'a', AR_SWITCH_APPEND, 1 },
|
|
+ { "directory", 'C', AR_SWITCH_DIRECTORY, 1 },
|
|
+ { "dryrun", 'n', AR_SWITCH_DRYRUN, 0 },
|
|
+ };
|
|
+ int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
|
|
+ struct ArSwitch *pEnd = &aSwitch[nSwitch];
|
|
+
|
|
+ if( nArg<=1 ){
|
|
+ utf8_printf(stderr, "Wrong number of arguments. Usage:\n");
|
|
+ return arUsage(stderr);
|
|
+ }else{
|
|
+ char *z = azArg[1];
|
|
+ if( z[0]!='-' ){
|
|
+ /* Traditional style [tar] invocation */
|
|
+ int i;
|
|
+ int iArg = 2;
|
|
+ for(i=0; z[i]; i++){
|
|
+ const char *zArg = 0;
|
|
+ struct ArSwitch *pOpt;
|
|
+ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
|
|
+ if( z[i]==pOpt->cShort ) break;
|
|
+ }
|
|
+ if( pOpt==pEnd ){
|
|
+ return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
|
|
+ }
|
|
+ if( pOpt->bArg ){
|
|
+ if( iArg>=nArg ){
|
|
+ return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
|
|
+ }
|
|
+ zArg = azArg[iArg++];
|
|
+ }
|
|
+ if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
|
|
+ }
|
|
+ pAr->nArg = nArg-iArg;
|
|
+ if( pAr->nArg>0 ){
|
|
+ pAr->azArg = &azArg[iArg];
|
|
+ }
|
|
+ }else{
|
|
+ /* Non-traditional invocation */
|
|
+ int iArg;
|
|
+ for(iArg=1; iArg<nArg; iArg++){
|
|
+ int n;
|
|
+ z = azArg[iArg];
|
|
+ if( z[0]!='-' ){
|
|
+ /* All remaining command line words are command arguments. */
|
|
+ pAr->azArg = &azArg[iArg];
|
|
+ pAr->nArg = nArg-iArg;
|
|
+ break;
|
|
+ }
|
|
+ n = strlen30(z);
|
|
+
|
|
+ if( z[1]!='-' ){
|
|
+ int i;
|
|
+ /* One or more short options */
|
|
+ for(i=1; i<n; i++){
|
|
+ const char *zArg = 0;
|
|
+ struct ArSwitch *pOpt;
|
|
+ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
|
|
+ if( z[i]==pOpt->cShort ) break;
|
|
+ }
|
|
+ if( pOpt==pEnd ){
|
|
+ return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
|
|
+ }
|
|
+ if( pOpt->bArg ){
|
|
+ if( i<(n-1) ){
|
|
+ zArg = &z[i+1];
|
|
+ i = n;
|
|
+ }else{
|
|
+ if( iArg>=(nArg-1) ){
|
|
+ return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
|
|
+ }
|
|
+ zArg = azArg[++iArg];
|
|
+ }
|
|
+ }
|
|
+ if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
|
|
+ }
|
|
+ }else if( z[2]=='\0' ){
|
|
+ /* A -- option, indicating that all remaining command line words
|
|
+ ** are command arguments. */
|
|
+ pAr->azArg = &azArg[iArg+1];
|
|
+ pAr->nArg = nArg-iArg-1;
|
|
+ break;
|
|
+ }else{
|
|
+ /* A long option */
|
|
+ const char *zArg = 0; /* Argument for option, if any */
|
|
+ struct ArSwitch *pMatch = 0; /* Matching option */
|
|
+ struct ArSwitch *pOpt; /* Iterator */
|
|
+ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
|
|
+ const char *zLong = pOpt->zLong;
|
|
+ if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
|
|
+ if( pMatch ){
|
|
+ return arErrorMsg(pAr, "ambiguous option: %s",z);
|
|
+ }else{
|
|
+ pMatch = pOpt;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( pMatch==0 ){
|
|
+ return arErrorMsg(pAr, "unrecognized option: %s", z);
|
|
+ }
|
|
+ if( pMatch->bArg ){
|
|
+ if( iArg>=(nArg-1) ){
|
|
+ return arErrorMsg(pAr, "option requires an argument: %s", z);
|
|
+ }
|
|
+ zArg = azArg[++iArg];
|
|
+ }
|
|
+ if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function assumes that all arguments within the ArCommand.azArg[]
|
|
+** array refer to archive members, as for the --extract or --list commands.
|
|
+** It checks that each of them are present. If any specified file is not
|
|
+** present in the archive, an error is printed to stderr and an error
|
|
+** code returned. Otherwise, if all specified arguments are present in
|
|
+** the archive, SQLITE_OK is returned.
|
|
+**
|
|
+** This function strips any trailing '/' characters from each argument.
|
|
+** This is consistent with the way the [tar] command seems to work on
|
|
+** Linux.
|
|
+*/
|
|
+static int arCheckEntries(ArCommand *pAr){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( pAr->nArg ){
|
|
+ int i, j;
|
|
+ sqlite3_stmt *pTest = 0;
|
|
+
|
|
+ shellPreparePrintf(pAr->db, &rc, &pTest,
|
|
+ "SELECT name FROM %s WHERE name=$name",
|
|
+ pAr->zSrcTable
|
|
+ );
|
|
+ j = sqlite3_bind_parameter_index(pTest, "$name");
|
|
+ for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
|
|
+ char *z = pAr->azArg[i];
|
|
+ int n = strlen30(z);
|
|
+ int bOk = 0;
|
|
+ while( n>0 && z[n-1]=='/' ) n--;
|
|
+ z[n] = '\0';
|
|
+ sqlite3_bind_text(pTest, j, z, -1, SQLITE_STATIC);
|
|
+ if( SQLITE_ROW==sqlite3_step(pTest) ){
|
|
+ bOk = 1;
|
|
+ }
|
|
+ shellReset(&rc, pTest);
|
|
+ if( rc==SQLITE_OK && bOk==0 ){
|
|
+ utf8_printf(stderr, "not found in archive: %s\n", z);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+ shellFinalize(&rc, pTest);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Format a WHERE clause that can be used against the "sqlar" table to
|
|
+** identify all archive members that match the command arguments held
|
|
+** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
|
|
+** The caller is responsible for eventually calling sqlite3_free() on
|
|
+** any non-NULL (*pzWhere) value.
|
|
+*/
|
|
+static void arWhereClause(
|
|
+ int *pRc,
|
|
+ ArCommand *pAr,
|
|
+ char **pzWhere /* OUT: New WHERE clause */
|
|
+){
|
|
+ char *zWhere = 0;
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ if( pAr->nArg==0 ){
|
|
+ zWhere = sqlite3_mprintf("1");
|
|
+ }else{
|
|
+ int i;
|
|
+ const char *zSep = "";
|
|
+ for(i=0; i<pAr->nArg; i++){
|
|
+ const char *z = pAr->azArg[i];
|
|
+ zWhere = sqlite3_mprintf(
|
|
+ "%z%s name = '%q' OR substr(name,1,%d) = '%q/'",
|
|
+ zWhere, zSep, z, strlen30(z)+1, z
|
|
+ );
|
|
+ if( zWhere==0 ){
|
|
+ *pRc = SQLITE_NOMEM;
|
|
+ break;
|
|
+ }
|
|
+ zSep = " OR ";
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ *pzWhere = zWhere;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of .ar "lisT" command.
|
|
+*/
|
|
+static int arListCommand(ArCommand *pAr){
|
|
+ const char *zSql = "SELECT %s FROM %s WHERE %s";
|
|
+ const char *azCols[] = {
|
|
+ "name",
|
|
+ "lsmode(mode), sz, datetime(mtime, 'unixepoch'), name"
|
|
+ };
|
|
+
|
|
+ char *zWhere = 0;
|
|
+ sqlite3_stmt *pSql = 0;
|
|
+ int rc;
|
|
+
|
|
+ rc = arCheckEntries(pAr);
|
|
+ arWhereClause(&rc, pAr, &zWhere);
|
|
+
|
|
+ shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose],
|
|
+ pAr->zSrcTable, zWhere);
|
|
+ if( pAr->bDryRun ){
|
|
+ utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql));
|
|
+ }else{
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
|
+ if( pAr->bVerbose ){
|
|
+ utf8_printf(pAr->p->out, "%s % 10d %s %s\n",
|
|
+ sqlite3_column_text(pSql, 0),
|
|
+ sqlite3_column_int(pSql, 1),
|
|
+ sqlite3_column_text(pSql, 2),
|
|
+ sqlite3_column_text(pSql, 3)
|
|
+ );
|
|
+ }else{
|
|
+ utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ shellFinalize(&rc, pSql);
|
|
+ sqlite3_free(zWhere);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Implementation of .ar "eXtract" command.
|
|
+*/
|
|
+static int arExtractCommand(ArCommand *pAr){
|
|
+ const char *zSql1 =
|
|
+ "SELECT "
|
|
+ " ($dir || name),"
|
|
+ " writefile(($dir || name), %s, mode, mtime) "
|
|
+ "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)"
|
|
+ " AND name NOT GLOB '*..[/\\]*'";
|
|
+
|
|
+ const char *azExtraArg[] = {
|
|
+ "sqlar_uncompress(data, sz)",
|
|
+ "data"
|
|
+ };
|
|
+
|
|
+ sqlite3_stmt *pSql = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zDir = 0;
|
|
+ char *zWhere = 0;
|
|
+ int i, j;
|
|
+
|
|
+ /* If arguments are specified, check that they actually exist within
|
|
+ ** the archive before proceeding. And formulate a WHERE clause to
|
|
+ ** match them. */
|
|
+ rc = arCheckEntries(pAr);
|
|
+ arWhereClause(&rc, pAr, &zWhere);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pAr->zDir ){
|
|
+ zDir = sqlite3_mprintf("%s/", pAr->zDir);
|
|
+ }else{
|
|
+ zDir = sqlite3_mprintf("");
|
|
+ }
|
|
+ if( zDir==0 ) rc = SQLITE_NOMEM;
|
|
+ }
|
|
+
|
|
+ shellPreparePrintf(pAr->db, &rc, &pSql, zSql1,
|
|
+ azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere
|
|
+ );
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ j = sqlite3_bind_parameter_index(pSql, "$dir");
|
|
+ sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC);
|
|
+
|
|
+ /* Run the SELECT statement twice. The first time, writefile() is called
|
|
+ ** for all archive members that should be extracted. The second time,
|
|
+ ** only for the directories. This is because the timestamps for
|
|
+ ** extracted directories must be reset after they are populated (as
|
|
+ ** populating them changes the timestamp). */
|
|
+ for(i=0; i<2; i++){
|
|
+ j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
|
|
+ sqlite3_bind_int(pSql, j, i);
|
|
+ if( pAr->bDryRun ){
|
|
+ utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql));
|
|
+ }else{
|
|
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
|
+ if( i==0 && pAr->bVerbose ){
|
|
+ utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ shellReset(&rc, pSql);
|
|
+ }
|
|
+ shellFinalize(&rc, pSql);
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zDir);
|
|
+ sqlite3_free(zWhere);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out.
|
|
+*/
|
|
+static int arExecSql(ArCommand *pAr, const char *zSql){
|
|
+ int rc;
|
|
+ if( pAr->bDryRun ){
|
|
+ utf8_printf(pAr->p->out, "%s\n", zSql);
|
|
+ rc = SQLITE_OK;
|
|
+ }else{
|
|
+ char *zErr = 0;
|
|
+ rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
|
|
+ if( zErr ){
|
|
+ utf8_printf(stdout, "ERROR: %s\n", zErr);
|
|
+ sqlite3_free(zErr);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Implementation of .ar "create" and "update" commands.
|
|
+**
|
|
+** Create the "sqlar" table in the database if it does not already exist.
|
|
+** Then add each file in the azFile[] array to the archive. Directories
|
|
+** are added recursively. If argument bVerbose is non-zero, a message is
|
|
+** printed on stdout for each file archived.
|
|
+**
|
|
+** The create command is the same as update, except that it drops
|
|
+** any existing "sqlar" table before beginning.
|
|
+*/
|
|
+static int arCreateOrUpdateCommand(
|
|
+ ArCommand *pAr, /* Command arguments and options */
|
|
+ int bUpdate /* true for a --create. false for --update */
|
|
+){
|
|
+ const char *zCreate =
|
|
+ "CREATE TABLE IF NOT EXISTS sqlar(\n"
|
|
+ " name TEXT PRIMARY KEY, -- name of the file\n"
|
|
+ " mode INT, -- access permissions\n"
|
|
+ " mtime INT, -- last modification time\n"
|
|
+ " sz INT, -- original file size\n"
|
|
+ " data BLOB -- compressed content\n"
|
|
+ ")";
|
|
+ const char *zDrop = "DROP TABLE IF EXISTS sqlar";
|
|
+ const char *zInsertFmt[2] = {
|
|
+ "REPLACE INTO %s(name,mode,mtime,sz,data)\n"
|
|
+ " SELECT\n"
|
|
+ " %s,\n"
|
|
+ " mode,\n"
|
|
+ " mtime,\n"
|
|
+ " CASE substr(lsmode(mode),1,1)\n"
|
|
+ " WHEN '-' THEN length(data)\n"
|
|
+ " WHEN 'd' THEN 0\n"
|
|
+ " ELSE -1 END,\n"
|
|
+ " sqlar_compress(data)\n"
|
|
+ " FROM fsdir(%Q,%Q)\n"
|
|
+ " WHERE lsmode(mode) NOT LIKE '?%%';",
|
|
+ "REPLACE INTO %s(name,mode,mtime,data)\n"
|
|
+ " SELECT\n"
|
|
+ " %s,\n"
|
|
+ " mode,\n"
|
|
+ " mtime,\n"
|
|
+ " data\n"
|
|
+ " FROM fsdir(%Q,%Q)\n"
|
|
+ " WHERE lsmode(mode) NOT LIKE '?%%';"
|
|
+ };
|
|
+ int i; /* For iterating through azFile[] */
|
|
+ int rc; /* Return code */
|
|
+ const char *zTab = 0; /* SQL table into which to insert */
|
|
+ char *zSql;
|
|
+ char zTemp[50];
|
|
+
|
|
+ arExecSql(pAr, "PRAGMA page_size=512");
|
|
+ rc = arExecSql(pAr, "SAVEPOINT ar;");
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ zTemp[0] = 0;
|
|
+ if( pAr->bZip ){
|
|
+ /* Initialize the zipfile virtual table, if necessary */
|
|
+ if( pAr->zFile ){
|
|
+ sqlite3_uint64 r;
|
|
+ sqlite3_randomness(sizeof(r),&r);
|
|
+ sqlite3_snprintf(sizeof(zTemp),zTemp,"zip%016llx",r);
|
|
+ zTab = zTemp;
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "CREATE VIRTUAL TABLE temp.%s USING zipfile(%Q)",
|
|
+ zTab, pAr->zFile
|
|
+ );
|
|
+ rc = arExecSql(pAr, zSql);
|
|
+ sqlite3_free(zSql);
|
|
+ }else{
|
|
+ zTab = "zip";
|
|
+ }
|
|
+ }else{
|
|
+ /* Initialize the table for an SQLAR */
|
|
+ zTab = "sqlar";
|
|
+ if( bUpdate==0 ){
|
|
+ rc = arExecSql(pAr, zDrop);
|
|
+ if( rc!=SQLITE_OK ) goto end_ar_transaction;
|
|
+ }
|
|
+ rc = arExecSql(pAr, zCreate);
|
|
+ }
|
|
+ for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
|
|
+ char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab,
|
|
+ pAr->bVerbose ? "shell_putsnl(name)" : "name",
|
|
+ pAr->azArg[i], pAr->zDir);
|
|
+ rc = arExecSql(pAr, zSql2);
|
|
+ sqlite3_free(zSql2);
|
|
+ }
|
|
+end_ar_transaction:
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;");
|
|
+ }else{
|
|
+ rc = arExecSql(pAr, "RELEASE ar;");
|
|
+ if( pAr->bZip && pAr->zFile ){
|
|
+ zSql = sqlite3_mprintf("DROP TABLE %s", zTemp);
|
|
+ arExecSql(pAr, zSql);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of ".ar" dot command.
|
|
+*/
|
|
+static int arDotCommand(
|
|
+ ShellState *pState, /* Current shell tool state */
|
|
+ int fromCmdLine, /* True if -A command-line option, not .ar cmd */
|
|
+ char **azArg, /* Array of arguments passed to dot command */
|
|
+ int nArg /* Number of entries in azArg[] */
|
|
+){
|
|
+ ArCommand cmd;
|
|
+ int rc;
|
|
+ memset(&cmd, 0, sizeof(cmd));
|
|
+ cmd.fromCmdLine = fromCmdLine;
|
|
+ rc = arParseCommand(azArg, nArg, &cmd);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int eDbType = SHELL_OPEN_UNSPEC;
|
|
+ cmd.p = pState;
|
|
+ cmd.db = pState->db;
|
|
+ if( cmd.zFile ){
|
|
+ eDbType = deduceDatabaseType(cmd.zFile, 1);
|
|
+ }else{
|
|
+ eDbType = pState->openMode;
|
|
+ }
|
|
+ if( eDbType==SHELL_OPEN_ZIPFILE ){
|
|
+ if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){
|
|
+ if( cmd.zFile==0 ){
|
|
+ cmd.zSrcTable = sqlite3_mprintf("zip");
|
|
+ }else{
|
|
+ cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile);
|
|
+ }
|
|
+ }
|
|
+ cmd.bZip = 1;
|
|
+ }else if( cmd.zFile ){
|
|
+ int flags;
|
|
+ if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
|
|
+ if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){
|
|
+ flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
|
+ }else{
|
|
+ flags = SQLITE_OPEN_READONLY;
|
|
+ }
|
|
+ cmd.db = 0;
|
|
+ if( cmd.bDryRun ){
|
|
+ utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile,
|
|
+ eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
|
|
+ }
|
|
+ rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
|
|
+ eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ utf8_printf(stderr, "cannot open file: %s (%s)\n",
|
|
+ cmd.zFile, sqlite3_errmsg(cmd.db)
|
|
+ );
|
|
+ goto end_ar_command;
|
|
+ }
|
|
+ sqlite3_fileio_init(cmd.db, 0, 0);
|
|
+ sqlite3_sqlar_init(cmd.db, 0, 0);
|
|
+ sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p,
|
|
+ shellPutsFunc, 0, 0);
|
|
+
|
|
+ }
|
|
+ if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){
|
|
+ if( cmd.eCmd!=AR_CMD_CREATE
|
|
+ && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
|
|
+ ){
|
|
+ utf8_printf(stderr, "database does not contain an 'sqlar' table\n");
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto end_ar_command;
|
|
+ }
|
|
+ cmd.zSrcTable = sqlite3_mprintf("sqlar");
|
|
+ }
|
|
+
|
|
+ switch( cmd.eCmd ){
|
|
+ case AR_CMD_CREATE:
|
|
+ rc = arCreateOrUpdateCommand(&cmd, 0);
|
|
+ break;
|
|
+
|
|
+ case AR_CMD_EXTRACT:
|
|
+ rc = arExtractCommand(&cmd);
|
|
+ break;
|
|
+
|
|
+ case AR_CMD_LIST:
|
|
+ rc = arListCommand(&cmd);
|
|
+ break;
|
|
+
|
|
+ case AR_CMD_HELP:
|
|
+ arUsage(pState->out);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ assert( cmd.eCmd==AR_CMD_UPDATE );
|
|
+ rc = arCreateOrUpdateCommand(&cmd, 1);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+end_ar_command:
|
|
+ if( cmd.db!=pState->db ){
|
|
+ close_db(cmd.db);
|
|
+ }
|
|
+ sqlite3_free(cmd.zSrcTable);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+/* End of the ".archive" or ".ar" command logic
|
|
+**********************************************************************************/
|
|
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
|
|
+
|
|
+
|
|
+/*
|
|
** If an input line begins with "." then invoke this routine to
|
|
** process that line.
|
|
**
|
|
@@ -5433,6 +13262,12 @@
|
|
int rc = 0;
|
|
char *azArg[50];
|
|
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( p->expert.pExpert ){
|
|
+ expertFinish(p, 1, 0);
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* Parse the input line into tokens.
|
|
*/
|
|
while( zLine[h] && nArg<ArraySize(azArg) ){
|
|
@@ -5462,6 +13297,7 @@
|
|
if( nArg==0 ) return 0; /* no tokens, no error */
|
|
n = strlen30(azArg[0]);
|
|
c = azArg[0][0];
|
|
+ clearTempFile(p);
|
|
|
|
#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
if( c=='a' && strncmp(azArg[0], "auth", n)==0 ){
|
|
@@ -5479,6 +13315,13 @@
|
|
}else
|
|
#endif
|
|
|
|
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
|
+ if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
|
|
+ open_db(p, 0);
|
|
+ rc = arDotCommand(p, 0, azArg, nArg);
|
|
+ }else
|
|
+#endif
|
|
+
|
|
if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
|
|
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
|
|
){
|
|
@@ -5487,11 +13330,14 @@
|
|
sqlite3 *pDest;
|
|
sqlite3_backup *pBackup;
|
|
int j;
|
|
+ const char *zVfs = 0;
|
|
for(j=1; j<nArg; j++){
|
|
const char *z = azArg[j];
|
|
if( z[0]=='-' ){
|
|
- while( z[0]=='-' ) z++;
|
|
- /* No options to process at this time */
|
|
+ if( z[1]=='-' ) z++;
|
|
+ if( strcmp(z, "-append")==0 ){
|
|
+ zVfs = "apndvfs";
|
|
+ }else
|
|
{
|
|
utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
|
|
return 1;
|
|
@@ -5502,7 +13348,7 @@
|
|
zDb = zDestFile;
|
|
zDestFile = azArg[j];
|
|
}else{
|
|
- raw_printf(stderr, "too many arguments to .backup\n");
|
|
+ raw_printf(stderr, "Usage: .backup ?DB? ?--append? FILENAME\n");
|
|
return 1;
|
|
}
|
|
}
|
|
@@ -5511,10 +13357,11 @@
|
|
return 1;
|
|
}
|
|
if( zDb==0 ) zDb = "main";
|
|
- rc = sqlite3_open(zDestFile, &pDest);
|
|
+ rc = sqlite3_open_v2(zDestFile, &pDest,
|
|
+ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
|
|
if( rc!=SQLITE_OK ){
|
|
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
|
|
- sqlite3_close(pDest);
|
|
+ close_db(pDest);
|
|
return 1;
|
|
}
|
|
open_db(p, 0);
|
|
@@ -5521,7 +13368,7 @@
|
|
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
|
|
if( pBackup==0 ){
|
|
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
|
|
- sqlite3_close(pDest);
|
|
+ close_db(pDest);
|
|
return 1;
|
|
}
|
|
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
|
|
@@ -5532,7 +13379,7 @@
|
|
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
|
|
rc = 1;
|
|
}
|
|
- sqlite3_close(pDest);
|
|
+ close_db(pDest);
|
|
}else
|
|
|
|
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
|
|
@@ -5609,7 +13456,7 @@
|
|
utf8_printf(stderr,
|
|
"testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n",
|
|
p->zTestcase, azArg[1], zRes);
|
|
- rc = 2;
|
|
+ rc = 1;
|
|
}else{
|
|
utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase);
|
|
p->nCheck++;
|
|
@@ -5644,7 +13491,39 @@
|
|
}
|
|
}else
|
|
|
|
- if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){
|
|
+ if( c=='d' && n>=3 && strncmp(azArg[0], "dbconfig", n)==0 ){
|
|
+ static const struct DbConfigChoices {
|
|
+ const char *zName;
|
|
+ int op;
|
|
+ } aDbConfig[] = {
|
|
+ { "enable_fkey", SQLITE_DBCONFIG_ENABLE_FKEY },
|
|
+ { "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER },
|
|
+ { "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER },
|
|
+ { "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
|
|
+ { "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE },
|
|
+ { "enable_qpsg", SQLITE_DBCONFIG_ENABLE_QPSG },
|
|
+ { "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP },
|
|
+ { "reset_database", SQLITE_DBCONFIG_RESET_DATABASE },
|
|
+ { "defensive", SQLITE_DBCONFIG_DEFENSIVE },
|
|
+ };
|
|
+ int ii, v;
|
|
+ open_db(p, 0);
|
|
+ for(ii=0; ii<ArraySize(aDbConfig); ii++){
|
|
+ if( nArg>1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
|
|
+ if( nArg>=3 ){
|
|
+ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
|
|
+ }
|
|
+ sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v);
|
|
+ utf8_printf(p->out, "%18s %s\n", aDbConfig[ii].zName, v ? "on" : "off");
|
|
+ if( nArg>1 ) break;
|
|
+ }
|
|
+ if( nArg>1 && ii==ArraySize(aDbConfig) ){
|
|
+ utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]);
|
|
+ utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n");
|
|
+ }
|
|
+ }else
|
|
+
|
|
+ if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){
|
|
rc = shell_dbinfo_command(p, nArg, azArg);
|
|
}else
|
|
|
|
@@ -5652,7 +13531,8 @@
|
|
const char *zLike = 0;
|
|
int i;
|
|
int savedShowHeader = p->showHeader;
|
|
- ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines);
|
|
+ int savedShellFlags = p->shellFlgs;
|
|
+ ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
|
|
for(i=1; i<nArg; i++){
|
|
if( azArg[i][0]=='-' ){
|
|
const char *z = azArg[i]+1;
|
|
@@ -5734,6 +13614,7 @@
|
|
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
|
|
raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
|
|
p->showHeader = savedShowHeader;
|
|
+ p->shellFlgs = savedShellFlags;
|
|
}else
|
|
|
|
if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){
|
|
@@ -5747,13 +13628,19 @@
|
|
|
|
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
|
|
if( nArg==2 ){
|
|
+ p->autoEQPtest = 0;
|
|
if( strcmp(azArg[1],"full")==0 ){
|
|
- p->autoEQP = 2;
|
|
+ p->autoEQP = AUTOEQP_full;
|
|
+ }else if( strcmp(azArg[1],"trigger")==0 ){
|
|
+ p->autoEQP = AUTOEQP_trigger;
|
|
+ }else if( strcmp(azArg[1],"test")==0 ){
|
|
+ p->autoEQP = AUTOEQP_on;
|
|
+ p->autoEQPtest = 1;
|
|
}else{
|
|
- p->autoEQP = booleanValue(azArg[1]);
|
|
+ p->autoEQP = (u8)booleanValue(azArg[1]);
|
|
}
|
|
}else{
|
|
- raw_printf(stderr, "Usage: .eqp on|off|full\n");
|
|
+ raw_printf(stderr, "Usage: .eqp off|on|trigger|full\n");
|
|
rc = 1;
|
|
}
|
|
}else
|
|
@@ -5787,6 +13674,13 @@
|
|
}
|
|
}else
|
|
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( c=='e' && strncmp(azArg[0], "expert", n)==0 ){
|
|
+ open_db(p, 0);
|
|
+ expertDotCommand(p, azArg, nArg);
|
|
+ }else
|
|
+#endif
|
|
+
|
|
if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){
|
|
ShellState data;
|
|
char *zErrMsg = 0;
|
|
@@ -5830,14 +13724,11 @@
|
|
callback, &data, &zErrMsg);
|
|
data.cMode = data.mode = MODE_Insert;
|
|
data.zDestTable = "sqlite_stat1";
|
|
- shell_exec(p->db, "SELECT * FROM sqlite_stat1",
|
|
- shell_callback, &data,&zErrMsg);
|
|
+ shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg);
|
|
data.zDestTable = "sqlite_stat3";
|
|
- shell_exec(p->db, "SELECT * FROM sqlite_stat3",
|
|
- shell_callback, &data,&zErrMsg);
|
|
+ shell_exec(&data, "SELECT * FROM sqlite_stat3", &zErrMsg);
|
|
data.zDestTable = "sqlite_stat4";
|
|
- shell_exec(p->db, "SELECT * FROM sqlite_stat4",
|
|
- shell_callback, &data, &zErrMsg);
|
|
+ shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg);
|
|
raw_printf(p->out, "ANALYZE sqlite_master;\n");
|
|
}
|
|
}else
|
|
@@ -5852,7 +13743,14 @@
|
|
}else
|
|
|
|
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
|
- utf8_printf(p->out, "%s", zHelp);
|
|
+ if( nArg>=2 ){
|
|
+ n = showHelp(p->out, azArg[1]);
|
|
+ if( n==0 ){
|
|
+ utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
|
|
+ }
|
|
+ }else{
|
|
+ showHelp(p->out, 0);
|
|
+ }
|
|
}else
|
|
|
|
if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
|
|
@@ -5935,9 +13833,8 @@
|
|
sCtx.cRowSep = p->rowSeparator[0];
|
|
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
|
|
if( zSql==0 ){
|
|
- raw_printf(stderr, "Error: out of memory\n");
|
|
xCloser(sCtx.in);
|
|
- return 1;
|
|
+ shell_out_of_memory();
|
|
}
|
|
nByte = strlen30(zSql);
|
|
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
|
@@ -5982,9 +13879,8 @@
|
|
if( nCol==0 ) return 0; /* no columns, no error */
|
|
zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 );
|
|
if( zSql==0 ){
|
|
- raw_printf(stderr, "Error: out of memory\n");
|
|
xCloser(sCtx.in);
|
|
- return 1;
|
|
+ shell_out_of_memory();
|
|
}
|
|
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
|
|
j = strlen30(zSql);
|
|
@@ -6060,12 +13956,17 @@
|
|
sqlite3_stmt *pStmt;
|
|
int tnum = 0;
|
|
int i;
|
|
- if( nArg!=3 ){
|
|
- utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n");
|
|
+ if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
|
|
+ utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"
|
|
+ " .imposter off\n");
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
open_db(p, 0);
|
|
+ if( nArg==2 ){
|
|
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1);
|
|
+ goto meta_command_exit;
|
|
+ }
|
|
zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master"
|
|
" WHERE name='%q' AND type='index'", azArg[1]);
|
|
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
|
@@ -6241,13 +14142,13 @@
|
|
}else{
|
|
const char *zFile = azArg[1];
|
|
output_file_close(p->pLog);
|
|
- p->pLog = output_file_open(zFile);
|
|
+ p->pLog = output_file_open(zFile, 0);
|
|
}
|
|
}else
|
|
|
|
if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
|
|
const char *zMode = nArg>=2 ? azArg[1] : "";
|
|
- int n2 = (int)strlen(zMode);
|
|
+ int n2 = strlen30(zMode);
|
|
int c2 = zMode[0];
|
|
if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
|
|
p->mode = MODE_Line;
|
|
@@ -6307,16 +14208,29 @@
|
|
int newFlag = 0; /* True to delete file before opening */
|
|
/* Close the existing database */
|
|
session_close_all(p);
|
|
- sqlite3_close(p->db);
|
|
+ close_db(p->db);
|
|
p->db = 0;
|
|
p->zDbFilename = 0;
|
|
sqlite3_free(p->zFreeOnClose);
|
|
p->zFreeOnClose = 0;
|
|
+ p->openMode = SHELL_OPEN_UNSPEC;
|
|
/* Check for command-line arguments */
|
|
for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){
|
|
const char *z = azArg[iName];
|
|
if( optionMatch(z,"new") ){
|
|
newFlag = 1;
|
|
+#ifdef SQLITE_HAVE_ZLIB
|
|
+ }else if( optionMatch(z, "zip") ){
|
|
+ p->openMode = SHELL_OPEN_ZIPFILE;
|
|
+#endif
|
|
+ }else if( optionMatch(z, "append") ){
|
|
+ p->openMode = SHELL_OPEN_APPENDVFS;
|
|
+ }else if( optionMatch(z, "readonly") ){
|
|
+ p->openMode = SHELL_OPEN_READONLY;
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ }else if( optionMatch(z, "deserialize") ){
|
|
+ p->openMode = SHELL_OPEN_DESERIALIZE;
|
|
+#endif
|
|
}else if( z[0]=='-' ){
|
|
utf8_printf(stderr, "unknown option: %s\n", z);
|
|
rc = 1;
|
|
@@ -6328,7 +14242,7 @@
|
|
if( zNewFilename ){
|
|
if( newFlag ) shellDeleteFile(zNewFilename);
|
|
p->zDbFilename = zNewFilename;
|
|
- open_db(p, 1);
|
|
+ open_db(p, OPEN_DB_KEEPALIVE);
|
|
if( p->db==0 ){
|
|
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
|
|
sqlite3_free(zNewFilename);
|
|
@@ -6343,18 +14257,27 @@
|
|
}
|
|
}else
|
|
|
|
- if( c=='o'
|
|
- && (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0)
|
|
+ if( (c=='o'
|
|
+ && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
|
|
+ || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
|
|
){
|
|
const char *zFile = nArg>=2 ? azArg[1] : "stdout";
|
|
+ int bTxtMode = 0;
|
|
+ if( azArg[0][0]=='e' ){
|
|
+ /* Transform the ".excel" command into ".once -x" */
|
|
+ nArg = 2;
|
|
+ azArg[0] = "once";
|
|
+ zFile = azArg[1] = "-x";
|
|
+ n = 4;
|
|
+ }
|
|
if( nArg>2 ){
|
|
- utf8_printf(stderr, "Usage: .%s FILE\n", azArg[0]);
|
|
+ utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]);
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
if( n>1 && strncmp(azArg[0], "once", n)==0 ){
|
|
if( nArg<2 ){
|
|
- raw_printf(stderr, "Usage: .once FILE\n");
|
|
+ raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n");
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
@@ -6363,6 +14286,23 @@
|
|
p->outCount = 0;
|
|
}
|
|
output_reset(p);
|
|
+ if( zFile[0]=='-' && zFile[1]=='-' ) zFile++;
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
+ if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){
|
|
+ p->doXdgOpen = 1;
|
|
+ outputModePush(p);
|
|
+ if( zFile[1]=='x' ){
|
|
+ newTempFile(p, "csv");
|
|
+ p->mode = MODE_Csv;
|
|
+ sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
|
|
+ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
|
|
+ }else{
|
|
+ newTempFile(p, "txt");
|
|
+ bTxtMode = 1;
|
|
+ }
|
|
+ zFile = p->zTempFile;
|
|
+ }
|
|
+#endif /* SQLITE_NOHAVE_SYSTEM */
|
|
if( zFile[0]=='|' ){
|
|
#ifdef SQLITE_OMIT_POPEN
|
|
raw_printf(stderr, "Error: pipes are not supported in this OS\n");
|
|
@@ -6379,7 +14319,7 @@
|
|
}
|
|
#endif
|
|
}else{
|
|
- p->out = output_file_open(zFile);
|
|
+ p->out = output_file_open(zFile, bTxtMode);
|
|
if( p->out==0 ){
|
|
if( strcmp(zFile,"off")!=0 ){
|
|
utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile);
|
|
@@ -6452,7 +14392,7 @@
|
|
rc = sqlite3_open(zSrcFile, &pSrc);
|
|
if( rc!=SQLITE_OK ){
|
|
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile);
|
|
- sqlite3_close(pSrc);
|
|
+ close_db(pSrc);
|
|
return 1;
|
|
}
|
|
open_db(p, 0);
|
|
@@ -6459,7 +14399,7 @@
|
|
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
|
|
if( pBackup==0 ){
|
|
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
|
- sqlite3_close(pSrc);
|
|
+ close_db(pSrc);
|
|
return 1;
|
|
}
|
|
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
|
|
@@ -6479,13 +14419,12 @@
|
|
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
|
rc = 1;
|
|
}
|
|
- sqlite3_close(pSrc);
|
|
+ close_db(pSrc);
|
|
}else
|
|
|
|
-
|
|
if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
|
|
if( nArg==2 ){
|
|
- p->scanstatsOn = booleanValue(azArg[1]);
|
|
+ p->scanstatsOn = (u8)booleanValue(azArg[1]);
|
|
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
|
|
raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
|
|
#endif
|
|
@@ -6499,8 +14438,11 @@
|
|
ShellText sSelect;
|
|
ShellState data;
|
|
char *zErrMsg = 0;
|
|
- const char *zDiv = 0;
|
|
+ const char *zDiv = "(";
|
|
+ const char *zName = 0;
|
|
int iSchema = 0;
|
|
+ int bDebug = 0;
|
|
+ int ii;
|
|
|
|
open_db(p, 0);
|
|
memcpy(&data, p, sizeof(data));
|
|
@@ -6507,51 +14449,37 @@
|
|
data.showHeader = 0;
|
|
data.cMode = data.mode = MODE_Semi;
|
|
initText(&sSelect);
|
|
- if( nArg>=2 && optionMatch(azArg[1], "indent") ){
|
|
- data.cMode = data.mode = MODE_Pretty;
|
|
- nArg--;
|
|
- if( nArg==2 ) azArg[1] = azArg[2];
|
|
+ for(ii=1; ii<nArg; ii++){
|
|
+ if( optionMatch(azArg[ii],"indent") ){
|
|
+ data.cMode = data.mode = MODE_Pretty;
|
|
+ }else if( optionMatch(azArg[ii],"debug") ){
|
|
+ bDebug = 1;
|
|
+ }else if( zName==0 ){
|
|
+ zName = azArg[ii];
|
|
+ }else{
|
|
+ raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n");
|
|
+ rc = 1;
|
|
+ goto meta_command_exit;
|
|
+ }
|
|
}
|
|
- if( nArg==2 && azArg[1][0]!='-' ){
|
|
- int i;
|
|
- for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
|
|
- if( strcmp(azArg[1],"sqlite_master")==0 ){
|
|
+ if( zName!=0 ){
|
|
+ int isMaster = sqlite3_strlike(zName, "sqlite_master", '\\')==0;
|
|
+ if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 ){
|
|
char *new_argv[2], *new_colv[2];
|
|
- new_argv[0] = "CREATE TABLE sqlite_master (\n"
|
|
+ new_argv[0] = sqlite3_mprintf(
|
|
+ "CREATE TABLE %s (\n"
|
|
" type text,\n"
|
|
" name text,\n"
|
|
" tbl_name text,\n"
|
|
" rootpage integer,\n"
|
|
" sql text\n"
|
|
- ")";
|
|
+ ")", isMaster ? "sqlite_master" : "sqlite_temp_master");
|
|
new_argv[1] = 0;
|
|
new_colv[0] = "sql";
|
|
new_colv[1] = 0;
|
|
callback(&data, 1, new_argv, new_colv);
|
|
- rc = SQLITE_OK;
|
|
- }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){
|
|
- char *new_argv[2], *new_colv[2];
|
|
- new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
|
|
- " type text,\n"
|
|
- " name text,\n"
|
|
- " tbl_name text,\n"
|
|
- " rootpage integer,\n"
|
|
- " sql text\n"
|
|
- ")";
|
|
- new_argv[1] = 0;
|
|
- new_colv[0] = "sql";
|
|
- new_colv[1] = 0;
|
|
- callback(&data, 1, new_argv, new_colv);
|
|
- rc = SQLITE_OK;
|
|
- }else{
|
|
- zDiv = "(";
|
|
+ sqlite3_free(new_argv[0]);
|
|
}
|
|
- }else if( nArg==1 ){
|
|
- zDiv = "(";
|
|
- }else{
|
|
- raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n");
|
|
- rc = 1;
|
|
- goto meta_command_exit;
|
|
}
|
|
if( zDiv ){
|
|
sqlite3_stmt *pStmt = 0;
|
|
@@ -6571,39 +14499,53 @@
|
|
sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema);
|
|
appendText(&sSelect, zDiv, 0);
|
|
zDiv = " UNION ALL ";
|
|
- if( strcmp(zDb, "main")!=0 ){
|
|
- appendText(&sSelect, "SELECT shell_add_schema(sql,", 0);
|
|
+ appendText(&sSelect, "SELECT shell_add_schema(sql,", 0);
|
|
+ if( sqlite3_stricmp(zDb, "main")!=0 ){
|
|
appendText(&sSelect, zDb, '"');
|
|
- appendText(&sSelect, ") AS sql, type, tbl_name, name, rowid,", 0);
|
|
- appendText(&sSelect, zScNum, 0);
|
|
- appendText(&sSelect, " AS snum, ", 0);
|
|
- appendText(&sSelect, zDb, '\'');
|
|
- appendText(&sSelect, " AS sname FROM ", 0);
|
|
- appendText(&sSelect, zDb, '"');
|
|
- appendText(&sSelect, ".sqlite_master", 0);
|
|
}else{
|
|
- appendText(&sSelect, "SELECT sql, type, tbl_name, name, rowid, ", 0);
|
|
- appendText(&sSelect, zScNum, 0);
|
|
- appendText(&sSelect, " AS snum, 'main' AS sname FROM sqlite_master",0);
|
|
+ appendText(&sSelect, "NULL", 0);
|
|
}
|
|
+ appendText(&sSelect, ",name) AS sql, type, tbl_name, name, rowid,", 0);
|
|
+ appendText(&sSelect, zScNum, 0);
|
|
+ appendText(&sSelect, " AS snum, ", 0);
|
|
+ appendText(&sSelect, zDb, '\'');
|
|
+ appendText(&sSelect, " AS sname FROM ", 0);
|
|
+ appendText(&sSelect, zDb, '"');
|
|
+ appendText(&sSelect, ".sqlite_master", 0);
|
|
}
|
|
sqlite3_finalize(pStmt);
|
|
+#ifdef SQLITE_INTROSPECTION_PRAGMAS
|
|
+ if( zName ){
|
|
+ appendText(&sSelect,
|
|
+ " UNION ALL SELECT shell_module_schema(name),"
|
|
+ " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list", 0);
|
|
+ }
|
|
+#endif
|
|
appendText(&sSelect, ") WHERE ", 0);
|
|
- if( nArg>1 ){
|
|
- char *zQarg = sqlite3_mprintf("%Q", azArg[1]);
|
|
- if( strchr(azArg[1], '.') ){
|
|
+ if( zName ){
|
|
+ char *zQarg = sqlite3_mprintf("%Q", zName);
|
|
+ int bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 ||
|
|
+ strchr(zName, '[') != 0;
|
|
+ if( strchr(zName, '.') ){
|
|
appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0);
|
|
}else{
|
|
appendText(&sSelect, "lower(tbl_name)", 0);
|
|
}
|
|
- appendText(&sSelect, strchr(azArg[1], '*') ? " GLOB " : " LIKE ", 0);
|
|
+ appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0);
|
|
appendText(&sSelect, zQarg, 0);
|
|
+ if( !bGlob ){
|
|
+ appendText(&sSelect, " ESCAPE '\\' ", 0);
|
|
+ }
|
|
appendText(&sSelect, " AND ", 0);
|
|
sqlite3_free(zQarg);
|
|
}
|
|
appendText(&sSelect, "type!='meta' AND sql IS NOT NULL"
|
|
" ORDER BY snum, rowid", 0);
|
|
- rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
|
|
+ if( bDebug ){
|
|
+ utf8_printf(p->out, "SQL: %s;\n", sSelect.z);
|
|
+ }else{
|
|
+ rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
|
|
+ }
|
|
freeText(&sSelect);
|
|
}
|
|
if( zErrMsg ){
|
|
@@ -6816,7 +14758,7 @@
|
|
}else
|
|
/* If no command name matches, show a syntax error */
|
|
session_syntax_error:
|
|
- session_help(p);
|
|
+ showHelp(p->out, "session");
|
|
}else
|
|
#endif
|
|
|
|
@@ -6997,7 +14939,7 @@
|
|
utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n",
|
|
azArg[i], azArg[0]);
|
|
raw_printf(stderr, "Should be one of: --schema"
|
|
- " --sha3-224 --sha3-255 --sha3-384 --sha3-512\n");
|
|
+ " --sha3-224 --sha3-256 --sha3-384 --sha3-512\n");
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
@@ -7008,7 +14950,7 @@
|
|
}else{
|
|
zLike = z;
|
|
bSeparate = 1;
|
|
- if( sqlite3_strlike("sqlite_%", zLike, 0)==0 ) bSchema = 1;
|
|
+ if( sqlite3_strlike("sqlite\\_%", zLike, '\\')==0 ) bSchema = 1;
|
|
}
|
|
}
|
|
if( bSchema ){
|
|
@@ -7075,11 +15017,12 @@
|
|
if( bDebug ){
|
|
utf8_printf(p->out, "%s\n", zSql);
|
|
}else{
|
|
- shell_exec(p->db, zSql, shell_callback, p, 0);
|
|
+ shell_exec(p, zSql, 0);
|
|
}
|
|
sqlite3_free(zSql);
|
|
}else
|
|
|
|
+#ifndef SQLITE_NOHAVE_SYSTEM
|
|
if( c=='s'
|
|
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
|
|
){
|
|
@@ -7099,9 +15042,10 @@
|
|
sqlite3_free(zCmd);
|
|
if( x ) raw_printf(stderr, "System command returns %d\n", x);
|
|
}else
|
|
+#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
|
|
|
|
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
|
|
- static const char *azBool[] = { "off", "on", "full", "unk" };
|
|
+ static const char *azBool[] = { "off", "on", "trigger", "full"};
|
|
int i;
|
|
if( nArg!=1 ){
|
|
raw_printf(stderr, "Usage: .show\n");
|
|
@@ -7138,7 +15082,7 @@
|
|
|
|
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
|
|
if( nArg==2 ){
|
|
- p->statsOn = booleanValue(azArg[1]);
|
|
+ p->statsOn = (u8)booleanValue(azArg[1]);
|
|
}else if( nArg==1 ){
|
|
display_stats(p->db, p, 0);
|
|
}else{
|
|
@@ -7159,7 +15103,10 @@
|
|
initText(&s);
|
|
open_db(p, 0);
|
|
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
|
|
- if( rc ) return shellDatabaseError(p->db);
|
|
+ if( rc ){
|
|
+ sqlite3_finalize(pStmt);
|
|
+ return shellDatabaseError(p->db);
|
|
+ }
|
|
|
|
if( nArg>2 && c=='i' ){
|
|
/* It is an historical accident that the .indexes command shows an error
|
|
@@ -7167,6 +15114,7 @@
|
|
** command does not. */
|
|
raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
|
|
rc = 1;
|
|
+ sqlite3_finalize(pStmt);
|
|
goto meta_command_exit;
|
|
}
|
|
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
|
|
@@ -7211,18 +15159,12 @@
|
|
char **azNew;
|
|
int n2 = nAlloc*2 + 10;
|
|
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
|
|
- if( azNew==0 ){
|
|
- rc = shellNomemError();
|
|
- break;
|
|
- }
|
|
+ if( azNew==0 ) shell_out_of_memory();
|
|
nAlloc = n2;
|
|
azResult = azNew;
|
|
}
|
|
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
|
|
- if( 0==azResult[nRow] ){
|
|
- rc = shellNomemError();
|
|
- break;
|
|
- }
|
|
+ if( 0==azResult[nRow] ) shell_out_of_memory();
|
|
nRow++;
|
|
}
|
|
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
|
|
@@ -7258,7 +15200,7 @@
|
|
/* Begin redirecting output to the file "testcase-out.txt" */
|
|
if( c=='t' && strcmp(azArg[0],"testcase")==0 ){
|
|
output_reset(p);
|
|
- p->out = output_file_open("testcase-out.txt");
|
|
+ p->out = output_file_open("testcase-out.txt", 0);
|
|
if( p->out==0 ){
|
|
raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n");
|
|
}
|
|
@@ -7270,50 +15212,78 @@
|
|
}else
|
|
|
|
#ifndef SQLITE_UNTESTABLE
|
|
- if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){
|
|
+ if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 ){
|
|
static const struct {
|
|
const char *zCtrlName; /* Name of a test-control option */
|
|
int ctrlCode; /* Integer code for that option */
|
|
+ const char *zUsage; /* Usage notes */
|
|
} aCtrl[] = {
|
|
- { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE },
|
|
- { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE },
|
|
- { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET },
|
|
- { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST },
|
|
- { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL },
|
|
- { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS },
|
|
- { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE },
|
|
- { "assert", SQLITE_TESTCTRL_ASSERT },
|
|
- { "always", SQLITE_TESTCTRL_ALWAYS },
|
|
- { "reserve", SQLITE_TESTCTRL_RESERVE },
|
|
- { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS },
|
|
- { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD },
|
|
- { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC },
|
|
- { "byteorder", SQLITE_TESTCTRL_BYTEORDER },
|
|
- { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT },
|
|
- { "imposter", SQLITE_TESTCTRL_IMPOSTER },
|
|
+ { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" },
|
|
+ { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" },
|
|
+ /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/
|
|
+ /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/
|
|
+ { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" },
|
|
+ /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" }, */
|
|
+ { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"},
|
|
+ { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "BOOLEAN" },
|
|
+ { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" },
|
|
+ { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" },
|
|
+ { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" },
|
|
+#ifdef YYCOVERAGE
|
|
+ { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" },
|
|
+#endif
|
|
+ { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
|
|
+ { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET, "" },
|
|
+ { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
|
|
+ { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
|
|
+ { "reserve", SQLITE_TESTCTRL_RESERVE, "BYTES-OF-RESERVE" },
|
|
};
|
|
int testctrl = -1;
|
|
- int rc2 = 0;
|
|
+ int iCtrl = -1;
|
|
+ int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
|
|
+ int isOk = 0;
|
|
int i, n2;
|
|
+ const char *zCmd = 0;
|
|
+
|
|
open_db(p, 0);
|
|
+ zCmd = nArg>=2 ? azArg[1] : "help";
|
|
|
|
+ /* The argument can optionally begin with "-" or "--" */
|
|
+ if( zCmd[0]=='-' && zCmd[1] ){
|
|
+ zCmd++;
|
|
+ if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
|
|
+ }
|
|
+
|
|
+ /* --help lists all test-controls */
|
|
+ if( strcmp(zCmd,"help")==0 ){
|
|
+ utf8_printf(p->out, "Available test-controls:\n");
|
|
+ for(i=0; i<ArraySize(aCtrl); i++){
|
|
+ utf8_printf(p->out, " .testctrl %s %s\n",
|
|
+ aCtrl[i].zCtrlName, aCtrl[i].zUsage);
|
|
+ }
|
|
+ rc = 1;
|
|
+ goto meta_command_exit;
|
|
+ }
|
|
+
|
|
/* convert testctrl text option to value. allow any unique prefix
|
|
** of the option name, or a numerical value. */
|
|
- n2 = strlen30(azArg[1]);
|
|
+ n2 = strlen30(zCmd);
|
|
for(i=0; i<ArraySize(aCtrl); i++){
|
|
- if( strncmp(azArg[1], aCtrl[i].zCtrlName, n2)==0 ){
|
|
+ if( strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){
|
|
if( testctrl<0 ){
|
|
testctrl = aCtrl[i].ctrlCode;
|
|
+ iCtrl = i;
|
|
}else{
|
|
- utf8_printf(stderr, "ambiguous option name: \"%s\"\n", azArg[1]);
|
|
- testctrl = -1;
|
|
- break;
|
|
+ utf8_printf(stderr, "Error: ambiguous test-control: \"%s\"\n"
|
|
+ "Use \".testctrl --help\" for help\n", zCmd);
|
|
+ rc = 1;
|
|
+ goto meta_command_exit;
|
|
}
|
|
}
|
|
}
|
|
- if( testctrl<0 ) testctrl = (int)integerValue(azArg[1]);
|
|
- if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){
|
|
- utf8_printf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]);
|
|
+ if( testctrl<0 ){
|
|
+ utf8_printf(stderr,"Error: unknown test-control: %s\n"
|
|
+ "Use \".testctrl --help\" for help\n", zCmd);
|
|
}else{
|
|
switch(testctrl){
|
|
|
|
@@ -7323,10 +15293,7 @@
|
|
if( nArg==3 ){
|
|
int opt = (int)strtol(azArg[2], 0, 0);
|
|
rc2 = sqlite3_test_control(testctrl, p->db, opt);
|
|
- raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
|
|
- } else {
|
|
- utf8_printf(stderr,"Error: testctrl %s takes a single int option\n",
|
|
- azArg[1]);
|
|
+ isOk = 3;
|
|
}
|
|
break;
|
|
|
|
@@ -7337,10 +15304,7 @@
|
|
case SQLITE_TESTCTRL_BYTEORDER:
|
|
if( nArg==2 ){
|
|
rc2 = sqlite3_test_control(testctrl);
|
|
- raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
|
|
- } else {
|
|
- utf8_printf(stderr,"Error: testctrl %s takes no options\n",
|
|
- azArg[1]);
|
|
+ isOk = testctrl==SQLITE_TESTCTRL_BYTEORDER ? 1 : 3;
|
|
}
|
|
break;
|
|
|
|
@@ -7349,10 +15313,7 @@
|
|
if( nArg==3 ){
|
|
unsigned int opt = (unsigned int)integerValue(azArg[2]);
|
|
rc2 = sqlite3_test_control(testctrl, opt);
|
|
- raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
|
|
- } else {
|
|
- utf8_printf(stderr,"Error: testctrl %s takes a single unsigned"
|
|
- " int option\n", azArg[1]);
|
|
+ isOk = 3;
|
|
}
|
|
break;
|
|
|
|
@@ -7359,31 +15320,23 @@
|
|
/* sqlite3_test_control(int, int) */
|
|
case SQLITE_TESTCTRL_ASSERT:
|
|
case SQLITE_TESTCTRL_ALWAYS:
|
|
- case SQLITE_TESTCTRL_NEVER_CORRUPT:
|
|
+ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS:
|
|
if( nArg==3 ){
|
|
int opt = booleanValue(azArg[2]);
|
|
rc2 = sqlite3_test_control(testctrl, opt);
|
|
- raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
|
|
- } else {
|
|
- utf8_printf(stderr,"Error: testctrl %s takes a single int option\n",
|
|
- azArg[1]);
|
|
+ isOk = 1;
|
|
}
|
|
break;
|
|
|
|
- /* sqlite3_test_control(int, char *) */
|
|
-#ifdef SQLITE_N_KEYWORD
|
|
- case SQLITE_TESTCTRL_ISKEYWORD:
|
|
+ /* sqlite3_test_control(int, int) */
|
|
+ case SQLITE_TESTCTRL_LOCALTIME_FAULT:
|
|
+ case SQLITE_TESTCTRL_NEVER_CORRUPT:
|
|
if( nArg==3 ){
|
|
- const char *opt = azArg[2];
|
|
+ int opt = booleanValue(azArg[2]);
|
|
rc2 = sqlite3_test_control(testctrl, opt);
|
|
- raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
|
|
- } else {
|
|
- utf8_printf(stderr,
|
|
- "Error: testctrl %s takes a single char * option\n",
|
|
- azArg[1]);
|
|
+ isOk = 3;
|
|
}
|
|
break;
|
|
-#endif
|
|
|
|
case SQLITE_TESTCTRL_IMPOSTER:
|
|
if( nArg==5 ){
|
|
@@ -7391,23 +15344,27 @@
|
|
azArg[2],
|
|
integerValue(azArg[3]),
|
|
integerValue(azArg[4]));
|
|
- raw_printf(p->out, "%d (0x%08x)\n", rc2, rc2);
|
|
- }else{
|
|
- raw_printf(stderr,"Usage: .testctrl imposter dbName onoff tnum\n");
|
|
+ isOk = 3;
|
|
}
|
|
break;
|
|
|
|
- case SQLITE_TESTCTRL_BITVEC_TEST:
|
|
- case SQLITE_TESTCTRL_FAULT_INSTALL:
|
|
- case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS:
|
|
- case SQLITE_TESTCTRL_SCRATCHMALLOC:
|
|
- default:
|
|
- utf8_printf(stderr,
|
|
- "Error: CLI support for testctrl %s not implemented\n",
|
|
- azArg[1]);
|
|
- break;
|
|
+#ifdef YYCOVERAGE
|
|
+ case SQLITE_TESTCTRL_PARSER_COVERAGE:
|
|
+ if( nArg==2 ){
|
|
+ sqlite3_test_control(testctrl, p->out);
|
|
+ isOk = 3;
|
|
+ }
|
|
+#endif
|
|
}
|
|
}
|
|
+ if( isOk==0 && iCtrl>=0 ){
|
|
+ utf8_printf(p->out, "Usage: .testctrl %s %s\n", zCmd, aCtrl[iCtrl].zUsage);
|
|
+ rc = 1;
|
|
+ }else if( isOk==1 ){
|
|
+ raw_printf(p->out, "%d\n", rc2);
|
|
+ }else if( isOk==2 ){
|
|
+ raw_printf(p->out, "0x%08x\n", rc2);
|
|
+ }
|
|
}else
|
|
#endif /* !defined(SQLITE_UNTESTABLE) */
|
|
|
|
@@ -7437,7 +15394,7 @@
|
|
goto meta_command_exit;
|
|
}
|
|
output_file_close(p->traceOut);
|
|
- p->traceOut = output_file_open(azArg[1]);
|
|
+ p->traceOut = output_file_open(azArg[1], 0);
|
|
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
|
if( p->traceOut==0 ){
|
|
sqlite3_trace_v2(p->db, 0, 0, 0);
|
|
@@ -7461,8 +15418,7 @@
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
- rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3],
|
|
- (int)strlen(azArg[3]));
|
|
+ rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], strlen30(azArg[3]));
|
|
if( rc ){
|
|
utf8_printf(stderr, "Authentication failed for user %s\n", azArg[2]);
|
|
rc = 1;
|
|
@@ -7473,8 +15429,7 @@
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
- rc = sqlite3_user_add(p->db, azArg[2],
|
|
- azArg[3], (int)strlen(azArg[3]),
|
|
+ rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
|
|
booleanValue(azArg[4]));
|
|
if( rc ){
|
|
raw_printf(stderr, "User-Add failed: %d\n", rc);
|
|
@@ -7486,8 +15441,7 @@
|
|
rc = 1;
|
|
goto meta_command_exit;
|
|
}
|
|
- rc = sqlite3_user_change(p->db, azArg[2],
|
|
- azArg[3], (int)strlen(azArg[3]),
|
|
+ rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
|
|
booleanValue(azArg[4]));
|
|
if( rc ){
|
|
raw_printf(stderr, "User-Edit failed: %d\n", rc);
|
|
@@ -7515,6 +15469,20 @@
|
|
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
|
|
utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/,
|
|
sqlite3_libversion(), sqlite3_sourceid());
|
|
+#if SQLITE_HAVE_ZLIB
|
|
+ utf8_printf(p->out, "zlib version %s\n", zlibVersion());
|
|
+#endif
|
|
+#define CTIMEOPT_VAL_(opt) #opt
|
|
+#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
|
|
+#if defined(__clang__) && defined(__clang_major__)
|
|
+ utf8_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "."
|
|
+ CTIMEOPT_VAL(__clang_minor__) "."
|
|
+ CTIMEOPT_VAL(__clang_patchlevel__) "\n");
|
|
+#elif defined(_MSC_VER)
|
|
+ utf8_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) "\n");
|
|
+#elif defined(__GNUC__) && defined(__VERSION__)
|
|
+ utf8_printf(p->out, "gcc-" __VERSION__ "\n");
|
|
+#endif
|
|
}else
|
|
|
|
if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){
|
|
@@ -7641,6 +15609,16 @@
|
|
}
|
|
|
|
/*
|
|
+** We need a default sqlite3_complete() implementation to use in case
|
|
+** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes
|
|
+** any arbitrary text is a complete SQL statement. This is not very
|
|
+** user-friendly, but it does seem to work.
|
|
+*/
|
|
+#ifdef SQLITE_OMIT_COMPLETE
|
|
+#define sqlite3_complete(x) 1
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Return true if zSql is a complete SQL statement. Return false if it
|
|
** ends in the middle of a string literal or C-style comment.
|
|
*/
|
|
@@ -7655,7 +15633,7 @@
|
|
}
|
|
|
|
/*
|
|
-** Run a single line of SQL
|
|
+** Run a single line of SQL. Return the number of errors.
|
|
*/
|
|
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
|
|
int rc;
|
|
@@ -7664,7 +15642,7 @@
|
|
open_db(p, 0);
|
|
if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql);
|
|
BEGIN_TIMER;
|
|
- rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
|
|
+ rc = shell_exec(p, zSql, &zErrMsg);
|
|
END_TIMER;
|
|
if( rc || zErrMsg ){
|
|
char zPrefix[100];
|
|
@@ -7728,13 +15706,15 @@
|
|
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
|
|
continue;
|
|
}
|
|
- if( zLine && zLine[0]=='.' && nSql==0 ){
|
|
+ if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
|
|
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
|
|
- rc = do_meta_command(zLine, p);
|
|
- if( rc==2 ){ /* exit requested */
|
|
- break;
|
|
- }else if( rc ){
|
|
- errCnt++;
|
|
+ if( zLine[0]=='.' ){
|
|
+ rc = do_meta_command(zLine, p);
|
|
+ if( rc==2 ){ /* exit requested */
|
|
+ break;
|
|
+ }else if( rc ){
|
|
+ errCnt++;
|
|
+ }
|
|
}
|
|
continue;
|
|
}
|
|
@@ -7745,10 +15725,7 @@
|
|
if( nSql+nLine+2>=nAlloc ){
|
|
nAlloc = nSql+nLine+100;
|
|
zSql = realloc(zSql, nAlloc);
|
|
- if( zSql==0 ){
|
|
- raw_printf(stderr, "Error: out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ if( zSql==0 ) shell_out_of_memory();
|
|
}
|
|
nSqlPrior = nSql;
|
|
if( nSql==0 ){
|
|
@@ -7770,6 +15747,8 @@
|
|
if( p->outCount ){
|
|
output_reset(p);
|
|
p->outCount = 0;
|
|
+ }else{
|
|
+ clearTempFile(p);
|
|
}
|
|
}else if( nSql && _all_whitespace(zSql) ){
|
|
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
|
|
@@ -7777,7 +15756,7 @@
|
|
}
|
|
}
|
|
if( nSql && !_all_whitespace(zSql) ){
|
|
- runOneSqlLine(p, zSql, in, startline);
|
|
+ errCnt += runOneSqlLine(p, zSql, in, startline);
|
|
}
|
|
free(zSql);
|
|
free(zLine);
|
|
@@ -7875,7 +15854,6 @@
|
|
" cannot read ~/.sqliterc\n");
|
|
return;
|
|
}
|
|
- sqlite3_initialize();
|
|
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
|
|
sqliterc = zBuf;
|
|
}
|
|
@@ -7894,6 +15872,10 @@
|
|
** Show available command line options
|
|
*/
|
|
static const char zOptions[] =
|
|
+#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
|
|
+ " -A ARGS... run \".archive ARGS\" and exit\n"
|
|
+#endif
|
|
+ " -append append the database to the end of the file\n"
|
|
" -ascii set output mode to 'ascii'\n"
|
|
" -bail stop after hitting an error\n"
|
|
" -batch force batch I/O\n"
|
|
@@ -7920,8 +15902,11 @@
|
|
" -nullvalue TEXT set text string for NULL values. Default ''\n"
|
|
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
|
|
" -quote set output mode to 'quote'\n"
|
|
- " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n"
|
|
+ " -readonly open the database read-only\n"
|
|
" -separator SEP set output column separator. Default: '|'\n"
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ " -sorterref SIZE sorter references threshold size\n"
|
|
+#endif
|
|
" -stats print memory stats before each finalize\n"
|
|
" -version show SQLite version\n"
|
|
" -vfs NAME use NAME as the default VFS\n"
|
|
@@ -7928,6 +15913,9 @@
|
|
#ifdef SQLITE_ENABLE_VFSTRACE
|
|
" -vfstrace enable tracing of all VFS calls\n"
|
|
#endif
|
|
+#ifdef SQLITE_HAVE_ZLIB
|
|
+ " -zip open the file as a ZIP Archive\n"
|
|
+#endif
|
|
;
|
|
static void usage(int showDetail){
|
|
utf8_printf(stderr,
|
|
@@ -7943,6 +15931,17 @@
|
|
}
|
|
|
|
/*
|
|
+** Internal check: Verify that the SQLite is uninitialized. Print a
|
|
+** error message if it is initialized.
|
|
+*/
|
|
+static void verify_uninitialized(void){
|
|
+ if( sqlite3_config(-1)==SQLITE_MISUSE ){
|
|
+ utf8_printf(stdout, "WARNING: attempt to configure SQLite after"
|
|
+ " initialization.\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
** Initialize the state information in data
|
|
*/
|
|
static void main_init(ShellState *data) {
|
|
@@ -7953,6 +15952,7 @@
|
|
memcpy(data->rowSeparator,SEP_Row, 2);
|
|
data->showHeader = 0;
|
|
data->shellFlgs = SHFLG_Lookaside;
|
|
+ verify_uninitialized();
|
|
sqlite3_config(SQLITE_CONFIG_URI, 1);
|
|
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
|
|
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
|
@@ -8016,6 +16016,11 @@
|
|
int readStdin = 1;
|
|
int nCmd = 0;
|
|
char **azCmd = 0;
|
|
+ const char *zVfs = 0; /* Value of -vfs command-line option */
|
|
+#if !SQLITE_SHELL_IS_UTF8
|
|
+ char **argvToFree = 0;
|
|
+ int argcToFree = 0;
|
|
+#endif
|
|
|
|
setBinaryMode(stdin, 0);
|
|
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
|
|
@@ -8022,8 +16027,25 @@
|
|
stdin_is_interactive = isatty(0);
|
|
stdout_is_console = isatty(1);
|
|
|
|
+#if !defined(_WIN32_WCE)
|
|
+ if( getenv("SQLITE_DEBUG_BREAK") ){
|
|
+ if( isatty(0) && isatty(2) ){
|
|
+ fprintf(stderr,
|
|
+ "attach debugger to process %d and press any key to continue.\n",
|
|
+ GETPID());
|
|
+ fgetc(stdin);
|
|
+ }else{
|
|
+#if defined(_WIN32) || defined(WIN32)
|
|
+ DebugBreak();
|
|
+#elif defined(SIGTRAP)
|
|
+ raise(SIGTRAP);
|
|
+#endif
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
#if USE_SYSTEM_SQLITE+0!=1
|
|
- if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){
|
|
+ if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
|
|
utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
|
|
sqlite3_sourceid(), SQLITE_SOURCE_ID);
|
|
exit(1);
|
|
@@ -8030,21 +16052,33 @@
|
|
}
|
|
#endif
|
|
main_init(&data);
|
|
+
|
|
+ /* On Windows, we must translate command-line arguments into UTF-8.
|
|
+ ** The SQLite memory allocator subsystem has to be enabled in order to
|
|
+ ** do this. But we want to run an sqlite3_shutdown() afterwards so that
|
|
+ ** subsequent sqlite3_config() calls will work. So copy all results into
|
|
+ ** memory that does not come from the SQLite memory allocator.
|
|
+ */
|
|
#if !SQLITE_SHELL_IS_UTF8
|
|
sqlite3_initialize();
|
|
- argv = sqlite3_malloc64(sizeof(argv[0])*argc);
|
|
- if( argv==0 ){
|
|
- raw_printf(stderr, "out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ argvToFree = malloc(sizeof(argv[0])*argc*2);
|
|
+ argcToFree = argc;
|
|
+ argv = argvToFree + argc;
|
|
+ if( argv==0 ) shell_out_of_memory();
|
|
for(i=0; i<argc; i++){
|
|
- argv[i] = sqlite3_win32_unicode_to_utf8(wargv[i]);
|
|
- if( argv[i]==0 ){
|
|
- raw_printf(stderr, "out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
|
|
+ int n;
|
|
+ if( z==0 ) shell_out_of_memory();
|
|
+ n = (int)strlen(z);
|
|
+ argv[i] = malloc( n+1 );
|
|
+ if( argv[i]==0 ) shell_out_of_memory();
|
|
+ memcpy(argv[i], z, n+1);
|
|
+ argvToFree[i] = argv[i];
|
|
+ sqlite3_free(z);
|
|
}
|
|
+ sqlite3_shutdown();
|
|
#endif
|
|
+
|
|
assert( argc>=1 && argv && argv[0] );
|
|
Argv0 = argv[0];
|
|
|
|
@@ -8053,6 +16087,8 @@
|
|
*/
|
|
#ifdef SIGINT
|
|
signal(SIGINT, interrupt_handler);
|
|
+#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
|
|
+ SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
|
#endif
|
|
|
|
#ifdef SQLITE_SHELL_DBNAME_PROC
|
|
@@ -8072,6 +16108,7 @@
|
|
** the size of the alternative malloc heap,
|
|
** and the first command to execute.
|
|
*/
|
|
+ verify_uninitialized();
|
|
for(i=1; i<argc; i++){
|
|
char *z;
|
|
z = argv[i];
|
|
@@ -8084,10 +16121,7 @@
|
|
readStdin = 0;
|
|
nCmd++;
|
|
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
|
|
- if( azCmd==0 ){
|
|
- raw_printf(stderr, "out of memory\n");
|
|
- exit(1);
|
|
- }
|
|
+ if( azCmd==0 ) shell_out_of_memory();
|
|
azCmd[nCmd-1] = z;
|
|
}
|
|
}
|
|
@@ -8118,16 +16152,6 @@
|
|
#else
|
|
(void)cmdline_option_value(argc, argv, ++i);
|
|
#endif
|
|
- }else if( strcmp(z,"-scratch")==0 ){
|
|
- int n, sz;
|
|
- sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
|
|
- if( sz>400000 ) sz = 400000;
|
|
- if( sz<2500 ) sz = 2500;
|
|
- n = (int)integerValue(cmdline_option_value(argc,argv,++i));
|
|
- if( n>10 ) n = 10;
|
|
- if( n<1 ) n = 1;
|
|
- sqlite3_config(SQLITE_CONFIG_SCRATCH, malloc(n*sz+1), sz, n);
|
|
- data.shellFlgs |= SHFLG_Scratch;
|
|
}else if( strcmp(z,"-pagecache")==0 ){
|
|
int n, sz;
|
|
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
|
|
@@ -8164,16 +16188,61 @@
|
|
}else if( strcmp(z,"-mmap")==0 ){
|
|
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
|
|
sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ }else if( strcmp(z,"-sorterref")==0 ){
|
|
+ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
|
|
+ sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
|
|
+#endif
|
|
}else if( strcmp(z,"-vfs")==0 ){
|
|
- sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i));
|
|
- if( pVfs ){
|
|
- sqlite3_vfs_register(pVfs, 1);
|
|
- }else{
|
|
- utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]);
|
|
- exit(1);
|
|
- }
|
|
+ zVfs = cmdline_option_value(argc, argv, ++i);
|
|
+#ifdef SQLITE_HAVE_ZLIB
|
|
+ }else if( strcmp(z,"-zip")==0 ){
|
|
+ data.openMode = SHELL_OPEN_ZIPFILE;
|
|
+#endif
|
|
+ }else if( strcmp(z,"-append")==0 ){
|
|
+ data.openMode = SHELL_OPEN_APPENDVFS;
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ }else if( strcmp(z,"-deserialize")==0 ){
|
|
+ data.openMode = SHELL_OPEN_DESERIALIZE;
|
|
+#endif
|
|
+ }else if( strcmp(z,"-readonly")==0 ){
|
|
+ data.openMode = SHELL_OPEN_READONLY;
|
|
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
|
+ }else if( strncmp(z, "-A",2)==0 ){
|
|
+ /* All remaining command-line arguments are passed to the ".archive"
|
|
+ ** command, so ignore them */
|
|
+ break;
|
|
+#endif
|
|
}
|
|
}
|
|
+ verify_uninitialized();
|
|
+
|
|
+
|
|
+#ifdef SQLITE_SHELL_INIT_PROC
|
|
+ {
|
|
+ /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
|
|
+ ** of a C-function that will perform initialization actions on SQLite that
|
|
+ ** occur just before or after sqlite3_initialize(). Use this compile-time
|
|
+ ** option to embed this shell program in larger applications. */
|
|
+ extern void SQLITE_SHELL_INIT_PROC(void);
|
|
+ SQLITE_SHELL_INIT_PROC();
|
|
+ }
|
|
+#else
|
|
+ /* All the sqlite3_config() calls have now been made. So it is safe
|
|
+ ** to call sqlite3_initialize() and process any command line -vfs option. */
|
|
+ sqlite3_initialize();
|
|
+#endif
|
|
+
|
|
+ if( zVfs ){
|
|
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
|
|
+ if( pVfs ){
|
|
+ sqlite3_vfs_register(pVfs, 1);
|
|
+ }else{
|
|
+ utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]);
|
|
+ exit(1);
|
|
+ }
|
|
+ }
|
|
+
|
|
if( data.zDbFilename==0 ){
|
|
#ifndef SQLITE_OMIT_MEMORYDB
|
|
data.zDbFilename = ":memory:";
|
|
@@ -8184,6 +16253,7 @@
|
|
#endif
|
|
}
|
|
data.out = stdout;
|
|
+ sqlite3_appendvfs_init(0,0,0);
|
|
|
|
/* Go ahead and open the database file if it already exists. If the
|
|
** file does not exist, delay opening it. This prevents empty database
|
|
@@ -8224,6 +16294,18 @@
|
|
}else if( strcmp(z,"-csv")==0 ){
|
|
data.mode = MODE_Csv;
|
|
memcpy(data.colSeparator,",",2);
|
|
+#ifdef SQLITE_HAVE_ZLIB
|
|
+ }else if( strcmp(z,"-zip")==0 ){
|
|
+ data.openMode = SHELL_OPEN_ZIPFILE;
|
|
+#endif
|
|
+ }else if( strcmp(z,"-append")==0 ){
|
|
+ data.openMode = SHELL_OPEN_APPENDVFS;
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ }else if( strcmp(z,"-deserialize")==0 ){
|
|
+ data.openMode = SHELL_OPEN_DESERIALIZE;
|
|
+#endif
|
|
+ }else if( strcmp(z,"-readonly")==0 ){
|
|
+ data.openMode = SHELL_OPEN_READONLY;
|
|
}else if( strcmp(z,"-ascii")==0 ){
|
|
data.mode = MODE_Ascii;
|
|
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
|
|
@@ -8246,9 +16328,9 @@
|
|
}else if( strcmp(z,"-echo")==0 ){
|
|
ShellSetFlag(&data, SHFLG_Echo);
|
|
}else if( strcmp(z,"-eqp")==0 ){
|
|
- data.autoEQP = 1;
|
|
+ data.autoEQP = AUTOEQP_on;
|
|
}else if( strcmp(z,"-eqpfull")==0 ){
|
|
- data.autoEQP = 2;
|
|
+ data.autoEQP = AUTOEQP_full;
|
|
}else if( strcmp(z,"-stats")==0 ){
|
|
data.statsOn = 1;
|
|
}else if( strcmp(z,"-scanstats")==0 ){
|
|
@@ -8271,8 +16353,6 @@
|
|
stdin_is_interactive = 0;
|
|
}else if( strcmp(z,"-heap")==0 ){
|
|
i++;
|
|
- }else if( strcmp(z,"-scratch")==0 ){
|
|
- i+=2;
|
|
}else if( strcmp(z,"-pagecache")==0 ){
|
|
i+=2;
|
|
}else if( strcmp(z,"-lookaside")==0 ){
|
|
@@ -8279,6 +16359,10 @@
|
|
i+=2;
|
|
}else if( strcmp(z,"-mmap")==0 ){
|
|
i++;
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ }else if( strcmp(z,"-sorterref")==0 ){
|
|
+ i++;
|
|
+#endif
|
|
}else if( strcmp(z,"-vfs")==0 ){
|
|
i++;
|
|
#ifdef SQLITE_ENABLE_VFSTRACE
|
|
@@ -8303,7 +16387,7 @@
|
|
if( rc && bail_on_error ) return rc==2 ? 0 : rc;
|
|
}else{
|
|
open_db(&data, 0);
|
|
- rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg);
|
|
+ rc = shell_exec(&data, z, &zErrMsg);
|
|
if( zErrMsg!=0 ){
|
|
utf8_printf(stderr,"Error: %s\n", zErrMsg);
|
|
if( bail_on_error ) return rc!=0 ? rc : 1;
|
|
@@ -8312,6 +16396,23 @@
|
|
if( bail_on_error ) return rc;
|
|
}
|
|
}
|
|
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
|
+ }else if( strncmp(z, "-A", 2)==0 ){
|
|
+ if( nCmd>0 ){
|
|
+ utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands"
|
|
+ " with \"%s\"\n", z);
|
|
+ return 1;
|
|
+ }
|
|
+ open_db(&data, OPEN_DB_ZIPFILE);
|
|
+ if( z[2] ){
|
|
+ argv[i] = &z[2];
|
|
+ arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
|
|
+ }else{
|
|
+ arDotCommand(&data, 1, argv+i, argc-i);
|
|
+ }
|
|
+ readStdin = 0;
|
|
+ break;
|
|
+#endif
|
|
}else{
|
|
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
|
|
raw_printf(stderr,"Use -help for a list of options.\n");
|
|
@@ -8331,7 +16432,7 @@
|
|
if( rc ) return rc==2 ? 0 : rc;
|
|
}else{
|
|
open_db(&data, 0);
|
|
- rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg);
|
|
+ rc = shell_exec(&data, azCmd[i], &zErrMsg);
|
|
if( zErrMsg!=0 ){
|
|
utf8_printf(stderr,"Error: %s\n", zErrMsg);
|
|
return rc!=0 ? rc : 1;
|
|
@@ -8347,7 +16448,7 @@
|
|
*/
|
|
if( stdin_is_interactive ){
|
|
char *zHome;
|
|
- char *zHistory = 0;
|
|
+ char *zHistory;
|
|
int nHistory;
|
|
printf(
|
|
"SQLite version %s %.19s\n" /*extra-version-info*/
|
|
@@ -8360,8 +16461,10 @@
|
|
printf(".\nUse \".open FILENAME\" to reopen on a "
|
|
"persistent database.\n");
|
|
}
|
|
- zHome = find_home_dir(0);
|
|
- if( zHome ){
|
|
+ zHistory = getenv("SQLITE_HISTORY");
|
|
+ if( zHistory ){
|
|
+ zHistory = strdup(zHistory);
|
|
+ }else if( (zHome = find_home_dir(0))!=0 ){
|
|
nHistory = strlen30(zHome) + 20;
|
|
if( (zHistory = malloc(nHistory))!=0 ){
|
|
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
|
|
@@ -8386,13 +16489,19 @@
|
|
set_table_name(&data, 0);
|
|
if( data.db ){
|
|
session_close_all(&data);
|
|
- sqlite3_close(data.db);
|
|
+ close_db(data.db);
|
|
}
|
|
sqlite3_free(data.zFreeOnClose);
|
|
find_home_dir(1);
|
|
+ output_reset(&data);
|
|
+ data.doXdgOpen = 0;
|
|
+ clearTempFile(&data);
|
|
#if !SQLITE_SHELL_IS_UTF8
|
|
- for(i=0; i<argc; i++) sqlite3_free(argv[i]);
|
|
- sqlite3_free(argv);
|
|
+ for(i=0; i<argcToFree; i++) free(argvToFree[i]);
|
|
+ free(argvToFree);
|
|
#endif
|
|
+ /* Clear the global data structure so that valgrind will detect memory
|
|
+ ** leaks */
|
|
+ memset(&data, 0, sizeof(data));
|
|
return rc;
|
|
}
|
|
--- contrib/sqlite3/sqlite3.c.orig
|
|
+++ contrib/sqlite3/sqlite3.c
|
|
@@ -1,6 +1,6 @@
|
|
/******************************************************************************
|
|
** This file is an amalgamation of many separate C source files from SQLite
|
|
-** version 3.20.0. By combining all the individual C code files into this
|
|
+** version 3.26.0. By combining all the individual C code files into this
|
|
** single large file, the entire code can be compiled as a single translation
|
|
** unit. This allows many compilers to do optimizations that would not be
|
|
** possible if the files were compiled separately. Performance improvements
|
|
@@ -55,6 +55,12 @@
|
|
#define CTIMEOPT_VAL_(opt) #opt
|
|
#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
|
|
|
|
+/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This
|
|
+** option requires a separate macro because legal values contain a single
|
|
+** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */
|
|
+#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2
|
|
+#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt)
|
|
+
|
|
/*
|
|
** An array of names of all compile-time options. This array should
|
|
** be sorted A-Z.
|
|
@@ -138,7 +144,7 @@
|
|
"DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
|
|
#endif
|
|
#ifdef SQLITE_DEFAULT_LOOKASIDE
|
|
- "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOOKASIDE),
|
|
+ "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE),
|
|
#endif
|
|
#if SQLITE_DEFAULT_MEMSTATUS
|
|
"DEFAULT_MEMSTATUS",
|
|
@@ -209,8 +215,11 @@
|
|
#if SQLITE_ENABLE_ATOMIC_WRITE
|
|
"ENABLE_ATOMIC_WRITE",
|
|
#endif
|
|
+#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ "ENABLE_BATCH_ATOMIC_WRITE",
|
|
+#endif
|
|
#if SQLITE_ENABLE_CEROD
|
|
- "ENABLE_CEROD",
|
|
+ "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
|
|
#endif
|
|
#if SQLITE_ENABLE_COLUMN_METADATA
|
|
"ENABLE_COLUMN_METADATA",
|
|
@@ -251,6 +260,9 @@
|
|
#if SQLITE_ENABLE_FTS5
|
|
"ENABLE_FTS5",
|
|
#endif
|
|
+#if SQLITE_ENABLE_GEOPOLY
|
|
+ "ENABLE_GEOPOLY",
|
|
+#endif
|
|
#if SQLITE_ENABLE_HIDDEN_COLUMNS
|
|
"ENABLE_HIDDEN_COLUMNS",
|
|
#endif
|
|
@@ -281,6 +293,9 @@
|
|
#if SQLITE_ENABLE_MULTIPLEX
|
|
"ENABLE_MULTIPLEX",
|
|
#endif
|
|
+#if SQLITE_ENABLE_NORMALIZE
|
|
+ "ENABLE_NORMALIZE",
|
|
+#endif
|
|
#if SQLITE_ENABLE_NULL_TRIM
|
|
"ENABLE_NULL_TRIM",
|
|
#endif
|
|
@@ -308,6 +323,9 @@
|
|
#if SQLITE_ENABLE_SNAPSHOT
|
|
"ENABLE_SNAPSHOT",
|
|
#endif
|
|
+#if SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ "ENABLE_SORTER_REFERENCES",
|
|
+#endif
|
|
#if SQLITE_ENABLE_SQLLOG
|
|
"ENABLE_SQLLOG",
|
|
#endif
|
|
@@ -828,14 +846,6 @@
|
|
#endif
|
|
|
|
/*
|
|
-** Make sure that rand_s() is available on Windows systems with MSVC 2005
|
|
-** or higher.
|
|
-*/
|
|
-#if defined(_MSC_VER) && _MSC_VER>=1400
|
|
-# define _CRT_RAND_S
|
|
-#endif
|
|
-
|
|
-/*
|
|
** Include the header file used to customize the compiler options for MSVC.
|
|
** This should be done first so that it can successfully prevent spurious
|
|
** compiler warnings due to subsequent content in this file and other files
|
|
@@ -1144,15 +1154,17 @@
|
|
** a string which identifies a particular check-in of SQLite
|
|
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
|
** string contains the date and time of the check-in (UTC) and a SHA1
|
|
-** or SHA3-256 hash of the entire source tree.
|
|
+** or SHA3-256 hash of the entire source tree. If the source code has
|
|
+** been edited in any way since it was last checked in, then the last
|
|
+** four hexadecimal digits of the hash may be modified.
|
|
**
|
|
** See also: [sqlite3_libversion()],
|
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
|
** [sqlite_version()] and [sqlite_source_id()].
|
|
*/
|
|
-#define SQLITE_VERSION "3.20.0"
|
|
-#define SQLITE_VERSION_NUMBER 3020000
|
|
-#define SQLITE_SOURCE_ID "2017-08-01 13:24:15 9501e22dfeebdcefa783575e47c60b514d7c2e0cad73b2a496c0bc4b680900a8"
|
|
+#define SQLITE_VERSION "3.26.0"
|
|
+#define SQLITE_VERSION_NUMBER 3026000
|
|
+#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
|
|
|
|
/*
|
|
** CAPI3REF: Run-Time Library Version Numbers
|
|
@@ -1168,7 +1180,7 @@
|
|
**
|
|
** <blockquote><pre>
|
|
** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
|
|
-** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
|
|
+** assert( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,80)==0 );
|
|
** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
|
|
** </pre></blockquote>)^
|
|
**
|
|
@@ -1178,9 +1190,11 @@
|
|
** function is provided for use in DLLs since DLL users usually do not have
|
|
** direct access to string constants within the DLL. ^The
|
|
** sqlite3_libversion_number() function returns an integer equal to
|
|
-** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns
|
|
+** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns
|
|
** a pointer to a string constant whose value is the same as the
|
|
-** [SQLITE_SOURCE_ID] C preprocessor macro.
|
|
+** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built
|
|
+** using an edited copy of [the amalgamation], then the last four characters
|
|
+** of the hash might be different from [SQLITE_SOURCE_ID].)^
|
|
**
|
|
** See also: [sqlite_version()] and [sqlite_source_id()].
|
|
*/
|
|
@@ -1461,7 +1475,7 @@
|
|
#define SQLITE_FULL 13 /* Insertion failed because database is full */
|
|
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
|
|
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
|
|
-#define SQLITE_EMPTY 16 /* Not used */
|
|
+#define SQLITE_EMPTY 16 /* Internal use only */
|
|
#define SQLITE_SCHEMA 17 /* The database schema changed */
|
|
#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
|
|
#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */
|
|
@@ -1495,6 +1509,9 @@
|
|
** the most recent error can be obtained using
|
|
** [sqlite3_extended_errcode()].
|
|
*/
|
|
+#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
|
|
+#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
|
|
+#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
|
|
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
|
|
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
|
|
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
|
|
@@ -1523,7 +1540,11 @@
|
|
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
|
|
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
|
|
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
|
|
+#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
|
|
+#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
|
|
+#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
|
|
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
|
+#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
|
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
|
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
|
|
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
|
@@ -1530,11 +1551,15 @@
|
|
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
|
|
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
|
|
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
|
|
+#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
|
|
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
|
+#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
|
|
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
|
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
|
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
|
#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
|
|
+#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
|
|
+#define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6<<8))
|
|
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
|
#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
|
|
#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
|
|
@@ -1609,6 +1634,11 @@
|
|
** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
|
|
** read-only media and cannot be changed even by processes with
|
|
** elevated privileges.
|
|
+**
|
|
+** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying
|
|
+** filesystem supports doing multiple write operations atomically when those
|
|
+** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and
|
|
+** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE].
|
|
*/
|
|
#define SQLITE_IOCAP_ATOMIC 0x00000001
|
|
#define SQLITE_IOCAP_ATOMIC512 0x00000002
|
|
@@ -1624,6 +1654,7 @@
|
|
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
|
|
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
|
|
#define SQLITE_IOCAP_IMMUTABLE 0x00002000
|
|
+#define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000
|
|
|
|
/*
|
|
** CAPI3REF: File Locking Levels
|
|
@@ -1758,6 +1789,7 @@
|
|
** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN]
|
|
** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE]
|
|
** <li> [SQLITE_IOCAP_IMMUTABLE]
|
|
+** <li> [SQLITE_IOCAP_BATCH_ATOMIC]
|
|
** </ul>
|
|
**
|
|
** The SQLITE_IOCAP_ATOMIC property means that all writes of
|
|
@@ -1895,7 +1927,8 @@
|
|
** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
|
|
** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
|
|
** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary
|
|
-** write ahead log and shared memory files used for transaction control
|
|
+** write ahead log ([WAL file]) and shared memory
|
|
+** files used for transaction control
|
|
** are automatically deleted when the latest connection to the database
|
|
** closes. Setting persistent WAL mode causes those files to persist after
|
|
** close. Persisting the files is useful when other processes that do not
|
|
@@ -2041,6 +2074,66 @@
|
|
** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
|
|
** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
|
|
** this opcode.
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]]
|
|
+** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then
|
|
+** the file descriptor is placed in "batch write mode", which
|
|
+** means all subsequent write operations will be deferred and done
|
|
+** atomically at the next [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. Systems
|
|
+** that do not support batch atomic writes will return SQLITE_NOTFOUND.
|
|
+** ^Following a successful SQLITE_FCNTL_BEGIN_ATOMIC_WRITE and prior to
|
|
+** the closing [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] or
|
|
+** [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE], SQLite will make
|
|
+** no VFS interface calls on the same [sqlite3_file] file descriptor
|
|
+** except for calls to the xWrite method and the xFileControl method
|
|
+** with [SQLITE_FCNTL_SIZE_HINT].
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]]
|
|
+** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write
|
|
+** operations since the previous successful call to
|
|
+** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically.
|
|
+** This file control returns [SQLITE_OK] if and only if the writes were
|
|
+** all performed successfully and have been committed to persistent storage.
|
|
+** ^Regardless of whether or not it is successful, this file control takes
|
|
+** the file descriptor out of batch write mode so that all subsequent
|
|
+** write operations are independent.
|
|
+** ^SQLite will never invoke SQLITE_FCNTL_COMMIT_ATOMIC_WRITE without
|
|
+** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]]
|
|
+** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write
|
|
+** operations since the previous successful call to
|
|
+** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back.
|
|
+** ^This file control takes the file descriptor out of batch write mode
|
|
+** so that all subsequent write operations are independent.
|
|
+** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
|
|
+** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
|
|
+** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
|
|
+** a file lock using the xLock or xShmLock methods of the VFS to wait
|
|
+** for up to M milliseconds before failing, where M is the single
|
|
+** unsigned integer parameter.
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
|
+** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
|
+** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
|
+** The "data version" for the pager is written into the pointer. The
|
|
+** "data version" changes whenever any change occurs to the corresponding
|
|
+** database file, either through SQL statements on the same database
|
|
+** connection or through transactions committed by separate database
|
|
+** connections possibly in other processes. The [sqlite3_total_changes()]
|
|
+** interface can be used to find if any database on the connection has changed,
|
|
+** but that interface responds to changes on TEMP as well as MAIN and does
|
|
+** not provide a mechanism to detect changes to MAIN only. Also, the
|
|
+** [sqlite3_total_changes()] interface responds to internal changes only and
|
|
+** omits changes made by other database connections. The
|
|
+** [PRAGMA data_version] command provide a mechanism to detect changes to
|
|
+** a single attached database that occur due to other database connections,
|
|
+** but omits changes implemented by the database connection on which it is
|
|
+** called. This file control is the only mechanism to detect changes that
|
|
+** happen either internally or externally and that are associated with
|
|
+** a particular attached database.
|
|
** </ul>
|
|
*/
|
|
#define SQLITE_FCNTL_LOCKSTATE 1
|
|
@@ -2072,6 +2165,11 @@
|
|
#define SQLITE_FCNTL_JOURNAL_POINTER 28
|
|
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
|
|
#define SQLITE_FCNTL_PDB 30
|
|
+#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31
|
|
+#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32
|
|
+#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
|
|
+#define SQLITE_FCNTL_LOCK_TIMEOUT 34
|
|
+#define SQLITE_FCNTL_DATA_VERSION 35
|
|
|
|
/* deprecated names */
|
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
|
@@ -2109,12 +2207,18 @@
|
|
** in the name of the object stands for "virtual file system". See
|
|
** the [VFS | VFS documentation] for further information.
|
|
**
|
|
-** The value of the iVersion field is initially 1 but may be larger in
|
|
-** future versions of SQLite. Additional fields may be appended to this
|
|
-** object when the iVersion value is increased. Note that the structure
|
|
-** of the sqlite3_vfs object changes in the transaction between
|
|
-** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not
|
|
-** modified.
|
|
+** The VFS interface is sometimes extended by adding new methods onto
|
|
+** the end. Each time such an extension occurs, the iVersion field
|
|
+** is incremented. The iVersion value started out as 1 in
|
|
+** SQLite [version 3.5.0] on [dateof:3.5.0], then increased to 2
|
|
+** with SQLite [version 3.7.0] on [dateof:3.7.0], and then increased
|
|
+** to 3 with SQLite [version 3.7.6] on [dateof:3.7.6]. Additional fields
|
|
+** may be appended to the sqlite3_vfs object and the iVersion value
|
|
+** may increase again in future versions of SQLite.
|
|
+** Note that the structure
|
|
+** of the sqlite3_vfs object changes in the transition from
|
|
+** SQLite [version 3.5.9] to [version 3.6.0] on [dateof:3.6.0]
|
|
+** and yet the iVersion field was not modified.
|
|
**
|
|
** The szOsFile field is the size of the subclassed [sqlite3_file]
|
|
** structure used by this VFS. mxPathname is the maximum length of
|
|
@@ -2642,6 +2746,16 @@
|
|
** routines with a wrapper that simulations memory allocation failure or
|
|
** tracks memory usage, for example. </dd>
|
|
**
|
|
+** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt>
|
|
+** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes single argument of
|
|
+** type int, interpreted as a boolean, which if true provides a hint to
|
|
+** SQLite that it should avoid large memory allocations if possible.
|
|
+** SQLite will run faster if it is free to make large memory allocations,
|
|
+** but some application might prefer to run slower in exchange for
|
|
+** guarantees about memory fragmentation that are possible if large
|
|
+** allocations are avoided. This hint is normally off.
|
|
+** </dd>
|
|
+**
|
|
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
|
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
|
|
** interpreted as a boolean, which enables or disables the collection of
|
|
@@ -2659,25 +2773,7 @@
|
|
** </dd>
|
|
**
|
|
** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
|
|
-** <dd> ^The SQLITE_CONFIG_SCRATCH option specifies a static memory buffer
|
|
-** that SQLite can use for scratch memory. ^(There are three arguments
|
|
-** to SQLITE_CONFIG_SCRATCH: A pointer an 8-byte
|
|
-** aligned memory buffer from which the scratch allocations will be
|
|
-** drawn, the size of each scratch allocation (sz),
|
|
-** and the maximum number of scratch allocations (N).)^
|
|
-** The first argument must be a pointer to an 8-byte aligned buffer
|
|
-** of at least sz*N bytes of memory.
|
|
-** ^SQLite will not use more than one scratch buffers per thread.
|
|
-** ^SQLite will never request a scratch buffer that is more than 6
|
|
-** times the database page size.
|
|
-** ^If SQLite needs needs additional
|
|
-** scratch memory beyond what is provided by this configuration option, then
|
|
-** [sqlite3_malloc()] will be used to obtain the memory needed.<p>
|
|
-** ^When the application provides any amount of scratch memory using
|
|
-** SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary large
|
|
-** [sqlite3_malloc|heap allocations].
|
|
-** This can help [Robson proof|prevent memory allocation failures] due to heap
|
|
-** fragmentation in low-memory embedded systems.
|
|
+** <dd> The SQLITE_CONFIG_SCRATCH option is no longer used.
|
|
** </dd>
|
|
**
|
|
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
|
|
@@ -2713,8 +2809,7 @@
|
|
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
|
|
** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
|
|
** that SQLite will use for all of its dynamic memory allocation needs
|
|
-** beyond those provided for by [SQLITE_CONFIG_SCRATCH] and
|
|
-** [SQLITE_CONFIG_PAGECACHE].
|
|
+** beyond those provided for by [SQLITE_CONFIG_PAGECACHE].
|
|
** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled
|
|
** with either [SQLITE_ENABLE_MEMSYS3] or [SQLITE_ENABLE_MEMSYS5] and returns
|
|
** [SQLITE_ERROR] if invoked otherwise.
|
|
@@ -2900,6 +2995,22 @@
|
|
** I/O required to support statement rollback.
|
|
** The default value for this setting is controlled by the
|
|
** [SQLITE_STMTJRNL_SPILL] compile-time option.
|
|
+**
|
|
+** [[SQLITE_CONFIG_SORTERREF_SIZE]]
|
|
+** <dt>SQLITE_CONFIG_SORTERREF_SIZE
|
|
+** <dd>The SQLITE_CONFIG_SORTERREF_SIZE option accepts a single parameter
|
|
+** of type (int) - the new value of the sorter-reference size threshold.
|
|
+** Usually, when SQLite uses an external sort to order records according
|
|
+** to an ORDER BY clause, all fields required by the caller are present in the
|
|
+** sorted records. However, if SQLite determines based on the declared type
|
|
+** of a table column that its values are likely to be very large - larger
|
|
+** than the configured sorter-reference size threshold - then a reference
|
|
+** is stored in each sorted record and the required column values loaded
|
|
+** from the database as records are returned in sorted order. The default
|
|
+** value for this option is to never use this optimization. Specifying a
|
|
+** negative value for this option restores the default behaviour.
|
|
+** This option is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
|
|
** </dl>
|
|
*/
|
|
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
|
@@ -2907,7 +3018,7 @@
|
|
#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
|
|
#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
|
|
#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
|
|
-#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */
|
|
+#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
|
|
#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
|
|
#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
|
|
#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
|
|
@@ -2928,6 +3039,8 @@
|
|
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
|
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
|
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
|
|
+#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
|
|
+#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
|
|
|
|
/*
|
|
** CAPI3REF: Database Connection Configuration Options
|
|
@@ -2943,6 +3056,7 @@
|
|
** is invoked.
|
|
**
|
|
** <dl>
|
|
+** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
|
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
|
** <dd> ^This option takes three additional arguments that determine the
|
|
** [lookaside memory allocator] configuration for the [database connection].
|
|
@@ -2965,6 +3079,7 @@
|
|
** memory is in use leaves the configuration unchanged and returns
|
|
** [SQLITE_BUSY].)^</dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
|
** <dd> ^This option is used to enable or disable the enforcement of
|
|
** [foreign key constraints]. There should be two additional arguments.
|
|
@@ -2975,6 +3090,7 @@
|
|
** following this call. The second parameter may be a NULL pointer, in
|
|
** which case the FK enforcement setting is not reported back. </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
|
|
** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
|
|
** There should be two additional arguments.
|
|
@@ -2985,6 +3101,7 @@
|
|
** following this call. The second parameter may be a NULL pointer, in
|
|
** which case the trigger setting is not reported back. </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
|
** <dd> ^This option is used to enable or disable the two-argument
|
|
** version of the [fts3_tokenizer()] function which is part of the
|
|
@@ -2998,6 +3115,7 @@
|
|
** following this call. The second parameter may be a NULL pointer, in
|
|
** which case the new setting is not reported back. </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
|
|
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
|
|
** interface independently of the [load_extension()] SQL function.
|
|
@@ -3015,7 +3133,7 @@
|
|
** be a NULL pointer, in which case the new setting is not reported back.
|
|
** </dd>
|
|
**
|
|
-** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
|
+** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
|
** <dd> ^This option is used to change the name of the "main" database
|
|
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
|
** which will become the new schema name in place of "main". ^SQLite
|
|
@@ -3024,6 +3142,7 @@
|
|
** until after the database connection closes.
|
|
** </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
|
|
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
|
|
** <dd> Usually, when a database in wal mode is closed or detached from a
|
|
** database handle, SQLite checks if this will mean that there are now no
|
|
@@ -3030,13 +3149,14 @@
|
|
** connections at all to the database. If so, it performs a checkpoint
|
|
** operation before closing the connection. This option may be used to
|
|
** override this behaviour. The first parameter passed to this operation
|
|
-** is an integer - non-zero to disable checkpoints-on-close, or zero (the
|
|
-** default) to enable them. The second parameter is a pointer to an integer
|
|
+** is an integer - positive to disable checkpoints-on-close, or zero (the
|
|
+** default) to enable them, and negative to leave the setting unchanged.
|
|
+** The second parameter is a pointer to an integer
|
|
** into which is written 0 or 1 to indicate whether checkpoints-on-close
|
|
** have been disabled - 0 if they are not disabled, 1 if they are.
|
|
** </dd>
|
|
**
|
|
-** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
|
|
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]] <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
|
|
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
|
|
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
|
|
** a single SQL query statement will always use the same algorithm regardless
|
|
@@ -3045,8 +3165,57 @@
|
|
** slower. But the QPSG has the advantage of more predictable behavior. With
|
|
** the QPSG active, SQLite will always use the same query plan in the field as
|
|
** was used during testing in the lab.
|
|
+** The first argument to this setting is an integer which is 0 to disable
|
|
+** the QPSG, positive to enable QPSG, or negative to leave the setting
|
|
+** unchanged. The second parameter is a pointer to an integer into which
|
|
+** is written 0 or 1 to indicate whether the QPSG is disabled or enabled
|
|
+** following this call.
|
|
** </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
|
|
+** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
|
|
+** include output for any operations performed by trigger programs. This
|
|
+** option is used to set or clear (the default) a flag that governs this
|
|
+** behavior. The first parameter passed to this operation is an integer -
|
|
+** positive to enable output for trigger programs, or zero to disable it,
|
|
+** or negative to leave the setting unchanged.
|
|
+** The second parameter is a pointer to an integer into which is written
|
|
+** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
|
|
+** it is not disabled, 1 if it is.
|
|
+** </dd>
|
|
+**
|
|
+** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
|
|
+** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
|
|
+** [VACUUM] in order to reset a database back to an empty database
|
|
+** with no schema and no content. The following process works even for
|
|
+** a badly corrupted database file:
|
|
+** <ol>
|
|
+** <li> If the database connection is newly opened, make sure it has read the
|
|
+** database schema by preparing then discarding some query against the
|
|
+** database, or calling sqlite3_table_column_metadata(), ignoring any
|
|
+** errors. This step is only necessary if the application desires to keep
|
|
+** the database in WAL mode after the reset if it was in WAL mode before
|
|
+** the reset.
|
|
+** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
|
|
+** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0);
|
|
+** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
|
+** </ol>
|
|
+** Because resetting a database is destructive and irreversible, the
|
|
+** process requires the use of this obscure API and multiple steps to help
|
|
+** ensure that it does not happen by accident.
|
|
+**
|
|
+** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
|
|
+** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
|
|
+** "defensive" flag for a database connection. When the defensive
|
|
+** flag is enabled, language features that allow ordinary SQL to
|
|
+** deliberately corrupt the database file are disabled. The disabled
|
|
+** features include but are not limited to the following:
|
|
+** <ul>
|
|
+** <li> The [PRAGMA writable_schema=ON] statement.
|
|
+** <li> Writes to the [sqlite_dbpage] virtual table.
|
|
+** <li> Direct writes to [shadow tables].
|
|
+** </ul>
|
|
+** </dd>
|
|
** </dl>
|
|
*/
|
|
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
|
@@ -3057,8 +3226,11 @@
|
|
#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
|
|
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
|
|
#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */
|
|
+#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
|
|
+#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
|
|
+#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
|
|
+#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
|
|
|
|
-
|
|
/*
|
|
** CAPI3REF: Enable Or Disable Extended Result Codes
|
|
** METHOD: sqlite3
|
|
@@ -3185,12 +3357,17 @@
|
|
** program, the value returned reflects the number of rows modified by the
|
|
** previous INSERT, UPDATE or DELETE statement within the same trigger.
|
|
**
|
|
-** See also the [sqlite3_total_changes()] interface, the
|
|
-** [count_changes pragma], and the [changes() SQL function].
|
|
-**
|
|
** If a separate thread makes changes on the same database connection
|
|
** while [sqlite3_changes()] is running then the value returned
|
|
** is unpredictable and not meaningful.
|
|
+**
|
|
+** See also:
|
|
+** <ul>
|
|
+** <li> the [sqlite3_total_changes()] interface
|
|
+** <li> the [count_changes pragma]
|
|
+** <li> the [changes() SQL function]
|
|
+** <li> the [data_version pragma]
|
|
+** </ul>
|
|
*/
|
|
SQLITE_API int sqlite3_changes(sqlite3*);
|
|
|
|
@@ -3208,13 +3385,26 @@
|
|
** count, but those made as part of REPLACE constraint resolution are
|
|
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
|
|
** are not counted.
|
|
+**
|
|
+** This the [sqlite3_total_changes(D)] interface only reports the number
|
|
+** of rows that changed due to SQL statement run against database
|
|
+** connection D. Any changes by other database connections are ignored.
|
|
+** To detect changes against a database file from other database
|
|
+** connections use the [PRAGMA data_version] command or the
|
|
+** [SQLITE_FCNTL_DATA_VERSION] [file control].
|
|
**
|
|
-** See also the [sqlite3_changes()] interface, the
|
|
-** [count_changes pragma], and the [total_changes() SQL function].
|
|
-**
|
|
** If a separate thread makes changes on the same database connection
|
|
** while [sqlite3_total_changes()] is running then the value
|
|
** returned is unpredictable and not meaningful.
|
|
+**
|
|
+** See also:
|
|
+** <ul>
|
|
+** <li> the [sqlite3_changes()] interface
|
|
+** <li> the [count_changes pragma]
|
|
+** <li> the [changes() SQL function]
|
|
+** <li> the [data_version pragma]
|
|
+** <li> the [SQLITE_FCNTL_DATA_VERSION] [file control]
|
|
+** </ul>
|
|
*/
|
|
SQLITE_API int sqlite3_total_changes(sqlite3*);
|
|
|
|
@@ -3463,16 +3653,16 @@
|
|
**
|
|
** These routines are work-alikes of the "printf()" family of functions
|
|
** from the standard C library.
|
|
-** These routines understand most of the common K&R formatting options,
|
|
-** plus some additional non-standard formats, detailed below.
|
|
-** Note that some of the more obscure formatting options from recent
|
|
-** C-library standards are omitted from this implementation.
|
|
+** These routines understand most of the common formatting options from
|
|
+** the standard library printf()
|
|
+** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]).
|
|
+** See the [built-in printf()] documentation for details.
|
|
**
|
|
** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
|
|
-** results into memory obtained from [sqlite3_malloc()].
|
|
+** results into memory obtained from [sqlite3_malloc64()].
|
|
** The strings returned by these two routines should be
|
|
** released by [sqlite3_free()]. ^Both routines return a
|
|
-** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
|
|
+** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough
|
|
** memory to hold the resulting string.
|
|
**
|
|
** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from
|
|
@@ -3496,71 +3686,7 @@
|
|
**
|
|
** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf().
|
|
**
|
|
-** These routines all implement some additional formatting
|
|
-** options that are useful for constructing SQL statements.
|
|
-** All of the usual printf() formatting options apply. In addition, there
|
|
-** is are "%q", "%Q", "%w" and "%z" options.
|
|
-**
|
|
-** ^(The %q option works like %s in that it substitutes a nul-terminated
|
|
-** string from the argument list. But %q also doubles every '\'' character.
|
|
-** %q is designed for use inside a string literal.)^ By doubling each '\''
|
|
-** character it escapes that character and allows it to be inserted into
|
|
-** the string.
|
|
-**
|
|
-** For example, assume the string variable zText contains text as follows:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** char *zText = "It's a happy day!";
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** One can use this text in an SQL statement as follows:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
|
|
-** sqlite3_exec(db, zSQL, 0, 0, 0);
|
|
-** sqlite3_free(zSQL);
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** Because the %q format string is used, the '\'' character in zText
|
|
-** is escaped and the SQL generated is as follows:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** INSERT INTO table1 VALUES('It''s a happy day!')
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** This is correct. Had we used %s instead of %q, the generated SQL
|
|
-** would have looked like this:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** INSERT INTO table1 VALUES('It's a happy day!');
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** This second example is an SQL syntax error. As a general rule you should
|
|
-** always use %q instead of %s when inserting text into a string literal.
|
|
-**
|
|
-** ^(The %Q option works like %q except it also adds single quotes around
|
|
-** the outside of the total string. Additionally, if the parameter in the
|
|
-** argument list is a NULL pointer, %Q substitutes the text "NULL" (without
|
|
-** single quotes).)^ So, for example, one could say:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
|
|
-** sqlite3_exec(db, zSQL, 0, 0, 0);
|
|
-** sqlite3_free(zSQL);
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** The code above will render a correct SQL statement in the zSQL
|
|
-** variable even if the zText variable is a NULL pointer.
|
|
-**
|
|
-** ^(The "%w" formatting option is like "%q" except that it expects to
|
|
-** be contained within double-quotes instead of single quotes, and it
|
|
-** escapes the double-quote character instead of the single-quote
|
|
-** character.)^ The "%w" formatting option is intended for safely inserting
|
|
-** table and column names into a constructed SQL statement.
|
|
-**
|
|
-** ^(The "%z" formatting option works like "%s" but with the
|
|
-** addition that after the string has been read and copied into
|
|
-** the result, [sqlite3_free()] is called on the input string.)^
|
|
+** See also: [built-in printf()], [printf() SQL function]
|
|
*/
|
|
SQLITE_API char *sqlite3_mprintf(const char*,...);
|
|
SQLITE_API char *sqlite3_vmprintf(const char*, va_list);
|
|
@@ -3918,8 +4044,8 @@
|
|
** KEYWORDS: SQLITE_TRACE
|
|
**
|
|
** These constants identify classes of events that can be monitored
|
|
-** using the [sqlite3_trace_v2()] tracing logic. The third argument
|
|
-** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of
|
|
+** using the [sqlite3_trace_v2()] tracing logic. The M argument
|
|
+** to [sqlite3_trace_v2(D,M,X,P)] is an OR-ed combination of one or more of
|
|
** the following constants. ^The first argument to the trace callback
|
|
** is one of the following constants.
|
|
**
|
|
@@ -4128,10 +4254,10 @@
|
|
** ^If [URI filename] interpretation is enabled, and the filename argument
|
|
** begins with "file:", then the filename is interpreted as a URI. ^URI
|
|
** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is
|
|
-** set in the fourth argument to sqlite3_open_v2(), or if it has
|
|
+** set in the third argument to sqlite3_open_v2(), or if it has
|
|
** been enabled globally using the [SQLITE_CONFIG_URI] option with the
|
|
** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option.
|
|
-** As of SQLite version 3.7.7, URI filename interpretation is turned off
|
|
+** URI filename interpretation is turned off
|
|
** by default, but future releases of SQLite might enable URI filename
|
|
** interpretation by default. See "[URI filenames]" for additional
|
|
** information.
|
|
@@ -4334,13 +4460,24 @@
|
|
** [database connection] D failed, then the sqlite3_errcode(D) interface
|
|
** returns the numeric [result code] or [extended result code] for that
|
|
** API call.
|
|
-** If the most recent API call was successful,
|
|
-** then the return value from sqlite3_errcode() is undefined.
|
|
** ^The sqlite3_extended_errcode()
|
|
** interface is the same except that it always returns the
|
|
** [extended result code] even when extended result codes are
|
|
** disabled.
|
|
**
|
|
+** The values returned by sqlite3_errcode() and/or
|
|
+** sqlite3_extended_errcode() might change with each API call.
|
|
+** Except, there are some interfaces that are guaranteed to never
|
|
+** change the value of the error code. The error-code preserving
|
|
+** interfaces are:
|
|
+**
|
|
+** <ul>
|
|
+** <li> sqlite3_errcode()
|
|
+** <li> sqlite3_extended_errcode()
|
|
+** <li> sqlite3_errmsg()
|
|
+** <li> sqlite3_errmsg16()
|
|
+** </ul>
|
|
+**
|
|
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
|
|
** text that describes the error, as either UTF-8 or UTF-16 respectively.
|
|
** ^(Memory to hold the error message string is managed internally.
|
|
@@ -4530,9 +4667,19 @@
|
|
** on this hint by avoiding the use of [lookaside memory] so as not to
|
|
** deplete the limited store of lookaside memory. Future versions of
|
|
** SQLite may act on this hint differently.
|
|
+**
|
|
+** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
|
|
+** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
|
|
+** representation of the SQL statement should be calculated and then
|
|
+** associated with the prepared statement, which can be obtained via
|
|
+** the [sqlite3_normalized_sql()] interface.)^ The semantics used to
|
|
+** normalize a SQL statement are unspecified and subject to change.
|
|
+** At a minimum, literal values will be replaced with suitable
|
|
+** placeholders.
|
|
** </dl>
|
|
*/
|
|
#define SQLITE_PREPARE_PERSISTENT 0x01
|
|
+#define SQLITE_PREPARE_NORMALIZE 0x02
|
|
|
|
/*
|
|
** CAPI3REF: Compiling An SQL Statement
|
|
@@ -4626,6 +4773,7 @@
|
|
** or [GLOB] operator or if the parameter is compared to an indexed column
|
|
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
|
|
** </li>
|
|
+** </ol>
|
|
**
|
|
** <p>^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having
|
|
** the extra prepFlags parameter, which is a bit array consisting of zero or
|
|
@@ -4632,7 +4780,6 @@
|
|
** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The
|
|
** sqlite3_prepare_v2() interface works exactly the same as
|
|
** sqlite3_prepare_v3() with a zero prepFlags parameter.
|
|
-** </ol>
|
|
*/
|
|
SQLITE_API int sqlite3_prepare(
|
|
sqlite3 *db, /* Database handle */
|
|
@@ -4690,6 +4837,11 @@
|
|
** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
|
|
** string containing the SQL text of prepared statement P with
|
|
** [bound parameters] expanded.
|
|
+** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8
|
|
+** string containing the normalized SQL text of prepared statement P. The
|
|
+** semantics used to normalize a SQL statement are unspecified and subject
|
|
+** to change. At a minimum, literal values will be replaced with suitable
|
|
+** placeholders.
|
|
**
|
|
** ^(For example, if a prepared statement is created using the SQL
|
|
** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
|
|
@@ -4705,8 +4857,9 @@
|
|
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
|
** option causes sqlite3_expanded_sql() to always return NULL.
|
|
**
|
|
-** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
|
|
-** automatically freed when the prepared statement is finalized.
|
|
+** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P)
|
|
+** are managed by SQLite and are automatically freed when the prepared
|
|
+** statement is finalized.
|
|
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
|
|
** is obtained from [sqlite3_malloc()] and must be free by the application
|
|
** by passing it to [sqlite3_free()].
|
|
@@ -4713,6 +4866,7 @@
|
|
*/
|
|
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
|
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
|
|
+SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
|
|
|
|
/*
|
|
** CAPI3REF: Determine If An SQL Statement Writes The Database
|
|
@@ -4805,8 +4959,9 @@
|
|
** implementation of [application-defined SQL functions] are protected.
|
|
** ^The sqlite3_value object returned by
|
|
** [sqlite3_column_value()] is unprotected.
|
|
-** Unprotected sqlite3_value objects may only be used with
|
|
-** [sqlite3_result_value()] and [sqlite3_bind_value()].
|
|
+** Unprotected sqlite3_value objects may only be used as arguments
|
|
+** to [sqlite3_result_value()], [sqlite3_bind_value()], and
|
|
+** [sqlite3_value_dup()].
|
|
** The [sqlite3_value_blob | sqlite3_value_type()] family of
|
|
** interfaces require protected sqlite3_value objects.
|
|
*/
|
|
@@ -5228,7 +5383,7 @@
|
|
** other than [SQLITE_ROW] before any subsequent invocation of
|
|
** sqlite3_step(). Failure to reset the prepared statement using
|
|
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
|
-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
|
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
|
** sqlite3_step() began
|
|
** calling [sqlite3_reset()] automatically in this circumstance rather
|
|
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
|
@@ -5493,11 +5648,25 @@
|
|
** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
|
|
** [sqlite3_free()].
|
|
**
|
|
-** ^(If a memory allocation error occurs during the evaluation of any
|
|
-** of these routines, a default value is returned. The default value
|
|
-** is either the integer 0, the floating point number 0.0, or a NULL
|
|
-** pointer. Subsequent calls to [sqlite3_errcode()] will return
|
|
-** [SQLITE_NOMEM].)^
|
|
+** As long as the input parameters are correct, these routines will only
|
|
+** fail if an out-of-memory error occurs during a format conversion.
|
|
+** Only the following subset of interfaces are subject to out-of-memory
|
|
+** errors:
|
|
+**
|
|
+** <ul>
|
|
+** <li> sqlite3_column_blob()
|
|
+** <li> sqlite3_column_text()
|
|
+** <li> sqlite3_column_text16()
|
|
+** <li> sqlite3_column_bytes()
|
|
+** <li> sqlite3_column_bytes16()
|
|
+** </ul>
|
|
+**
|
|
+** If an out-of-memory error occurs, then the return value from these
|
|
+** routines is the same as if the column had contained an SQL NULL value.
|
|
+** Valid SQL NULL returns can be distinguished from out-of-memory errors
|
|
+** by invoking the [sqlite3_errcode()] immediately after the suspect
|
|
+** return value is obtained and before any
|
|
+** other SQLite interface is called on the same [database connection].
|
|
*/
|
|
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
|
|
SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);
|
|
@@ -5574,11 +5743,13 @@
|
|
**
|
|
** ^These functions (collectively known as "function creation routines")
|
|
** are used to add SQL functions or aggregates or to redefine the behavior
|
|
-** of existing SQL functions or aggregates. The only differences between
|
|
-** these routines are the text encoding expected for
|
|
-** the second parameter (the name of the function being created)
|
|
-** and the presence or absence of a destructor callback for
|
|
-** the application data pointer.
|
|
+** of existing SQL functions or aggregates. The only differences between
|
|
+** the three "sqlite3_create_function*" routines are the text encoding
|
|
+** expected for the second parameter (the name of the function being
|
|
+** created) and the presence or absence of a destructor callback for
|
|
+** the application data pointer. Function sqlite3_create_window_function()
|
|
+** is similar, but allows the user to supply the extra callback functions
|
|
+** needed by [aggregate window functions].
|
|
**
|
|
** ^The first parameter is the [database connection] to which the SQL
|
|
** function is to be added. ^If an application uses more than one database
|
|
@@ -5624,7 +5795,8 @@
|
|
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
|
|
** function can gain access to this pointer using [sqlite3_user_data()].)^
|
|
**
|
|
-** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are
|
|
+** ^The sixth, seventh and eighth parameters passed to the three
|
|
+** "sqlite3_create_function*" functions, xFunc, xStep and xFinal, are
|
|
** pointers to C-language functions that implement the SQL function or
|
|
** aggregate. ^A scalar SQL function requires an implementation of the xFunc
|
|
** callback only; NULL pointers must be passed as the xStep and xFinal
|
|
@@ -5633,16 +5805,25 @@
|
|
** SQL function or aggregate, pass NULL pointers for all three function
|
|
** callbacks.
|
|
**
|
|
-** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL,
|
|
-** then it is destructor for the application data pointer.
|
|
-** The destructor is invoked when the function is deleted, either by being
|
|
-** overloaded or when the database connection closes.)^
|
|
-** ^The destructor is also invoked if the call to
|
|
-** sqlite3_create_function_v2() fails.
|
|
-** ^When the destructor callback of the tenth parameter is invoked, it
|
|
-** is passed a single argument which is a copy of the application data
|
|
-** pointer which was the fifth parameter to sqlite3_create_function_v2().
|
|
+** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue
|
|
+** and xInverse) passed to sqlite3_create_window_function are pointers to
|
|
+** C-language callbacks that implement the new function. xStep and xFinal
|
|
+** must both be non-NULL. xValue and xInverse may either both be NULL, in
|
|
+** which case a regular aggregate function is created, or must both be
|
|
+** non-NULL, in which case the new function may be used as either an aggregate
|
|
+** or aggregate window function. More details regarding the implementation
|
|
+** of aggregate window functions are
|
|
+** [user-defined window functions|available here].
|
|
**
|
|
+** ^(If the final parameter to sqlite3_create_function_v2() or
|
|
+** sqlite3_create_window_function() is not NULL, then it is destructor for
|
|
+** the application data pointer. The destructor is invoked when the function
|
|
+** is deleted, either by being overloaded or when the database connection
|
|
+** closes.)^ ^The destructor is also invoked if the call to
|
|
+** sqlite3_create_function_v2() fails. ^When the destructor callback is
|
|
+** invoked, it is passed a single argument which is a copy of the application
|
|
+** data pointer which was the fifth parameter to sqlite3_create_function_v2().
|
|
+**
|
|
** ^It is permitted to register multiple implementations of the same
|
|
** functions with the same name but with either differing numbers of
|
|
** arguments or differing preferred text encodings. ^SQLite will use
|
|
@@ -5694,6 +5875,18 @@
|
|
void (*xFinal)(sqlite3_context*),
|
|
void(*xDestroy)(void*)
|
|
);
|
|
+SQLITE_API int sqlite3_create_window_function(
|
|
+ sqlite3 *db,
|
|
+ const char *zFunctionName,
|
|
+ int nArg,
|
|
+ int eTextRep,
|
|
+ void *pApp,
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void (*xFinal)(sqlite3_context*),
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void(*xDestroy)(void*)
|
|
+);
|
|
|
|
/*
|
|
** CAPI3REF: Text Encodings
|
|
@@ -5764,6 +5957,9 @@
|
|
** datatype of the value
|
|
** <tr><td><b>sqlite3_value_numeric_type </b>
|
|
** <td>→ <td>Best numeric datatype of the value
|
|
+** <tr><td><b>sqlite3_value_nochange </b>
|
|
+** <td>→ <td>True if the column is unchanged in an UPDATE
|
|
+** against a virtual table.
|
|
** </table></blockquote>
|
|
**
|
|
** <b>Details:</b>
|
|
@@ -5812,6 +6008,19 @@
|
|
** then the conversion is performed. Otherwise no conversion occurs.
|
|
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
|
|
**
|
|
+** ^Within the [xUpdate] method of a [virtual table], the
|
|
+** sqlite3_value_nochange(X) interface returns true if and only if
|
|
+** the column corresponding to X is unchanged by the UPDATE operation
|
|
+** that the xUpdate method call was invoked to implement and if
|
|
+** and the prior [xColumn] method call that was invoked to extracted
|
|
+** the value for that column returned without setting a result (probably
|
|
+** because it queried [sqlite3_vtab_nochange()] and found that the column
|
|
+** was unchanging). ^Within an [xUpdate] method, any value for which
|
|
+** sqlite3_value_nochange(X) is true will in all other respects appear
|
|
+** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other
|
|
+** than within an [xUpdate] method call for an UPDATE statement, then
|
|
+** the return value is arbitrary and meaningless.
|
|
+**
|
|
** Please pay particular attention to the fact that the pointer returned
|
|
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
|
|
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
|
|
@@ -5820,6 +6029,28 @@
|
|
**
|
|
** These routines must be called from the same thread as
|
|
** the SQL function that supplied the [sqlite3_value*] parameters.
|
|
+**
|
|
+** As long as the input parameter is correct, these routines can only
|
|
+** fail if an out-of-memory error occurs during a format conversion.
|
|
+** Only the following subset of interfaces are subject to out-of-memory
|
|
+** errors:
|
|
+**
|
|
+** <ul>
|
|
+** <li> sqlite3_value_blob()
|
|
+** <li> sqlite3_value_text()
|
|
+** <li> sqlite3_value_text16()
|
|
+** <li> sqlite3_value_text16le()
|
|
+** <li> sqlite3_value_text16be()
|
|
+** <li> sqlite3_value_bytes()
|
|
+** <li> sqlite3_value_bytes16()
|
|
+** </ul>
|
|
+**
|
|
+** If an out-of-memory error occurs, then the return value from these
|
|
+** routines is the same as if the column had contained an SQL NULL value.
|
|
+** Valid SQL NULL returns can be distinguished from out-of-memory errors
|
|
+** by invoking the [sqlite3_errcode()] immediately after the suspect
|
|
+** return value is obtained and before any
|
|
+** other SQLite interface is called on the same [database connection].
|
|
*/
|
|
SQLITE_API const void *sqlite3_value_blob(sqlite3_value*);
|
|
SQLITE_API double sqlite3_value_double(sqlite3_value*);
|
|
@@ -5834,6 +6065,7 @@
|
|
SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
|
+SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
|
|
|
/*
|
|
** CAPI3REF: Finding The Subtype Of SQL Values
|
|
@@ -6490,6 +6722,41 @@
|
|
SQLITE_API char *sqlite3_data_directory;
|
|
|
|
/*
|
|
+** CAPI3REF: Win32 Specific Interface
|
|
+**
|
|
+** These interfaces are available only on Windows. The
|
|
+** [sqlite3_win32_set_directory] interface is used to set the value associated
|
|
+** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to
|
|
+** zValue, depending on the value of the type parameter. The zValue parameter
|
|
+** should be NULL to cause the previous value to be freed via [sqlite3_free];
|
|
+** a non-NULL value will be copied into memory obtained from [sqlite3_malloc]
|
|
+** prior to being used. The [sqlite3_win32_set_directory] interface returns
|
|
+** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported,
|
|
+** or [SQLITE_NOMEM] if memory could not be allocated. The value of the
|
|
+** [sqlite3_data_directory] variable is intended to act as a replacement for
|
|
+** the current directory on the sub-platforms of Win32 where that concept is
|
|
+** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and
|
|
+** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the
|
|
+** sqlite3_win32_set_directory interface except the string parameter must be
|
|
+** UTF-8 or UTF-16, respectively.
|
|
+*/
|
|
+SQLITE_API int sqlite3_win32_set_directory(
|
|
+ unsigned long type, /* Identifier for directory being set or reset */
|
|
+ void *zValue /* New value for directory being set or reset */
|
|
+);
|
|
+SQLITE_API int sqlite3_win32_set_directory8(unsigned long type, const char *zValue);
|
|
+SQLITE_API int sqlite3_win32_set_directory16(unsigned long type, const void *zValue);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Win32 Directory Types
|
|
+**
|
|
+** These macros are only available on Windows. They define the allowed values
|
|
+** for the type argument to the [sqlite3_win32_set_directory] interface.
|
|
+*/
|
|
+#define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1
|
|
+#define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2
|
|
+
|
|
+/*
|
|
** CAPI3REF: Test For Auto-Commit Mode
|
|
** KEYWORDS: {autocommit mode}
|
|
** METHOD: sqlite3
|
|
@@ -7089,6 +7356,9 @@
|
|
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
|
|
int (*xRelease)(sqlite3_vtab *pVTab, int);
|
|
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
|
|
+ /* The methods above are in versions 1 and 2 of the sqlite_module object.
|
|
+ ** Those below are for version 3 and greater. */
|
|
+ int (*xShadowName)(const char*);
|
|
};
|
|
|
|
/*
|
|
@@ -7221,6 +7491,10 @@
|
|
|
|
/*
|
|
** CAPI3REF: Virtual Table Scan Flags
|
|
+**
|
|
+** Virtual table implementations are allowed to set the
|
|
+** [sqlite3_index_info].idxFlags field to some combination of
|
|
+** these bits.
|
|
*/
|
|
#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
|
|
|
|
@@ -7232,15 +7506,21 @@
|
|
** an operator that is part of a constraint term in the wHERE clause of
|
|
** a query that uses a [virtual table].
|
|
*/
|
|
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
|
-#define SQLITE_INDEX_CONSTRAINT_GT 4
|
|
-#define SQLITE_INDEX_CONSTRAINT_LE 8
|
|
-#define SQLITE_INDEX_CONSTRAINT_LT 16
|
|
-#define SQLITE_INDEX_CONSTRAINT_GE 32
|
|
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
|
|
-#define SQLITE_INDEX_CONSTRAINT_LIKE 65
|
|
-#define SQLITE_INDEX_CONSTRAINT_GLOB 66
|
|
-#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
|
|
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
|
+#define SQLITE_INDEX_CONSTRAINT_GT 4
|
|
+#define SQLITE_INDEX_CONSTRAINT_LE 8
|
|
+#define SQLITE_INDEX_CONSTRAINT_LT 16
|
|
+#define SQLITE_INDEX_CONSTRAINT_GE 32
|
|
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
|
|
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
|
|
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
|
|
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
|
|
+#define SQLITE_INDEX_CONSTRAINT_NE 68
|
|
+#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
|
|
+#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
|
|
+#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
|
|
+#define SQLITE_INDEX_CONSTRAINT_IS 72
|
|
+#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
|
|
|
|
/*
|
|
** CAPI3REF: Register A Virtual Table Implementation
|
|
@@ -7917,6 +8197,7 @@
|
|
/*
|
|
** CAPI3REF: Low-Level Control Of Database Files
|
|
** METHOD: sqlite3
|
|
+** KEYWORDS: {file control}
|
|
**
|
|
** ^The [sqlite3_file_control()] interface makes a direct call to the
|
|
** xFileControl method for the [sqlite3_io_methods] object associated
|
|
@@ -7931,11 +8212,18 @@
|
|
** the xFileControl method. ^The return value of the xFileControl
|
|
** method becomes the return value of this routine.
|
|
**
|
|
-** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes
|
|
+** A few opcodes for [sqlite3_file_control()] are handled directly
|
|
+** by the SQLite core and never invoke the
|
|
+** sqlite3_io_methods.xFileControl method.
|
|
+** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes
|
|
** a pointer to the underlying [sqlite3_file] object to be written into
|
|
-** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER
|
|
-** case is a short-circuit path which does not actually invoke the
|
|
-** underlying sqlite3_io_methods.xFileControl method.
|
|
+** the space pointed to by the 4th parameter. The
|
|
+** [SQLITE_FCNTL_JOURNAL_POINTER] works similarly except that it returns
|
|
+** the [sqlite3_file] object associated with the journal file instead of
|
|
+** the main database. The [SQLITE_FCNTL_VFS_POINTER] opcode returns
|
|
+** a pointer to the underlying [sqlite3_vfs] object for the file.
|
|
+** The [SQLITE_FCNTL_DATA_VERSION] returns the data version counter
|
|
+** from the pager.
|
|
**
|
|
** ^If the second parameter (zDbName) does not match the name of any
|
|
** open database file, then SQLITE_ERROR is returned. ^This error
|
|
@@ -7945,7 +8233,7 @@
|
|
** an incorrect zDbName and an SQLITE_ERROR return from the underlying
|
|
** xFileControl method.
|
|
**
|
|
-** See also: [SQLITE_FCNTL_LOCKSTATE]
|
|
+** See also: [file control opcodes]
|
|
*/
|
|
SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);
|
|
|
|
@@ -7991,8 +8279,9 @@
|
|
#define SQLITE_TESTCTRL_ALWAYS 13
|
|
#define SQLITE_TESTCTRL_RESERVE 14
|
|
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
|
|
-#define SQLITE_TESTCTRL_ISKEYWORD 16
|
|
-#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
|
+#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
|
|
+#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
|
|
+#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
|
|
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
|
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
|
|
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
|
|
@@ -8002,9 +8291,193 @@
|
|
#define SQLITE_TESTCTRL_ISINIT 23
|
|
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
|
#define SQLITE_TESTCTRL_IMPOSTER 25
|
|
-#define SQLITE_TESTCTRL_LAST 25
|
|
+#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
|
+#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
|
|
|
/*
|
|
+** CAPI3REF: SQL Keyword Checking
|
|
+**
|
|
+** These routines provide access to the set of SQL language keywords
|
|
+** recognized by SQLite. Applications can uses these routines to determine
|
|
+** whether or not a specific identifier needs to be escaped (for example,
|
|
+** by enclosing in double-quotes) so as not to confuse the parser.
|
|
+**
|
|
+** The sqlite3_keyword_count() interface returns the number of distinct
|
|
+** keywords understood by SQLite.
|
|
+**
|
|
+** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
|
|
+** makes *Z point to that keyword expressed as UTF8 and writes the number
|
|
+** of bytes in the keyword into *L. The string that *Z points to is not
|
|
+** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
|
|
+** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z
|
|
+** or L are NULL or invalid pointers then calls to
|
|
+** sqlite3_keyword_name(N,Z,L) result in undefined behavior.
|
|
+**
|
|
+** The sqlite3_keyword_check(Z,L) interface checks to see whether or not
|
|
+** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero
|
|
+** if it is and zero if not.
|
|
+**
|
|
+** The parser used by SQLite is forgiving. It is often possible to use
|
|
+** a keyword as an identifier as long as such use does not result in a
|
|
+** parsing ambiguity. For example, the statement
|
|
+** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and
|
|
+** creates a new table named "BEGIN" with three columns named
|
|
+** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid
|
|
+** using keywords as identifiers. Common techniques used to avoid keyword
|
|
+** name collisions include:
|
|
+** <ul>
|
|
+** <li> Put all identifier names inside double-quotes. This is the official
|
|
+** SQL way to escape identifier names.
|
|
+** <li> Put identifier names inside [...]. This is not standard SQL,
|
|
+** but it is what SQL Server does and so lots of programmers use this
|
|
+** technique.
|
|
+** <li> Begin every identifier with the letter "Z" as no SQL keywords start
|
|
+** with "Z".
|
|
+** <li> Include a digit somewhere in every identifier name.
|
|
+** </ul>
|
|
+**
|
|
+** Note that the number of keywords understood by SQLite can depend on
|
|
+** compile-time options. For example, "VACUUM" is not a keyword if
|
|
+** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also,
|
|
+** new keywords may be added to future releases of SQLite.
|
|
+*/
|
|
+SQLITE_API int sqlite3_keyword_count(void);
|
|
+SQLITE_API int sqlite3_keyword_name(int,const char**,int*);
|
|
+SQLITE_API int sqlite3_keyword_check(const char*,int);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Dynamic String Object
|
|
+** KEYWORDS: {dynamic string}
|
|
+**
|
|
+** An instance of the sqlite3_str object contains a dynamically-sized
|
|
+** string under construction.
|
|
+**
|
|
+** The lifecycle of an sqlite3_str object is as follows:
|
|
+** <ol>
|
|
+** <li> ^The sqlite3_str object is created using [sqlite3_str_new()].
|
|
+** <li> ^Text is appended to the sqlite3_str object using various
|
|
+** methods, such as [sqlite3_str_appendf()].
|
|
+** <li> ^The sqlite3_str object is destroyed and the string it created
|
|
+** is returned using the [sqlite3_str_finish()] interface.
|
|
+** </ol>
|
|
+*/
|
|
+typedef struct sqlite3_str sqlite3_str;
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Create A New Dynamic String Object
|
|
+** CONSTRUCTOR: sqlite3_str
|
|
+**
|
|
+** ^The [sqlite3_str_new(D)] interface allocates and initializes
|
|
+** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
|
|
+** [sqlite3_str_new()] must be freed by a subsequent call to
|
|
+** [sqlite3_str_finish(X)].
|
|
+**
|
|
+** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
|
|
+** valid [sqlite3_str] object, though in the event of an out-of-memory
|
|
+** error the returned object might be a special singleton that will
|
|
+** silently reject new text, always return SQLITE_NOMEM from
|
|
+** [sqlite3_str_errcode()], always return 0 for
|
|
+** [sqlite3_str_length()], and always return NULL from
|
|
+** [sqlite3_str_finish(X)]. It is always safe to use the value
|
|
+** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
|
|
+** to any of the other [sqlite3_str] methods.
|
|
+**
|
|
+** The D parameter to [sqlite3_str_new(D)] may be NULL. If the
|
|
+** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum
|
|
+** length of the string contained in the [sqlite3_str] object will be
|
|
+** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead
|
|
+** of [SQLITE_MAX_LENGTH].
|
|
+*/
|
|
+SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Finalize A Dynamic String
|
|
+** DESTRUCTOR: sqlite3_str
|
|
+**
|
|
+** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X
|
|
+** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()]
|
|
+** that contains the constructed string. The calling application should
|
|
+** pass the returned value to [sqlite3_free()] to avoid a memory leak.
|
|
+** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
|
|
+** errors were encountered during construction of the string. ^The
|
|
+** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
|
|
+** string in [sqlite3_str] object X is zero bytes long.
|
|
+*/
|
|
+SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Add Content To A Dynamic String
|
|
+** METHOD: sqlite3_str
|
|
+**
|
|
+** These interfaces add content to an sqlite3_str object previously obtained
|
|
+** from [sqlite3_str_new()].
|
|
+**
|
|
+** ^The [sqlite3_str_appendf(X,F,...)] and
|
|
+** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
|
|
+** functionality of SQLite to append formatted text onto the end of
|
|
+** [sqlite3_str] object X.
|
|
+**
|
|
+** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
|
|
+** onto the end of the [sqlite3_str] object X. N must be non-negative.
|
|
+** S must contain at least N non-zero bytes of content. To append a
|
|
+** zero-terminated string in its entirety, use the [sqlite3_str_appendall()]
|
|
+** method instead.
|
|
+**
|
|
+** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of
|
|
+** zero-terminated string S onto the end of [sqlite3_str] object X.
|
|
+**
|
|
+** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the
|
|
+** single-byte character C onto the end of [sqlite3_str] object X.
|
|
+** ^This method can be used, for example, to add whitespace indentation.
|
|
+**
|
|
+** ^The [sqlite3_str_reset(X)] method resets the string under construction
|
|
+** inside [sqlite3_str] object X back to zero bytes in length.
|
|
+**
|
|
+** These methods do not return a result code. ^If an error occurs, that fact
|
|
+** is recorded in the [sqlite3_str] object and can be recovered by a
|
|
+** subsequent call to [sqlite3_str_errcode(X)].
|
|
+*/
|
|
+SQLITE_API void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...);
|
|
+SQLITE_API void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list);
|
|
+SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N);
|
|
+SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn);
|
|
+SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C);
|
|
+SQLITE_API void sqlite3_str_reset(sqlite3_str*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Status Of A Dynamic String
|
|
+** METHOD: sqlite3_str
|
|
+**
|
|
+** These interfaces return the current status of an [sqlite3_str] object.
|
|
+**
|
|
+** ^If any prior errors have occurred while constructing the dynamic string
|
|
+** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return
|
|
+** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns
|
|
+** [SQLITE_NOMEM] following any out-of-memory error, or
|
|
+** [SQLITE_TOOBIG] if the size of the dynamic string exceeds
|
|
+** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors.
|
|
+**
|
|
+** ^The [sqlite3_str_length(X)] method returns the current length, in bytes,
|
|
+** of the dynamic string under construction in [sqlite3_str] object X.
|
|
+** ^The length returned by [sqlite3_str_length(X)] does not include the
|
|
+** zero-termination byte.
|
|
+**
|
|
+** ^The [sqlite3_str_value(X)] method returns a pointer to the current
|
|
+** content of the dynamic string under construction in X. The value
|
|
+** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
|
|
+** and might be freed or altered by any subsequent method on the same
|
|
+** [sqlite3_str] object. Applications must not used the pointer returned
|
|
+** [sqlite3_str_value(X)] after any subsequent method call on the same
|
|
+** object. ^Applications may change the content of the string returned
|
|
+** by [sqlite3_str_value(X)] as long as they do not write into any bytes
|
|
+** outside the range of 0 to [sqlite3_str_length(X)] and do not read or
|
|
+** write any byte after any subsequent sqlite3_str method call.
|
|
+*/
|
|
+SQLITE_API int sqlite3_str_errcode(sqlite3_str*);
|
|
+SQLITE_API int sqlite3_str_length(sqlite3_str*);
|
|
+SQLITE_API char *sqlite3_str_value(sqlite3_str*);
|
|
+
|
|
+/*
|
|
** CAPI3REF: SQLite Runtime Status
|
|
**
|
|
** ^These interfaces are used to retrieve runtime status information
|
|
@@ -8051,8 +8524,7 @@
|
|
** <dd>This parameter is the current amount of memory checked out
|
|
** using [sqlite3_malloc()], either directly or indirectly. The
|
|
** figure includes calls made to [sqlite3_malloc()] by the application
|
|
-** and internal memory usage by the SQLite library. Scratch memory
|
|
-** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache
|
|
+** and internal memory usage by the SQLite library. Auxiliary page-cache
|
|
** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in
|
|
** this parameter. The amount returned is the sum of the allocation
|
|
** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^
|
|
@@ -8090,29 +8562,14 @@
|
|
** *pHighwater parameter to [sqlite3_status()] is of interest.
|
|
** The value written into the *pCurrent parameter is undefined.</dd>)^
|
|
**
|
|
-** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
|
|
-** <dd>This parameter returns the number of allocations used out of the
|
|
-** [scratch memory allocator] configured using
|
|
-** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not
|
|
-** in bytes. Since a single thread may only have one scratch allocation
|
|
-** outstanding at time, this parameter also reports the number of threads
|
|
-** using scratch memory at the same time.</dd>)^
|
|
+** [[SQLITE_STATUS_SCRATCH_USED]] <dt>SQLITE_STATUS_SCRATCH_USED</dt>
|
|
+** <dd>No longer used.</dd>
|
|
**
|
|
** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
|
|
-** <dd>This parameter returns the number of bytes of scratch memory
|
|
-** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH]
|
|
-** buffer and where forced to overflow to [sqlite3_malloc()]. The values
|
|
-** returned include overflows because the requested allocation was too
|
|
-** larger (that is, because the requested allocation was larger than the
|
|
-** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer
|
|
-** slots were available.
|
|
-** </dd>)^
|
|
+** <dd>No longer used.</dd>
|
|
**
|
|
-** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
|
|
-** <dd>This parameter records the largest memory allocation request
|
|
-** handed to [scratch memory allocator]. Only the value returned in the
|
|
-** *pHighwater parameter to [sqlite3_status()] is of interest.
|
|
-** The value written into the *pCurrent parameter is undefined.</dd>)^
|
|
+** [[SQLITE_STATUS_SCRATCH_SIZE]] <dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
|
|
+** <dd>No longer used.</dd>
|
|
**
|
|
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
|
|
** <dd>The *pHighwater parameter records the deepest parser stack.
|
|
@@ -8125,12 +8582,12 @@
|
|
#define SQLITE_STATUS_MEMORY_USED 0
|
|
#define SQLITE_STATUS_PAGECACHE_USED 1
|
|
#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2
|
|
-#define SQLITE_STATUS_SCRATCH_USED 3
|
|
-#define SQLITE_STATUS_SCRATCH_OVERFLOW 4
|
|
+#define SQLITE_STATUS_SCRATCH_USED 3 /* NOT USED */
|
|
+#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 /* NOT USED */
|
|
#define SQLITE_STATUS_MALLOC_SIZE 5
|
|
#define SQLITE_STATUS_PARSER_STACK 6
|
|
#define SQLITE_STATUS_PAGECACHE_SIZE 7
|
|
-#define SQLITE_STATUS_SCRATCH_SIZE 8
|
|
+#define SQLITE_STATUS_SCRATCH_SIZE 8 /* NOT USED */
|
|
#define SQLITE_STATUS_MALLOC_COUNT 9
|
|
|
|
/*
|
|
@@ -8253,6 +8710,15 @@
|
|
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
|
|
** </dd>
|
|
**
|
|
+** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
|
|
+** <dd>This parameter returns the number of dirty cache entries that have
|
|
+** been written to disk in the middle of a transaction due to the page
|
|
+** cache overflowing. Transactions are more efficient if they are written
|
|
+** to disk all at once. When pages spill mid-transaction, that introduces
|
|
+** additional overhead. This parameter can be used help identify
|
|
+** inefficiencies that can be resolve by increasing the cache size.
|
|
+** </dd>
|
|
+**
|
|
** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
|
|
** <dd>This parameter returns zero for the current value if and only if
|
|
** all foreign key constraints (deferred or immediate) have been
|
|
@@ -8272,7 +8738,8 @@
|
|
#define SQLITE_DBSTATUS_CACHE_WRITE 9
|
|
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
|
|
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
|
|
-#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */
|
|
+#define SQLITE_DBSTATUS_CACHE_SPILL 12
|
|
+#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
|
|
|
|
|
|
/*
|
|
@@ -9227,6 +9694,7 @@
|
|
** can use to customize and optimize their behavior.
|
|
**
|
|
** <dl>
|
|
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
|
|
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
|
|
** <dd>Calls of the form
|
|
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
|
|
@@ -9273,6 +9741,40 @@
|
|
SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
|
|
|
|
/*
|
|
+** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE
|
|
+**
|
|
+** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn]
|
|
+** method of a [virtual table], then it returns true if and only if the
|
|
+** column is being fetched as part of an UPDATE operation during which the
|
|
+** column value will not change. Applications might use this to substitute
|
|
+** a return value that is less expensive to compute and that the corresponding
|
|
+** [xUpdate] method understands as a "no-change" value.
|
|
+**
|
|
+** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
|
|
+** the column is not changed by the UPDATE statement, then the xColumn
|
|
+** method can optionally return without setting a result, without calling
|
|
+** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
|
|
+** In that case, [sqlite3_value_nochange(X)] will return true for the
|
|
+** same column in the [xUpdate] method.
|
|
+*/
|
|
+SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Determine The Collation For a Virtual Table Constraint
|
|
+**
|
|
+** This function may only be called from within a call to the [xBestIndex]
|
|
+** method of a [virtual table].
|
|
+**
|
|
+** The first argument must be the sqlite3_index_info object that is the
|
|
+** first parameter to the xBestIndex() method. The second argument must be
|
|
+** an index into the aConstraint[] array belonging to the sqlite3_index_info
|
|
+** structure passed to xBestIndex. This function returns a pointer to a buffer
|
|
+** containing the name of the collation sequence for the corresponding
|
|
+** constraint.
|
|
+*/
|
|
+SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
|
+
|
|
+/*
|
|
** CAPI3REF: Conflict resolution modes
|
|
** KEYWORDS: {conflict resolution mode}
|
|
**
|
|
@@ -9542,7 +10044,6 @@
|
|
/*
|
|
** CAPI3REF: Database Snapshot
|
|
** KEYWORDS: {snapshot} {sqlite3_snapshot}
|
|
-** EXPERIMENTAL
|
|
**
|
|
** An instance of the snapshot object records the state of a [WAL mode]
|
|
** database for some specific point in history.
|
|
@@ -9559,11 +10060,6 @@
|
|
** version of the database file so that it is possible to later open a new read
|
|
** transaction that sees that historical version of the database rather than
|
|
** the most recent version.
|
|
-**
|
|
-** The constructor for this object is [sqlite3_snapshot_get()]. The
|
|
-** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
|
|
-** to an historical snapshot (if possible). The destructor for
|
|
-** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
|
|
*/
|
|
typedef struct sqlite3_snapshot {
|
|
unsigned char hidden[48];
|
|
@@ -9571,7 +10067,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Record A Database Snapshot
|
|
-** EXPERIMENTAL
|
|
+** CONSTRUCTOR: sqlite3_snapshot
|
|
**
|
|
** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
|
|
** new [sqlite3_snapshot] object that records the current state of
|
|
@@ -9587,7 +10083,7 @@
|
|
** in this case.
|
|
**
|
|
** <ul>
|
|
-** <li> The database handle must be in [autocommit mode].
|
|
+** <li> The database handle must not be in [autocommit mode].
|
|
**
|
|
** <li> Schema S of [database connection] D must be a [WAL mode] database.
|
|
**
|
|
@@ -9610,7 +10106,7 @@
|
|
** to avoid a memory leak.
|
|
**
|
|
** The [sqlite3_snapshot_get()] interface is only available when the
|
|
-** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
|
+** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
|
|
sqlite3 *db,
|
|
@@ -9620,24 +10116,35 @@
|
|
|
|
/*
|
|
** CAPI3REF: Start a read transaction on an historical snapshot
|
|
-** EXPERIMENTAL
|
|
+** METHOD: sqlite3_snapshot
|
|
**
|
|
-** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
|
|
-** read transaction for schema S of
|
|
-** [database connection] D such that the read transaction
|
|
-** refers to historical [snapshot] P, rather than the most
|
|
-** recent change to the database.
|
|
-** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
|
|
-** or an appropriate [error code] if it fails.
|
|
+** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read
|
|
+** transaction or upgrades an existing one for schema S of
|
|
+** [database connection] D such that the read transaction refers to
|
|
+** historical [snapshot] P, rather than the most recent change to the
|
|
+** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK
|
|
+** on success or an appropriate [error code] if it fails.
|
|
**
|
|
-** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
|
|
-** the first operation following the [BEGIN] that takes the schema S
|
|
-** out of [autocommit mode].
|
|
-** ^In other words, schema S must not currently be in
|
|
-** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
|
|
-** database connection D must be out of [autocommit mode].
|
|
-** ^A [snapshot] will fail to open if it has been overwritten by a
|
|
-** [checkpoint].
|
|
+** ^In order to succeed, the database connection must not be in
|
|
+** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there
|
|
+** is already a read transaction open on schema S, then the database handle
|
|
+** must have no active statements (SELECT statements that have been passed
|
|
+** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()).
|
|
+** SQLITE_ERROR is returned if either of these conditions is violated, or
|
|
+** if schema S does not exist, or if the snapshot object is invalid.
|
|
+**
|
|
+** ^A call to sqlite3_snapshot_open() will fail to open if the specified
|
|
+** snapshot has been overwritten by a [checkpoint]. In this case
|
|
+** SQLITE_ERROR_SNAPSHOT is returned.
|
|
+**
|
|
+** If there is already a read transaction open when this function is
|
|
+** invoked, then the same read transaction remains open (on the same
|
|
+** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
|
|
+** is returned. If another error code - for example SQLITE_PROTOCOL or an
|
|
+** SQLITE_IOERR error code - is returned, then the final state of the
|
|
+** read transaction is undefined. If SQLITE_OK is returned, then the
|
|
+** read transaction is now open on database snapshot P.
|
|
+**
|
|
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
|
|
** database connection D does not know that the database file for
|
|
** schema S is in [WAL mode]. A database connection might not know
|
|
@@ -9648,7 +10155,7 @@
|
|
** database connection in order to make it ready to use snapshots.)
|
|
**
|
|
** The [sqlite3_snapshot_open()] interface is only available when the
|
|
-** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
|
+** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
|
|
sqlite3 *db,
|
|
@@ -9658,7 +10165,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Destroy a snapshot
|
|
-** EXPERIMENTAL
|
|
+** DESTRUCTOR: sqlite3_snapshot
|
|
**
|
|
** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
|
|
** The application must eventually free every [sqlite3_snapshot] object
|
|
@@ -9665,13 +10172,13 @@
|
|
** using this routine to avoid a memory leak.
|
|
**
|
|
** The [sqlite3_snapshot_free()] interface is only available when the
|
|
-** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
|
+** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
|
|
|
|
/*
|
|
** CAPI3REF: Compare the ages of two snapshot handles.
|
|
-** EXPERIMENTAL
|
|
+** METHOD: sqlite3_snapshot
|
|
**
|
|
** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
|
|
** of two valid snapshot handles.
|
|
@@ -9690,6 +10197,9 @@
|
|
** Otherwise, this API returns a negative value if P1 refers to an older
|
|
** snapshot than P2, zero if the two handles refer to the same database
|
|
** snapshot, and a positive value if P1 is a newer snapshot than P2.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_SNAPSHOT] option.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
|
sqlite3_snapshot *p1,
|
|
@@ -9698,27 +10208,152 @@
|
|
|
|
/*
|
|
** CAPI3REF: Recover snapshots from a wal file
|
|
-** EXPERIMENTAL
|
|
+** METHOD: sqlite3_snapshot
|
|
**
|
|
-** If all connections disconnect from a database file but do not perform
|
|
-** a checkpoint, the existing wal file is opened along with the database
|
|
-** file the next time the database is opened. At this point it is only
|
|
-** possible to successfully call sqlite3_snapshot_open() to open the most
|
|
-** recent snapshot of the database (the one at the head of the wal file),
|
|
-** even though the wal file may contain other valid snapshots for which
|
|
-** clients have sqlite3_snapshot handles.
|
|
+** If a [WAL file] remains on disk after all database connections close
|
|
+** (either through the use of the [SQLITE_FCNTL_PERSIST_WAL] [file control]
|
|
+** or because the last process to have the database opened exited without
|
|
+** calling [sqlite3_close()]) and a new connection is subsequently opened
|
|
+** on that database and [WAL file], the [sqlite3_snapshot_open()] interface
|
|
+** will only be able to open the last transaction added to the WAL file
|
|
+** even though the WAL file contains other valid transactions.
|
|
**
|
|
-** This function attempts to scan the wal file associated with database zDb
|
|
+** This function attempts to scan the WAL file associated with database zDb
|
|
** of database handle db and make all valid snapshots available to
|
|
** sqlite3_snapshot_open(). It is an error if there is already a read
|
|
-** transaction open on the database, or if the database is not a wal mode
|
|
+** transaction open on the database, or if the database is not a WAL mode
|
|
** database.
|
|
**
|
|
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_SNAPSHOT] option.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
|
|
|
|
/*
|
|
+** CAPI3REF: Serialize a database
|
|
+**
|
|
+** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
|
|
+** that is a serialization of the S database on [database connection] D.
|
|
+** If P is not a NULL pointer, then the size of the database in bytes
|
|
+** is written into *P.
|
|
+**
|
|
+** For an ordinary on-disk database file, the serialization is just a
|
|
+** copy of the disk file. For an in-memory database or a "TEMP" database,
|
|
+** the serialization is the same sequence of bytes which would be written
|
|
+** to disk if that database where backed up to disk.
|
|
+**
|
|
+** The usual case is that sqlite3_serialize() copies the serialization of
|
|
+** the database into memory obtained from [sqlite3_malloc64()] and returns
|
|
+** a pointer to that memory. The caller is responsible for freeing the
|
|
+** returned value to avoid a memory leak. However, if the F argument
|
|
+** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations
|
|
+** are made, and the sqlite3_serialize() function will return a pointer
|
|
+** to the contiguous memory representation of the database that SQLite
|
|
+** is currently using for that database, or NULL if the no such contiguous
|
|
+** memory representation of the database exists. A contiguous memory
|
|
+** representation of the database will usually only exist if there has
|
|
+** been a prior call to [sqlite3_deserialize(D,S,...)] with the same
|
|
+** values of D and S.
|
|
+** The size of the database is written into *P even if the
|
|
+** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
|
|
+** of the database exists.
|
|
+**
|
|
+** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
|
|
+** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
|
|
+** allocation error occurs.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_DESERIALIZE] option.
|
|
+*/
|
|
+SQLITE_API unsigned char *sqlite3_serialize(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */
|
|
+ sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */
|
|
+ unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3_serialize
|
|
+**
|
|
+** Zero or more of the following constants can be OR-ed together for
|
|
+** the F argument to [sqlite3_serialize(D,S,P,F)].
|
|
+**
|
|
+** SQLITE_SERIALIZE_NOCOPY means that [sqlite3_serialize()] will return
|
|
+** a pointer to contiguous in-memory database that it is currently using,
|
|
+** without making a copy of the database. If SQLite is not currently using
|
|
+** a contiguous in-memory database, then this option causes
|
|
+** [sqlite3_serialize()] to return a NULL pointer. SQLite will only be
|
|
+** using a contiguous in-memory database if it has been initialized by a
|
|
+** prior call to [sqlite3_deserialize()].
|
|
+*/
|
|
+#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Deserialize a database
|
|
+**
|
|
+** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
|
|
+** [database connection] D to disconnect from database S and then
|
|
+** reopen S as an in-memory database based on the serialization contained
|
|
+** in P. The serialized database P is N bytes in size. M is the size of
|
|
+** the buffer P, which might be larger than N. If M is larger than N, and
|
|
+** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
|
|
+** permitted to add content to the in-memory database as long as the total
|
|
+** size does not exceed M bytes.
|
|
+**
|
|
+** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
|
|
+** invoke sqlite3_free() on the serialization buffer when the database
|
|
+** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
|
|
+** SQLite will try to increase the buffer size using sqlite3_realloc64()
|
|
+** if writes on the database cause it to grow larger than M bytes.
|
|
+**
|
|
+** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
|
|
+** database is currently in a read transaction or is involved in a backup
|
|
+** operation.
|
|
+**
|
|
+** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
|
|
+** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
|
|
+** [sqlite3_free()] is invoked on argument P prior to returning.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_DESERIALIZE] option.
|
|
+*/
|
|
+SQLITE_API int sqlite3_deserialize(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which DB to reopen with the deserialization */
|
|
+ unsigned char *pData, /* The serialized database content */
|
|
+ sqlite3_int64 szDb, /* Number bytes in the deserialization */
|
|
+ sqlite3_int64 szBuf, /* Total size of buffer pData[] */
|
|
+ unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3_deserialize()
|
|
+**
|
|
+** The following are allowed values for 6th argument (the F argument) to
|
|
+** the [sqlite3_deserialize(D,S,P,N,M,F)] interface.
|
|
+**
|
|
+** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization
|
|
+** in the P argument is held in memory obtained from [sqlite3_malloc64()]
|
|
+** and that SQLite should take ownership of this memory and automatically
|
|
+** free it when it has finished using it. Without this flag, the caller
|
|
+** is responsible for freeing any dynamically allocated memory.
|
|
+**
|
|
+** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
|
|
+** grow the size of the database using calls to [sqlite3_realloc64()]. This
|
|
+** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
|
|
+** Without this flag, the deserialized database cannot increase in size beyond
|
|
+** the number of bytes specified by the M parameter.
|
|
+**
|
|
+** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database
|
|
+** should be treated as read-only.
|
|
+*/
|
|
+#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
|
|
+#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
|
|
+#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
|
|
+
|
|
+/*
|
|
** Undo the hack that converts floating point types to integer for
|
|
** builds on processors without floating point support.
|
|
*/
|
|
@@ -9829,7 +10464,7 @@
|
|
sqlite3_int64 iRowid; /* Rowid for current entry */
|
|
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
|
|
int eParentWithin; /* Visibility of parent node */
|
|
- int eWithin; /* OUT: Visiblity */
|
|
+ int eWithin; /* OUT: Visibility */
|
|
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
|
|
/* The following fields are only available in 3.8.11 and later */
|
|
sqlite3_value **apSqlParam; /* Original SQL values of parameters */
|
|
@@ -9865,16 +10500,23 @@
|
|
|
|
/*
|
|
** CAPI3REF: Session Object Handle
|
|
+**
|
|
+** An instance of this object is a [session] that can be used to
|
|
+** record changes to a database.
|
|
*/
|
|
typedef struct sqlite3_session sqlite3_session;
|
|
|
|
/*
|
|
** CAPI3REF: Changeset Iterator Handle
|
|
+**
|
|
+** An instance of this object acts as a cursor for iterating
|
|
+** over the elements of a [changeset] or [patchset].
|
|
*/
|
|
typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
|
|
|
|
/*
|
|
** CAPI3REF: Create A New Session Object
|
|
+** CONSTRUCTOR: sqlite3_session
|
|
**
|
|
** Create a new session object attached to database handle db. If successful,
|
|
** a pointer to the new object is written to *ppSession and SQLITE_OK is
|
|
@@ -9911,6 +10553,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Delete A Session Object
|
|
+** DESTRUCTOR: sqlite3_session
|
|
**
|
|
** Delete a session object previously allocated using
|
|
** [sqlite3session_create()]. Once a session object has been deleted, the
|
|
@@ -9926,6 +10569,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Enable Or Disable A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** Enable or disable the recording of changes by a session object. When
|
|
** enabled, a session object records changes made to the database. When
|
|
@@ -9945,6 +10589,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Set Or Clear the Indirect Change Flag
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** Each change recorded by a session object is marked as either direct or
|
|
** indirect. A change is marked as indirect if either:
|
|
@@ -9974,6 +10619,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Attach A Table To A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** If argument zTab is not NULL, then it is the name of a table to attach
|
|
** to the session object passed as the first argument. All subsequent changes
|
|
@@ -9999,6 +10645,35 @@
|
|
**
|
|
** SQLITE_OK is returned if the call completes without error. Or, if an error
|
|
** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
|
|
+**
|
|
+** <h3>Special sqlite_stat1 Handling</h3>
|
|
+**
|
|
+** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to
|
|
+** some of the rules above. In SQLite, the schema of sqlite_stat1 is:
|
|
+** <pre>
|
|
+** CREATE TABLE sqlite_stat1(tbl,idx,stat)
|
|
+** </pre>
|
|
+**
|
|
+** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are
|
|
+** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes
|
|
+** are recorded for rows for which (idx IS NULL) is true. However, for such
|
|
+** rows a zero-length blob (SQL value X'') is stored in the changeset or
|
|
+** patchset instead of a NULL value. This allows such changesets to be
|
|
+** manipulated by legacy implementations of sqlite3changeset_invert(),
|
|
+** concat() and similar.
|
|
+**
|
|
+** The sqlite3changeset_apply() function automatically converts the
|
|
+** zero-length blob back to a NULL value when updating the sqlite_stat1
|
|
+** table. However, if the application calls sqlite3changeset_new(),
|
|
+** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset
|
|
+** iterator directly (including on a changeset iterator passed to a
|
|
+** conflict-handler callback) then the X'' value is returned. The application
|
|
+** must translate X'' to NULL itself if required.
|
|
+**
|
|
+** Legacy (older than 3.22.0) versions of the sessions module cannot capture
|
|
+** changes made to the sqlite_stat1 table. Legacy versions of the
|
|
+** sqlite3changeset_apply() function silently ignore any modifications to the
|
|
+** sqlite_stat1 table that are part of a changeset or patchset.
|
|
*/
|
|
SQLITE_API int sqlite3session_attach(
|
|
sqlite3_session *pSession, /* Session object */
|
|
@@ -10007,6 +10682,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Set a table filter on a Session Object.
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** 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
|
|
@@ -10025,6 +10701,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Generate A Changeset From A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** Obtain a changeset containing changes to the tables attached to the
|
|
** session object passed as the first argument. If successful,
|
|
@@ -10134,7 +10811,8 @@
|
|
);
|
|
|
|
/*
|
|
-** CAPI3REF: Load The Difference Between Tables Into A Session
|
|
+** CAPI3REF: Load The Difference Between Tables Into A Session
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** If it is not already attached to the session object passed as the first
|
|
** argument, this function attaches table zTbl in the same manner as the
|
|
@@ -10199,6 +10877,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Generate A Patchset From A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** The differences between a patchset and a changeset are that:
|
|
**
|
|
@@ -10227,8 +10906,8 @@
|
|
*/
|
|
SQLITE_API int sqlite3session_patchset(
|
|
sqlite3_session *pSession, /* Session object */
|
|
- int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
|
|
- void **ppPatchset /* OUT: Buffer containing changeset */
|
|
+ int *pnPatchset, /* OUT: Size of buffer at *ppPatchset */
|
|
+ void **ppPatchset /* OUT: Buffer containing patchset */
|
|
);
|
|
|
|
/*
|
|
@@ -10250,6 +10929,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Create An Iterator To Traverse A Changeset
|
|
+** CONSTRUCTOR: sqlite3_changeset_iter
|
|
**
|
|
** Create an iterator used to iterate through the contents of a changeset.
|
|
** If successful, *pp is set to point to the iterator handle and SQLITE_OK
|
|
@@ -10280,6 +10960,13 @@
|
|
** consecutively. There is no chance that the iterator will visit a change
|
|
** the applies to table X, then one for table Y, and then later on visit
|
|
** another change for table X.
|
|
+**
|
|
+** The behavior of sqlite3changeset_start_v2() and its streaming equivalent
|
|
+** may be modified by passing a combination of
|
|
+** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter.
|
|
+**
|
|
+** Note that the sqlite3changeset_start_v2() API is still <b>experimental</b>
|
|
+** and therefore subject to change.
|
|
*/
|
|
SQLITE_API int sqlite3changeset_start(
|
|
sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
|
|
@@ -10286,10 +10973,30 @@
|
|
int nChangeset, /* Size of changeset blob in bytes */
|
|
void *pChangeset /* Pointer to blob containing changeset */
|
|
);
|
|
+SQLITE_API int sqlite3changeset_start_v2(
|
|
+ sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
|
|
+ int nChangeset, /* Size of changeset blob in bytes */
|
|
+ void *pChangeset, /* Pointer to blob containing changeset */
|
|
+ int flags /* SESSION_CHANGESETSTART_* flags */
|
|
+);
|
|
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3changeset_start_v2
|
|
+**
|
|
+** The following flags may passed via the 4th parameter to
|
|
+** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
|
+**
|
|
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
|
+** Invert the changeset while iterating through it. This is equivalent to
|
|
+** inverting a changeset using sqlite3changeset_invert() before applying it.
|
|
+** It is an error to specify this flag with a patchset.
|
|
+*/
|
|
+#define SQLITE_CHANGESETSTART_INVERT 0x0002
|
|
|
|
+
|
|
/*
|
|
** CAPI3REF: Advance A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function may only be used with iterators created by function
|
|
** [sqlite3changeset_start()]. If it is called on an iterator passed to
|
|
@@ -10314,6 +11021,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain The Current Operation From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** The pIter argument passed to this function may either be an iterator
|
|
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
|
|
@@ -10348,6 +11056,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain The Primary Key Definition Of A Table
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** For each modified table, a changeset includes the following:
|
|
**
|
|
@@ -10379,6 +11088,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain old.* Values From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** The pIter argument passed to this function may either be an iterator
|
|
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
|
|
@@ -10409,6 +11119,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain new.* Values From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** The pIter argument passed to this function may either be an iterator
|
|
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
|
|
@@ -10442,6 +11153,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function should only be used with iterator objects passed to a
|
|
** conflict-handler callback by [sqlite3changeset_apply()] with either
|
|
@@ -10469,6 +11181,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function may only be called with an iterator passed to an
|
|
** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
|
|
@@ -10485,6 +11198,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Finalize A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function is used to finalize an iterator allocated with
|
|
** [sqlite3changeset_start()].
|
|
@@ -10501,6 +11215,7 @@
|
|
** to that error is returned by this function. Otherwise, SQLITE_OK is
|
|
** returned. This is to allow the following pattern (pseudo-code):
|
|
**
|
|
+** <pre>
|
|
** sqlite3changeset_start();
|
|
** while( SQLITE_ROW==sqlite3changeset_next() ){
|
|
** // Do something with change.
|
|
@@ -10509,6 +11224,7 @@
|
|
** if( rc!=SQLITE_OK ){
|
|
** // An error has occurred
|
|
** }
|
|
+** </pre>
|
|
*/
|
|
SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
|
|
|
|
@@ -10556,6 +11272,7 @@
|
|
** sqlite3_changegroup object. Calling it produces similar results as the
|
|
** following code fragment:
|
|
**
|
|
+** <pre>
|
|
** sqlite3_changegroup *pGrp;
|
|
** rc = sqlite3_changegroup_new(&pGrp);
|
|
** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
|
|
@@ -10566,6 +11283,7 @@
|
|
** *ppOut = 0;
|
|
** *pnOut = 0;
|
|
** }
|
|
+** </pre>
|
|
**
|
|
** Refer to the sqlite3_changegroup documentation below for details.
|
|
*/
|
|
@@ -10581,11 +11299,15 @@
|
|
|
|
/*
|
|
** CAPI3REF: Changegroup Handle
|
|
+**
|
|
+** A changegroup is an object used to combine two or more
|
|
+** [changesets] or [patchsets]
|
|
*/
|
|
typedef struct sqlite3_changegroup sqlite3_changegroup;
|
|
|
|
/*
|
|
** CAPI3REF: Create A New Changegroup Object
|
|
+** CONSTRUCTOR: sqlite3_changegroup
|
|
**
|
|
** An sqlite3_changegroup object is used to combine two or more changesets
|
|
** (or patchsets) into a single changeset (or patchset). A single changegroup
|
|
@@ -10623,6 +11345,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Add A Changeset To A Changegroup
|
|
+** METHOD: sqlite3_changegroup
|
|
**
|
|
** Add all changes within the changeset (or patchset) in buffer pData (size
|
|
** nData bytes) to the changegroup.
|
|
@@ -10700,6 +11423,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
|
|
+** METHOD: sqlite3_changegroup
|
|
**
|
|
** Obtain a buffer containing a changeset (or patchset) representing the
|
|
** current contents of the changegroup. If the inputs to the changegroup
|
|
@@ -10730,6 +11454,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Delete A Changegroup Object
|
|
+** DESTRUCTOR: sqlite3_changegroup
|
|
*/
|
|
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
|
|
|
|
@@ -10736,19 +11461,18 @@
|
|
/*
|
|
** CAPI3REF: Apply A Changeset To A Database
|
|
**
|
|
-** Apply a changeset to a database. This function attempts to update the
|
|
-** "main" database attached to handle db with the changes found in the
|
|
-** changeset passed via the second and third arguments.
|
|
+** Apply a changeset or patchset to a database. These functions attempt to
|
|
+** update the "main" database attached to handle db with the changes found in
|
|
+** the changeset passed via the second and third arguments.
|
|
**
|
|
-** The fourth argument (xFilter) passed to this function is the "filter
|
|
+** The fourth argument (xFilter) passed to these functions is the "filter
|
|
** callback". If it is not NULL, then for each table affected by at least one
|
|
** change in the changeset, the filter callback is invoked with
|
|
** the table name as the second argument, and a copy of the context pointer
|
|
-** passed as the sixth argument to this function as the first. If the "filter
|
|
-** callback" returns zero, then no attempt is made to apply any changes to
|
|
-** the table. Otherwise, if the return value is non-zero or the xFilter
|
|
-** argument to this function is NULL, all changes related to the table are
|
|
-** attempted.
|
|
+** passed as the sixth argument as the first. If the "filter callback"
|
|
+** returns zero, then no attempt is made to apply any changes to the table.
|
|
+** Otherwise, if the return value is non-zero or the xFilter argument to
|
|
+** is NULL, all changes related to the table are attempted.
|
|
**
|
|
** For each table that is not excluded by the filter callback, this function
|
|
** tests that the target database contains a compatible table. A table is
|
|
@@ -10793,7 +11517,7 @@
|
|
**
|
|
** <dl>
|
|
** <dt>DELETE Changes<dd>
|
|
-** For each DELETE change, this function checks if the target database
|
|
+** For each DELETE change, the function checks if the target database
|
|
** contains a row with the same primary key value (or values) as the
|
|
** original row values stored in the changeset. If it does, and the values
|
|
** stored in all non-primary key columns also match the values stored in
|
|
@@ -10838,7 +11562,7 @@
|
|
** [SQLITE_CHANGESET_REPLACE].
|
|
**
|
|
** <dt>UPDATE Changes<dd>
|
|
-** For each UPDATE change, this function checks if the target database
|
|
+** For each UPDATE change, the function checks if the target database
|
|
** contains a row with the same primary key value (or values) as the
|
|
** original row values stored in the changeset. If it does, and the values
|
|
** stored in all modified non-primary key columns also match the values
|
|
@@ -10869,11 +11593,28 @@
|
|
** This can be used to further customize the applications conflict
|
|
** resolution strategy.
|
|
**
|
|
-** All changes made by this function are enclosed in a savepoint transaction.
|
|
+** All changes made by these functions are enclosed in a savepoint transaction.
|
|
** If any other error (aside from a constraint failure when attempting to
|
|
** write to the target database) occurs, then the savepoint transaction is
|
|
** rolled back, restoring the target database to its original state, and an
|
|
** SQLite error code returned.
|
|
+**
|
|
+** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
|
|
+** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
|
|
+** may set (*ppRebase) to point to a "rebase" that may be used with the
|
|
+** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
|
|
+** is set to the size of the buffer in bytes. It is the responsibility of the
|
|
+** caller to eventually free any such buffer using sqlite3_free(). The buffer
|
|
+** is only allocated and populated if one or more conflicts were encountered
|
|
+** while applying the patchset. See comments surrounding the sqlite3_rebaser
|
|
+** APIs for further details.
|
|
+**
|
|
+** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent
|
|
+** may be modified by passing a combination of
|
|
+** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter.
|
|
+**
|
|
+** Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b>
|
|
+** and therefore subject to change.
|
|
*/
|
|
SQLITE_API int sqlite3changeset_apply(
|
|
sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
@@ -10890,7 +11631,48 @@
|
|
),
|
|
void *pCtx /* First argument passed to xConflict */
|
|
);
|
|
+SQLITE_API int sqlite3changeset_apply_v2(
|
|
+ sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
+ int nChangeset, /* Size of changeset in bytes */
|
|
+ void *pChangeset, /* Changeset blob */
|
|
+ int(*xFilter)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ const char *zTab /* Table name */
|
|
+ ),
|
|
+ int(*xConflict)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
+ ),
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
|
+ int flags /* SESSION_CHANGESETAPPLY_* flags */
|
|
+);
|
|
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3changeset_apply_v2
|
|
+**
|
|
+** The following flags may passed via the 9th parameter to
|
|
+** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]:
|
|
+**
|
|
+** <dl>
|
|
+** <dt>SQLITE_CHANGESETAPPLY_NOSAVEPOINT <dd>
|
|
+** Usually, the sessions module encloses all operations performed by
|
|
+** a single call to apply_v2() or apply_v2_strm() in a [SAVEPOINT]. The
|
|
+** SAVEPOINT is committed if the changeset or patchset is successfully
|
|
+** applied, or rolled back if an error occurs. Specifying this flag
|
|
+** causes the sessions module to omit this savepoint. In this case, if the
|
|
+** caller has an open transaction or savepoint when apply_v2() is called,
|
|
+** it may revert the partially applied changeset by rolling it back.
|
|
+**
|
|
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
|
+** Invert the changeset before applying it. This is equivalent to inverting
|
|
+** a changeset using sqlite3changeset_invert() before applying it. It is
|
|
+** an error to specify this flag with a patchset.
|
|
+*/
|
|
+#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
|
+#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
|
+
|
|
/*
|
|
** CAPI3REF: Constants Passed To The Conflict Handler
|
|
**
|
|
@@ -10987,7 +11769,162 @@
|
|
#define SQLITE_CHANGESET_REPLACE 1
|
|
#define SQLITE_CHANGESET_ABORT 2
|
|
|
|
+/*
|
|
+** CAPI3REF: Rebasing changesets
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Suppose there is a site hosting a database in state S0. And that
|
|
+** modifications are made that move that database to state S1 and a
|
|
+** changeset recorded (the "local" changeset). Then, a changeset based
|
|
+** on S0 is received from another site (the "remote" changeset) and
|
|
+** applied to the database. The database is then in state
|
|
+** (S1+"remote"), where the exact state depends on any conflict
|
|
+** resolution decisions (OMIT or REPLACE) made while applying "remote".
|
|
+** Rebasing a changeset is to update it to take those conflict
|
|
+** resolution decisions into account, so that the same conflicts
|
|
+** do not have to be resolved elsewhere in the network.
|
|
+**
|
|
+** For example, if both the local and remote changesets contain an
|
|
+** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)":
|
|
+**
|
|
+** local: INSERT INTO t1 VALUES(1, 'v1');
|
|
+** remote: INSERT INTO t1 VALUES(1, 'v2');
|
|
+**
|
|
+** and the conflict resolution is REPLACE, then the INSERT change is
|
|
+** removed from the local changeset (it was overridden). Or, if the
|
|
+** conflict resolution was "OMIT", then the local changeset is modified
|
|
+** to instead contain:
|
|
+**
|
|
+** UPDATE t1 SET b = 'v2' WHERE a=1;
|
|
+**
|
|
+** Changes within the local changeset are rebased as follows:
|
|
+**
|
|
+** <dl>
|
|
+** <dt>Local INSERT<dd>
|
|
+** This may only conflict with a remote INSERT. If the conflict
|
|
+** resolution was OMIT, then add an UPDATE change to the rebased
|
|
+** changeset. Or, if the conflict resolution was REPLACE, add
|
|
+** nothing to the rebased changeset.
|
|
+**
|
|
+** <dt>Local DELETE<dd>
|
|
+** This may conflict with a remote UPDATE or DELETE. In both cases the
|
|
+** only possible resolution is OMIT. If the remote operation was a
|
|
+** DELETE, then add no change to the rebased changeset. If the remote
|
|
+** operation was an UPDATE, then the old.* fields of change are updated
|
|
+** to reflect the new.* values in the UPDATE.
|
|
+**
|
|
+** <dt>Local UPDATE<dd>
|
|
+** This may conflict with a remote UPDATE or DELETE. If it conflicts
|
|
+** with a DELETE, and the conflict resolution was OMIT, then the update
|
|
+** is changed into an INSERT. Any undefined values in the new.* record
|
|
+** from the update change are filled in using the old.* values from
|
|
+** the conflicting DELETE. Or, if the conflict resolution was REPLACE,
|
|
+** the UPDATE change is simply omitted from the rebased changeset.
|
|
+**
|
|
+** If conflict is with a remote UPDATE and the resolution is OMIT, then
|
|
+** the old.* values are rebased using the new.* values in the remote
|
|
+** change. Or, if the resolution is REPLACE, then the change is copied
|
|
+** into the rebased changeset with updates to columns also updated by
|
|
+** the conflicting remote UPDATE removed. If this means no columns would
|
|
+** be updated, the change is omitted.
|
|
+** </dl>
|
|
+**
|
|
+** A local change may be rebased against multiple remote changes
|
|
+** simultaneously. If a single key is modified by multiple remote
|
|
+** changesets, they are combined as follows before the local changeset
|
|
+** is rebased:
|
|
+**
|
|
+** <ul>
|
|
+** <li> If there has been one or more REPLACE resolutions on a
|
|
+** key, it is rebased according to a REPLACE.
|
|
+**
|
|
+** <li> If there have been no REPLACE resolutions on a key, then
|
|
+** the local changeset is rebased according to the most recent
|
|
+** of the OMIT resolutions.
|
|
+** </ul>
|
|
+**
|
|
+** Note that conflict resolutions from multiple remote changesets are
|
|
+** combined on a per-field basis, not per-row. This means that in the
|
|
+** case of multiple remote UPDATE operations, some fields of a single
|
|
+** local change may be rebased for REPLACE while others are rebased for
|
|
+** OMIT.
|
|
+**
|
|
+** In order to rebase a local changeset, the remote changeset must first
|
|
+** be applied to the local database using sqlite3changeset_apply_v2() and
|
|
+** the buffer of rebase information captured. Then:
|
|
+**
|
|
+** <ol>
|
|
+** <li> An sqlite3_rebaser object is created by calling
|
|
+** sqlite3rebaser_create().
|
|
+** <li> The new object is configured with the rebase buffer obtained from
|
|
+** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure().
|
|
+** If the local changeset is to be rebased against multiple remote
|
|
+** changesets, then sqlite3rebaser_configure() should be called
|
|
+** multiple times, in the same order that the multiple
|
|
+** sqlite3changeset_apply_v2() calls were made.
|
|
+** <li> Each local changeset is rebased by calling sqlite3rebaser_rebase().
|
|
+** <li> The sqlite3_rebaser object is deleted by calling
|
|
+** sqlite3rebaser_delete().
|
|
+** </ol>
|
|
+*/
|
|
+typedef struct sqlite3_rebaser sqlite3_rebaser;
|
|
+
|
|
/*
|
|
+** CAPI3REF: Create a changeset rebaser object.
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Allocate a new changeset rebaser object. If successful, set (*ppNew) to
|
|
+** point to the new object and return SQLITE_OK. Otherwise, if an error
|
|
+** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
|
|
+** to NULL.
|
|
+*/
|
|
+SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Configure a changeset rebaser object.
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Configure the changeset rebaser object to rebase changesets according
|
|
+** to the conflict resolutions described by buffer pRebase (size nRebase
|
|
+** bytes), which must have been obtained from a previous call to
|
|
+** sqlite3changeset_apply_v2().
|
|
+*/
|
|
+SQLITE_API int sqlite3rebaser_configure(
|
|
+ sqlite3_rebaser*,
|
|
+ int nRebase, const void *pRebase
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Rebase a changeset
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** 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
|
|
+** rebaser object passed as the first argument. If successful, (*ppOut)
|
|
+** is set to point to the new buffer containing the rebased changset and
|
|
+** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
|
|
+** responsibility of the caller to eventually free the new buffer using
|
|
+** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
|
|
+** are set to zero and an SQLite error code returned.
|
|
+*/
|
|
+SQLITE_API int sqlite3rebaser_rebase(
|
|
+ sqlite3_rebaser*,
|
|
+ int nIn, const void *pIn,
|
|
+ int *pnOut, void **ppOut
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Delete a changeset rebaser object.
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Delete the changeset rebaser object and all associated resources. There
|
|
+** should be one call to this function for each successful invocation
|
|
+** of sqlite3rebaser_create().
|
|
+*/
|
|
+SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
|
|
+
|
|
+/*
|
|
** CAPI3REF: Streaming Versions of API functions.
|
|
**
|
|
** The six streaming API xxx_strm() functions serve similar purposes to the
|
|
@@ -10995,12 +11932,13 @@
|
|
**
|
|
** <table border=1 style="margin-left:8ex;margin-right:8ex">
|
|
** <tr><th>Streaming function<th>Non-streaming equivalent</th>
|
|
-** <tr><td>sqlite3changeset_apply_str<td>[sqlite3changeset_apply]
|
|
-** <tr><td>sqlite3changeset_concat_str<td>[sqlite3changeset_concat]
|
|
-** <tr><td>sqlite3changeset_invert_str<td>[sqlite3changeset_invert]
|
|
-** <tr><td>sqlite3changeset_start_str<td>[sqlite3changeset_start]
|
|
-** <tr><td>sqlite3session_changeset_str<td>[sqlite3session_changeset]
|
|
-** <tr><td>sqlite3session_patchset_str<td>[sqlite3session_patchset]
|
|
+** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
|
|
+** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
|
|
+** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
|
|
+** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
|
|
+** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
|
|
+** <tr><td>sqlite3session_changeset_strm<td>[sqlite3session_changeset]
|
|
+** <tr><td>sqlite3session_patchset_strm<td>[sqlite3session_patchset]
|
|
** </table>
|
|
**
|
|
** Non-streaming functions that accept changesets (or patchsets) as input
|
|
@@ -11091,6 +12029,23 @@
|
|
),
|
|
void *pCtx /* First argument passed to xConflict */
|
|
);
|
|
+SQLITE_API int sqlite3changeset_apply_v2_strm(
|
|
+ sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
|
+ void *pIn, /* First arg for xInput */
|
|
+ int(*xFilter)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ const char *zTab /* Table name */
|
|
+ ),
|
|
+ int(*xConflict)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
+ ),
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase,
|
|
+ int flags
|
|
+);
|
|
SQLITE_API int sqlite3changeset_concat_strm(
|
|
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
|
void *pInA,
|
|
@@ -11110,6 +12065,12 @@
|
|
int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
void *pIn
|
|
);
|
|
+SQLITE_API int sqlite3changeset_start_v2_strm(
|
|
+ sqlite3_changeset_iter **pp,
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
+ void *pIn,
|
|
+ int flags
|
|
+);
|
|
SQLITE_API int sqlite3session_changeset_strm(
|
|
sqlite3_session *pSession,
|
|
int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
@@ -11128,9 +12089,55 @@
|
|
int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
void *pOut
|
|
);
|
|
+SQLITE_API int sqlite3rebaser_rebase_strm(
|
|
+ sqlite3_rebaser *pRebaser,
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
+ void *pIn,
|
|
+ int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
+ void *pOut
|
|
+);
|
|
|
|
+/*
|
|
+** CAPI3REF: Configure global parameters
|
|
+**
|
|
+** The sqlite3session_config() interface is used to make global configuration
|
|
+** changes to the sessions module in order to tune it to the specific needs
|
|
+** of the application.
|
|
+**
|
|
+** The sqlite3session_config() interface is not threadsafe. If it is invoked
|
|
+** while any other thread is inside any other sessions method then the
|
|
+** results are undefined. Furthermore, if it is invoked after any sessions
|
|
+** related objects have been created, the results are also undefined.
|
|
+**
|
|
+** The first argument to the sqlite3session_config() function must be one
|
|
+** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
|
|
+** interpretation of the (void*) value passed as the second parameter and
|
|
+** the effect of calling this function depends on the value of the first
|
|
+** parameter.
|
|
+**
|
|
+** <dl>
|
|
+** <dt>SQLITE_SESSION_CONFIG_STRMSIZE<dd>
|
|
+** By default, the sessions module streaming interfaces attempt to input
|
|
+** and output data in approximately 1 KiB chunks. This operand may be used
|
|
+** to set and query the value of this configuration setting. The pointer
|
|
+** passed as the second argument must point to a value of type (int).
|
|
+** If this value is greater than 0, it is used as the new streaming data
|
|
+** chunk size for both input and output. Before returning, the (int) value
|
|
+** pointed to by pArg is set to the final value of the streaming interface
|
|
+** chunk size.
|
|
+** </dl>
|
|
+**
|
|
+** This function returns SQLITE_OK if successful, or an SQLite error code
|
|
+** otherwise.
|
|
+*/
|
|
+SQLITE_API int sqlite3session_config(int op, void *pArg);
|
|
|
|
/*
|
|
+** CAPI3REF: Values for sqlite3session_config().
|
|
+*/
|
|
+#define SQLITE_SESSION_CONFIG_STRMSIZE 1
|
|
+
|
|
+/*
|
|
** Make sure we can call this stuff from C++.
|
|
*/
|
|
#if 0
|
|
@@ -11586,7 +12593,7 @@
|
|
** This way, even if the tokenizer does not provide synonyms
|
|
** when tokenizing query text (it should not - to do would be
|
|
** inefficient), it doesn't matter if the user queries for
|
|
-** 'first + place' or '1st + place', as there are entires in the
|
|
+** 'first + place' or '1st + place', as there are entries in the
|
|
** FTS index corresponding to both forms of the first token.
|
|
** </ol>
|
|
**
|
|
@@ -11614,7 +12621,7 @@
|
|
** extra data to the FTS index or require FTS5 to query for multiple terms,
|
|
** so it is efficient in terms of disk space and query speed. However, it
|
|
** does not support prefix queries very well. If, as suggested above, the
|
|
-** token "first" is subsituted for "1st" by the tokenizer, then the query:
|
|
+** token "first" is substituted for "1st" by the tokenizer, then the query:
|
|
**
|
|
** <codeblock>
|
|
** ... MATCH '1s*'</codeblock>
|
|
@@ -12221,6 +13228,21 @@
|
|
#endif
|
|
|
|
/*
|
|
+** Some conditionals are optimizations only. In other words, if the
|
|
+** conditionals are replaced with a constant 1 (true) or 0 (false) then
|
|
+** the correct answer is still obtained, though perhaps not as quickly.
|
|
+**
|
|
+** The following macros mark these optimizations conditionals.
|
|
+*/
|
|
+#if defined(SQLITE_MUTATION_TEST)
|
|
+# define OK_IF_ALWAYS_TRUE(X) (1)
|
|
+# define OK_IF_ALWAYS_FALSE(X) (0)
|
|
+#else
|
|
+# define OK_IF_ALWAYS_TRUE(X) (X)
|
|
+# define OK_IF_ALWAYS_FALSE(X) (X)
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Some malloc failures are only possible if SQLITE_TEST_REALLOC_STRESS is
|
|
** defined. We need to defend against those failures when testing with
|
|
** SQLITE_TEST_REALLOC_STRESS, but we don't want the unreachable branches
|
|
@@ -12414,144 +13436,153 @@
|
|
#define TK_AS 24
|
|
#define TK_WITHOUT 25
|
|
#define TK_COMMA 26
|
|
-#define TK_ID 27
|
|
-#define TK_ABORT 28
|
|
-#define TK_ACTION 29
|
|
-#define TK_AFTER 30
|
|
-#define TK_ANALYZE 31
|
|
-#define TK_ASC 32
|
|
-#define TK_ATTACH 33
|
|
-#define TK_BEFORE 34
|
|
-#define TK_BY 35
|
|
-#define TK_CASCADE 36
|
|
-#define TK_CAST 37
|
|
-#define TK_COLUMNKW 38
|
|
-#define TK_CONFLICT 39
|
|
-#define TK_DATABASE 40
|
|
-#define TK_DESC 41
|
|
-#define TK_DETACH 42
|
|
-#define TK_EACH 43
|
|
-#define TK_FAIL 44
|
|
-#define TK_FOR 45
|
|
-#define TK_IGNORE 46
|
|
-#define TK_INITIALLY 47
|
|
-#define TK_INSTEAD 48
|
|
-#define TK_LIKE_KW 49
|
|
-#define TK_MATCH 50
|
|
-#define TK_NO 51
|
|
-#define TK_KEY 52
|
|
-#define TK_OF 53
|
|
-#define TK_OFFSET 54
|
|
-#define TK_PRAGMA 55
|
|
-#define TK_RAISE 56
|
|
-#define TK_RECURSIVE 57
|
|
-#define TK_REPLACE 58
|
|
-#define TK_RESTRICT 59
|
|
-#define TK_ROW 60
|
|
-#define TK_TRIGGER 61
|
|
-#define TK_VACUUM 62
|
|
-#define TK_VIEW 63
|
|
-#define TK_VIRTUAL 64
|
|
-#define TK_WITH 65
|
|
-#define TK_REINDEX 66
|
|
-#define TK_RENAME 67
|
|
-#define TK_CTIME_KW 68
|
|
-#define TK_ANY 69
|
|
-#define TK_OR 70
|
|
-#define TK_AND 71
|
|
-#define TK_IS 72
|
|
-#define TK_BETWEEN 73
|
|
-#define TK_IN 74
|
|
-#define TK_ISNULL 75
|
|
-#define TK_NOTNULL 76
|
|
-#define TK_NE 77
|
|
-#define TK_EQ 78
|
|
-#define TK_GT 79
|
|
-#define TK_LE 80
|
|
-#define TK_LT 81
|
|
-#define TK_GE 82
|
|
-#define TK_ESCAPE 83
|
|
-#define TK_BITAND 84
|
|
-#define TK_BITOR 85
|
|
-#define TK_LSHIFT 86
|
|
-#define TK_RSHIFT 87
|
|
-#define TK_PLUS 88
|
|
-#define TK_MINUS 89
|
|
-#define TK_STAR 90
|
|
-#define TK_SLASH 91
|
|
-#define TK_REM 92
|
|
-#define TK_CONCAT 93
|
|
-#define TK_COLLATE 94
|
|
-#define TK_BITNOT 95
|
|
-#define TK_INDEXED 96
|
|
-#define TK_STRING 97
|
|
-#define TK_JOIN_KW 98
|
|
-#define TK_CONSTRAINT 99
|
|
-#define TK_DEFAULT 100
|
|
-#define TK_NULL 101
|
|
-#define TK_PRIMARY 102
|
|
-#define TK_UNIQUE 103
|
|
-#define TK_CHECK 104
|
|
-#define TK_REFERENCES 105
|
|
-#define TK_AUTOINCR 106
|
|
-#define TK_ON 107
|
|
-#define TK_INSERT 108
|
|
-#define TK_DELETE 109
|
|
-#define TK_UPDATE 110
|
|
-#define TK_SET 111
|
|
-#define TK_DEFERRABLE 112
|
|
-#define TK_FOREIGN 113
|
|
-#define TK_DROP 114
|
|
-#define TK_UNION 115
|
|
-#define TK_ALL 116
|
|
-#define TK_EXCEPT 117
|
|
-#define TK_INTERSECT 118
|
|
-#define TK_SELECT 119
|
|
-#define TK_VALUES 120
|
|
-#define TK_DISTINCT 121
|
|
-#define TK_DOT 122
|
|
-#define TK_FROM 123
|
|
-#define TK_JOIN 124
|
|
-#define TK_USING 125
|
|
-#define TK_ORDER 126
|
|
-#define TK_GROUP 127
|
|
-#define TK_HAVING 128
|
|
-#define TK_LIMIT 129
|
|
-#define TK_WHERE 130
|
|
-#define TK_INTO 131
|
|
-#define TK_FLOAT 132
|
|
-#define TK_BLOB 133
|
|
-#define TK_INTEGER 134
|
|
-#define TK_VARIABLE 135
|
|
-#define TK_CASE 136
|
|
-#define TK_WHEN 137
|
|
-#define TK_THEN 138
|
|
-#define TK_ELSE 139
|
|
-#define TK_INDEX 140
|
|
-#define TK_ALTER 141
|
|
-#define TK_ADD 142
|
|
-#define TK_TO_TEXT 143
|
|
-#define TK_TO_BLOB 144
|
|
-#define TK_TO_NUMERIC 145
|
|
-#define TK_TO_INT 146
|
|
-#define TK_TO_REAL 147
|
|
-#define TK_ISNOT 148
|
|
-#define TK_END_OF_FILE 149
|
|
-#define TK_UNCLOSED_STRING 150
|
|
-#define TK_FUNCTION 151
|
|
-#define TK_COLUMN 152
|
|
-#define TK_AGG_FUNCTION 153
|
|
-#define TK_AGG_COLUMN 154
|
|
-#define TK_UMINUS 155
|
|
-#define TK_UPLUS 156
|
|
-#define TK_REGISTER 157
|
|
-#define TK_VECTOR 158
|
|
-#define TK_SELECT_COLUMN 159
|
|
-#define TK_IF_NULL_ROW 160
|
|
-#define TK_ASTERISK 161
|
|
-#define TK_SPAN 162
|
|
-#define TK_SPACE 163
|
|
-#define TK_ILLEGAL 164
|
|
+#define TK_ABORT 27
|
|
+#define TK_ACTION 28
|
|
+#define TK_AFTER 29
|
|
+#define TK_ANALYZE 30
|
|
+#define TK_ASC 31
|
|
+#define TK_ATTACH 32
|
|
+#define TK_BEFORE 33
|
|
+#define TK_BY 34
|
|
+#define TK_CASCADE 35
|
|
+#define TK_CAST 36
|
|
+#define TK_CONFLICT 37
|
|
+#define TK_DATABASE 38
|
|
+#define TK_DESC 39
|
|
+#define TK_DETACH 40
|
|
+#define TK_EACH 41
|
|
+#define TK_FAIL 42
|
|
+#define TK_OR 43
|
|
+#define TK_AND 44
|
|
+#define TK_IS 45
|
|
+#define TK_MATCH 46
|
|
+#define TK_LIKE_KW 47
|
|
+#define TK_BETWEEN 48
|
|
+#define TK_IN 49
|
|
+#define TK_ISNULL 50
|
|
+#define TK_NOTNULL 51
|
|
+#define TK_NE 52
|
|
+#define TK_EQ 53
|
|
+#define TK_GT 54
|
|
+#define TK_LE 55
|
|
+#define TK_LT 56
|
|
+#define TK_GE 57
|
|
+#define TK_ESCAPE 58
|
|
+#define TK_ID 59
|
|
+#define TK_COLUMNKW 60
|
|
+#define TK_DO 61
|
|
+#define TK_FOR 62
|
|
+#define TK_IGNORE 63
|
|
+#define TK_INITIALLY 64
|
|
+#define TK_INSTEAD 65
|
|
+#define TK_NO 66
|
|
+#define TK_KEY 67
|
|
+#define TK_OF 68
|
|
+#define TK_OFFSET 69
|
|
+#define TK_PRAGMA 70
|
|
+#define TK_RAISE 71
|
|
+#define TK_RECURSIVE 72
|
|
+#define TK_REPLACE 73
|
|
+#define TK_RESTRICT 74
|
|
+#define TK_ROW 75
|
|
+#define TK_ROWS 76
|
|
+#define TK_TRIGGER 77
|
|
+#define TK_VACUUM 78
|
|
+#define TK_VIEW 79
|
|
+#define TK_VIRTUAL 80
|
|
+#define TK_WITH 81
|
|
+#define TK_CURRENT 82
|
|
+#define TK_FOLLOWING 83
|
|
+#define TK_PARTITION 84
|
|
+#define TK_PRECEDING 85
|
|
+#define TK_RANGE 86
|
|
+#define TK_UNBOUNDED 87
|
|
+#define TK_REINDEX 88
|
|
+#define TK_RENAME 89
|
|
+#define TK_CTIME_KW 90
|
|
+#define TK_ANY 91
|
|
+#define TK_BITAND 92
|
|
+#define TK_BITOR 93
|
|
+#define TK_LSHIFT 94
|
|
+#define TK_RSHIFT 95
|
|
+#define TK_PLUS 96
|
|
+#define TK_MINUS 97
|
|
+#define TK_STAR 98
|
|
+#define TK_SLASH 99
|
|
+#define TK_REM 100
|
|
+#define TK_CONCAT 101
|
|
+#define TK_COLLATE 102
|
|
+#define TK_BITNOT 103
|
|
+#define TK_ON 104
|
|
+#define TK_INDEXED 105
|
|
+#define TK_STRING 106
|
|
+#define TK_JOIN_KW 107
|
|
+#define TK_CONSTRAINT 108
|
|
+#define TK_DEFAULT 109
|
|
+#define TK_NULL 110
|
|
+#define TK_PRIMARY 111
|
|
+#define TK_UNIQUE 112
|
|
+#define TK_CHECK 113
|
|
+#define TK_REFERENCES 114
|
|
+#define TK_AUTOINCR 115
|
|
+#define TK_INSERT 116
|
|
+#define TK_DELETE 117
|
|
+#define TK_UPDATE 118
|
|
+#define TK_SET 119
|
|
+#define TK_DEFERRABLE 120
|
|
+#define TK_FOREIGN 121
|
|
+#define TK_DROP 122
|
|
+#define TK_UNION 123
|
|
+#define TK_ALL 124
|
|
+#define TK_EXCEPT 125
|
|
+#define TK_INTERSECT 126
|
|
+#define TK_SELECT 127
|
|
+#define TK_VALUES 128
|
|
+#define TK_DISTINCT 129
|
|
+#define TK_DOT 130
|
|
+#define TK_FROM 131
|
|
+#define TK_JOIN 132
|
|
+#define TK_USING 133
|
|
+#define TK_ORDER 134
|
|
+#define TK_GROUP 135
|
|
+#define TK_HAVING 136
|
|
+#define TK_LIMIT 137
|
|
+#define TK_WHERE 138
|
|
+#define TK_INTO 139
|
|
+#define TK_NOTHING 140
|
|
+#define TK_FLOAT 141
|
|
+#define TK_BLOB 142
|
|
+#define TK_INTEGER 143
|
|
+#define TK_VARIABLE 144
|
|
+#define TK_CASE 145
|
|
+#define TK_WHEN 146
|
|
+#define TK_THEN 147
|
|
+#define TK_ELSE 148
|
|
+#define TK_INDEX 149
|
|
+#define TK_ALTER 150
|
|
+#define TK_ADD 151
|
|
+#define TK_WINDOW 152
|
|
+#define TK_OVER 153
|
|
+#define TK_FILTER 154
|
|
+#define TK_TRUEFALSE 155
|
|
+#define TK_ISNOT 156
|
|
+#define TK_FUNCTION 157
|
|
+#define TK_COLUMN 158
|
|
+#define TK_AGG_FUNCTION 159
|
|
+#define TK_AGG_COLUMN 160
|
|
+#define TK_UMINUS 161
|
|
+#define TK_UPLUS 162
|
|
+#define TK_TRUTH 163
|
|
+#define TK_REGISTER 164
|
|
+#define TK_VECTOR 165
|
|
+#define TK_SELECT_COLUMN 166
|
|
+#define TK_IF_NULL_ROW 167
|
|
+#define TK_ASTERISK 168
|
|
+#define TK_SPAN 169
|
|
+#define TK_END_OF_FILE 170
|
|
+#define TK_UNCLOSED_STRING 171
|
|
+#define TK_SPACE 172
|
|
+#define TK_ILLEGAL 173
|
|
|
|
/* The token codes above must all fit in 8 bits */
|
|
#define TKFLG_MASK 0xff
|
|
@@ -12672,6 +13703,22 @@
|
|
#endif
|
|
|
|
/*
|
|
+** Default value for the SQLITE_CONFIG_SORTERREF_SIZE option.
|
|
+*/
|
|
+#ifndef SQLITE_DEFAULT_SORTERREF_SIZE
|
|
+# define SQLITE_DEFAULT_SORTERREF_SIZE 0x7fffffff
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The compile-time options SQLITE_MMAP_READWRITE and
|
|
+** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another.
|
|
+** You must choose one or the other (or neither) but not both.
|
|
+*/
|
|
+#if defined(SQLITE_MMAP_READWRITE) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
+#error Cannot use both SQLITE_MMAP_READWRITE and SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+#endif
|
|
+
|
|
+/*
|
|
** GCC does not define the offsetof() macro so we'll have to do it
|
|
** ourselves.
|
|
*/
|
|
@@ -12809,7 +13856,8 @@
|
|
# if defined(__SIZEOF_POINTER__)
|
|
# define SQLITE_PTRSIZE __SIZEOF_POINTER__
|
|
# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
|
|
- defined(_M_ARM) || defined(__arm__) || defined(__x86)
|
|
+ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
|
|
+ (defined(__TOS_AIX__) && !defined(__64BIT__))
|
|
# define SQLITE_PTRSIZE 4
|
|
# else
|
|
# define SQLITE_PTRSIZE 8
|
|
@@ -12850,7 +13898,7 @@
|
|
# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
|
|
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
|
|
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
|
|
- defined(__arm__)
|
|
+ defined(__arm__) || defined(_M_ARM64)
|
|
# define SQLITE_BYTEORDER 1234
|
|
# elif defined(sparc) || defined(__ppc__)
|
|
# define SQLITE_BYTEORDER 4321
|
|
@@ -12969,7 +14017,7 @@
|
|
** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
|
|
** the Select query generator tracing logic is turned on.
|
|
*/
|
|
-#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_SELECTTRACE)
|
|
+#if defined(SQLITE_ENABLE_SELECTTRACE)
|
|
# define SELECTTRACE_ENABLED 1
|
|
#else
|
|
# define SELECTTRACE_ENABLED 0
|
|
@@ -12986,9 +14034,10 @@
|
|
*/
|
|
typedef struct BusyHandler BusyHandler;
|
|
struct BusyHandler {
|
|
- int (*xFunc)(void *,int); /* The busy callback */
|
|
- void *pArg; /* First arg to busy callback */
|
|
- int nBusy; /* Incremented with each busy call */
|
|
+ int (*xBusyHandler)(void *,int); /* The busy callback */
|
|
+ void *pBusyArg; /* First arg to busy callback */
|
|
+ int nBusy; /* Incremented with each busy call */
|
|
+ u8 bExtraFileArg; /* Include sqlite3_file as callback arg */
|
|
};
|
|
|
|
/*
|
|
@@ -13088,7 +14137,6 @@
|
|
typedef struct Schema Schema;
|
|
typedef struct Expr Expr;
|
|
typedef struct ExprList ExprList;
|
|
-typedef struct ExprSpan ExprSpan;
|
|
typedef struct FKey FKey;
|
|
typedef struct FuncDestructor FuncDestructor;
|
|
typedef struct FuncDef FuncDef;
|
|
@@ -13105,6 +14153,7 @@
|
|
typedef struct Parse Parse;
|
|
typedef struct PreUpdate PreUpdate;
|
|
typedef struct PrintfArguments PrintfArguments;
|
|
+typedef struct RenameToken RenameToken;
|
|
typedef struct RowSet RowSet;
|
|
typedef struct Savepoint Savepoint;
|
|
typedef struct Select Select;
|
|
@@ -13111,7 +14160,7 @@
|
|
typedef struct SQLiteThread SQLiteThread;
|
|
typedef struct SelectDest SelectDest;
|
|
typedef struct SrcList SrcList;
|
|
-typedef struct StrAccum StrAccum;
|
|
+typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
|
|
typedef struct Table Table;
|
|
typedef struct TableLock TableLock;
|
|
typedef struct Token Token;
|
|
@@ -13120,12 +14169,40 @@
|
|
typedef struct TriggerPrg TriggerPrg;
|
|
typedef struct TriggerStep TriggerStep;
|
|
typedef struct UnpackedRecord UnpackedRecord;
|
|
+typedef struct Upsert Upsert;
|
|
typedef struct VTable VTable;
|
|
typedef struct VtabCtx VtabCtx;
|
|
typedef struct Walker Walker;
|
|
typedef struct WhereInfo WhereInfo;
|
|
+typedef struct Window Window;
|
|
typedef struct With With;
|
|
|
|
+
|
|
+/*
|
|
+** The bitmask datatype defined below is used for various optimizations.
|
|
+**
|
|
+** Changing this from a 64-bit to a 32-bit type limits the number of
|
|
+** tables in a join to 32 instead of 64. But it also reduces the size
|
|
+** of the library by 738 bytes on ix86.
|
|
+*/
|
|
+#ifdef SQLITE_BITMASK_TYPE
|
|
+ typedef SQLITE_BITMASK_TYPE Bitmask;
|
|
+#else
|
|
+ typedef u64 Bitmask;
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** The number of bits in a Bitmask. "BMS" means "BitMask Size".
|
|
+*/
|
|
+#define BMS ((int)(sizeof(Bitmask)*8))
|
|
+
|
|
+/*
|
|
+** A bit in a Bitmask
|
|
+*/
|
|
+#define MASKBIT(n) (((Bitmask)1)<<(n))
|
|
+#define MASKBIT32(n) (((unsigned int)1)<<(n))
|
|
+#define ALLBITS ((Bitmask)-1)
|
|
+
|
|
/* A VList object records a mapping between parameters/variables/wildcards
|
|
** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer
|
|
** variable number associated with that parameter. See the format description
|
|
@@ -13221,7 +14298,7 @@
|
|
SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
|
|
SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
|
|
SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
|
|
-SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int);
|
|
+SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*);
|
|
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
|
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
|
|
SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*);
|
|
@@ -13373,6 +14450,7 @@
|
|
struct KeyInfo*, /* First argument to compare function */
|
|
BtCursor *pCursor /* Space to write cursor structure */
|
|
);
|
|
+SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void);
|
|
SQLITE_PRIVATE int sqlite3BtreeCursorSize(void);
|
|
SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*);
|
|
SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned);
|
|
@@ -13401,14 +14479,29 @@
|
|
** entry in either an index or table btree.
|
|
**
|
|
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
|
|
-** an arbitrary key and no data. These btrees have pKey,nKey set to their
|
|
-** key and pData,nData,nZero set to zero.
|
|
+** an arbitrary key and no data. These btrees have pKey,nKey set to the
|
|
+** key and the pData,nData,nZero fields are uninitialized. The aMem,nMem
|
|
+** fields give an array of Mem objects that are a decomposition of the key.
|
|
+** The nMem field might be zero, indicating that no decomposition is available.
|
|
**
|
|
** Table btrees (used for rowid tables) contain an integer rowid used as
|
|
** the key and passed in the nKey field. The pKey field is zero.
|
|
** pData,nData hold the content of the new entry. nZero extra zero bytes
|
|
** are appended to the end of the content when constructing the entry.
|
|
+** The aMem,nMem fields are uninitialized for table btrees.
|
|
**
|
|
+** Field usage summary:
|
|
+**
|
|
+** Table BTrees Index Btrees
|
|
+**
|
|
+** pKey always NULL encoded key
|
|
+** nKey the ROWID length of pKey
|
|
+** pData data not used
|
|
+** aMem not used decomposed key value
|
|
+** nMem not used entries in aMem
|
|
+** nData length of pData not used
|
|
+** nZero extra zeros after pData not used
|
|
+**
|
|
** This object is used to pass information into sqlite3BtreeInsert(). The
|
|
** same information used to be passed as five separate parameters. But placing
|
|
** the information into this object helps to keep the interface more
|
|
@@ -13418,7 +14511,7 @@
|
|
struct BtreePayload {
|
|
const void *pKey; /* Key content for indexes. NULL for tables */
|
|
sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
|
|
- const void *pData; /* Data for tables. NULL for indexes */
|
|
+ const void *pData; /* Data for tables. */
|
|
sqlite3_value *aMem; /* First of nMem value in the unpacked pKey */
|
|
u16 nMem; /* Number of aMem[] value. Might be zero */
|
|
int nData; /* Size of pData. 0 if none. */
|
|
@@ -13428,11 +14521,17 @@
|
|
SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
|
int flags, int seekResult);
|
|
SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor*);
|
|
+#endif
|
|
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes);
|
|
SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int flags);
|
|
SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*);
|
|
SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int flags);
|
|
SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*);
|
|
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
|
+SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*);
|
|
+#endif
|
|
SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*);
|
|
SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
|
|
SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*);
|
|
@@ -13592,7 +14691,8 @@
|
|
u64 cycles; /* Total time spent executing this instruction */
|
|
#endif
|
|
#ifdef SQLITE_VDBE_COVERAGE
|
|
- int iSrcLine; /* Source-code line that generated this opcode */
|
|
+ u32 iSrcLine; /* Source-code line that generated this opcode
|
|
+ ** with flags in the upper 8 bits */
|
|
#endif
|
|
};
|
|
typedef struct VdbeOp VdbeOp;
|
|
@@ -13646,6 +14746,7 @@
|
|
#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */
|
|
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
|
|
#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlite3_context object */
|
|
+#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */
|
|
|
|
/* Error message codes for OP_Halt */
|
|
#define P5_ConstraintNotNull 1
|
|
@@ -13691,171 +14792,177 @@
|
|
#define OP_Savepoint 0
|
|
#define OP_AutoCommit 1
|
|
#define OP_Transaction 2
|
|
-#define OP_SorterNext 3
|
|
-#define OP_PrevIfOpen 4
|
|
-#define OP_NextIfOpen 5
|
|
-#define OP_Prev 6
|
|
-#define OP_Next 7
|
|
-#define OP_Checkpoint 8
|
|
-#define OP_JournalMode 9
|
|
-#define OP_Vacuum 10
|
|
-#define OP_VFilter 11 /* synopsis: iplan=r[P3] zplan='P4' */
|
|
-#define OP_VUpdate 12 /* synopsis: data=r[P3@P2] */
|
|
-#define OP_Goto 13
|
|
-#define OP_Gosub 14
|
|
-#define OP_InitCoroutine 15
|
|
-#define OP_Yield 16
|
|
-#define OP_MustBeInt 17
|
|
-#define OP_Jump 18
|
|
+#define OP_SorterNext 3 /* jump */
|
|
+#define OP_Prev 4 /* jump */
|
|
+#define OP_Next 5 /* jump */
|
|
+#define OP_Checkpoint 6
|
|
+#define OP_JournalMode 7
|
|
+#define OP_Vacuum 8
|
|
+#define OP_VFilter 9 /* jump, synopsis: iplan=r[P3] zplan='P4' */
|
|
+#define OP_VUpdate 10 /* synopsis: data=r[P3@P2] */
|
|
+#define OP_Goto 11 /* jump */
|
|
+#define OP_Gosub 12 /* jump */
|
|
+#define OP_InitCoroutine 13 /* jump */
|
|
+#define OP_Yield 14 /* jump */
|
|
+#define OP_MustBeInt 15 /* jump */
|
|
+#define OP_Jump 16 /* jump */
|
|
+#define OP_Once 17 /* jump */
|
|
+#define OP_If 18 /* jump */
|
|
#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
|
|
-#define OP_Once 20
|
|
-#define OP_If 21
|
|
-#define OP_IfNot 22
|
|
-#define OP_IfNullRow 23 /* synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
|
|
-#define OP_SeekLT 24 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_SeekLE 25 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_SeekGE 26 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_SeekGT 27 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_NoConflict 28 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_NotFound 29 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_Found 30 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_SeekRowid 31 /* synopsis: intkey=r[P3] */
|
|
-#define OP_NotExists 32 /* synopsis: intkey=r[P3] */
|
|
-#define OP_Last 33
|
|
-#define OP_IfSmaller 34
|
|
-#define OP_SorterSort 35
|
|
-#define OP_Sort 36
|
|
-#define OP_Rewind 37
|
|
-#define OP_IdxLE 38 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_IdxGT 39 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_IdxLT 40 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_IdxGE 41 /* synopsis: key=r[P3@P4] */
|
|
-#define OP_RowSetRead 42 /* synopsis: r[P3]=rowset(P1) */
|
|
-#define OP_RowSetTest 43 /* synopsis: if r[P3] in rowset(P1) goto P2 */
|
|
-#define OP_Program 44
|
|
-#define OP_FkIfZero 45 /* synopsis: if fkctr[P1]==0 goto P2 */
|
|
-#define OP_IfPos 46 /* synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
|
|
-#define OP_IfNotZero 47 /* synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
|
|
-#define OP_DecrJumpZero 48 /* synopsis: if (--r[P1])==0 goto P2 */
|
|
-#define OP_IncrVacuum 49
|
|
-#define OP_VNext 50
|
|
-#define OP_Init 51 /* synopsis: Start at P2 */
|
|
-#define OP_Return 52
|
|
-#define OP_EndCoroutine 53
|
|
-#define OP_HaltIfNull 54 /* synopsis: if r[P3]=null halt */
|
|
-#define OP_Halt 55
|
|
-#define OP_Integer 56 /* synopsis: r[P2]=P1 */
|
|
-#define OP_Int64 57 /* synopsis: r[P2]=P4 */
|
|
-#define OP_String 58 /* synopsis: r[P2]='P4' (len=P1) */
|
|
-#define OP_Null 59 /* synopsis: r[P2..P3]=NULL */
|
|
-#define OP_SoftNull 60 /* synopsis: r[P1]=NULL */
|
|
-#define OP_Blob 61 /* synopsis: r[P2]=P4 (len=P1) */
|
|
-#define OP_Variable 62 /* synopsis: r[P2]=parameter(P1,P4) */
|
|
-#define OP_Move 63 /* synopsis: r[P2@P3]=r[P1@P3] */
|
|
-#define OP_Copy 64 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
|
|
-#define OP_SCopy 65 /* synopsis: r[P2]=r[P1] */
|
|
-#define OP_IntCopy 66 /* synopsis: r[P2]=r[P1] */
|
|
-#define OP_ResultRow 67 /* synopsis: output=r[P1@P2] */
|
|
-#define OP_CollSeq 68
|
|
-#define OP_AddImm 69 /* synopsis: r[P1]=r[P1]+P2 */
|
|
-#define OP_Or 70 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
|
|
-#define OP_And 71 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
|
|
-#define OP_RealAffinity 72
|
|
-#define OP_Cast 73 /* synopsis: affinity(r[P1]) */
|
|
-#define OP_Permutation 74
|
|
-#define OP_IsNull 75 /* same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
|
|
-#define OP_NotNull 76 /* same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
|
|
-#define OP_Ne 77 /* same as TK_NE, synopsis: IF r[P3]!=r[P1] */
|
|
-#define OP_Eq 78 /* same as TK_EQ, synopsis: IF r[P3]==r[P1] */
|
|
-#define OP_Gt 79 /* same as TK_GT, synopsis: IF r[P3]>r[P1] */
|
|
-#define OP_Le 80 /* same as TK_LE, synopsis: IF r[P3]<=r[P1] */
|
|
-#define OP_Lt 81 /* same as TK_LT, synopsis: IF r[P3]<r[P1] */
|
|
-#define OP_Ge 82 /* same as TK_GE, synopsis: IF r[P3]>=r[P1] */
|
|
-#define OP_ElseNotEq 83 /* same as TK_ESCAPE */
|
|
-#define OP_BitAnd 84 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
|
|
-#define OP_BitOr 85 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
|
|
-#define OP_ShiftLeft 86 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
|
|
-#define OP_ShiftRight 87 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
|
|
-#define OP_Add 88 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
|
|
-#define OP_Subtract 89 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
|
|
-#define OP_Multiply 90 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
|
|
-#define OP_Divide 91 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
|
|
-#define OP_Remainder 92 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
|
|
-#define OP_Concat 93 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
|
|
-#define OP_Compare 94 /* synopsis: r[P1@P3] <-> r[P2@P3] */
|
|
-#define OP_BitNot 95 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */
|
|
-#define OP_Column 96 /* synopsis: r[P3]=PX */
|
|
-#define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */
|
|
-#define OP_Affinity 98 /* synopsis: affinity(r[P1@P2]) */
|
|
-#define OP_MakeRecord 99 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
|
|
-#define OP_Count 100 /* synopsis: r[P2]=count() */
|
|
-#define OP_ReadCookie 101
|
|
-#define OP_SetCookie 102
|
|
-#define OP_ReopenIdx 103 /* synopsis: root=P2 iDb=P3 */
|
|
-#define OP_OpenRead 104 /* synopsis: root=P2 iDb=P3 */
|
|
-#define OP_OpenWrite 105 /* synopsis: root=P2 iDb=P3 */
|
|
-#define OP_OpenDup 106
|
|
-#define OP_OpenAutoindex 107 /* synopsis: nColumn=P2 */
|
|
-#define OP_OpenEphemeral 108 /* synopsis: nColumn=P2 */
|
|
-#define OP_SorterOpen 109
|
|
-#define OP_SequenceTest 110 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
|
|
-#define OP_OpenPseudo 111 /* synopsis: P3 columns in r[P2] */
|
|
-#define OP_Close 112
|
|
-#define OP_ColumnsUsed 113
|
|
-#define OP_Sequence 114 /* synopsis: r[P2]=cursor[P1].ctr++ */
|
|
-#define OP_NewRowid 115 /* synopsis: r[P2]=rowid */
|
|
-#define OP_Insert 116 /* synopsis: intkey=r[P3] data=r[P2] */
|
|
-#define OP_InsertInt 117 /* synopsis: intkey=P3 data=r[P2] */
|
|
-#define OP_Delete 118
|
|
-#define OP_ResetCount 119
|
|
-#define OP_SorterCompare 120 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
|
|
-#define OP_SorterData 121 /* synopsis: r[P2]=data */
|
|
-#define OP_RowData 122 /* synopsis: r[P2]=data */
|
|
-#define OP_Rowid 123 /* synopsis: r[P2]=rowid */
|
|
-#define OP_NullRow 124
|
|
-#define OP_SorterInsert 125 /* synopsis: key=r[P2] */
|
|
-#define OP_IdxInsert 126 /* synopsis: key=r[P2] */
|
|
-#define OP_IdxDelete 127 /* synopsis: key=r[P2@P3] */
|
|
-#define OP_DeferredSeek 128 /* synopsis: Move P3 to P1.rowid if needed */
|
|
-#define OP_IdxRowid 129 /* synopsis: r[P2]=rowid */
|
|
-#define OP_Destroy 130
|
|
-#define OP_Clear 131
|
|
-#define OP_Real 132 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
|
|
-#define OP_ResetSorter 133
|
|
-#define OP_CreateIndex 134 /* synopsis: r[P2]=root iDb=P1 */
|
|
-#define OP_CreateTable 135 /* synopsis: r[P2]=root iDb=P1 */
|
|
-#define OP_SqlExec 136
|
|
-#define OP_ParseSchema 137
|
|
-#define OP_LoadAnalysis 138
|
|
-#define OP_DropTable 139
|
|
-#define OP_DropIndex 140
|
|
-#define OP_DropTrigger 141
|
|
-#define OP_IntegrityCk 142
|
|
-#define OP_RowSetAdd 143 /* synopsis: rowset(P1)=r[P2] */
|
|
-#define OP_Param 144
|
|
-#define OP_FkCounter 145 /* synopsis: fkctr[P1]+=P2 */
|
|
-#define OP_MemMax 146 /* synopsis: r[P1]=max(r[P1],r[P2]) */
|
|
-#define OP_OffsetLimit 147 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
|
|
-#define OP_AggStep0 148 /* synopsis: accum=r[P3] step(r[P2@P5]) */
|
|
-#define OP_AggStep 149 /* synopsis: accum=r[P3] step(r[P2@P5]) */
|
|
-#define OP_AggFinal 150 /* synopsis: accum=r[P1] N=P2 */
|
|
-#define OP_Expire 151
|
|
-#define OP_TableLock 152 /* synopsis: iDb=P1 root=P2 write=P3 */
|
|
-#define OP_VBegin 153
|
|
-#define OP_VCreate 154
|
|
-#define OP_VDestroy 155
|
|
-#define OP_VOpen 156
|
|
-#define OP_VColumn 157 /* synopsis: r[P3]=vcolumn(P2) */
|
|
-#define OP_VRename 158
|
|
-#define OP_Pagecount 159
|
|
-#define OP_MaxPgcnt 160
|
|
-#define OP_PureFunc0 161
|
|
-#define OP_Function0 162 /* synopsis: r[P3]=func(r[P2@P5]) */
|
|
-#define OP_PureFunc 163
|
|
-#define OP_Function 164 /* synopsis: r[P3]=func(r[P2@P5]) */
|
|
-#define OP_CursorHint 165
|
|
-#define OP_Noop 166
|
|
-#define OP_Explain 167
|
|
+#define OP_IfNot 20 /* jump */
|
|
+#define OP_IfNullRow 21 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
|
|
+#define OP_SeekLT 22 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_SeekLE 23 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_SeekGE 24 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_SeekGT 25 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_SeekRowid 30 /* jump, synopsis: intkey=r[P3] */
|
|
+#define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */
|
|
+#define OP_Last 32 /* jump */
|
|
+#define OP_IfSmaller 33 /* jump */
|
|
+#define OP_SorterSort 34 /* jump */
|
|
+#define OP_Sort 35 /* jump */
|
|
+#define OP_Rewind 36 /* jump */
|
|
+#define OP_IdxLE 37 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_IdxGT 38 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_IdxLT 39 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_IdxGE 40 /* jump, synopsis: key=r[P3@P4] */
|
|
+#define OP_RowSetRead 41 /* jump, synopsis: r[P3]=rowset(P1) */
|
|
+#define OP_RowSetTest 42 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
|
|
+#define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
|
|
+#define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
|
|
+#define OP_Program 45 /* jump */
|
|
+#define OP_FkIfZero 46 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
|
|
+#define OP_IfPos 47 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
|
|
+#define OP_IfNotZero 48 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
|
|
+#define OP_DecrJumpZero 49 /* jump, synopsis: if (--r[P1])==0 goto P2 */
|
|
+#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
|
|
+#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
|
|
+#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */
|
|
+#define OP_Eq 53 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */
|
|
+#define OP_Gt 54 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */
|
|
+#define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */
|
|
+#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]<r[P1] */
|
|
+#define OP_Ge 57 /* jump, same as TK_GE, synopsis: IF r[P3]>=r[P1] */
|
|
+#define OP_ElseNotEq 58 /* jump, same as TK_ESCAPE */
|
|
+#define OP_IncrVacuum 59 /* jump */
|
|
+#define OP_VNext 60 /* jump */
|
|
+#define OP_Init 61 /* jump, synopsis: Start at P2 */
|
|
+#define OP_PureFunc0 62
|
|
+#define OP_Function0 63 /* synopsis: r[P3]=func(r[P2@P5]) */
|
|
+#define OP_PureFunc 64
|
|
+#define OP_Function 65 /* synopsis: r[P3]=func(r[P2@P5]) */
|
|
+#define OP_Return 66
|
|
+#define OP_EndCoroutine 67
|
|
+#define OP_HaltIfNull 68 /* synopsis: if r[P3]=null halt */
|
|
+#define OP_Halt 69
|
|
+#define OP_Integer 70 /* synopsis: r[P2]=P1 */
|
|
+#define OP_Int64 71 /* synopsis: r[P2]=P4 */
|
|
+#define OP_String 72 /* synopsis: r[P2]='P4' (len=P1) */
|
|
+#define OP_Null 73 /* synopsis: r[P2..P3]=NULL */
|
|
+#define OP_SoftNull 74 /* synopsis: r[P1]=NULL */
|
|
+#define OP_Blob 75 /* synopsis: r[P2]=P4 (len=P1) */
|
|
+#define OP_Variable 76 /* synopsis: r[P2]=parameter(P1,P4) */
|
|
+#define OP_Move 77 /* synopsis: r[P2@P3]=r[P1@P3] */
|
|
+#define OP_Copy 78 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
|
|
+#define OP_SCopy 79 /* synopsis: r[P2]=r[P1] */
|
|
+#define OP_IntCopy 80 /* synopsis: r[P2]=r[P1] */
|
|
+#define OP_ResultRow 81 /* synopsis: output=r[P1@P2] */
|
|
+#define OP_CollSeq 82
|
|
+#define OP_AddImm 83 /* synopsis: r[P1]=r[P1]+P2 */
|
|
+#define OP_RealAffinity 84
|
|
+#define OP_Cast 85 /* synopsis: affinity(r[P1]) */
|
|
+#define OP_Permutation 86
|
|
+#define OP_Compare 87 /* synopsis: r[P1@P3] <-> r[P2@P3] */
|
|
+#define OP_IsTrue 88 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */
|
|
+#define OP_Offset 89 /* synopsis: r[P3] = sqlite_offset(P1) */
|
|
+#define OP_Column 90 /* synopsis: r[P3]=PX */
|
|
+#define OP_Affinity 91 /* synopsis: affinity(r[P1@P2]) */
|
|
+#define OP_BitAnd 92 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
|
|
+#define OP_BitOr 93 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
|
|
+#define OP_ShiftLeft 94 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
|
|
+#define OP_ShiftRight 95 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
|
|
+#define OP_Add 96 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
|
|
+#define OP_Subtract 97 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
|
|
+#define OP_Multiply 98 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
|
|
+#define OP_Divide 99 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
|
|
+#define OP_Remainder 100 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
|
|
+#define OP_Concat 101 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
|
|
+#define OP_MakeRecord 102 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
|
|
+#define OP_BitNot 103 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
|
|
+#define OP_Count 104 /* synopsis: r[P2]=count() */
|
|
+#define OP_ReadCookie 105
|
|
+#define OP_String8 106 /* same as TK_STRING, synopsis: r[P2]='P4' */
|
|
+#define OP_SetCookie 107
|
|
+#define OP_ReopenIdx 108 /* synopsis: root=P2 iDb=P3 */
|
|
+#define OP_OpenRead 109 /* synopsis: root=P2 iDb=P3 */
|
|
+#define OP_OpenWrite 110 /* synopsis: root=P2 iDb=P3 */
|
|
+#define OP_OpenDup 111
|
|
+#define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */
|
|
+#define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */
|
|
+#define OP_SorterOpen 114
|
|
+#define OP_SequenceTest 115 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
|
|
+#define OP_OpenPseudo 116 /* synopsis: P3 columns in r[P2] */
|
|
+#define OP_Close 117
|
|
+#define OP_ColumnsUsed 118
|
|
+#define OP_SeekHit 119 /* synopsis: seekHit=P2 */
|
|
+#define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */
|
|
+#define OP_NewRowid 121 /* synopsis: r[P2]=rowid */
|
|
+#define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */
|
|
+#define OP_InsertInt 123 /* synopsis: intkey=P3 data=r[P2] */
|
|
+#define OP_Delete 124
|
|
+#define OP_ResetCount 125
|
|
+#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
|
|
+#define OP_SorterData 127 /* synopsis: r[P2]=data */
|
|
+#define OP_RowData 128 /* synopsis: r[P2]=data */
|
|
+#define OP_Rowid 129 /* synopsis: r[P2]=rowid */
|
|
+#define OP_NullRow 130
|
|
+#define OP_SeekEnd 131
|
|
+#define OP_SorterInsert 132 /* synopsis: key=r[P2] */
|
|
+#define OP_IdxInsert 133 /* synopsis: key=r[P2] */
|
|
+#define OP_IdxDelete 134 /* synopsis: key=r[P2@P3] */
|
|
+#define OP_DeferredSeek 135 /* synopsis: Move P3 to P1.rowid if needed */
|
|
+#define OP_IdxRowid 136 /* synopsis: r[P2]=rowid */
|
|
+#define OP_Destroy 137
|
|
+#define OP_Clear 138
|
|
+#define OP_ResetSorter 139
|
|
+#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */
|
|
+#define OP_Real 141 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
|
|
+#define OP_SqlExec 142
|
|
+#define OP_ParseSchema 143
|
|
+#define OP_LoadAnalysis 144
|
|
+#define OP_DropTable 145
|
|
+#define OP_DropIndex 146
|
|
+#define OP_DropTrigger 147
|
|
+#define OP_IntegrityCk 148
|
|
+#define OP_RowSetAdd 149 /* synopsis: rowset(P1)=r[P2] */
|
|
+#define OP_Param 150
|
|
+#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */
|
|
+#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */
|
|
+#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
|
|
+#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */
|
|
+#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */
|
|
+#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */
|
|
+#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */
|
|
+#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */
|
|
+#define OP_Expire 159
|
|
+#define OP_TableLock 160 /* synopsis: iDb=P1 root=P2 write=P3 */
|
|
+#define OP_VBegin 161
|
|
+#define OP_VCreate 162
|
|
+#define OP_VDestroy 163
|
|
+#define OP_VOpen 164
|
|
+#define OP_VColumn 165 /* synopsis: r[P3]=vcolumn(P2) */
|
|
+#define OP_VRename 166
|
|
+#define OP_Pagecount 167
|
|
+#define OP_MaxPgcnt 168
|
|
+#define OP_Trace 169
|
|
+#define OP_CursorHint 170
|
|
+#define OP_Noop 171
|
|
+#define OP_Explain 172
|
|
+#define OP_Abortable 173
|
|
|
|
/* Properties such as "out2" or "jump" that are specified in
|
|
** comments following the "case" for each opcode in the vdbe.c
|
|
@@ -13868,28 +14975,28 @@
|
|
#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
|
|
#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
|
|
#define OPFLG_INITIALIZER {\
|
|
-/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,\
|
|
-/* 8 */ 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,\
|
|
-/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x03, 0x03, 0x01,\
|
|
+/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\
|
|
+/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\
|
|
+/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x01, 0x09, 0x09,\
|
|
/* 24 */ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\
|
|
-/* 32 */ 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\
|
|
-/* 40 */ 0x01, 0x01, 0x23, 0x0b, 0x01, 0x01, 0x03, 0x03,\
|
|
-/* 48 */ 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x08, 0x00,\
|
|
-/* 56 */ 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x00,\
|
|
-/* 64 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x02, 0x26, 0x26,\
|
|
-/* 72 */ 0x02, 0x02, 0x00, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\
|
|
-/* 80 */ 0x0b, 0x0b, 0x0b, 0x01, 0x26, 0x26, 0x26, 0x26,\
|
|
-/* 88 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\
|
|
-/* 96 */ 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
|
|
-/* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
|
|
-/* 112 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\
|
|
-/* 120 */ 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x04, 0x00,\
|
|
-/* 128 */ 0x00, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10,\
|
|
-/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,\
|
|
-/* 144 */ 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00,\
|
|
-/* 152 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
|
|
-/* 160 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
|
|
-}
|
|
+/* 32 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\
|
|
+/* 40 */ 0x01, 0x23, 0x0b, 0x26, 0x26, 0x01, 0x01, 0x03,\
|
|
+/* 48 */ 0x03, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
|
|
+/* 56 */ 0x0b, 0x0b, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,\
|
|
+/* 64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\
|
|
+/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
|
|
+/* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
|
|
+/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26,\
|
|
+/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\
|
|
+/* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
|
|
+/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
|
|
+/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
|
|
+/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\
|
|
+/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
|
|
+/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\
|
|
+/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
|
|
+/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
|
|
+/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,}
|
|
|
|
/* The sqlite3P2Values() routine is able to run faster if it knows
|
|
** the value of the largest JUMP opcode. The smaller the maximum
|
|
@@ -13897,7 +15004,7 @@
|
|
** generated this include file strives to group all JUMP opcodes
|
|
** together near the beginning of the list.
|
|
*/
|
|
-#define SQLITE_MX_JUMP_OPCODE 83 /* Maximum JUMP opcode */
|
|
+#define SQLITE_MX_JUMP_OPCODE 61 /* Maximum JUMP opcode */
|
|
|
|
/************** End of opcodes.h *********************************************/
|
|
/************** Continuing where we left off in vdbe.h ***********************/
|
|
@@ -13931,7 +15038,24 @@
|
|
# define sqlite3VdbeVerifyNoMallocRequired(A,B)
|
|
# define sqlite3VdbeVerifyNoResultRow(A)
|
|
#endif
|
|
-SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
|
|
+#if defined(SQLITE_DEBUG)
|
|
+SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int);
|
|
+#else
|
|
+# define sqlite3VdbeVerifyAbortable(A,B)
|
|
+#endif
|
|
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
|
|
+#ifndef SQLITE_OMIT_EXPLAIN
|
|
+SQLITE_PRIVATE void sqlite3VdbeExplain(Parse*,u8,const char*,...);
|
|
+SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse*);
|
|
+SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*);
|
|
+# define ExplainQueryPlan(P) sqlite3VdbeExplain P
|
|
+# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
|
|
+# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
|
|
+#else
|
|
+# define ExplainQueryPlan(P)
|
|
+# define ExplainQueryPlanPop(P)
|
|
+# define ExplainQueryPlanParent(P) 0
|
|
+#endif
|
|
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
|
SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
|
|
SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
|
@@ -13975,6 +15099,7 @@
|
|
SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
|
|
#endif
|
|
SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
|
|
+SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*);
|
|
|
|
SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
|
|
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
|
|
@@ -14030,17 +15155,43 @@
|
|
**
|
|
** VdbeCoverageNeverTaken(v) // Previous branch is never taken
|
|
**
|
|
+** VdbeCoverageNeverNull(v) // Previous three-way branch is only
|
|
+** // taken on the first two ways. The
|
|
+** // NULL option is not possible
|
|
+**
|
|
+** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested
|
|
+** // in distingishing equal and not-equal.
|
|
+**
|
|
** Every VDBE branch operation must be tagged with one of the macros above.
|
|
** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and
|
|
** -DSQLITE_DEBUG then an ALWAYS() will fail in the vdbeTakeBranch()
|
|
** routine in vdbe.c, alerting the developer to the missed tag.
|
|
+**
|
|
+** During testing, the test application will invoke
|
|
+** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback
|
|
+** routine that is invoked as each bytecode branch is taken. The callback
|
|
+** contains the sqlite3.c source line number ov the VdbeCoverage macro and
|
|
+** flags to indicate whether or not the branch was taken. The test application
|
|
+** is responsible for keeping track of this and reporting byte-code branches
|
|
+** that are never taken.
|
|
+**
|
|
+** See the VdbeBranchTaken() macro and vdbeTakeBranch() function in the
|
|
+** vdbe.c source file for additional information.
|
|
*/
|
|
#ifdef SQLITE_VDBE_COVERAGE
|
|
SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int);
|
|
# define VdbeCoverage(v) sqlite3VdbeSetLineNumber(v,__LINE__)
|
|
# define VdbeCoverageIf(v,x) if(x)sqlite3VdbeSetLineNumber(v,__LINE__)
|
|
-# define VdbeCoverageAlwaysTaken(v) sqlite3VdbeSetLineNumber(v,2);
|
|
-# define VdbeCoverageNeverTaken(v) sqlite3VdbeSetLineNumber(v,1);
|
|
+# define VdbeCoverageAlwaysTaken(v) \
|
|
+ sqlite3VdbeSetLineNumber(v,__LINE__|0x5000000);
|
|
+# define VdbeCoverageNeverTaken(v) \
|
|
+ sqlite3VdbeSetLineNumber(v,__LINE__|0x6000000);
|
|
+# define VdbeCoverageNeverNull(v) \
|
|
+ sqlite3VdbeSetLineNumber(v,__LINE__|0x4000000);
|
|
+# define VdbeCoverageNeverNullIf(v,x) \
|
|
+ if(x)sqlite3VdbeSetLineNumber(v,__LINE__|0x4000000);
|
|
+# define VdbeCoverageEqNe(v) \
|
|
+ sqlite3VdbeSetLineNumber(v,__LINE__|0x8000000);
|
|
# define VDBE_OFFSET_LINENO(x) (__LINE__+x)
|
|
#else
|
|
# define VdbeCoverage(v)
|
|
@@ -14047,6 +15198,9 @@
|
|
# define VdbeCoverageIf(v,x)
|
|
# define VdbeCoverageAlwaysTaken(v)
|
|
# define VdbeCoverageNeverTaken(v)
|
|
+# define VdbeCoverageNeverNull(v)
|
|
+# define VdbeCoverageNeverNullIf(v,x)
|
|
+# define VdbeCoverageEqNe(v)
|
|
# define VDBE_OFFSET_LINENO(x) 0
|
|
#endif
|
|
|
|
@@ -14056,6 +15210,10 @@
|
|
# define sqlite3VdbeScanStatus(a,b,c,d,e)
|
|
#endif
|
|
|
|
+#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
|
+SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*);
|
|
+#endif
|
|
+
|
|
#endif /* SQLITE_VDBE_H */
|
|
|
|
/************** End of vdbe.h ************************************************/
|
|
@@ -14190,7 +15348,7 @@
|
|
SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
|
|
|
|
/* Functions used to configure a Pager object. */
|
|
-SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
|
|
+SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *);
|
|
SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
|
|
#ifdef SQLITE_HAS_CODEC
|
|
SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager*,Pager*);
|
|
@@ -14215,6 +15373,7 @@
|
|
SQLITE_PRIVATE void sqlite3PagerRef(DbPage*);
|
|
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*);
|
|
SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*);
|
|
+SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*);
|
|
|
|
/* Operations on page references. */
|
|
SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*);
|
|
@@ -14242,18 +15401,19 @@
|
|
SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
|
|
SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
|
|
SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
|
|
-# ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
-SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager, Pgno);
|
|
-# endif
|
|
# ifdef SQLITE_ENABLE_SNAPSHOT
|
|
SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
|
|
SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
|
SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
|
|
+SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
|
+SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
|
|
# endif
|
|
-#else
|
|
-# define sqlite3PagerUseWal(x,y) 0
|
|
#endif
|
|
|
|
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
+SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
|
|
+#endif
|
|
+
|
|
#ifdef SQLITE_ENABLE_ZIPVFS
|
|
SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager);
|
|
#endif
|
|
@@ -14275,6 +15435,11 @@
|
|
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
|
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
|
|
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
|
|
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
+SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager);
|
|
+#else
|
|
+# define sqlite3PagerResetLockTimeout(X)
|
|
+#endif
|
|
|
|
/* Functions used to truncate the database file. */
|
|
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
|
|
@@ -14351,6 +15516,8 @@
|
|
i16 nRef; /* Number of users of this page */
|
|
PgHdr *pDirtyNext; /* Next element in list of dirty pages */
|
|
PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */
|
|
+ /* NB: pDirtyNext and pDirtyPrev are undefined if the
|
|
+ ** PgHdr object is not dirty */
|
|
};
|
|
|
|
/* Bit values for PgHdr.flags */
|
|
@@ -14489,6 +15656,10 @@
|
|
/* Number of dirty pages as a percentage of the configured cache size */
|
|
SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache*);
|
|
|
|
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
+SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache);
|
|
+#endif
|
|
+
|
|
#endif /* _PCACHE_H_ */
|
|
|
|
/************** End of pcache.h **********************************************/
|
|
@@ -14732,10 +15903,12 @@
|
|
#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
|
|
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id);
|
|
SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
|
|
+#ifndef SQLITE_OMIT_WAL
|
|
SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
|
|
SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
|
|
SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id);
|
|
SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int);
|
|
+#endif /* SQLITE_OMIT_WAL */
|
|
SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
|
|
SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
|
|
|
|
@@ -14944,6 +16117,7 @@
|
|
#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */
|
|
#define DB_UnresetViews 0x0002 /* Some views have defined column names */
|
|
#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */
|
|
+#define DB_ResetWanted 0x0008 /* Reset the schema when nSchemaLock==0 */
|
|
|
|
/*
|
|
** The number of different kinds of things that can be limited
|
|
@@ -14975,9 +16149,9 @@
|
|
u32 bDisable; /* Only operate the lookaside when zero */
|
|
u16 sz; /* Size of each buffer in bytes */
|
|
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
|
|
- int nOut; /* Number of buffers currently checked out */
|
|
- int mxOut; /* Highwater mark for nOut */
|
|
- int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */
|
|
+ u32 nSlot; /* Number of lookaside slots allocated */
|
|
+ u32 anStat[3]; /* 0: hits. 1: size misses. 2: full misses */
|
|
+ LookasideSlot *pInit; /* List of buffers not previously used */
|
|
LookasideSlot *pFree; /* List of available buffers */
|
|
void *pStart; /* First byte of available memory space */
|
|
void *pEnd; /* First byte past end of available space */
|
|
@@ -14991,12 +16165,14 @@
|
|
** functions use a regular table table from hash.h.)
|
|
**
|
|
** Hash each FuncDef structure into one of the FuncDefHash.a[] slots.
|
|
-** Collisions are on the FuncDef.u.pHash chain.
|
|
+** Collisions are on the FuncDef.u.pHash chain. Use the SQLITE_FUNC_HASH()
|
|
+** macro to compute a hash on the function name.
|
|
*/
|
|
#define SQLITE_FUNC_HASH_SZ 23
|
|
struct FuncDefHash {
|
|
FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
|
|
};
|
|
+#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
|
|
|
|
#ifdef SQLITE_USER_AUTHENTICATION
|
|
/*
|
|
@@ -15056,9 +16232,11 @@
|
|
sqlite3_mutex *mutex; /* Connection mutex */
|
|
Db *aDb; /* All backends */
|
|
int nDb; /* Number of backends currently in use */
|
|
- int flags; /* Miscellaneous flags. See below */
|
|
+ u32 mDbFlags; /* flags recording internal state */
|
|
+ u64 flags; /* flags settable by pragmas. See below */
|
|
i64 lastRowid; /* ROWID of most recent insert (see above) */
|
|
i64 szMmap; /* Default mmap_size setting */
|
|
+ u32 nSchemaLock; /* Do not reset the schema when non-zero */
|
|
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
|
|
int errCode; /* Most recent error code (SQLITE_*) */
|
|
int errMask; /* & result codes with this before returning */
|
|
@@ -15075,7 +16253,7 @@
|
|
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
|
|
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
|
|
u8 mTrace; /* zero or more SQLITE_TRACE flags */
|
|
- u8 skipBtreeMutex; /* True if no shared-cache backends */
|
|
+ u8 noSharedCache; /* True if no shared-cache backends */
|
|
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
|
|
int nextPagesize; /* Pagesize after VACUUM if >0 */
|
|
u32 magic; /* Magic number for detect library misuse */
|
|
@@ -15087,8 +16265,9 @@
|
|
int newTnum; /* Rootpage of table being initialized */
|
|
u8 iDb; /* Which db file is being initialized */
|
|
u8 busy; /* TRUE if currently initializing */
|
|
- u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
|
|
- u8 imposterTable; /* Building an imposter table */
|
|
+ unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
|
|
+ unsigned imposterTable : 1; /* Building an imposter table */
|
|
+ unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
|
|
} init;
|
|
int nVdbeActive; /* Number of VDBEs currently running */
|
|
int nVdbeRead; /* Number of active VDBEs that read or write */
|
|
@@ -15141,7 +16320,7 @@
|
|
Hash aModule; /* populated by sqlite3_create_module() */
|
|
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
|
|
VTable **aVTrans; /* Virtual tables with open transactions */
|
|
- VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
|
|
+ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
|
|
#endif
|
|
Hash aFunc; /* Hash table of connection functions */
|
|
Hash aCollSeq; /* All collating sequences */
|
|
@@ -15210,27 +16389,36 @@
|
|
#define SQLITE_ForeignKeys 0x00004000 /* Enforce foreign key constraints */
|
|
#define SQLITE_AutoIndex 0x00008000 /* Enable automatic indexes */
|
|
#define SQLITE_LoadExtension 0x00010000 /* Enable load_extension */
|
|
-#define SQLITE_EnableTrigger 0x00020000 /* True to enable triggers */
|
|
-#define SQLITE_DeferFKs 0x00040000 /* Defer all FK constraints */
|
|
-#define SQLITE_QueryOnly 0x00080000 /* Disable database changes */
|
|
-#define SQLITE_CellSizeCk 0x00100000 /* Check btree cell sizes on load */
|
|
-#define SQLITE_Fts3Tokenizer 0x00200000 /* Enable fts3_tokenizer(2) */
|
|
-#define SQLITE_EnableQPSG 0x00400000 /* Query Planner Stability Guarantee */
|
|
-/* The next four values are not used by PRAGMAs or by sqlite3_dbconfig() and
|
|
-** could be factored out into a separate bit vector of the sqlite3 object. */
|
|
-#define SQLITE_InternChanges 0x00800000 /* Uncommitted Hash table changes */
|
|
-#define SQLITE_LoadExtFunc 0x01000000 /* Enable load_extension() SQL func */
|
|
-#define SQLITE_PreferBuiltin 0x02000000 /* Preference to built-in funcs */
|
|
-#define SQLITE_Vacuum 0x04000000 /* Currently in a VACUUM */
|
|
+#define SQLITE_LoadExtFunc 0x00020000 /* Enable load_extension() SQL func */
|
|
+#define SQLITE_EnableTrigger 0x00040000 /* True to enable triggers */
|
|
+#define SQLITE_DeferFKs 0x00080000 /* Defer all FK constraints */
|
|
+#define SQLITE_QueryOnly 0x00100000 /* Disable database changes */
|
|
+#define SQLITE_CellSizeCk 0x00200000 /* Check btree cell sizes on load */
|
|
+#define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */
|
|
+#define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee*/
|
|
+#define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */
|
|
+#define SQLITE_ResetDatabase 0x02000000 /* Reset the database */
|
|
+#define SQLITE_LegacyAlter 0x04000000 /* Legacy ALTER TABLE behaviour */
|
|
+#define SQLITE_NoSchemaError 0x08000000 /* Do not report schema parse errors*/
|
|
+#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */
|
|
+
|
|
/* Flags used only if debugging */
|
|
+#define HI(X) ((u64)(X)<<32)
|
|
#ifdef SQLITE_DEBUG
|
|
-#define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */
|
|
-#define SQLITE_VdbeListing 0x10000000 /* Debug listings of VDBE programs */
|
|
-#define SQLITE_VdbeTrace 0x20000000 /* True to trace VDBE execution */
|
|
-#define SQLITE_VdbeAddopTrace 0x40000000 /* Trace sqlite3VdbeAddOp() calls */
|
|
-#define SQLITE_VdbeEQP 0x80000000 /* Debug EXPLAIN QUERY PLAN */
|
|
+#define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */
|
|
+#define SQLITE_VdbeListing HI(0x0002) /* Debug listings of VDBE progs */
|
|
+#define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */
|
|
+#define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */
|
|
+#define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */
|
|
#endif
|
|
|
|
+/*
|
|
+** Allowed values for sqlite3.mDbFlags
|
|
+*/
|
|
+#define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */
|
|
+#define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */
|
|
+#define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */
|
|
+#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */
|
|
|
|
/*
|
|
** Bits of the sqlite3.dbOptFlags field that are used by the
|
|
@@ -15238,19 +16426,22 @@
|
|
** selectively disable various optimizations.
|
|
*/
|
|
#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
|
|
-#define SQLITE_ColumnCache 0x0002 /* Column cache */
|
|
+ /* 0x0002 available for reuse */
|
|
#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
|
|
#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
|
|
-/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */
|
|
-#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
|
|
-#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
|
|
-#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
|
|
-#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
|
|
-#define SQLITE_Transitive 0x0200 /* Transitive constraints */
|
|
-#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
|
|
+#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */
|
|
+#define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */
|
|
+#define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */
|
|
+#define SQLITE_Transitive 0x0080 /* Transitive constraints */
|
|
+#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */
|
|
+#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */
|
|
+#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */
|
|
#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
|
|
-#define SQLITE_CountOfView 0x1000 /* The count-of-view optimization */
|
|
-#define SQLITE_CursorHints 0x2000 /* Add OP_CursorHint opcodes */
|
|
+ /* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */
|
|
+#define SQLITE_PushDown 0x1000 /* The push-down optimization */
|
|
+#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
|
|
+#define SQLITE_SkipScan 0x4000 /* Skip-scans */
|
|
+#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */
|
|
#define SQLITE_AllOpts 0xffff /* All optimizations */
|
|
|
|
/*
|
|
@@ -15289,11 +16480,13 @@
|
|
*/
|
|
struct FuncDef {
|
|
i8 nArg; /* Number of arguments. -1 means unlimited */
|
|
- u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
|
|
+ u32 funcFlags; /* Some combination of SQLITE_FUNC_* */
|
|
void *pUserData; /* User data parameter */
|
|
FuncDef *pNext; /* Next function with same name */
|
|
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
|
|
void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
|
|
+ void (*xValue)(sqlite3_context*); /* Current agg value */
|
|
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value**); /* inverse agg-step */
|
|
const char *zName; /* SQL name of the function. */
|
|
union {
|
|
FuncDef *pHash; /* Next with a different name but the same hash */
|
|
@@ -15349,6 +16542,10 @@
|
|
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
|
|
** single query - might change over time */
|
|
#define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */
|
|
+#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
|
|
+#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
|
|
+#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */
|
|
+#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
|
|
|
|
/*
|
|
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
|
|
@@ -15383,6 +16580,12 @@
|
|
** are interpreted in the same way as the first 4 parameters to
|
|
** FUNCTION().
|
|
**
|
|
+** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse)
|
|
+** Used to create an aggregate function definition implemented by
|
|
+** the C functions xStep and xFinal. The first four parameters
|
|
+** are interpreted in the same way as the first 4 parameters to
|
|
+** FUNCTION().
|
|
+**
|
|
** LIKEFUNC(zName, nArg, pArg, flags)
|
|
** Used to create a scalar function definition of a function zName
|
|
** that accepts nArg arguments and is implemented by a call to C
|
|
@@ -15393,32 +16596,39 @@
|
|
*/
|
|
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
|
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
|
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
|
|
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
|
|
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
|
{nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
|
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
|
|
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
|
|
#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
|
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \
|
|
- 0, 0, xFunc, 0, #zName, {0} }
|
|
+ 0, 0, xFunc, 0, 0, 0, #zName, {0} }
|
|
#define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \
|
|
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
|
|
- (void*)&sqlite3Config, 0, xFunc, 0, #zName, {0} }
|
|
+ (void*)&sqlite3Config, 0, xFunc, 0, 0, 0, #zName, {0} }
|
|
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
|
|
{nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
|
|
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
|
|
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
|
|
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
|
|
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
|
- pArg, 0, xFunc, 0, #zName, }
|
|
+ pArg, 0, xFunc, 0, 0, 0, #zName, }
|
|
#define LIKEFUNC(zName, nArg, arg, flags) \
|
|
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
|
|
- (void *)arg, 0, likeFunc, 0, #zName, {0} }
|
|
-#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
|
|
+ (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} }
|
|
+#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue) \
|
|
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
|
|
- SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
|
|
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,0,#zName, {0}}
|
|
#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
|
|
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
|
|
- SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
|
|
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
|
|
+#define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \
|
|
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \
|
|
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}}
|
|
+#define INTERNAL_FUNCTION(zName, nArg, xFunc) \
|
|
+ {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
|
|
+ 0, 0, xFunc, 0, 0, 0, #zName, {0} }
|
|
|
|
+
|
|
/*
|
|
** All current savepoints are stored in a linked list starting at
|
|
** sqlite3.pSavepoint. The first element in the list is the most recently
|
|
@@ -15473,6 +16683,8 @@
|
|
#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
|
|
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
|
|
#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
|
|
+#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */
|
|
+#define COLFLAG_SORTERREF 0x0010 /* Use sorter-refs with this column */
|
|
|
|
/*
|
|
** A "Collating Sequence" is defined by an instance of the following
|
|
@@ -15600,6 +16812,9 @@
|
|
struct Table {
|
|
char *zName; /* Name of the table or view */
|
|
Column *aCol; /* Information about each column */
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ Hash *pColHash; /* All columns indexed by name */
|
|
+#endif
|
|
Index *pIndex; /* List of SQL indexes on this table. */
|
|
Select *pSelect; /* NULL for tables. Points to definition if a view. */
|
|
FKey *pFKey; /* Linked list of all foreign keys in this table */
|
|
@@ -15650,6 +16865,7 @@
|
|
#define TF_StatsUsed 0x0100 /* Query planner decisions affected by
|
|
** Index.aiRowLogEst[] values */
|
|
#define TF_HasNotNull 0x0200 /* Contains NOT NULL constraints */
|
|
+#define TF_Shadow 0x0400 /* True for a shadow table */
|
|
|
|
/*
|
|
** Test to see whether or not a table is a virtual table. This is
|
|
@@ -15760,15 +16976,14 @@
|
|
#define OE_Fail 3 /* Stop the operation but leave all prior changes */
|
|
#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */
|
|
#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */
|
|
+#define OE_Update 6 /* Process as a DO UPDATE in an upsert */
|
|
+#define OE_Restrict 7 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
|
|
+#define OE_SetNull 8 /* Set the foreign key value to NULL */
|
|
+#define OE_SetDflt 9 /* Set the foreign key value to its default */
|
|
+#define OE_Cascade 10 /* Cascade the changes */
|
|
+#define OE_Default 11 /* Do whatever the default action is */
|
|
|
|
-#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
|
|
-#define OE_SetNull 7 /* Set the foreign key value to NULL */
|
|
-#define OE_SetDflt 8 /* Set the foreign key value to its default */
|
|
-#define OE_Cascade 9 /* Cascade the changes */
|
|
|
|
-#define OE_Default 10 /* Do whatever the default action is */
|
|
-
|
|
-
|
|
/*
|
|
** An instance of the following structure is passed as the first
|
|
** argument to sqlite3VdbeKeyCompare and is used to control the
|
|
@@ -15781,8 +16996,8 @@
|
|
struct KeyInfo {
|
|
u32 nRef; /* Number of references to this KeyInfo object */
|
|
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
|
|
- u16 nField; /* Number of key columns in the index */
|
|
- u16 nXField; /* Number of columns beyond the key columns */
|
|
+ u16 nKeyField; /* Number of key columns in the index */
|
|
+ u16 nAllField; /* Total columns, including key plus others */
|
|
sqlite3 *db; /* The database connection */
|
|
u8 *aSortOrder; /* Sort order for each column. */
|
|
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
|
|
@@ -15829,8 +17044,8 @@
|
|
u16 nField; /* Number of entries in apMem[] */
|
|
i8 default_rc; /* Comparison result if keys are equal */
|
|
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
|
|
- i8 r1; /* Value to return if (lhs > rhs) */
|
|
- i8 r2; /* Value to return if (rhs < lhs) */
|
|
+ i8 r1; /* Value to return if (lhs < rhs) */
|
|
+ i8 r2; /* Value to return if (lhs > rhs) */
|
|
u8 eqSeen; /* True if an equality comparison has been seen */
|
|
};
|
|
|
|
@@ -15893,6 +17108,7 @@
|
|
unsigned isCovering:1; /* True if this is a covering index */
|
|
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
|
|
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
|
|
+ unsigned bNoQuery:1; /* Do not use this index to optimize queries */
|
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
int nSample; /* Number of elements in aSample[] */
|
|
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
|
|
@@ -15901,6 +17117,7 @@
|
|
tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
|
|
tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
|
|
#endif
|
|
+ Bitmask colNotIdxed; /* 0 for unindexed columns in pTab */
|
|
};
|
|
|
|
/*
|
|
@@ -15936,12 +17153,20 @@
|
|
};
|
|
|
|
/*
|
|
+** Possible values to use within the flags argument to sqlite3GetToken().
|
|
+*/
|
|
+#define SQLITE_TOKEN_QUOTED 0x1 /* Token is a quoted identifier. */
|
|
+#define SQLITE_TOKEN_KEYWORD 0x2 /* Token is a keyword. */
|
|
+
|
|
+/*
|
|
** Each token coming out of the lexer is an instance of
|
|
** this structure. Tokens are also used as part of an expression.
|
|
**
|
|
-** Note if Token.z==0 then Token.dyn and Token.n are undefined and
|
|
-** may contain random values. Do not make any assumptions about Token.dyn
|
|
-** and Token.n when Token.z==0.
|
|
+** The memory that "z" points to is owned by other objects. Take care
|
|
+** that the owner of the "z" string does not deallocate the string before
|
|
+** the Token goes out of scope! Very often, the "z" points to some place
|
|
+** in the middle of the Parse.zSql text. But it might also point to a
|
|
+** static string.
|
|
*/
|
|
struct Token {
|
|
const char *z; /* Text of the token. Not NULL-terminated! */
|
|
@@ -16114,7 +17339,11 @@
|
|
** TK_COLUMN: the value of p5 for OP_Column
|
|
** TK_AGG_FUNCTION: nesting depth */
|
|
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
|
|
- Table *pTab; /* Table for TK_COLUMN expressions. */
|
|
+ union {
|
|
+ Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
|
|
+ ** for a column of an index on an expression */
|
|
+ Window *pWin; /* TK_FUNCTION: Window definition for the func */
|
|
+ } y;
|
|
};
|
|
|
|
/*
|
|
@@ -16122,8 +17351,8 @@
|
|
*/
|
|
#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
|
|
#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
|
|
- /* 0x000004 // available for use */
|
|
- /* 0x000008 // available for use */
|
|
+#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
|
|
+#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
|
|
#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
|
|
#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
|
|
#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
|
|
@@ -16144,11 +17373,13 @@
|
|
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
|
|
#define EP_Alias 0x400000 /* Is an alias for a result set column */
|
|
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
|
|
+#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
|
|
|
|
/*
|
|
-** Combinations of two or more EP_* flags
|
|
+** The EP_Propagate mask is a set of properties that automatically propagate
|
|
+** upwards into parent nodes.
|
|
*/
|
|
-#define EP_Propagate (EP_Collate|EP_Subquery) /* Propagate these bits up tree */
|
|
+#define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc)
|
|
|
|
/*
|
|
** These macros can be used to test, set, or clear bits in the
|
|
@@ -16202,7 +17433,6 @@
|
|
*/
|
|
struct ExprList {
|
|
int nExpr; /* Number of expressions on the list */
|
|
- int nAlloc; /* Number of a[] slots allocated */
|
|
struct ExprList_item { /* For each expression in the list */
|
|
Expr *pExpr; /* The parse tree for this expression */
|
|
char *zName; /* Token associated with this expression */
|
|
@@ -16211,6 +17441,7 @@
|
|
unsigned done :1; /* A flag to indicate when processing is finished */
|
|
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
|
|
unsigned reusable :1; /* Constant expression is reusable */
|
|
+ unsigned bSorterRef :1; /* Defer evaluation until after sorting */
|
|
union {
|
|
struct {
|
|
u16 iOrderByCol; /* For ORDER BY, column number in result set */
|
|
@@ -16222,17 +17453,6 @@
|
|
};
|
|
|
|
/*
|
|
-** An instance of this structure is used by the parser to record both
|
|
-** the parse tree for an expression and the span of input text for an
|
|
-** expression.
|
|
-*/
|
|
-struct ExprSpan {
|
|
- Expr *pExpr; /* The expression parse tree */
|
|
- const char *zStart; /* First character of input text */
|
|
- const char *zEnd; /* One character past the end of input text */
|
|
-};
|
|
-
|
|
-/*
|
|
** An instance of this structure can hold a simple list of identifiers,
|
|
** such as the list "a,b,c" in the following statements:
|
|
**
|
|
@@ -16256,31 +17476,6 @@
|
|
};
|
|
|
|
/*
|
|
-** The bitmask datatype defined below is used for various optimizations.
|
|
-**
|
|
-** Changing this from a 64-bit to a 32-bit type limits the number of
|
|
-** tables in a join to 32 instead of 64. But it also reduces the size
|
|
-** of the library by 738 bytes on ix86.
|
|
-*/
|
|
-#ifdef SQLITE_BITMASK_TYPE
|
|
- typedef SQLITE_BITMASK_TYPE Bitmask;
|
|
-#else
|
|
- typedef u64 Bitmask;
|
|
-#endif
|
|
-
|
|
-/*
|
|
-** The number of bits in a Bitmask. "BMS" means "BitMask Size".
|
|
-*/
|
|
-#define BMS ((int)(sizeof(Bitmask)*8))
|
|
-
|
|
-/*
|
|
-** A bit in a Bitmask
|
|
-*/
|
|
-#define MASKBIT(n) (((Bitmask)1)<<(n))
|
|
-#define MASKBIT32(n) (((unsigned int)1)<<(n))
|
|
-#define ALLBITS ((Bitmask)-1)
|
|
-
|
|
-/*
|
|
** The following structure describes the FROM clause of a SELECT statement.
|
|
** Each table or subquery in the FROM clause is a separate element of
|
|
** the SrcList.a[] array.
|
|
@@ -16321,9 +17516,6 @@
|
|
unsigned viaCoroutine :1; /* Implemented as a co-routine */
|
|
unsigned isRecursive :1; /* True for recursive reference in WITH */
|
|
} fg;
|
|
-#ifndef SQLITE_OMIT_EXPLAIN
|
|
- u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
|
|
-#endif
|
|
int iCursor; /* The VDBE cursor number used to access this table */
|
|
Expr *pOn; /* The ON clause of a join */
|
|
IdList *pUsing; /* The USING clause of a join */
|
|
@@ -16405,12 +17597,16 @@
|
|
struct NameContext {
|
|
Parse *pParse; /* The parser */
|
|
SrcList *pSrcList; /* One or more tables used to resolve names */
|
|
- ExprList *pEList; /* Optional list of result-set columns */
|
|
- AggInfo *pAggInfo; /* Information about aggregates at this level */
|
|
+ union {
|
|
+ ExprList *pEList; /* Optional list of result-set columns */
|
|
+ AggInfo *pAggInfo; /* Information about aggregates at this level */
|
|
+ Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
|
|
+ } uNC;
|
|
NameContext *pNext; /* Next outer name context. NULL for outermost */
|
|
int nRef; /* Number of names resolved by this context */
|
|
int nErr; /* Number of errors encountered while resolving names */
|
|
u16 ncFlags; /* Zero or more NC_* flags defined below */
|
|
+ Select *pWinSelect; /* SELECT statement for any window functions */
|
|
};
|
|
|
|
/*
|
|
@@ -16428,17 +17624,49 @@
|
|
#define NC_HasAgg 0x0010 /* One or more aggregate functions seen */
|
|
#define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */
|
|
#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */
|
|
+#define NC_UEList 0x0080 /* True if uNC.pEList is used */
|
|
+#define NC_UAggInfo 0x0100 /* True if uNC.pAggInfo is used */
|
|
+#define NC_UUpsert 0x0200 /* True if uNC.pUpsert is used */
|
|
#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
|
|
+#define NC_Complex 0x2000 /* True if a function or subquery seen */
|
|
+#define NC_AllowWin 0x4000 /* Window functions are allowed here */
|
|
|
|
/*
|
|
+** An instance of the following object describes a single ON CONFLICT
|
|
+** clause in an upsert.
|
|
+**
|
|
+** The pUpsertTarget field is only set if the ON CONFLICT clause includes
|
|
+** conflict-target clause. (In "ON CONFLICT(a,b)" the "(a,b)" is the
|
|
+** conflict-target clause.) The pUpsertTargetWhere is the optional
|
|
+** WHERE clause used to identify partial unique indexes.
|
|
+**
|
|
+** pUpsertSet is the list of column=expr terms of the UPDATE statement.
|
|
+** The pUpsertSet field is NULL for a ON CONFLICT DO NOTHING. The
|
|
+** pUpsertWhere is the WHERE clause for the UPDATE and is NULL if the
|
|
+** WHERE clause is omitted.
|
|
+*/
|
|
+struct Upsert {
|
|
+ ExprList *pUpsertTarget; /* Optional description of conflicting index */
|
|
+ Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
|
|
+ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
|
|
+ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
|
|
+ /* The fields above comprise the parse tree for the upsert clause.
|
|
+ ** The fields below are used to transfer information from the INSERT
|
|
+ ** processing down into the UPDATE processing while generating code.
|
|
+ ** Upsert owns the memory allocated above, but not the memory below. */
|
|
+ Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */
|
|
+ SrcList *pUpsertSrc; /* Table to be updated */
|
|
+ int regData; /* First register holding array of VALUES */
|
|
+ int iDataCur; /* Index of the data cursor */
|
|
+ int iIdxCur; /* Index of the first index cursor */
|
|
+};
|
|
+
|
|
+/*
|
|
** An instance of the following structure contains all information
|
|
** needed to generate code for a single SELECT statement.
|
|
**
|
|
-** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0.
|
|
-** If there is a LIMIT clause, the parser sets nLimit to the value of the
|
|
-** limit and nOffset to the value of the offset (or 0 if there is not
|
|
-** offset). But later on, nLimit and nOffset become the memory locations
|
|
-** in the VDBE that record the limit and offset counters.
|
|
+** See the header comment on the computeLimitRegisters() routine for a
|
|
+** detailed description of the meaning of the iLimit and iOffset fields.
|
|
**
|
|
** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes.
|
|
** These addresses must be stored so that we can go back and fill in
|
|
@@ -16456,9 +17684,7 @@
|
|
LogEst nSelectRow; /* Estimated number of result rows */
|
|
u32 selFlags; /* Various SF_* values */
|
|
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
|
-#if SELECTTRACE_ENABLED
|
|
- char zSelName[12]; /* Symbolic name of this SELECT use for debugging */
|
|
-#endif
|
|
+ u32 selId; /* Unique identifier number for this SELECT */
|
|
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
|
|
SrcList *pSrc; /* The FROM clause */
|
|
Expr *pWhere; /* The WHERE clause */
|
|
@@ -16468,8 +17694,11 @@
|
|
Select *pPrior; /* Prior select in a compound select statement */
|
|
Select *pNext; /* Next select to the left in a compound */
|
|
Expr *pLimit; /* LIMIT expression. NULL means not used. */
|
|
- Expr *pOffset; /* OFFSET expression. NULL means not used. */
|
|
With *pWith; /* WITH clause attached to this select. Or NULL. */
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ Window *pWin; /* List of window functions */
|
|
+ Window *pWinDefn; /* List of named window definitions */
|
|
+#endif
|
|
};
|
|
|
|
/*
|
|
@@ -16499,8 +17728,8 @@
|
|
#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
|
|
#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
|
|
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
|
|
+#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
|
|
|
|
-
|
|
/*
|
|
** The results of a SELECT can be distributed in several ways, as defined
|
|
** by one of the following macros. The "SRT" prefix means "SELECT Result
|
|
@@ -16614,13 +17843,6 @@
|
|
};
|
|
|
|
/*
|
|
-** Size of the column cache
|
|
-*/
|
|
-#ifndef SQLITE_N_COLCACHE
|
|
-# define SQLITE_N_COLCACHE 10
|
|
-#endif
|
|
-
|
|
-/*
|
|
** At least one instance of the following structure is created for each
|
|
** trigger that may be fired while parsing an INSERT, UPDATE or DELETE
|
|
** statement. All such objects are stored in the linked list headed at
|
|
@@ -16695,7 +17917,6 @@
|
|
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
|
|
u8 okConstFactor; /* OK to factor out constants */
|
|
u8 disableLookaside; /* Number of times lookaside has been disabled */
|
|
- u8 nColCache; /* Number of entries in aColCache[] */
|
|
int nRangeReg; /* Size of the temporary register block */
|
|
int iRangeReg; /* First register in temporary register block */
|
|
int nErr; /* Number of errors seen */
|
|
@@ -16703,10 +17924,8 @@
|
|
int nMem; /* Number of memory cells used so far */
|
|
int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
|
|
int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
|
|
- int iSelfTab; /* Table for associated with an index on expr, or negative
|
|
+ int iSelfTab; /* Table associated with an index on expr, or negative
|
|
** of the base register during check-constraint eval */
|
|
- int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
|
|
- int iCacheCnt; /* Counter used to generate aColCache[].lru values */
|
|
int nLabel; /* Number of labels used */
|
|
int *aLabel; /* Space to hold the labels */
|
|
ExprList *pConstExpr;/* Constant expressions */
|
|
@@ -16716,10 +17935,7 @@
|
|
int regRowid; /* Register holding rowid of CREATE TABLE entry */
|
|
int regRoot; /* Register holding root page number for new objects */
|
|
int nMaxArg; /* Max args passed to user function by sub-program */
|
|
-#if SELECTTRACE_ENABLED
|
|
- int nSelect; /* Number of SELECT statements seen */
|
|
- int nSelectIndent; /* How far to indent SELECTTRACE() output */
|
|
-#endif
|
|
+ int nSelect; /* Number of SELECT stmts. Counter for Select.selId */
|
|
#ifndef SQLITE_OMIT_SHARED_CACHE
|
|
int nTableLock; /* Number of locks in aTableLock */
|
|
TableLock *aTableLock; /* Required table locks for shared-cache mode */
|
|
@@ -16727,7 +17943,7 @@
|
|
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
|
|
Parse *pToplevel; /* Parse structure for main program (or NULL) */
|
|
Table *pTriggerTab; /* Table triggers are being coded for */
|
|
- int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
|
|
+ int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
|
|
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
|
|
u32 oldmask; /* Mask of old.* columns referenced */
|
|
u32 newmask; /* Mask of new.* columns referenced */
|
|
@@ -16739,17 +17955,9 @@
|
|
** Fields above must be initialized to zero. The fields that follow,
|
|
** down to the beginning of the recursive section, do not need to be
|
|
** initialized as they will be set before being used. The boundary is
|
|
- ** determined by offsetof(Parse,aColCache).
|
|
+ ** determined by offsetof(Parse,aTempReg).
|
|
**************************************************************************/
|
|
|
|
- struct yColCache {
|
|
- int iTable; /* Table cursor number */
|
|
- i16 iColumn; /* Table column number */
|
|
- u8 tempReg; /* iReg is a temp register that needs to be freed */
|
|
- int iLevel; /* Nesting level */
|
|
- int iReg; /* Reg with value of this column. 0 means none. */
|
|
- int lru; /* Least recently used entry has the smallest value */
|
|
- } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
|
|
int aTempReg[8]; /* Holding area for temporary registers */
|
|
Token sNameToken; /* Token with unqualified schema object name */
|
|
|
|
@@ -16764,19 +17972,21 @@
|
|
ynVar nVar; /* Number of '?' variables seen in the SQL so far */
|
|
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
|
|
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
|
+#if !(defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE))
|
|
+ u8 eParseMode; /* PARSE_MODE_XXX constant */
|
|
+#endif
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
|
|
int nVtabLock; /* Number of virtual tables to lock */
|
|
#endif
|
|
int nHeight; /* Expression tree height of current sub-select */
|
|
#ifndef SQLITE_OMIT_EXPLAIN
|
|
- int iSelectId; /* ID of current select for EXPLAIN output */
|
|
- int iNextSelectId; /* Next available select ID for EXPLAIN output */
|
|
+ int addrExplain; /* Address of current OP_Explain opcode */
|
|
#endif
|
|
VList *pVList; /* Mapping between variable names and numbers */
|
|
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
|
const char *zTail; /* All SQL text past the last semicolon parsed */
|
|
Table *pNewTable; /* A table being constructed by CREATE TABLE */
|
|
+ Index *pNewIndex; /* An index being constructed by CREATE INDEX */
|
|
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
|
|
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
@@ -16787,12 +17997,20 @@
|
|
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
|
|
With *pWith; /* Current WITH clause, or NULL */
|
|
With *pWithToFree; /* Free this WITH object at the end of the parse */
|
|
+#ifndef SQLITE_OMIT_ALTERTABLE
|
|
+ RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */
|
|
+#endif
|
|
};
|
|
|
|
+#define PARSE_MODE_NORMAL 0
|
|
+#define PARSE_MODE_DECLARE_VTAB 1
|
|
+#define PARSE_MODE_RENAME_COLUMN 2
|
|
+#define PARSE_MODE_RENAME_TABLE 3
|
|
+
|
|
/*
|
|
** Sizes and pointers of various parts of the Parse object.
|
|
*/
|
|
-#define PARSE_HDR_SZ offsetof(Parse,aColCache) /* Recursive part w/o aColCache*/
|
|
+#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/
|
|
#define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */
|
|
#define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */
|
|
#define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */
|
|
@@ -16803,9 +18021,21 @@
|
|
#ifdef SQLITE_OMIT_VIRTUALTABLE
|
|
#define IN_DECLARE_VTAB 0
|
|
#else
|
|
- #define IN_DECLARE_VTAB (pParse->declareVtab)
|
|
+ #define IN_DECLARE_VTAB (pParse->eParseMode==PARSE_MODE_DECLARE_VTAB)
|
|
#endif
|
|
|
|
+#if defined(SQLITE_OMIT_ALTERTABLE)
|
|
+ #define IN_RENAME_OBJECT 0
|
|
+#else
|
|
+ #define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME_COLUMN)
|
|
+#endif
|
|
+
|
|
+#if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)
|
|
+ #define IN_SPECIAL_PARSE 0
|
|
+#else
|
|
+ #define IN_SPECIAL_PARSE (pParse->eParseMode!=PARSE_MODE_NORMAL)
|
|
+#endif
|
|
+
|
|
/*
|
|
** An instance of the following structure can be declared on a stack and used
|
|
** to save the Parse.zAuthContext value so that it can be restored later.
|
|
@@ -16829,6 +18059,7 @@
|
|
*/
|
|
#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
|
|
/* Also used in P2 (not P5) of OP_Delete */
|
|
+#define OPFLAG_NOCHNG 0x01 /* OP_VColumn nochange for UPDATE */
|
|
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
|
|
#define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */
|
|
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
|
|
@@ -16844,6 +18075,7 @@
|
|
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
|
|
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */
|
|
#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
|
|
+#define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */
|
|
|
|
/*
|
|
* Each trigger present in the database schema is stored as an instance of
|
|
@@ -16929,8 +18161,10 @@
|
|
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
|
|
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
|
|
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
|
|
- ExprList *pExprList; /* SET clause for UPDATE. */
|
|
+ ExprList *pExprList; /* SET clause for UPDATE */
|
|
IdList *pIdList; /* Column names for INSERT */
|
|
+ Upsert *pUpsert; /* Upsert clauses on an INSERT */
|
|
+ char *zSpan; /* Original SQL text of this command */
|
|
TriggerStep *pNext; /* Next in the link-list */
|
|
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
|
|
};
|
|
@@ -16954,18 +18188,15 @@
|
|
** An objected used to accumulate the text of a string where we
|
|
** do not necessarily know how big the string will be in the end.
|
|
*/
|
|
-struct StrAccum {
|
|
+struct sqlite3_str {
|
|
sqlite3 *db; /* Optional database for lookaside. Can be NULL */
|
|
- char *zBase; /* A base allocation. Not from malloc. */
|
|
char *zText; /* The string collected so far */
|
|
- u32 nChar; /* Length of the string so far */
|
|
u32 nAlloc; /* Amount of space allocated in zText */
|
|
u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
|
|
- u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
|
|
+ u32 nChar; /* Length of the string so far */
|
|
+ u8 accError; /* SQLITE_NOMEM or SQLITE_TOOBIG */
|
|
u8 printfFlags; /* SQLITE_PRINTF flags below */
|
|
};
|
|
-#define STRACCUM_NOMEM 1
|
|
-#define STRACCUM_TOOBIG 2
|
|
#define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */
|
|
#define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
|
|
#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */
|
|
@@ -16982,9 +18213,15 @@
|
|
char **pzErrMsg; /* Error message stored here */
|
|
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
|
|
int rc; /* Result code stored here */
|
|
+ u32 mInitFlags; /* Flags controlling error messages */
|
|
} InitData;
|
|
|
|
/*
|
|
+** Allowed values for mInitFlags
|
|
+*/
|
|
+#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
|
|
+
|
|
+/*
|
|
** Structure containing global configuration data for the SQLite library.
|
|
**
|
|
** This structure also contains some state information.
|
|
@@ -16995,6 +18232,7 @@
|
|
int bFullMutex; /* True to enable full mutexing */
|
|
int bOpenUri; /* True to interpret filenames as URIs */
|
|
int bUseCis; /* Use covering indices for full-scans */
|
|
+ int bSmallMalloc; /* Avoid large memory allocations if true */
|
|
int mxStrlen; /* Maximum string length */
|
|
int neverCorrupt; /* Database is always well-formed */
|
|
int szLookaside; /* Default lookaside buffer size */
|
|
@@ -17008,9 +18246,6 @@
|
|
int mnReq, mxReq; /* Min and max heap requests sizes */
|
|
sqlite3_int64 szMmap; /* mmap() space per open file */
|
|
sqlite3_int64 mxMmap; /* Maximum value for szMmap */
|
|
- void *pScratch; /* Scratch memory */
|
|
- int szScratch; /* Size of each scratch buffer */
|
|
- int nScratch; /* Number of scratch buffers */
|
|
void *pPage; /* Page cache memory */
|
|
int szPage; /* Size of each page in pPage[] */
|
|
int nPage; /* Number of pages in pPage[] */
|
|
@@ -17036,7 +18271,7 @@
|
|
/* The following callback (if not NULL) is invoked on every VDBE branch
|
|
** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE.
|
|
*/
|
|
- void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */
|
|
+ void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */
|
|
void *pVdbeBranchArg; /* 1st argument */
|
|
#endif
|
|
#ifndef SQLITE_UNTESTABLE
|
|
@@ -17043,7 +18278,9 @@
|
|
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
|
|
#endif
|
|
int bLocaltimeFault; /* True to fail localtime() calls */
|
|
+ int bInternalFunctions; /* Internal SQL functions are visible */
|
|
int iOnceResetThreshold; /* When to reset OP_Once counters */
|
|
+ u32 szSorterRef; /* Min size in bytes to use sorter-refs */
|
|
};
|
|
|
|
/*
|
|
@@ -17083,9 +18320,12 @@
|
|
struct CCurHint *pCCurHint; /* Used by codeCursorHint() */
|
|
int *aiCol; /* array of column indexes */
|
|
struct IdxCover *pIdxCover; /* Check for index coverage */
|
|
- struct IdxExprTrans *pIdxTrans; /* Convert indexed expr to column */
|
|
+ struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */
|
|
ExprList *pGroupBy; /* GROUP BY clause */
|
|
- struct HavingToWhereCtx *pHavingCtx; /* HAVING to WHERE clause ctx */
|
|
+ Select *pSelect; /* HAVING to WHERE clause ctx */
|
|
+ struct WindowRewrite *pRewrite; /* Window rewrite context */
|
|
+ struct WhereConst *pConst; /* WHERE clause constants */
|
|
+ struct RenameCtx *pRename; /* RENAME COLUMN context */
|
|
} u;
|
|
};
|
|
|
|
@@ -17097,6 +18337,7 @@
|
|
SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
|
|
SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker*, Expr*);
|
|
SQLITE_PRIVATE int sqlite3SelectWalkNoop(Walker*, Select*);
|
|
+SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker*, Select*);
|
|
#ifdef SQLITE_DEBUG
|
|
SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*);
|
|
#endif
|
|
@@ -17136,6 +18377,68 @@
|
|
#endif /* SQLITE_DEBUG */
|
|
|
|
/*
|
|
+** This object is used in varioius ways, all related to window functions
|
|
+**
|
|
+** (1) A single instance of this structure is attached to the
|
|
+** the Expr.pWin field for each window function in an expression tree.
|
|
+** This object holds the information contained in the OVER clause,
|
|
+** plus additional fields used during code generation.
|
|
+**
|
|
+** (2) All window functions in a single SELECT form a linked-list
|
|
+** attached to Select.pWin. The Window.pFunc and Window.pExpr
|
|
+** fields point back to the expression that is the window function.
|
|
+**
|
|
+** (3) The terms of the WINDOW clause of a SELECT are instances of this
|
|
+** object on a linked list attached to Select.pWinDefn.
|
|
+**
|
|
+** The uses (1) and (2) are really the same Window object that just happens
|
|
+** to be accessible in two different ways. Use (3) is are separate objects.
|
|
+*/
|
|
+struct Window {
|
|
+ char *zName; /* Name of window (may be NULL) */
|
|
+ ExprList *pPartition; /* PARTITION BY clause */
|
|
+ ExprList *pOrderBy; /* ORDER BY clause */
|
|
+ u8 eType; /* TK_RANGE or TK_ROWS */
|
|
+ u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
|
|
+ u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
|
|
+ Expr *pStart; /* Expression for "<expr> PRECEDING" */
|
|
+ Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
|
|
+ Window *pNextWin; /* Next window function belonging to this SELECT */
|
|
+ Expr *pFilter; /* The FILTER expression */
|
|
+ FuncDef *pFunc; /* The function */
|
|
+ int iEphCsr; /* Partition buffer or Peer buffer */
|
|
+ int regAccum;
|
|
+ int regResult;
|
|
+ int csrApp; /* Function cursor (used by min/max) */
|
|
+ int regApp; /* Function register (also used by min/max) */
|
|
+ int regPart; /* First in a set of registers holding PARTITION BY
|
|
+ ** and ORDER BY values for the window */
|
|
+ Expr *pOwner; /* Expression object this window is attached to */
|
|
+ int nBufferCol; /* Number of columns in buffer table */
|
|
+ int iArgCol; /* Offset of first argument for this function */
|
|
+};
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
|
|
+SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p);
|
|
+SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
|
|
+SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
|
|
+SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*);
|
|
+SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*);
|
|
+SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
|
|
+SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*);
|
|
+SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
|
|
+SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
|
|
+SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
|
|
+SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
|
|
+SQLITE_PRIVATE void sqlite3WindowFunctions(void);
|
|
+#else
|
|
+# define sqlite3WindowDelete(a,b)
|
|
+# define sqlite3WindowFunctions()
|
|
+# define sqlite3WindowAttach(a,b,c)
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Assuming zIn points to the first byte of a UTF-8 character,
|
|
** advance zIn to point to the first byte of the next UTF-8 character.
|
|
*/
|
|
@@ -17152,6 +18455,7 @@
|
|
** using sqlite3_log(). The routines also provide a convenient place
|
|
** to set a debugger breakpoint.
|
|
*/
|
|
+SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType);
|
|
SQLITE_PRIVATE int sqlite3CorruptError(int);
|
|
SQLITE_PRIVATE int sqlite3MisuseError(int);
|
|
SQLITE_PRIVATE int sqlite3CantopenError(int);
|
|
@@ -17221,9 +18525,7 @@
|
|
# define sqlite3Tolower(x) tolower((unsigned char)(x))
|
|
# define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`')
|
|
#endif
|
|
-#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
|
SQLITE_PRIVATE int sqlite3IsIdChar(u8);
|
|
-#endif
|
|
|
|
/*
|
|
** Internal function prototypes
|
|
@@ -17230,6 +18532,7 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*);
|
|
SQLITE_PRIVATE int sqlite3Strlen30(const char*);
|
|
+#define sqlite3Strlen30NN(C) (strlen(C)&0x3fffffff)
|
|
SQLITE_PRIVATE char *sqlite3ColumnType(Column*,char*);
|
|
#define sqlite3StrNICmp sqlite3_strnicmp
|
|
|
|
@@ -17242,6 +18545,7 @@
|
|
SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3*, u64);
|
|
SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3*,const char*);
|
|
SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
|
|
+SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3*,const char*,const char*);
|
|
SQLITE_PRIVATE void *sqlite3Realloc(void*, u64);
|
|
SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
|
|
SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64);
|
|
@@ -17249,8 +18553,6 @@
|
|
SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*);
|
|
SQLITE_PRIVATE int sqlite3MallocSize(void*);
|
|
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*);
|
|
-SQLITE_PRIVATE void *sqlite3ScratchMalloc(int);
|
|
-SQLITE_PRIVATE void sqlite3ScratchFree(void*);
|
|
SQLITE_PRIVATE void *sqlite3PageMalloc(int);
|
|
SQLITE_PRIVATE void sqlite3PageFree(void*);
|
|
SQLITE_PRIVATE void sqlite3MemSetDefault(void);
|
|
@@ -17306,11 +18608,18 @@
|
|
SQLITE_PRIVATE void sqlite3StatusUp(int, int);
|
|
SQLITE_PRIVATE void sqlite3StatusDown(int, int);
|
|
SQLITE_PRIVATE void sqlite3StatusHighwater(int, int);
|
|
+SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3*,int*);
|
|
|
|
/* Access to mutexes used by sqlite3_status() */
|
|
SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void);
|
|
SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void);
|
|
|
|
+#if defined(SQLITE_ENABLE_MULTITHREADED_CHECKS) && !defined(SQLITE_MUTEX_OMIT)
|
|
+SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*);
|
|
+#else
|
|
+# define sqlite3MutexWarnOnContention(x)
|
|
+#endif
|
|
+
|
|
#ifndef SQLITE_OMIT_FLOATING_POINT
|
|
SQLITE_PRIVATE int sqlite3IsNaN(double);
|
|
#else
|
|
@@ -17327,8 +18636,6 @@
|
|
sqlite3_value **apArg; /* The argument values */
|
|
};
|
|
|
|
-SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, const char*, va_list);
|
|
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, const char*, ...);
|
|
SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...);
|
|
SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
|
|
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
|
@@ -17342,9 +18649,14 @@
|
|
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
|
|
SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
|
|
SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
|
|
+SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*);
|
|
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
|
|
SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView*, const Window*, u8);
|
|
+SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8);
|
|
#endif
|
|
+#endif
|
|
|
|
|
|
SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*);
|
|
@@ -17368,7 +18680,7 @@
|
|
SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*);
|
|
SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
|
|
SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*);
|
|
-SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
|
|
+SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*, int);
|
|
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
|
|
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
|
|
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
|
|
@@ -17375,11 +18687,12 @@
|
|
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
|
|
SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
|
|
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
|
|
-SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
|
|
+SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
|
|
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
|
|
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
|
|
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
|
|
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
|
|
+SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32);
|
|
SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
SQLITE_PRIVATE Module *sqlite3PragmaVtabRegister(sqlite3*,const char *zName);
|
|
@@ -17405,7 +18718,7 @@
|
|
SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int);
|
|
SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
|
|
SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*);
|
|
-SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*);
|
|
+SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
|
|
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
|
|
SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
|
|
SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
|
@@ -17429,8 +18742,9 @@
|
|
SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int,int*);
|
|
#endif
|
|
|
|
-SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
|
|
-SQLITE_PRIVATE void sqlite3RowSetClear(RowSet*);
|
|
+SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3*);
|
|
+SQLITE_PRIVATE void sqlite3RowSetDelete(void*);
|
|
+SQLITE_PRIVATE void sqlite3RowSetClear(void*);
|
|
SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet*, i64);
|
|
SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, int iBatch, i64);
|
|
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet*, i64*);
|
|
@@ -17449,6 +18763,7 @@
|
|
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
|
|
SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
|
|
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
|
|
+SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*);
|
|
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
|
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse);
|
|
SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse);
|
|
@@ -17456,9 +18771,9 @@
|
|
# define sqlite3AutoincrementBegin(X)
|
|
# define sqlite3AutoincrementEnd(X)
|
|
#endif
|
|
-SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
|
|
+SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*);
|
|
SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
|
|
-SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
|
|
+SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse*, IdList*, Token*);
|
|
SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*);
|
|
SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
|
|
SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
|
|
@@ -17477,22 +18792,23 @@
|
|
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
|
|
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
|
|
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
|
|
- Expr*,ExprList*,u32,Expr*,Expr*);
|
|
+ Expr*,ExprList*,u32,Expr*);
|
|
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
|
|
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
|
|
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
|
|
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
|
|
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
|
|
-SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*);
|
|
+SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
|
|
#endif
|
|
-SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
|
-SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
|
+SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
|
|
+SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
|
|
+ Upsert*);
|
|
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
|
|
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
|
|
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*);
|
|
SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
|
|
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
|
|
-SQLITE_PRIVATE int sqlite3WhereOrderedInnerLoop(WhereInfo*);
|
|
+SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);
|
|
SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
|
|
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
|
|
SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
|
|
@@ -17502,15 +18818,8 @@
|
|
#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
|
|
SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
|
|
SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
|
|
-SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(Parse*, Table*, int, int, int);
|
|
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
|
|
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int);
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int);
|
|
-SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*);
|
|
-SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*);
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int);
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*);
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int);
|
|
SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int);
|
|
SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
|
|
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
|
|
@@ -17541,6 +18850,7 @@
|
|
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int);
|
|
SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int);
|
|
SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int);
|
|
+SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int);
|
|
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
|
|
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
|
|
SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx);
|
|
@@ -17558,6 +18868,8 @@
|
|
SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*);
|
|
SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *);
|
|
SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
|
|
+SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*);
|
|
+SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*);
|
|
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
|
|
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
|
|
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
|
|
@@ -17570,13 +18882,17 @@
|
|
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
|
|
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
|
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE int sqlite3IsRowidN(const char*, int);
|
|
+#endif
|
|
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
|
|
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
|
|
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
|
|
SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
|
|
SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse*,int);
|
|
+SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(Expr*,int*,int);
|
|
SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
|
- u8,u8,int,int*,int*);
|
|
+ u8,u8,int,int*,int*,Upsert*);
|
|
#ifdef SQLITE_ENABLE_NULL_TRIM
|
|
SQLITE_PRIVATE void sqlite3SetMakeRecordP5(Vdbe*,Table*);
|
|
#else
|
|
@@ -17595,10 +18911,8 @@
|
|
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
|
|
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*);
|
|
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int);
|
|
-#if SELECTTRACE_ENABLED
|
|
-SQLITE_PRIVATE void sqlite3SelectSetName(Select*,const char*);
|
|
-#else
|
|
-# define sqlite3SelectSetName(A,B)
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(int,const char*,int);
|
|
#endif
|
|
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
|
|
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
|
|
@@ -17610,7 +18924,7 @@
|
|
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
|
|
|
|
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
|
-SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
|
|
+SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
|
|
#endif
|
|
|
|
#ifndef SQLITE_OMIT_TRIGGER
|
|
@@ -17626,11 +18940,15 @@
|
|
SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int);
|
|
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
|
|
SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
|
|
-SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
|
|
-SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
|
|
- Select*,u8);
|
|
-SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8);
|
|
-SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
|
|
+SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
|
|
+ const char*,const char*);
|
|
+SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*,
|
|
+ Select*,u8,Upsert*,
|
|
+ const char*,const char*);
|
|
+SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,ExprList*, Expr*, u8,
|
|
+ const char*,const char*);
|
|
+SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*,
|
|
+ const char*,const char*);
|
|
SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*);
|
|
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
|
|
SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
|
|
@@ -17737,15 +19055,23 @@
|
|
SQLITE_PRIVATE const char *sqlite3ErrName(int);
|
|
#endif
|
|
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+SQLITE_PRIVATE int sqlite3MemdbInit(void);
|
|
+#endif
|
|
+
|
|
SQLITE_PRIVATE const char *sqlite3ErrStr(int);
|
|
SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse);
|
|
SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
|
|
+SQLITE_PRIVATE int sqlite3IsBinary(const CollSeq*);
|
|
SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
|
|
SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
|
|
+SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr);
|
|
+SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,Expr*,Expr*);
|
|
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
|
|
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
|
|
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
|
|
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
|
|
+SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*);
|
|
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
|
|
SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
|
|
SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64);
|
|
@@ -17783,13 +19109,20 @@
|
|
SQLITE_PRIVATE int sqlite3PendingByte;
|
|
#endif
|
|
#endif
|
|
+#ifdef VDBE_PROFILE
|
|
+SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt;
|
|
+#endif
|
|
SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int);
|
|
SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*);
|
|
SQLITE_PRIVATE void sqlite3AlterFunctions(void);
|
|
SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
|
|
+SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
|
|
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE int sqlite3GetTokenNormalized(const unsigned char *, int *, int *);
|
|
+#endif
|
|
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
|
|
-SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
|
|
+SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
|
|
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int);
|
|
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
|
|
SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
|
|
@@ -17802,10 +19135,14 @@
|
|
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
|
|
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
|
|
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
|
|
+SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*);
|
|
+SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
|
|
+SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
|
|
+SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
|
|
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
|
|
-SQLITE_PRIVATE char sqlite3AffinityType(const char*, u8*);
|
|
+SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
|
|
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
|
|
-SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
|
|
+SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
|
|
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
|
|
SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
|
|
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
|
|
@@ -17820,14 +19157,20 @@
|
|
SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
|
|
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
|
|
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
|
|
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
|
|
+
|
|
#ifdef SQLITE_DEBUG
|
|
SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
|
|
#endif
|
|
SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
|
|
void (*)(sqlite3_context*,int,sqlite3_value **),
|
|
- void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
|
|
+ void (*)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*)(sqlite3_context*),
|
|
+ void (*)(sqlite3_context*),
|
|
+ void (*)(sqlite3_context*,int,sqlite3_value **),
|
|
FuncDestructor *pDestructor
|
|
);
|
|
+SQLITE_PRIVATE void sqlite3NoopDestructor(void*);
|
|
SQLITE_PRIVATE void sqlite3OomFault(sqlite3*);
|
|
SQLITE_PRIVATE void sqlite3OomClear(sqlite3*);
|
|
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
|
|
@@ -17834,11 +19177,7 @@
|
|
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
|
|
|
|
SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
|
|
-SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int);
|
|
-SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*);
|
|
-SQLITE_PRIVATE void sqlite3AppendChar(StrAccum*,int,char);
|
|
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
|
|
-SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*);
|
|
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int);
|
|
SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
|
|
|
|
@@ -17865,10 +19204,11 @@
|
|
** The interface to the LEMON-generated parser
|
|
*/
|
|
#ifndef SQLITE_AMALGAMATION
|
|
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(u64));
|
|
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(u64), Parse*);
|
|
SQLITE_PRIVATE void sqlite3ParserFree(void*, void(*)(void*));
|
|
#endif
|
|
-SQLITE_PRIVATE void sqlite3Parser(void*, int, Token, Parse*);
|
|
+SQLITE_PRIVATE void sqlite3Parser(void*, int, Token);
|
|
+SQLITE_PRIVATE int sqlite3ParserFallback(int);
|
|
#ifdef YYTRACKMAXSTACKDEPTH
|
|
SQLITE_PRIVATE int sqlite3ParserStackPeak(void*);
|
|
#endif
|
|
@@ -17934,11 +19274,13 @@
|
|
SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
|
|
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
|
|
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
|
|
-SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
|
|
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
|
|
SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
|
|
SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
|
|
SQLITE_PRIVATE void sqlite3ParserReset(Parse*);
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE void sqlite3Normalize(Vdbe*, const char*, int, u8);
|
|
+#endif
|
|
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
|
|
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
|
|
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
|
|
@@ -17956,7 +19298,19 @@
|
|
#define sqlite3WithPush(x,y,z)
|
|
#define sqlite3WithDelete(x,y)
|
|
#endif
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*);
|
|
+SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*);
|
|
+SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
|
|
+SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
|
|
+SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
|
|
+#else
|
|
+#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0)
|
|
+#define sqlite3UpsertDelete(x,y)
|
|
+#define sqlite3UpsertDup(x,y) ((Upsert*)0)
|
|
+#endif
|
|
|
|
+
|
|
/* Declarations for functions in fkey.c. All of these are replaced by
|
|
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
|
|
** key functionality is available. If OMIT_TRIGGER is defined but
|
|
@@ -18025,7 +19379,8 @@
|
|
|
|
SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
|
SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
|
|
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
+#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
|
+ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *);
|
|
#endif
|
|
|
|
@@ -18057,6 +19412,9 @@
|
|
#ifdef SQLITE_DEBUG
|
|
SQLITE_PRIVATE void sqlite3ParserTrace(FILE*, char *);
|
|
#endif
|
|
+#if defined(YYCOVERAGE)
|
|
+SQLITE_PRIVATE int sqlite3ParserCoverage(FILE*);
|
|
+#endif
|
|
|
|
/*
|
|
** If the SQLITE_ENABLE IOTRACE exists then the global variable
|
|
@@ -18111,8 +19469,7 @@
|
|
#endif
|
|
#define MEMTYPE_HEAP 0x01 /* General heap allocations */
|
|
#define MEMTYPE_LOOKASIDE 0x02 /* Heap that might have been lookaside */
|
|
-#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */
|
|
-#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
|
|
+#define MEMTYPE_PCACHE 0x04 /* Page cache allocations */
|
|
|
|
/*
|
|
** Threading interface
|
|
@@ -18122,6 +19479,9 @@
|
|
SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread*, void**);
|
|
#endif
|
|
|
|
+#if defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)
|
|
+SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3*);
|
|
+#endif
|
|
#if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)
|
|
SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*);
|
|
#endif
|
|
@@ -18341,6 +19701,7 @@
|
|
SQLITE_THREADSAFE==1, /* bFullMutex */
|
|
SQLITE_USE_URI, /* bOpenUri */
|
|
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
|
|
+ 0, /* bSmallMalloc */
|
|
0x7ffffffe, /* mxStrlen */
|
|
0, /* neverCorrupt */
|
|
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
|
|
@@ -18353,9 +19714,6 @@
|
|
0, 0, /* mnHeap, mxHeap */
|
|
SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */
|
|
SQLITE_MAX_MMAP_SIZE, /* mxMmap */
|
|
- (void*)0, /* pScratch */
|
|
- 0, /* szScratch */
|
|
- 0, /* nScratch */
|
|
(void*)0, /* pPage */
|
|
0, /* szPage */
|
|
SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */
|
|
@@ -18384,7 +19742,9 @@
|
|
0, /* xTestCallback */
|
|
#endif
|
|
0, /* bLocaltimeFault */
|
|
- 0x7ffffffe /* iOnceResetThreshold */
|
|
+ 0, /* bInternalFunctions */
|
|
+ 0x7ffffffe, /* iOnceResetThreshold */
|
|
+ SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */
|
|
};
|
|
|
|
/*
|
|
@@ -18402,6 +19762,13 @@
|
|
{ "1", 1 }
|
|
};
|
|
|
|
+#ifdef VDBE_PROFILE
|
|
+/*
|
|
+** The following performance counter can be used in place of
|
|
+** sqlite3Hwtime() for profiling. This is a no-op on standard builds.
|
|
+*/
|
|
+SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0;
|
|
+#endif
|
|
|
|
/*
|
|
** The value of the "pending" byte must be 0x40000000 (1 byte past the
|
|
@@ -18546,6 +19913,7 @@
|
|
Bool isEphemeral:1; /* True for an ephemeral table */
|
|
Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
|
|
Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
|
|
+ Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */
|
|
Btree *pBtx; /* Separate file holding temporary table */
|
|
i64 seqCount; /* Sequence counter */
|
|
int *aAltMap; /* Mapping from table to index column numbers */
|
|
@@ -18557,8 +19925,9 @@
|
|
u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
|
|
int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0
|
|
** if there have been no prior seeks on the cursor. */
|
|
- /* NB: seekResult does not distinguish between "no seeks have ever occurred
|
|
- ** on this cursor" and "the most recent seek was an exact match". */
|
|
+ /* seekResult does not distinguish between "no seeks have ever occurred
|
|
+ ** on this cursor" and "the most recent seek was an exact match".
|
|
+ ** For CURTYPE_PSEUDO, seekResult is the register holding the record */
|
|
|
|
/* When a new VdbeCursor is allocated, only the fields above are zeroed.
|
|
** The fields that follow are uninitialized, and must be individually
|
|
@@ -18565,10 +19934,9 @@
|
|
** initialized prior to first use. */
|
|
VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
|
|
union {
|
|
- BtCursor *pCursor; /* CURTYPE_BTREE. Btree cursor */
|
|
- sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */
|
|
- int pseudoTableReg; /* CURTYPE_PSEUDO. Reg holding content. */
|
|
- VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */
|
|
+ BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */
|
|
+ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */
|
|
+ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */
|
|
} uc;
|
|
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
|
|
u32 iHdrOffset; /* Offset to next unparsed byte of the header */
|
|
@@ -18629,6 +19997,9 @@
|
|
void *token; /* Copy of SubProgram.token */
|
|
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
|
|
AuxData *pAuxData; /* Linked list of auxdata allocations */
|
|
+#if SQLITE_DEBUG
|
|
+ u32 iFrameMagic; /* magic number for sanity checking */
|
|
+#endif
|
|
int nCursor; /* Number of entries in apCsr */
|
|
int pc; /* Program Counter in parent (calling) frame */
|
|
int nOp; /* Size of aOp array */
|
|
@@ -18639,6 +20010,13 @@
|
|
int nDbChange; /* Value of db->nChange */
|
|
};
|
|
|
|
+/* Magic number for sanity checking on VdbeFrame objects */
|
|
+#define SQLITE_FRAME_MAGIC 0x879fb71e
|
|
+
|
|
+/*
|
|
+** Return a pointer to the array of registers allocated for use
|
|
+** by a VdbeFrame.
|
|
+*/
|
|
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
|
|
|
|
/*
|
|
@@ -18653,8 +20031,6 @@
|
|
int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */
|
|
const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */
|
|
FuncDef *pDef; /* Used only when flags==MEM_Agg */
|
|
- RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
|
|
- VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
|
|
} u;
|
|
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
|
|
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
|
|
@@ -18669,7 +20045,7 @@
|
|
void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
|
|
#ifdef SQLITE_DEBUG
|
|
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
|
|
- void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
|
|
+ u16 mScopyFlags; /* flags value immediately after the shallow copy */
|
|
#endif
|
|
};
|
|
|
|
@@ -18698,8 +20074,8 @@
|
|
#define MEM_Real 0x0008 /* Value is a real number */
|
|
#define MEM_Blob 0x0010 /* Value is a BLOB */
|
|
#define MEM_AffMask 0x001f /* Mask of affinity bits */
|
|
-#define MEM_RowSet 0x0020 /* Value is a RowSet object */
|
|
-#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
|
|
+/* Available 0x0020 */
|
|
+/* Available 0x0040 */
|
|
#define MEM_Undefined 0x0080 /* Value is undefined */
|
|
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
|
|
#define MEM_TypeMask 0xc1ff /* Mask of type bits */
|
|
@@ -18726,7 +20102,7 @@
|
|
** that needs to be deallocated to avoid a leak.
|
|
*/
|
|
#define VdbeMemDynamic(X) \
|
|
- (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
|
|
+ (((X)->flags&(MEM_Agg|MEM_Dyn))!=0)
|
|
|
|
/*
|
|
** Clear any existing type flags from a Mem and replace them with f
|
|
@@ -18778,7 +20154,6 @@
|
|
int iOp; /* Instruction number of OP_Function */
|
|
int isError; /* Error code returned by the function. */
|
|
u8 skipFlag; /* Skip accumulator loading if true */
|
|
- u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
|
|
u8 argc; /* Number of arguments */
|
|
sqlite3_value *argv[1]; /* Argument set */
|
|
};
|
|
@@ -18841,14 +20216,15 @@
|
|
int nOp; /* Number of instructions in the program */
|
|
#ifdef SQLITE_DEBUG
|
|
int rcApp; /* errcode set by sqlite3_result_error_code() */
|
|
+ u32 nWrite; /* Number of write operations that have occurred */
|
|
#endif
|
|
u16 nResColumn; /* Number of columns in one row of the result set */
|
|
u8 errorAction; /* Recovery action to do in case of an error */
|
|
u8 minWriteFileFormat; /* Minimum file format for writable database files */
|
|
u8 prepFlags; /* SQLITE_PREPARE_* flags */
|
|
- bft expired:1; /* True if the VM needs to be recompiled */
|
|
+ bft expired:2; /* 1: recompile VM immediately 2: when convenient */
|
|
+ bft explain:2; /* True if EXPLAIN present on SQL command */
|
|
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
|
|
- bft explain:2; /* True if EXPLAIN present on SQL command */
|
|
bft changeCntOn:1; /* True to update the change-counter */
|
|
bft runOnlyOnce:1; /* Automatically expire on reset */
|
|
bft usesStmtJournal:1; /* True if uses a statement journal */
|
|
@@ -18858,6 +20234,9 @@
|
|
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
|
|
u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */
|
|
char *zSql; /* Text of the SQL statement that generated this */
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ char *zNormSql; /* Normalization of the associated SQL statement */
|
|
+#endif
|
|
void *pFree; /* Free this when deleting the vdbe */
|
|
VdbeFrame *pFrame; /* Parent frame */
|
|
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
|
|
@@ -18909,9 +20288,6 @@
|
|
void sqliteVdbePopStack(Vdbe*,int);
|
|
SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
|
|
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
|
|
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
|
-SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*);
|
|
-#endif
|
|
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
|
|
SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
|
|
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
|
|
@@ -18923,7 +20299,9 @@
|
|
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
|
|
SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
|
|
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
|
|
+#ifndef SQLITE_OMIT_EXPLAIN
|
|
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
|
|
+#endif
|
|
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
|
|
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*);
|
|
@@ -18942,12 +20320,16 @@
|
|
SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
|
|
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*);
|
|
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int);
|
|
-SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem*);
|
|
+#ifdef SQLITE_DEBUG
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*);
|
|
+#endif
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8);
|
|
SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*);
|
|
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*);
|
|
+SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem*, int ifNull);
|
|
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*);
|
|
@@ -18955,11 +20337,20 @@
|
|
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*);
|
|
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
|
|
+#endif
|
|
+#ifndef SQLITE_OMIT_EXPLAIN
|
|
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
|
|
+#endif
|
|
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
|
SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
|
|
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
|
|
-SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
|
|
+#ifdef SQLITE_DEBUG
|
|
+SQLITE_PRIVATE int sqlite3VdbeFrameIsValid(VdbeFrame*);
|
|
+#endif
|
|
+SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */
|
|
+SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */
|
|
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
|
|
@@ -18975,6 +20366,14 @@
|
|
SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
|
|
SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
+SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
|
|
+SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*);
|
|
+#else
|
|
+# define sqlite3VdbeIncrWriteCounter(V,C)
|
|
+# define sqlite3VdbeAssertAbortable(V)
|
|
+#endif
|
|
+
|
|
#if !defined(SQLITE_OMIT_SHARED_CACHE)
|
|
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
|
|
#else
|
|
@@ -19126,7 +20525,6 @@
|
|
: sqlite3MallocMutex()) );
|
|
assert( op==SQLITE_STATUS_MALLOC_SIZE
|
|
|| op==SQLITE_STATUS_PAGECACHE_SIZE
|
|
- || op==SQLITE_STATUS_SCRATCH_SIZE
|
|
|| op==SQLITE_STATUS_PARSER_STACK );
|
|
if( newValue>wsdStat.mxValue[op] ){
|
|
wsdStat.mxValue[op] = newValue;
|
|
@@ -19176,6 +20574,28 @@
|
|
}
|
|
|
|
/*
|
|
+** Return the number of LookasideSlot elements on the linked list
|
|
+*/
|
|
+static u32 countLookasideSlots(LookasideSlot *p){
|
|
+ u32 cnt = 0;
|
|
+ while( p ){
|
|
+ p = p->pNext;
|
|
+ cnt++;
|
|
+ }
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Count the number of slots of lookaside memory that are outstanding
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){
|
|
+ u32 nInit = countLookasideSlots(db->lookaside.pInit);
|
|
+ u32 nFree = countLookasideSlots(db->lookaside.pFree);
|
|
+ if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit;
|
|
+ return db->lookaside.nSlot - (nInit+nFree);
|
|
+}
|
|
+
|
|
+/*
|
|
** Query status information for a single database connection
|
|
*/
|
|
SQLITE_API int sqlite3_db_status(
|
|
@@ -19194,10 +20614,15 @@
|
|
sqlite3_mutex_enter(db->mutex);
|
|
switch( op ){
|
|
case SQLITE_DBSTATUS_LOOKASIDE_USED: {
|
|
- *pCurrent = db->lookaside.nOut;
|
|
- *pHighwater = db->lookaside.mxOut;
|
|
+ *pCurrent = sqlite3LookasideUsed(db, pHighwater);
|
|
if( resetFlag ){
|
|
- db->lookaside.mxOut = db->lookaside.nOut;
|
|
+ LookasideSlot *p = db->lookaside.pFree;
|
|
+ if( p ){
|
|
+ while( p->pNext ) p = p->pNext;
|
|
+ p->pNext = db->lookaside.pInit;
|
|
+ db->lookaside.pInit = db->lookaside.pFree;
|
|
+ db->lookaside.pFree = 0;
|
|
+ }
|
|
}
|
|
break;
|
|
}
|
|
@@ -19315,6 +20740,9 @@
|
|
** pagers the database handle is connected to. *pHighwater is always set
|
|
** to zero.
|
|
*/
|
|
+ case SQLITE_DBSTATUS_CACHE_SPILL:
|
|
+ op = SQLITE_DBSTATUS_CACHE_WRITE+1;
|
|
+ /* Fall through into the next case */
|
|
case SQLITE_DBSTATUS_CACHE_HIT:
|
|
case SQLITE_DBSTATUS_CACHE_MISS:
|
|
case SQLITE_DBSTATUS_CACHE_WRITE:{
|
|
@@ -19397,7 +20825,7 @@
|
|
**
|
|
** Jean Meeus
|
|
** Astronomical Algorithms, 2nd Edition, 1998
|
|
-** ISBM 0-943396-61-1
|
|
+** ISBN 0-943396-61-1
|
|
** Willmann-Bell, Inc
|
|
** Richmond, Virginia (USA)
|
|
*/
|
|
@@ -20708,7 +22136,7 @@
|
|
}
|
|
SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file *id, int flags){
|
|
DO_OS_MALLOC_TEST(id);
|
|
- return id->pMethods->xSync(id, flags);
|
|
+ return flags ? id->pMethods->xSync(id, flags) : SQLITE_OK;
|
|
}
|
|
SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){
|
|
DO_OS_MALLOC_TEST(id);
|
|
@@ -20735,8 +22163,11 @@
|
|
** routine has no return value since the return value would be meaningless.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
|
|
+ if( id->pMethods==0 ) return SQLITE_NOTFOUND;
|
|
#ifdef SQLITE_TEST
|
|
- if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
|
|
+ if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
|
|
+ && op!=SQLITE_FCNTL_LOCK_TIMEOUT
|
|
+ ){
|
|
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
|
|
** is using a regular VFS, it is called after the corresponding
|
|
** transaction has been committed. Injecting a fault at this point
|
|
@@ -20753,7 +22184,7 @@
|
|
return id->pMethods->xFileControl(id, op, pArg);
|
|
}
|
|
SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
|
|
- (void)id->pMethods->xFileControl(id, op, pArg);
|
|
+ if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg);
|
|
}
|
|
|
|
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
|
|
@@ -20763,6 +22194,7 @@
|
|
SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
|
|
return id->pMethods->xDeviceCharacteristics(id);
|
|
}
|
|
+#ifndef SQLITE_OMIT_WAL
|
|
SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){
|
|
return id->pMethods->xShmLock(id, offset, n, flags);
|
|
}
|
|
@@ -20782,6 +22214,7 @@
|
|
DO_OS_MALLOC_TEST(id);
|
|
return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp);
|
|
}
|
|
+#endif /* SQLITE_OMIT_WAL */
|
|
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
/* The real implementation of xFetch and xUnfetch */
|
|
@@ -21015,9 +22448,12 @@
|
|
** Unregister a VFS so that it is no longer accessible.
|
|
*/
|
|
SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
|
|
-#if SQLITE_THREADSAFE
|
|
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
|
+ MUTEX_LOGIC(sqlite3_mutex *mutex;)
|
|
+#ifndef SQLITE_OMIT_AUTOINIT
|
|
+ int rc = sqlite3_initialize();
|
|
+ if( rc ) return rc;
|
|
#endif
|
|
+ MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
|
sqlite3_mutex_enter(mutex);
|
|
vfsUnlink(pVfs);
|
|
sqlite3_mutex_leave(mutex);
|
|
@@ -23300,7 +24736,194 @@
|
|
|
|
|
|
#ifndef SQLITE_MUTEX_OMIT
|
|
+
|
|
+#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|
|
/*
|
|
+** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains
|
|
+** the implementation of a wrapper around the system default mutex
|
|
+** implementation (sqlite3DefaultMutex()).
|
|
+**
|
|
+** Most calls are passed directly through to the underlying default
|
|
+** mutex implementation. Except, if a mutex is configured by calling
|
|
+** sqlite3MutexWarnOnContention() on it, then if contention is ever
|
|
+** encountered within xMutexEnter() a warning is emitted via sqlite3_log().
|
|
+**
|
|
+** This type of mutex is used as the database handle mutex when testing
|
|
+** apps that usually use SQLITE_CONFIG_MULTITHREAD mode.
|
|
+*/
|
|
+
|
|
+/*
|
|
+** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS
|
|
+** is defined. Variable CheckMutex.mutex is a pointer to the real mutex
|
|
+** allocated by the system mutex implementation. Variable iType is usually set
|
|
+** to the type of mutex requested - SQLITE_MUTEX_RECURSIVE, SQLITE_MUTEX_FAST
|
|
+** or one of the static mutex identifiers. Or, if this is a recursive mutex
|
|
+** that has been configured using sqlite3MutexWarnOnContention(), it is
|
|
+** set to SQLITE_MUTEX_WARNONCONTENTION.
|
|
+*/
|
|
+typedef struct CheckMutex CheckMutex;
|
|
+struct CheckMutex {
|
|
+ int iType;
|
|
+ sqlite3_mutex *mutex;
|
|
+};
|
|
+
|
|
+#define SQLITE_MUTEX_WARNONCONTENTION (-1)
|
|
+
|
|
+/*
|
|
+** Pointer to real mutex methods object used by the CheckMutex
|
|
+** implementation. Set by checkMutexInit().
|
|
+*/
|
|
+static SQLITE_WSD const sqlite3_mutex_methods *pGlobalMutexMethods;
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+static int checkMutexHeld(sqlite3_mutex *p){
|
|
+ return pGlobalMutexMethods->xMutexHeld(((CheckMutex*)p)->mutex);
|
|
+}
|
|
+static int checkMutexNotheld(sqlite3_mutex *p){
|
|
+ return pGlobalMutexMethods->xMutexNotheld(((CheckMutex*)p)->mutex);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Initialize and deinitialize the mutex subsystem.
|
|
+*/
|
|
+static int checkMutexInit(void){
|
|
+ pGlobalMutexMethods = sqlite3DefaultMutex();
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+static int checkMutexEnd(void){
|
|
+ pGlobalMutexMethods = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate a mutex.
|
|
+*/
|
|
+static sqlite3_mutex *checkMutexAlloc(int iType){
|
|
+ static CheckMutex staticMutexes[] = {
|
|
+ {2, 0}, {3, 0}, {4, 0}, {5, 0},
|
|
+ {6, 0}, {7, 0}, {8, 0}, {9, 0},
|
|
+ {10, 0}, {11, 0}, {12, 0}, {13, 0}
|
|
+ };
|
|
+ CheckMutex *p = 0;
|
|
+
|
|
+ assert( SQLITE_MUTEX_RECURSIVE==1 && SQLITE_MUTEX_FAST==0 );
|
|
+ if( iType<2 ){
|
|
+ p = sqlite3MallocZero(sizeof(CheckMutex));
|
|
+ if( p==0 ) return 0;
|
|
+ p->iType = iType;
|
|
+ }else{
|
|
+#ifdef SQLITE_ENABLE_API_ARMOR
|
|
+ if( iType-2>=ArraySize(staticMutexes) ){
|
|
+ (void)SQLITE_MISUSE_BKPT;
|
|
+ return 0;
|
|
+ }
|
|
+#endif
|
|
+ p = &staticMutexes[iType-2];
|
|
+ }
|
|
+
|
|
+ if( p->mutex==0 ){
|
|
+ p->mutex = pGlobalMutexMethods->xMutexAlloc(iType);
|
|
+ if( p->mutex==0 ){
|
|
+ if( iType<2 ){
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+ p = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (sqlite3_mutex*)p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free a mutex.
|
|
+*/
|
|
+static void checkMutexFree(sqlite3_mutex *p){
|
|
+ assert( SQLITE_MUTEX_RECURSIVE<2 );
|
|
+ assert( SQLITE_MUTEX_FAST<2 );
|
|
+ assert( SQLITE_MUTEX_WARNONCONTENTION<2 );
|
|
+
|
|
+#if SQLITE_ENABLE_API_ARMOR
|
|
+ if( ((CheckMutex*)p)->iType<2 )
|
|
+#endif
|
|
+ {
|
|
+ CheckMutex *pCheck = (CheckMutex*)p;
|
|
+ pGlobalMutexMethods->xMutexFree(pCheck->mutex);
|
|
+ sqlite3_free(pCheck);
|
|
+ }
|
|
+#ifdef SQLITE_ENABLE_API_ARMOR
|
|
+ else{
|
|
+ (void)SQLITE_MISUSE_BKPT;
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** Enter the mutex.
|
|
+*/
|
|
+static void checkMutexEnter(sqlite3_mutex *p){
|
|
+ CheckMutex *pCheck = (CheckMutex*)p;
|
|
+ if( pCheck->iType==SQLITE_MUTEX_WARNONCONTENTION ){
|
|
+ if( SQLITE_OK==pGlobalMutexMethods->xMutexTry(pCheck->mutex) ){
|
|
+ return;
|
|
+ }
|
|
+ sqlite3_log(SQLITE_MISUSE,
|
|
+ "illegal multi-threaded access to database connection"
|
|
+ );
|
|
+ }
|
|
+ pGlobalMutexMethods->xMutexEnter(pCheck->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Enter the mutex (do not block).
|
|
+*/
|
|
+static int checkMutexTry(sqlite3_mutex *p){
|
|
+ CheckMutex *pCheck = (CheckMutex*)p;
|
|
+ return pGlobalMutexMethods->xMutexTry(pCheck->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Leave the mutex.
|
|
+*/
|
|
+static void checkMutexLeave(sqlite3_mutex *p){
|
|
+ CheckMutex *pCheck = (CheckMutex*)p;
|
|
+ pGlobalMutexMethods->xMutexLeave(pCheck->mutex);
|
|
+}
|
|
+
|
|
+sqlite3_mutex_methods const *multiThreadedCheckMutex(void){
|
|
+ static const sqlite3_mutex_methods sMutex = {
|
|
+ checkMutexInit,
|
|
+ checkMutexEnd,
|
|
+ checkMutexAlloc,
|
|
+ checkMutexFree,
|
|
+ checkMutexEnter,
|
|
+ checkMutexTry,
|
|
+ checkMutexLeave,
|
|
+#ifdef SQLITE_DEBUG
|
|
+ checkMutexHeld,
|
|
+ checkMutexNotheld
|
|
+#else
|
|
+ 0,
|
|
+ 0
|
|
+#endif
|
|
+ };
|
|
+ return &sMutex;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Mark the SQLITE_MUTEX_RECURSIVE mutex passed as the only argument as
|
|
+** one on which there should be no contention.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex *p){
|
|
+ if( sqlite3GlobalConfig.mutex.xMutexAlloc==checkMutexAlloc ){
|
|
+ CheckMutex *pCheck = (CheckMutex*)p;
|
|
+ assert( pCheck->iType==SQLITE_MUTEX_RECURSIVE );
|
|
+ pCheck->iType = SQLITE_MUTEX_WARNONCONTENTION;
|
|
+ }
|
|
+}
|
|
+#endif /* ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS */
|
|
+
|
|
+/*
|
|
** Initialize the mutex system.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3MutexInit(void){
|
|
@@ -23315,7 +24938,11 @@
|
|
sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex;
|
|
|
|
if( sqlite3GlobalConfig.bCoreMutex ){
|
|
+#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|
|
+ pFrom = multiThreadedCheckMutex();
|
|
+#else
|
|
pFrom = sqlite3DefaultMutex();
|
|
+#endif
|
|
}else{
|
|
pFrom = sqlite3NoopMutex();
|
|
}
|
|
@@ -23714,11 +25341,12 @@
|
|
#endif
|
|
};
|
|
#if SQLITE_MUTEX_NREF
|
|
-#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0}
|
|
+# define SQLITE3_MUTEX_INITIALIZER(id) \
|
|
+ {PTHREAD_MUTEX_INITIALIZER,id,0,(pthread_t)0,0}
|
|
#elif defined(SQLITE_ENABLE_API_ARMOR)
|
|
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 }
|
|
+# define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER, id }
|
|
#else
|
|
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
|
|
+#define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER }
|
|
#endif
|
|
|
|
/*
|
|
@@ -23815,18 +25443,18 @@
|
|
*/
|
|
static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
|
static sqlite3_mutex staticMutexes[] = {
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER
|
|
+ SQLITE3_MUTEX_INITIALIZER(2),
|
|
+ SQLITE3_MUTEX_INITIALIZER(3),
|
|
+ SQLITE3_MUTEX_INITIALIZER(4),
|
|
+ SQLITE3_MUTEX_INITIALIZER(5),
|
|
+ SQLITE3_MUTEX_INITIALIZER(6),
|
|
+ SQLITE3_MUTEX_INITIALIZER(7),
|
|
+ SQLITE3_MUTEX_INITIALIZER(8),
|
|
+ SQLITE3_MUTEX_INITIALIZER(9),
|
|
+ SQLITE3_MUTEX_INITIALIZER(10),
|
|
+ SQLITE3_MUTEX_INITIALIZER(11),
|
|
+ SQLITE3_MUTEX_INITIALIZER(12),
|
|
+ SQLITE3_MUTEX_INITIALIZER(13)
|
|
};
|
|
sqlite3_mutex *p;
|
|
switch( iType ){
|
|
@@ -23845,6 +25473,9 @@
|
|
pthread_mutex_init(&p->mutex, &recursiveAttr);
|
|
pthread_mutexattr_destroy(&recursiveAttr);
|
|
#endif
|
|
+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
|
+ p->id = SQLITE_MUTEX_RECURSIVE;
|
|
+#endif
|
|
}
|
|
break;
|
|
}
|
|
@@ -23852,6 +25483,9 @@
|
|
p = sqlite3MallocZero( sizeof(*p) );
|
|
if( p ){
|
|
pthread_mutex_init(&p->mutex, 0);
|
|
+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
|
+ p->id = SQLITE_MUTEX_FAST;
|
|
+#endif
|
|
}
|
|
break;
|
|
}
|
|
@@ -23867,7 +25501,7 @@
|
|
}
|
|
}
|
|
#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
|
- if( p ) p->id = iType;
|
|
+ assert( p==0 || p->id==iType );
|
|
#endif
|
|
return p;
|
|
}
|
|
@@ -24384,7 +26018,7 @@
|
|
#ifdef SQLITE_DEBUG
|
|
volatile int nRef; /* Number of enterances */
|
|
volatile DWORD owner; /* Thread holding this mutex */
|
|
- volatile int trace; /* True to trace changes */
|
|
+ volatile LONG trace; /* True to trace changes */
|
|
#endif
|
|
};
|
|
|
|
@@ -24396,10 +26030,10 @@
|
|
#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
|
|
+#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \
|
|
0L, (DWORD)0, 0 }
|
|
#else
|
|
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
|
|
+#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id }
|
|
#endif
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
@@ -24442,18 +26076,18 @@
|
|
** Initialize and deinitialize the mutex subsystem.
|
|
*/
|
|
static sqlite3_mutex winMutex_staticMutexes[] = {
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
- SQLITE3_MUTEX_INITIALIZER
|
|
+ SQLITE3_MUTEX_INITIALIZER(2),
|
|
+ SQLITE3_MUTEX_INITIALIZER(3),
|
|
+ SQLITE3_MUTEX_INITIALIZER(4),
|
|
+ SQLITE3_MUTEX_INITIALIZER(5),
|
|
+ SQLITE3_MUTEX_INITIALIZER(6),
|
|
+ SQLITE3_MUTEX_INITIALIZER(7),
|
|
+ SQLITE3_MUTEX_INITIALIZER(8),
|
|
+ SQLITE3_MUTEX_INITIALIZER(9),
|
|
+ SQLITE3_MUTEX_INITIALIZER(10),
|
|
+ SQLITE3_MUTEX_INITIALIZER(11),
|
|
+ SQLITE3_MUTEX_INITIALIZER(12),
|
|
+ SQLITE3_MUTEX_INITIALIZER(13)
|
|
};
|
|
|
|
static int winMutex_isInit = 0;
|
|
@@ -24583,15 +26217,15 @@
|
|
}
|
|
#endif
|
|
p = &winMutex_staticMutexes[iType-2];
|
|
- p->id = iType;
|
|
#ifdef SQLITE_DEBUG
|
|
#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
|
|
- p->trace = 1;
|
|
+ InterlockedCompareExchange(&p->trace, 1, 0);
|
|
#endif
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
+ assert( p==0 || p->id==iType );
|
|
return p;
|
|
}
|
|
|
|
@@ -24779,14 +26413,6 @@
|
|
}
|
|
|
|
/*
|
|
-** An instance of the following object records the location of
|
|
-** each unused scratch buffer.
|
|
-*/
|
|
-typedef struct ScratchFreeslot {
|
|
- struct ScratchFreeslot *pNext; /* Next unused scratch buffer */
|
|
-} ScratchFreeslot;
|
|
-
|
|
-/*
|
|
** State information local to the memory allocation subsystem.
|
|
*/
|
|
static SQLITE_WSD struct Mem0Global {
|
|
@@ -24794,21 +26420,11 @@
|
|
sqlite3_int64 alarmThreshold; /* The soft heap limit */
|
|
|
|
/*
|
|
- ** Pointers to the end of sqlite3GlobalConfig.pScratch memory
|
|
- ** (so that a range test can be used to determine if an allocation
|
|
- ** being freed came from pScratch) and a pointer to the list of
|
|
- ** unused scratch allocations.
|
|
- */
|
|
- void *pScratchEnd;
|
|
- ScratchFreeslot *pScratchFree;
|
|
- u32 nScratchFree;
|
|
-
|
|
- /*
|
|
** True if heap is nearly "full" where "full" is defined by the
|
|
** sqlite3_soft_heap_limit() setting.
|
|
*/
|
|
int nearlyFull;
|
|
-} mem0 = { 0, 0, 0, 0, 0, 0 };
|
|
+} mem0 = { 0, 0, 0 };
|
|
|
|
#define mem0 GLOBAL(struct Mem0Global, mem0)
|
|
|
|
@@ -24878,28 +26494,6 @@
|
|
}
|
|
memset(&mem0, 0, sizeof(mem0));
|
|
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
|
- if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
|
- && sqlite3GlobalConfig.nScratch>0 ){
|
|
- int i, n, sz;
|
|
- ScratchFreeslot *pSlot;
|
|
- sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch);
|
|
- sqlite3GlobalConfig.szScratch = sz;
|
|
- pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch;
|
|
- n = sqlite3GlobalConfig.nScratch;
|
|
- mem0.pScratchFree = pSlot;
|
|
- mem0.nScratchFree = n;
|
|
- for(i=0; i<n-1; i++){
|
|
- pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot);
|
|
- pSlot = pSlot->pNext;
|
|
- }
|
|
- pSlot->pNext = 0;
|
|
- mem0.pScratchEnd = (void*)&pSlot[1];
|
|
- }else{
|
|
- mem0.pScratchEnd = 0;
|
|
- sqlite3GlobalConfig.pScratch = 0;
|
|
- sqlite3GlobalConfig.szScratch = 0;
|
|
- sqlite3GlobalConfig.nScratch = 0;
|
|
- }
|
|
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
|
|
|| sqlite3GlobalConfig.nPage<=0 ){
|
|
sqlite3GlobalConfig.pPage = 0;
|
|
@@ -25051,105 +26645,6 @@
|
|
}
|
|
|
|
/*
|
|
-** Each thread may only have a single outstanding allocation from
|
|
-** xScratchMalloc(). We verify this constraint in the single-threaded
|
|
-** case by setting scratchAllocOut to 1 when an allocation
|
|
-** is outstanding clearing it when the allocation is freed.
|
|
-*/
|
|
-#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
-static int scratchAllocOut = 0;
|
|
-#endif
|
|
-
|
|
-
|
|
-/*
|
|
-** Allocate memory that is to be used and released right away.
|
|
-** This routine is similar to alloca() in that it is not intended
|
|
-** for situations where the memory might be held long-term. This
|
|
-** routine is intended to get memory to old large transient data
|
|
-** structures that would not normally fit on the stack of an
|
|
-** embedded processor.
|
|
-*/
|
|
-SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
|
|
- void *p;
|
|
- assert( n>0 );
|
|
-
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
- sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n);
|
|
- if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
|
|
- p = mem0.pScratchFree;
|
|
- mem0.pScratchFree = mem0.pScratchFree->pNext;
|
|
- mem0.nScratchFree--;
|
|
- sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1);
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
- }else{
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
- p = sqlite3Malloc(n);
|
|
- if( sqlite3GlobalConfig.bMemstat && p ){
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
- sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p));
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
- }
|
|
- sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
|
- }
|
|
- assert( sqlite3_mutex_notheld(mem0.mutex) );
|
|
-
|
|
-
|
|
-#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
- /* EVIDENCE-OF: R-12970-05880 SQLite will not use more than one scratch
|
|
- ** buffers per thread.
|
|
- **
|
|
- ** This can only be checked in single-threaded mode.
|
|
- */
|
|
- assert( scratchAllocOut==0 );
|
|
- if( p ) scratchAllocOut++;
|
|
-#endif
|
|
-
|
|
- return p;
|
|
-}
|
|
-SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
|
|
- if( p ){
|
|
-
|
|
-#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
- /* Verify that no more than two scratch allocation per thread
|
|
- ** is outstanding at one time. (This is only checked in the
|
|
- ** single-threaded case since checking in the multi-threaded case
|
|
- ** would be much more complicated.) */
|
|
- assert( scratchAllocOut>=1 && scratchAllocOut<=2 );
|
|
- scratchAllocOut--;
|
|
-#endif
|
|
-
|
|
- if( SQLITE_WITHIN(p, sqlite3GlobalConfig.pScratch, mem0.pScratchEnd) ){
|
|
- /* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
|
|
- ScratchFreeslot *pSlot;
|
|
- pSlot = (ScratchFreeslot*)p;
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
- pSlot->pNext = mem0.pScratchFree;
|
|
- mem0.pScratchFree = pSlot;
|
|
- mem0.nScratchFree++;
|
|
- assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch );
|
|
- sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1);
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
- }else{
|
|
- /* Release memory back to the heap */
|
|
- assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
|
|
- assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) );
|
|
- sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
- if( sqlite3GlobalConfig.bMemstat ){
|
|
- int iSize = sqlite3MallocSize(p);
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
- sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize);
|
|
- sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize);
|
|
- sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
- }else{
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
** TRUE if p is a lookaside memory allocation from db
|
|
*/
|
|
#ifndef SQLITE_OMIT_LOOKASIDE
|
|
@@ -25239,7 +26734,6 @@
|
|
#endif
|
|
pBuf->pNext = db->lookaside.pFree;
|
|
db->lookaside.pFree = pBuf;
|
|
- db->lookaside.nOut--;
|
|
return;
|
|
}
|
|
}
|
|
@@ -25400,16 +26894,16 @@
|
|
assert( db->mallocFailed==0 );
|
|
if( n>db->lookaside.sz ){
|
|
db->lookaside.anStat[1]++;
|
|
- }else if( (pBuf = db->lookaside.pFree)==0 ){
|
|
- db->lookaside.anStat[2]++;
|
|
- }else{
|
|
+ }else if( (pBuf = db->lookaside.pFree)!=0 ){
|
|
db->lookaside.pFree = pBuf->pNext;
|
|
- db->lookaside.nOut++;
|
|
db->lookaside.anStat[0]++;
|
|
- if( db->lookaside.nOut>db->lookaside.mxOut ){
|
|
- db->lookaside.mxOut = db->lookaside.nOut;
|
|
- }
|
|
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 if( db->mallocFailed ){
|
|
return 0;
|
|
@@ -25514,6 +27008,19 @@
|
|
}
|
|
|
|
/*
|
|
+** The text between zStart and zEnd represents a phrase within a larger
|
|
+** SQL statement. Make a copy of this phrase in space obtained form
|
|
+** sqlite3DbMalloc(). Omit leading and trailing whitespace.
|
|
+*/
|
|
+SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
|
+ int n;
|
|
+ while( sqlite3Isspace(zStart[0]) ) zStart++;
|
|
+ n = (int)(zEnd - zStart);
|
|
+ while( ALWAYS(n>0) && sqlite3Isspace(zStart[n-1]) ) n--;
|
|
+ return sqlite3DbStrNDup(db, zStart, n);
|
|
+}
|
|
+
|
|
+/*
|
|
** Free any prior content in *pz and replace it with a copy of zNew.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
|
|
@@ -25725,7 +27232,7 @@
|
|
** Set the StrAccum object to an error mode.
|
|
*/
|
|
static void setStrAccumError(StrAccum *p, u8 eError){
|
|
- assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG );
|
|
+ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
|
|
p->accError = eError;
|
|
p->nAlloc = 0;
|
|
}
|
|
@@ -25759,8 +27266,8 @@
|
|
/*
|
|
** Render a string given by "fmt" into the StrAccum object.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3VXPrintf(
|
|
- StrAccum *pAccum, /* Accumulate results here */
|
|
+SQLITE_API void sqlite3_str_vappendf(
|
|
+ sqlite3_str *pAccum, /* Accumulate results here */
|
|
const char *fmt, /* Format string */
|
|
va_list ap /* arguments */
|
|
){
|
|
@@ -25797,6 +27304,11 @@
|
|
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
|
char buf[etBUFSIZE]; /* Conversion buffer */
|
|
|
|
+ /* pAccum never starts out with an empty buffer that was obtained from
|
|
+ ** malloc(). This precondition is required by the mprintf("%z...")
|
|
+ ** optimization. */
|
|
+ assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
|
|
+
|
|
bufpt = 0;
|
|
if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){
|
|
pArgList = va_arg(ap, PrintfArguments*);
|
|
@@ -25812,11 +27324,11 @@
|
|
#else
|
|
do{ fmt++; }while( *fmt && *fmt != '%' );
|
|
#endif
|
|
- sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
|
|
+ sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
|
|
if( *fmt==0 ) break;
|
|
}
|
|
if( (c=(*++fmt))==0 ){
|
|
- sqlite3StrAccumAppend(pAccum, "%", 1);
|
|
+ sqlite3_str_append(pAccum, "%", 1);
|
|
break;
|
|
}
|
|
/* Find out what flags are present */
|
|
@@ -25994,7 +27506,7 @@
|
|
u64 n = (u64)precision + 10 + precision/3;
|
|
zOut = zExtra = sqlite3Malloc( n );
|
|
if( zOut==0 ){
|
|
- setStrAccumError(pAccum, STRACCUM_NOMEM);
|
|
+ setStrAccumError(pAccum, SQLITE_NOMEM);
|
|
return;
|
|
}
|
|
nOut = (int)n;
|
|
@@ -26119,7 +27631,7 @@
|
|
bufpt = zExtra
|
|
= sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
|
|
if( bufpt==0 ){
|
|
- setStrAccumError(pAccum, STRACCUM_NOMEM);
|
|
+ setStrAccumError(pAccum, SQLITE_NOMEM);
|
|
return;
|
|
}
|
|
}
|
|
@@ -26215,22 +27727,52 @@
|
|
case etCHARX:
|
|
if( bArgList ){
|
|
bufpt = getTextArg(pArgList);
|
|
- c = bufpt ? bufpt[0] : 0;
|
|
+ length = 1;
|
|
+ if( bufpt ){
|
|
+ buf[0] = c = *(bufpt++);
|
|
+ if( (c&0xc0)==0xc0 ){
|
|
+ while( length<4 && (bufpt[0]&0xc0)==0x80 ){
|
|
+ buf[length++] = *(bufpt++);
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ buf[0] = 0;
|
|
+ }
|
|
}else{
|
|
- c = va_arg(ap,int);
|
|
+ unsigned int ch = va_arg(ap,unsigned int);
|
|
+ if( ch<0x00080 ){
|
|
+ buf[0] = ch & 0xff;
|
|
+ length = 1;
|
|
+ }else if( ch<0x00800 ){
|
|
+ buf[0] = 0xc0 + (u8)((ch>>6)&0x1f);
|
|
+ buf[1] = 0x80 + (u8)(ch & 0x3f);
|
|
+ length = 2;
|
|
+ }else if( ch<0x10000 ){
|
|
+ buf[0] = 0xe0 + (u8)((ch>>12)&0x0f);
|
|
+ buf[1] = 0x80 + (u8)((ch>>6) & 0x3f);
|
|
+ buf[2] = 0x80 + (u8)(ch & 0x3f);
|
|
+ length = 3;
|
|
+ }else{
|
|
+ buf[0] = 0xf0 + (u8)((ch>>18) & 0x07);
|
|
+ buf[1] = 0x80 + (u8)((ch>>12) & 0x3f);
|
|
+ buf[2] = 0x80 + (u8)((ch>>6) & 0x3f);
|
|
+ buf[3] = 0x80 + (u8)(ch & 0x3f);
|
|
+ length = 4;
|
|
+ }
|
|
}
|
|
if( precision>1 ){
|
|
width -= precision-1;
|
|
if( width>1 && !flag_leftjustify ){
|
|
- sqlite3AppendChar(pAccum, width-1, ' ');
|
|
+ sqlite3_str_appendchar(pAccum, width-1, ' ');
|
|
width = 0;
|
|
}
|
|
- sqlite3AppendChar(pAccum, precision-1, c);
|
|
+ while( precision-- > 1 ){
|
|
+ sqlite3_str_append(pAccum, buf, length);
|
|
+ }
|
|
}
|
|
- length = 1;
|
|
- buf[0] = c;
|
|
bufpt = buf;
|
|
- break;
|
|
+ flag_altform2 = 1;
|
|
+ goto adjust_width_for_utf8;
|
|
case etSTRING:
|
|
case etDYNSTRING:
|
|
if( bArgList ){
|
|
@@ -26242,17 +27784,50 @@
|
|
if( bufpt==0 ){
|
|
bufpt = "";
|
|
}else if( xtype==etDYNSTRING ){
|
|
+ if( pAccum->nChar==0
|
|
+ && pAccum->mxAlloc
|
|
+ && width==0
|
|
+ && precision<0
|
|
+ && pAccum->accError==0
|
|
+ ){
|
|
+ /* Special optimization for sqlite3_mprintf("%z..."):
|
|
+ ** Extend an existing memory allocation rather than creating
|
|
+ ** a new one. */
|
|
+ assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
|
|
+ pAccum->zText = bufpt;
|
|
+ pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt);
|
|
+ pAccum->nChar = 0x7fffffff & (int)strlen(bufpt);
|
|
+ pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
|
+ length = 0;
|
|
+ break;
|
|
+ }
|
|
zExtra = bufpt;
|
|
}
|
|
if( precision>=0 ){
|
|
- for(length=0; length<precision && bufpt[length]; length++){}
|
|
+ if( flag_altform2 ){
|
|
+ /* Set length to the number of bytes needed in order to display
|
|
+ ** precision characters */
|
|
+ unsigned char *z = (unsigned char*)bufpt;
|
|
+ while( precision-- > 0 && z[0] ){
|
|
+ SQLITE_SKIP_UTF8(z);
|
|
+ }
|
|
+ length = (int)(z - (unsigned char*)bufpt);
|
|
+ }else{
|
|
+ for(length=0; length<precision && bufpt[length]; length++){}
|
|
+ }
|
|
}else{
|
|
- length = sqlite3Strlen30(bufpt);
|
|
+ length = 0x7fffffff & (int)strlen(bufpt);
|
|
}
|
|
+ adjust_width_for_utf8:
|
|
+ if( flag_altform2 && width>0 ){
|
|
+ /* Adjust width to account for extra bytes in UTF-8 characters */
|
|
+ int ii = length - 1;
|
|
+ while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
|
|
+ }
|
|
break;
|
|
- case etSQLESCAPE: /* Escape ' characters */
|
|
- case etSQLESCAPE2: /* Escape ' and enclose in '...' */
|
|
- case etSQLESCAPE3: { /* Escape " characters */
|
|
+ case etSQLESCAPE: /* %q: Escape ' characters */
|
|
+ case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */
|
|
+ case etSQLESCAPE3: { /* %w: Escape " characters */
|
|
int i, j, k, n, isnull;
|
|
int needQuote;
|
|
char ch;
|
|
@@ -26266,9 +27841,17 @@
|
|
}
|
|
isnull = escarg==0;
|
|
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
|
|
+ /* For %q, %Q, and %w, the precision is the number of byte (or
|
|
+ ** characters if the ! flags is present) to use from the input.
|
|
+ ** Because of the extra quoting characters inserted, the number
|
|
+ ** of output characters may be larger than the precision.
|
|
+ */
|
|
k = precision;
|
|
for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){
|
|
if( ch==q ) n++;
|
|
+ if( flag_altform2 && (ch&0xc0)==0xc0 ){
|
|
+ while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
|
|
+ }
|
|
}
|
|
needQuote = !isnull && xtype==etSQLESCAPE2;
|
|
n += i + 3;
|
|
@@ -26275,7 +27858,7 @@
|
|
if( n>etBUFSIZE ){
|
|
bufpt = zExtra = sqlite3Malloc( n );
|
|
if( bufpt==0 ){
|
|
- setStrAccumError(pAccum, STRACCUM_NOMEM);
|
|
+ setStrAccumError(pAccum, SQLITE_NOMEM);
|
|
return;
|
|
}
|
|
}else{
|
|
@@ -26291,10 +27874,7 @@
|
|
if( needQuote ) bufpt[j++] = q;
|
|
bufpt[j] = 0;
|
|
length = j;
|
|
- /* The precision in %q and %Q means how many input characters to
|
|
- ** consume, not the length of the output...
|
|
- ** if( precision>=0 && precision<length ) length = precision; */
|
|
- break;
|
|
+ goto adjust_width_for_utf8;
|
|
}
|
|
case etTOKEN: {
|
|
Token *pToken;
|
|
@@ -26302,7 +27882,7 @@
|
|
pToken = va_arg(ap, Token*);
|
|
assert( bArgList==0 );
|
|
if( pToken && pToken->n ){
|
|
- sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
|
+ sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
|
|
}
|
|
length = width = 0;
|
|
break;
|
|
@@ -26318,10 +27898,10 @@
|
|
assert( bArgList==0 );
|
|
assert( k>=0 && k<pSrc->nSrc );
|
|
if( pItem->zDatabase ){
|
|
- sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
|
|
- sqlite3StrAccumAppend(pAccum, ".", 1);
|
|
+ sqlite3_str_appendall(pAccum, pItem->zDatabase);
|
|
+ sqlite3_str_append(pAccum, ".", 1);
|
|
}
|
|
- sqlite3StrAccumAppendAll(pAccum, pItem->zName);
|
|
+ sqlite3_str_appendall(pAccum, pItem->zName);
|
|
length = width = 0;
|
|
break;
|
|
}
|
|
@@ -26333,15 +27913,18 @@
|
|
/*
|
|
** The text of the conversion is pointed to by "bufpt" and is
|
|
** "length" characters long. The field width is "width". Do
|
|
- ** the output.
|
|
+ ** the output. Both length and width are in bytes, not characters,
|
|
+ ** at this point. If the "!" flag was present on string conversions
|
|
+ ** indicating that width and precision should be expressed in characters,
|
|
+ ** then the values have been translated prior to reaching this point.
|
|
*/
|
|
width -= length;
|
|
if( width>0 ){
|
|
- if( !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
|
- sqlite3StrAccumAppend(pAccum, bufpt, length);
|
|
- if( flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
|
+ if( !flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
|
|
+ sqlite3_str_append(pAccum, bufpt, length);
|
|
+ if( flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
|
|
}else{
|
|
- sqlite3StrAccumAppend(pAccum, bufpt, length);
|
|
+ sqlite3_str_append(pAccum, bufpt, length);
|
|
}
|
|
|
|
if( zExtra ){
|
|
@@ -26362,18 +27945,17 @@
|
|
char *zNew;
|
|
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
|
|
if( p->accError ){
|
|
- testcase(p->accError==STRACCUM_TOOBIG);
|
|
- testcase(p->accError==STRACCUM_NOMEM);
|
|
+ testcase(p->accError==SQLITE_TOOBIG);
|
|
+ testcase(p->accError==SQLITE_NOMEM);
|
|
return 0;
|
|
}
|
|
if( p->mxAlloc==0 ){
|
|
N = p->nAlloc - p->nChar - 1;
|
|
- setStrAccumError(p, STRACCUM_TOOBIG);
|
|
+ setStrAccumError(p, SQLITE_TOOBIG);
|
|
return N;
|
|
}else{
|
|
char *zOld = isMalloced(p) ? p->zText : 0;
|
|
i64 szNew = p->nChar;
|
|
- assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
|
szNew += N + 1;
|
|
if( szNew+p->nChar<=p->mxAlloc ){
|
|
/* Force exponential buffer size growth as long as it does not overflow,
|
|
@@ -26381,8 +27963,8 @@
|
|
szNew += p->nChar;
|
|
}
|
|
if( szNew > p->mxAlloc ){
|
|
- sqlite3StrAccumReset(p);
|
|
- setStrAccumError(p, STRACCUM_TOOBIG);
|
|
+ sqlite3_str_reset(p);
|
|
+ setStrAccumError(p, SQLITE_TOOBIG);
|
|
return 0;
|
|
}else{
|
|
p->nAlloc = (int)szNew;
|
|
@@ -26399,8 +27981,8 @@
|
|
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
|
|
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
|
}else{
|
|
- sqlite3StrAccumReset(p);
|
|
- setStrAccumError(p, STRACCUM_NOMEM);
|
|
+ sqlite3_str_reset(p);
|
|
+ setStrAccumError(p, SQLITE_NOMEM);
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -26410,12 +27992,11 @@
|
|
/*
|
|
** Append N copies of character c to the given string buffer.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){
|
|
+SQLITE_API void sqlite3_str_appendchar(sqlite3_str *p, int N, char c){
|
|
testcase( p->nChar + (i64)N > 0x7fffffff );
|
|
if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
|
|
return;
|
|
}
|
|
- assert( (p->zText==p->zBase)==!isMalloced(p) );
|
|
while( (N--)>0 ) p->zText[p->nChar++] = c;
|
|
}
|
|
|
|
@@ -26423,9 +28004,9 @@
|
|
** The StrAccum "p" is not large enough to accept N new bytes of z[].
|
|
** So enlarge if first, then do the append.
|
|
**
|
|
-** This is a helper routine to sqlite3StrAccumAppend() that does special-case
|
|
+** This is a helper routine to sqlite3_str_append() that does special-case
|
|
** work (enlarging the buffer) using tail recursion, so that the
|
|
-** sqlite3StrAccumAppend() routine can use fast calling semantics.
|
|
+** sqlite3_str_append() routine can use fast calling semantics.
|
|
*/
|
|
static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
|
N = sqlite3StrAccumEnlarge(p, N);
|
|
@@ -26433,7 +28014,6 @@
|
|
memcpy(&p->zText[p->nChar], z, N);
|
|
p->nChar += N;
|
|
}
|
|
- assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
|
}
|
|
|
|
/*
|
|
@@ -26440,7 +28020,7 @@
|
|
** Append N bytes of text from z to the StrAccum object. Increase the
|
|
** size of the memory allocation for StrAccum if necessary.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
|
+SQLITE_API void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
|
|
assert( z!=0 || N==0 );
|
|
assert( p->zText!=0 || p->nChar==0 || p->accError );
|
|
assert( N>=0 );
|
|
@@ -26457,8 +28037,8 @@
|
|
/*
|
|
** Append the complete text of zero-terminated string z[] to the p string.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
|
|
- sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
|
|
+SQLITE_API void sqlite3_str_appendall(sqlite3_str *p, const char *z){
|
|
+ sqlite3_str_append(p, z, sqlite3Strlen30(z));
|
|
}
|
|
|
|
|
|
@@ -26468,19 +28048,20 @@
|
|
** pointer if any kind of error was encountered.
|
|
*/
|
|
static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
|
|
+ char *zText;
|
|
assert( p->mxAlloc>0 && !isMalloced(p) );
|
|
- p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
|
- if( p->zText ){
|
|
- memcpy(p->zText, p->zBase, p->nChar+1);
|
|
+ zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
|
+ if( zText ){
|
|
+ memcpy(zText, p->zText, p->nChar+1);
|
|
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
|
}else{
|
|
- setStrAccumError(p, STRACCUM_NOMEM);
|
|
+ setStrAccumError(p, SQLITE_NOMEM);
|
|
}
|
|
- return p->zText;
|
|
+ p->zText = zText;
|
|
+ return zText;
|
|
}
|
|
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
|
|
if( p->zText ){
|
|
- assert( (p->zText==p->zBase)==!isMalloced(p) );
|
|
p->zText[p->nChar] = 0;
|
|
if( p->mxAlloc>0 && !isMalloced(p) ){
|
|
return strAccumFinishRealloc(p);
|
|
@@ -26490,14 +28071,55 @@
|
|
}
|
|
|
|
/*
|
|
+** This singleton is an sqlite3_str object that is returned if
|
|
+** sqlite3_malloc() fails to provide space for a real one. This
|
|
+** sqlite3_str object accepts no new text and always returns
|
|
+** an SQLITE_NOMEM error.
|
|
+*/
|
|
+static sqlite3_str sqlite3OomStr = {
|
|
+ 0, 0, 0, 0, 0, SQLITE_NOMEM, 0
|
|
+};
|
|
+
|
|
+/* Finalize a string created using sqlite3_str_new().
|
|
+*/
|
|
+SQLITE_API char *sqlite3_str_finish(sqlite3_str *p){
|
|
+ char *z;
|
|
+ if( p!=0 && p!=&sqlite3OomStr ){
|
|
+ z = sqlite3StrAccumFinish(p);
|
|
+ sqlite3_free(p);
|
|
+ }else{
|
|
+ z = 0;
|
|
+ }
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/* Return any error code associated with p */
|
|
+SQLITE_API int sqlite3_str_errcode(sqlite3_str *p){
|
|
+ return p ? p->accError : SQLITE_NOMEM;
|
|
+}
|
|
+
|
|
+/* Return the current length of p in bytes */
|
|
+SQLITE_API int sqlite3_str_length(sqlite3_str *p){
|
|
+ return p ? p->nChar : 0;
|
|
+}
|
|
+
|
|
+/* Return the current value for p */
|
|
+SQLITE_API char *sqlite3_str_value(sqlite3_str *p){
|
|
+ if( p==0 || p->nChar==0 ) return 0;
|
|
+ p->zText[p->nChar] = 0;
|
|
+ return p->zText;
|
|
+}
|
|
+
|
|
+/*
|
|
** Reset an StrAccum string. Reclaim all malloced memory.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){
|
|
- assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
|
+SQLITE_API void sqlite3_str_reset(StrAccum *p){
|
|
if( isMalloced(p) ){
|
|
sqlite3DbFree(p->db, p->zText);
|
|
p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
|
|
}
|
|
+ p->nAlloc = 0;
|
|
+ p->nChar = 0;
|
|
p->zText = 0;
|
|
}
|
|
|
|
@@ -26516,15 +28138,27 @@
|
|
** allocations will ever occur.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
|
|
- p->zText = p->zBase = zBase;
|
|
+ p->zText = zBase;
|
|
p->db = db;
|
|
- p->nChar = 0;
|
|
p->nAlloc = n;
|
|
p->mxAlloc = mx;
|
|
+ p->nChar = 0;
|
|
p->accError = 0;
|
|
p->printfFlags = 0;
|
|
}
|
|
|
|
+/* Allocate and initialize a new dynamic string object */
|
|
+SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3 *db){
|
|
+ sqlite3_str *p = sqlite3_malloc64(sizeof(*p));
|
|
+ if( p ){
|
|
+ sqlite3StrAccumInit(p, 0, 0, 0,
|
|
+ db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH);
|
|
+ }else{
|
|
+ p = &sqlite3OomStr;
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
/*
|
|
** Print into memory obtained from sqliteMalloc(). Use the internal
|
|
** %-conversion extensions.
|
|
@@ -26537,9 +28171,9 @@
|
|
sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
|
|
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
|
acc.printfFlags = SQLITE_PRINTF_INTERNAL;
|
|
- sqlite3VXPrintf(&acc, zFormat, ap);
|
|
+ sqlite3_str_vappendf(&acc, zFormat, ap);
|
|
z = sqlite3StrAccumFinish(&acc);
|
|
- if( acc.accError==STRACCUM_NOMEM ){
|
|
+ if( acc.accError==SQLITE_NOMEM ){
|
|
sqlite3OomFault(db);
|
|
}
|
|
return z;
|
|
@@ -26577,7 +28211,7 @@
|
|
if( sqlite3_initialize() ) return 0;
|
|
#endif
|
|
sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
|
|
- sqlite3VXPrintf(&acc, zFormat, ap);
|
|
+ sqlite3_str_vappendf(&acc, zFormat, ap);
|
|
z = sqlite3StrAccumFinish(&acc);
|
|
return z;
|
|
}
|
|
@@ -26622,7 +28256,7 @@
|
|
}
|
|
#endif
|
|
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
|
|
- sqlite3VXPrintf(&acc, zFormat, ap);
|
|
+ sqlite3_str_vappendf(&acc, zFormat, ap);
|
|
zBuf[acc.nChar] = 0;
|
|
return zBuf;
|
|
}
|
|
@@ -26644,7 +28278,7 @@
|
|
** allocate memory because it might be called while the memory allocator
|
|
** mutex is held.
|
|
**
|
|
-** sqlite3VXPrintf() might ask for *temporary* memory allocations for
|
|
+** sqlite3_str_vappendf() might ask for *temporary* memory allocations for
|
|
** certain format characters (%q) or for very large precisions or widths.
|
|
** Care must be taken that any sqlite3_log() calls that occur while the
|
|
** memory mutex is held do not use these mechanisms.
|
|
@@ -26654,7 +28288,7 @@
|
|
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
|
|
|
|
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
|
|
- sqlite3VXPrintf(&acc, zFormat, ap);
|
|
+ sqlite3_str_vappendf(&acc, zFormat, ap);
|
|
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
|
|
sqlite3StrAccumFinish(&acc));
|
|
}
|
|
@@ -26683,23 +28317,30 @@
|
|
char zBuf[500];
|
|
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
|
va_start(ap,zFormat);
|
|
- sqlite3VXPrintf(&acc, zFormat, ap);
|
|
+ sqlite3_str_vappendf(&acc, zFormat, ap);
|
|
va_end(ap);
|
|
sqlite3StrAccumFinish(&acc);
|
|
+#ifdef SQLITE_OS_TRACE_PROC
|
|
+ {
|
|
+ extern void SQLITE_OS_TRACE_PROC(const char *zBuf, int nBuf);
|
|
+ SQLITE_OS_TRACE_PROC(zBuf, sizeof(zBuf));
|
|
+ }
|
|
+#else
|
|
fprintf(stdout,"%s", zBuf);
|
|
fflush(stdout);
|
|
+#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
-** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
|
|
+** variable-argument wrapper around sqlite3_str_vappendf(). The bFlags argument
|
|
** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
|
+SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
|
|
va_list ap;
|
|
va_start(ap,zFormat);
|
|
- sqlite3VXPrintf(p, zFormat, ap);
|
|
+ sqlite3_str_vappendf(p, zFormat, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
@@ -26765,15 +28406,17 @@
|
|
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
|
if( p ){
|
|
for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
|
|
- sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
|
|
+ sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4);
|
|
}
|
|
- sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
|
+ sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
|
}
|
|
- va_start(ap, zFormat);
|
|
- sqlite3VXPrintf(&acc, zFormat, ap);
|
|
- va_end(ap);
|
|
- assert( acc.nChar>0 );
|
|
- if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
|
|
+ if( zFormat!=0 ){
|
|
+ va_start(ap, zFormat);
|
|
+ sqlite3_str_vappendf(&acc, zFormat, ap);
|
|
+ va_end(ap);
|
|
+ assert( acc.nChar>0 );
|
|
+ sqlite3_str_append(&acc, "\n", 1);
|
|
+ }
|
|
sqlite3StrAccumFinish(&acc);
|
|
fprintf(stdout,"%s", zBuf);
|
|
fflush(stdout);
|
|
@@ -26806,17 +28449,17 @@
|
|
char zLine[1000];
|
|
const struct Cte *pCte = &pWith->a[i];
|
|
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
|
- sqlite3XPrintf(&x, "%s", pCte->zName);
|
|
+ sqlite3_str_appendf(&x, "%s", pCte->zName);
|
|
if( pCte->pCols && pCte->pCols->nExpr>0 ){
|
|
char cSep = '(';
|
|
int j;
|
|
for(j=0; j<pCte->pCols->nExpr; j++){
|
|
- sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
|
+ sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
|
cSep = ',';
|
|
}
|
|
- sqlite3XPrintf(&x, ")");
|
|
+ sqlite3_str_appendf(&x, ")");
|
|
}
|
|
- sqlite3XPrintf(&x, " AS");
|
|
+ sqlite3_str_appendf(&x, " AS");
|
|
sqlite3StrAccumFinish(&x);
|
|
sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
|
|
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
|
|
@@ -26826,6 +28469,42 @@
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+** Generate a human-readable description of a SrcList object.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
|
|
+ int i;
|
|
+ for(i=0; i<pSrc->nSrc; i++){
|
|
+ const struct SrcList_item *pItem = &pSrc->a[i];
|
|
+ StrAccum x;
|
|
+ char zLine[100];
|
|
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
|
+ sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor);
|
|
+ if( pItem->zDatabase ){
|
|
+ sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
|
+ }else if( pItem->zName ){
|
|
+ sqlite3_str_appendf(&x, " %s", pItem->zName);
|
|
+ }
|
|
+ if( pItem->pTab ){
|
|
+ sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
|
|
+ }
|
|
+ if( pItem->zAlias ){
|
|
+ sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
|
|
+ }
|
|
+ if( pItem->fg.jointype & JT_LEFT ){
|
|
+ sqlite3_str_appendf(&x, " LEFT-JOIN");
|
|
+ }
|
|
+ sqlite3StrAccumFinish(&x);
|
|
+ sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
|
|
+ if( pItem->pSelect ){
|
|
+ sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
|
|
+ }
|
|
+ if( pItem->fg.isTabFunc ){
|
|
+ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
|
|
+ }
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ }
|
|
+}
|
|
|
|
/*
|
|
** Generate a human-readable description of a Select object.
|
|
@@ -26844,9 +28523,11 @@
|
|
sqlite3TreeViewPush(pView, 1);
|
|
}
|
|
do{
|
|
- sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d",
|
|
+ sqlite3TreeViewLine(pView,
|
|
+ "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
|
|
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
|
|
- ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags,
|
|
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
|
|
+ p->selId, p, p->selFlags,
|
|
(int)p->nSelectRow
|
|
);
|
|
if( cnt++ ) sqlite3TreeViewPop(pView);
|
|
@@ -26860,43 +28541,27 @@
|
|
if( p->pHaving ) n++;
|
|
if( p->pOrderBy ) n++;
|
|
if( p->pLimit ) n++;
|
|
- if( p->pOffset ) n++;
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( p->pWin ) n++;
|
|
+ if( p->pWinDefn ) n++;
|
|
+#endif
|
|
}
|
|
sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( p->pWin ){
|
|
+ Window *pX;
|
|
+ pView = sqlite3TreeViewPush(pView, (n--)>0);
|
|
+ sqlite3TreeViewLine(pView, "window-functions");
|
|
+ for(pX=p->pWin; pX; pX=pX->pNextWin){
|
|
+ sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0);
|
|
+ }
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ }
|
|
+#endif
|
|
if( p->pSrc && p->pSrc->nSrc ){
|
|
- int i;
|
|
pView = sqlite3TreeViewPush(pView, (n--)>0);
|
|
sqlite3TreeViewLine(pView, "FROM");
|
|
- for(i=0; i<p->pSrc->nSrc; i++){
|
|
- struct SrcList_item *pItem = &p->pSrc->a[i];
|
|
- StrAccum x;
|
|
- char zLine[100];
|
|
- sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
|
- sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
|
|
- if( pItem->zDatabase ){
|
|
- sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
|
- }else if( pItem->zName ){
|
|
- sqlite3XPrintf(&x, " %s", pItem->zName);
|
|
- }
|
|
- if( pItem->pTab ){
|
|
- sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
|
|
- }
|
|
- if( pItem->zAlias ){
|
|
- sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
|
|
- }
|
|
- if( pItem->fg.jointype & JT_LEFT ){
|
|
- sqlite3XPrintf(&x, " LEFT-JOIN");
|
|
- }
|
|
- sqlite3StrAccumFinish(&x);
|
|
- sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
|
|
- if( pItem->pSelect ){
|
|
- sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
|
|
- }
|
|
- if( pItem->fg.isTabFunc ){
|
|
- sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
|
|
- }
|
|
- sqlite3TreeViewPop(pView);
|
|
- }
|
|
+ sqlite3TreeViewSrcList(pView, p->pSrc);
|
|
sqlite3TreeViewPop(pView);
|
|
}
|
|
if( p->pWhere ){
|
|
@@ -26912,19 +28577,29 @@
|
|
sqlite3TreeViewExpr(pView, p->pHaving, 0);
|
|
sqlite3TreeViewPop(pView);
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( p->pWinDefn ){
|
|
+ Window *pX;
|
|
+ sqlite3TreeViewItem(pView, "WINDOW", (n--)>0);
|
|
+ for(pX=p->pWinDefn; pX; pX=pX->pNextWin){
|
|
+ sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0);
|
|
+ }
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ }
|
|
+#endif
|
|
if( p->pOrderBy ){
|
|
sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
|
|
}
|
|
if( p->pLimit ){
|
|
sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
|
|
- sqlite3TreeViewExpr(pView, p->pLimit, 0);
|
|
+ sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0);
|
|
+ if( p->pLimit->pRight ){
|
|
+ sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
|
|
+ sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ }
|
|
sqlite3TreeViewPop(pView);
|
|
}
|
|
- if( p->pOffset ){
|
|
- sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
|
|
- sqlite3TreeViewExpr(pView, p->pOffset, 0);
|
|
- sqlite3TreeViewPop(pView);
|
|
- }
|
|
if( p->pPrior ){
|
|
const char *zOp = "UNION";
|
|
switch( p->op ){
|
|
@@ -26939,7 +28614,84 @@
|
|
sqlite3TreeViewPop(pView);
|
|
}
|
|
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
/*
|
|
+** Generate a description of starting or stopping bounds
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3TreeViewBound(
|
|
+ TreeView *pView, /* View context */
|
|
+ u8 eBound, /* UNBOUNDED, CURRENT, PRECEDING, FOLLOWING */
|
|
+ Expr *pExpr, /* Value for PRECEDING or FOLLOWING */
|
|
+ u8 moreToFollow /* True if more to follow */
|
|
+){
|
|
+ switch( eBound ){
|
|
+ case TK_UNBOUNDED: {
|
|
+ sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ break;
|
|
+ }
|
|
+ case TK_CURRENT: {
|
|
+ sqlite3TreeViewItem(pView, "CURRENT", moreToFollow);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ break;
|
|
+ }
|
|
+ case TK_PRECEDING: {
|
|
+ sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow);
|
|
+ sqlite3TreeViewExpr(pView, pExpr, 0);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ break;
|
|
+ }
|
|
+ case TK_FOLLOWING: {
|
|
+ sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow);
|
|
+ sqlite3TreeViewExpr(pView, pExpr, 0);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+/*
|
|
+** Generate a human-readable explanation for a Window object
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
|
|
+ pView = sqlite3TreeViewPush(pView, more);
|
|
+ if( pWin->zName ){
|
|
+ sqlite3TreeViewLine(pView, "OVER %s", pWin->zName);
|
|
+ }else{
|
|
+ sqlite3TreeViewLine(pView, "OVER");
|
|
+ }
|
|
+ if( pWin->pPartition ){
|
|
+ sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY");
|
|
+ }
|
|
+ if( pWin->pOrderBy ){
|
|
+ sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY");
|
|
+ }
|
|
+ if( pWin->eType ){
|
|
+ sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0);
|
|
+ sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1);
|
|
+ sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ }
|
|
+ sqlite3TreeViewPop(pView);
|
|
+}
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+/*
|
|
+** Generate a human-readable explanation for a Window Function object
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){
|
|
+ pView = sqlite3TreeViewPush(pView, more);
|
|
+ sqlite3TreeViewLine(pView, "WINFUNC %s(%d)",
|
|
+ pWin->pFunc->zName, pWin->pFunc->nArg);
|
|
+ sqlite3TreeViewWindow(pView, pWin, 0);
|
|
+ sqlite3TreeViewPop(pView);
|
|
+}
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
+/*
|
|
** Generate a human-readable explanation of an expression tree.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
|
@@ -26976,6 +28728,9 @@
|
|
sqlite3TreeViewLine(pView, "{%d:%d}%s",
|
|
pExpr->iTable, pExpr->iColumn, zFlgs);
|
|
}
|
|
+ if( ExprHasProperty(pExpr, EP_FixedCol) ){
|
|
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
|
+ }
|
|
break;
|
|
}
|
|
case TK_INTEGER: {
|
|
@@ -27000,6 +28755,11 @@
|
|
sqlite3TreeViewLine(pView,"NULL");
|
|
break;
|
|
}
|
|
+ case TK_TRUEFALSE: {
|
|
+ sqlite3TreeViewLine(pView,
|
|
+ sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE");
|
|
+ break;
|
|
+ }
|
|
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
|
case TK_BLOB: {
|
|
sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
|
|
@@ -27056,6 +28816,19 @@
|
|
case TK_ISNULL: zUniOp = "ISNULL"; break;
|
|
case TK_NOTNULL: zUniOp = "NOTNULL"; break;
|
|
|
|
+ case TK_TRUTH: {
|
|
+ int x;
|
|
+ const char *azOp[] = {
|
|
+ "IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE"
|
|
+ };
|
|
+ assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT );
|
|
+ assert( pExpr->pRight );
|
|
+ assert( pExpr->pRight->op==TK_TRUEFALSE );
|
|
+ x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight);
|
|
+ zUniOp = azOp[x];
|
|
+ break;
|
|
+ }
|
|
+
|
|
case TK_SPAN: {
|
|
sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
|
|
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
|
@@ -27071,10 +28844,17 @@
|
|
case TK_AGG_FUNCTION:
|
|
case TK_FUNCTION: {
|
|
ExprList *pFarg; /* List of function arguments */
|
|
+ Window *pWin;
|
|
if( ExprHasProperty(pExpr, EP_TokenOnly) ){
|
|
pFarg = 0;
|
|
+ pWin = 0;
|
|
}else{
|
|
pFarg = pExpr->x.pList;
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ pWin = pExpr->y.pWin;
|
|
+#else
|
|
+ pWin = 0;
|
|
+#endif
|
|
}
|
|
if( pExpr->op==TK_AGG_FUNCTION ){
|
|
sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
|
|
@@ -27083,8 +28863,13 @@
|
|
sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
|
|
}
|
|
if( pFarg ){
|
|
- sqlite3TreeViewExprList(pView, pFarg, 0, 0);
|
|
+ sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pWin ){
|
|
+ sqlite3TreeViewWindow(pView, pWin, 0);
|
|
+ }
|
|
+#endif
|
|
break;
|
|
}
|
|
#ifndef SQLITE_OMIT_SUBQUERY
|
|
@@ -27215,12 +29000,25 @@
|
|
sqlite3TreeViewLine(pView, "%s", zLabel);
|
|
for(i=0; i<pList->nExpr; i++){
|
|
int j = pList->a[i].u.x.iOrderByCol;
|
|
- if( j ){
|
|
- sqlite3TreeViewPush(pView, 0);
|
|
- sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
|
|
+ char *zName = pList->a[i].zName;
|
|
+ int moreToFollow = i<pList->nExpr - 1;
|
|
+ if( j || zName ){
|
|
+ sqlite3TreeViewPush(pView, moreToFollow);
|
|
+ moreToFollow = 0;
|
|
+ sqlite3TreeViewLine(pView, 0);
|
|
+ if( zName ){
|
|
+ fprintf(stdout, "AS %s ", zName);
|
|
+ }
|
|
+ if( j ){
|
|
+ fprintf(stdout, "iOrderByCol=%d", j);
|
|
+ }
|
|
+ fprintf(stdout, "\n");
|
|
+ fflush(stdout);
|
|
}
|
|
- sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
|
|
- if( j ) sqlite3TreeViewPop(pView);
|
|
+ sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow);
|
|
+ if( j || zName ){
|
|
+ sqlite3TreeViewPop(pView);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -28511,6 +30309,45 @@
|
|
}
|
|
|
|
/*
|
|
+** Compute 10 to the E-th power. Examples: E==1 results in 10.
|
|
+** E==2 results in 100. E==50 results in 1.0e50.
|
|
+**
|
|
+** This routine only works for values of E between 1 and 341.
|
|
+*/
|
|
+static LONGDOUBLE_TYPE sqlite3Pow10(int E){
|
|
+#if defined(_MSC_VER)
|
|
+ static const LONGDOUBLE_TYPE x[] = {
|
|
+ 1.0e+001,
|
|
+ 1.0e+002,
|
|
+ 1.0e+004,
|
|
+ 1.0e+008,
|
|
+ 1.0e+016,
|
|
+ 1.0e+032,
|
|
+ 1.0e+064,
|
|
+ 1.0e+128,
|
|
+ 1.0e+256
|
|
+ };
|
|
+ LONGDOUBLE_TYPE r = 1.0;
|
|
+ int i;
|
|
+ assert( E>=0 && E<=307 );
|
|
+ for(i=0; E!=0; i++, E >>=1){
|
|
+ if( E & 1 ) r *= x[i];
|
|
+ }
|
|
+ return r;
|
|
+#else
|
|
+ LONGDOUBLE_TYPE x = 10.0;
|
|
+ LONGDOUBLE_TYPE r = 1.0;
|
|
+ while(1){
|
|
+ if( E & 1 ) r *= x;
|
|
+ E >>= 1;
|
|
+ if( E==0 ) break;
|
|
+ x *= x;
|
|
+ }
|
|
+ return r;
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
** The string z[] is an text representation of a real number.
|
|
** Convert this string to a double and write it into *pResult.
|
|
**
|
|
@@ -28577,12 +30414,12 @@
|
|
/* copy max significant digits to significand */
|
|
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
|
s = s*10 + (*z - '0');
|
|
- z+=incr, nDigits++;
|
|
+ z+=incr; nDigits++;
|
|
}
|
|
|
|
/* skip non-significant significand digits
|
|
** (increase exponent by d to shift decimal left) */
|
|
- while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++;
|
|
+ while( z<zEnd && sqlite3Isdigit(*z) ){ z+=incr; nDigits++; d++; }
|
|
if( z>=zEnd ) goto do_atof_calc;
|
|
|
|
/* if decimal point is present */
|
|
@@ -28595,7 +30432,7 @@
|
|
s = s*10 + (*z - '0');
|
|
d--;
|
|
}
|
|
- z+=incr, nDigits++;
|
|
+ z+=incr; nDigits++;
|
|
}
|
|
}
|
|
if( z>=zEnd ) goto do_atof_calc;
|
|
@@ -28665,11 +30502,10 @@
|
|
if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
|
result = (double)s;
|
|
}else{
|
|
- LONGDOUBLE_TYPE scale = 1.0;
|
|
/* attempt to handle extremely small/large numbers better */
|
|
if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
|
|
if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
|
|
- while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
|
+ LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308);
|
|
if( esign<0 ){
|
|
result = s / scale;
|
|
result /= 1.0e+308;
|
|
@@ -28681,14 +30517,15 @@
|
|
if( esign<0 ){
|
|
result = 0.0*s;
|
|
}else{
|
|
+#ifdef INFINITY
|
|
+ result = INFINITY*s;
|
|
+#else
|
|
result = 1e308*1e308*s; /* Infinity */
|
|
+#endif
|
|
}
|
|
}
|
|
}else{
|
|
- /* 1.0e+22 is the largest power of 10 than can be
|
|
- ** represented exactly. */
|
|
- while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
|
- while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
|
+ LONGDOUBLE_TYPE scale = sqlite3Pow10(e);
|
|
if( esign<0 ){
|
|
result = s / scale;
|
|
}else{
|
|
@@ -28743,17 +30580,13 @@
|
|
** Convert zNum to a 64-bit signed integer. zNum must be decimal. This
|
|
** routine does *not* accept hexadecimal notation.
|
|
**
|
|
-** If the zNum value is representable as a 64-bit twos-complement
|
|
-** integer, then write that value into *pNum and return 0.
|
|
+** Returns:
|
|
**
|
|
-** If zNum is exactly 9223372036854775808, return 2. This special
|
|
-** case is broken out because while 9223372036854775808 cannot be a
|
|
-** signed 64-bit integer, its negative -9223372036854775808 can be.
|
|
+** 0 Successful transformation. Fits in a 64-bit signed integer.
|
|
+** 1 Excess non-space text after the integer value
|
|
+** 2 Integer too large for a 64-bit signed integer or is malformed
|
|
+** 3 Special case of 9223372036854775808
|
|
**
|
|
-** If zNum is too big for a 64-bit integer and is not
|
|
-** 9223372036854775808 or if zNum contains any non-numeric text,
|
|
-** then return 1.
|
|
-**
|
|
** length is the number of bytes in the string (bytes, not characters).
|
|
** The string is not necessarily zero-terminated. The encoding is
|
|
** given by enc.
|
|
@@ -28765,6 +30598,7 @@
|
|
int i;
|
|
int c = 0;
|
|
int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
|
|
+ int rc; /* Baseline return code */
|
|
const char *zStart;
|
|
const char *zEnd = zNum + length;
|
|
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
|
@@ -28792,7 +30626,14 @@
|
|
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
|
|
u = u*10 + c - '0';
|
|
}
|
|
+ testcase( i==18*incr );
|
|
+ testcase( i==19*incr );
|
|
+ testcase( i==20*incr );
|
|
if( u>LARGEST_INT64 ){
|
|
+ /* This test and assignment is needed only to suppress UB warnings
|
|
+ ** from clang and -fsanitize=undefined. This test and assignment make
|
|
+ ** the code a little larger and slower, and no harm comes from omitting
|
|
+ ** them, but we must appaise the undefined-behavior pharisees. */
|
|
*pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
|
|
}else if( neg ){
|
|
*pNum = -(i64)u;
|
|
@@ -28799,36 +30640,43 @@
|
|
}else{
|
|
*pNum = (i64)u;
|
|
}
|
|
- testcase( i==18 );
|
|
- testcase( i==19 );
|
|
- testcase( i==20 );
|
|
- if( &zNum[i]<zEnd /* Extra bytes at the end */
|
|
- || (i==0 && zStart==zNum) /* No digits */
|
|
- || i>19*incr /* Too many digits */
|
|
+ rc = 0;
|
|
+ if( (i==0 && zStart==zNum) /* No digits */
|
|
|| nonNum /* UTF16 with high-order bytes non-zero */
|
|
){
|
|
- /* zNum is empty or contains non-numeric text or is longer
|
|
- ** than 19 digits (thus guaranteeing that it is too large) */
|
|
- return 1;
|
|
- }else if( i<19*incr ){
|
|
+ rc = 1;
|
|
+ }else if( &zNum[i]<zEnd ){ /* Extra bytes at the end */
|
|
+ int jj = i;
|
|
+ do{
|
|
+ if( !sqlite3Isspace(zNum[jj]) ){
|
|
+ rc = 1; /* Extra non-space text after the integer */
|
|
+ break;
|
|
+ }
|
|
+ jj += incr;
|
|
+ }while( &zNum[jj]<zEnd );
|
|
+ }
|
|
+ if( i<19*incr ){
|
|
/* Less than 19 digits, so we know that it fits in 64 bits */
|
|
assert( u<=LARGEST_INT64 );
|
|
- return 0;
|
|
+ return rc;
|
|
}else{
|
|
/* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
|
- c = compare2pow63(zNum, incr);
|
|
+ c = i>19*incr ? 1 : compare2pow63(zNum, incr);
|
|
if( c<0 ){
|
|
/* zNum is less than 9223372036854775808 so it fits */
|
|
assert( u<=LARGEST_INT64 );
|
|
- return 0;
|
|
- }else if( c>0 ){
|
|
- /* zNum is greater than 9223372036854775808 so it overflows */
|
|
- return 1;
|
|
+ return rc;
|
|
}else{
|
|
- /* zNum is exactly 9223372036854775808. Fits if negative. The
|
|
- ** special case 2 overflow if positive */
|
|
- assert( u-1==LARGEST_INT64 );
|
|
- return neg ? 0 : 2;
|
|
+ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
|
|
+ if( c>0 ){
|
|
+ /* zNum is greater than 9223372036854775808 so it overflows */
|
|
+ return 2;
|
|
+ }else{
|
|
+ /* zNum is exactly 9223372036854775808. Fits if negative. The
|
|
+ ** special case 2 overflow if positive */
|
|
+ assert( u-1==LARGEST_INT64 );
|
|
+ return neg ? rc : 3;
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -28841,8 +30689,9 @@
|
|
** Returns:
|
|
**
|
|
** 0 Successful transformation. Fits in a 64-bit signed integer.
|
|
-** 1 Integer too large for a 64-bit signed integer or is malformed
|
|
-** 2 Special case of 9223372036854775808
|
|
+** 1 Excess text after the integer value
|
|
+** 2 Integer too large for a 64-bit signed integer or is malformed
|
|
+** 3 Special case of 9223372036854775808
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
|
|
#ifndef SQLITE_OMIT_HEX_INTEGER
|
|
@@ -28856,7 +30705,7 @@
|
|
u = u*16 + sqlite3HexToInt(z[k]);
|
|
}
|
|
memcpy(pOut, &u, 8);
|
|
- return (z[k]==0 && k-i<=16) ? 0 : 1;
|
|
+ return (z[k]==0 && k-i<=16) ? 0 : 2;
|
|
}else
|
|
#endif /* SQLITE_OMIT_HEX_INTEGER */
|
|
{
|
|
@@ -29466,7 +31315,7 @@
|
|
** overflow, leave *pA unchanged and return 1.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){
|
|
-#if GCC_VERSION>=5004000
|
|
+#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER)
|
|
return __builtin_add_overflow(*pA, iB, pA);
|
|
#else
|
|
i64 iA = *pA;
|
|
@@ -29486,7 +31335,7 @@
|
|
#endif
|
|
}
|
|
SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
|
|
-#if GCC_VERSION>=5004000
|
|
+#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER)
|
|
return __builtin_sub_overflow(*pA, iB, pA);
|
|
#else
|
|
testcase( iB==SMALLEST_INT64+1 );
|
|
@@ -29501,7 +31350,7 @@
|
|
#endif
|
|
}
|
|
SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
|
|
-#if GCC_VERSION>=5004000
|
|
+#if GCC_VERSION>=5004000 && !defined(__INTEL_COMPILER)
|
|
return __builtin_mul_overflow(*pA, iB, pA);
|
|
#else
|
|
i64 iA = *pA;
|
|
@@ -29603,8 +31452,14 @@
|
|
if( x<2 ) return 0;
|
|
while( x<8 ){ y -= 10; x <<= 1; }
|
|
}else{
|
|
+#if GCC_VERSION>=5004000
|
|
+ int i = 60 - __builtin_clzll(x);
|
|
+ y += i*10;
|
|
+ x >>= i;
|
|
+#else
|
|
while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/
|
|
while( x>15 ){ y += 10; x >>= 1; }
|
|
+#endif
|
|
}
|
|
return a[x&7] + y - 10;
|
|
}
|
|
@@ -29824,6 +31679,20 @@
|
|
}
|
|
return h;
|
|
}
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+static unsigned int strHashN(const char *z, int n){
|
|
+ unsigned int h = 0;
|
|
+ int i;
|
|
+ for(i=0; i<n; i++){
|
|
+ /* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
|
|
+ ** 0x9e3779b1 is 2654435761 which is the closest prime number to
|
|
+ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
|
|
+ h += sqlite3UpperToLower[z[i]];
|
|
+ h *= 0x9e3779b1;
|
|
+ }
|
|
+ return h;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
|
|
|
|
/* Link pNew element into the hash table pH. If pEntry!=0 then also
|
|
@@ -29935,7 +31804,41 @@
|
|
}
|
|
return &nullElement;
|
|
}
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+static HashElem *findElementWithHashN(
|
|
+ const Hash *pH, /* The pH to be searched */
|
|
+ const char *pKey, /* The key we are searching for */
|
|
+ int nKey, /* Number of key bytes to use */
|
|
+ unsigned int *pHash /* Write the hash value here */
|
|
+){
|
|
+ HashElem *elem; /* Used to loop thru the element list */
|
|
+ int count; /* Number of elements left to test */
|
|
+ unsigned int h; /* The computed hash */
|
|
+ static HashElem nullElement = { 0, 0, 0, 0 };
|
|
|
|
+ if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
|
|
+ struct _ht *pEntry;
|
|
+ h = strHashN(pKey, nKey) % pH->htsize;
|
|
+ pEntry = &pH->ht[h];
|
|
+ elem = pEntry->chain;
|
|
+ count = pEntry->count;
|
|
+ }else{
|
|
+ h = 0;
|
|
+ elem = pH->first;
|
|
+ count = pH->count;
|
|
+ }
|
|
+ if( pHash ) *pHash = h;
|
|
+ while( count-- ){
|
|
+ assert( elem!=0 );
|
|
+ if( sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
|
|
+ return elem;
|
|
+ }
|
|
+ elem = elem->next;
|
|
+ }
|
|
+ return &nullElement;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
+
|
|
/* Remove a single entry from the hash table given a pointer to that
|
|
** element and a hash on the element's key.
|
|
*/
|
|
@@ -29979,6 +31882,14 @@
|
|
assert( pKey!=0 );
|
|
return findElementWithHash(pH, pKey, 0)->data;
|
|
}
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE void *sqlite3HashFindN(const Hash *pH, const char *pKey, int nKey){
|
|
+ assert( pH!=0 );
|
|
+ assert( pKey!=0 );
|
|
+ assert( nKey>=0 );
|
|
+ return findElementWithHashN(pH, pKey, nKey, 0)->data;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
|
|
/* Insert an element into the hash table pH. The key is pKey
|
|
** and the data is "data".
|
|
@@ -30046,170 +31957,176 @@
|
|
/* 1 */ "AutoCommit" OpHelp(""),
|
|
/* 2 */ "Transaction" OpHelp(""),
|
|
/* 3 */ "SorterNext" OpHelp(""),
|
|
- /* 4 */ "PrevIfOpen" OpHelp(""),
|
|
- /* 5 */ "NextIfOpen" OpHelp(""),
|
|
- /* 6 */ "Prev" OpHelp(""),
|
|
- /* 7 */ "Next" OpHelp(""),
|
|
- /* 8 */ "Checkpoint" OpHelp(""),
|
|
- /* 9 */ "JournalMode" OpHelp(""),
|
|
- /* 10 */ "Vacuum" OpHelp(""),
|
|
- /* 11 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
|
|
- /* 12 */ "VUpdate" OpHelp("data=r[P3@P2]"),
|
|
- /* 13 */ "Goto" OpHelp(""),
|
|
- /* 14 */ "Gosub" OpHelp(""),
|
|
- /* 15 */ "InitCoroutine" OpHelp(""),
|
|
- /* 16 */ "Yield" OpHelp(""),
|
|
- /* 17 */ "MustBeInt" OpHelp(""),
|
|
- /* 18 */ "Jump" OpHelp(""),
|
|
+ /* 4 */ "Prev" OpHelp(""),
|
|
+ /* 5 */ "Next" OpHelp(""),
|
|
+ /* 6 */ "Checkpoint" OpHelp(""),
|
|
+ /* 7 */ "JournalMode" OpHelp(""),
|
|
+ /* 8 */ "Vacuum" OpHelp(""),
|
|
+ /* 9 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
|
|
+ /* 10 */ "VUpdate" OpHelp("data=r[P3@P2]"),
|
|
+ /* 11 */ "Goto" OpHelp(""),
|
|
+ /* 12 */ "Gosub" OpHelp(""),
|
|
+ /* 13 */ "InitCoroutine" OpHelp(""),
|
|
+ /* 14 */ "Yield" OpHelp(""),
|
|
+ /* 15 */ "MustBeInt" OpHelp(""),
|
|
+ /* 16 */ "Jump" OpHelp(""),
|
|
+ /* 17 */ "Once" OpHelp(""),
|
|
+ /* 18 */ "If" OpHelp(""),
|
|
/* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
|
|
- /* 20 */ "Once" OpHelp(""),
|
|
- /* 21 */ "If" OpHelp(""),
|
|
- /* 22 */ "IfNot" OpHelp(""),
|
|
- /* 23 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
|
|
- /* 24 */ "SeekLT" OpHelp("key=r[P3@P4]"),
|
|
- /* 25 */ "SeekLE" OpHelp("key=r[P3@P4]"),
|
|
- /* 26 */ "SeekGE" OpHelp("key=r[P3@P4]"),
|
|
- /* 27 */ "SeekGT" OpHelp("key=r[P3@P4]"),
|
|
- /* 28 */ "NoConflict" OpHelp("key=r[P3@P4]"),
|
|
- /* 29 */ "NotFound" OpHelp("key=r[P3@P4]"),
|
|
- /* 30 */ "Found" OpHelp("key=r[P3@P4]"),
|
|
- /* 31 */ "SeekRowid" OpHelp("intkey=r[P3]"),
|
|
- /* 32 */ "NotExists" OpHelp("intkey=r[P3]"),
|
|
- /* 33 */ "Last" OpHelp(""),
|
|
- /* 34 */ "IfSmaller" OpHelp(""),
|
|
- /* 35 */ "SorterSort" OpHelp(""),
|
|
- /* 36 */ "Sort" OpHelp(""),
|
|
- /* 37 */ "Rewind" OpHelp(""),
|
|
- /* 38 */ "IdxLE" OpHelp("key=r[P3@P4]"),
|
|
- /* 39 */ "IdxGT" OpHelp("key=r[P3@P4]"),
|
|
- /* 40 */ "IdxLT" OpHelp("key=r[P3@P4]"),
|
|
- /* 41 */ "IdxGE" OpHelp("key=r[P3@P4]"),
|
|
- /* 42 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
|
|
- /* 43 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
|
|
- /* 44 */ "Program" OpHelp(""),
|
|
- /* 45 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
|
|
- /* 46 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
|
|
- /* 47 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
|
|
- /* 48 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
|
|
- /* 49 */ "IncrVacuum" OpHelp(""),
|
|
- /* 50 */ "VNext" OpHelp(""),
|
|
- /* 51 */ "Init" OpHelp("Start at P2"),
|
|
- /* 52 */ "Return" OpHelp(""),
|
|
- /* 53 */ "EndCoroutine" OpHelp(""),
|
|
- /* 54 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
|
|
- /* 55 */ "Halt" OpHelp(""),
|
|
- /* 56 */ "Integer" OpHelp("r[P2]=P1"),
|
|
- /* 57 */ "Int64" OpHelp("r[P2]=P4"),
|
|
- /* 58 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
|
|
- /* 59 */ "Null" OpHelp("r[P2..P3]=NULL"),
|
|
- /* 60 */ "SoftNull" OpHelp("r[P1]=NULL"),
|
|
- /* 61 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
|
|
- /* 62 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
|
|
- /* 63 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
|
|
- /* 64 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
|
|
- /* 65 */ "SCopy" OpHelp("r[P2]=r[P1]"),
|
|
- /* 66 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
|
|
- /* 67 */ "ResultRow" OpHelp("output=r[P1@P2]"),
|
|
- /* 68 */ "CollSeq" OpHelp(""),
|
|
- /* 69 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
|
|
- /* 70 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
|
|
- /* 71 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
|
|
- /* 72 */ "RealAffinity" OpHelp(""),
|
|
- /* 73 */ "Cast" OpHelp("affinity(r[P1])"),
|
|
- /* 74 */ "Permutation" OpHelp(""),
|
|
- /* 75 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
|
|
- /* 76 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
|
|
- /* 77 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
|
|
- /* 78 */ "Eq" OpHelp("IF r[P3]==r[P1]"),
|
|
- /* 79 */ "Gt" OpHelp("IF r[P3]>r[P1]"),
|
|
- /* 80 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
|
|
- /* 81 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
|
|
- /* 82 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
|
|
- /* 83 */ "ElseNotEq" OpHelp(""),
|
|
- /* 84 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
|
|
- /* 85 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
|
|
- /* 86 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
|
|
- /* 87 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
|
|
- /* 88 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
|
|
- /* 89 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
|
|
- /* 90 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
|
|
- /* 91 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
|
|
- /* 92 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
|
|
- /* 93 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
|
|
- /* 94 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
|
|
- /* 95 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
|
|
- /* 96 */ "Column" OpHelp("r[P3]=PX"),
|
|
- /* 97 */ "String8" OpHelp("r[P2]='P4'"),
|
|
- /* 98 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
|
|
- /* 99 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
|
|
- /* 100 */ "Count" OpHelp("r[P2]=count()"),
|
|
- /* 101 */ "ReadCookie" OpHelp(""),
|
|
- /* 102 */ "SetCookie" OpHelp(""),
|
|
- /* 103 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
|
|
- /* 104 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
|
|
- /* 105 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
|
|
- /* 106 */ "OpenDup" OpHelp(""),
|
|
- /* 107 */ "OpenAutoindex" OpHelp("nColumn=P2"),
|
|
- /* 108 */ "OpenEphemeral" OpHelp("nColumn=P2"),
|
|
- /* 109 */ "SorterOpen" OpHelp(""),
|
|
- /* 110 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
|
|
- /* 111 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
|
|
- /* 112 */ "Close" OpHelp(""),
|
|
- /* 113 */ "ColumnsUsed" OpHelp(""),
|
|
- /* 114 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
|
|
- /* 115 */ "NewRowid" OpHelp("r[P2]=rowid"),
|
|
- /* 116 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
|
|
- /* 117 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
|
|
- /* 118 */ "Delete" OpHelp(""),
|
|
- /* 119 */ "ResetCount" OpHelp(""),
|
|
- /* 120 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
|
|
- /* 121 */ "SorterData" OpHelp("r[P2]=data"),
|
|
- /* 122 */ "RowData" OpHelp("r[P2]=data"),
|
|
- /* 123 */ "Rowid" OpHelp("r[P2]=rowid"),
|
|
- /* 124 */ "NullRow" OpHelp(""),
|
|
- /* 125 */ "SorterInsert" OpHelp("key=r[P2]"),
|
|
- /* 126 */ "IdxInsert" OpHelp("key=r[P2]"),
|
|
- /* 127 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
|
|
- /* 128 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
|
|
- /* 129 */ "IdxRowid" OpHelp("r[P2]=rowid"),
|
|
- /* 130 */ "Destroy" OpHelp(""),
|
|
- /* 131 */ "Clear" OpHelp(""),
|
|
- /* 132 */ "Real" OpHelp("r[P2]=P4"),
|
|
- /* 133 */ "ResetSorter" OpHelp(""),
|
|
- /* 134 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
|
|
- /* 135 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
|
|
- /* 136 */ "SqlExec" OpHelp(""),
|
|
- /* 137 */ "ParseSchema" OpHelp(""),
|
|
- /* 138 */ "LoadAnalysis" OpHelp(""),
|
|
- /* 139 */ "DropTable" OpHelp(""),
|
|
- /* 140 */ "DropIndex" OpHelp(""),
|
|
- /* 141 */ "DropTrigger" OpHelp(""),
|
|
- /* 142 */ "IntegrityCk" OpHelp(""),
|
|
- /* 143 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
|
|
- /* 144 */ "Param" OpHelp(""),
|
|
- /* 145 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
|
|
- /* 146 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
|
|
- /* 147 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
|
|
- /* 148 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"),
|
|
- /* 149 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
|
|
- /* 150 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
|
|
- /* 151 */ "Expire" OpHelp(""),
|
|
- /* 152 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
|
|
- /* 153 */ "VBegin" OpHelp(""),
|
|
- /* 154 */ "VCreate" OpHelp(""),
|
|
- /* 155 */ "VDestroy" OpHelp(""),
|
|
- /* 156 */ "VOpen" OpHelp(""),
|
|
- /* 157 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
|
|
- /* 158 */ "VRename" OpHelp(""),
|
|
- /* 159 */ "Pagecount" OpHelp(""),
|
|
- /* 160 */ "MaxPgcnt" OpHelp(""),
|
|
- /* 161 */ "PureFunc0" OpHelp(""),
|
|
- /* 162 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
|
|
- /* 163 */ "PureFunc" OpHelp(""),
|
|
- /* 164 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
|
|
- /* 165 */ "CursorHint" OpHelp(""),
|
|
- /* 166 */ "Noop" OpHelp(""),
|
|
- /* 167 */ "Explain" OpHelp(""),
|
|
+ /* 20 */ "IfNot" OpHelp(""),
|
|
+ /* 21 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
|
|
+ /* 22 */ "SeekLT" OpHelp("key=r[P3@P4]"),
|
|
+ /* 23 */ "SeekLE" OpHelp("key=r[P3@P4]"),
|
|
+ /* 24 */ "SeekGE" OpHelp("key=r[P3@P4]"),
|
|
+ /* 25 */ "SeekGT" OpHelp("key=r[P3@P4]"),
|
|
+ /* 26 */ "IfNoHope" OpHelp("key=r[P3@P4]"),
|
|
+ /* 27 */ "NoConflict" OpHelp("key=r[P3@P4]"),
|
|
+ /* 28 */ "NotFound" OpHelp("key=r[P3@P4]"),
|
|
+ /* 29 */ "Found" OpHelp("key=r[P3@P4]"),
|
|
+ /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"),
|
|
+ /* 31 */ "NotExists" OpHelp("intkey=r[P3]"),
|
|
+ /* 32 */ "Last" OpHelp(""),
|
|
+ /* 33 */ "IfSmaller" OpHelp(""),
|
|
+ /* 34 */ "SorterSort" OpHelp(""),
|
|
+ /* 35 */ "Sort" OpHelp(""),
|
|
+ /* 36 */ "Rewind" OpHelp(""),
|
|
+ /* 37 */ "IdxLE" OpHelp("key=r[P3@P4]"),
|
|
+ /* 38 */ "IdxGT" OpHelp("key=r[P3@P4]"),
|
|
+ /* 39 */ "IdxLT" OpHelp("key=r[P3@P4]"),
|
|
+ /* 40 */ "IdxGE" OpHelp("key=r[P3@P4]"),
|
|
+ /* 41 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
|
|
+ /* 42 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
|
|
+ /* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
|
|
+ /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
|
|
+ /* 45 */ "Program" OpHelp(""),
|
|
+ /* 46 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
|
|
+ /* 47 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
|
|
+ /* 48 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
|
|
+ /* 49 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
|
|
+ /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
|
|
+ /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
|
|
+ /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
|
|
+ /* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"),
|
|
+ /* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"),
|
|
+ /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
|
|
+ /* 56 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
|
|
+ /* 57 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
|
|
+ /* 58 */ "ElseNotEq" OpHelp(""),
|
|
+ /* 59 */ "IncrVacuum" OpHelp(""),
|
|
+ /* 60 */ "VNext" OpHelp(""),
|
|
+ /* 61 */ "Init" OpHelp("Start at P2"),
|
|
+ /* 62 */ "PureFunc0" OpHelp(""),
|
|
+ /* 63 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
|
|
+ /* 64 */ "PureFunc" OpHelp(""),
|
|
+ /* 65 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
|
|
+ /* 66 */ "Return" OpHelp(""),
|
|
+ /* 67 */ "EndCoroutine" OpHelp(""),
|
|
+ /* 68 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
|
|
+ /* 69 */ "Halt" OpHelp(""),
|
|
+ /* 70 */ "Integer" OpHelp("r[P2]=P1"),
|
|
+ /* 71 */ "Int64" OpHelp("r[P2]=P4"),
|
|
+ /* 72 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
|
|
+ /* 73 */ "Null" OpHelp("r[P2..P3]=NULL"),
|
|
+ /* 74 */ "SoftNull" OpHelp("r[P1]=NULL"),
|
|
+ /* 75 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
|
|
+ /* 76 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
|
|
+ /* 77 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
|
|
+ /* 78 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
|
|
+ /* 79 */ "SCopy" OpHelp("r[P2]=r[P1]"),
|
|
+ /* 80 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
|
|
+ /* 81 */ "ResultRow" OpHelp("output=r[P1@P2]"),
|
|
+ /* 82 */ "CollSeq" OpHelp(""),
|
|
+ /* 83 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
|
|
+ /* 84 */ "RealAffinity" OpHelp(""),
|
|
+ /* 85 */ "Cast" OpHelp("affinity(r[P1])"),
|
|
+ /* 86 */ "Permutation" OpHelp(""),
|
|
+ /* 87 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
|
|
+ /* 88 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"),
|
|
+ /* 89 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"),
|
|
+ /* 90 */ "Column" OpHelp("r[P3]=PX"),
|
|
+ /* 91 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
|
|
+ /* 92 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
|
|
+ /* 93 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
|
|
+ /* 94 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
|
|
+ /* 95 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
|
|
+ /* 96 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
|
|
+ /* 97 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
|
|
+ /* 98 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
|
|
+ /* 99 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
|
|
+ /* 100 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
|
|
+ /* 101 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
|
|
+ /* 102 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
|
|
+ /* 103 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
|
|
+ /* 104 */ "Count" OpHelp("r[P2]=count()"),
|
|
+ /* 105 */ "ReadCookie" OpHelp(""),
|
|
+ /* 106 */ "String8" OpHelp("r[P2]='P4'"),
|
|
+ /* 107 */ "SetCookie" OpHelp(""),
|
|
+ /* 108 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
|
|
+ /* 109 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
|
|
+ /* 110 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
|
|
+ /* 111 */ "OpenDup" OpHelp(""),
|
|
+ /* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"),
|
|
+ /* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"),
|
|
+ /* 114 */ "SorterOpen" OpHelp(""),
|
|
+ /* 115 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
|
|
+ /* 116 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
|
|
+ /* 117 */ "Close" OpHelp(""),
|
|
+ /* 118 */ "ColumnsUsed" OpHelp(""),
|
|
+ /* 119 */ "SeekHit" OpHelp("seekHit=P2"),
|
|
+ /* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
|
|
+ /* 121 */ "NewRowid" OpHelp("r[P2]=rowid"),
|
|
+ /* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
|
|
+ /* 123 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
|
|
+ /* 124 */ "Delete" OpHelp(""),
|
|
+ /* 125 */ "ResetCount" OpHelp(""),
|
|
+ /* 126 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
|
|
+ /* 127 */ "SorterData" OpHelp("r[P2]=data"),
|
|
+ /* 128 */ "RowData" OpHelp("r[P2]=data"),
|
|
+ /* 129 */ "Rowid" OpHelp("r[P2]=rowid"),
|
|
+ /* 130 */ "NullRow" OpHelp(""),
|
|
+ /* 131 */ "SeekEnd" OpHelp(""),
|
|
+ /* 132 */ "SorterInsert" OpHelp("key=r[P2]"),
|
|
+ /* 133 */ "IdxInsert" OpHelp("key=r[P2]"),
|
|
+ /* 134 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
|
|
+ /* 135 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
|
|
+ /* 136 */ "IdxRowid" OpHelp("r[P2]=rowid"),
|
|
+ /* 137 */ "Destroy" OpHelp(""),
|
|
+ /* 138 */ "Clear" OpHelp(""),
|
|
+ /* 139 */ "ResetSorter" OpHelp(""),
|
|
+ /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"),
|
|
+ /* 141 */ "Real" OpHelp("r[P2]=P4"),
|
|
+ /* 142 */ "SqlExec" OpHelp(""),
|
|
+ /* 143 */ "ParseSchema" OpHelp(""),
|
|
+ /* 144 */ "LoadAnalysis" OpHelp(""),
|
|
+ /* 145 */ "DropTable" OpHelp(""),
|
|
+ /* 146 */ "DropIndex" OpHelp(""),
|
|
+ /* 147 */ "DropTrigger" OpHelp(""),
|
|
+ /* 148 */ "IntegrityCk" OpHelp(""),
|
|
+ /* 149 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
|
|
+ /* 150 */ "Param" OpHelp(""),
|
|
+ /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
|
|
+ /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
|
|
+ /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
|
|
+ /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"),
|
|
+ /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
|
|
+ /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"),
|
|
+ /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"),
|
|
+ /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
|
|
+ /* 159 */ "Expire" OpHelp(""),
|
|
+ /* 160 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
|
|
+ /* 161 */ "VBegin" OpHelp(""),
|
|
+ /* 162 */ "VCreate" OpHelp(""),
|
|
+ /* 163 */ "VDestroy" OpHelp(""),
|
|
+ /* 164 */ "VOpen" OpHelp(""),
|
|
+ /* 165 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
|
|
+ /* 166 */ "VRename" OpHelp(""),
|
|
+ /* 167 */ "Pagecount" OpHelp(""),
|
|
+ /* 168 */ "MaxPgcnt" OpHelp(""),
|
|
+ /* 169 */ "Trace" OpHelp(""),
|
|
+ /* 170 */ "CursorHint" OpHelp(""),
|
|
+ /* 171 */ "Noop" OpHelp(""),
|
|
+ /* 172 */ "Explain" OpHelp(""),
|
|
+ /* 173 */ "Abortable" OpHelp(""),
|
|
};
|
|
return azName[i];
|
|
}
|
|
@@ -30309,6 +32226,7 @@
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
+#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
/* #include <time.h> */
|
|
#include <sys/time.h>
|
|
@@ -30318,7 +32236,7 @@
|
|
#endif
|
|
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
-# include <sys/ioctl.h>
|
|
+/* # include <sys/ioctl.h> */
|
|
# include <sys/file.h>
|
|
# include <sys/param.h>
|
|
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
|
|
@@ -30354,12 +32272,10 @@
|
|
#define SQLITE_FSFLAGS_IS_MSDOS 0x1
|
|
|
|
/*
|
|
-** If we are to be thread-safe, include the pthreads header and define
|
|
-** the SQLITE_UNIX_THREADS macro.
|
|
+** If we are to be thread-safe, include the pthreads header.
|
|
*/
|
|
#if SQLITE_THREADSAFE
|
|
/* # include <pthread.h> */
|
|
-# define SQLITE_UNIX_THREADS 1
|
|
#endif
|
|
|
|
/*
|
|
@@ -30428,7 +32344,7 @@
|
|
unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
|
|
int lastErrno; /* The unix errno from last I/O error */
|
|
void *lockingContext; /* Locking style specific state */
|
|
- UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
|
|
+ UnixUnusedFd *pPreallocatedUnused; /* Pre-allocated UnixUnusedFd */
|
|
const char *zPath; /* Name of the file */
|
|
unixShm *pShm; /* Shared memory segment information */
|
|
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
|
|
@@ -30439,10 +32355,8 @@
|
|
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
|
void *pMapRegion; /* Memory mapped region */
|
|
#endif
|
|
-#ifdef __QNXNTO__
|
|
int sectorSize; /* Device sector size */
|
|
int deviceCharacteristics; /* Precomputed device characteristics */
|
|
-#endif
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
int openFlags; /* The flags specified at open() */
|
|
#endif
|
|
@@ -30449,6 +32363,9 @@
|
|
#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
|
|
unsigned fsFlags; /* cached details from statfs() */
|
|
#endif
|
|
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
+ unsigned iBusyTimeout; /* Wait this many millisec on locks */
|
|
+#endif
|
|
#if OS_VXWORKS
|
|
struct vxworksFileId *pId; /* Unique file ID */
|
|
#endif
|
|
@@ -30745,7 +32662,21 @@
|
|
# define lseek lseek64
|
|
#endif
|
|
|
|
+#ifdef __linux__
|
|
/*
|
|
+** Linux-specific IOCTL magic numbers used for controlling F2FS
|
|
+*/
|
|
+#define F2FS_IOCTL_MAGIC 0xf5
|
|
+#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1)
|
|
+#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2)
|
|
+#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
|
|
+#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
|
|
+#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32)
|
|
+#define F2FS_FEATURE_ATOMIC_WRITE 0x0004
|
|
+#endif /* __linux__ */
|
|
+
|
|
+
|
|
+/*
|
|
** Different Unix systems declare open() in different ways. Same use
|
|
** open(const char*,int,mode_t). Others use open(const char*,int,...).
|
|
** The difference is important when using a pointer to the function.
|
|
@@ -30872,7 +32803,11 @@
|
|
#endif
|
|
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
|
|
|
|
+#if defined(HAVE_FCHOWN)
|
|
{ "geteuid", (sqlite3_syscall_ptr)geteuid, 0 },
|
|
+#else
|
|
+ { "geteuid", (sqlite3_syscall_ptr)0, 0 },
|
|
+#endif
|
|
#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
|
|
|
|
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
|
@@ -30887,7 +32822,7 @@
|
|
#else
|
|
{ "munmap", (sqlite3_syscall_ptr)0, 0 },
|
|
#endif
|
|
-#define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent)
|
|
+#define osMunmap ((int(*)(void*,size_t))aSyscall[23].pCurrent)
|
|
|
|
#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
|
{ "mremap", (sqlite3_syscall_ptr)mremap, 0 },
|
|
@@ -30917,6 +32852,17 @@
|
|
#endif
|
|
#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
|
|
|
|
+#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
+# ifdef __ANDROID__
|
|
+ { "ioctl", (sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 },
|
|
+# else
|
|
+ { "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
|
|
+# endif
|
|
+#else
|
|
+ { "ioctl", (sqlite3_syscall_ptr)0, 0 },
|
|
+#endif
|
|
+#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
|
|
+
|
|
}; /* End of the overrideable system calls */
|
|
|
|
|
|
@@ -31092,16 +33038,30 @@
|
|
** unixEnterMutex()
|
|
** assert( unixMutexHeld() );
|
|
** unixEnterLeave()
|
|
+**
|
|
+** To prevent deadlock, the global unixBigLock must must be acquired
|
|
+** before the unixInodeInfo.pLockMutex mutex, if both are held. It is
|
|
+** OK to get the pLockMutex without holding unixBigLock first, but if
|
|
+** that happens, the unixBigLock mutex must not be acquired until after
|
|
+** pLockMutex is released.
|
|
+**
|
|
+** OK: enter(unixBigLock), enter(pLockInfo)
|
|
+** OK: enter(unixBigLock)
|
|
+** OK: enter(pLockInfo)
|
|
+** ERROR: enter(pLockInfo), enter(unixBigLock)
|
|
*/
|
|
+static sqlite3_mutex *unixBigLock = 0;
|
|
static void unixEnterMutex(void){
|
|
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
|
+ assert( sqlite3_mutex_notheld(unixBigLock) ); /* Not a recursive mutex */
|
|
+ sqlite3_mutex_enter(unixBigLock);
|
|
}
|
|
static void unixLeaveMutex(void){
|
|
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
|
+ assert( sqlite3_mutex_held(unixBigLock) );
|
|
+ sqlite3_mutex_leave(unixBigLock);
|
|
}
|
|
#ifdef SQLITE_DEBUG
|
|
static int unixMutexHeld(void) {
|
|
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
|
+ return sqlite3_mutex_held(unixBigLock);
|
|
}
|
|
#endif
|
|
|
|
@@ -31491,22 +33451,39 @@
|
|
|
|
/*
|
|
** An instance of the following structure is allocated for each open
|
|
-** inode. Or, on LinuxThreads, there is one of these structures for
|
|
-** each inode opened by each thread.
|
|
+** inode.
|
|
**
|
|
** A single inode can have multiple file descriptors, so each unixFile
|
|
** structure contains a pointer to an instance of this object and this
|
|
** object keeps a count of the number of unixFile pointing to it.
|
|
+**
|
|
+** Mutex rules:
|
|
+**
|
|
+** (1) Only the pLockMutex mutex must be held in order to read or write
|
|
+** any of the locking fields:
|
|
+** nShared, nLock, eFileLock, bProcessLock, pUnused
|
|
+**
|
|
+** (2) When nRef>0, then the following fields are unchanging and can
|
|
+** be read (but not written) without holding any mutex:
|
|
+** fileId, pLockMutex
|
|
+**
|
|
+** (3) With the exceptions above, all the fields may only be read
|
|
+** or written while holding the global unixBigLock mutex.
|
|
+**
|
|
+** Deadlock prevention: The global unixBigLock mutex may not
|
|
+** be acquired while holding the pLockMutex mutex. If both unixBigLock
|
|
+** and pLockMutex are needed, then unixBigLock must be acquired first.
|
|
*/
|
|
struct unixInodeInfo {
|
|
struct unixFileId fileId; /* The lookup key */
|
|
- int nShared; /* Number of SHARED locks held */
|
|
- unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
|
|
- unsigned char bProcessLock; /* An exclusive process lock is held */
|
|
+ sqlite3_mutex *pLockMutex; /* Hold this mutex for... */
|
|
+ int nShared; /* Number of SHARED locks held */
|
|
+ int nLock; /* Number of outstanding file locks */
|
|
+ unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
|
|
+ unsigned char bProcessLock; /* An exclusive process lock is held */
|
|
+ UnixUnusedFd *pUnused; /* Unused file descriptors to close */
|
|
int nRef; /* Number of pointers to this structure */
|
|
unixShmNode *pShmNode; /* Shared memory associated with this inode */
|
|
- int nLock; /* Number of outstanding file locks */
|
|
- UnixUnusedFd *pUnused; /* Unused file descriptors to close */
|
|
unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
|
|
unixInodeInfo *pPrev; /* .... doubly linked */
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
@@ -31520,10 +33497,28 @@
|
|
|
|
/*
|
|
** A lists of all unixInodeInfo objects.
|
|
+**
|
|
+** Must hold unixBigLock in order to read or write this variable.
|
|
*/
|
|
-static unixInodeInfo *inodeList = 0;
|
|
+static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
/*
|
|
+** True if the inode mutex (on the unixFile.pFileMutex field) is held, or not.
|
|
+** This routine is used only within assert() to help verify correct mutex
|
|
+** usage.
|
|
+*/
|
|
+int unixFileMutexHeld(unixFile *pFile){
|
|
+ assert( pFile->pInode );
|
|
+ return sqlite3_mutex_held(pFile->pInode->pLockMutex);
|
|
+}
|
|
+int unixFileMutexNotheld(unixFile *pFile){
|
|
+ assert( pFile->pInode );
|
|
+ return sqlite3_mutex_notheld(pFile->pInode->pLockMutex);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
**
|
|
** This function - unixLogErrorAtLine(), is only ever called via the macro
|
|
** unixLogError().
|
|
@@ -31627,6 +33622,7 @@
|
|
unixInodeInfo *pInode = pFile->pInode;
|
|
UnixUnusedFd *p;
|
|
UnixUnusedFd *pNext;
|
|
+ assert( unixFileMutexHeld(pFile) );
|
|
for(p=pInode->pUnused; p; p=pNext){
|
|
pNext = p->pNext;
|
|
robust_close(pFile, p->fd, __LINE__);
|
|
@@ -31638,17 +33634,20 @@
|
|
/*
|
|
** Release a unixInodeInfo structure previously allocated by findInodeInfo().
|
|
**
|
|
-** The mutex entered using the unixEnterMutex() function must be held
|
|
-** when this function is called.
|
|
+** The global mutex must be held when this routine is called, but the mutex
|
|
+** on the inode being deleted must NOT be held.
|
|
*/
|
|
static void releaseInodeInfo(unixFile *pFile){
|
|
unixInodeInfo *pInode = pFile->pInode;
|
|
assert( unixMutexHeld() );
|
|
+ assert( unixFileMutexNotheld(pFile) );
|
|
if( ALWAYS(pInode) ){
|
|
pInode->nRef--;
|
|
if( pInode->nRef==0 ){
|
|
assert( pInode->pShmNode==0 );
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
closePendingFds(pFile);
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
if( pInode->pPrev ){
|
|
assert( pInode->pPrev->pNext==pInode );
|
|
pInode->pPrev->pNext = pInode->pNext;
|
|
@@ -31660,6 +33659,7 @@
|
|
assert( pInode->pNext->pPrev==pInode );
|
|
pInode->pNext->pPrev = pInode->pPrev;
|
|
}
|
|
+ sqlite3_mutex_free(pInode->pLockMutex);
|
|
sqlite3_free(pInode);
|
|
}
|
|
}
|
|
@@ -31670,8 +33670,7 @@
|
|
** describes that file descriptor. Create a new one if necessary. The
|
|
** return value might be uninitialized if an error occurs.
|
|
**
|
|
-** The mutex entered using the unixEnterMutex() function must be held
|
|
-** when this function is called.
|
|
+** The global mutex must held when calling this routine.
|
|
**
|
|
** Return an appropriate error code.
|
|
*/
|
|
@@ -31732,6 +33731,7 @@
|
|
#else
|
|
fileId.ino = (u64)statbuf.st_ino;
|
|
#endif
|
|
+ assert( unixMutexHeld() );
|
|
pInode = inodeList;
|
|
while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){
|
|
pInode = pInode->pNext;
|
|
@@ -31743,7 +33743,15 @@
|
|
}
|
|
memset(pInode, 0, sizeof(*pInode));
|
|
memcpy(&pInode->fileId, &fileId, sizeof(fileId));
|
|
+ if( sqlite3GlobalConfig.bCoreMutex ){
|
|
+ pInode->pLockMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
|
+ if( pInode->pLockMutex==0 ){
|
|
+ sqlite3_free(pInode);
|
|
+ return SQLITE_NOMEM_BKPT;
|
|
+ }
|
|
+ }
|
|
pInode->nRef = 1;
|
|
+ assert( unixMutexHeld() );
|
|
pInode->pNext = inodeList;
|
|
pInode->pPrev = 0;
|
|
if( inodeList ) inodeList->pPrev = pInode;
|
|
@@ -31821,7 +33829,7 @@
|
|
|
|
assert( pFile );
|
|
assert( pFile->eFileLock<=SHARED_LOCK );
|
|
- unixEnterMutex(); /* Because pFile->pInode is shared across threads */
|
|
+ sqlite3_mutex_enter(pFile->pInode->pLockMutex);
|
|
|
|
/* Check if a thread in this process holds such a lock */
|
|
if( pFile->pInode->eFileLock>SHARED_LOCK ){
|
|
@@ -31846,7 +33854,7 @@
|
|
}
|
|
#endif
|
|
|
|
- unixLeaveMutex();
|
|
+ sqlite3_mutex_leave(pFile->pInode->pLockMutex);
|
|
OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved));
|
|
|
|
*pResOut = reserved;
|
|
@@ -31854,6 +33862,43 @@
|
|
}
|
|
|
|
/*
|
|
+** Set a posix-advisory-lock.
|
|
+**
|
|
+** There are two versions of this routine. If compiled with
|
|
+** SQLITE_ENABLE_SETLK_TIMEOUT then the routine has an extra parameter
|
|
+** which is a pointer to a unixFile. If the unixFile->iBusyTimeout
|
|
+** value is set, then it is the number of milliseconds to wait before
|
|
+** failing the lock. The iBusyTimeout value is always reset back to
|
|
+** zero on each call.
|
|
+**
|
|
+** If SQLITE_ENABLE_SETLK_TIMEOUT is not defined, then do a non-blocking
|
|
+** attempt to set the lock.
|
|
+*/
|
|
+#ifndef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
+# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x)
|
|
+#else
|
|
+static int osSetPosixAdvisoryLock(
|
|
+ int h, /* The file descriptor on which to take the lock */
|
|
+ struct flock *pLock, /* The description of the lock */
|
|
+ unixFile *pFile /* Structure holding timeout value */
|
|
+){
|
|
+ int rc = osFcntl(h,F_SETLK,pLock);
|
|
+ while( rc<0 && pFile->iBusyTimeout>0 ){
|
|
+ /* On systems that support some kind of blocking file lock with a timeout,
|
|
+ ** make appropriate changes here to invoke that blocking file lock. On
|
|
+ ** generic posix, however, there is no such API. So we simply try the
|
|
+ ** lock once every millisecond until either the timeout expires, or until
|
|
+ ** the lock is obtained. */
|
|
+ usleep(1000);
|
|
+ rc = osFcntl(h,F_SETLK,pLock);
|
|
+ pFile->iBusyTimeout--;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
|
|
+
|
|
+
|
|
+/*
|
|
** Attempt to set a system-lock on the file pFile. The lock is
|
|
** described by pLock.
|
|
**
|
|
@@ -31875,8 +33920,8 @@
|
|
static int unixFileLock(unixFile *pFile, struct flock *pLock){
|
|
int rc;
|
|
unixInodeInfo *pInode = pFile->pInode;
|
|
- assert( unixMutexHeld() );
|
|
assert( pInode!=0 );
|
|
+ assert( sqlite3_mutex_held(pInode->pLockMutex) );
|
|
if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){
|
|
if( pInode->bProcessLock==0 ){
|
|
struct flock lock;
|
|
@@ -31885,7 +33930,7 @@
|
|
lock.l_start = SHARED_FIRST;
|
|
lock.l_len = SHARED_SIZE;
|
|
lock.l_type = F_WRLCK;
|
|
- rc = osFcntl(pFile->h, F_SETLK, &lock);
|
|
+ rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile);
|
|
if( rc<0 ) return rc;
|
|
pInode->bProcessLock = 1;
|
|
pInode->nLock++;
|
|
@@ -31893,7 +33938,7 @@
|
|
rc = 0;
|
|
}
|
|
}else{
|
|
- rc = osFcntl(pFile->h, F_SETLK, pLock);
|
|
+ rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -31995,8 +34040,8 @@
|
|
|
|
/* This mutex is needed because pFile->pInode is shared across threads
|
|
*/
|
|
- unixEnterMutex();
|
|
pInode = pFile->pInode;
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
|
|
/* If some thread using this PID has a lock via a different unixFile*
|
|
** handle that precludes the requested lock, return BUSY.
|
|
@@ -32139,7 +34184,7 @@
|
|
}
|
|
|
|
end_lock:
|
|
- unixLeaveMutex();
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock),
|
|
rc==SQLITE_OK ? "ok" : "failed"));
|
|
return rc;
|
|
@@ -32151,11 +34196,12 @@
|
|
*/
|
|
static void setPendingFd(unixFile *pFile){
|
|
unixInodeInfo *pInode = pFile->pInode;
|
|
- UnixUnusedFd *p = pFile->pUnused;
|
|
+ UnixUnusedFd *p = pFile->pPreallocatedUnused;
|
|
+ assert( unixFileMutexHeld(pFile) );
|
|
p->pNext = pInode->pUnused;
|
|
pInode->pUnused = p;
|
|
pFile->h = -1;
|
|
- pFile->pUnused = 0;
|
|
+ pFile->pPreallocatedUnused = 0;
|
|
}
|
|
|
|
/*
|
|
@@ -32186,8 +34232,8 @@
|
|
if( pFile->eFileLock<=eFileLock ){
|
|
return SQLITE_OK;
|
|
}
|
|
- unixEnterMutex();
|
|
pInode = pFile->pInode;
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
assert( pInode->nShared!=0 );
|
|
if( pFile->eFileLock>SHARED_LOCK ){
|
|
assert( pInode->eFileLock==pFile->eFileLock );
|
|
@@ -32313,14 +34359,14 @@
|
|
*/
|
|
pInode->nLock--;
|
|
assert( pInode->nLock>=0 );
|
|
- if( pInode->nLock==0 ){
|
|
- closePendingFds(pFile);
|
|
- }
|
|
+ if( pInode->nLock==0 ) closePendingFds(pFile);
|
|
}
|
|
|
|
end_unlock:
|
|
- unixLeaveMutex();
|
|
- if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pFile->eFileLock = eFileLock;
|
|
+ }
|
|
return rc;
|
|
}
|
|
|
|
@@ -32380,7 +34426,7 @@
|
|
#endif
|
|
OSTRACE(("CLOSE %-3d\n", pFile->h));
|
|
OpenCounter(-1);
|
|
- sqlite3_free(pFile->pUnused);
|
|
+ sqlite3_free(pFile->pPreallocatedUnused);
|
|
memset(pFile, 0, sizeof(unixFile));
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -32391,8 +34437,12 @@
|
|
static int unixClose(sqlite3_file *id){
|
|
int rc = SQLITE_OK;
|
|
unixFile *pFile = (unixFile *)id;
|
|
+ unixInodeInfo *pInode = pFile->pInode;
|
|
+
|
|
+ assert( pInode!=0 );
|
|
verifyDbFile(pFile);
|
|
unixUnlock(id, NO_LOCK);
|
|
+ assert( unixFileMutexNotheld(pFile) );
|
|
unixEnterMutex();
|
|
|
|
/* unixFile.pInode is always valid here. Otherwise, a different close
|
|
@@ -32399,7 +34449,8 @@
|
|
** routine (e.g. nolockClose()) would be called instead.
|
|
*/
|
|
assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 );
|
|
- if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
+ if( pInode->nLock ){
|
|
/* If there are outstanding locks, do not actually close the file just
|
|
** yet because that would clear those locks. Instead, add the file
|
|
** descriptor to pInode->pUnused list. It will be automatically closed
|
|
@@ -32407,6 +34458,7 @@
|
|
*/
|
|
setPendingFd(pFile);
|
|
}
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
releaseInodeInfo(pFile);
|
|
rc = closeUnixFile(id);
|
|
unixLeaveMutex();
|
|
@@ -32717,7 +34769,7 @@
|
|
OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved));
|
|
|
|
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
|
- if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
|
+ if( (rc & 0xff) == SQLITE_IOERR ){
|
|
rc = SQLITE_OK;
|
|
reserved=1;
|
|
}
|
|
@@ -32784,7 +34836,7 @@
|
|
OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock),
|
|
rc==SQLITE_OK ? "ok" : "failed"));
|
|
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
|
- if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
|
+ if( (rc & 0xff) == SQLITE_IOERR ){
|
|
rc = SQLITE_BUSY;
|
|
}
|
|
#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
|
@@ -33004,6 +35056,7 @@
|
|
unixFile *pFile = (unixFile*)id;
|
|
semXUnlock(id, NO_LOCK);
|
|
assert( pFile );
|
|
+ assert( unixFileMutexNotheld(pFile) );
|
|
unixEnterMutex();
|
|
releaseInodeInfo(pFile);
|
|
unixLeaveMutex();
|
|
@@ -33118,8 +35171,7 @@
|
|
*pResOut = 1;
|
|
return SQLITE_OK;
|
|
}
|
|
- unixEnterMutex(); /* Because pFile->pInode is shared across threads */
|
|
-
|
|
+ sqlite3_mutex_enter(pFile->pInode->pLockMutex);
|
|
/* Check if a thread in this process holds such a lock */
|
|
if( pFile->pInode->eFileLock>SHARED_LOCK ){
|
|
reserved = 1;
|
|
@@ -33143,7 +35195,7 @@
|
|
}
|
|
}
|
|
|
|
- unixLeaveMutex();
|
|
+ sqlite3_mutex_leave(pFile->pInode->pLockMutex);
|
|
OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved));
|
|
|
|
*pResOut = reserved;
|
|
@@ -33206,8 +35258,8 @@
|
|
|
|
/* This mutex is needed because pFile->pInode is shared across threads
|
|
*/
|
|
- unixEnterMutex();
|
|
pInode = pFile->pInode;
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
|
|
/* If some thread using this PID has a lock via a different unixFile*
|
|
** handle that precludes the requested lock, return BUSY.
|
|
@@ -33321,7 +35373,7 @@
|
|
/* Can't reestablish the shared lock. Sqlite can't deal, this is
|
|
** a critical I/O error
|
|
*/
|
|
- rc = ((failed & SQLITE_IOERR) == SQLITE_IOERR) ? failed2 :
|
|
+ rc = ((failed & 0xff) == SQLITE_IOERR) ? failed2 :
|
|
SQLITE_IOERR_LOCK;
|
|
goto afp_end_lock;
|
|
}
|
|
@@ -33343,7 +35395,7 @@
|
|
}
|
|
|
|
afp_end_lock:
|
|
- unixLeaveMutex();
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock),
|
|
rc==SQLITE_OK ? "ok" : "failed"));
|
|
return rc;
|
|
@@ -33375,8 +35427,8 @@
|
|
if( pFile->eFileLock<=eFileLock ){
|
|
return SQLITE_OK;
|
|
}
|
|
- unixEnterMutex();
|
|
pInode = pFile->pInode;
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
assert( pInode->nShared!=0 );
|
|
if( pFile->eFileLock>SHARED_LOCK ){
|
|
assert( pInode->eFileLock==pFile->eFileLock );
|
|
@@ -33445,14 +35497,14 @@
|
|
if( rc==SQLITE_OK ){
|
|
pInode->nLock--;
|
|
assert( pInode->nLock>=0 );
|
|
- if( pInode->nLock==0 ){
|
|
- closePendingFds(pFile);
|
|
- }
|
|
+ if( pInode->nLock==0 ) closePendingFds(pFile);
|
|
}
|
|
}
|
|
|
|
- unixLeaveMutex();
|
|
- if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pFile->eFileLock = eFileLock;
|
|
+ }
|
|
return rc;
|
|
}
|
|
|
|
@@ -33464,14 +35516,20 @@
|
|
unixFile *pFile = (unixFile*)id;
|
|
assert( id!=0 );
|
|
afpUnlock(id, NO_LOCK);
|
|
+ assert( unixFileMutexNotheld(pFile) );
|
|
unixEnterMutex();
|
|
- if( pFile->pInode && pFile->pInode->nLock ){
|
|
- /* If there are outstanding locks, do not actually close the file just
|
|
- ** yet because that would clear those locks. Instead, add the file
|
|
- ** descriptor to pInode->aPending. It will be automatically closed when
|
|
- ** the last lock is cleared.
|
|
- */
|
|
- setPendingFd(pFile);
|
|
+ if( pFile->pInode ){
|
|
+ unixInodeInfo *pInode = pFile->pInode;
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
+ if( pInode->nLock ){
|
|
+ /* If there are outstanding locks, do not actually close the file just
|
|
+ ** yet because that would clear those locks. Instead, add the file
|
|
+ ** descriptor to pInode->aPending. It will be automatically closed when
|
|
+ ** the last lock is cleared.
|
|
+ */
|
|
+ setPendingFd(pFile);
|
|
+ }
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
}
|
|
releaseInodeInfo(pFile);
|
|
sqlite3_free(pFile->lockingContext);
|
|
@@ -33601,7 +35659,7 @@
|
|
/* If this is a database file (not a journal, master-journal or temp
|
|
** file), the bytes in the locking range should never be read or written. */
|
|
#if 0
|
|
- assert( pFile->pUnused==0
|
|
+ assert( pFile->pPreallocatedUnused==0
|
|
|| offset>=PENDING_BYTE+512
|
|
|| offset+amt<=PENDING_BYTE
|
|
);
|
|
@@ -33714,7 +35772,7 @@
|
|
/* If this is a database file (not a journal, master-journal or temp
|
|
** file), the bytes in the locking range should never be read or written. */
|
|
#if 0
|
|
- assert( pFile->pUnused==0
|
|
+ assert( pFile->pPreallocatedUnused==0
|
|
|| offset>=PENDING_BYTE+512
|
|
|| offset+amt<=PENDING_BYTE
|
|
);
|
|
@@ -34126,7 +36184,7 @@
|
|
do{
|
|
err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size);
|
|
}while( err==EINTR );
|
|
- if( err ) return SQLITE_IOERR_WRITE;
|
|
+ if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE;
|
|
#else
|
|
/* If the OS does not have posix_fallocate(), fake it. Write a
|
|
** single byte to the last byte in each block that falls entirely
|
|
@@ -34194,6 +36252,21 @@
|
|
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
|
unixFile *pFile = (unixFile*)id;
|
|
switch( op ){
|
|
+#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
+ case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: {
|
|
+ int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE);
|
|
+ return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK;
|
|
+ }
|
|
+ case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: {
|
|
+ int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE);
|
|
+ return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK;
|
|
+ }
|
|
+ case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
|
|
+ int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
|
|
+ return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
|
|
+ }
|
|
+#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
|
|
+
|
|
case SQLITE_FCNTL_LOCKSTATE: {
|
|
*(int*)pArg = pFile->eFileLock;
|
|
return SQLITE_OK;
|
|
@@ -34237,6 +36310,12 @@
|
|
*(int*)pArg = fileHasMoved(pFile);
|
|
return SQLITE_OK;
|
|
}
|
|
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
+ case SQLITE_FCNTL_LOCK_TIMEOUT: {
|
|
+ pFile->iBusyTimeout = *(int*)pArg;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+#endif
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
case SQLITE_FCNTL_MMAP_SIZE: {
|
|
i64 newLimit = *(i64*)pArg;
|
|
@@ -34244,6 +36323,14 @@
|
|
if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
|
newLimit = sqlite3GlobalConfig.mxMmap;
|
|
}
|
|
+
|
|
+ /* The value of newLimit may be eventually cast to (size_t) and passed
|
|
+ ** to mmap(). Restrict its value to 2GB if (size_t) is not at least a
|
|
+ ** 64-bit type. */
|
|
+ if( newLimit>0 && sizeof(size_t)<8 ){
|
|
+ newLimit = (newLimit & 0x7FFFFFFF);
|
|
+ }
|
|
+
|
|
*(i64*)pArg = pFile->mmapSizeMax;
|
|
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
|
pFile->mmapSizeMax = newLimit;
|
|
@@ -34277,30 +36364,41 @@
|
|
}
|
|
|
|
/*
|
|
-** Return the sector size in bytes of the underlying block device for
|
|
-** the specified file. This is almost always 512 bytes, but may be
|
|
-** larger for some devices.
|
|
+** If pFd->sectorSize is non-zero when this function is called, it is a
|
|
+** no-op. Otherwise, the values of pFd->sectorSize and
|
|
+** pFd->deviceCharacteristics are set according to the file-system
|
|
+** characteristics.
|
|
**
|
|
-** SQLite code assumes this function cannot fail. It also assumes that
|
|
-** if two files are created in the same file-system directory (i.e.
|
|
-** a database and its journal file) that the sector size will be the
|
|
-** same for both.
|
|
+** There are two versions of this function. One for QNX and one for all
|
|
+** other systems.
|
|
*/
|
|
-#ifndef __QNXNTO__
|
|
-static int unixSectorSize(sqlite3_file *NotUsed){
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
- return SQLITE_DEFAULT_SECTOR_SIZE;
|
|
+#ifndef __QNXNTO__
|
|
+static void setDeviceCharacteristics(unixFile *pFd){
|
|
+ assert( pFd->deviceCharacteristics==0 || pFd->sectorSize!=0 );
|
|
+ if( pFd->sectorSize==0 ){
|
|
+#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
+ int res;
|
|
+ u32 f = 0;
|
|
+
|
|
+ /* Check for support for F2FS atomic batch writes. */
|
|
+ res = osIoctl(pFd->h, F2FS_IOC_GET_FEATURES, &f);
|
|
+ if( res==0 && (f & F2FS_FEATURE_ATOMIC_WRITE) ){
|
|
+ pFd->deviceCharacteristics = SQLITE_IOCAP_BATCH_ATOMIC;
|
|
+ }
|
|
+#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
|
|
+
|
|
+ /* Set the POWERSAFE_OVERWRITE flag if requested. */
|
|
+ if( pFd->ctrlFlags & UNIXFILE_PSOW ){
|
|
+ pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
|
+ }
|
|
+
|
|
+ pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
|
+ }
|
|
}
|
|
-#endif
|
|
-
|
|
-/*
|
|
-** The following version of unixSectorSize() is optimized for QNX.
|
|
-*/
|
|
-#ifdef __QNXNTO__
|
|
+#else
|
|
#include <sys/dcmd_blk.h>
|
|
#include <sys/statvfs.h>
|
|
-static int unixSectorSize(sqlite3_file *id){
|
|
- unixFile *pFile = (unixFile*)id;
|
|
+static void setDeviceCharacteristics(unixFile *pFile){
|
|
if( pFile->sectorSize == 0 ){
|
|
struct statvfs fsInfo;
|
|
|
|
@@ -34308,7 +36406,7 @@
|
|
pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
|
pFile->deviceCharacteristics = 0;
|
|
if( fstatvfs(pFile->h, &fsInfo) == -1 ) {
|
|
- return pFile->sectorSize;
|
|
+ return;
|
|
}
|
|
|
|
if( !strcmp(fsInfo.f_basetype, "tmp") ) {
|
|
@@ -34369,11 +36467,26 @@
|
|
pFile->deviceCharacteristics = 0;
|
|
pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
|
}
|
|
- return pFile->sectorSize;
|
|
}
|
|
-#endif /* __QNXNTO__ */
|
|
+#endif
|
|
|
|
/*
|
|
+** Return the sector size in bytes of the underlying block device for
|
|
+** the specified file. This is almost always 512 bytes, but may be
|
|
+** larger for some devices.
|
|
+**
|
|
+** SQLite code assumes this function cannot fail. It also assumes that
|
|
+** if two files are created in the same file-system directory (i.e.
|
|
+** a database and its journal file) that the sector size will be the
|
|
+** same for both.
|
|
+*/
|
|
+static int unixSectorSize(sqlite3_file *id){
|
|
+ unixFile *pFd = (unixFile*)id;
|
|
+ setDeviceCharacteristics(pFd);
|
|
+ return pFd->sectorSize;
|
|
+}
|
|
+
|
|
+/*
|
|
** Return the device characteristics for the file.
|
|
**
|
|
** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
|
|
@@ -34387,16 +36500,9 @@
|
|
** available to turn it off and URI query parameter available to turn it off.
|
|
*/
|
|
static int unixDeviceCharacteristics(sqlite3_file *id){
|
|
- unixFile *p = (unixFile*)id;
|
|
- int rc = 0;
|
|
-#ifdef __QNXNTO__
|
|
- if( p->sectorSize==0 ) unixSectorSize(id);
|
|
- rc = p->deviceCharacteristics;
|
|
-#endif
|
|
- if( p->ctrlFlags & UNIXFILE_PSOW ){
|
|
- rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
|
- }
|
|
- return rc;
|
|
+ unixFile *pFd = (unixFile*)id;
|
|
+ setDeviceCharacteristics(pFd);
|
|
+ return pFd->deviceCharacteristics;
|
|
}
|
|
|
|
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
|
@@ -34443,21 +36549,22 @@
|
|
**
|
|
** The following fields are read-only after the object is created:
|
|
**
|
|
-** fid
|
|
+** hShm
|
|
** zFilename
|
|
**
|
|
-** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and
|
|
+** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
|
|
** unixMutexHeld() is true when reading or writing any other field
|
|
** in this structure.
|
|
*/
|
|
struct unixShmNode {
|
|
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
|
|
- sqlite3_mutex *mutex; /* Mutex to access this object */
|
|
+ sqlite3_mutex *pShmMutex; /* Mutex to access this object */
|
|
char *zFilename; /* Name of the mmapped file */
|
|
- int h; /* Open file descriptor */
|
|
+ int hShm; /* Open file descriptor */
|
|
int szRegion; /* Size of shared-memory regions */
|
|
u16 nRegion; /* Size of array apRegion */
|
|
u8 isReadonly; /* True if read-only */
|
|
+ u8 isUnlocked; /* True if no DMS lock held */
|
|
char **apRegion; /* Array of mapped shared-memory regions */
|
|
int nRef; /* Number of unixShm objects pointing to this */
|
|
unixShm *pFirst; /* All unixShm objects pointing to this */
|
|
@@ -34475,16 +36582,16 @@
|
|
** The following fields are initialized when this object is created and
|
|
** are read-only thereafter:
|
|
**
|
|
-** unixShm.pFile
|
|
+** unixShm.pShmNode
|
|
** unixShm.id
|
|
**
|
|
-** All other fields are read/write. The unixShm.pFile->mutex must be held
|
|
-** while accessing any read/write fields.
|
|
+** All other fields are read/write. The unixShm.pShmNode->pShmMutex must
|
|
+** be held while accessing any read/write fields.
|
|
*/
|
|
struct unixShm {
|
|
unixShmNode *pShmNode; /* The underlying unixShmNode object */
|
|
unixShm *pNext; /* Next unixShm with the same unixShmNode */
|
|
- u8 hasMutex; /* True if holding the unixShmNode mutex */
|
|
+ u8 hasMutex; /* True if holding the unixShmNode->pShmMutex */
|
|
u8 id; /* Id of this connection within its unixShmNode */
|
|
u16 sharedMask; /* Mask of shared locks held */
|
|
u16 exclMask; /* Mask of exclusive locks held */
|
|
@@ -34514,7 +36621,8 @@
|
|
|
|
/* Access to the unixShmNode object is serialized by the caller */
|
|
pShmNode = pFile->pInode->pShmNode;
|
|
- assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
|
|
+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
|
|
+ assert( pShmNode->nRef>0 || unixMutexHeld() );
|
|
|
|
/* Shared locks never span more than one byte */
|
|
assert( n==1 || lockType!=F_RDLCK );
|
|
@@ -34522,15 +36630,13 @@
|
|
/* Locks are within range */
|
|
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
|
|
|
|
- if( pShmNode->h>=0 ){
|
|
+ if( pShmNode->hShm>=0 ){
|
|
/* Initialize the locking parameters */
|
|
- memset(&f, 0, sizeof(f));
|
|
f.l_type = lockType;
|
|
f.l_whence = SEEK_SET;
|
|
f.l_start = ofst;
|
|
f.l_len = n;
|
|
-
|
|
- rc = osFcntl(pShmNode->h, F_SETLK, &f);
|
|
+ rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
|
|
rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
|
|
}
|
|
|
|
@@ -34602,9 +36708,9 @@
|
|
int nShmPerMap = unixShmRegionPerMap();
|
|
int i;
|
|
assert( p->pInode==pFd->pInode );
|
|
- sqlite3_mutex_free(p->mutex);
|
|
+ sqlite3_mutex_free(p->pShmMutex);
|
|
for(i=0; i<p->nRegion; i+=nShmPerMap){
|
|
- if( p->h>=0 ){
|
|
+ if( p->hShm>=0 ){
|
|
osMunmap(p->apRegion[i], p->szRegion);
|
|
}else{
|
|
sqlite3_free(p->apRegion[i]);
|
|
@@ -34611,9 +36717,9 @@
|
|
}
|
|
}
|
|
sqlite3_free(p->apRegion);
|
|
- if( p->h>=0 ){
|
|
- robust_close(pFd, p->h, __LINE__);
|
|
- p->h = -1;
|
|
+ if( p->hShm>=0 ){
|
|
+ robust_close(pFd, p->hShm, __LINE__);
|
|
+ p->hShm = -1;
|
|
}
|
|
p->pInode->pShmNode = 0;
|
|
sqlite3_free(p);
|
|
@@ -34621,6 +36727,69 @@
|
|
}
|
|
|
|
/*
|
|
+** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
|
|
+** take it now. Return SQLITE_OK if successful, or an SQLite error
|
|
+** code otherwise.
|
|
+**
|
|
+** If the DMS cannot be locked because this is a readonly_shm=1
|
|
+** connection and no other process already holds a lock, return
|
|
+** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
|
|
+*/
|
|
+static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
|
|
+ struct flock lock;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ /* Use F_GETLK to determine the locks other processes are holding
|
|
+ ** on the DMS byte. If it indicates that another process is holding
|
|
+ ** a SHARED lock, then this process may also take a SHARED lock
|
|
+ ** and proceed with opening the *-shm file.
|
|
+ **
|
|
+ ** Or, if no other process is holding any lock, then this process
|
|
+ ** is the first to open it. In this case take an EXCLUSIVE lock on the
|
|
+ ** DMS byte and truncate the *-shm file to zero bytes in size. Then
|
|
+ ** downgrade to a SHARED lock on the DMS byte.
|
|
+ **
|
|
+ ** If another process is holding an EXCLUSIVE lock on the DMS byte,
|
|
+ ** return SQLITE_BUSY to the caller (it will try again). An earlier
|
|
+ ** version of this code attempted the SHARED lock at this point. But
|
|
+ ** this introduced a subtle race condition: if the process holding
|
|
+ ** EXCLUSIVE failed just before truncating the *-shm file, then this
|
|
+ ** process might open and use the *-shm file without truncating it.
|
|
+ ** And if the *-shm file has been corrupted by a power failure or
|
|
+ ** system crash, the database itself may also become corrupt. */
|
|
+ lock.l_whence = SEEK_SET;
|
|
+ lock.l_start = UNIX_SHM_DMS;
|
|
+ lock.l_len = 1;
|
|
+ lock.l_type = F_WRLCK;
|
|
+ if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) {
|
|
+ rc = SQLITE_IOERR_LOCK;
|
|
+ }else if( lock.l_type==F_UNLCK ){
|
|
+ if( pShmNode->isReadonly ){
|
|
+ pShmNode->isUnlocked = 1;
|
|
+ rc = SQLITE_READONLY_CANTINIT;
|
|
+ }else{
|
|
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
|
|
+ /* The first connection to attach must truncate the -shm file. We
|
|
+ ** truncate to 3 bytes (an arbitrary small number, less than the
|
|
+ ** -shm header size) rather than 0 as a system debugging aid, to
|
|
+ ** help detect if a -shm file truncation is legitimate or is the work
|
|
+ ** or a rogue process. */
|
|
+ if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){
|
|
+ rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
|
|
+ }
|
|
+ }
|
|
+ }else if( lock.l_type==F_WRLCK ){
|
|
+ rc = SQLITE_BUSY;
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
|
|
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
** Open a shared-memory area associated with open database file pDbFd.
|
|
** This particular implementation uses mmapped files.
|
|
**
|
|
@@ -34658,9 +36827,9 @@
|
|
static int unixOpenSharedMemory(unixFile *pDbFd){
|
|
struct unixShm *p = 0; /* The connection to be opened */
|
|
struct unixShmNode *pShmNode; /* The underlying mmapped file */
|
|
- int rc; /* Result code */
|
|
+ int rc = SQLITE_OK; /* Result code */
|
|
unixInodeInfo *pInode; /* The inode of fd */
|
|
- char *zShmFilename; /* Name of the file used for SHM */
|
|
+ char *zShm; /* Name of the file used for SHM */
|
|
int nShmFilename; /* Size of the SHM filename in bytes */
|
|
|
|
/* Allocate space for the new unixShm object. */
|
|
@@ -34672,6 +36841,7 @@
|
|
/* Check to see if a unixShmNode object already exists. Reuse an existing
|
|
** one if present. Create a new one if necessary.
|
|
*/
|
|
+ assert( unixFileMutexNotheld(pDbFd) );
|
|
unixEnterMutex();
|
|
pInode = pDbFd->pInode;
|
|
pShmNode = pInode->pShmNode;
|
|
@@ -34701,21 +36871,21 @@
|
|
goto shm_open_err;
|
|
}
|
|
memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
|
|
- zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
|
|
+ zShm = pShmNode->zFilename = (char*)&pShmNode[1];
|
|
#ifdef SQLITE_SHM_DIRECTORY
|
|
- sqlite3_snprintf(nShmFilename, zShmFilename,
|
|
+ sqlite3_snprintf(nShmFilename, zShm,
|
|
SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
|
|
(u32)sStat.st_ino, (u32)sStat.st_dev);
|
|
#else
|
|
- sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath);
|
|
- sqlite3FileSuffix3(pDbFd->zPath, zShmFilename);
|
|
+ sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath);
|
|
+ sqlite3FileSuffix3(pDbFd->zPath, zShm);
|
|
#endif
|
|
- pShmNode->h = -1;
|
|
+ pShmNode->hShm = -1;
|
|
pDbFd->pInode->pShmNode = pShmNode;
|
|
pShmNode->pInode = pDbFd->pInode;
|
|
if( sqlite3GlobalConfig.bCoreMutex ){
|
|
- pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
|
- if( pShmNode->mutex==0 ){
|
|
+ pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
|
+ if( pShmNode->pShmMutex==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto shm_open_err;
|
|
}
|
|
@@ -34722,36 +36892,26 @@
|
|
}
|
|
|
|
if( pInode->bProcessLock==0 ){
|
|
- int openFlags = O_RDWR | O_CREAT;
|
|
- if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
|
- openFlags = O_RDONLY;
|
|
+ if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
|
+ pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT,(sStat.st_mode&0777));
|
|
+ }
|
|
+ if( pShmNode->hShm<0 ){
|
|
+ pShmNode->hShm = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777));
|
|
+ if( pShmNode->hShm<0 ){
|
|
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm);
|
|
+ goto shm_open_err;
|
|
+ }
|
|
pShmNode->isReadonly = 1;
|
|
}
|
|
- pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
|
|
- if( pShmNode->h<0 ){
|
|
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
|
- goto shm_open_err;
|
|
- }
|
|
|
|
/* If this process is running as root, make sure that the SHM file
|
|
** is owned by the same user that owns the original database. Otherwise,
|
|
** the original owner will not be able to connect.
|
|
*/
|
|
- robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
|
|
-
|
|
- /* Check to see if another process is holding the dead-man switch.
|
|
- ** If not, truncate the file to zero length.
|
|
- */
|
|
- rc = SQLITE_OK;
|
|
- if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
|
|
- if( robust_ftruncate(pShmNode->h, 0) ){
|
|
- rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
|
|
- }
|
|
- }
|
|
- if( rc==SQLITE_OK ){
|
|
- rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
|
|
- }
|
|
- if( rc ) goto shm_open_err;
|
|
+ robustFchown(pShmNode->hShm, sStat.st_uid, sStat.st_gid);
|
|
+
|
|
+ rc = unixLockSharedMemory(pDbFd, pShmNode);
|
|
+ if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
|
|
}
|
|
}
|
|
|
|
@@ -34768,14 +36928,14 @@
|
|
** the cover of the unixEnterMutex() mutex and the pointer from the
|
|
** new (struct unixShm) object to the pShmNode has been set. All that is
|
|
** left to do is to link the new object into the linked list starting
|
|
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
|
|
- ** mutex.
|
|
+ ** at pShmNode->pFirst. This must be done while holding the
|
|
+ ** pShmNode->pShmMutex.
|
|
*/
|
|
- sqlite3_mutex_enter(pShmNode->mutex);
|
|
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
|
|
p->pNext = pShmNode->pFirst;
|
|
pShmNode->pFirst = p;
|
|
- sqlite3_mutex_leave(pShmNode->mutex);
|
|
- return SQLITE_OK;
|
|
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
|
|
+ return rc;
|
|
|
|
/* Jump here on any error */
|
|
shm_open_err:
|
|
@@ -34826,11 +36986,16 @@
|
|
|
|
p = pDbFd->pShm;
|
|
pShmNode = p->pShmNode;
|
|
- sqlite3_mutex_enter(pShmNode->mutex);
|
|
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
|
|
+ if( pShmNode->isUnlocked ){
|
|
+ rc = unixLockSharedMemory(pDbFd, pShmNode);
|
|
+ if( rc!=SQLITE_OK ) goto shmpage_out;
|
|
+ pShmNode->isUnlocked = 0;
|
|
+ }
|
|
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
|
|
assert( pShmNode->pInode==pDbFd->pInode );
|
|
- assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
|
- assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
|
+ assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
|
|
+ assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
|
|
|
|
/* Minimum number of regions required to be mapped. */
|
|
nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
|
|
@@ -34842,12 +37007,12 @@
|
|
|
|
pShmNode->szRegion = szRegion;
|
|
|
|
- if( pShmNode->h>=0 ){
|
|
+ if( pShmNode->hShm>=0 ){
|
|
/* The requested region is not mapped into this processes address space.
|
|
** Check to see if it has been allocated (i.e. if the wal-index file is
|
|
** large enough to contain the requested region).
|
|
*/
|
|
- if( osFstat(pShmNode->h, &sStat) ){
|
|
+ if( osFstat(pShmNode->hShm, &sStat) ){
|
|
rc = SQLITE_IOERR_SHMSIZE;
|
|
goto shmpage_out;
|
|
}
|
|
@@ -34875,7 +37040,7 @@
|
|
assert( (nByte % pgsz)==0 );
|
|
for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
|
|
int x = 0;
|
|
- if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){
|
|
+ if( seekAndWriteFd(pShmNode->hShm, iPg*pgsz + pgsz-1,"",1,&x)!=1 ){
|
|
const char *zFile = pShmNode->zFilename;
|
|
rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
|
|
goto shmpage_out;
|
|
@@ -34898,10 +37063,10 @@
|
|
int nMap = szRegion*nShmPerMap;
|
|
int i;
|
|
void *pMem;
|
|
- if( pShmNode->h>=0 ){
|
|
+ if( pShmNode->hShm>=0 ){
|
|
pMem = osMmap(0, nMap,
|
|
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
|
|
- MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
|
|
+ MAP_SHARED, pShmNode->hShm, szRegion*(i64)pShmNode->nRegion
|
|
);
|
|
if( pMem==MAP_FAILED ){
|
|
rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
|
|
@@ -34908,12 +37073,12 @@
|
|
goto shmpage_out;
|
|
}
|
|
}else{
|
|
- pMem = sqlite3_malloc64(szRegion);
|
|
+ pMem = sqlite3_malloc64(nMap);
|
|
if( pMem==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto shmpage_out;
|
|
}
|
|
- memset(pMem, 0, szRegion);
|
|
+ memset(pMem, 0, nMap);
|
|
}
|
|
|
|
for(i=0; i<nShmPerMap; i++){
|
|
@@ -34930,7 +37095,7 @@
|
|
*pp = 0;
|
|
}
|
|
if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
|
|
- sqlite3_mutex_leave(pShmNode->mutex);
|
|
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
|
|
return rc;
|
|
}
|
|
|
|
@@ -34964,12 +37129,12 @@
|
|
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
|
|
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
|
|
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
|
|
- assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
|
- assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
|
+ assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
|
|
+ assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
|
|
|
|
mask = (1<<(ofst+n)) - (1<<ofst);
|
|
assert( n>1 || mask==(1<<ofst) );
|
|
- sqlite3_mutex_enter(pShmNode->mutex);
|
|
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
|
|
if( flags & SQLITE_SHM_UNLOCK ){
|
|
u16 allMask = 0; /* Mask of locks held by siblings */
|
|
|
|
@@ -35042,7 +37207,7 @@
|
|
}
|
|
}
|
|
}
|
|
- sqlite3_mutex_leave(pShmNode->mutex);
|
|
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
|
|
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
|
|
p->id, osGetpid(0), p->sharedMask, p->exclMask));
|
|
return rc;
|
|
@@ -35059,6 +37224,9 @@
|
|
){
|
|
UNUSED_PARAMETER(fd);
|
|
sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
|
|
+ assert( fd->pMethods->xLock==nolockLock
|
|
+ || unixFileMutexNotheld((unixFile*)fd)
|
|
+ );
|
|
unixEnterMutex(); /* Also mutex, for redundancy */
|
|
unixLeaveMutex();
|
|
}
|
|
@@ -35089,7 +37257,7 @@
|
|
|
|
/* Remove connection p from the set of connections associated
|
|
** with pShmNode */
|
|
- sqlite3_mutex_enter(pShmNode->mutex);
|
|
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
|
|
for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
|
|
*pp = p->pNext;
|
|
|
|
@@ -35096,15 +37264,16 @@
|
|
/* Free the connection p */
|
|
sqlite3_free(p);
|
|
pDbFd->pShm = 0;
|
|
- sqlite3_mutex_leave(pShmNode->mutex);
|
|
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
|
|
|
|
/* If pShmNode->nRef has reached 0, then close the underlying
|
|
** shared-memory file, too */
|
|
+ assert( unixFileMutexNotheld(pDbFd) );
|
|
unixEnterMutex();
|
|
assert( pShmNode->nRef>0 );
|
|
pShmNode->nRef--;
|
|
if( pShmNode->nRef==0 ){
|
|
- if( deleteFlag && pShmNode->h>=0 ){
|
|
+ if( deleteFlag && pShmNode->hShm>=0 ){
|
|
osUnlink(pShmNode->zFilename);
|
|
}
|
|
unixShmPurge(pDbFd);
|
|
@@ -35426,7 +37595,7 @@
|
|
IOMETHODS(
|
|
nolockIoFinder, /* Finder function name */
|
|
nolockIoMethods, /* sqlite3_io_methods object name */
|
|
- 3, /* shared memory is disabled */
|
|
+ 3, /* shared memory and mmap are enabled */
|
|
nolockClose, /* xClose method */
|
|
nolockLock, /* xLock method */
|
|
nolockUnlock, /* xUnlock method */
|
|
@@ -35654,17 +37823,6 @@
|
|
|
|
assert( pNew->pInode==NULL );
|
|
|
|
- /* Usually the path zFilename should not be a relative pathname. The
|
|
- ** exception is when opening the proxy "conch" file in builds that
|
|
- ** include the special Apple locking styles.
|
|
- */
|
|
-#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
|
- assert( zFilename==0 || zFilename[0]=='/'
|
|
- || pVfs->pAppData==(void*)&autolockIoFinder );
|
|
-#else
|
|
- assert( zFilename==0 || zFilename[0]=='/' );
|
|
-#endif
|
|
-
|
|
/* No locking occurs in temporary files */
|
|
assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 );
|
|
|
|
@@ -35923,6 +38081,8 @@
|
|
#if !OS_VXWORKS
|
|
struct stat sStat; /* Results of stat() call */
|
|
|
|
+ unixEnterMutex();
|
|
+
|
|
/* A stat() call may fail for various reasons. If this happens, it is
|
|
** almost certain that an open() call on the same path will also fail.
|
|
** For this reason, if an error occurs in the stat() call here, it is
|
|
@@ -35931,10 +38091,9 @@
|
|
**
|
|
** Even if a subsequent open() call does succeed, the consequences of
|
|
** not searching for a reusable file descriptor are not dire. */
|
|
- if( 0==osStat(zPath, &sStat) ){
|
|
+ if( inodeList!=0 && 0==osStat(zPath, &sStat) ){
|
|
unixInodeInfo *pInode;
|
|
|
|
- unixEnterMutex();
|
|
pInode = inodeList;
|
|
while( pInode && (pInode->fileId.dev!=sStat.st_dev
|
|
|| pInode->fileId.ino!=(u64)sStat.st_ino) ){
|
|
@@ -35942,14 +38101,17 @@
|
|
}
|
|
if( pInode ){
|
|
UnixUnusedFd **pp;
|
|
+ assert( sqlite3_mutex_notheld(pInode->pLockMutex) );
|
|
+ sqlite3_mutex_enter(pInode->pLockMutex);
|
|
for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
|
|
pUnused = *pp;
|
|
if( pUnused ){
|
|
*pp = pUnused->pNext;
|
|
}
|
|
+ sqlite3_mutex_leave(pInode->pLockMutex);
|
|
}
|
|
- unixLeaveMutex();
|
|
}
|
|
+ unixLeaveMutex();
|
|
#endif /* if !OS_VXWORKS */
|
|
return pUnused;
|
|
}
|
|
@@ -36025,16 +38187,11 @@
|
|
*/
|
|
nDb = sqlite3Strlen30(zPath) - 1;
|
|
while( zPath[nDb]!='-' ){
|
|
-#ifndef SQLITE_ENABLE_8_3_NAMES
|
|
- /* In the normal case (8+3 filenames disabled) the journal filename
|
|
- ** is guaranteed to contain a '-' character. */
|
|
- assert( nDb>0 );
|
|
- assert( sqlite3Isalnum(zPath[nDb]) );
|
|
-#else
|
|
- /* If 8+3 names are possible, then the journal file might not contain
|
|
- ** a '-' character. So check for that case and return early. */
|
|
+ /* In normal operation, the journal file name will always contain
|
|
+ ** a '-' character. However in 8+3 filename mode, or if a corrupt
|
|
+ ** rollback journal specifies a master journal with a goofy name, then
|
|
+ ** the '-' might be missing. */
|
|
if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
|
|
-#endif
|
|
nDb--;
|
|
}
|
|
memcpy(zDb, zPath, nDb);
|
|
@@ -36109,7 +38266,7 @@
|
|
** a file-descriptor on the directory too. The first time unixSync()
|
|
** is called the directory file descriptor will be fsync()ed and close()d.
|
|
*/
|
|
- int syncDir = (isCreate && (
|
|
+ int isNewJrnl = (isCreate && (
|
|
eType==SQLITE_OPEN_MASTER_JOURNAL
|
|
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|
|
|| eType==SQLITE_OPEN_WAL
|
|
@@ -36156,7 +38313,6 @@
|
|
randomnessPid = osGetpid(0);
|
|
sqlite3_randomness(0,0);
|
|
}
|
|
-
|
|
memset(p, 0, sizeof(unixFile));
|
|
|
|
if( eType==SQLITE_OPEN_MAIN_DB ){
|
|
@@ -36170,7 +38326,7 @@
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
}
|
|
- p->pUnused = pUnused;
|
|
+ p->pPreallocatedUnused = pUnused;
|
|
|
|
/* Database filenames are double-zero terminated if they are not
|
|
** URIs with parameters. Hence, they can always be passed into
|
|
@@ -36179,7 +38335,7 @@
|
|
|
|
}else if( !zName ){
|
|
/* If zName is NULL, the upper layer is requesting a temp file. */
|
|
- assert(isDelete && !syncDir);
|
|
+ assert(isDelete && !isNewJrnl);
|
|
rc = unixGetTempname(pVfs->mxPathname, zTmpname);
|
|
if( rc!=SQLITE_OK ){
|
|
return rc;
|
|
@@ -36207,7 +38363,7 @@
|
|
gid_t gid; /* Groupid for the file */
|
|
rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid);
|
|
if( rc!=SQLITE_OK ){
|
|
- assert( !p->pUnused );
|
|
+ assert( !p->pPreallocatedUnused );
|
|
assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
|
|
return rc;
|
|
}
|
|
@@ -36214,17 +38370,24 @@
|
|
fd = robust_open(zName, openFlags, openMode);
|
|
OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags));
|
|
assert( !isExclusive || (openFlags & O_CREAT)!=0 );
|
|
- if( fd<0 && errno!=EISDIR && isReadWrite ){
|
|
- /* Failed to open the file for read/write access. Try read-only. */
|
|
- flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
|
- openFlags &= ~(O_RDWR|O_CREAT);
|
|
- flags |= SQLITE_OPEN_READONLY;
|
|
- openFlags |= O_RDONLY;
|
|
- isReadonly = 1;
|
|
- fd = robust_open(zName, openFlags, openMode);
|
|
+ if( fd<0 ){
|
|
+ if( isNewJrnl && errno==EACCES && osAccess(zName, F_OK) ){
|
|
+ /* If unable to create a journal because the directory is not
|
|
+ ** writable, change the error code to indicate that. */
|
|
+ rc = SQLITE_READONLY_DIRECTORY;
|
|
+ }else if( errno!=EISDIR && isReadWrite ){
|
|
+ /* Failed to open the file for read/write access. Try read-only. */
|
|
+ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
|
+ openFlags &= ~(O_RDWR|O_CREAT);
|
|
+ flags |= SQLITE_OPEN_READONLY;
|
|
+ openFlags |= O_RDONLY;
|
|
+ isReadonly = 1;
|
|
+ fd = robust_open(zName, openFlags, openMode);
|
|
+ }
|
|
}
|
|
if( fd<0 ){
|
|
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
|
|
+ int rc2 = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
|
|
+ if( rc==SQLITE_OK ) rc = rc2;
|
|
goto open_finished;
|
|
}
|
|
|
|
@@ -36241,9 +38404,9 @@
|
|
*pOutFlags = flags;
|
|
}
|
|
|
|
- if( p->pUnused ){
|
|
- p->pUnused->fd = fd;
|
|
- p->pUnused->flags = flags;
|
|
+ if( p->pPreallocatedUnused ){
|
|
+ p->pPreallocatedUnused->fd = fd;
|
|
+ p->pPreallocatedUnused->flags = flags;
|
|
}
|
|
|
|
if( isDelete ){
|
|
@@ -36284,7 +38447,7 @@
|
|
if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
|
|
noLock = eType!=SQLITE_OPEN_MAIN_DB;
|
|
if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
|
|
- if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
|
|
+ if( isNewJrnl ) ctrlFlags |= UNIXFILE_DIRSYNC;
|
|
if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
|
|
|
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
|
@@ -36320,11 +38483,14 @@
|
|
}
|
|
#endif
|
|
|
|
+ assert( zPath==0 || zPath[0]=='/'
|
|
+ || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL
|
|
+ );
|
|
rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
|
|
|
open_finished:
|
|
if( rc!=SQLITE_OK ){
|
|
- sqlite3_free(p->pUnused);
|
|
+ sqlite3_free(p->pPreallocatedUnused);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -37065,7 +39231,7 @@
|
|
dummyVfs.zName = "dummy";
|
|
pUnused->fd = fd;
|
|
pUnused->flags = openFlags;
|
|
- pNew->pUnused = pUnused;
|
|
+ pNew->pPreallocatedUnused = pUnused;
|
|
|
|
rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0);
|
|
if( rc==SQLITE_OK ){
|
|
@@ -38015,12 +40181,13 @@
|
|
|
|
/* Double-check that the aSyscall[] array has been constructed
|
|
** correctly. See ticket [bb3a86e890c8e96ab] */
|
|
- assert( ArraySize(aSyscall)==28 );
|
|
+ assert( ArraySize(aSyscall)==29 );
|
|
|
|
/* Register all VFSes defined in the aVfs[] array */
|
|
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
|
sqlite3_vfs_register(&aVfs[i], i==0);
|
|
}
|
|
+ unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -38032,6 +40199,7 @@
|
|
** This routine is a no-op for unix.
|
|
*/
|
|
SQLITE_API int sqlite3_os_end(void){
|
|
+ unixBigLock = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -38523,8 +40691,7 @@
|
|
int nFetchOut; /* Number of outstanding xFetch references */
|
|
HANDLE hMap; /* Handle for accessing memory mapping */
|
|
void *pMapRegion; /* Area memory mapped */
|
|
- sqlite3_int64 mmapSize; /* Usable size of mapped region */
|
|
- sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */
|
|
+ sqlite3_int64 mmapSize; /* Size of mapped region */
|
|
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
|
#endif
|
|
};
|
|
@@ -38555,22 +40722,6 @@
|
|
#endif
|
|
|
|
/*
|
|
- * The value used with sqlite3_win32_set_directory() to specify that
|
|
- * the data directory should be changed.
|
|
- */
|
|
-#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
|
|
-# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
|
|
-#endif
|
|
-
|
|
-/*
|
|
- * The value used with sqlite3_win32_set_directory() to specify that
|
|
- * the temporary directory should be changed.
|
|
- */
|
|
-#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
|
|
-# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
|
|
-#endif
|
|
-
|
|
-/*
|
|
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
|
|
* various Win32 API heap functions instead of our own.
|
|
*/
|
|
@@ -40166,13 +42317,13 @@
|
|
}
|
|
|
|
/*
|
|
-** This function sets the data directory or the temporary directory based on
|
|
-** the provided arguments. The type argument must be 1 in order to set the
|
|
-** data directory or 2 in order to set the temporary directory. The zValue
|
|
-** argument is the name of the directory to use. The return value will be
|
|
-** SQLITE_OK if successful.
|
|
+** This function is the same as sqlite3_win32_set_directory (below); however,
|
|
+** it accepts a UTF-8 string.
|
|
*/
|
|
-SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
|
|
+SQLITE_API int sqlite3_win32_set_directory8(
|
|
+ unsigned long type, /* Identifier for directory being set or reset */
|
|
+ const char *zValue /* New value for directory being set or reset */
|
|
+){
|
|
char **ppDirectory = 0;
|
|
#ifndef SQLITE_OMIT_AUTOINIT
|
|
int rc = sqlite3_initialize();
|
|
@@ -40188,15 +42339,15 @@
|
|
);
|
|
assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
|
|
if( ppDirectory ){
|
|
- char *zValueUtf8 = 0;
|
|
+ char *zCopy = 0;
|
|
if( zValue && zValue[0] ){
|
|
- zValueUtf8 = winUnicodeToUtf8(zValue);
|
|
- if ( zValueUtf8==0 ){
|
|
+ zCopy = sqlite3_mprintf("%s", zValue);
|
|
+ if ( zCopy==0 ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
}
|
|
sqlite3_free(*ppDirectory);
|
|
- *ppDirectory = zValueUtf8;
|
|
+ *ppDirectory = zCopy;
|
|
return SQLITE_OK;
|
|
}
|
|
return SQLITE_ERROR;
|
|
@@ -40203,6 +42354,39 @@
|
|
}
|
|
|
|
/*
|
|
+** This function is the same as sqlite3_win32_set_directory (below); however,
|
|
+** it accepts a UTF-16 string.
|
|
+*/
|
|
+SQLITE_API int sqlite3_win32_set_directory16(
|
|
+ unsigned long type, /* Identifier for directory being set or reset */
|
|
+ const void *zValue /* New value for directory being set or reset */
|
|
+){
|
|
+ int rc;
|
|
+ char *zUtf8 = 0;
|
|
+ if( zValue ){
|
|
+ zUtf8 = sqlite3_win32_unicode_to_utf8(zValue);
|
|
+ if( zUtf8==0 ) return SQLITE_NOMEM_BKPT;
|
|
+ }
|
|
+ rc = sqlite3_win32_set_directory8(type, zUtf8);
|
|
+ if( zUtf8 ) sqlite3_free(zUtf8);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function sets the data directory or the temporary directory based on
|
|
+** the provided arguments. The type argument must be 1 in order to set the
|
|
+** data directory or 2 in order to set the temporary directory. The zValue
|
|
+** argument is the name of the directory to use. The return value will be
|
|
+** SQLITE_OK if successful.
|
|
+*/
|
|
+SQLITE_API int sqlite3_win32_set_directory(
|
|
+ unsigned long type, /* Identifier for directory being set or reset */
|
|
+ void *zValue /* New value for directory being set or reset */
|
|
+){
|
|
+ return sqlite3_win32_set_directory16(type, zValue);
|
|
+}
|
|
+
|
|
+/*
|
|
** The return value of winGetLastErrorMsg
|
|
** is zero if the error message fits in the buffer, or non-zero
|
|
** otherwise (if the message was truncated).
|
|
@@ -41126,6 +43310,29 @@
|
|
winFile *pFile = (winFile*)id; /* File handle object */
|
|
int rc = SQLITE_OK; /* Return code for this function */
|
|
DWORD lastErrno;
|
|
+#if SQLITE_MAX_MMAP_SIZE>0
|
|
+ sqlite3_int64 oldMmapSize;
|
|
+ if( pFile->nFetchOut>0 ){
|
|
+ /* File truncation is a no-op if there are outstanding memory mapped
|
|
+ ** pages. This is because truncating the file means temporarily unmapping
|
|
+ ** the file, and that might delete memory out from under existing cursors.
|
|
+ **
|
|
+ ** This can result in incremental vacuum not truncating the file,
|
|
+ ** if there is an active read cursor when the incremental vacuum occurs.
|
|
+ ** No real harm comes of this - the database file is not corrupted,
|
|
+ ** though some folks might complain that the file is bigger than it
|
|
+ ** needs to be.
|
|
+ **
|
|
+ ** The only feasible work-around is to defer the truncation until after
|
|
+ ** all references to memory-mapped content are closed. That is doable,
|
|
+ ** but involves adding a few branches in the common write code path which
|
|
+ ** could slow down normal operations slightly. Hence, we have decided for
|
|
+ ** now to simply make trancations a no-op if there are pending reads. We
|
|
+ ** can maybe revisit this decision in the future.
|
|
+ */
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+#endif
|
|
|
|
assert( pFile );
|
|
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
|
@@ -41141,6 +43348,15 @@
|
|
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
|
}
|
|
|
|
+#if SQLITE_MAX_MMAP_SIZE>0
|
|
+ if( pFile->pMapRegion ){
|
|
+ oldMmapSize = pFile->mmapSize;
|
|
+ }else{
|
|
+ oldMmapSize = 0;
|
|
+ }
|
|
+ winUnmapfile(pFile);
|
|
+#endif
|
|
+
|
|
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
|
|
if( winSeekFile(pFile, nByte) ){
|
|
rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
|
|
@@ -41153,12 +43369,12 @@
|
|
}
|
|
|
|
#if SQLITE_MAX_MMAP_SIZE>0
|
|
- /* If the file was truncated to a size smaller than the currently
|
|
- ** mapped region, reduce the effective mapping size as well. SQLite will
|
|
- ** use read() and write() to access data beyond this point from now on.
|
|
- */
|
|
- if( pFile->pMapRegion && nByte<pFile->mmapSize ){
|
|
- pFile->mmapSize = nByte;
|
|
+ if( rc==SQLITE_OK && oldMmapSize>0 ){
|
|
+ if( oldMmapSize>nByte ){
|
|
+ winMapfile(pFile, -1);
|
|
+ }else{
|
|
+ winMapfile(pFile, oldMmapSize);
|
|
+ }
|
|
}
|
|
#endif
|
|
|
|
@@ -41798,6 +44014,14 @@
|
|
if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
|
newLimit = sqlite3GlobalConfig.mxMmap;
|
|
}
|
|
+
|
|
+ /* The value of newLimit may be eventually cast to (SIZE_T) and passed
|
|
+ ** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at
|
|
+ ** least a 64-bit type. */
|
|
+ if( newLimit>0 && sizeof(SIZE_T)<8 ){
|
|
+ newLimit = (newLimit & 0x7FFFFFFF);
|
|
+ }
|
|
+
|
|
*(i64*)pArg = pFile->mmapSizeMax;
|
|
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
|
pFile->mmapSizeMax = newLimit;
|
|
@@ -41862,15 +44086,16 @@
|
|
** assert( winShmMutexHeld() );
|
|
** winShmLeaveMutex()
|
|
*/
|
|
+static sqlite3_mutex *winBigLock = 0;
|
|
static void winShmEnterMutex(void){
|
|
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
|
+ sqlite3_mutex_enter(winBigLock);
|
|
}
|
|
static void winShmLeaveMutex(void){
|
|
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
|
+ sqlite3_mutex_leave(winBigLock);
|
|
}
|
|
#ifndef NDEBUG
|
|
static int winShmMutexHeld(void) {
|
|
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
|
+ return sqlite3_mutex_held(winBigLock);
|
|
}
|
|
#endif
|
|
|
|
@@ -41904,6 +44129,9 @@
|
|
|
|
int szRegion; /* Size of shared-memory regions */
|
|
int nRegion; /* Size of array apRegion */
|
|
+ u8 isReadonly; /* True if read-only */
|
|
+ u8 isUnlocked; /* True if no DMS lock held */
|
|
+
|
|
struct ShmRegion {
|
|
HANDLE hMap; /* File handle from CreateFileMapping */
|
|
void *pMap;
|
|
@@ -41970,7 +44198,7 @@
|
|
int rc = 0; /* Result code form Lock/UnlockFileEx() */
|
|
|
|
/* Access to the winShmNode object is serialized by the caller */
|
|
- assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
|
|
+ assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) );
|
|
|
|
OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
|
|
pFile->hFile.h, lockType, ofst, nByte));
|
|
@@ -42052,6 +44280,37 @@
|
|
}
|
|
|
|
/*
|
|
+** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
|
|
+** take it now. Return SQLITE_OK if successful, or an SQLite error
|
|
+** code otherwise.
|
|
+**
|
|
+** If the DMS cannot be locked because this is a readonly_shm=1
|
|
+** connection and no other process already holds a lock, return
|
|
+** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
|
|
+*/
|
|
+static int winLockSharedMemory(winShmNode *pShmNode){
|
|
+ int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pShmNode->isReadonly ){
|
|
+ pShmNode->isUnlocked = 1;
|
|
+ winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
|
+ return SQLITE_READONLY_CANTINIT;
|
|
+ }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
|
|
+ winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
|
+ return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
|
|
+ "winLockSharedMemory", pShmNode->zFilename);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
|
+ }
|
|
+
|
|
+ return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
|
|
+}
|
|
+
|
|
+/*
|
|
** Open the shared-memory area associated with database file pDbFd.
|
|
**
|
|
** When opening a new shared-memory file, if no other instances of that
|
|
@@ -42060,9 +44319,9 @@
|
|
*/
|
|
static int winOpenSharedMemory(winFile *pDbFd){
|
|
struct winShm *p; /* The connection to be opened */
|
|
- struct winShmNode *pShmNode = 0; /* The underlying mmapped file */
|
|
- int rc; /* Result code */
|
|
- struct winShmNode *pNew; /* Newly allocated winShmNode */
|
|
+ winShmNode *pShmNode = 0; /* The underlying mmapped file */
|
|
+ int rc = SQLITE_OK; /* Result code */
|
|
+ winShmNode *pNew; /* Newly allocated winShmNode */
|
|
int nName; /* Size of zName in bytes */
|
|
|
|
assert( pDbFd->pShm==0 ); /* Not previously opened */
|
|
@@ -42095,6 +44354,9 @@
|
|
if( pShmNode ){
|
|
sqlite3_free(pNew);
|
|
}else{
|
|
+ int inFlags = SQLITE_OPEN_WAL;
|
|
+ int outFlags = 0;
|
|
+
|
|
pShmNode = pNew;
|
|
pNew = 0;
|
|
((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
|
|
@@ -42109,30 +44371,23 @@
|
|
}
|
|
}
|
|
|
|
- rc = winOpen(pDbFd->pVfs,
|
|
- pShmNode->zFilename, /* Name of the file (UTF-8) */
|
|
- (sqlite3_file*)&pShmNode->hFile, /* File handle here */
|
|
- SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
|
|
- 0);
|
|
- if( SQLITE_OK!=rc ){
|
|
+ if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
|
+ inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
|
+ }else{
|
|
+ inFlags |= SQLITE_OPEN_READONLY;
|
|
+ }
|
|
+ rc = winOpen(pDbFd->pVfs, pShmNode->zFilename,
|
|
+ (sqlite3_file*)&pShmNode->hFile,
|
|
+ inFlags, &outFlags);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ rc = winLogError(rc, osGetLastError(), "winOpenShm",
|
|
+ pShmNode->zFilename);
|
|
goto shm_open_err;
|
|
}
|
|
+ if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1;
|
|
|
|
- /* Check to see if another process is holding the dead-man switch.
|
|
- ** If not, truncate the file to zero length.
|
|
- */
|
|
- if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
|
|
- rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
|
|
- if( rc!=SQLITE_OK ){
|
|
- rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
|
|
- "winOpenShm", pDbFd->zPath);
|
|
- }
|
|
- }
|
|
- if( rc==SQLITE_OK ){
|
|
- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
|
- rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
|
|
- }
|
|
- if( rc ) goto shm_open_err;
|
|
+ rc = winLockSharedMemory(pShmNode);
|
|
+ if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
|
|
}
|
|
|
|
/* Make the new connection a child of the winShmNode */
|
|
@@ -42155,7 +44410,7 @@
|
|
p->pNext = pShmNode->pFirst;
|
|
pShmNode->pFirst = p;
|
|
sqlite3_mutex_leave(pShmNode->mutex);
|
|
- return SQLITE_OK;
|
|
+ return rc;
|
|
|
|
/* Jump here on any error */
|
|
shm_open_err:
|
|
@@ -42359,6 +44614,8 @@
|
|
winFile *pDbFd = (winFile*)fd;
|
|
winShm *pShm = pDbFd->pShm;
|
|
winShmNode *pShmNode;
|
|
+ DWORD protect = PAGE_READWRITE;
|
|
+ DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ;
|
|
int rc = SQLITE_OK;
|
|
|
|
if( !pShm ){
|
|
@@ -42369,6 +44626,11 @@
|
|
pShmNode = pShm->pShmNode;
|
|
|
|
sqlite3_mutex_enter(pShmNode->mutex);
|
|
+ if( pShmNode->isUnlocked ){
|
|
+ rc = winLockSharedMemory(pShmNode);
|
|
+ if( rc!=SQLITE_OK ) goto shmpage_out;
|
|
+ pShmNode->isUnlocked = 0;
|
|
+ }
|
|
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
|
|
|
|
if( pShmNode->nRegion<=iRegion ){
|
|
@@ -42415,6 +44677,11 @@
|
|
}
|
|
pShmNode->aRegion = apNew;
|
|
|
|
+ if( pShmNode->isReadonly ){
|
|
+ protect = PAGE_READONLY;
|
|
+ flags = FILE_MAP_READ;
|
|
+ }
|
|
+
|
|
while( pShmNode->nRegion<=iRegion ){
|
|
HANDLE hMap = NULL; /* file-mapping handle */
|
|
void *pMap = 0; /* Mapped memory region */
|
|
@@ -42421,15 +44688,15 @@
|
|
|
|
#if SQLITE_OS_WINRT
|
|
hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
|
|
- NULL, PAGE_READWRITE, nByte, NULL
|
|
+ NULL, protect, nByte, NULL
|
|
);
|
|
#elif defined(SQLITE_WIN32_HAS_WIDE)
|
|
hMap = osCreateFileMappingW(pShmNode->hFile.h,
|
|
- NULL, PAGE_READWRITE, 0, nByte, NULL
|
|
+ NULL, protect, 0, nByte, NULL
|
|
);
|
|
#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
|
|
hMap = osCreateFileMappingA(pShmNode->hFile.h,
|
|
- NULL, PAGE_READWRITE, 0, nByte, NULL
|
|
+ NULL, protect, 0, nByte, NULL
|
|
);
|
|
#endif
|
|
OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
|
|
@@ -42439,11 +44706,11 @@
|
|
int iOffset = pShmNode->nRegion*szRegion;
|
|
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
|
|
#if SQLITE_OS_WINRT
|
|
- pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
|
+ pMap = osMapViewOfFileFromApp(hMap, flags,
|
|
iOffset - iOffsetShift, szRegion + iOffsetShift
|
|
);
|
|
#else
|
|
- pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
|
+ pMap = osMapViewOfFile(hMap, flags,
|
|
0, iOffset - iOffsetShift, szRegion + iOffsetShift
|
|
);
|
|
#endif
|
|
@@ -42474,6 +44741,7 @@
|
|
}else{
|
|
*pp = 0;
|
|
}
|
|
+ if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
|
|
sqlite3_mutex_leave(pShmNode->mutex);
|
|
return rc;
|
|
}
|
|
@@ -42492,9 +44760,9 @@
|
|
static int winUnmapfile(winFile *pFile){
|
|
assert( pFile!=0 );
|
|
OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, "
|
|
- "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n",
|
|
+ "mmapSize=%lld, mmapSizeMax=%lld\n",
|
|
osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion,
|
|
- pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax));
|
|
+ pFile->mmapSize, pFile->mmapSizeMax));
|
|
if( pFile->pMapRegion ){
|
|
if( !osUnmapViewOfFile(pFile->pMapRegion) ){
|
|
pFile->lastErrno = osGetLastError();
|
|
@@ -42506,7 +44774,6 @@
|
|
}
|
|
pFile->pMapRegion = 0;
|
|
pFile->mmapSize = 0;
|
|
- pFile->mmapSizeActual = 0;
|
|
}
|
|
if( pFile->hMap!=NULL ){
|
|
if( !osCloseHandle(pFile->hMap) ){
|
|
@@ -42617,7 +44884,6 @@
|
|
}
|
|
pFd->pMapRegion = pNew;
|
|
pFd->mmapSize = nMap;
|
|
- pFd->mmapSizeActual = nMap;
|
|
}
|
|
|
|
OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
|
@@ -43110,6 +45376,14 @@
|
|
return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
|
|
+/* forward reference */
|
|
+static int winAccess(
|
|
+ sqlite3_vfs *pVfs, /* Not used on win32 */
|
|
+ const char *zFilename, /* Name of file to check */
|
|
+ int flags, /* Type of test to make on this file */
|
|
+ int *pResOut /* OUT: Result */
|
|
+);
|
|
+
|
|
/*
|
|
** Open a file.
|
|
*/
|
|
@@ -43286,37 +45560,58 @@
|
|
extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
|
|
extendedParameters.lpSecurityAttributes = NULL;
|
|
extendedParameters.hTemplateFile = NULL;
|
|
- while( (h = osCreateFile2((LPCWSTR)zConverted,
|
|
- dwDesiredAccess,
|
|
- dwShareMode,
|
|
- dwCreationDisposition,
|
|
- &extendedParameters))==INVALID_HANDLE_VALUE &&
|
|
- winRetryIoerr(&cnt, &lastErrno) ){
|
|
- /* Noop */
|
|
- }
|
|
+ do{
|
|
+ h = osCreateFile2((LPCWSTR)zConverted,
|
|
+ dwDesiredAccess,
|
|
+ dwShareMode,
|
|
+ dwCreationDisposition,
|
|
+ &extendedParameters);
|
|
+ if( h!=INVALID_HANDLE_VALUE ) break;
|
|
+ if( isReadWrite ){
|
|
+ int rc2, isRO = 0;
|
|
+ sqlite3BeginBenignMalloc();
|
|
+ rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
|
|
+ sqlite3EndBenignMalloc();
|
|
+ if( rc2==SQLITE_OK && isRO ) break;
|
|
+ }
|
|
+ }while( winRetryIoerr(&cnt, &lastErrno) );
|
|
#else
|
|
- while( (h = osCreateFileW((LPCWSTR)zConverted,
|
|
- dwDesiredAccess,
|
|
- dwShareMode, NULL,
|
|
- dwCreationDisposition,
|
|
- dwFlagsAndAttributes,
|
|
- NULL))==INVALID_HANDLE_VALUE &&
|
|
- winRetryIoerr(&cnt, &lastErrno) ){
|
|
- /* Noop */
|
|
- }
|
|
+ do{
|
|
+ h = osCreateFileW((LPCWSTR)zConverted,
|
|
+ dwDesiredAccess,
|
|
+ dwShareMode, NULL,
|
|
+ dwCreationDisposition,
|
|
+ dwFlagsAndAttributes,
|
|
+ NULL);
|
|
+ if( h!=INVALID_HANDLE_VALUE ) break;
|
|
+ if( isReadWrite ){
|
|
+ int rc2, isRO = 0;
|
|
+ sqlite3BeginBenignMalloc();
|
|
+ rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
|
|
+ sqlite3EndBenignMalloc();
|
|
+ if( rc2==SQLITE_OK && isRO ) break;
|
|
+ }
|
|
+ }while( winRetryIoerr(&cnt, &lastErrno) );
|
|
#endif
|
|
}
|
|
#ifdef SQLITE_WIN32_HAS_ANSI
|
|
else{
|
|
- while( (h = osCreateFileA((LPCSTR)zConverted,
|
|
- dwDesiredAccess,
|
|
- dwShareMode, NULL,
|
|
- dwCreationDisposition,
|
|
- dwFlagsAndAttributes,
|
|
- NULL))==INVALID_HANDLE_VALUE &&
|
|
- winRetryIoerr(&cnt, &lastErrno) ){
|
|
- /* Noop */
|
|
- }
|
|
+ do{
|
|
+ h = osCreateFileA((LPCSTR)zConverted,
|
|
+ dwDesiredAccess,
|
|
+ dwShareMode, NULL,
|
|
+ dwCreationDisposition,
|
|
+ dwFlagsAndAttributes,
|
|
+ NULL);
|
|
+ if( h!=INVALID_HANDLE_VALUE ) break;
|
|
+ if( isReadWrite ){
|
|
+ int rc2, isRO = 0;
|
|
+ sqlite3BeginBenignMalloc();
|
|
+ rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
|
|
+ sqlite3EndBenignMalloc();
|
|
+ if( rc2==SQLITE_OK && isRO ) break;
|
|
+ }
|
|
+ }while( winRetryIoerr(&cnt, &lastErrno) );
|
|
}
|
|
#endif
|
|
winLogIoerr(cnt, __LINE__);
|
|
@@ -43325,8 +45620,6 @@
|
|
dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
|
|
|
|
if( h==INVALID_HANDLE_VALUE ){
|
|
- pFile->lastErrno = lastErrno;
|
|
- winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
|
sqlite3_free(zConverted);
|
|
sqlite3_free(zTmpname);
|
|
if( isReadWrite && !isExclusive ){
|
|
@@ -43335,6 +45628,8 @@
|
|
~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
|
|
pOutFlags);
|
|
}else{
|
|
+ pFile->lastErrno = lastErrno;
|
|
+ winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
|
return SQLITE_CANTOPEN_BKPT;
|
|
}
|
|
}
|
|
@@ -43390,7 +45685,6 @@
|
|
pFile->hMap = NULL;
|
|
pFile->pMapRegion = 0;
|
|
pFile->mmapSize = 0;
|
|
- pFile->mmapSizeActual = 0;
|
|
pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap;
|
|
#endif
|
|
|
|
@@ -43927,9 +46221,6 @@
|
|
EntropyGatherer e;
|
|
UNUSED_PARAMETER(pVfs);
|
|
memset(zBuf, 0, nBuf);
|
|
-#if defined(_MSC_VER) && _MSC_VER>=1400 && !SQLITE_OS_WINCE
|
|
- rand_s((unsigned int*)zBuf); /* rand_s() is not available with MinGW */
|
|
-#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */
|
|
e.a = (unsigned char*)zBuf;
|
|
e.na = nBuf;
|
|
e.nXor = 0;
|
|
@@ -44224,6 +46515,10 @@
|
|
sqlite3_vfs_register(&winLongPathNolockVfs, 0);
|
|
#endif
|
|
|
|
+#ifndef SQLITE_OMIT_WAL
|
|
+ winBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
|
|
+#endif
|
|
+
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -44234,6 +46529,11 @@
|
|
sleepObj = NULL;
|
|
}
|
|
#endif
|
|
+
|
|
+#ifndef SQLITE_OMIT_WAL
|
|
+ winBigLock = 0;
|
|
+#endif
|
|
+
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -44240,6 +46540,598 @@
|
|
#endif /* SQLITE_OS_WIN */
|
|
|
|
/************** End of os_win.c **********************************************/
|
|
+/************** Begin file memdb.c *******************************************/
|
|
+/*
|
|
+** 2016-09-07
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements an in-memory VFS. A database is held as a contiguous
|
|
+** block of memory.
|
|
+**
|
|
+** This file also implements interface sqlite3_serialize() and
|
|
+** sqlite3_deserialize().
|
|
+*/
|
|
+/* #include "sqliteInt.h" */
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+
|
|
+/*
|
|
+** Forward declaration of objects used by this utility
|
|
+*/
|
|
+typedef struct sqlite3_vfs MemVfs;
|
|
+typedef struct MemFile MemFile;
|
|
+
|
|
+/* Access to a lower-level VFS that (might) implement dynamic loading,
|
|
+** access to randomness, etc.
|
|
+*/
|
|
+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
|
+
|
|
+/* An open file */
|
|
+struct MemFile {
|
|
+ sqlite3_file base; /* IO methods */
|
|
+ sqlite3_int64 sz; /* Size of the file */
|
|
+ sqlite3_int64 szMax; /* Space allocated to aData */
|
|
+ unsigned char *aData; /* content of the file */
|
|
+ int nMmap; /* Number of memory mapped pages */
|
|
+ unsigned mFlags; /* Flags */
|
|
+ int eLock; /* Most recent lock against this file */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Methods for MemFile
|
|
+*/
|
|
+static int memdbClose(sqlite3_file*);
|
|
+static int memdbRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
+static int memdbWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
+static int memdbTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
+static int memdbSync(sqlite3_file*, int flags);
|
|
+static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
+static int memdbLock(sqlite3_file*, int);
|
|
+/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */
|
|
+static int memdbFileControl(sqlite3_file*, int op, void *pArg);
|
|
+/* static int memdbSectorSize(sqlite3_file*); // not used */
|
|
+static int memdbDeviceCharacteristics(sqlite3_file*);
|
|
+static int memdbFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
|
+static int memdbUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
|
+
|
|
+/*
|
|
+** Methods for MemVfs
|
|
+*/
|
|
+static int memdbOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
+/* static int memdbDelete(sqlite3_vfs*, const char *zName, int syncDir); */
|
|
+static int memdbAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
+static int memdbFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
+static void *memdbDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
+static void memdbDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
+static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
+static void memdbDlClose(sqlite3_vfs*, void*);
|
|
+static int memdbRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
+static int memdbSleep(sqlite3_vfs*, int microseconds);
|
|
+/* static int memdbCurrentTime(sqlite3_vfs*, double*); */
|
|
+static int memdbGetLastError(sqlite3_vfs*, int, char *);
|
|
+static int memdbCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
+
|
|
+static sqlite3_vfs memdb_vfs = {
|
|
+ 2, /* iVersion */
|
|
+ 0, /* szOsFile (set when registered) */
|
|
+ 1024, /* mxPathname */
|
|
+ 0, /* pNext */
|
|
+ "memdb", /* zName */
|
|
+ 0, /* pAppData (set when registered) */
|
|
+ memdbOpen, /* xOpen */
|
|
+ 0, /* memdbDelete, */ /* xDelete */
|
|
+ memdbAccess, /* xAccess */
|
|
+ memdbFullPathname, /* xFullPathname */
|
|
+ memdbDlOpen, /* xDlOpen */
|
|
+ memdbDlError, /* xDlError */
|
|
+ memdbDlSym, /* xDlSym */
|
|
+ memdbDlClose, /* xDlClose */
|
|
+ memdbRandomness, /* xRandomness */
|
|
+ memdbSleep, /* xSleep */
|
|
+ 0, /* memdbCurrentTime, */ /* xCurrentTime */
|
|
+ memdbGetLastError, /* xGetLastError */
|
|
+ memdbCurrentTimeInt64 /* xCurrentTimeInt64 */
|
|
+};
|
|
+
|
|
+static const sqlite3_io_methods memdb_io_methods = {
|
|
+ 3, /* iVersion */
|
|
+ memdbClose, /* xClose */
|
|
+ memdbRead, /* xRead */
|
|
+ memdbWrite, /* xWrite */
|
|
+ memdbTruncate, /* xTruncate */
|
|
+ memdbSync, /* xSync */
|
|
+ memdbFileSize, /* xFileSize */
|
|
+ memdbLock, /* xLock */
|
|
+ memdbLock, /* xUnlock - same as xLock in this case */
|
|
+ 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */
|
|
+ memdbFileControl, /* xFileControl */
|
|
+ 0, /* memdbSectorSize,*/ /* xSectorSize */
|
|
+ memdbDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
+ 0, /* xShmMap */
|
|
+ 0, /* xShmLock */
|
|
+ 0, /* xShmBarrier */
|
|
+ 0, /* xShmUnmap */
|
|
+ memdbFetch, /* xFetch */
|
|
+ memdbUnfetch /* xUnfetch */
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Close an memdb-file.
|
|
+**
|
|
+** The pData pointer is owned by the application, so there is nothing
|
|
+** to free.
|
|
+*/
|
|
+static int memdbClose(sqlite3_file *pFile){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Read data from an memdb-file.
|
|
+*/
|
|
+static int memdbRead(
|
|
+ sqlite3_file *pFile,
|
|
+ void *zBuf,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( iOfst+iAmt>p->sz ){
|
|
+ memset(zBuf, 0, iAmt);
|
|
+ if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst);
|
|
+ return SQLITE_IOERR_SHORT_READ;
|
|
+ }
|
|
+ memcpy(zBuf, p->aData+iOfst, iAmt);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Try to enlarge the memory allocation to hold at least sz bytes
|
|
+*/
|
|
+static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){
|
|
+ unsigned char *pNew;
|
|
+ if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){
|
|
+ return SQLITE_FULL;
|
|
+ }
|
|
+ pNew = sqlite3_realloc64(p->aData, newSz);
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ p->aData = pNew;
|
|
+ p->szMax = newSz;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Write data to an memdb-file.
|
|
+*/
|
|
+static int memdbWrite(
|
|
+ sqlite3_file *pFile,
|
|
+ const void *z,
|
|
+ int iAmt,
|
|
+ sqlite_int64 iOfst
|
|
+){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( iOfst+iAmt>p->sz ){
|
|
+ int rc;
|
|
+ if( iOfst+iAmt>p->szMax
|
|
+ && (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK
|
|
+ ){
|
|
+ return rc;
|
|
+ }
|
|
+ if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz);
|
|
+ p->sz = iOfst+iAmt;
|
|
+ }
|
|
+ memcpy(p->aData+iOfst, z, iAmt);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Truncate an memdb-file.
|
|
+**
|
|
+** In rollback mode (which is always the case for memdb, as it does not
|
|
+** support WAL mode) the truncate() method is only used to reduce
|
|
+** the size of a file, never to increase the size.
|
|
+*/
|
|
+static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ if( NEVER(size>p->sz) ) return SQLITE_FULL;
|
|
+ p->sz = size;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sync an memdb-file.
|
|
+*/
|
|
+static int memdbSync(sqlite3_file *pFile, int flags){
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the current file-size of an memdb-file.
|
|
+*/
|
|
+static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ *pSize = p->sz;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Lock an memdb-file.
|
|
+*/
|
|
+static int memdbLock(sqlite3_file *pFile, int eLock){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ p->eLock = eLock;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+#if 0 /* Never used because memdbAccess() always returns false */
|
|
+/*
|
|
+** Check if another file-handle holds a RESERVED lock on an memdb-file.
|
|
+*/
|
|
+static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
+ *pResOut = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** File control method. For custom operations on an memdb-file.
|
|
+*/
|
|
+static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ int rc = SQLITE_NOTFOUND;
|
|
+ if( op==SQLITE_FCNTL_VFSNAME ){
|
|
+ *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz);
|
|
+ rc = SQLITE_OK;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
|
+/*
|
|
+** Return the sector-size in bytes for an memdb-file.
|
|
+*/
|
|
+static int memdbSectorSize(sqlite3_file *pFile){
|
|
+ return 1024;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Return the device characteristic flags supported by an memdb-file.
|
|
+*/
|
|
+static int memdbDeviceCharacteristics(sqlite3_file *pFile){
|
|
+ return SQLITE_IOCAP_ATOMIC |
|
|
+ SQLITE_IOCAP_POWERSAFE_OVERWRITE |
|
|
+ SQLITE_IOCAP_SAFE_APPEND |
|
|
+ SQLITE_IOCAP_SEQUENTIAL;
|
|
+}
|
|
+
|
|
+/* Fetch a page of a memory-mapped file */
|
|
+static int memdbFetch(
|
|
+ sqlite3_file *pFile,
|
|
+ sqlite3_int64 iOfst,
|
|
+ int iAmt,
|
|
+ void **pp
|
|
+){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ p->nMmap++;
|
|
+ *pp = (void*)(p->aData + iOfst);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Release a memory-mapped page */
|
|
+static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
|
+ MemFile *p = (MemFile *)pFile;
|
|
+ p->nMmap--;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open an mem file handle.
|
|
+*/
|
|
+static int memdbOpen(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zName,
|
|
+ sqlite3_file *pFile,
|
|
+ int flags,
|
|
+ int *pOutFlags
|
|
+){
|
|
+ MemFile *p = (MemFile*)pFile;
|
|
+ if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
|
|
+ return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags);
|
|
+ }
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE;
|
|
+ assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */
|
|
+ *pOutFlags = flags | SQLITE_OPEN_MEMORY;
|
|
+ p->base.pMethods = &memdb_io_methods;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+#if 0 /* Only used to delete rollback journals, master journals, and WAL
|
|
+ ** files, none of which exist in memdb. So this routine is never used */
|
|
+/*
|
|
+** Delete the file located at zPath. If the dirSync argument is true,
|
|
+** ensure the file-system modifications are synced to disk before
|
|
+** returning.
|
|
+*/
|
|
+static int memdbDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
+ return SQLITE_IOERR_DELETE;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Test for access permissions. Return true if the requested permission
|
|
+** is available, or false otherwise.
|
|
+**
|
|
+** With memdb, no files ever exist on disk. So always return false.
|
|
+*/
|
|
+static int memdbAccess(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int flags,
|
|
+ int *pResOut
|
|
+){
|
|
+ *pResOut = 0;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Populate buffer zOut with the full canonical pathname corresponding
|
|
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
|
|
+** of at least (INST_MAX_PATHNAME+1) bytes.
|
|
+*/
|
|
+static int memdbFullPathname(
|
|
+ sqlite3_vfs *pVfs,
|
|
+ const char *zPath,
|
|
+ int nOut,
|
|
+ char *zOut
|
|
+){
|
|
+ sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open the dynamic library located at zPath and return a handle.
|
|
+*/
|
|
+static void *memdbDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
|
|
+** utf-8 string describing the most recent error encountered associated
|
|
+** with dynamic libraries.
|
|
+*/
|
|
+static void memdbDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
|
+*/
|
|
+static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close the dynamic library handle pHandle.
|
|
+*/
|
|
+static void memdbDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
+** random data.
|
|
+*/
|
|
+static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sleep for nMicro microseconds. Return the number of microseconds
|
|
+** actually slept.
|
|
+*/
|
|
+static int memdbSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
|
+}
|
|
+
|
|
+#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */
|
|
+/*
|
|
+** Return the current time as a Julian Day number in *pTimeOut.
|
|
+*/
|
|
+static int memdbCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int memdbGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
|
+}
|
|
+static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
+ return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Translate a database connection pointer and schema name into a
|
|
+** MemFile pointer.
|
|
+*/
|
|
+static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){
|
|
+ MemFile *p = 0;
|
|
+ int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p);
|
|
+ if( rc ) return 0;
|
|
+ if( p->base.pMethods!=&memdb_io_methods ) return 0;
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the serialization of a database
|
|
+*/
|
|
+SQLITE_API unsigned char *sqlite3_serialize(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which database within the connection */
|
|
+ sqlite3_int64 *piSize, /* Write size here, if not NULL */
|
|
+ unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */
|
|
+){
|
|
+ MemFile *p;
|
|
+ int iDb;
|
|
+ Btree *pBt;
|
|
+ sqlite3_int64 sz;
|
|
+ int szPage = 0;
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ unsigned char *pOut;
|
|
+ char *zSql;
|
|
+ int rc;
|
|
+
|
|
+#ifdef SQLITE_ENABLE_API_ARMOR
|
|
+ if( !sqlite3SafetyCheckOk(db) ){
|
|
+ (void)SQLITE_MISUSE_BKPT;
|
|
+ return 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
|
|
+ p = memdbFromDbSchema(db, zSchema);
|
|
+ iDb = sqlite3FindDbName(db, zSchema);
|
|
+ if( piSize ) *piSize = -1;
|
|
+ if( iDb<0 ) return 0;
|
|
+ if( p ){
|
|
+ if( piSize ) *piSize = p->sz;
|
|
+ if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
|
|
+ pOut = p->aData;
|
|
+ }else{
|
|
+ pOut = sqlite3_malloc64( p->sz );
|
|
+ if( pOut ) memcpy(pOut, p->aData, p->sz);
|
|
+ }
|
|
+ return pOut;
|
|
+ }
|
|
+ pBt = db->aDb[iDb].pBt;
|
|
+ if( pBt==0 ) return 0;
|
|
+ szPage = sqlite3BtreeGetPageSize(pBt);
|
|
+ zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema);
|
|
+ rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM;
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) return 0;
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ if( rc!=SQLITE_ROW ){
|
|
+ pOut = 0;
|
|
+ }else{
|
|
+ sz = sqlite3_column_int64(pStmt, 0)*szPage;
|
|
+ if( piSize ) *piSize = sz;
|
|
+ if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
|
|
+ pOut = 0;
|
|
+ }else{
|
|
+ pOut = sqlite3_malloc64( sz );
|
|
+ if( pOut ){
|
|
+ int nPage = sqlite3_column_int(pStmt, 0);
|
|
+ Pager *pPager = sqlite3BtreePager(pBt);
|
|
+ int pgno;
|
|
+ for(pgno=1; pgno<=nPage; pgno++){
|
|
+ DbPage *pPage = 0;
|
|
+ unsigned char *pTo = pOut + szPage*(sqlite3_int64)(pgno-1);
|
|
+ rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ memcpy(pTo, sqlite3PagerGetData(pPage), szPage);
|
|
+ }else{
|
|
+ memset(pTo, 0, szPage);
|
|
+ }
|
|
+ sqlite3PagerUnref(pPage);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_finalize(pStmt);
|
|
+ return pOut;
|
|
+}
|
|
+
|
|
+/* Convert zSchema to a MemDB and initialize its content.
|
|
+*/
|
|
+SQLITE_API int sqlite3_deserialize(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which DB to reopen with the deserialization */
|
|
+ unsigned char *pData, /* The serialized database content */
|
|
+ sqlite3_int64 szDb, /* Number bytes in the deserialization */
|
|
+ sqlite3_int64 szBuf, /* Total size of buffer pData[] */
|
|
+ unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */
|
|
+){
|
|
+ MemFile *p;
|
|
+ char *zSql;
|
|
+ sqlite3_stmt *pStmt = 0;
|
|
+ int rc;
|
|
+ int iDb;
|
|
+
|
|
+#ifdef SQLITE_ENABLE_API_ARMOR
|
|
+ if( !sqlite3SafetyCheckOk(db) ){
|
|
+ return SQLITE_MISUSE_BKPT;
|
|
+ }
|
|
+ if( szDb<0 ) return SQLITE_MISUSE_BKPT;
|
|
+ if( szBuf<0 ) return SQLITE_MISUSE_BKPT;
|
|
+#endif
|
|
+
|
|
+ sqlite3_mutex_enter(db->mutex);
|
|
+ if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
|
|
+ iDb = sqlite3FindDbName(db, zSchema);
|
|
+ if( iDb<0 ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto end_deserialize;
|
|
+ }
|
|
+ zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema);
|
|
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) goto end_deserialize;
|
|
+ db->init.iDb = (u8)iDb;
|
|
+ db->init.reopenMemdb = 1;
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ db->init.reopenMemdb = 0;
|
|
+ if( rc!=SQLITE_DONE ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto end_deserialize;
|
|
+ }
|
|
+ p = memdbFromDbSchema(db, zSchema);
|
|
+ if( p==0 ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else{
|
|
+ p->aData = pData;
|
|
+ p->sz = szDb;
|
|
+ p->szMax = szBuf;
|
|
+ p->mFlags = mFlags;
|
|
+ rc = SQLITE_OK;
|
|
+ }
|
|
+
|
|
+end_deserialize:
|
|
+ sqlite3_finalize(pStmt);
|
|
+ sqlite3_mutex_leave(db->mutex);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This routine is called when the extension is loaded.
|
|
+** Register the new VFS.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3MemdbInit(void){
|
|
+ sqlite3_vfs *pLower = sqlite3_vfs_find(0);
|
|
+ int sz = pLower->szOsFile;
|
|
+ memdb_vfs.pAppData = pLower;
|
|
+ /* In all known configurations of SQLite, the size of a default
|
|
+ ** sqlite3_file is greater than the size of a memdb sqlite3_file.
|
|
+ ** Should that ever change, remove the following NEVER() */
|
|
+ if( NEVER(sz<sizeof(MemFile)) ) sz = sizeof(MemFile);
|
|
+ memdb_vfs.szOsFile = sz;
|
|
+ return sqlite3_vfs_register(&memdb_vfs, 0);
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_DESERIALIZE */
|
|
+
|
|
+/************** End of memdb.c ***********************************************/
|
|
/************** Begin file bitvec.c ******************************************/
|
|
/*
|
|
** 2008 February 16
|
|
@@ -44689,7 +47581,7 @@
|
|
** The PCache.pSynced variable is used to optimize searching for a dirty
|
|
** page to eject from the cache mid-transaction. It is better to eject
|
|
** a page that does not require a journal sync than one that does.
|
|
-** Therefore, pSynced is maintained to that it *almost* always points
|
|
+** Therefore, pSynced is maintained so that it *almost* always points
|
|
** to either the oldest page in the pDirty/pDirtyTail list that has a
|
|
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
|
|
** (so that the right page to eject can be found by following pDirtyPrev
|
|
@@ -44848,12 +47740,9 @@
|
|
p->eCreate = 2;
|
|
}
|
|
}
|
|
- pPage->pDirtyNext = 0;
|
|
- pPage->pDirtyPrev = 0;
|
|
}
|
|
if( addRemove & PCACHE_DIRTYLIST_ADD ){
|
|
- assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
|
|
-
|
|
+ pPage->pDirtyPrev = 0;
|
|
pPage->pDirtyNext = p->pDirty;
|
|
if( pPage->pDirtyNext ){
|
|
assert( pPage->pDirtyNext->pDirtyPrev==0 );
|
|
@@ -45091,7 +47980,7 @@
|
|
sqlite3_log(SQLITE_FULL,
|
|
"spill page %d making room for %d - cache used: %d/%d",
|
|
pPg->pgno, pgno,
|
|
- sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
|
+ sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache),
|
|
numberOfCachePages(pCache));
|
|
#endif
|
|
pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
|
|
@@ -45170,11 +48059,7 @@
|
|
if( (--p->nRef)==0 ){
|
|
if( p->flags&PGHDR_CLEAN ){
|
|
pcacheUnpin(p);
|
|
- }else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/
|
|
- /* Move the page to the head of the dirty list. If p->pDirtyPrev==0,
|
|
- ** then page p is already at the head of the dirty list and the
|
|
- ** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE
|
|
- ** tag above. */
|
|
+ }else{
|
|
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
|
}
|
|
}
|
|
@@ -45230,16 +48115,15 @@
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
|
|
assert( sqlite3PcachePageSanity(p) );
|
|
- if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){
|
|
- assert( (p->flags & PGHDR_CLEAN)==0 );
|
|
- pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
|
- p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
|
- p->flags |= PGHDR_CLEAN;
|
|
- pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
|
|
- assert( sqlite3PcachePageSanity(p) );
|
|
- if( p->nRef==0 ){
|
|
- pcacheUnpin(p);
|
|
- }
|
|
+ assert( (p->flags & PGHDR_DIRTY)!=0 );
|
|
+ assert( (p->flags & PGHDR_CLEAN)==0 );
|
|
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
|
+ p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
|
+ p->flags |= PGHDR_CLEAN;
|
|
+ pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
|
|
+ assert( sqlite3PcachePageSanity(p) );
|
|
+ if( p->nRef==0 ){
|
|
+ pcacheUnpin(p);
|
|
}
|
|
}
|
|
|
|
@@ -45521,6 +48405,15 @@
|
|
return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
|
|
}
|
|
|
|
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
+/*
|
|
+** Return true if there are one or more dirty pages in the cache. Else false.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache){
|
|
+ return (pCache->pDirty!=0);
|
|
+}
|
|
+#endif
|
|
+
|
|
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
|
|
/*
|
|
** For all dirty pages currently in the cache, invoke the specified
|
|
@@ -45635,7 +48528,6 @@
|
|
struct PgHdr1 {
|
|
sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
|
|
unsigned int iKey; /* Key value (page number) */
|
|
- u8 isPinned; /* Page in use, not on the LRU list */
|
|
u8 isBulkLocal; /* This page from bulk local storage */
|
|
u8 isAnchor; /* This is the PGroup.lru element */
|
|
PgHdr1 *pNext; /* Next in hash table chain */
|
|
@@ -45644,6 +48536,13 @@
|
|
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
|
|
};
|
|
|
|
+/*
|
|
+** A page is pinned if it is not on the LRU list. To be "pinned" means
|
|
+** that the page is in active use and must not be deallocated.
|
|
+*/
|
|
+#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
|
|
+#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
|
|
+
|
|
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
|
** of one or more PCaches that are able to recycle each other's unpinned
|
|
** pages when they are under memory pressure. A PGroup is an instance of
|
|
@@ -45671,7 +48570,7 @@
|
|
unsigned int nMaxPage; /* Sum of nMax for purgeable caches */
|
|
unsigned int nMinPage; /* Sum of nMin for purgeable caches */
|
|
unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
|
|
- unsigned int nCurrentPage; /* Number of purgeable pages allocated */
|
|
+ unsigned int nPurgeable; /* Number of purgeable pages allocated */
|
|
PgHdr1 lru; /* The beginning and end of the LRU list */
|
|
};
|
|
|
|
@@ -45685,11 +48584,13 @@
|
|
*/
|
|
struct PCache1 {
|
|
/* Cache configuration parameters. Page size (szPage) and the purgeable
|
|
- ** flag (bPurgeable) are set when the cache is created. nMax may be
|
|
+ ** flag (bPurgeable) and the pnPurgeable pointer are all set when the
|
|
+ ** cache is created and are never changed thereafter. nMax may be
|
|
** modified at any time by a call to the pcache1Cachesize() method.
|
|
** The PGroup mutex must be held when accessing nMax.
|
|
*/
|
|
PGroup *pGroup; /* PGroup this cache belongs to */
|
|
+ unsigned int *pnPurgeable; /* Pointer to pGroup->nPurgeable */
|
|
int szPage; /* Size of database content section */
|
|
int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
|
|
int szAlloc; /* Total size of one pcache line */
|
|
@@ -45784,6 +48685,7 @@
|
|
if( pcache1.isInit ){
|
|
PgFreeslot *p;
|
|
if( pBuf==0 ) sz = n = 0;
|
|
+ if( n==0 ) sz = 0;
|
|
sz = ROUNDDOWN8(sz);
|
|
pcache1.szSlot = sz;
|
|
pcache1.nSlot = pcache1.nFreeSlot = n;
|
|
@@ -45976,9 +48878,7 @@
|
|
p->isBulkLocal = 0;
|
|
p->isAnchor = 0;
|
|
}
|
|
- if( pCache->bPurgeable ){
|
|
- pCache->pGroup->nCurrentPage++;
|
|
- }
|
|
+ (*pCache->pnPurgeable)++;
|
|
return p;
|
|
}
|
|
|
|
@@ -45999,9 +48899,7 @@
|
|
sqlite3_free(p);
|
|
#endif
|
|
}
|
|
- if( pCache->bPurgeable ){
|
|
- pCache->pGroup->nCurrentPage--;
|
|
- }
|
|
+ (*pCache->pnPurgeable)--;
|
|
}
|
|
|
|
/*
|
|
@@ -46096,22 +48994,18 @@
|
|
** The PGroup mutex must be held when this function is called.
|
|
*/
|
|
static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
|
|
- PCache1 *pCache;
|
|
-
|
|
assert( pPage!=0 );
|
|
- assert( pPage->isPinned==0 );
|
|
- pCache = pPage->pCache;
|
|
+ assert( PAGE_IS_UNPINNED(pPage) );
|
|
assert( pPage->pLruNext );
|
|
assert( pPage->pLruPrev );
|
|
- assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
|
+ assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) );
|
|
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
|
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
|
pPage->pLruNext = 0;
|
|
pPage->pLruPrev = 0;
|
|
- pPage->isPinned = 1;
|
|
assert( pPage->isAnchor==0 );
|
|
- assert( pCache->pGroup->lru.isAnchor==1 );
|
|
- pCache->nRecyclable--;
|
|
+ assert( pPage->pCache->pGroup->lru.isAnchor==1 );
|
|
+ pPage->pCache->nRecyclable--;
|
|
return pPage;
|
|
}
|
|
|
|
@@ -46145,11 +49039,11 @@
|
|
PGroup *pGroup = pCache->pGroup;
|
|
PgHdr1 *p;
|
|
assert( sqlite3_mutex_held(pGroup->mutex) );
|
|
- while( pGroup->nCurrentPage>pGroup->nMaxPage
|
|
+ while( pGroup->nPurgeable>pGroup->nMaxPage
|
|
&& (p=pGroup->lru.pLruPrev)->isAnchor==0
|
|
){
|
|
assert( p->pCache->pGroup==pGroup );
|
|
- assert( p->isPinned==0 );
|
|
+ assert( PAGE_IS_UNPINNED(p) );
|
|
pcache1PinPage(p);
|
|
pcache1RemoveFromHash(p, 1);
|
|
}
|
|
@@ -46198,7 +49092,7 @@
|
|
if( pPage->iKey>=iLimit ){
|
|
pCache->nPage--;
|
|
*pp = pPage->pNext;
|
|
- if( !pPage->isPinned ) pcache1PinPage(pPage);
|
|
+ if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);
|
|
pcache1FreePage(pPage);
|
|
}else{
|
|
pp = &pPage->pNext;
|
|
@@ -46316,6 +49210,10 @@
|
|
pCache->nMin = 10;
|
|
pGroup->nMinPage += pCache->nMin;
|
|
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
|
|
+ pCache->pnPurgeable = &pGroup->nPurgeable;
|
|
+ }else{
|
|
+ static unsigned int dummyCurrentPage;
|
|
+ pCache->pnPurgeable = &dummyCurrentPage;
|
|
}
|
|
pcache1LeaveMutex(pGroup);
|
|
if( pCache->nHash==0 ){
|
|
@@ -46417,7 +49315,7 @@
|
|
){
|
|
PCache1 *pOther;
|
|
pPage = pGroup->lru.pLruPrev;
|
|
- assert( pPage->isPinned==0 );
|
|
+ assert( PAGE_IS_UNPINNED(pPage) );
|
|
pcache1RemoveFromHash(pPage, 0);
|
|
pcache1PinPage(pPage);
|
|
pOther = pPage->pCache;
|
|
@@ -46425,7 +49323,7 @@
|
|
pcache1FreePage(pPage);
|
|
pPage = 0;
|
|
}else{
|
|
- pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable);
|
|
+ pGroup->nPurgeable -= (pOther->bPurgeable - pCache->bPurgeable);
|
|
}
|
|
}
|
|
|
|
@@ -46444,7 +49342,6 @@
|
|
pPage->pCache = pCache;
|
|
pPage->pLruPrev = 0;
|
|
pPage->pLruNext = 0;
|
|
- pPage->isPinned = 1;
|
|
*(void **)pPage->page.pExtra = 0;
|
|
pCache->apHash[h] = pPage;
|
|
if( iKey>pCache->iMaxKey ){
|
|
@@ -46530,7 +49427,7 @@
|
|
** Otherwise (page not in hash and createFlag!=0) continue with
|
|
** subsequent steps to try to create the page. */
|
|
if( pPage ){
|
|
- if( !pPage->isPinned ){
|
|
+ if( PAGE_IS_UNPINNED(pPage) ){
|
|
return pcache1PinPage(pPage);
|
|
}else{
|
|
return pPage;
|
|
@@ -46605,9 +49502,9 @@
|
|
** part of the PGroup LRU list.
|
|
*/
|
|
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
|
- assert( pPage->isPinned==1 );
|
|
+ assert( PAGE_IS_PINNED(pPage) );
|
|
|
|
- if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
|
|
+ if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){
|
|
pcache1RemoveFromHash(pPage, 1);
|
|
}else{
|
|
/* Add the page to the PGroup LRU list. */
|
|
@@ -46616,7 +49513,6 @@
|
|
(pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
|
|
*ppFirst = pPage;
|
|
pCache->nRecyclable++;
|
|
- pPage->isPinned = 0;
|
|
}
|
|
|
|
pcache1LeaveMutex(pCache->pGroup);
|
|
@@ -46760,7 +49656,7 @@
|
|
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
|
nFree += sqlite3MemSize(p);
|
|
#endif
|
|
- assert( p->isPinned==0 );
|
|
+ assert( PAGE_IS_UNPINNED(p) );
|
|
pcache1PinPage(p);
|
|
pcache1RemoveFromHash(p, 1);
|
|
}
|
|
@@ -46784,10 +49680,10 @@
|
|
PgHdr1 *p;
|
|
int nRecyclable = 0;
|
|
for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
|
|
- assert( p->isPinned==0 );
|
|
+ assert( PAGE_IS_UNPINNED(p) );
|
|
nRecyclable++;
|
|
}
|
|
- *pnCurrent = pcache1.grp.nCurrentPage;
|
|
+ *pnCurrent = pcache1.grp.nPurgeable;
|
|
*pnMax = (int)pcache1.grp.nMaxPage;
|
|
*pnMin = (int)pcache1.grp.nMinPage;
|
|
*pnRecyclable = nRecyclable;
|
|
@@ -46922,30 +49818,23 @@
|
|
#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */
|
|
|
|
/*
|
|
-** Turn bulk memory into a RowSet object. N bytes of memory
|
|
-** are available at pSpace. The db pointer is used as a memory context
|
|
-** for any subsequent allocations that need to occur.
|
|
-** Return a pointer to the new RowSet object.
|
|
-**
|
|
-** It must be the case that N is sufficient to make a Rowset. If not
|
|
-** an assertion fault occurs.
|
|
-**
|
|
-** If N is larger than the minimum, use the surplus as an initial
|
|
-** allocation of entries available to be filled.
|
|
+** Allocate a RowSet object. Return NULL if a memory allocation
|
|
+** error occurs.
|
|
*/
|
|
-SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
|
|
- RowSet *p;
|
|
- assert( N >= ROUND8(sizeof(*p)) );
|
|
- p = pSpace;
|
|
- p->pChunk = 0;
|
|
- p->db = db;
|
|
- p->pEntry = 0;
|
|
- p->pLast = 0;
|
|
- p->pForest = 0;
|
|
- p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
|
|
- p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
|
|
- p->rsFlags = ROWSET_SORTED;
|
|
- p->iBatch = 0;
|
|
+SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3 *db){
|
|
+ RowSet *p = sqlite3DbMallocRawNN(db, sizeof(*p));
|
|
+ if( p ){
|
|
+ int N = sqlite3DbMallocSize(db, p);
|
|
+ p->pChunk = 0;
|
|
+ p->db = db;
|
|
+ p->pEntry = 0;
|
|
+ p->pLast = 0;
|
|
+ p->pForest = 0;
|
|
+ p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
|
|
+ p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
|
|
+ p->rsFlags = ROWSET_SORTED;
|
|
+ p->iBatch = 0;
|
|
+ }
|
|
return p;
|
|
}
|
|
|
|
@@ -46954,7 +49843,8 @@
|
|
** the RowSet has allocated over its lifetime. This routine is
|
|
** the destructor for the RowSet.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3RowSetClear(RowSet *p){
|
|
+SQLITE_PRIVATE void sqlite3RowSetClear(void *pArg){
|
|
+ RowSet *p = (RowSet*)pArg;
|
|
struct RowSetChunk *pChunk, *pNextChunk;
|
|
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
|
|
pNextChunk = pChunk->pNextChunk;
|
|
@@ -46969,6 +49859,16 @@
|
|
}
|
|
|
|
/*
|
|
+** Deallocate all chunks from a RowSet. This frees all memory that
|
|
+** the RowSet has allocated over its lifetime. This routine is
|
|
+** the destructor for the RowSet.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3RowSetDelete(void *pArg){
|
|
+ sqlite3RowSetClear(pArg);
|
|
+ sqlite3DbFree(((RowSet*)pArg)->db, pArg);
|
|
+}
|
|
+
|
|
+/*
|
|
** Allocate a new RowSetEntry object that is associated with the
|
|
** given RowSet. Return a pointer to the new and completely uninitialized
|
|
** objected.
|
|
@@ -47342,11 +50242,11 @@
|
|
|
|
/* #include "sqliteInt.h" */
|
|
|
|
-/* Additional values that can be added to the sync_flags argument of
|
|
-** sqlite3WalFrames():
|
|
+/* Macros for extracting appropriate sync flags for either transaction
|
|
+** commits (WAL_SYNC_FLAGS(X)) or for checkpoint ops (CKPT_SYNC_FLAGS(X)):
|
|
*/
|
|
-#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */
|
|
-#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */
|
|
+#define WAL_SYNC_FLAGS(X) ((X)&0x03)
|
|
+#define CKPT_SYNC_FLAGS(X) (((X)>>2)&0x03)
|
|
|
|
#ifdef SQLITE_OMIT_WAL
|
|
# define sqlite3WalOpen(x,y,z) 0
|
|
@@ -47455,6 +50355,8 @@
|
|
SQLITE_PRIVATE int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot);
|
|
SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot);
|
|
SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal);
|
|
+SQLITE_PRIVATE int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot);
|
|
+SQLITE_PRIVATE void sqlite3WalSnapshotUnlock(Wal *pWal);
|
|
#endif
|
|
|
|
#ifdef SQLITE_ENABLE_ZIPVFS
|
|
@@ -47579,8 +50481,8 @@
|
|
** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
|
|
** struct as its argument.
|
|
*/
|
|
-#define PAGERID(p) ((int)(p->fd))
|
|
-#define FILEHANDLEID(fd) ((int)fd)
|
|
+#define PAGERID(p) (SQLITE_PTR_TO_INT(p->fd))
|
|
+#define FILEHANDLEID(fd) (SQLITE_PTR_TO_INT(fd))
|
|
|
|
/*
|
|
** The Pager.eState variable stores the current 'state' of a pager. A
|
|
@@ -48067,6 +50969,18 @@
|
|
** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
|
|
** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
|
|
** sub-codes.
|
|
+**
|
|
+** syncFlags, walSyncFlags
|
|
+**
|
|
+** syncFlags is either SQLITE_SYNC_NORMAL (0x02) or SQLITE_SYNC_FULL (0x03).
|
|
+** syncFlags is used for rollback mode. walSyncFlags is used for WAL mode
|
|
+** and contains the flags used to sync the checkpoint operations in the
|
|
+** lower two bits, and sync flags used for transaction commits in the WAL
|
|
+** file in bits 0x04 and 0x08. In other words, to get the correct sync flags
|
|
+** for checkpoint operations, use (walSyncFlags&0x03) and to get the correct
|
|
+** sync flags for transaction commit, use ((walSyncFlags>>2)&0x03). Note
|
|
+** that with synchronous=NORMAL in WAL mode, transaction commit is not synced
|
|
+** meaning that the 0x04 and 0x08 bits are both zero.
|
|
*/
|
|
struct Pager {
|
|
sqlite3_vfs *pVfs; /* OS functions to use for IO */
|
|
@@ -48076,9 +50990,8 @@
|
|
u8 noSync; /* Do not sync the journal if true */
|
|
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
|
u8 extraSync; /* sync directory after journal delete */
|
|
- u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
|
|
- u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
|
|
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
|
|
+ u8 walSyncFlags; /* See description above */
|
|
u8 tempFile; /* zFilename is a temporary or immutable file */
|
|
u8 noLock; /* Do not lock (except in WAL mode) */
|
|
u8 readOnly; /* True for a read-only database */
|
|
@@ -48139,7 +51052,7 @@
|
|
char *zJournal; /* Name of the journal file */
|
|
int (*xBusyHandler)(void*); /* Function to call when busy */
|
|
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
|
|
- int aStat[3]; /* Total cache hits, misses and writes */
|
|
+ int aStat[4]; /* Total cache hits, misses, writes, spills */
|
|
#ifdef SQLITE_TEST
|
|
int nRead; /* Database pages read */
|
|
#endif
|
|
@@ -48167,6 +51080,7 @@
|
|
#define PAGER_STAT_HIT 0
|
|
#define PAGER_STAT_MISS 1
|
|
#define PAGER_STAT_WRITE 2
|
|
+#define PAGER_STAT_SPILL 3
|
|
|
|
/*
|
|
** The following global variables hold counters used for
|
|
@@ -48264,19 +51178,30 @@
|
|
*/
|
|
#define isOpen(pFd) ((pFd)->pMethods!=0)
|
|
|
|
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
/*
|
|
-** Return true if this pager uses a write-ahead log to read page pgno.
|
|
-** Return false if the pager reads pgno directly from the database.
|
|
+** Return true if page pgno can be read directly from the database file
|
|
+** by the b-tree layer. This is the case if:
|
|
+**
|
|
+** * the database file is open,
|
|
+** * there are no dirty pages in the cache, and
|
|
+** * the desired page is not currently in the wal file.
|
|
*/
|
|
-#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ)
|
|
-SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
|
|
- u32 iRead = 0;
|
|
- int rc;
|
|
- if( pPager->pWal==0 ) return 0;
|
|
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
|
|
- return rc || iRead;
|
|
+SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
|
|
+ if( pPager->fd->pMethods==0 ) return 0;
|
|
+ if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
|
|
+#ifndef SQLITE_OMIT_WAL
|
|
+ if( pPager->pWal ){
|
|
+ u32 iRead = 0;
|
|
+ int rc;
|
|
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
|
|
+ return (rc==SQLITE_OK && iRead==0);
|
|
+ }
|
|
+#endif
|
|
+ return 1;
|
|
}
|
|
#endif
|
|
+
|
|
#ifndef SQLITE_OMIT_WAL
|
|
# define pagerUseWal(x) ((x)->pWal!=0)
|
|
#else
|
|
@@ -48398,6 +51323,7 @@
|
|
assert( isOpen(p->jfd)
|
|
|| p->journalMode==PAGER_JOURNALMODE_OFF
|
|
|| p->journalMode==PAGER_JOURNALMODE_WAL
|
|
+ || (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
|
);
|
|
assert( pPager->dbOrigSize<=pPager->dbHintSize );
|
|
break;
|
|
@@ -48409,6 +51335,7 @@
|
|
assert( isOpen(p->jfd)
|
|
|| p->journalMode==PAGER_JOURNALMODE_OFF
|
|
|| p->journalMode==PAGER_JOURNALMODE_WAL
|
|
+ || (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
|
);
|
|
break;
|
|
|
|
@@ -48434,8 +51361,12 @@
|
|
** to "print *pPager" in gdb:
|
|
**
|
|
** (gdb) printf "%s", print_pager_state(pPager)
|
|
+**
|
|
+** This routine has external linkage in order to suppress compiler warnings
|
|
+** about an unused function. It is enclosed within SQLITE_DEBUG and so does
|
|
+** not appear in normal builds.
|
|
*/
|
|
-static char *print_pager_state(Pager *p){
|
|
+char *print_pager_state(Pager *p){
|
|
static char zRet[1024];
|
|
|
|
sqlite3_snprintf(1024, zRet,
|
|
@@ -48619,8 +51550,9 @@
|
|
}
|
|
|
|
/*
|
|
-** This function determines whether or not the atomic-write optimization
|
|
-** can be used with this pager. The optimization can be used if:
|
|
+** This function determines whether or not the atomic-write or
|
|
+** atomic-batch-write optimizations can be used with this pager. The
|
|
+** atomic-write optimization can be used if:
|
|
**
|
|
** (a) the value returned by OsDeviceCharacteristics() indicates that
|
|
** a database page may be written atomically, and
|
|
@@ -48627,27 +51559,39 @@
|
|
** (b) the value returned by OsSectorSize() is less than or equal
|
|
** to the page size.
|
|
**
|
|
-** The optimization is also always enabled for temporary files. It is
|
|
-** an error to call this function if pPager is opened on an in-memory
|
|
-** database.
|
|
+** If it can be used, then the value returned is the size of the journal
|
|
+** file when it contains rollback data for exactly one page.
|
|
**
|
|
-** If the optimization cannot be used, 0 is returned. If it can be used,
|
|
-** then the value returned is the size of the journal file when it
|
|
-** contains rollback data for exactly one page.
|
|
+** The atomic-batch-write optimization can be used if OsDeviceCharacteristics()
|
|
+** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is
|
|
+** returned in this case.
|
|
+**
|
|
+** If neither optimization can be used, 0 is returned.
|
|
*/
|
|
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
static int jrnlBufferSize(Pager *pPager){
|
|
assert( !MEMDB );
|
|
- if( !pPager->tempFile ){
|
|
- int dc; /* Device characteristics */
|
|
- int nSector; /* Sector size */
|
|
- int szPage; /* Page size */
|
|
|
|
- assert( isOpen(pPager->fd) );
|
|
- dc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
|
- nSector = pPager->sectorSize;
|
|
- szPage = pPager->pageSize;
|
|
+#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
|
+ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
+ int dc; /* Device characteristics */
|
|
|
|
+ assert( isOpen(pPager->fd) );
|
|
+ dc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
|
+#else
|
|
+ UNUSED_PARAMETER(pPager);
|
|
+#endif
|
|
+
|
|
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ if( pPager->dbSize>0 && (dc&SQLITE_IOCAP_BATCH_ATOMIC) ){
|
|
+ return -1;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
+ {
|
|
+ int nSector = pPager->sectorSize;
|
|
+ int szPage = pPager->pageSize;
|
|
+
|
|
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
|
|
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
|
|
if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){
|
|
@@ -48656,11 +51600,11 @@
|
|
}
|
|
|
|
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
|
|
-}
|
|
-#else
|
|
-# define jrnlBufferSize(x) 0
|
|
#endif
|
|
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/*
|
|
** If SQLITE_CHECK_PAGES is defined then we do some sanity checking
|
|
** on the cache using a hash function. This is used for testing
|
|
@@ -48742,6 +51686,7 @@
|
|
|| szJ<16
|
|
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
|
|
|| len>=nMaster
|
|
+ || len>szJ-16
|
|
|| len==0
|
|
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|
|
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|
|
@@ -49187,7 +52132,6 @@
|
|
** Return the pPager->iDataVersion value
|
|
*/
|
|
SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager *pPager){
|
|
- assert( pPager->eState>PAGER_OPEN );
|
|
return pPager->iDataVersion;
|
|
}
|
|
|
|
@@ -49463,7 +52407,9 @@
|
|
}
|
|
|
|
releaseAllSavepoints(pPager);
|
|
- assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
|
|
+ assert( isOpen(pPager->jfd) || pPager->pInJournal==0
|
|
+ || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
|
+ );
|
|
if( isOpen(pPager->jfd) ){
|
|
assert( !pagerUseWal(pPager) );
|
|
|
|
@@ -49551,7 +52497,7 @@
|
|
rc = pager_truncate(pPager, pPager->dbSize);
|
|
}
|
|
|
|
- if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
|
|
+ if( rc==SQLITE_OK && bCommit ){
|
|
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
|
|
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
|
}
|
|
@@ -50231,6 +53177,7 @@
|
|
char *zMaster = 0; /* Name of master journal file if any */
|
|
int needPagerReset; /* True to reset page prior to first page rollback */
|
|
int nPlayback = 0; /* Total number of pages restored from journal */
|
|
+ u32 savedPageSize = pPager->pageSize;
|
|
|
|
/* Figure out how many records are in the journal. Abort early if
|
|
** the journal is empty.
|
|
@@ -50360,6 +53307,9 @@
|
|
assert( 0 );
|
|
|
|
end_playback:
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3PagerSetPagesize(pPager, &savedPageSize, -1);
|
|
+ }
|
|
/* Following a rollback, the database file should be back in its original
|
|
** state prior to the start of the transaction, so invoke the
|
|
** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the
|
|
@@ -50366,9 +53316,7 @@
|
|
** assertion that the transaction counter was modified.
|
|
*/
|
|
#ifdef SQLITE_DEBUG
|
|
- if( pPager->fd->pMethods ){
|
|
- sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
|
|
- }
|
|
+ sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
|
|
#endif
|
|
|
|
/* If this playback is happening automatically as a result of an IO or
|
|
@@ -50418,7 +53366,8 @@
|
|
|
|
|
|
/*
|
|
-** Read the content for page pPg out of the database file and into
|
|
+** Read the content for page pPg out of the database file (or out of
|
|
+** the WAL if that is where the most recent copy if found) into
|
|
** pPg->pData. A shared lock or greater must be held on the database
|
|
** file before this function is called.
|
|
**
|
|
@@ -50428,30 +53377,33 @@
|
|
** If an IO error occurs, then the IO error is returned to the caller.
|
|
** Otherwise, SQLITE_OK is returned.
|
|
*/
|
|
-static int readDbPage(PgHdr *pPg, u32 iFrame){
|
|
+static int readDbPage(PgHdr *pPg){
|
|
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
|
|
- Pgno pgno = pPg->pgno; /* Page number to read */
|
|
int rc = SQLITE_OK; /* Return code */
|
|
- int pgsz = pPager->pageSize; /* Number of bytes to read */
|
|
|
|
+#ifndef SQLITE_OMIT_WAL
|
|
+ u32 iFrame = 0; /* Frame of WAL containing pgno */
|
|
+
|
|
assert( pPager->eState>=PAGER_READER && !MEMDB );
|
|
assert( isOpen(pPager->fd) );
|
|
|
|
-#ifndef SQLITE_OMIT_WAL
|
|
+ if( pagerUseWal(pPager) ){
|
|
+ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
|
|
+ if( rc ) return rc;
|
|
+ }
|
|
if( iFrame ){
|
|
- /* Try to pull the page from the write-ahead log. */
|
|
- rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
|
|
+ rc = sqlite3WalReadFrame(pPager->pWal, iFrame,pPager->pageSize,pPg->pData);
|
|
}else
|
|
#endif
|
|
{
|
|
- i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
|
|
- rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
|
|
+ i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize;
|
|
+ rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
|
|
if( rc==SQLITE_IOERR_SHORT_READ ){
|
|
rc = SQLITE_OK;
|
|
}
|
|
}
|
|
|
|
- if( pgno==1 ){
|
|
+ if( pPg->pgno==1 ){
|
|
if( rc ){
|
|
/* If the read is unsuccessful, set the dbFileVers[] to something
|
|
** that will never be a valid file version. dbFileVers[] is a copy
|
|
@@ -50471,13 +53423,13 @@
|
|
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
|
|
}
|
|
}
|
|
- CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT);
|
|
+ CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT);
|
|
|
|
PAGER_INCR(sqlite3_pager_readdb_count);
|
|
PAGER_INCR(pPager->nRead);
|
|
- IOTRACE(("PGIN %p %d\n", pPager, pgno));
|
|
+ IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno));
|
|
PAGERTRACE(("FETCH %d page %d hash(%08x)\n",
|
|
- PAGERID(pPager), pgno, pager_pagehash(pPg)));
|
|
+ PAGERID(pPager), pPg->pgno, pager_pagehash(pPg)));
|
|
|
|
return rc;
|
|
}
|
|
@@ -50528,12 +53480,8 @@
|
|
if( sqlite3PcachePageRefcount(pPg)==1 ){
|
|
sqlite3PcacheDrop(pPg);
|
|
}else{
|
|
- u32 iFrame = 0;
|
|
- rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
|
|
+ rc = readDbPage(pPg);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = readDbPage(pPg, iFrame);
|
|
- }
|
|
- if( rc==SQLITE_OK ){
|
|
pPager->xReiniter(pPg);
|
|
}
|
|
sqlite3PagerUnrefNotNull(pPg);
|
|
@@ -51038,21 +53986,18 @@
|
|
}
|
|
if( pPager->noSync ){
|
|
pPager->syncFlags = 0;
|
|
- pPager->ckptSyncFlags = 0;
|
|
}else if( pgFlags & PAGER_FULLFSYNC ){
|
|
pPager->syncFlags = SQLITE_SYNC_FULL;
|
|
- pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
|
|
- }else if( pgFlags & PAGER_CKPT_FULLFSYNC ){
|
|
- pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
|
- pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
|
|
}else{
|
|
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
|
- pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
|
}
|
|
- pPager->walSyncFlags = pPager->syncFlags;
|
|
+ pPager->walSyncFlags = (pPager->syncFlags<<2);
|
|
if( pPager->fullSync ){
|
|
- pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
|
|
+ pPager->walSyncFlags |= pPager->syncFlags;
|
|
}
|
|
+ if( (pgFlags & PAGER_CKPT_FULLFSYNC) && !pPager->noSync ){
|
|
+ pPager->walSyncFlags |= (SQLITE_SYNC_FULL<<2);
|
|
+ }
|
|
if( pgFlags & PAGER_CACHESPILL ){
|
|
pPager->doNotSpill &= ~SPILLFLAG_OFF;
|
|
}else{
|
|
@@ -51124,20 +54069,18 @@
|
|
** retried. If it returns zero, then the SQLITE_BUSY error is
|
|
** returned to the caller of the pager API function.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
|
|
+SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(
|
|
Pager *pPager, /* Pager object */
|
|
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
|
|
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
|
|
){
|
|
+ void **ap;
|
|
pPager->xBusyHandler = xBusyHandler;
|
|
pPager->pBusyHandlerArg = pBusyHandlerArg;
|
|
-
|
|
- if( isOpen(pPager->fd) ){
|
|
- void **ap = (void **)&pPager->xBusyHandler;
|
|
- assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
|
|
- assert( ap[1]==pBusyHandlerArg );
|
|
- sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
|
|
- }
|
|
+ ap = (void **)&pPager->xBusyHandler;
|
|
+ assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
|
|
+ assert( ap[1]==pBusyHandlerArg );
|
|
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
|
|
}
|
|
|
|
/*
|
|
@@ -51523,7 +54466,31 @@
|
|
}
|
|
}
|
|
|
|
+/* Verify that the database file has not be deleted or renamed out from
|
|
+** under the pager. Return SQLITE_OK if the database is still where it ought
|
|
+** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
|
|
+** code from sqlite3OsAccess()) if the database has gone missing.
|
|
+*/
|
|
+static int databaseIsUnmoved(Pager *pPager){
|
|
+ int bHasMoved = 0;
|
|
+ int rc;
|
|
|
|
+ if( pPager->tempFile ) return SQLITE_OK;
|
|
+ if( pPager->dbSize==0 ) return SQLITE_OK;
|
|
+ assert( pPager->zFilename && pPager->zFilename[0] );
|
|
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
|
|
+ if( rc==SQLITE_NOTFOUND ){
|
|
+ /* If the HAS_MOVED file-control is unimplemented, assume that the file
|
|
+ ** has not been moved. That is the historical behavior of SQLite: prior to
|
|
+ ** version 3.8.3, it never checked */
|
|
+ rc = SQLITE_OK;
|
|
+ }else if( rc==SQLITE_OK && bHasMoved ){
|
|
+ rc = SQLITE_READONLY_DBMOVED;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
/*
|
|
** Shutdown the page cache. Free all memory and close all files.
|
|
**
|
|
@@ -51539,8 +54506,7 @@
|
|
** to the caller.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
|
|
- u8 *pTmp = (u8 *)pPager->pTmpSpace;
|
|
-
|
|
+ u8 *pTmp = (u8*)pPager->pTmpSpace;
|
|
assert( db || pagerUseWal(pPager)==0 );
|
|
assert( assert_pager_state(pPager) );
|
|
disable_simulated_io_errors();
|
|
@@ -51549,11 +54515,17 @@
|
|
/* pPager->errCode = 0; */
|
|
pPager->exclusiveMode = 0;
|
|
#ifndef SQLITE_OMIT_WAL
|
|
- assert( db || pPager->pWal==0 );
|
|
- sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize,
|
|
- (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
|
|
- );
|
|
- pPager->pWal = 0;
|
|
+ {
|
|
+ u8 *a = 0;
|
|
+ assert( db || pPager->pWal==0 );
|
|
+ if( db && 0==(db->flags & SQLITE_NoCkptOnClose)
|
|
+ && SQLITE_OK==databaseIsUnmoved(pPager)
|
|
+ ){
|
|
+ a = pTmp;
|
|
+ }
|
|
+ sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
|
|
+ pPager->pWal = 0;
|
|
+ }
|
|
#endif
|
|
pager_reset(pPager);
|
|
if( MEMDB ){
|
|
@@ -52010,6 +54982,7 @@
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
+ pPager->aStat[PAGER_STAT_SPILL]++;
|
|
pPg->pDirty = 0;
|
|
if( pagerUseWal(pPager) ){
|
|
/* Write a single frame for this page to the log. */
|
|
@@ -52018,6 +54991,13 @@
|
|
rc = pagerWalFrames(pPager, pPg, 0, 0);
|
|
}
|
|
}else{
|
|
+
|
|
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ if( pPager->tempFile==0 ){
|
|
+ rc = sqlite3JournalCreate(pPager->jfd);
|
|
+ if( rc!=SQLITE_OK ) return pager_error(pPager, rc);
|
|
+ }
|
|
+#endif
|
|
|
|
/* Sync the journal file if required. */
|
|
if( pPg->flags&PGHDR_NEED_SYNC
|
|
@@ -52108,6 +55088,11 @@
|
|
int rc = SQLITE_OK; /* Return code */
|
|
int tempFile = 0; /* True for temp files (incl. in-memory files) */
|
|
int memDb = 0; /* True if this is an in-memory file */
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ int memJM = 0; /* Memory journal mode */
|
|
+#else
|
|
+# define memJM 0
|
|
+#endif
|
|
int readOnly = 0; /* True if this is a read-only file */
|
|
int journalFileSize; /* Bytes to allocate for each journal fd */
|
|
char *zPathname = 0; /* Full path to database file */
|
|
@@ -52235,7 +55220,10 @@
|
|
int fout = 0; /* VFS flags returned by xOpen() */
|
|
rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
|
|
assert( !memDb );
|
|
- readOnly = (fout&SQLITE_OPEN_READONLY);
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+ memJM = (fout&SQLITE_OPEN_MEMORY)!=0;
|
|
+#endif
|
|
+ readOnly = (fout&SQLITE_OPEN_READONLY)!=0;
|
|
|
|
/* If the file was successfully opened for read/write access,
|
|
** choose a default page size in case we have to create the
|
|
@@ -52351,13 +55339,11 @@
|
|
assert( pPager->extraSync==0 );
|
|
assert( pPager->syncFlags==0 );
|
|
assert( pPager->walSyncFlags==0 );
|
|
- assert( pPager->ckptSyncFlags==0 );
|
|
}else{
|
|
pPager->fullSync = 1;
|
|
pPager->extraSync = 0;
|
|
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
|
- pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
|
|
- pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
|
+ pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2);
|
|
}
|
|
/* pPager->pFirst = 0; */
|
|
/* pPager->pFirstSynced = 0; */
|
|
@@ -52368,7 +55354,7 @@
|
|
setSectorSize(pPager);
|
|
if( !useJournal ){
|
|
pPager->journalMode = PAGER_JOURNALMODE_OFF;
|
|
- }else if( memDb ){
|
|
+ }else if( memDb || memJM ){
|
|
pPager->journalMode = PAGER_JOURNALMODE_MEMORY;
|
|
}
|
|
/* pPager->xBusyHandler = 0; */
|
|
@@ -52383,31 +55369,7 @@
|
|
}
|
|
|
|
|
|
-/* Verify that the database file has not be deleted or renamed out from
|
|
-** under the pager. Return SQLITE_OK if the database is still were it ought
|
|
-** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
|
|
-** code from sqlite3OsAccess()) if the database has gone missing.
|
|
-*/
|
|
-static int databaseIsUnmoved(Pager *pPager){
|
|
- int bHasMoved = 0;
|
|
- int rc;
|
|
|
|
- if( pPager->tempFile ) return SQLITE_OK;
|
|
- if( pPager->dbSize==0 ) return SQLITE_OK;
|
|
- assert( pPager->zFilename && pPager->zFilename[0] );
|
|
- rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
|
|
- if( rc==SQLITE_NOTFOUND ){
|
|
- /* If the HAS_MOVED file-control is unimplemented, assume that the file
|
|
- ** has not been moved. That is the historical behavior of SQLite: prior to
|
|
- ** version 3.8.3, it never checked */
|
|
- rc = SQLITE_OK;
|
|
- }else if( rc==SQLITE_OK && bHasMoved ){
|
|
- rc = SQLITE_READONLY_DBMOVED;
|
|
- }
|
|
- return rc;
|
|
-}
|
|
-
|
|
-
|
|
/*
|
|
** This function is called after transitioning from PAGER_UNLOCK to
|
|
** PAGER_SHARED state. It tests if there is a hot journal present in
|
|
@@ -52777,7 +55739,8 @@
|
|
** nothing to rollback, so this routine is a no-op.
|
|
*/
|
|
static void pagerUnlockIfUnused(Pager *pPager){
|
|
- if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
|
|
+ if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){
|
|
+ assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */
|
|
pagerUnlockAndRollback(pPager);
|
|
}
|
|
}
|
|
@@ -52918,14 +55881,9 @@
|
|
memset(pPg->pData, 0, pPager->pageSize);
|
|
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
|
}else{
|
|
- u32 iFrame = 0; /* Frame to read from WAL file */
|
|
- if( pagerUseWal(pPager) ){
|
|
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
|
|
- if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
|
- }
|
|
assert( pPg->pPager==pPager );
|
|
pPager->aStat[PAGER_STAT_MISS]++;
|
|
- rc = readDbPage(pPg, iFrame);
|
|
+ rc = readDbPage(pPg);
|
|
if( rc!=SQLITE_OK ){
|
|
goto pager_acquire_err;
|
|
}
|
|
@@ -52999,7 +55957,7 @@
|
|
}
|
|
if( pPg==0 ){
|
|
rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg);
|
|
- }else{
|
|
+ }else{
|
|
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData);
|
|
}
|
|
if( pPg ){
|
|
@@ -53068,25 +56026,40 @@
|
|
/*
|
|
** Release a page reference.
|
|
**
|
|
-** If the number of references to the page drop to zero, then the
|
|
-** page is added to the LRU list. When all references to all pages
|
|
-** are released, a rollback occurs and the lock on the database is
|
|
-** removed.
|
|
+** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be
|
|
+** used if we know that the page being released is not the last page.
|
|
+** The btree layer always holds page1 open until the end, so these first
|
|
+** to routines can be used to release any page other than BtShared.pPage1.
|
|
+**
|
|
+** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine
|
|
+** checks the total number of outstanding pages and if the number of
|
|
+** pages reaches zero it drops the database lock.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){
|
|
- Pager *pPager;
|
|
+ TESTONLY( Pager *pPager = pPg->pPager; )
|
|
assert( pPg!=0 );
|
|
- pPager = pPg->pPager;
|
|
if( pPg->flags & PGHDR_MMAP ){
|
|
+ assert( pPg->pgno!=1 ); /* Page1 is never memory mapped */
|
|
pagerReleaseMapPage(pPg);
|
|
}else{
|
|
sqlite3PcacheRelease(pPg);
|
|
}
|
|
- pagerUnlockIfUnused(pPager);
|
|
+ /* Do not use this routine to release the last reference to page1 */
|
|
+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
|
|
}
|
|
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
|
|
if( pPg ) sqlite3PagerUnrefNotNull(pPg);
|
|
}
|
|
+SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){
|
|
+ Pager *pPager;
|
|
+ assert( pPg!=0 );
|
|
+ assert( pPg->pgno==1 );
|
|
+ assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
|
|
+ pPager = pPg->pPager;
|
|
+ sqlite3PagerResetLockTimeout(pPager);
|
|
+ sqlite3PcacheRelease(pPg);
|
|
+ pagerUnlockIfUnused(pPager);
|
|
+}
|
|
|
|
/*
|
|
** This function is called at the start of every write transaction.
|
|
@@ -53679,12 +56652,9 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){
|
|
int rc = SQLITE_OK;
|
|
-
|
|
- if( isOpen(pPager->fd) ){
|
|
- void *pArg = (void*)zMaster;
|
|
- rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
|
|
- if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
|
- }
|
|
+ void *pArg = (void*)zMaster;
|
|
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
|
|
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
|
if( rc==SQLITE_OK && !pPager->noSync ){
|
|
assert( !MEMDB );
|
|
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
|
|
@@ -53779,9 +56749,10 @@
|
|
** backup in progress needs to be restarted. */
|
|
sqlite3BackupRestart(pPager->pBackup);
|
|
}else{
|
|
+ PgHdr *pList;
|
|
if( pagerUseWal(pPager) ){
|
|
- PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
|
PgHdr *pPageOne = 0;
|
|
+ pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
|
if( pList==0 ){
|
|
/* Must have at least one page for the WAL commit flag.
|
|
** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */
|
|
@@ -53798,6 +56769,21 @@
|
|
sqlite3PcacheCleanAll(pPager->pPCache);
|
|
}
|
|
}else{
|
|
+ /* The bBatch boolean is true if the batch-atomic-write commit method
|
|
+ ** should be used. No rollback journal is created if batch-atomic-write
|
|
+ ** is enabled.
|
|
+ */
|
|
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ sqlite3_file *fd = pPager->fd;
|
|
+ int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */
|
|
+ && (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC)
|
|
+ && !pPager->noSync
|
|
+ && sqlite3JournalIsInMemory(pPager->jfd);
|
|
+#else
|
|
+# define bBatch 0
|
|
+#endif
|
|
+
|
|
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
/* The following block updates the change-counter. Exactly how it
|
|
** does this depends on whether or not the atomic-update optimization
|
|
** was enabled at compile time, and if this transaction meets the
|
|
@@ -53821,33 +56807,41 @@
|
|
** in 'direct' mode. In this case the journal file will never be
|
|
** created for this transaction.
|
|
*/
|
|
- #ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
- PgHdr *pPg;
|
|
- assert( isOpen(pPager->jfd)
|
|
- || pPager->journalMode==PAGER_JOURNALMODE_OFF
|
|
- || pPager->journalMode==PAGER_JOURNALMODE_WAL
|
|
- );
|
|
- if( !zMaster && isOpen(pPager->jfd)
|
|
- && pPager->journalOff==jrnlBufferSize(pPager)
|
|
- && pPager->dbSize>=pPager->dbOrigSize
|
|
- && (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
|
|
- ){
|
|
- /* Update the db file change counter via the direct-write method. The
|
|
- ** following call will modify the in-memory representation of page 1
|
|
- ** to include the updated change counter and then write page 1
|
|
- ** directly to the database file. Because of the atomic-write
|
|
- ** property of the host file-system, this is safe.
|
|
- */
|
|
- rc = pager_incr_changecounter(pPager, 1);
|
|
- }else{
|
|
- rc = sqlite3JournalCreate(pPager->jfd);
|
|
- if( rc==SQLITE_OK ){
|
|
- rc = pager_incr_changecounter(pPager, 0);
|
|
+ if( bBatch==0 ){
|
|
+ PgHdr *pPg;
|
|
+ assert( isOpen(pPager->jfd)
|
|
+ || pPager->journalMode==PAGER_JOURNALMODE_OFF
|
|
+ || pPager->journalMode==PAGER_JOURNALMODE_WAL
|
|
+ );
|
|
+ if( !zMaster && isOpen(pPager->jfd)
|
|
+ && pPager->journalOff==jrnlBufferSize(pPager)
|
|
+ && pPager->dbSize>=pPager->dbOrigSize
|
|
+ && (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
|
|
+ ){
|
|
+ /* Update the db file change counter via the direct-write method. The
|
|
+ ** following call will modify the in-memory representation of page 1
|
|
+ ** to include the updated change counter and then write page 1
|
|
+ ** directly to the database file. Because of the atomic-write
|
|
+ ** property of the host file-system, this is safe.
|
|
+ */
|
|
+ rc = pager_incr_changecounter(pPager, 1);
|
|
+ }else{
|
|
+ rc = sqlite3JournalCreate(pPager->jfd);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = pager_incr_changecounter(pPager, 0);
|
|
+ }
|
|
}
|
|
}
|
|
- #else
|
|
+#else /* SQLITE_ENABLE_ATOMIC_WRITE */
|
|
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ if( zMaster ){
|
|
+ rc = sqlite3JournalCreate(pPager->jfd);
|
|
+ if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
|
+ assert( bBatch==0 );
|
|
+ }
|
|
+#endif
|
|
rc = pager_incr_changecounter(pPager, 0);
|
|
- #endif
|
|
+#endif /* !SQLITE_ENABLE_ATOMIC_WRITE */
|
|
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
|
|
|
/* Write the master journal name into the journal file. If a master
|
|
@@ -53870,8 +56864,37 @@
|
|
*/
|
|
rc = syncJournal(pPager, 0);
|
|
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
|
-
|
|
- rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
|
|
+
|
|
+ pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
|
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ if( bBatch ){
|
|
+ rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = pager_write_pagelist(pPager, pList);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( (rc&0xFF)==SQLITE_IOERR && rc!=SQLITE_IOERR_NOMEM ){
|
|
+ rc = sqlite3JournalCreate(pPager->jfd);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3OsClose(pPager->jfd);
|
|
+ goto commit_phase_one_exit;
|
|
+ }
|
|
+ bBatch = 0;
|
|
+ }else{
|
|
+ sqlite3OsClose(pPager->jfd);
|
|
+ }
|
|
+ }
|
|
+#endif /* SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
|
|
+
|
|
+ if( bBatch==0 ){
|
|
+ rc = pager_write_pagelist(pPager, pList);
|
|
+ }
|
|
if( rc!=SQLITE_OK ){
|
|
assert( rc!=SQLITE_IOERR_BLOCKED );
|
|
goto commit_phase_one_exit;
|
|
@@ -54092,8 +57115,12 @@
|
|
#endif
|
|
|
|
/*
|
|
-** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
|
|
-** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
|
|
+** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE,
|
|
+** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation
|
|
+** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because
|
|
+** it was added later.
|
|
+**
|
|
+** Before returning, *pnVal is incremented by the
|
|
** current cache hit or miss count, according to the value of eStat. If the
|
|
** reset parameter is non-zero, the cache hit or miss count is zeroed before
|
|
** returning.
|
|
@@ -54103,15 +57130,18 @@
|
|
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|
|
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
|
|
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE
|
|
+ || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1
|
|
);
|
|
|
|
assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS );
|
|
assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE );
|
|
- assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 );
|
|
+ assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1
|
|
+ && PAGER_STAT_WRITE==2 && PAGER_STAT_SPILL==3 );
|
|
|
|
- *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT];
|
|
+ eStat -= SQLITE_DBSTATUS_CACHE_HIT;
|
|
+ *pnVal += pPager->aStat[eStat];
|
|
if( reset ){
|
|
- pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
|
|
+ pPager->aStat[eStat] = 0;
|
|
}
|
|
}
|
|
|
|
@@ -54315,7 +57345,17 @@
|
|
return pPager->fd;
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
/*
|
|
+** Reset the lock timeout for pager.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){
|
|
+ int x = 0;
|
|
+ sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Return the file handle for the journal file (if it exists).
|
|
** This will be either the rollback journal or the WAL file.
|
|
*/
|
|
@@ -54345,7 +57385,11 @@
|
|
void (*xCodecFree)(void*),
|
|
void *pCodec
|
|
){
|
|
- if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec);
|
|
+ if( pPager->xCodecFree ){
|
|
+ pPager->xCodecFree(pPager->pCodec);
|
|
+ }else{
|
|
+ pager_reset(pPager);
|
|
+ }
|
|
pPager->xCodec = pPager->memDb ? 0 : xCodec;
|
|
pPager->xCodecSizeChng = xCodecSizeChng;
|
|
pPager->xCodecFree = xCodecFree;
|
|
@@ -54606,13 +57650,6 @@
|
|
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
|
|
u8 eOld = pPager->journalMode; /* Prior journalmode */
|
|
|
|
-#ifdef SQLITE_DEBUG
|
|
- /* The print_pager_state() routine is intended to be used by the debugger
|
|
- ** only. We invoke it once here to suppress a compiler warning. */
|
|
- print_pager_state(pPager);
|
|
-#endif
|
|
-
|
|
-
|
|
/* The eMode parameter is always valid */
|
|
assert( eMode==PAGER_JOURNALMODE_DELETE
|
|
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|
|
@@ -54772,9 +57809,10 @@
|
|
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
|
|
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
|
|
pPager->pBusyHandlerArg,
|
|
- pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
|
+ pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
|
pnLog, pnCkpt
|
|
);
|
|
+ sqlite3PagerResetLockTimeout(pPager);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -54929,7 +57967,7 @@
|
|
if( rc==SQLITE_OK && pPager->pWal ){
|
|
rc = pagerExclusiveLock(pPager);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags,
|
|
+ rc = sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags,
|
|
pPager->pageSize, (u8*)pPager->pTmpSpace);
|
|
pPager->pWal = 0;
|
|
pagerFixMaplimit(pPager);
|
|
@@ -54980,6 +58018,38 @@
|
|
}
|
|
return rc;
|
|
}
|
|
+
|
|
+/*
|
|
+** The caller currently has a read transaction open on the database.
|
|
+** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise,
|
|
+** this function takes a SHARED lock on the CHECKPOINTER slot and then
|
|
+** checks if the snapshot passed as the second argument is still
|
|
+** available. If so, SQLITE_OK is returned.
|
|
+**
|
|
+** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
|
|
+** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error
|
|
+** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER
|
|
+** lock is released before returning.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot){
|
|
+ int rc;
|
|
+ if( pPager->pWal ){
|
|
+ rc = sqlite3WalSnapshotCheck(pPager->pWal, pSnapshot);
|
|
+ }else{
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Release a lock obtained by an earlier successful call to
|
|
+** sqlite3PagerSnapshotCheck().
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager){
|
|
+ assert( pPager->pWal );
|
|
+ return sqlite3WalSnapshotUnlock(pPager->pWal);
|
|
+}
|
|
+
|
|
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
|
#endif /* !SQLITE_OMIT_WAL */
|
|
|
|
@@ -55135,6 +58205,10 @@
|
|
** on a network filesystem. All users of the database must be able to
|
|
** share memory.
|
|
**
|
|
+** In the default unix and windows implementation, the wal-index is a mmapped
|
|
+** file whose name is the database name with a "-shm" suffix added. For that
|
|
+** reason, the wal-index is sometimes called the "shm" file.
|
|
+**
|
|
** The wal-index is transient. After a crash, the wal-index can (and should
|
|
** be) reconstructed from the original WAL file. In fact, the VFS is required
|
|
** to either truncate or zero the header of the wal-index when the last
|
|
@@ -55258,6 +58332,18 @@
|
|
#endif
|
|
|
|
/*
|
|
+** WAL mode depends on atomic aligned 32-bit loads and stores in a few
|
|
+** places. The following macros try to make this explicit.
|
|
+*/
|
|
+#if GCC_VESRION>=5004000
|
|
+# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
|
|
+# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
|
|
+#else
|
|
+# define AtomicLoad(PTR) (*(PTR))
|
|
+# define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
|
|
+#endif
|
|
+
|
|
+/*
|
|
** The maximum (and only) versions of the wal and wal-index formats
|
|
** that may be interpreted by this version of SQLite.
|
|
**
|
|
@@ -55274,9 +58360,18 @@
|
|
#define WALINDEX_MAX_VERSION 3007000
|
|
|
|
/*
|
|
-** Indices of various locking bytes. WAL_NREADER is the number
|
|
+** Index numbers for various locking bytes. WAL_NREADER is the number
|
|
** of available reader locks and should be at least 3. The default
|
|
** is SQLITE_SHM_NLOCK==8 and WAL_NREADER==5.
|
|
+**
|
|
+** Technically, the various VFSes are free to implement these locks however
|
|
+** they see fit. However, compatibility is encouraged so that VFSes can
|
|
+** interoperate. The standard implemention used on both unix and windows
|
|
+** is for the index number to indicate a byte offset into the
|
|
+** WalCkptInfo.aLock[] array in the wal-index header. In other words, all
|
|
+** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which
|
|
+** should be 120) is the location in the shm file for the first locking
|
|
+** byte.
|
|
*/
|
|
#define WAL_WRITE_LOCK 0
|
|
#define WAL_ALL_BUT_WRITE 1
|
|
@@ -55400,7 +58495,6 @@
|
|
#define WAL_FRAME_HDRSIZE 24
|
|
|
|
/* Size of write ahead log header, including checksum. */
|
|
-/* #define WAL_HDRSIZE 24 */
|
|
#define WAL_HDRSIZE 32
|
|
|
|
/* WAL magic value. Either this value, or the same value with the least
|
|
@@ -55446,6 +58540,7 @@
|
|
u8 truncateOnCommit; /* True to truncate WAL file on commit */
|
|
u8 syncHeader; /* Fsync the WAL header if true */
|
|
u8 padToSectorBoundary; /* Pad transactions out to the next sector */
|
|
+ u8 bShmUnreliable; /* SHM content is read-only and unreliable */
|
|
WalIndexHdr hdr; /* Wal-index header for current transaction */
|
|
u32 minFrame; /* Ignore wal frames before this one */
|
|
u32 iReCksum; /* On commit, recalculate checksums from here */
|
|
@@ -55535,11 +58630,20 @@
|
|
** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are
|
|
** numbered from zero.
|
|
**
|
|
+** If the wal-index is currently smaller the iPage pages then the size
|
|
+** of the wal-index might be increased, but only if it is safe to do
|
|
+** so. It is safe to enlarge the wal-index if pWal->writeLock is true
|
|
+** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE.
|
|
+**
|
|
** If this call is successful, *ppPage is set to point to the wal-index
|
|
** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs,
|
|
** then an SQLite error code is returned and *ppPage is set to 0.
|
|
*/
|
|
-static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
|
|
+static SQLITE_NOINLINE int walIndexPageRealloc(
|
|
+ Wal *pWal, /* The WAL context */
|
|
+ int iPage, /* The page we seek */
|
|
+ volatile u32 **ppPage /* Write the page pointer here */
|
|
+){
|
|
int rc = SQLITE_OK;
|
|
|
|
/* Enlarge the pWal->apWiData[] array if required */
|
|
@@ -55558,16 +58662,19 @@
|
|
}
|
|
|
|
/* Request a pointer to the required page from the VFS */
|
|
- if( pWal->apWiData[iPage]==0 ){
|
|
- if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
|
|
- pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
|
|
- if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
|
|
- }else{
|
|
- rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
|
|
- pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
|
|
- );
|
|
+ assert( pWal->apWiData[iPage]==0 );
|
|
+ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
|
|
+ pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
|
|
+ if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
|
|
+ }else{
|
|
+ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
|
|
+ pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
|
|
+ );
|
|
+ assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 );
|
|
+ testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK );
|
|
+ if( (rc&0xff)==SQLITE_READONLY ){
|
|
+ pWal->readOnly |= WAL_SHM_RDONLY;
|
|
if( rc==SQLITE_READONLY ){
|
|
- pWal->readOnly |= WAL_SHM_RDONLY;
|
|
rc = SQLITE_OK;
|
|
}
|
|
}
|
|
@@ -55577,6 +58684,16 @@
|
|
assert( iPage==0 || *ppPage || rc!=SQLITE_OK );
|
|
return rc;
|
|
}
|
|
+static int walIndexPage(
|
|
+ Wal *pWal, /* The WAL context */
|
|
+ int iPage, /* The page we seek */
|
|
+ volatile u32 **ppPage /* Write the page pointer here */
|
|
+){
|
|
+ if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){
|
|
+ return walIndexPageRealloc(pWal, iPage, ppPage);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
|
|
/*
|
|
** Return a pointer to the WalCkptInfo structure in the wal-index.
|
|
@@ -55848,48 +58965,51 @@
|
|
return (iPriorHash+1)&(HASHTABLE_NSLOT-1);
|
|
}
|
|
|
|
+/*
|
|
+** An instance of the WalHashLoc object is used to describe the location
|
|
+** of a page hash table in the wal-index. This becomes the return value
|
|
+** from walHashGet().
|
|
+*/
|
|
+typedef struct WalHashLoc WalHashLoc;
|
|
+struct WalHashLoc {
|
|
+ volatile ht_slot *aHash; /* Start of the wal-index hash table */
|
|
+ volatile u32 *aPgno; /* aPgno[1] is the page of first frame indexed */
|
|
+ u32 iZero; /* One less than the frame number of first indexed*/
|
|
+};
|
|
+
|
|
/*
|
|
** Return pointers to the hash table and page number array stored on
|
|
** page iHash of the wal-index. The wal-index is broken into 32KB pages
|
|
** numbered starting from 0.
|
|
**
|
|
-** Set output variable *paHash to point to the start of the hash table
|
|
-** in the wal-index file. Set *piZero to one less than the frame
|
|
+** Set output variable pLoc->aHash to point to the start of the hash table
|
|
+** in the wal-index file. Set pLoc->iZero to one less than the frame
|
|
** number of the first frame indexed by this hash table. If a
|
|
** slot in the hash table is set to N, it refers to frame number
|
|
-** (*piZero+N) in the log.
|
|
+** (pLoc->iZero+N) in the log.
|
|
**
|
|
-** Finally, set *paPgno so that *paPgno[1] is the page number of the
|
|
-** first frame indexed by the hash table, frame (*piZero+1).
|
|
+** Finally, set pLoc->aPgno so that pLoc->aPgno[1] is the page number of the
|
|
+** first frame indexed by the hash table, frame (pLoc->iZero+1).
|
|
*/
|
|
static int walHashGet(
|
|
Wal *pWal, /* WAL handle */
|
|
int iHash, /* Find the iHash'th table */
|
|
- volatile ht_slot **paHash, /* OUT: Pointer to hash index */
|
|
- volatile u32 **paPgno, /* OUT: Pointer to page number array */
|
|
- u32 *piZero /* OUT: Frame associated with *paPgno[0] */
|
|
+ WalHashLoc *pLoc /* OUT: Hash table location */
|
|
){
|
|
int rc; /* Return code */
|
|
- volatile u32 *aPgno;
|
|
|
|
- rc = walIndexPage(pWal, iHash, &aPgno);
|
|
+ rc = walIndexPage(pWal, iHash, &pLoc->aPgno);
|
|
assert( rc==SQLITE_OK || iHash>0 );
|
|
|
|
if( rc==SQLITE_OK ){
|
|
- u32 iZero;
|
|
- volatile ht_slot *aHash;
|
|
-
|
|
- aHash = (volatile ht_slot *)&aPgno[HASHTABLE_NPAGE];
|
|
+ pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE];
|
|
if( iHash==0 ){
|
|
- aPgno = &aPgno[WALINDEX_HDR_SIZE/sizeof(u32)];
|
|
- iZero = 0;
|
|
+ pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)];
|
|
+ pLoc->iZero = 0;
|
|
}else{
|
|
- iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE;
|
|
+ pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE;
|
|
}
|
|
-
|
|
- *paPgno = &aPgno[-1];
|
|
- *paHash = aHash;
|
|
- *piZero = iZero;
|
|
+ pLoc->aPgno = &pLoc->aPgno[-1];
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -55935,9 +59055,7 @@
|
|
** actually needed.
|
|
*/
|
|
static void walCleanupHash(Wal *pWal){
|
|
- volatile ht_slot *aHash = 0; /* Pointer to hash table to clear */
|
|
- volatile u32 *aPgno = 0; /* Page number array for hash table */
|
|
- u32 iZero = 0; /* frame == (aHash[x]+iZero) */
|
|
+ WalHashLoc sLoc; /* Hash table location */
|
|
int iLimit = 0; /* Zero values greater than this */
|
|
int nByte; /* Number of bytes to zero in aPgno[] */
|
|
int i; /* Used to iterate through aHash[] */
|
|
@@ -55955,16 +59073,16 @@
|
|
*/
|
|
assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) );
|
|
assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] );
|
|
- walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &aHash, &aPgno, &iZero);
|
|
+ walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc);
|
|
|
|
/* Zero all hash-table entries that correspond to frame numbers greater
|
|
** than pWal->hdr.mxFrame.
|
|
*/
|
|
- iLimit = pWal->hdr.mxFrame - iZero;
|
|
+ iLimit = pWal->hdr.mxFrame - sLoc.iZero;
|
|
assert( iLimit>0 );
|
|
for(i=0; i<HASHTABLE_NSLOT; i++){
|
|
- if( aHash[i]>iLimit ){
|
|
- aHash[i] = 0;
|
|
+ if( sLoc.aHash[i]>iLimit ){
|
|
+ sLoc.aHash[i] = 0;
|
|
}
|
|
}
|
|
|
|
@@ -55971,8 +59089,8 @@
|
|
/* Zero the entries in the aPgno array that correspond to frames with
|
|
** frame numbers greater than pWal->hdr.mxFrame.
|
|
*/
|
|
- nByte = (int)((char *)aHash - (char *)&aPgno[iLimit+1]);
|
|
- memset((void *)&aPgno[iLimit+1], 0, nByte);
|
|
+ nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]);
|
|
+ memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte);
|
|
|
|
#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
|
|
/* Verify that the every entry in the mapping region is still reachable
|
|
@@ -55982,10 +59100,10 @@
|
|
int j; /* Loop counter */
|
|
int iKey; /* Hash key */
|
|
for(j=1; j<=iLimit; j++){
|
|
- for(iKey=walHash(aPgno[j]); aHash[iKey]; iKey=walNextHash(iKey)){
|
|
- if( aHash[iKey]==j ) break;
|
|
+ for(iKey=walHash(sLoc.aPgno[j]);sLoc.aHash[iKey];iKey=walNextHash(iKey)){
|
|
+ if( sLoc.aHash[iKey]==j ) break;
|
|
}
|
|
- assert( aHash[iKey]==j );
|
|
+ assert( sLoc.aHash[iKey]==j );
|
|
}
|
|
}
|
|
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
|
|
@@ -55998,11 +59116,9 @@
|
|
*/
|
|
static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
|
|
int rc; /* Return code */
|
|
- u32 iZero = 0; /* One less than frame number of aPgno[1] */
|
|
- volatile u32 *aPgno = 0; /* Page number array */
|
|
- volatile ht_slot *aHash = 0; /* Hash table */
|
|
+ WalHashLoc sLoc; /* Wal-index hash table location */
|
|
|
|
- rc = walHashGet(pWal, walFramePage(iFrame), &aHash, &aPgno, &iZero);
|
|
+ rc = walHashGet(pWal, walFramePage(iFrame), &sLoc);
|
|
|
|
/* Assuming the wal-index file was successfully mapped, populate the
|
|
** page number array and hash table entry.
|
|
@@ -56012,7 +59128,7 @@
|
|
int idx; /* Value to write to hash-table slot */
|
|
int nCollide; /* Number of hash collisions */
|
|
|
|
- idx = iFrame - iZero;
|
|
+ idx = iFrame - sLoc.iZero;
|
|
assert( idx <= HASHTABLE_NSLOT/2 + 1 );
|
|
|
|
/* If this is the first entry to be added to this hash-table, zero the
|
|
@@ -56019,8 +59135,9 @@
|
|
** entire hash table and aPgno[] array before proceeding.
|
|
*/
|
|
if( idx==1 ){
|
|
- int nByte = (int)((u8 *)&aHash[HASHTABLE_NSLOT] - (u8 *)&aPgno[1]);
|
|
- memset((void*)&aPgno[1], 0, nByte);
|
|
+ int nByte = (int)((u8 *)&sLoc.aHash[HASHTABLE_NSLOT]
|
|
+ - (u8 *)&sLoc.aPgno[1]);
|
|
+ memset((void*)&sLoc.aPgno[1], 0, nByte);
|
|
}
|
|
|
|
/* If the entry in aPgno[] is already set, then the previous writer
|
|
@@ -56029,18 +59146,18 @@
|
|
** Remove the remnants of that writers uncommitted transaction from
|
|
** the hash-table before writing any new entries.
|
|
*/
|
|
- if( aPgno[idx] ){
|
|
+ if( sLoc.aPgno[idx] ){
|
|
walCleanupHash(pWal);
|
|
- assert( !aPgno[idx] );
|
|
+ assert( !sLoc.aPgno[idx] );
|
|
}
|
|
|
|
/* Write the aPgno[] array entry and the hash-table slot. */
|
|
nCollide = idx;
|
|
- for(iKey=walHash(iPage); aHash[iKey]; iKey=walNextHash(iKey)){
|
|
+ for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
|
|
if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT;
|
|
}
|
|
- aPgno[idx] = iPage;
|
|
- aHash[iKey] = (ht_slot)idx;
|
|
+ sLoc.aPgno[idx] = iPage;
|
|
+ sLoc.aHash[iKey] = (ht_slot)idx;
|
|
|
|
#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
|
|
/* Verify that the number of entries in the hash table exactly equals
|
|
@@ -56049,7 +59166,7 @@
|
|
{
|
|
int i; /* Loop counter */
|
|
int nEntry = 0; /* Number of entries in the hash table */
|
|
- for(i=0; i<HASHTABLE_NSLOT; i++){ if( aHash[i] ) nEntry++; }
|
|
+ for(i=0; i<HASHTABLE_NSLOT; i++){ if( sLoc.aHash[i] ) nEntry++; }
|
|
assert( nEntry==idx );
|
|
}
|
|
|
|
@@ -56061,10 +59178,12 @@
|
|
if( (idx&0x3ff)==0 ){
|
|
int i; /* Loop counter */
|
|
for(i=1; i<=idx; i++){
|
|
- for(iKey=walHash(aPgno[i]); aHash[iKey]; iKey=walNextHash(iKey)){
|
|
- if( aHash[iKey]==i ) break;
|
|
+ for(iKey=walHash(sLoc.aPgno[i]);
|
|
+ sLoc.aHash[iKey];
|
|
+ iKey=walNextHash(iKey)){
|
|
+ if( sLoc.aHash[iKey]==i ) break;
|
|
}
|
|
- assert( aHash[iKey]==i );
|
|
+ assert( sLoc.aHash[iKey]==i );
|
|
}
|
|
}
|
|
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
|
|
@@ -56090,7 +59209,6 @@
|
|
i64 nSize; /* Size of log file */
|
|
u32 aFrameCksum[2] = {0, 0};
|
|
int iLock; /* Lock offset to lock for checkpoint */
|
|
- int nLock; /* Number of locks to hold */
|
|
|
|
/* Obtain an exclusive lock on all byte in the locking range not already
|
|
** locked by the caller. The caller is guaranteed to have locked the
|
|
@@ -56103,11 +59221,17 @@
|
|
assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE );
|
|
assert( pWal->writeLock );
|
|
iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock;
|
|
- nLock = SQLITE_SHM_NLOCK - iLock;
|
|
- rc = walLockExclusive(pWal, iLock, nLock);
|
|
+ rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
|
|
+ }
|
|
+ }
|
|
if( rc ){
|
|
return rc;
|
|
}
|
|
+
|
|
WALTRACE(("WAL%p: recovery begin...\n", pWal));
|
|
|
|
memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
|
|
@@ -56245,7 +59369,8 @@
|
|
|
|
recovery_error:
|
|
WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
|
|
- walUnlockExclusive(pWal, iLock, nLock);
|
|
+ walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
|
|
+ walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
|
|
return rc;
|
|
}
|
|
|
|
@@ -56253,13 +59378,14 @@
|
|
** Close an open wal-index.
|
|
*/
|
|
static void walIndexClose(Wal *pWal, int isDelete){
|
|
- if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
|
|
+ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE || pWal->bShmUnreliable ){
|
|
int i;
|
|
for(i=0; i<pWal->nWiData; i++){
|
|
sqlite3_free((void *)pWal->apWiData[i]);
|
|
pWal->apWiData[i] = 0;
|
|
}
|
|
- }else{
|
|
+ }
|
|
+ if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
|
|
sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
|
|
}
|
|
}
|
|
@@ -56546,8 +59672,9 @@
|
|
|
|
/*
|
|
** Construct a WalInterator object that can be used to loop over all
|
|
-** pages in the WAL in ascending order. The caller must hold the checkpoint
|
|
-** lock.
|
|
+** pages in the WAL following frame nBackfill in ascending order. Frames
|
|
+** nBackfill or earlier may be included - excluding them is an optimization
|
|
+** only. The caller must hold the checkpoint lock.
|
|
**
|
|
** On success, make *pp point to the newly allocated WalInterator object
|
|
** return SQLITE_OK. Otherwise, return an error code. If this routine
|
|
@@ -56556,7 +59683,7 @@
|
|
** The calling routine should invoke walIteratorFree() to destroy the
|
|
** WalIterator object when it has finished with it.
|
|
*/
|
|
-static int walIteratorInit(Wal *pWal, WalIterator **pp){
|
|
+static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
|
|
WalIterator *p; /* Return value */
|
|
int nSegment; /* Number of segments to merge */
|
|
u32 iLast; /* Last frame in log */
|
|
@@ -56593,34 +59720,32 @@
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}
|
|
|
|
- for(i=0; rc==SQLITE_OK && i<nSegment; i++){
|
|
- volatile ht_slot *aHash;
|
|
- u32 iZero;
|
|
- volatile u32 *aPgno;
|
|
+ for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){
|
|
+ WalHashLoc sLoc;
|
|
|
|
- rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero);
|
|
+ rc = walHashGet(pWal, i, &sLoc);
|
|
if( rc==SQLITE_OK ){
|
|
int j; /* Counter variable */
|
|
int nEntry; /* Number of entries in this segment */
|
|
ht_slot *aIndex; /* Sorted index for this segment */
|
|
|
|
- aPgno++;
|
|
+ sLoc.aPgno++;
|
|
if( (i+1)==nSegment ){
|
|
- nEntry = (int)(iLast - iZero);
|
|
+ nEntry = (int)(iLast - sLoc.iZero);
|
|
}else{
|
|
- nEntry = (int)((u32*)aHash - (u32*)aPgno);
|
|
+ nEntry = (int)((u32*)sLoc.aHash - (u32*)sLoc.aPgno);
|
|
}
|
|
- aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[iZero];
|
|
- iZero++;
|
|
+ aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[sLoc.iZero];
|
|
+ sLoc.iZero++;
|
|
|
|
for(j=0; j<nEntry; j++){
|
|
aIndex[j] = (ht_slot)j;
|
|
}
|
|
- walMergesort((u32 *)aPgno, aTmp, aIndex, &nEntry);
|
|
- p->aSegment[i].iZero = iZero;
|
|
+ walMergesort((u32 *)sLoc.aPgno, aTmp, aIndex, &nEntry);
|
|
+ p->aSegment[i].iZero = sLoc.iZero;
|
|
p->aSegment[i].nEntry = nEntry;
|
|
p->aSegment[i].aIndex = aIndex;
|
|
- p->aSegment[i].aPgno = (u32 *)aPgno;
|
|
+ p->aSegment[i].aPgno = (u32 *)sLoc.aPgno;
|
|
}
|
|
}
|
|
sqlite3_free(aTmp);
|
|
@@ -56627,6 +59752,7 @@
|
|
|
|
if( rc!=SQLITE_OK ){
|
|
walIteratorFree(p);
|
|
+ p = 0;
|
|
}
|
|
*pp = p;
|
|
return rc;
|
|
@@ -56749,13 +59875,6 @@
|
|
pInfo = walCkptInfo(pWal);
|
|
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
|
|
|
|
- /* Allocate the iterator */
|
|
- rc = walIteratorInit(pWal, &pIter);
|
|
- if( rc!=SQLITE_OK ){
|
|
- return rc;
|
|
- }
|
|
- assert( pIter );
|
|
-
|
|
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
|
|
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
|
|
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
|
|
@@ -56792,18 +59911,21 @@
|
|
}
|
|
}
|
|
|
|
- if( pInfo->nBackfill<mxSafeFrame
|
|
+ /* Allocate the iterator */
|
|
+ if( pInfo->nBackfill<mxSafeFrame ){
|
|
+ rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter);
|
|
+ assert( rc==SQLITE_OK || pIter==0 );
|
|
+ }
|
|
+
|
|
+ if( pIter
|
|
&& (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
|
|
){
|
|
- i64 nSize; /* Current size of database file */
|
|
u32 nBackfill = pInfo->nBackfill;
|
|
|
|
pInfo->nBackfillAttempted = mxSafeFrame;
|
|
|
|
/* Sync the WAL to disk */
|
|
- if( sync_flags ){
|
|
- rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
|
|
- }
|
|
+ rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
|
|
|
|
/* If the database may grow as a result of this checkpoint, hint
|
|
** about the eventual size of the db file to the VFS layer.
|
|
@@ -56810,6 +59932,7 @@
|
|
*/
|
|
if( rc==SQLITE_OK ){
|
|
i64 nReq = ((i64)mxPage * szPage);
|
|
+ i64 nSize; /* Current size of database file */
|
|
rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
|
|
if( rc==SQLITE_OK && nSize<nReq ){
|
|
sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
|
|
@@ -56844,8 +59967,8 @@
|
|
i64 szDb = pWal->hdr.nPage*(i64)szPage;
|
|
testcase( IS_BIG_INT(szDb) );
|
|
rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
|
|
- if( rc==SQLITE_OK && sync_flags ){
|
|
- rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
|
|
}
|
|
}
|
|
if( rc==SQLITE_OK ){
|
|
@@ -57055,6 +60178,12 @@
|
|
}
|
|
|
|
/*
|
|
+** This is the value that walTryBeginRead returns when it needs to
|
|
+** be retried.
|
|
+*/
|
|
+#define WAL_RETRY (-1)
|
|
+
|
|
+/*
|
|
** Read the wal-index header from the wal-index and into pWal->hdr.
|
|
** If the wal-header appears to be corrupt, try to reconstruct the
|
|
** wal-index from the WAL before returning.
|
|
@@ -57077,9 +60206,29 @@
|
|
assert( pChanged );
|
|
rc = walIndexPage(pWal, 0, &page0);
|
|
if( rc!=SQLITE_OK ){
|
|
- return rc;
|
|
- };
|
|
- assert( page0 || pWal->writeLock==0 );
|
|
+ assert( rc!=SQLITE_READONLY ); /* READONLY changed to OK in walIndexPage */
|
|
+ if( rc==SQLITE_READONLY_CANTINIT ){
|
|
+ /* The SQLITE_READONLY_CANTINIT return means that the shared-memory
|
|
+ ** was openable but is not writable, and this thread is unable to
|
|
+ ** confirm that another write-capable connection has the shared-memory
|
|
+ ** open, and hence the content of the shared-memory is unreliable,
|
|
+ ** since the shared-memory might be inconsistent with the WAL file
|
|
+ ** and there is no writer on hand to fix it. */
|
|
+ assert( page0==0 );
|
|
+ assert( pWal->writeLock==0 );
|
|
+ assert( pWal->readOnly & WAL_SHM_RDONLY );
|
|
+ pWal->bShmUnreliable = 1;
|
|
+ pWal->exclusiveMode = WAL_HEAPMEMORY_MODE;
|
|
+ *pChanged = 1;
|
|
+ }else{
|
|
+ return rc; /* Any other non-OK return is just an error */
|
|
+ }
|
|
+ }else{
|
|
+ /* page0 can be NULL if the SHM is zero bytes in size and pWal->writeLock
|
|
+ ** is zero, which prevents the SHM from growing */
|
|
+ testcase( page0!=0 );
|
|
+ }
|
|
+ assert( page0!=0 || pWal->writeLock==0 );
|
|
|
|
/* If the first page of the wal-index has been mapped, try to read the
|
|
** wal-index header immediately, without holding any lock. This usually
|
|
@@ -57093,7 +60242,7 @@
|
|
*/
|
|
assert( badHdr==0 || pWal->writeLock==0 );
|
|
if( badHdr ){
|
|
- if( pWal->readOnly & WAL_SHM_RDONLY ){
|
|
+ if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
|
|
if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
|
|
walUnlockShared(pWal, WAL_WRITE_LOCK);
|
|
rc = SQLITE_READONLY_RECOVERY;
|
|
@@ -57123,16 +60272,194 @@
|
|
if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){
|
|
rc = SQLITE_CANTOPEN_BKPT;
|
|
}
|
|
+ if( pWal->bShmUnreliable ){
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ walIndexClose(pWal, 0);
|
|
+ pWal->bShmUnreliable = 0;
|
|
+ assert( pWal->nWiData>0 && pWal->apWiData[0]==0 );
|
|
+ /* walIndexRecover() might have returned SHORT_READ if a concurrent
|
|
+ ** writer truncated the WAL out from under it. If that happens, it
|
|
+ ** indicates that a writer has fixed the SHM file for us, so retry */
|
|
+ if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY;
|
|
+ }
|
|
+ pWal->exclusiveMode = WAL_NORMAL_MODE;
|
|
+ }
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
-** This is the value that walTryBeginRead returns when it needs to
|
|
-** be retried.
|
|
+** Open a transaction in a connection where the shared-memory is read-only
|
|
+** and where we cannot verify that there is a separate write-capable connection
|
|
+** on hand to keep the shared-memory up-to-date with the WAL file.
|
|
+**
|
|
+** This can happen, for example, when the shared-memory is implemented by
|
|
+** memory-mapping a *-shm file, where a prior writer has shut down and
|
|
+** left the *-shm file on disk, and now the present connection is trying
|
|
+** to use that database but lacks write permission on the *-shm file.
|
|
+** Other scenarios are also possible, depending on the VFS implementation.
|
|
+**
|
|
+** Precondition:
|
|
+**
|
|
+** The *-wal file has been read and an appropriate wal-index has been
|
|
+** constructed in pWal->apWiData[] using heap memory instead of shared
|
|
+** memory.
|
|
+**
|
|
+** If this function returns SQLITE_OK, then the read transaction has
|
|
+** been successfully opened. In this case output variable (*pChanged)
|
|
+** is set to true before returning if the caller should discard the
|
|
+** contents of the page cache before proceeding. Or, if it returns
|
|
+** WAL_RETRY, then the heap memory wal-index has been discarded and
|
|
+** the caller should retry opening the read transaction from the
|
|
+** beginning (including attempting to map the *-shm file).
|
|
+**
|
|
+** If an error occurs, an SQLite error code is returned.
|
|
*/
|
|
-#define WAL_RETRY (-1)
|
|
+static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
|
|
+ i64 szWal; /* Size of wal file on disk in bytes */
|
|
+ i64 iOffset; /* Current offset when reading wal file */
|
|
+ u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */
|
|
+ u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */
|
|
+ int szFrame; /* Number of bytes in buffer aFrame[] */
|
|
+ u8 *aData; /* Pointer to data part of aFrame buffer */
|
|
+ volatile void *pDummy; /* Dummy argument for xShmMap */
|
|
+ int rc; /* Return code */
|
|
+ u32 aSaveCksum[2]; /* Saved copy of pWal->hdr.aFrameCksum */
|
|
|
|
+ assert( pWal->bShmUnreliable );
|
|
+ assert( pWal->readOnly & WAL_SHM_RDONLY );
|
|
+ assert( pWal->nWiData>0 && pWal->apWiData[0] );
|
|
+
|
|
+ /* Take WAL_READ_LOCK(0). This has the effect of preventing any
|
|
+ ** writers from running a checkpoint, but does not stop them
|
|
+ ** from running recovery. */
|
|
+ rc = walLockShared(pWal, WAL_READ_LOCK(0));
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ if( rc==SQLITE_BUSY ) rc = WAL_RETRY;
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+ pWal->readLock = 0;
|
|
+
|
|
+ /* Check to see if a separate writer has attached to the shared-memory area,
|
|
+ ** thus making the shared-memory "reliable" again. Do this by invoking
|
|
+ ** the xShmMap() routine of the VFS and looking to see if the return
|
|
+ ** is SQLITE_READONLY instead of SQLITE_READONLY_CANTINIT.
|
|
+ **
|
|
+ ** If the shared-memory is now "reliable" return WAL_RETRY, which will
|
|
+ ** cause the heap-memory WAL-index to be discarded and the actual
|
|
+ ** shared memory to be used in its place.
|
|
+ **
|
|
+ ** This step is important because, even though this connection is holding
|
|
+ ** the WAL_READ_LOCK(0) which prevents a checkpoint, a writer might
|
|
+ ** have already checkpointed the WAL file and, while the current
|
|
+ ** is active, wrap the WAL and start overwriting frames that this
|
|
+ ** process wants to use.
|
|
+ **
|
|
+ ** Once sqlite3OsShmMap() has been called for an sqlite3_file and has
|
|
+ ** returned any SQLITE_READONLY value, it must return only SQLITE_READONLY
|
|
+ ** or SQLITE_READONLY_CANTINIT or some error for all subsequent invocations,
|
|
+ ** even if some external agent does a "chmod" to make the shared-memory
|
|
+ ** writable by us, until sqlite3OsShmUnmap() has been called.
|
|
+ ** This is a requirement on the VFS implementation.
|
|
+ */
|
|
+ rc = sqlite3OsShmMap(pWal->pDbFd, 0, WALINDEX_PGSZ, 0, &pDummy);
|
|
+ assert( rc!=SQLITE_OK ); /* SQLITE_OK not possible for read-only connection */
|
|
+ if( rc!=SQLITE_READONLY_CANTINIT ){
|
|
+ rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc);
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+
|
|
+ /* We reach this point only if the real shared-memory is still unreliable.
|
|
+ ** Assume the in-memory WAL-index substitute is correct and load it
|
|
+ ** into pWal->hdr.
|
|
+ */
|
|
+ memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
|
|
+
|
|
+ /* Make sure some writer hasn't come in and changed the WAL file out
|
|
+ ** from under us, then disconnected, while we were not looking.
|
|
+ */
|
|
+ rc = sqlite3OsFileSize(pWal->pWalFd, &szWal);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+ if( szWal<WAL_HDRSIZE ){
|
|
+ /* If the wal file is too small to contain a wal-header and the
|
|
+ ** wal-index header has mxFrame==0, then it must be safe to proceed
|
|
+ ** reading the database file only. However, the page cache cannot
|
|
+ ** be trusted, as a read/write connection may have connected, written
|
|
+ ** the db, run a checkpoint, truncated the wal file and disconnected
|
|
+ ** since this client's last read transaction. */
|
|
+ *pChanged = 1;
|
|
+ rc = (pWal->hdr.mxFrame==0 ? SQLITE_OK : WAL_RETRY);
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+
|
|
+ /* Check the salt keys at the start of the wal file still match. */
|
|
+ rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+ if( memcmp(&pWal->hdr.aSalt, &aBuf[16], 8) ){
|
|
+ /* Some writer has wrapped the WAL file while we were not looking.
|
|
+ ** Return WAL_RETRY which will cause the in-memory WAL-index to be
|
|
+ ** rebuilt. */
|
|
+ rc = WAL_RETRY;
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+
|
|
+ /* Allocate a buffer to read frames into */
|
|
+ szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE;
|
|
+ aFrame = (u8 *)sqlite3_malloc64(szFrame);
|
|
+ if( aFrame==0 ){
|
|
+ rc = SQLITE_NOMEM_BKPT;
|
|
+ goto begin_unreliable_shm_out;
|
|
+ }
|
|
+ aData = &aFrame[WAL_FRAME_HDRSIZE];
|
|
+
|
|
+ /* Check to see if a complete transaction has been appended to the
|
|
+ ** wal file since the heap-memory wal-index was created. If so, the
|
|
+ ** heap-memory wal-index is discarded and WAL_RETRY returned to
|
|
+ ** the caller. */
|
|
+ aSaveCksum[0] = pWal->hdr.aFrameCksum[0];
|
|
+ aSaveCksum[1] = pWal->hdr.aFrameCksum[1];
|
|
+ for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage);
|
|
+ iOffset+szFrame<=szWal;
|
|
+ iOffset+=szFrame
|
|
+ ){
|
|
+ u32 pgno; /* Database page number for frame */
|
|
+ u32 nTruncate; /* dbsize field from frame header */
|
|
+
|
|
+ /* Read and decode the next log frame. */
|
|
+ rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
|
|
+ if( rc!=SQLITE_OK ) break;
|
|
+ if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break;
|
|
+
|
|
+ /* If nTruncate is non-zero, then a complete transaction has been
|
|
+ ** appended to this wal file. Set rc to WAL_RETRY and break out of
|
|
+ ** the loop. */
|
|
+ if( nTruncate ){
|
|
+ rc = WAL_RETRY;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ pWal->hdr.aFrameCksum[0] = aSaveCksum[0];
|
|
+ pWal->hdr.aFrameCksum[1] = aSaveCksum[1];
|
|
+
|
|
+ begin_unreliable_shm_out:
|
|
+ sqlite3_free(aFrame);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ int i;
|
|
+ for(i=0; i<pWal->nWiData; i++){
|
|
+ sqlite3_free((void*)pWal->apWiData[i]);
|
|
+ pWal->apWiData[i] = 0;
|
|
+ }
|
|
+ pWal->bShmUnreliable = 0;
|
|
+ sqlite3WalEndReadTransaction(pWal);
|
|
+ *pChanged = 1;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
/*
|
|
** Attempt to start a read transaction. This might fail due to a race or
|
|
** other transient condition. When that happens, it returns WAL_RETRY to
|
|
@@ -57147,7 +60474,7 @@
|
|
** checkpointed. If useWal==0 then this routine calls walIndexReadHdr()
|
|
** to make a copy of the wal-index header into pWal->hdr. If the
|
|
** wal-index header has changed, *pChanged is set to 1 (as an indication
|
|
-** to the caller that the local paget cache is obsolete and needs to be
|
|
+** to the caller that the local page cache is obsolete and needs to be
|
|
** flushed.) When useWal==1, the wal-index header is assumed to already
|
|
** be loaded and the pChanged parameter is unused.
|
|
**
|
|
@@ -57193,6 +60520,9 @@
|
|
|
|
assert( pWal->readLock<0 ); /* Not currently locked */
|
|
|
|
+ /* useWal may only be set for read/write connections */
|
|
+ assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 );
|
|
+
|
|
/* Take steps to avoid spinning forever if there is a protocol error.
|
|
**
|
|
** Circumstances that cause a RETRY should only last for the briefest
|
|
@@ -57221,7 +60551,10 @@
|
|
}
|
|
|
|
if( !useWal ){
|
|
- rc = walIndexReadHdr(pWal, pChanged);
|
|
+ assert( rc==SQLITE_OK );
|
|
+ if( pWal->bShmUnreliable==0 ){
|
|
+ rc = walIndexReadHdr(pWal, pChanged);
|
|
+ }
|
|
if( rc==SQLITE_BUSY ){
|
|
/* If there is not a recovery running in another thread or process
|
|
** then convert BUSY errors to WAL_RETRY. If recovery is known to
|
|
@@ -57250,13 +60583,17 @@
|
|
if( rc!=SQLITE_OK ){
|
|
return rc;
|
|
}
|
|
+ else if( pWal->bShmUnreliable ){
|
|
+ return walBeginShmUnreliable(pWal, pChanged);
|
|
+ }
|
|
}
|
|
|
|
+ assert( pWal->nWiData>0 );
|
|
+ assert( pWal->apWiData[0]!=0 );
|
|
pInfo = walCkptInfo(pWal);
|
|
- if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
|
|
+ if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
|
|
#ifdef SQLITE_ENABLE_SNAPSHOT
|
|
- && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0
|
|
- || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr)))
|
|
+ && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
|
|
#endif
|
|
){
|
|
/* The WAL has been completely backfilled (or it is empty).
|
|
@@ -57303,7 +60640,7 @@
|
|
}
|
|
#endif
|
|
for(i=1; i<WAL_NREADER; i++){
|
|
- u32 thisMark = pInfo->aReadMark[i];
|
|
+ u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
|
|
if( mxReadMark<=thisMark && thisMark<=mxFrame ){
|
|
assert( thisMark!=READMARK_NOT_USED );
|
|
mxReadMark = thisMark;
|
|
@@ -57316,7 +60653,7 @@
|
|
for(i=1; i<WAL_NREADER; i++){
|
|
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
|
|
if( rc==SQLITE_OK ){
|
|
- mxReadMark = pInfo->aReadMark[i] = mxFrame;
|
|
+ mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
|
|
mxI = i;
|
|
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
|
|
break;
|
|
@@ -57327,7 +60664,7 @@
|
|
}
|
|
if( mxI==0 ){
|
|
assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
|
|
- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
|
|
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
|
|
}
|
|
|
|
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
|
|
@@ -57368,9 +60705,9 @@
|
|
** we can guarantee that the checkpointer that set nBackfill could not
|
|
** see any pages past pWal->hdr.mxFrame, this problem does not come up.
|
|
*/
|
|
- pWal->minFrame = pInfo->nBackfill+1;
|
|
+ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
|
|
walShmBarrier(pWal);
|
|
- if( pInfo->aReadMark[mxI]!=mxReadMark
|
|
+ if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
|
|
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
|
|
){
|
|
walUnlockShared(pWal, WAL_READ_LOCK(mxI));
|
|
@@ -57421,16 +60758,14 @@
|
|
}else{
|
|
u32 i = pInfo->nBackfillAttempted;
|
|
for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
|
|
- volatile ht_slot *dummy;
|
|
- volatile u32 *aPgno; /* Array of page numbers */
|
|
- u32 iZero; /* Frame corresponding to aPgno[0] */
|
|
+ WalHashLoc sLoc; /* Hash table location */
|
|
u32 pgno; /* Page number in db file */
|
|
i64 iDbOff; /* Offset of db file entry */
|
|
i64 iWalOff; /* Offset of wal file entry */
|
|
|
|
- rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);
|
|
+ rc = walHashGet(pWal, walFramePage(i), &sLoc);
|
|
if( rc!=SQLITE_OK ) break;
|
|
- pgno = aPgno[i-iZero];
|
|
+ pgno = sLoc.aPgno[i-sLoc.iZero];
|
|
iDbOff = (i64)(pgno-1) * szPage;
|
|
|
|
if( iDbOff+szPage<=szDb ){
|
|
@@ -57471,7 +60806,7 @@
|
|
**
|
|
** If the database contents have changes since the previous read
|
|
** transaction, then *pChanged is set to 1 before returning. The
|
|
-** Pager layer will use this to know that is cache is stale and
|
|
+** Pager layer will use this to know that its cache is stale and
|
|
** needs to be flushed.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
|
|
@@ -57533,7 +60868,7 @@
|
|
/* Check that the wal file has not been wrapped. Assuming that it has
|
|
** not, also check that no checkpointer has attempted to checkpoint any
|
|
** frames beyond pSnapshot->mxFrame. If either of these conditions are
|
|
- ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr
|
|
+ ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
|
|
** with *pSnapshot and set *pChanged as appropriate for opening the
|
|
** snapshot. */
|
|
if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
|
|
@@ -57543,11 +60878,12 @@
|
|
memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
|
|
*pChanged = bChanged;
|
|
}else{
|
|
- rc = SQLITE_BUSY_SNAPSHOT;
|
|
+ rc = SQLITE_ERROR_SNAPSHOT;
|
|
}
|
|
|
|
/* Release the shared CKPT lock obtained above. */
|
|
walUnlockShared(pWal, WAL_CKPT_LOCK);
|
|
+ pWal->minFrame = 1;
|
|
}
|
|
|
|
|
|
@@ -57599,7 +60935,7 @@
|
|
** then the WAL is ignored by the reader so return early, as if the
|
|
** WAL were empty.
|
|
*/
|
|
- if( iLast==0 || pWal->readLock==0 ){
|
|
+ if( iLast==0 || (pWal->readLock==0 && pWal->bShmUnreliable==0) ){
|
|
*piRead = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -57630,22 +60966,21 @@
|
|
** table after the current read-transaction had started.
|
|
*/
|
|
iMinHash = walFramePage(pWal->minFrame);
|
|
- for(iHash=walFramePage(iLast); iHash>=iMinHash && iRead==0; iHash--){
|
|
- volatile ht_slot *aHash; /* Pointer to hash table */
|
|
- volatile u32 *aPgno; /* Pointer to array of page numbers */
|
|
- u32 iZero; /* Frame number corresponding to aPgno[0] */
|
|
+ for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){
|
|
+ WalHashLoc sLoc; /* Hash table location */
|
|
int iKey; /* Hash slot index */
|
|
int nCollide; /* Number of hash collisions remaining */
|
|
int rc; /* Error code */
|
|
|
|
- rc = walHashGet(pWal, iHash, &aHash, &aPgno, &iZero);
|
|
+ rc = walHashGet(pWal, iHash, &sLoc);
|
|
if( rc!=SQLITE_OK ){
|
|
return rc;
|
|
}
|
|
nCollide = HASHTABLE_NSLOT;
|
|
- for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
|
|
- u32 iFrame = aHash[iKey] + iZero;
|
|
- if( iFrame<=iLast && iFrame>=pWal->minFrame && aPgno[aHash[iKey]]==pgno ){
|
|
+ for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
|
|
+ u32 iFrame = sLoc.aHash[iKey] + sLoc.iZero;
|
|
+ if( iFrame<=iLast && iFrame>=pWal->minFrame
|
|
+ && sLoc.aPgno[sLoc.aHash[iKey]]==pgno ){
|
|
assert( iFrame>iRead || CORRUPT_DB );
|
|
iRead = iFrame;
|
|
}
|
|
@@ -57653,6 +60988,7 @@
|
|
return SQLITE_CORRUPT_BKPT;
|
|
}
|
|
}
|
|
+ if( iRead ) break;
|
|
}
|
|
|
|
#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
|
|
@@ -57662,8 +60998,8 @@
|
|
{
|
|
u32 iRead2 = 0;
|
|
u32 iTest;
|
|
- assert( pWal->minFrame>0 );
|
|
- for(iTest=iLast; iTest>=pWal->minFrame; iTest--){
|
|
+ assert( pWal->bShmUnreliable || pWal->minFrame>0 );
|
|
+ for(iTest=iLast; iTest>=pWal->minFrame && iTest>0; iTest--){
|
|
if( walFramePgno(pWal, iTest)==pgno ){
|
|
iRead2 = iTest;
|
|
break;
|
|
@@ -57951,8 +61287,8 @@
|
|
iOffset += iFirstAmt;
|
|
iAmt -= iFirstAmt;
|
|
pContent = (void*)(iFirstAmt + (char*)pContent);
|
|
- assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
|
|
- rc = sqlite3OsSync(p->pFd, p->syncFlags & SQLITE_SYNC_MASK);
|
|
+ assert( WAL_SYNC_FLAGS(p->syncFlags)!=0 );
|
|
+ rc = sqlite3OsSync(p->pFd, WAL_SYNC_FLAGS(p->syncFlags));
|
|
if( iAmt==0 || rc ) return rc;
|
|
}
|
|
rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
|
|
@@ -58122,10 +61458,10 @@
|
|
** an out-of-order write following a WAL restart could result in
|
|
** database corruption. See the ticket:
|
|
**
|
|
- ** http://localhost:591/sqlite/info/ff5be73dee
|
|
+ ** https://sqlite.org/src/info/ff5be73dee
|
|
*/
|
|
- if( pWal->syncHeader && sync_flags ){
|
|
- rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
|
|
+ if( pWal->syncHeader ){
|
|
+ rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
|
|
if( rc ) return rc;
|
|
}
|
|
}
|
|
@@ -58200,7 +61536,7 @@
|
|
** sector boundary is synced; the part of the last frame that extends
|
|
** past the sector boundary is written after the sync.
|
|
*/
|
|
- if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
|
|
+ if( isCommit && WAL_SYNC_FLAGS(sync_flags)!=0 ){
|
|
int bSync = 1;
|
|
if( pWal->padToSectorBoundary ){
|
|
int sectorSize = sqlite3SectorSize(pWal->pWalFd);
|
|
@@ -58216,7 +61552,7 @@
|
|
}
|
|
if( bSync ){
|
|
assert( rc==SQLITE_OK );
|
|
- rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
|
|
+ rc = sqlite3OsSync(w.pFd, WAL_SYNC_FLAGS(sync_flags));
|
|
}
|
|
}
|
|
|
|
@@ -58439,24 +61775,24 @@
|
|
assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
|
|
|
|
if( op==0 ){
|
|
- if( pWal->exclusiveMode ){
|
|
- pWal->exclusiveMode = 0;
|
|
+ if( pWal->exclusiveMode!=WAL_NORMAL_MODE ){
|
|
+ pWal->exclusiveMode = WAL_NORMAL_MODE;
|
|
if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){
|
|
- pWal->exclusiveMode = 1;
|
|
+ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
|
|
}
|
|
- rc = pWal->exclusiveMode==0;
|
|
+ rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
|
|
}else{
|
|
/* Already in locking_mode=NORMAL */
|
|
rc = 0;
|
|
}
|
|
}else if( op>0 ){
|
|
- assert( pWal->exclusiveMode==0 );
|
|
+ assert( pWal->exclusiveMode==WAL_NORMAL_MODE );
|
|
assert( pWal->readLock>=0 );
|
|
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
|
|
- pWal->exclusiveMode = 1;
|
|
+ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
|
|
rc = 1;
|
|
}else{
|
|
- rc = pWal->exclusiveMode==0;
|
|
+ rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -58519,6 +61855,43 @@
|
|
if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1;
|
|
return 0;
|
|
}
|
|
+
|
|
+/*
|
|
+** The caller currently has a read transaction open on the database.
|
|
+** This function takes a SHARED lock on the CHECKPOINTER slot and then
|
|
+** checks if the snapshot passed as the second argument is still
|
|
+** available. If so, SQLITE_OK is returned.
|
|
+**
|
|
+** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
|
|
+** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error
|
|
+** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER
|
|
+** lock is released before returning.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){
|
|
+ int rc;
|
|
+ rc = walLockShared(pWal, WAL_CKPT_LOCK);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
|
|
+ if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
|
|
+ || pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
|
|
+ ){
|
|
+ rc = SQLITE_ERROR_SNAPSHOT;
|
|
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Release a lock obtained by an earlier successful call to
|
|
+** sqlite3WalSnapshotCheck().
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WalSnapshotUnlock(Wal *pWal){
|
|
+ assert( pWal );
|
|
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
|
|
+}
|
|
+
|
|
+
|
|
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
|
|
|
#ifdef SQLITE_ENABLE_ZIPVFS
|
|
@@ -59063,30 +62436,31 @@
|
|
** eState==FAULT: Cursor fault with skipNext as error code.
|
|
*/
|
|
struct BtCursor {
|
|
+ u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
|
+ u8 curFlags; /* zero or more BTCF_* flags defined below */
|
|
+ u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */
|
|
+ u8 hints; /* As configured by CursorSetHints() */
|
|
+ int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
|
|
+ ** Error code if eState==CURSOR_FAULT */
|
|
Btree *pBtree; /* The Btree to which this cursor belongs */
|
|
+ Pgno *aOverflow; /* Cache of overflow page locations */
|
|
+ void *pKey; /* Saved key that was cursor last known position */
|
|
+ /* All fields above are zeroed when the cursor is allocated. See
|
|
+ ** sqlite3BtreeCursorZero(). Fields that follow must be manually
|
|
+ ** initialized. */
|
|
+#define BTCURSOR_FIRST_UNINIT pBt /* Name of first uninitialized field */
|
|
BtShared *pBt; /* The BtShared this cursor points to */
|
|
BtCursor *pNext; /* Forms a linked list of all cursors */
|
|
- Pgno *aOverflow; /* Cache of overflow page locations */
|
|
CellInfo info; /* A parse of the cell we are pointing at */
|
|
i64 nKey; /* Size of pKey, or last integer key */
|
|
- void *pKey; /* Saved key that was cursor last known position */
|
|
Pgno pgnoRoot; /* The root page of this tree */
|
|
- int nOvflAlloc; /* Allocated size of aOverflow[] array */
|
|
- int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
|
|
- ** Error code if eState==CURSOR_FAULT */
|
|
- u8 curFlags; /* zero or more BTCF_* flags defined below */
|
|
- u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */
|
|
- u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
|
- u8 hints; /* As configured by CursorSetHints() */
|
|
- /* All fields above are zeroed when the cursor is allocated. See
|
|
- ** sqlite3BtreeCursorZero(). Fields that follow must be manually
|
|
- ** initialized. */
|
|
i8 iPage; /* Index of current page in apPage */
|
|
u8 curIntKey; /* Value of apPage[0]->intKey */
|
|
u16 ix; /* Current index for apPage[iPage] */
|
|
u16 aiIdx[BTCURSOR_MAX_DEPTH-1]; /* Current index in apPage[i] */
|
|
struct KeyInfo *pKeyInfo; /* Arg passed to comparison function */
|
|
- MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
|
+ MemPage *pPage; /* Current page */
|
|
+ MemPage *apPage[BTCURSOR_MAX_DEPTH-1]; /* Stack of parents of current page */
|
|
};
|
|
|
|
/*
|
|
@@ -59129,8 +62503,8 @@
|
|
** Do nothing else with this cursor. Any attempt to use the cursor
|
|
** should return the error code stored in BtCursor.skipNext
|
|
*/
|
|
-#define CURSOR_INVALID 0
|
|
-#define CURSOR_VALID 1
|
|
+#define CURSOR_VALID 0
|
|
+#define CURSOR_INVALID 1
|
|
#define CURSOR_SKIPNEXT 2
|
|
#define CURSOR_REQUIRESEEK 3
|
|
#define CURSOR_FAULT 4
|
|
@@ -59447,10 +62821,10 @@
|
|
skipOk = 0;
|
|
}
|
|
}
|
|
- db->skipBtreeMutex = skipOk;
|
|
+ db->noSharedCache = skipOk;
|
|
}
|
|
SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
|
|
- if( db->skipBtreeMutex==0 ) btreeEnterAll(db);
|
|
+ if( db->noSharedCache==0 ) btreeEnterAll(db);
|
|
}
|
|
static void SQLITE_NOINLINE btreeLeaveAll(sqlite3 *db){
|
|
int i;
|
|
@@ -59462,7 +62836,7 @@
|
|
}
|
|
}
|
|
SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3 *db){
|
|
- if( db->skipBtreeMutex==0 ) btreeLeaveAll(db);
|
|
+ if( db->noSharedCache==0 ) btreeLeaveAll(db);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
@@ -59675,6 +63049,34 @@
|
|
#define hasReadConflicts(a, b) 0
|
|
#endif
|
|
|
|
+/*
|
|
+** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single
|
|
+** (MemPage*) as an argument. The (MemPage*) must not be NULL.
|
|
+**
|
|
+** If SQLITE_DEBUG is not defined, then this macro is equivalent to
|
|
+** SQLITE_CORRUPT_BKPT. Or, if SQLITE_DEBUG is set, then the log message
|
|
+** normally produced as a side-effect of SQLITE_CORRUPT_BKPT is augmented
|
|
+** with the page number and filename associated with the (MemPage*).
|
|
+*/
|
|
+#ifdef SQLITE_DEBUG
|
|
+int corruptPageError(int lineno, MemPage *p){
|
|
+ char *zMsg;
|
|
+ sqlite3BeginBenignMalloc();
|
|
+ zMsg = sqlite3_mprintf("database corruption page %d of %s",
|
|
+ (int)p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0)
|
|
+ );
|
|
+ sqlite3EndBenignMalloc();
|
|
+ if( zMsg ){
|
|
+ sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
|
|
+ }
|
|
+ sqlite3_free(zMsg);
|
|
+ return SQLITE_CORRUPT_BKPT;
|
|
+}
|
|
+# define SQLITE_CORRUPT_PAGE(pMemPage) corruptPageError(__LINE__, pMemPage)
|
|
+#else
|
|
+# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno)
|
|
+#endif
|
|
+
|
|
#ifndef SQLITE_OMIT_SHARED_CACHE
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
@@ -60002,7 +63404,9 @@
|
|
|
|
#endif /* SQLITE_OMIT_SHARED_CACHE */
|
|
|
|
-static void releasePage(MemPage *pPage); /* Forward reference */
|
|
+static void releasePage(MemPage *pPage); /* Forward reference */
|
|
+static void releasePageOne(MemPage *pPage); /* Forward reference */
|
|
+static void releasePageNotNull(MemPage *pPage); /* Forward reference */
|
|
|
|
/*
|
|
***** This routine is used inside of assert() only ****
|
|
@@ -60161,11 +63565,13 @@
|
|
*/
|
|
static void btreeReleaseAllCursorPages(BtCursor *pCur){
|
|
int i;
|
|
- for(i=0; i<=pCur->iPage; i++){
|
|
- releasePage(pCur->apPage[i]);
|
|
- pCur->apPage[i] = 0;
|
|
+ if( pCur->iPage>=0 ){
|
|
+ for(i=0; i<pCur->iPage; i++){
|
|
+ releasePageNotNull(pCur->apPage[i]);
|
|
+ }
|
|
+ releasePageNotNull(pCur->pPage);
|
|
+ pCur->iPage = -1;
|
|
}
|
|
- pCur->iPage = -1;
|
|
}
|
|
|
|
/*
|
|
@@ -60294,7 +63700,7 @@
|
|
return rc;
|
|
}
|
|
}else{
|
|
- testcase( p->iPage>0 );
|
|
+ testcase( p->iPage>=0 );
|
|
btreeReleaseAllCursorPages(p);
|
|
}
|
|
}
|
|
@@ -60334,7 +63740,7 @@
|
|
if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
|
|
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
|
|
if( pIdxKey->nField==0 ){
|
|
- rc = SQLITE_CORRUPT_PGNO(pCur->apPage[pCur->iPage]->pgno);
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
goto moveto_done;
|
|
}
|
|
}else{
|
|
@@ -60395,10 +63801,25 @@
|
|
** back to where it ought to be if this routine returns true.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur){
|
|
- return pCur->eState!=CURSOR_VALID;
|
|
+ assert( EIGHT_BYTE_ALIGNMENT(pCur)
|
|
+ || pCur==sqlite3BtreeFakeValidCursor() );
|
|
+ assert( offsetof(BtCursor, eState)==0 );
|
|
+ assert( sizeof(pCur->eState)==1 );
|
|
+ return CURSOR_VALID != *(u8*)pCur;
|
|
}
|
|
|
|
/*
|
|
+** Return a pointer to a fake BtCursor object that will always answer
|
|
+** false to the sqlite3BtreeCursorHasMoved() routine above. The fake
|
|
+** cursor returned must not be used with any other Btree interface.
|
|
+*/
|
|
+SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void){
|
|
+ static u8 fakeCursor = CURSOR_VALID;
|
|
+ assert( offsetof(BtCursor, eState)==0 );
|
|
+ return (BtCursor*)&fakeCursor;
|
|
+}
|
|
+
|
|
+/*
|
|
** This routine restores a cursor back to its original position after it
|
|
** has been moved by some outside activity (such as a btree rebalance or
|
|
** a row having been deleted out from under the cursor).
|
|
@@ -60947,8 +64368,11 @@
|
|
int sz2 = 0;
|
|
int sz = get2byte(&data[iFree+2]);
|
|
int top = get2byte(&data[hdr+5]);
|
|
+ if( top>=iFree ){
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
+ }
|
|
if( iFree2 ){
|
|
- if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ assert( iFree+sz<=iFree2 ); /* Verified by pageFindSlot() */
|
|
sz2 = get2byte(&data[iFree2+2]);
|
|
assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize );
|
|
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
|
|
@@ -60979,13 +64403,13 @@
|
|
** if PRAGMA cell_size_check=ON.
|
|
*/
|
|
if( pc<iCellFirst || pc>iCellLast ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
assert( pc>=iCellFirst && pc<=iCellLast );
|
|
size = pPage->xCellSize(pPage, &src[pc]);
|
|
cbrk -= size;
|
|
if( cbrk<iCellFirst || pc+size>usableSize ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
assert( cbrk+size<=usableSize && cbrk>=iCellFirst );
|
|
testcase( cbrk+size==usableSize );
|
|
@@ -61005,7 +64429,7 @@
|
|
|
|
defragment_out:
|
|
if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
assert( cbrk>=iCellFirst );
|
|
put2byte(&data[hdr+5], cbrk);
|
|
@@ -61037,16 +64461,10 @@
|
|
int pc = get2byte(&aData[iAddr]);
|
|
int x;
|
|
int usableSize = pPg->pBt->usableSize;
|
|
+ int size; /* Size of the free slot */
|
|
|
|
assert( pc>0 );
|
|
- do{
|
|
- int size; /* Size of the free slot */
|
|
- /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
|
|
- ** increasing offset. */
|
|
- if( pc>usableSize-4 || pc<iAddr+4 ){
|
|
- *pRc = SQLITE_CORRUPT_PGNO(pPg->pgno);
|
|
- return 0;
|
|
- }
|
|
+ while( pc<=usableSize-4 ){
|
|
/* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each
|
|
** freeblock form a big-endian integer which is the size of the freeblock
|
|
** in bytes, including the 4-byte header. */
|
|
@@ -61054,8 +64472,8 @@
|
|
if( (x = size - nByte)>=0 ){
|
|
testcase( x==4 );
|
|
testcase( x==3 );
|
|
- if( pc < pPg->cellOffset+2*pPg->nCell || size+pc > usableSize ){
|
|
- *pRc = SQLITE_CORRUPT_PGNO(pPg->pgno);
|
|
+ if( size+pc > usableSize ){
|
|
+ *pRc = SQLITE_CORRUPT_PAGE(pPg);
|
|
return 0;
|
|
}else if( x<4 ){
|
|
/* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
|
|
@@ -61075,7 +64493,11 @@
|
|
}
|
|
iAddr = pc;
|
|
pc = get2byte(&aData[pc]);
|
|
- }while( pc );
|
|
+ if( pc<iAddr+size ) break;
|
|
+ }
|
|
+ if( pc ){
|
|
+ *pRc = SQLITE_CORRUPT_PAGE(pPg);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -61122,7 +64544,7 @@
|
|
if( top==0 && pPage->pBt->usableSize==65536 ){
|
|
top = 65536;
|
|
}else{
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
}
|
|
|
|
@@ -61189,7 +64611,7 @@
|
|
u8 hdr; /* Page header size. 0 or 100 */
|
|
u8 nFrag = 0; /* Reduction in fragmentation */
|
|
u16 iOrigSize = iSize; /* Original value of iSize */
|
|
- u32 iLast = pPage->pBt->usableSize-4; /* Largest possible freeblock offset */
|
|
+ u16 x; /* Offset to cell content area */
|
|
u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */
|
|
unsigned char *data = pPage->aData; /* Page content */
|
|
|
|
@@ -61199,14 +64621,8 @@
|
|
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
|
|
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
|
assert( iSize>=4 ); /* Minimum cell size is 4 */
|
|
- assert( iStart<=iLast );
|
|
+ assert( iStart<=pPage->pBt->usableSize-4 );
|
|
|
|
- /* Overwrite deleted information with zeros when the secure_delete
|
|
- ** option is enabled */
|
|
- if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
|
|
- memset(&data[iStart], 0, iSize);
|
|
- }
|
|
-
|
|
/* The list of freeblocks must be in ascending order. Find the
|
|
** spot on the list where iStart should be inserted.
|
|
*/
|
|
@@ -61218,11 +64634,13 @@
|
|
while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
|
|
if( iFreeBlk<iPtr+4 ){
|
|
if( iFreeBlk==0 ) break;
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
iPtr = iFreeBlk;
|
|
}
|
|
- if( iFreeBlk>iLast ) return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ if( iFreeBlk>pPage->pBt->usableSize-4 ){
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
+ }
|
|
assert( iFreeBlk>iPtr || iFreeBlk==0 );
|
|
|
|
/* At this point:
|
|
@@ -61233,10 +64651,10 @@
|
|
*/
|
|
if( iFreeBlk && iEnd+3>=iFreeBlk ){
|
|
nFrag = iFreeBlk - iEnd;
|
|
- if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
|
|
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
|
|
if( iEnd > pPage->pBt->usableSize ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
iSize = iEnd - iStart;
|
|
iFreeBlk = get2byte(&data[iFreeBlk]);
|
|
@@ -61249,28 +64667,34 @@
|
|
if( iPtr>hdr+1 ){
|
|
int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
|
|
if( iPtrEnd+3>=iStart ){
|
|
- if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PAGE(pPage);
|
|
nFrag += iStart - iPtrEnd;
|
|
iSize = iEnd - iPtr;
|
|
iStart = iPtr;
|
|
}
|
|
}
|
|
- if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
|
|
data[hdr+7] -= nFrag;
|
|
}
|
|
- if( iStart==get2byte(&data[hdr+5]) ){
|
|
+ x = get2byte(&data[hdr+5]);
|
|
+ if( iStart<=x ){
|
|
/* 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( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ if( iStart<x || iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
|
|
put2byte(&data[hdr+1], iFreeBlk);
|
|
put2byte(&data[hdr+5], iEnd);
|
|
}else{
|
|
/* Insert the new freeblock into the freelist */
|
|
put2byte(&data[iPtr], iStart);
|
|
- put2byte(&data[iStart], iFreeBlk);
|
|
- put2byte(&data[iStart+2], iSize);
|
|
}
|
|
+ if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
|
|
+ /* Overwrite deleted information with zeros when the secure_delete
|
|
+ ** option is enabled */
|
|
+ memset(&data[iStart], 0, iSize);
|
|
+ }
|
|
+ put2byte(&data[iStart], iFreeBlk);
|
|
+ put2byte(&data[iStart+2], iSize);
|
|
pPage->nFree += iOrigSize;
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -61330,7 +64754,7 @@
|
|
}else{
|
|
/* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
|
|
** an error. */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
pPage->max1bytePayload = pBt->max1bytePayload;
|
|
return SQLITE_OK;
|
|
@@ -61371,7 +64795,7 @@
|
|
/* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
|
|
** the b-tree page type. */
|
|
if( decodeFlags(pPage, data[hdr]) ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
|
|
pPage->maskPage = (u16)(pBt->pageSize - 1);
|
|
@@ -61390,7 +64814,7 @@
|
|
pPage->nCell = get2byte(&data[hdr+3]);
|
|
if( pPage->nCell>MX_CELL(pBt) ){
|
|
/* To many cells for a single page. The page must be corrupt */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
testcase( pPage->nCell==MX_CELL(pBt) );
|
|
/* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
|
|
@@ -61418,12 +64842,12 @@
|
|
testcase( pc==iCellFirst );
|
|
testcase( pc==iCellLast );
|
|
if( pc<iCellFirst || pc>iCellLast ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
sz = pPage->xCellSize(pPage, &data[pc]);
|
|
testcase( pc+sz==usableSize );
|
|
if( pc+sz>usableSize ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
}
|
|
if( !pPage->leaf ) iCellLast++;
|
|
@@ -61441,12 +64865,12 @@
|
|
/* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will
|
|
** always be at least one cell before the first freeblock.
|
|
*/
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
while( 1 ){
|
|
if( pc>iCellLast ){
|
|
/* Freeblock off the end of the page */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
next = get2byte(&data[pc]);
|
|
size = get2byte(&data[pc+2]);
|
|
@@ -61456,11 +64880,11 @@
|
|
}
|
|
if( next>0 ){
|
|
/* Freeblock not in ascending order */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
if( pc+size>(unsigned int)usableSize ){
|
|
/* Last freeblock extends past page end */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
}
|
|
|
|
@@ -61472,7 +64896,7 @@
|
|
** area, according to the page header, lies within the page.
|
|
*/
|
|
if( nFree>usableSize ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
pPage->nFree = (u16)(nFree - iCellFirst);
|
|
pPage->isInit = 1;
|
|
@@ -61585,7 +65009,7 @@
|
|
}
|
|
SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){
|
|
assert( sqlite3BtreeHoldsMutex(p) );
|
|
- assert( ((p->pBt->nPage)&0x8000000)==0 );
|
|
+ assert( ((p->pBt->nPage)&0x80000000)==0 );
|
|
return btreePagecount(p->pBt);
|
|
}
|
|
|
|
@@ -61612,7 +65036,7 @@
|
|
int rc;
|
|
DbPage *pDbPage;
|
|
assert( sqlite3_mutex_held(pBt->mutex) );
|
|
- assert( pCur==0 || ppPage==&pCur->apPage[pCur->iPage] );
|
|
+ assert( pCur==0 || ppPage==&pCur->pPage );
|
|
assert( pCur==0 || bReadOnly==pCur->curPagerFlags );
|
|
assert( pCur==0 || pCur->iPage>0 );
|
|
|
|
@@ -61646,7 +65070,10 @@
|
|
return SQLITE_OK;
|
|
|
|
getAndInitPage_error:
|
|
- if( pCur ) pCur->iPage--;
|
|
+ if( pCur ){
|
|
+ pCur->iPage--;
|
|
+ pCur->pPage = pCur->apPage[pCur->iPage];
|
|
+ }
|
|
testcase( pgno==0 );
|
|
assert( pgno!=0 || rc==SQLITE_CORRUPT );
|
|
return rc;
|
|
@@ -61655,6 +65082,8 @@
|
|
/*
|
|
** Release a MemPage. This should be called once for each prior
|
|
** call to btreeGetPage.
|
|
+**
|
|
+** Page1 is a special case and must be released using releasePageOne().
|
|
*/
|
|
static void releasePageNotNull(MemPage *pPage){
|
|
assert( pPage->aData );
|
|
@@ -61668,6 +65097,16 @@
|
|
static void releasePage(MemPage *pPage){
|
|
if( pPage ) releasePageNotNull(pPage);
|
|
}
|
|
+static void releasePageOne(MemPage *pPage){
|
|
+ assert( pPage!=0 );
|
|
+ assert( pPage->aData );
|
|
+ assert( pPage->pBt );
|
|
+ assert( pPage->pDbPage!=0 );
|
|
+ assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
|
|
+ assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
|
|
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
|
+ sqlite3PagerUnrefPageOne(pPage->pDbPage);
|
|
+}
|
|
|
|
/*
|
|
** Get an unused page.
|
|
@@ -61733,7 +65172,8 @@
|
|
BtShared *pBt = (BtShared*)pArg;
|
|
assert( pBt->db );
|
|
assert( sqlite3_mutex_held(pBt->db->mutex) );
|
|
- return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
|
|
+ return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
|
|
+ sqlite3PagerFile(pBt->pPager));
|
|
}
|
|
|
|
/*
|
|
@@ -61911,7 +65351,7 @@
|
|
}
|
|
pBt->openFlags = (u8)flags;
|
|
pBt->db = db;
|
|
- sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
|
|
+ sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
|
|
p->pBt = pBt;
|
|
|
|
pBt->pCursor = 0;
|
|
@@ -62452,7 +65892,8 @@
|
|
** set to the value passed to this function as the second parameter,
|
|
** set it so.
|
|
*/
|
|
-#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS
|
|
+#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS \
|
|
+ && !defined(SQLITE_OMIT_WAL)
|
|
static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){
|
|
sqlite3 *db;
|
|
Db *pDb;
|
|
@@ -62472,6 +65913,10 @@
|
|
# define setDefaultSyncFlag(pBt,safety_level)
|
|
#endif
|
|
|
|
+/* Forward declaration */
|
|
+static int newDatabase(BtShared*);
|
|
+
|
|
+
|
|
/*
|
|
** Get a reference to pPage1 of the database file. This will
|
|
** also acquire a readlock on that file.
|
|
@@ -62503,6 +65948,9 @@
|
|
if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
|
|
nPage = nPageFile;
|
|
}
|
|
+ if( (pBt->db->flags & SQLITE_ResetDatabase)!=0 ){
|
|
+ nPage = 0;
|
|
+ }
|
|
if( nPage>0 ){
|
|
u32 pageSize;
|
|
u32 usableSize;
|
|
@@ -62546,7 +65994,7 @@
|
|
}else{
|
|
setDefaultSyncFlag(pBt, SQLITE_DEFAULT_WAL_SYNCHRONOUS+1);
|
|
if( isOpen==0 ){
|
|
- releasePage(pPage1);
|
|
+ releasePageOne(pPage1);
|
|
return SQLITE_OK;
|
|
}
|
|
}
|
|
@@ -62593,7 +66041,7 @@
|
|
** zero and return SQLITE_OK. The caller will call this function
|
|
** again with the correct page-size.
|
|
*/
|
|
- releasePage(pPage1);
|
|
+ releasePageOne(pPage1);
|
|
pBt->usableSize = usableSize;
|
|
pBt->pageSize = pageSize;
|
|
freeTempSpace(pBt);
|
|
@@ -62601,7 +66049,7 @@
|
|
pageSize-usableSize);
|
|
return rc;
|
|
}
|
|
- if( (pBt->db->flags & SQLITE_WriteSchema)==0 && nPage>nPageFile ){
|
|
+ if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){
|
|
rc = SQLITE_CORRUPT_BKPT;
|
|
goto page1_init_failed;
|
|
}
|
|
@@ -62647,7 +66095,7 @@
|
|
return SQLITE_OK;
|
|
|
|
page1_init_failed:
|
|
- releasePage(pPage1);
|
|
+ releasePageOne(pPage1);
|
|
pBt->pPage1 = 0;
|
|
return rc;
|
|
}
|
|
@@ -62692,7 +66140,7 @@
|
|
assert( pPage1->aData );
|
|
assert( sqlite3PagerRefcount(pBt->pPager)==1 );
|
|
pBt->pPage1 = 0;
|
|
- releasePageNotNull(pPage1);
|
|
+ releasePageOne(pPage1);
|
|
}
|
|
}
|
|
|
|
@@ -62789,7 +66237,7 @@
|
|
** when A already has a read lock, we encourage A to give up and let B
|
|
** proceed.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|
+SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
|
|
BtShared *pBt = p->pBt;
|
|
int rc = SQLITE_OK;
|
|
|
|
@@ -62805,6 +66253,12 @@
|
|
}
|
|
assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
|
|
|
|
+ if( (p->db->flags & SQLITE_ResetDatabase)
|
|
+ && sqlite3PagerIsreadonly(pBt->pPager)==0
|
|
+ ){
|
|
+ pBt->btsFlags &= ~BTS_READ_ONLY;
|
|
+ }
|
|
+
|
|
/* Write transactions are not possible on a read-only database */
|
|
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
|
|
rc = SQLITE_READONLY;
|
|
@@ -62864,6 +66318,11 @@
|
|
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
|
|
if( rc==SQLITE_OK ){
|
|
rc = newDatabase(pBt);
|
|
+ }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
|
|
+ /* if there was no transaction opened when this function was
|
|
+ ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error
|
|
+ ** code to SQLITE_BUSY. */
|
|
+ rc = SQLITE_BUSY;
|
|
}
|
|
}
|
|
}
|
|
@@ -62873,6 +66332,7 @@
|
|
}
|
|
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
|
|
btreeInvokeBusyHandler(pBt) );
|
|
+ sqlite3PagerResetLockTimeout(pBt->pPager);
|
|
|
|
if( rc==SQLITE_OK ){
|
|
if( p->inTrans==TRANS_NONE ){
|
|
@@ -62914,14 +66374,18 @@
|
|
}
|
|
}
|
|
|
|
-
|
|
trans_begun:
|
|
- if( rc==SQLITE_OK && wrflag ){
|
|
- /* This call makes sure that the pager has the correct number of
|
|
- ** open savepoints. If the second parameter is greater than 0 and
|
|
- ** the sub-journal is not already open, then it will be opened here.
|
|
- */
|
|
- rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( pSchemaVersion ){
|
|
+ *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]);
|
|
+ }
|
|
+ if( wrflag ){
|
|
+ /* This call makes sure that the pager has the correct number of
|
|
+ ** open savepoints. If the second parameter is greater than 0 and
|
|
+ ** the sub-journal is not already open, then it will be opened here.
|
|
+ */
|
|
+ rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
|
|
+ }
|
|
}
|
|
|
|
btreeIntegrity(p);
|
|
@@ -62987,7 +66451,7 @@
|
|
if( eType==PTRMAP_OVERFLOW2 ){
|
|
/* The pointer is always the first 4 bytes of the page in this case. */
|
|
if( get4byte(pPage->aData)!=iFrom ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
put4byte(pPage->aData, iTo);
|
|
}else{
|
|
@@ -63006,7 +66470,7 @@
|
|
pPage->xParseCell(pPage, pCell, &info);
|
|
if( info.nLocal<info.nPayload ){
|
|
if( pCell+info.nSize > pPage->aData+pPage->pBt->usableSize ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
if( iFrom==get4byte(pCell+info.nSize-4) ){
|
|
put4byte(pCell+info.nSize-4, iTo);
|
|
@@ -63024,7 +66488,7 @@
|
|
if( i==nCell ){
|
|
if( eType!=PTRMAP_BTREE ||
|
|
get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
put4byte(&pPage->aData[pPage->hdrOffset+8], iTo);
|
|
}
|
|
@@ -63059,6 +66523,7 @@
|
|
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
|
|
assert( sqlite3_mutex_held(pBt->mutex) );
|
|
assert( pDbPage->pBt==pBt );
|
|
+ if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT;
|
|
|
|
/* Move page iDbPage from its current location to page number iFreePage */
|
|
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
|
|
@@ -63544,7 +67009,6 @@
|
|
if( pBtree ){
|
|
sqlite3BtreeEnter(pBtree);
|
|
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
|
- int i;
|
|
if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
|
|
if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){
|
|
rc = saveCursorPosition(p);
|
|
@@ -63558,10 +67022,7 @@
|
|
p->eState = CURSOR_FAULT;
|
|
p->skipNext = errCode;
|
|
}
|
|
- for(i=0; i<=p->iPage; i++){
|
|
- releasePage(p->apPage[i]);
|
|
- p->apPage[i] = 0;
|
|
- }
|
|
+ btreeReleaseAllCursorPages(p);
|
|
}
|
|
sqlite3BtreeLeave(pBtree);
|
|
}
|
|
@@ -63618,7 +67079,7 @@
|
|
if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
|
|
testcase( pBt->nPage!=nPage );
|
|
pBt->nPage = nPage;
|
|
- releasePage(pPage1);
|
|
+ releasePageOne(pPage1);
|
|
}
|
|
assert( countValidCursors(pBt, 1)==0 );
|
|
pBt->inTransaction = TRANS_READ;
|
|
@@ -63850,7 +67311,7 @@
|
|
** of run-time by skipping the initialization of those elements.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){
|
|
- memset(p, 0, offsetof(BtCursor, iPage));
|
|
+ memset(p, 0, offsetof(BtCursor, BTCURSOR_FIRST_UNINIT));
|
|
}
|
|
|
|
/*
|
|
@@ -63860,10 +67321,8 @@
|
|
SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
|
|
Btree *pBtree = pCur->pBtree;
|
|
if( pBtree ){
|
|
- int i;
|
|
BtShared *pBt = pCur->pBt;
|
|
sqlite3BtreeEnter(pBtree);
|
|
- sqlite3BtreeClearCursor(pCur);
|
|
assert( pBt->pCursor!=0 );
|
|
if( pBt->pCursor==pCur ){
|
|
pBt->pCursor = pCur->pNext;
|
|
@@ -63877,12 +67336,10 @@
|
|
pPrev = pPrev->pNext;
|
|
}while( ALWAYS(pPrev) );
|
|
}
|
|
- for(i=0; i<=pCur->iPage; i++){
|
|
- releasePage(pCur->apPage[i]);
|
|
- }
|
|
+ btreeReleaseAllCursorPages(pCur);
|
|
unlockBtreeIfUnused(pBt);
|
|
sqlite3_free(pCur->aOverflow);
|
|
- /* sqlite3_free(pCur); */
|
|
+ sqlite3_free(pCur->pKey);
|
|
sqlite3BtreeLeave(pBtree);
|
|
}
|
|
return SQLITE_OK;
|
|
@@ -63897,12 +67354,19 @@
|
|
** Using this cache reduces the number of calls to btreeParseCell().
|
|
*/
|
|
#ifndef NDEBUG
|
|
+ static int cellInfoEqual(CellInfo *a, CellInfo *b){
|
|
+ if( a->nKey!=b->nKey ) return 0;
|
|
+ if( a->pPayload!=b->pPayload ) return 0;
|
|
+ if( a->nPayload!=b->nPayload ) return 0;
|
|
+ if( a->nLocal!=b->nLocal ) return 0;
|
|
+ if( a->nSize!=b->nSize ) return 0;
|
|
+ return 1;
|
|
+ }
|
|
static void assertCellInfo(BtCursor *pCur){
|
|
CellInfo info;
|
|
- int iPage = pCur->iPage;
|
|
memset(&info, 0, sizeof(info));
|
|
- btreeParseCell(pCur->apPage[iPage], pCur->ix, &info);
|
|
- assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
|
|
+ btreeParseCell(pCur->pPage, pCur->ix, &info);
|
|
+ assert( CORRUPT_DB || cellInfoEqual(&info, &pCur->info) );
|
|
}
|
|
#else
|
|
#define assertCellInfo(x)
|
|
@@ -63909,9 +67373,8 @@
|
|
#endif
|
|
static SQLITE_NOINLINE void getCellInfo(BtCursor *pCur){
|
|
if( pCur->info.nSize==0 ){
|
|
- int iPage = pCur->iPage;
|
|
pCur->curFlags |= BTCF_ValidNKey;
|
|
- btreeParseCell(pCur->apPage[iPage],pCur->ix,&pCur->info);
|
|
+ btreeParseCell(pCur->pPage,pCur->ix,&pCur->info);
|
|
}else{
|
|
assertCellInfo(pCur);
|
|
}
|
|
@@ -63946,7 +67409,21 @@
|
|
return pCur->info.nKey;
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
|
/*
|
|
+** Return the offset into the database file for the start of the
|
|
+** payload to which the cursor is pointing.
|
|
+*/
|
|
+SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor *pCur){
|
|
+ assert( cursorHoldsMutex(pCur) );
|
|
+ assert( pCur->eState==CURSOR_VALID );
|
|
+ getCellInfo(pCur);
|
|
+ return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) +
|
|
+ (i64)(pCur->info.pPayload - pCur->pPage->aData);
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */
|
|
+
|
|
+/*
|
|
** Return the number of bytes of payload for the entry that pCur is
|
|
** currently pointing to. For table btrees, this will be the amount
|
|
** of data. For index btrees, this will be the size of the key.
|
|
@@ -64109,7 +67586,7 @@
|
|
unsigned char *aPayload;
|
|
int rc = SQLITE_OK;
|
|
int iIdx = 0;
|
|
- MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
|
|
+ MemPage *pPage = pCur->pPage; /* Btree page of current entry */
|
|
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
|
|
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
unsigned char * const pBufStart = pBuf; /* Start of original out buffer */
|
|
@@ -64132,7 +67609,7 @@
|
|
** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
|
|
** but is recast into its current form to avoid integer overflow problems
|
|
*/
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
|
|
/* Check if data must be read/written to/from the btree page itself. */
|
|
@@ -64165,7 +67642,9 @@
|
|
*/
|
|
if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){
|
|
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
|
|
- if( nOvfl>pCur->nOvflAlloc ){
|
|
+ if( pCur->aOverflow==0
|
|
+ || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
|
|
+ ){
|
|
Pgno *aNew = (Pgno*)sqlite3Realloc(
|
|
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
|
);
|
|
@@ -64172,7 +67651,6 @@
|
|
if( aNew==0 ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}else{
|
|
- pCur->nOvflAlloc = nOvfl*2;
|
|
pCur->aOverflow = aNew;
|
|
}
|
|
}
|
|
@@ -64217,9 +67695,6 @@
|
|
/* Need to read this page properly. It contains some of the
|
|
** range of data that is being read (eOp==0) or written (eOp!=0).
|
|
*/
|
|
-#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
|
- sqlite3_file *fd; /* File from which to do direct overflow read */
|
|
-#endif
|
|
int a = amt;
|
|
if( a + offset > ovflSize ){
|
|
a = ovflSize - offset;
|
|
@@ -64230,7 +67705,7 @@
|
|
**
|
|
** 1) this is a read operation, and
|
|
** 2) data is required from the start of this overflow page, and
|
|
- ** 3) there is no open write-transaction, and
|
|
+ ** 3) there are no dirty pages in the page-cache
|
|
** 4) the database is file-backed, and
|
|
** 5) the page is not in the WAL file
|
|
** 6) at least 4 bytes have already been read into the output buffer
|
|
@@ -64241,11 +67716,10 @@
|
|
*/
|
|
if( eOp==0 /* (1) */
|
|
&& offset==0 /* (2) */
|
|
- && pBt->inTransaction==TRANS_READ /* (3) */
|
|
- && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */
|
|
- && 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */
|
|
+ && sqlite3PagerDirectReadOk(pBt->pPager, nextPage) /* (3,4,5) */
|
|
&& &pBuf[-4]>=pBufStart /* (6) */
|
|
){
|
|
+ sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
|
|
u8 aSave[4];
|
|
u8 *aWrite = &pBuf[-4];
|
|
assert( aWrite>=pBufStart ); /* due to (6) */
|
|
@@ -64280,7 +67754,7 @@
|
|
|
|
if( rc==SQLITE_OK && amt>0 ){
|
|
/* Overflow chain ends prematurely */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -64305,8 +67779,8 @@
|
|
SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
|
assert( cursorHoldsMutex(pCur) );
|
|
assert( pCur->eState==CURSOR_VALID );
|
|
- assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] );
|
|
- assert( pCur->ix<pCur->apPage[pCur->iPage]->nCell );
|
|
+ assert( pCur->iPage>=0 && pCur->pPage );
|
|
+ assert( pCur->ix<pCur->pPage->nCell );
|
|
return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
|
|
}
|
|
|
|
@@ -64363,18 +67837,23 @@
|
|
BtCursor *pCur, /* Cursor pointing to entry to read from */
|
|
u32 *pAmt /* Write the number of available bytes here */
|
|
){
|
|
- u32 amt;
|
|
- assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
|
|
+ int amt;
|
|
+ assert( pCur!=0 && pCur->iPage>=0 && pCur->pPage);
|
|
assert( pCur->eState==CURSOR_VALID );
|
|
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
|
assert( cursorOwnsBtShared(pCur) );
|
|
- assert( pCur->ix<pCur->apPage[pCur->iPage]->nCell );
|
|
+ assert( pCur->ix<pCur->pPage->nCell );
|
|
assert( pCur->info.nSize>0 );
|
|
- assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB );
|
|
- assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB);
|
|
- amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload);
|
|
- if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal;
|
|
- *pAmt = amt;
|
|
+ assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB );
|
|
+ assert( pCur->info.pPayload<pCur->pPage->aDataEnd ||CORRUPT_DB);
|
|
+ amt = pCur->info.nLocal;
|
|
+ if( amt>(int)(pCur->pPage->aDataEnd - pCur->info.pPayload) ){
|
|
+ /* There is too little space on the page for the expected amount
|
|
+ ** of local content. Database must be corrupt. */
|
|
+ assert( CORRUPT_DB );
|
|
+ amt = MAX(0, (int)(pCur->pPage->aDataEnd - pCur->info.pPayload));
|
|
+ }
|
|
+ *pAmt = (u32)amt;
|
|
return (void*)pCur->info.pPayload;
|
|
}
|
|
|
|
@@ -64419,10 +67898,11 @@
|
|
}
|
|
pCur->info.nSize = 0;
|
|
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
|
|
- pCur->aiIdx[pCur->iPage++] = pCur->ix;
|
|
+ pCur->aiIdx[pCur->iPage] = pCur->ix;
|
|
+ pCur->apPage[pCur->iPage] = pCur->pPage;
|
|
pCur->ix = 0;
|
|
- return getAndInitPage(pBt, newPgno, &pCur->apPage[pCur->iPage],
|
|
- pCur, pCur->curPagerFlags);
|
|
+ pCur->iPage++;
|
|
+ return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags);
|
|
}
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
@@ -64456,20 +67936,23 @@
|
|
** the largest cell index.
|
|
*/
|
|
static void moveToParent(BtCursor *pCur){
|
|
+ MemPage *pLeaf;
|
|
assert( cursorOwnsBtShared(pCur) );
|
|
assert( pCur->eState==CURSOR_VALID );
|
|
assert( pCur->iPage>0 );
|
|
- assert( pCur->apPage[pCur->iPage] );
|
|
+ assert( pCur->pPage );
|
|
assertParentIndex(
|
|
pCur->apPage[pCur->iPage-1],
|
|
pCur->aiIdx[pCur->iPage-1],
|
|
- pCur->apPage[pCur->iPage]->pgno
|
|
+ pCur->pPage->pgno
|
|
);
|
|
testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell );
|
|
pCur->info.nSize = 0;
|
|
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
|
|
pCur->ix = pCur->aiIdx[pCur->iPage-1];
|
|
- releasePageNotNull(pCur->apPage[pCur->iPage--]);
|
|
+ pLeaf = pCur->pPage;
|
|
+ pCur->pPage = pCur->apPage[--pCur->iPage];
|
|
+ releasePageNotNull(pLeaf);
|
|
}
|
|
|
|
/*
|
|
@@ -64481,9 +67964,9 @@
|
|
** single child page. This can only happen with the table rooted at page 1.
|
|
**
|
|
** If the b-tree structure is empty, the cursor state is set to
|
|
-** CURSOR_INVALID. Otherwise, the cursor is set to point to the first
|
|
-** cell located on the root (or virtual root) page and the cursor state
|
|
-** is set to CURSOR_VALID.
|
|
+** CURSOR_INVALID and this routine returns SQLITE_EMPTY. Otherwise,
|
|
+** the cursor is set to point to the first cell located on the root
|
|
+** (or virtual root) page and the cursor state is set to CURSOR_VALID.
|
|
**
|
|
** If this function returns successfully, it may be assumed that the
|
|
** page-header flags indicate that the [virtual] root-page is the expected
|
|
@@ -64501,37 +67984,40 @@
|
|
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
|
|
assert( CURSOR_VALID < CURSOR_REQUIRESEEK );
|
|
assert( CURSOR_FAULT > CURSOR_REQUIRESEEK );
|
|
- if( pCur->eState>=CURSOR_REQUIRESEEK ){
|
|
- if( pCur->eState==CURSOR_FAULT ){
|
|
- assert( pCur->skipNext!=SQLITE_OK );
|
|
- return pCur->skipNext;
|
|
- }
|
|
- sqlite3BtreeClearCursor(pCur);
|
|
- }
|
|
+ assert( pCur->eState < CURSOR_REQUIRESEEK || pCur->iPage<0 );
|
|
+ assert( pCur->pgnoRoot>0 || pCur->iPage<0 );
|
|
|
|
if( pCur->iPage>=0 ){
|
|
if( pCur->iPage ){
|
|
- do{
|
|
- assert( pCur->apPage[pCur->iPage]!=0 );
|
|
- releasePageNotNull(pCur->apPage[pCur->iPage--]);
|
|
- }while( pCur->iPage);
|
|
+ releasePageNotNull(pCur->pPage);
|
|
+ while( --pCur->iPage ){
|
|
+ releasePageNotNull(pCur->apPage[pCur->iPage]);
|
|
+ }
|
|
+ pCur->pPage = pCur->apPage[0];
|
|
goto skip_init;
|
|
}
|
|
}else if( pCur->pgnoRoot==0 ){
|
|
pCur->eState = CURSOR_INVALID;
|
|
- return SQLITE_OK;
|
|
+ return SQLITE_EMPTY;
|
|
}else{
|
|
assert( pCur->iPage==(-1) );
|
|
- rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
|
|
+ if( pCur->eState>=CURSOR_REQUIRESEEK ){
|
|
+ if( pCur->eState==CURSOR_FAULT ){
|
|
+ assert( pCur->skipNext!=SQLITE_OK );
|
|
+ return pCur->skipNext;
|
|
+ }
|
|
+ sqlite3BtreeClearCursor(pCur);
|
|
+ }
|
|
+ rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage,
|
|
0, pCur->curPagerFlags);
|
|
if( rc!=SQLITE_OK ){
|
|
pCur->eState = CURSOR_INVALID;
|
|
- return rc;
|
|
+ return rc;
|
|
}
|
|
pCur->iPage = 0;
|
|
- pCur->curIntKey = pCur->apPage[0]->intKey;
|
|
+ pCur->curIntKey = pCur->pPage->intKey;
|
|
}
|
|
- pRoot = pCur->apPage[0];
|
|
+ pRoot = pCur->pPage;
|
|
assert( pRoot->pgno==pCur->pgnoRoot );
|
|
|
|
/* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
|
|
@@ -64546,7 +68032,7 @@
|
|
** (or the freelist). */
|
|
assert( pRoot->intKey==1 || pRoot->intKey==0 );
|
|
if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
|
|
- return SQLITE_CORRUPT_PGNO(pCur->apPage[pCur->iPage]->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pCur->pPage);
|
|
}
|
|
|
|
skip_init:
|
|
@@ -64554,7 +68040,7 @@
|
|
pCur->info.nSize = 0;
|
|
pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
|
|
|
|
- pRoot = pCur->apPage[0];
|
|
+ pRoot = pCur->pPage;
|
|
if( pRoot->nCell>0 ){
|
|
pCur->eState = CURSOR_VALID;
|
|
}else if( !pRoot->leaf ){
|
|
@@ -64565,6 +68051,7 @@
|
|
rc = moveToChild(pCur, subpage);
|
|
}else{
|
|
pCur->eState = CURSOR_INVALID;
|
|
+ rc = SQLITE_EMPTY;
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -64583,7 +68070,7 @@
|
|
|
|
assert( cursorOwnsBtShared(pCur) );
|
|
assert( pCur->eState==CURSOR_VALID );
|
|
- while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
|
|
+ while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
|
|
assert( pCur->ix<pPage->nCell );
|
|
pgno = get4byte(findCell(pPage, pCur->ix));
|
|
rc = moveToChild(pCur, pgno);
|
|
@@ -64608,7 +68095,7 @@
|
|
|
|
assert( cursorOwnsBtShared(pCur) );
|
|
assert( pCur->eState==CURSOR_VALID );
|
|
- while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){
|
|
+ while( !(pPage = pCur->pPage)->leaf ){
|
|
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
|
|
pCur->ix = pPage->nCell;
|
|
rc = moveToChild(pCur, pgno);
|
|
@@ -64631,18 +68118,34 @@
|
|
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
|
rc = moveToRoot(pCur);
|
|
if( rc==SQLITE_OK ){
|
|
- if( pCur->eState==CURSOR_INVALID ){
|
|
- assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
|
|
- *pRes = 1;
|
|
- }else{
|
|
- assert( pCur->apPage[pCur->iPage]->nCell>0 );
|
|
- *pRes = 0;
|
|
- rc = moveToLeftmost(pCur);
|
|
- }
|
|
+ assert( pCur->pPage->nCell>0 );
|
|
+ *pRes = 0;
|
|
+ rc = moveToLeftmost(pCur);
|
|
+ }else if( rc==SQLITE_EMPTY ){
|
|
+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
|
+ *pRes = 1;
|
|
+ rc = SQLITE_OK;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
+/*
|
|
+** This function is a no-op if cursor pCur does not point to a valid row.
|
|
+** Otherwise, if pCur is valid, configure it so that the next call to
|
|
+** sqlite3BtreeNext() is a no-op.
|
|
+*/
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor *pCur){
|
|
+ /* We believe that the cursor must always be in the valid state when
|
|
+ ** this routine is called, but the proof is difficult, so we add an
|
|
+ ** ALWaYS() test just in case we are wrong. */
|
|
+ if( ALWAYS(pCur->eState==CURSOR_VALID) ){
|
|
+ pCur->eState = CURSOR_SKIPNEXT;
|
|
+ pCur->skipNext = 1;
|
|
+ }
|
|
+}
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
/* Move the cursor to the last entry in the table. Return SQLITE_OK
|
|
** on success. Set *pRes to 0 if the cursor actually points to something
|
|
** or set *pRes to 1 if the table is empty.
|
|
@@ -64662,8 +68165,8 @@
|
|
for(ii=0; ii<pCur->iPage; ii++){
|
|
assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell );
|
|
}
|
|
- assert( pCur->ix==pCur->apPage[pCur->iPage]->nCell-1 );
|
|
- assert( pCur->apPage[pCur->iPage]->leaf );
|
|
+ assert( pCur->ix==pCur->pPage->nCell-1 );
|
|
+ assert( pCur->pPage->leaf );
|
|
#endif
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -64670,20 +68173,18 @@
|
|
|
|
rc = moveToRoot(pCur);
|
|
if( rc==SQLITE_OK ){
|
|
- if( CURSOR_INVALID==pCur->eState ){
|
|
- assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
|
|
- *pRes = 1;
|
|
+ assert( pCur->eState==CURSOR_VALID );
|
|
+ *pRes = 0;
|
|
+ rc = moveToRightmost(pCur);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pCur->curFlags |= BTCF_AtLast;
|
|
}else{
|
|
- assert( pCur->eState==CURSOR_VALID );
|
|
- *pRes = 0;
|
|
- rc = moveToRightmost(pCur);
|
|
- if( rc==SQLITE_OK ){
|
|
- pCur->curFlags |= BTCF_AtLast;
|
|
- }else{
|
|
- pCur->curFlags &= ~BTCF_AtLast;
|
|
- }
|
|
-
|
|
+ pCur->curFlags &= ~BTCF_AtLast;
|
|
}
|
|
+ }else if( rc==SQLITE_EMPTY ){
|
|
+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
|
+ *pRes = 1;
|
|
+ rc = SQLITE_OK;
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -64782,22 +68283,23 @@
|
|
|
|
rc = moveToRoot(pCur);
|
|
if( rc ){
|
|
+ if( rc==SQLITE_EMPTY ){
|
|
+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
|
+ *pRes = -1;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
return rc;
|
|
}
|
|
- assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage] );
|
|
- assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit );
|
|
- assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 );
|
|
- if( pCur->eState==CURSOR_INVALID ){
|
|
- *pRes = -1;
|
|
- assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
|
|
- return SQLITE_OK;
|
|
- }
|
|
- assert( pCur->apPage[0]->intKey==pCur->curIntKey );
|
|
+ assert( pCur->pPage );
|
|
+ assert( pCur->pPage->isInit );
|
|
+ assert( pCur->eState==CURSOR_VALID );
|
|
+ assert( pCur->pPage->nCell > 0 );
|
|
+ assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey );
|
|
assert( pCur->curIntKey || pIdxKey );
|
|
for(;;){
|
|
int lwr, upr, idx, c;
|
|
Pgno chldPg;
|
|
- MemPage *pPage = pCur->apPage[pCur->iPage];
|
|
+ MemPage *pPage = pCur->pPage;
|
|
u8 *pCell; /* Pointer to current cell in pPage */
|
|
|
|
/* pPage->nCell must be greater than zero. If this is the root-page
|
|
@@ -64820,7 +68322,7 @@
|
|
if( pPage->intKeyLeaf ){
|
|
while( 0x80 <= *(pCell++) ){
|
|
if( pCell>=pPage->aDataEnd ){
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
}
|
|
}
|
|
@@ -64894,7 +68396,7 @@
|
|
testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
|
|
testcase( nCell==2 ); /* Minimum legal index key size */
|
|
if( nCell<2 ){
|
|
- rc = SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ rc = SQLITE_CORRUPT_PAGE(pPage);
|
|
goto moveto_finish;
|
|
}
|
|
pCellKey = sqlite3Malloc( nCell+18 );
|
|
@@ -64925,7 +68427,7 @@
|
|
*pRes = 0;
|
|
rc = SQLITE_OK;
|
|
pCur->ix = (u16)idx;
|
|
- if( pIdxKey->errCode ) rc = SQLITE_CORRUPT;
|
|
+ if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT;
|
|
goto moveto_finish;
|
|
}
|
|
if( lwr>upr ) break;
|
|
@@ -64936,7 +68438,7 @@
|
|
assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
|
|
assert( pPage->isInit );
|
|
if( pPage->leaf ){
|
|
- assert( pCur->ix<pCur->apPage[pCur->iPage]->nCell );
|
|
+ assert( pCur->ix<pCur->pPage->nCell );
|
|
pCur->ix = (u16)idx;
|
|
*pRes = c;
|
|
rc = SQLITE_OK;
|
|
@@ -64990,9 +68492,10 @@
|
|
** opcode, and it that case the cursor will always be valid and
|
|
** will always point to a leaf node. */
|
|
if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1;
|
|
- if( NEVER(pCur->apPage[pCur->iPage]->leaf==0) ) return -1;
|
|
+ if( NEVER(pCur->pPage->leaf==0) ) return -1;
|
|
|
|
- for(n=1, i=0; i<=pCur->iPage; i++){
|
|
+ n = pCur->pPage->nCell;
|
|
+ for(i=0; i<pCur->iPage; i++){
|
|
n *= pCur->apPage[i]->nCell;
|
|
}
|
|
return n;
|
|
@@ -65045,9 +68548,18 @@
|
|
}
|
|
}
|
|
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
idx = ++pCur->ix;
|
|
- assert( pPage->isInit );
|
|
+ if( !pPage->isInit ){
|
|
+ /* The only known way for this to happen is for there to be a
|
|
+ ** recursive SQL function that does a DELETE operation as part of a
|
|
+ ** SELECT which deletes content out from under an active cursor
|
|
+ ** in a corrupt database file where the table being DELETE-ed from
|
|
+ ** has pages in common with the table being queried. See TH3
|
|
+ ** module cov1/btree78.test testcase 220 (2018-06-08) for an
|
|
+ ** example. */
|
|
+ return SQLITE_CORRUPT_BKPT;
|
|
+ }
|
|
|
|
/* If the database file is corrupt, it is possible for the value of idx
|
|
** to be invalid here. This can only occur if a second cursor modifies
|
|
@@ -65068,7 +68580,7 @@
|
|
return SQLITE_DONE;
|
|
}
|
|
moveToParent(pCur);
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
}while( pCur->ix>=pPage->nCell );
|
|
if( pPage->intKey ){
|
|
return sqlite3BtreeNext(pCur, 0);
|
|
@@ -65091,7 +68603,7 @@
|
|
pCur->info.nSize = 0;
|
|
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
|
|
if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur);
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
if( (++pCur->ix)>=pPage->nCell ){
|
|
pCur->ix--;
|
|
return btreeNext(pCur);
|
|
@@ -65150,7 +68662,7 @@
|
|
}
|
|
}
|
|
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
assert( pPage->isInit );
|
|
if( !pPage->leaf ){
|
|
int idx = pCur->ix;
|
|
@@ -65169,7 +68681,7 @@
|
|
assert( (pCur->curFlags & (BTCF_ValidOvfl))==0 );
|
|
|
|
pCur->ix--;
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
if( pPage->intKey && !pPage->leaf ){
|
|
rc = sqlite3BtreePrevious(pCur, 0);
|
|
}else{
|
|
@@ -65187,7 +68699,7 @@
|
|
pCur->info.nSize = 0;
|
|
if( pCur->eState!=CURSOR_VALID
|
|
|| pCur->ix==0
|
|
- || pCur->apPage[pCur->iPage]->leaf==0
|
|
+ || pCur->pPage->leaf==0
|
|
){
|
|
return btreePrevious(pCur);
|
|
}
|
|
@@ -65674,9 +69186,8 @@
|
|
}
|
|
|
|
/*
|
|
-** Free any overflow pages associated with the given Cell. Write the
|
|
-** local Cell size (the number of bytes on the original page, omitting
|
|
-** overflow) into *pnSize.
|
|
+** Free any overflow pages associated with the given Cell. Store
|
|
+** size information about the cell in pInfo.
|
|
*/
|
|
static int clearCell(
|
|
MemPage *pPage, /* The page that contains the Cell */
|
|
@@ -65683,7 +69194,7 @@
|
|
unsigned char *pCell, /* First byte of the Cell */
|
|
CellInfo *pInfo /* Size information about the cell */
|
|
){
|
|
- BtShared *pBt = pPage->pBt;
|
|
+ BtShared *pBt;
|
|
Pgno ovflPgno;
|
|
int rc;
|
|
int nOvfl;
|
|
@@ -65694,11 +69205,14 @@
|
|
if( pInfo->nLocal==pInfo->nPayload ){
|
|
return SQLITE_OK; /* No overflow pages. Return without doing anything */
|
|
}
|
|
- if( pCell+pInfo->nSize-1 > pPage->aData+pPage->maskPage ){
|
|
+ testcase( pCell + pInfo->nSize == pPage->aDataEnd );
|
|
+ testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd );
|
|
+ if( pCell + pInfo->nSize > pPage->aDataEnd ){
|
|
/* Cell extends past end of page */
|
|
- return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
|
+ return SQLITE_CORRUPT_PAGE(pPage);
|
|
}
|
|
ovflPgno = get4byte(pCell + pInfo->nSize - 4);
|
|
+ pBt = pPage->pBt;
|
|
assert( pBt->usableSize > 4 );
|
|
ovflPageSize = pBt->usableSize - 4;
|
|
nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize;
|
|
@@ -65766,14 +69280,13 @@
|
|
){
|
|
int nPayload;
|
|
const u8 *pSrc;
|
|
- int nSrc, n, rc;
|
|
+ int nSrc, n, rc, mn;
|
|
int spaceLeft;
|
|
- MemPage *pOvfl = 0;
|
|
- MemPage *pToRelease = 0;
|
|
+ MemPage *pToRelease;
|
|
unsigned char *pPrior;
|
|
unsigned char *pPayload;
|
|
- BtShared *pBt = pPage->pBt;
|
|
- Pgno pgnoOvfl = 0;
|
|
+ BtShared *pBt;
|
|
+ Pgno pgnoOvfl;
|
|
int nHeader;
|
|
|
|
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
|
@@ -65780,7 +69293,7 @@
|
|
|
|
/* pPage is not necessarily writeable since pCell might be auxiliary
|
|
** buffer space that is separate from the pPage buffer area */
|
|
- assert( pCell<pPage->aData || pCell>=&pPage->aData[pBt->pageSize]
|
|
+ assert( pCell<pPage->aData || pCell>=&pPage->aData[pPage->pBt->pageSize]
|
|
|| sqlite3PagerIswriteable(pPage->pDbPage) );
|
|
|
|
/* Fill in the header. */
|
|
@@ -65800,26 +69313,37 @@
|
|
}
|
|
|
|
/* Fill in the payload */
|
|
+ pPayload = &pCell[nHeader];
|
|
if( nPayload<=pPage->maxLocal ){
|
|
+ /* This is the common case where everything fits on the btree page
|
|
+ ** and no overflow pages are required. */
|
|
n = nHeader + nPayload;
|
|
testcase( n==3 );
|
|
testcase( n==4 );
|
|
if( n<4 ) n = 4;
|
|
*pnSize = n;
|
|
- spaceLeft = nPayload;
|
|
- pPrior = pCell;
|
|
- }else{
|
|
- int mn = pPage->minLocal;
|
|
- n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4);
|
|
- testcase( n==pPage->maxLocal );
|
|
- testcase( n==pPage->maxLocal+1 );
|
|
- if( n > pPage->maxLocal ) n = mn;
|
|
- spaceLeft = n;
|
|
- *pnSize = n + nHeader + 4;
|
|
- pPrior = &pCell[nHeader+n];
|
|
+ assert( nSrc<=nPayload );
|
|
+ testcase( nSrc<nPayload );
|
|
+ memcpy(pPayload, pSrc, nSrc);
|
|
+ memset(pPayload+nSrc, 0, nPayload-nSrc);
|
|
+ return SQLITE_OK;
|
|
}
|
|
- pPayload = &pCell[nHeader];
|
|
|
|
+ /* If we reach this point, it means that some of the content will need
|
|
+ ** to spill onto overflow pages.
|
|
+ */
|
|
+ mn = pPage->minLocal;
|
|
+ n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4);
|
|
+ testcase( n==pPage->maxLocal );
|
|
+ testcase( n==pPage->maxLocal+1 );
|
|
+ if( n > pPage->maxLocal ) n = mn;
|
|
+ spaceLeft = n;
|
|
+ *pnSize = n + nHeader + 4;
|
|
+ pPrior = &pCell[nHeader+n];
|
|
+ pToRelease = 0;
|
|
+ pgnoOvfl = 0;
|
|
+ pBt = pPage->pBt;
|
|
+
|
|
/* At this point variables should be set as follows:
|
|
**
|
|
** nPayload Total payload size in bytes
|
|
@@ -65844,8 +69368,35 @@
|
|
#endif
|
|
|
|
/* Write the payload into the local Cell and any extra into overflow pages */
|
|
- while( nPayload>0 ){
|
|
+ while( 1 ){
|
|
+ n = nPayload;
|
|
+ if( n>spaceLeft ) n = spaceLeft;
|
|
+
|
|
+ /* If pToRelease is not zero than pPayload points into the data area
|
|
+ ** of pToRelease. Make sure pToRelease is still writeable. */
|
|
+ assert( pToRelease==0 || sqlite3PagerIswriteable(pToRelease->pDbPage) );
|
|
+
|
|
+ /* If pPayload is part of the data area of pPage, then make sure pPage
|
|
+ ** is still writeable */
|
|
+ assert( pPayload<pPage->aData || pPayload>=&pPage->aData[pBt->pageSize]
|
|
+ || sqlite3PagerIswriteable(pPage->pDbPage) );
|
|
+
|
|
+ if( nSrc>=n ){
|
|
+ memcpy(pPayload, pSrc, n);
|
|
+ }else if( nSrc>0 ){
|
|
+ n = nSrc;
|
|
+ memcpy(pPayload, pSrc, n);
|
|
+ }else{
|
|
+ memset(pPayload, 0, n);
|
|
+ }
|
|
+ nPayload -= n;
|
|
+ if( nPayload<=0 ) break;
|
|
+ pPayload += n;
|
|
+ pSrc += n;
|
|
+ nSrc -= n;
|
|
+ spaceLeft -= n;
|
|
if( spaceLeft==0 ){
|
|
+ MemPage *pOvfl = 0;
|
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
|
Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */
|
|
if( pBt->autoVacuum ){
|
|
@@ -65898,30 +69449,6 @@
|
|
pPayload = &pOvfl->aData[4];
|
|
spaceLeft = pBt->usableSize - 4;
|
|
}
|
|
- n = nPayload;
|
|
- if( n>spaceLeft ) n = spaceLeft;
|
|
-
|
|
- /* If pToRelease is not zero than pPayload points into the data area
|
|
- ** of pToRelease. Make sure pToRelease is still writeable. */
|
|
- assert( pToRelease==0 || sqlite3PagerIswriteable(pToRelease->pDbPage) );
|
|
-
|
|
- /* If pPayload is part of the data area of pPage, then make sure pPage
|
|
- ** is still writeable */
|
|
- assert( pPayload<pPage->aData || pPayload>=&pPage->aData[pBt->pageSize]
|
|
- || sqlite3PagerIswriteable(pPage->pDbPage) );
|
|
-
|
|
- if( nSrc>0 ){
|
|
- if( n>nSrc ) n = nSrc;
|
|
- assert( pSrc );
|
|
- memcpy(pPayload, pSrc, n);
|
|
- }else{
|
|
- memset(pPayload, 0, n);
|
|
- }
|
|
- nPayload -= n;
|
|
- pPayload += n;
|
|
- pSrc += n;
|
|
- nSrc -= n;
|
|
- spaceLeft -= n;
|
|
}
|
|
releasePage(pToRelease);
|
|
return SQLITE_OK;
|
|
@@ -65953,7 +69480,7 @@
|
|
hdr = pPage->hdrOffset;
|
|
testcase( pc==get2byte(&data[hdr+5]) );
|
|
testcase( pc+sz==pPage->pBt->usableSize );
|
|
- if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
|
|
+ if( pc+sz > pPage->pBt->usableSize ){
|
|
*pRC = SQLITE_CORRUPT_BKPT;
|
|
return;
|
|
}
|
|
@@ -66820,10 +70347,8 @@
|
|
+ nMaxCells*sizeof(u16) /* b.szCell */
|
|
+ pBt->pageSize; /* aSpace1 */
|
|
|
|
- /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer
|
|
- ** that is more than 6 times the database page size. */
|
|
assert( szScratch<=6*(int)pBt->pageSize );
|
|
- b.apCell = sqlite3ScratchMalloc( szScratch );
|
|
+ b.apCell = sqlite3StackAllocRaw(0, szScratch );
|
|
if( b.apCell==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
goto balance_cleanup;
|
|
@@ -66868,7 +70393,7 @@
|
|
}
|
|
|
|
/* Load b.apCell[] with pointers to all cells in pOld. If pOld
|
|
- ** constains overflow cells, include them in the b.apCell[] array
|
|
+ ** contains overflow cells, include them in the b.apCell[] array
|
|
** in the correct spot.
|
|
**
|
|
** Note that when there are multiple overflow cells, it is always the
|
|
@@ -67401,7 +70926,7 @@
|
|
** Cleanup before returning.
|
|
*/
|
|
balance_cleanup:
|
|
- sqlite3ScratchFree(b.apCell);
|
|
+ sqlite3StackFree(0, b.apCell);
|
|
for(i=0; i<nOld; i++){
|
|
releasePage(apOld[i]);
|
|
}
|
|
@@ -67500,7 +71025,7 @@
|
|
|
|
do {
|
|
int iPage = pCur->iPage;
|
|
- MemPage *pPage = pCur->apPage[iPage];
|
|
+ MemPage *pPage = pCur->pPage;
|
|
|
|
if( iPage==0 ){
|
|
if( pPage->nOverflow ){
|
|
@@ -67516,7 +71041,9 @@
|
|
pCur->iPage = 1;
|
|
pCur->ix = 0;
|
|
pCur->aiIdx[0] = 0;
|
|
- assert( pCur->apPage[1]->nOverflow );
|
|
+ pCur->apPage[0] = pPage;
|
|
+ pCur->pPage = pCur->apPage[1];
|
|
+ assert( pCur->pPage->nOverflow );
|
|
}
|
|
}else{
|
|
break;
|
|
@@ -67596,6 +71123,7 @@
|
|
releasePage(pPage);
|
|
pCur->iPage--;
|
|
assert( pCur->iPage>=0 );
|
|
+ pCur->pPage = pCur->apPage[pCur->iPage];
|
|
}
|
|
}while( rc==SQLITE_OK );
|
|
|
|
@@ -67605,8 +71133,96 @@
|
|
return rc;
|
|
}
|
|
|
|
+/* Overwrite content from pX into pDest. Only do the write if the
|
|
+** content is different from what is already there.
|
|
+*/
|
|
+static int btreeOverwriteContent(
|
|
+ MemPage *pPage, /* MemPage on which writing will occur */
|
|
+ u8 *pDest, /* Pointer to the place to start writing */
|
|
+ const BtreePayload *pX, /* Source of data to write */
|
|
+ int iOffset, /* Offset of first byte to write */
|
|
+ int iAmt /* Number of bytes to be written */
|
|
+){
|
|
+ int nData = pX->nData - iOffset;
|
|
+ if( nData<=0 ){
|
|
+ /* Overwritting with zeros */
|
|
+ int i;
|
|
+ for(i=0; i<iAmt && pDest[i]==0; i++){}
|
|
+ if( i<iAmt ){
|
|
+ int rc = sqlite3PagerWrite(pPage->pDbPage);
|
|
+ if( rc ) return rc;
|
|
+ memset(pDest + i, 0, iAmt - i);
|
|
+ }
|
|
+ }else{
|
|
+ if( nData<iAmt ){
|
|
+ /* Mixed read data and zeros at the end. Make a recursive call
|
|
+ ** to write the zeros then fall through to write the real data */
|
|
+ int rc = btreeOverwriteContent(pPage, pDest+nData, pX, iOffset+nData,
|
|
+ iAmt-nData);
|
|
+ if( rc ) return rc;
|
|
+ iAmt = nData;
|
|
+ }
|
|
+ if( memcmp(pDest, ((u8*)pX->pData) + iOffset, iAmt)!=0 ){
|
|
+ int rc = sqlite3PagerWrite(pPage->pDbPage);
|
|
+ if( rc ) return rc;
|
|
+ memcpy(pDest, ((u8*)pX->pData) + iOffset, iAmt);
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
|
|
/*
|
|
+** Overwrite the cell that cursor pCur is pointing to with fresh content
|
|
+** contained in pX.
|
|
+*/
|
|
+static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
|
|
+ int iOffset; /* Next byte of pX->pData to write */
|
|
+ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
|
|
+ int rc; /* Return code */
|
|
+ MemPage *pPage = pCur->pPage; /* Page being written */
|
|
+ BtShared *pBt; /* Btree */
|
|
+ Pgno ovflPgno; /* Next overflow page to write */
|
|
+ u32 ovflPageSize; /* Size to write on overflow page */
|
|
+
|
|
+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
|
|
+ return SQLITE_CORRUPT_BKPT;
|
|
+ }
|
|
+ /* Overwrite the local portion first */
|
|
+ rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
|
|
+ 0, pCur->info.nLocal);
|
|
+ if( rc ) return rc;
|
|
+ if( pCur->info.nLocal==nTotal ) return SQLITE_OK;
|
|
+
|
|
+ /* Now overwrite the overflow pages */
|
|
+ iOffset = pCur->info.nLocal;
|
|
+ assert( nTotal>=0 );
|
|
+ assert( iOffset>=0 );
|
|
+ ovflPgno = get4byte(pCur->info.pPayload + iOffset);
|
|
+ pBt = pPage->pBt;
|
|
+ ovflPageSize = pBt->usableSize - 4;
|
|
+ do{
|
|
+ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
|
|
+ if( rc ) return rc;
|
|
+ if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }else{
|
|
+ if( iOffset+ovflPageSize<(u32)nTotal ){
|
|
+ ovflPgno = get4byte(pPage->aData);
|
|
+ }else{
|
|
+ ovflPageSize = nTotal - iOffset;
|
|
+ }
|
|
+ rc = btreeOverwriteContent(pPage, pPage->aData+4, pX,
|
|
+ iOffset, ovflPageSize);
|
|
+ }
|
|
+ sqlite3PagerUnref(pPage->pDbPage);
|
|
+ if( rc ) return rc;
|
|
+ iOffset += ovflPageSize;
|
|
+ }while( iOffset<nTotal );
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
** Insert a new record into the BTree. The content of the new record
|
|
** is described by the pX object. The pCur cursor is used only to
|
|
** define what table the record should be inserted into, and is left
|
|
@@ -67695,39 +71311,90 @@
|
|
invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0);
|
|
|
|
/* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
|
|
- ** to a row with the same key as the new entry being inserted. */
|
|
- assert( (flags & BTREE_SAVEPOSITION)==0 ||
|
|
- ((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) );
|
|
+ ** to a row with the same key as the new entry being inserted.
|
|
+ */
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( flags & BTREE_SAVEPOSITION ){
|
|
+ assert( pCur->curFlags & BTCF_ValidNKey );
|
|
+ assert( pX->nKey==pCur->info.nKey );
|
|
+ assert( pCur->info.nSize!=0 );
|
|
+ assert( loc==0 );
|
|
+ }
|
|
+#endif
|
|
|
|
- /* If the cursor is currently on the last row and we are appending a
|
|
- ** new row onto the end, set the "loc" to avoid an unnecessary
|
|
- ** btreeMoveto() call */
|
|
+ /* On the other hand, BTREE_SAVEPOSITION==0 does not imply
|
|
+ ** that the cursor is not pointing to a row to be overwritten.
|
|
+ ** So do a complete check.
|
|
+ */
|
|
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){
|
|
- loc = 0;
|
|
+ /* The cursor is pointing to the entry that is to be
|
|
+ ** overwritten */
|
|
+ assert( pX->nData>=0 && pX->nZero>=0 );
|
|
+ if( pCur->info.nSize!=0
|
|
+ && pCur->info.nPayload==(u32)pX->nData+pX->nZero
|
|
+ ){
|
|
+ /* New entry is the same size as the old. Do an overwrite */
|
|
+ return btreeOverwriteCell(pCur, pX);
|
|
+ }
|
|
+ assert( loc==0 );
|
|
}else if( loc==0 ){
|
|
+ /* The cursor is *not* pointing to the cell to be overwritten, nor
|
|
+ ** to an adjacent cell. Move the cursor so that it is pointing either
|
|
+ ** to the cell to be overwritten or an adjacent cell.
|
|
+ */
|
|
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
|
|
if( rc ) return rc;
|
|
}
|
|
- }else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
|
|
- if( pX->nMem ){
|
|
- UnpackedRecord r;
|
|
- r.pKeyInfo = pCur->pKeyInfo;
|
|
- r.aMem = pX->aMem;
|
|
- r.nField = pX->nMem;
|
|
- r.default_rc = 0;
|
|
- r.errCode = 0;
|
|
- r.r1 = 0;
|
|
- r.r2 = 0;
|
|
- r.eqSeen = 0;
|
|
- rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
|
|
- }else{
|
|
- rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
|
|
+ }else{
|
|
+ /* This is an index or a WITHOUT ROWID table */
|
|
+
|
|
+ /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
|
|
+ ** to a row with the same key as the new entry being inserted.
|
|
+ */
|
|
+ assert( (flags & BTREE_SAVEPOSITION)==0 || loc==0 );
|
|
+
|
|
+ /* If the cursor is not already pointing either to the cell to be
|
|
+ ** overwritten, or if a new cell is being inserted, if the cursor is
|
|
+ ** not pointing to an immediately adjacent cell, then move the cursor
|
|
+ ** so that it does.
|
|
+ */
|
|
+ if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
|
|
+ if( pX->nMem ){
|
|
+ UnpackedRecord r;
|
|
+ r.pKeyInfo = pCur->pKeyInfo;
|
|
+ r.aMem = pX->aMem;
|
|
+ r.nField = pX->nMem;
|
|
+ r.default_rc = 0;
|
|
+ r.errCode = 0;
|
|
+ r.r1 = 0;
|
|
+ r.r2 = 0;
|
|
+ r.eqSeen = 0;
|
|
+ rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
|
|
+ }else{
|
|
+ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
|
|
+ }
|
|
+ if( rc ) return rc;
|
|
}
|
|
- if( rc ) return rc;
|
|
+
|
|
+ /* If the cursor is currently pointing to an entry to be overwritten
|
|
+ ** and the new content is the same as as the old, then use the
|
|
+ ** overwrite optimization.
|
|
+ */
|
|
+ if( loc==0 ){
|
|
+ getCellInfo(pCur);
|
|
+ if( pCur->info.nKey==pX->nKey ){
|
|
+ BtreePayload x2;
|
|
+ x2.pData = pX->pKey;
|
|
+ x2.nData = pX->nKey;
|
|
+ x2.nZero = 0;
|
|
+ return btreeOverwriteCell(pCur, &x2);
|
|
+ }
|
|
+ }
|
|
+
|
|
}
|
|
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
|
|
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
assert( pPage->intKey || pX->nKey>=0 );
|
|
assert( pPage->leaf || !pPage->intKey );
|
|
|
|
@@ -67814,10 +71481,10 @@
|
|
** fails. Internal data structure corruption will result otherwise.
|
|
** Also, set the cursor state to invalid. This stops saveCursorPosition()
|
|
** from trying to save the current position of the cursor. */
|
|
- pCur->apPage[pCur->iPage]->nOverflow = 0;
|
|
+ pCur->pPage->nOverflow = 0;
|
|
pCur->eState = CURSOR_INVALID;
|
|
if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){
|
|
- rc = moveToRoot(pCur);
|
|
+ btreeReleaseAllCursorPages(pCur);
|
|
if( pCur->pKeyInfo ){
|
|
assert( pCur->pKey==0 );
|
|
pCur->pKey = sqlite3Malloc( pX->nKey );
|
|
@@ -67831,7 +71498,7 @@
|
|
pCur->nKey = pX->nKey;
|
|
}
|
|
}
|
|
- assert( pCur->apPage[pCur->iPage]->nOverflow==0 );
|
|
+ assert( pCur->iPage<0 || pCur->pPage->nOverflow==0 );
|
|
|
|
end_insert:
|
|
return rc;
|
|
@@ -67872,13 +71539,13 @@
|
|
assert( pCur->curFlags & BTCF_WriteFlag );
|
|
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
|
|
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
|
|
- assert( pCur->ix<pCur->apPage[pCur->iPage]->nCell );
|
|
+ assert( pCur->ix<pCur->pPage->nCell );
|
|
assert( pCur->eState==CURSOR_VALID );
|
|
assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 );
|
|
|
|
iCellDepth = pCur->iPage;
|
|
iCellIdx = pCur->ix;
|
|
- pPage = pCur->apPage[iCellDepth];
|
|
+ pPage = pCur->pPage;
|
|
pCell = findCell(pPage, iCellIdx);
|
|
|
|
/* If the bPreserve flag is set to true, then the cursor position must
|
|
@@ -67944,11 +71611,16 @@
|
|
** node. The cell from the leaf node needs to be moved to the internal
|
|
** node to replace the deleted cell. */
|
|
if( !pPage->leaf ){
|
|
- MemPage *pLeaf = pCur->apPage[pCur->iPage];
|
|
+ MemPage *pLeaf = pCur->pPage;
|
|
int nCell;
|
|
- Pgno n = pCur->apPage[iCellDepth+1]->pgno;
|
|
+ Pgno n;
|
|
unsigned char *pTmp;
|
|
|
|
+ if( iCellDepth<pCur->iPage-1 ){
|
|
+ n = pCur->apPage[iCellDepth+1]->pgno;
|
|
+ }else{
|
|
+ n = pCur->pPage->pgno;
|
|
+ }
|
|
pCell = findCell(pLeaf, pLeaf->nCell-1);
|
|
if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
|
|
nCell = pLeaf->xCellSize(pLeaf, pCell);
|
|
@@ -67980,9 +71652,12 @@
|
|
** well. */
|
|
rc = balance(pCur);
|
|
if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){
|
|
+ releasePageNotNull(pCur->pPage);
|
|
+ pCur->iPage--;
|
|
while( pCur->iPage>iCellDepth ){
|
|
releasePage(pCur->apPage[pCur->iPage--]);
|
|
}
|
|
+ pCur->pPage = pCur->apPage[pCur->iPage];
|
|
rc = balance(pCur);
|
|
}
|
|
|
|
@@ -67989,7 +71664,7 @@
|
|
if( rc==SQLITE_OK ){
|
|
if( bSkipnext ){
|
|
assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) );
|
|
- assert( pPage==pCur->apPage[pCur->iPage] || CORRUPT_DB );
|
|
+ assert( pPage==pCur->pPage || CORRUPT_DB );
|
|
assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
|
|
pCur->eState = CURSOR_SKIPNEXT;
|
|
if( iCellIdx>=pPage->nCell ){
|
|
@@ -68001,8 +71676,10 @@
|
|
}else{
|
|
rc = moveToRoot(pCur);
|
|
if( bPreserve ){
|
|
+ btreeReleaseAllCursorPages(pCur);
|
|
pCur->eState = CURSOR_REQUIRESEEK;
|
|
}
|
|
+ if( rc==SQLITE_EMPTY ) rc = SQLITE_OK;
|
|
}
|
|
}
|
|
return rc;
|
|
@@ -68467,11 +72144,11 @@
|
|
i64 nEntry = 0; /* Value to return in *pnEntry */
|
|
int rc; /* Return code */
|
|
|
|
- if( pCur->pgnoRoot==0 ){
|
|
+ rc = moveToRoot(pCur);
|
|
+ if( rc==SQLITE_EMPTY ){
|
|
*pnEntry = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
- rc = moveToRoot(pCur);
|
|
|
|
/* Unless an error occurs, the following loop runs one iteration for each
|
|
** page in the B-Tree structure (not including overflow pages).
|
|
@@ -68484,7 +72161,7 @@
|
|
** this page contains countable entries. Increment the entry counter
|
|
** accordingly.
|
|
*/
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
if( pPage->leaf || !pPage->intKey ){
|
|
nEntry += pPage->nCell;
|
|
}
|
|
@@ -68507,10 +72184,10 @@
|
|
return moveToRoot(pCur);
|
|
}
|
|
moveToParent(pCur);
|
|
- }while ( pCur->ix>=pCur->apPage[pCur->iPage]->nCell );
|
|
+ }while ( pCur->ix>=pCur->pPage->nCell );
|
|
|
|
pCur->ix++;
|
|
- pPage = pCur->apPage[pCur->iPage];
|
|
+ pPage = pCur->pPage;
|
|
}
|
|
|
|
/* Descend to the child node of the cell that the cursor currently
|
|
@@ -68552,14 +72229,14 @@
|
|
pCheck->nErr++;
|
|
va_start(ap, zFormat);
|
|
if( pCheck->errMsg.nChar ){
|
|
- sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
|
|
+ sqlite3_str_append(&pCheck->errMsg, "\n", 1);
|
|
}
|
|
if( pCheck->zPfx ){
|
|
- sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
|
|
+ sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
|
|
}
|
|
- sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap);
|
|
+ sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
|
|
va_end(ap);
|
|
- if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
|
|
+ if( pCheck->errMsg.accError==SQLITE_NOMEM ){
|
|
pCheck->mallocFailed = 1;
|
|
}
|
|
}
|
|
@@ -68594,8 +72271,7 @@
|
|
** Also check that the page number is in bounds.
|
|
*/
|
|
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
|
|
- if( iPage==0 ) return 1;
|
|
- if( iPage>pCheck->nPage ){
|
|
+ if( iPage>pCheck->nPage || iPage==0 ){
|
|
checkAppendMsg(pCheck, "invalid page number %d", iPage);
|
|
return 1;
|
|
}
|
|
@@ -68650,17 +72326,12 @@
|
|
){
|
|
int i;
|
|
int expected = N;
|
|
- int iFirst = iPage;
|
|
- while( N-- > 0 && pCheck->mxErr ){
|
|
+ int nErrAtStart = pCheck->nErr;
|
|
+ while( iPage!=0 && pCheck->mxErr ){
|
|
DbPage *pOvflPage;
|
|
unsigned char *pOvflData;
|
|
- if( iPage<1 ){
|
|
- checkAppendMsg(pCheck,
|
|
- "%d of %d pages missing from overflow list starting at %d",
|
|
- N+1, expected, iFirst);
|
|
- break;
|
|
- }
|
|
if( checkRef(pCheck, iPage) ) break;
|
|
+ N--;
|
|
if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){
|
|
checkAppendMsg(pCheck, "failed to get page %d", iPage);
|
|
break;
|
|
@@ -68704,11 +72375,13 @@
|
|
#endif
|
|
iPage = get4byte(pOvflData);
|
|
sqlite3PagerUnref(pOvflPage);
|
|
-
|
|
- if( isFreeList && N<(iPage!=0) ){
|
|
- checkAppendMsg(pCheck, "free-page count in header is too small");
|
|
- }
|
|
}
|
|
+ if( N && nErrAtStart==pCheck->nErr ){
|
|
+ checkAppendMsg(pCheck,
|
|
+ "%s is %d but should be %d",
|
|
+ isFreeList ? "size" : "overflow list length",
|
|
+ expected-N, expected);
|
|
+ }
|
|
}
|
|
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
|
|
|
@@ -69101,6 +72774,24 @@
|
|
|
|
/* Check all the tables.
|
|
*/
|
|
+#ifndef SQLITE_OMIT_AUTOVACUUM
|
|
+ if( pBt->autoVacuum ){
|
|
+ int mx = 0;
|
|
+ int mxInHdr;
|
|
+ for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
|
|
+ mxInHdr = get4byte(&pBt->pPage1->aData[52]);
|
|
+ if( mx!=mxInHdr ){
|
|
+ checkAppendMsg(&sCheck,
|
|
+ "max rootpage (%d) disagrees with header (%d)",
|
|
+ mx, mxInHdr
|
|
+ );
|
|
+ }
|
|
+ }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
|
|
+ checkAppendMsg(&sCheck,
|
|
+ "incremental_vacuum enabled with a max rootpage of zero"
|
|
+ );
|
|
+ }
|
|
+#endif
|
|
testcase( pBt->db->flags & SQLITE_CellSizeCk );
|
|
pBt->db->flags &= ~SQLITE_CellSizeCk;
|
|
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
|
|
@@ -69143,11 +72834,11 @@
|
|
sqlite3PageFree(sCheck.heap);
|
|
sqlite3_free(sCheck.aPgRef);
|
|
if( sCheck.mallocFailed ){
|
|
- sqlite3StrAccumReset(&sCheck.errMsg);
|
|
+ sqlite3_str_reset(&sCheck.errMsg);
|
|
sCheck.nErr++;
|
|
}
|
|
*pnErr = sCheck.nErr;
|
|
- if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg);
|
|
+ if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg);
|
|
/* Make sure this analysis did not leave any unref() pages. */
|
|
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
|
|
sqlite3BtreeLeave(p);
|
|
@@ -69351,7 +73042,7 @@
|
|
&& pCsr->pBt->inTransaction==TRANS_WRITE );
|
|
assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
|
|
assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );
|
|
- assert( pCsr->apPage[pCsr->iPage]->intKey );
|
|
+ assert( pCsr->pPage->intKey );
|
|
|
|
return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1);
|
|
}
|
|
@@ -69382,11 +73073,11 @@
|
|
pBt->btsFlags &= ~BTS_NO_WAL;
|
|
if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;
|
|
|
|
- rc = sqlite3BtreeBeginTrans(pBtree, 0);
|
|
+ rc = sqlite3BtreeBeginTrans(pBtree, 0, 0);
|
|
if( rc==SQLITE_OK ){
|
|
u8 *aData = pBt->pPage1->aData;
|
|
if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){
|
|
- rc = sqlite3BtreeBeginTrans(pBtree, 2);
|
|
+ rc = sqlite3BtreeBeginTrans(pBtree, 2, 0);
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
|
if( rc==SQLITE_OK ){
|
|
@@ -69826,7 +73517,7 @@
|
|
** before this function exits.
|
|
*/
|
|
if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){
|
|
- rc = sqlite3BtreeBeginTrans(p->pSrc, 0);
|
|
+ rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0);
|
|
bCloseTrans = 1;
|
|
}
|
|
|
|
@@ -69842,10 +73533,10 @@
|
|
|
|
/* Lock the destination database, if it is not locked already. */
|
|
if( SQLITE_OK==rc && p->bDestLocked==0
|
|
- && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
|
+ && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2,
|
|
+ (int*)&p->iDestSchema))
|
|
){
|
|
p->bDestLocked = 1;
|
|
- sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
|
}
|
|
|
|
/* Do not allow backup if the destination database is in WAL mode
|
|
@@ -70289,8 +73980,7 @@
|
|
|
|
if( p->flags & MEM_Null ){
|
|
/* Cannot be both MEM_Null and some other type */
|
|
- assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob
|
|
- |MEM_RowSet|MEM_Frame|MEM_Agg|MEM_Zero))==0 );
|
|
+ assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob|MEM_Agg))==0 );
|
|
|
|
/* If MEM_Null is set, then either the value is a pure NULL (the usual
|
|
** case) or it is a pointer set using sqlite3_bind_pointer() or
|
|
@@ -70340,6 +74030,51 @@
|
|
}
|
|
#endif
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
+/*
|
|
+** Check that string value of pMem agrees with its integer or real value.
|
|
+**
|
|
+** A single int or real value always converts to the same strings. But
|
|
+** many different strings can be converted into the same int or real.
|
|
+** If a table contains a numeric value and an index is based on the
|
|
+** corresponding string value, then it is important that the string be
|
|
+** derived from the numeric value, not the other way around, to ensure
|
|
+** that the index and table are consistent. See ticket
|
|
+** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for
|
|
+** an example.
|
|
+**
|
|
+** This routine looks at pMem to verify that if it has both a numeric
|
|
+** representation and a string representation then the string rep has
|
|
+** been derived from the numeric and not the other way around. It returns
|
|
+** true if everything is ok and false if there is a problem.
|
|
+**
|
|
+** This routine is for use inside of assert() statements only.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemConsistentDualRep(Mem *p){
|
|
+ char zBuf[100];
|
|
+ char *z;
|
|
+ int i, j, incr;
|
|
+ if( (p->flags & MEM_Str)==0 ) return 1;
|
|
+ if( (p->flags & (MEM_Int|MEM_Real))==0 ) return 1;
|
|
+ if( p->flags & MEM_Int ){
|
|
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%lld",p->u.i);
|
|
+ }else{
|
|
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%!.15g",p->u.r);
|
|
+ }
|
|
+ z = p->z;
|
|
+ i = j = 0;
|
|
+ incr = 1;
|
|
+ if( p->enc!=SQLITE_UTF8 ){
|
|
+ incr = 2;
|
|
+ if( p->enc==SQLITE_UTF16BE ) z++;
|
|
+ }
|
|
+ while( zBuf[j] ){
|
|
+ if( zBuf[j++]!=z[i] ) return 0;
|
|
+ i += incr;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+#endif /* SQLITE_DEBUG */
|
|
|
|
/*
|
|
** If pMem is an object with a valid string representation, this routine
|
|
@@ -70358,7 +74093,7 @@
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
int rc;
|
|
#endif
|
|
- assert( (pMem->flags&MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|
|
|| desiredEnc==SQLITE_UTF16BE );
|
|
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
|
|
@@ -70391,7 +74126,7 @@
|
|
*/
|
|
SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
|
|
assert( sqlite3VdbeCheckMemInvariants(pMem) );
|
|
- assert( (pMem->flags&MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
testcase( pMem->db==0 );
|
|
|
|
/* If the bPreserve flag is set to true, then the memory cell must already
|
|
@@ -70402,7 +74137,7 @@
|
|
assert( pMem->szMalloc==0
|
|
|| pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
|
|
if( n<32 ) n = 32;
|
|
- if( bPreserve && pMem->szMalloc>0 && pMem->z==pMem->zMalloc ){
|
|
+ if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){
|
|
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
|
|
bPreserve = 0;
|
|
}else{
|
|
@@ -70418,7 +74153,8 @@
|
|
pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
|
|
}
|
|
|
|
- if( bPreserve && pMem->z && ALWAYS(pMem->z!=pMem->zMalloc) ){
|
|
+ if( bPreserve && pMem->z ){
|
|
+ assert( pMem->z!=pMem->zMalloc );
|
|
memcpy(pMem->zMalloc, pMem->z, pMem->n);
|
|
}
|
|
if( (pMem->flags&MEM_Dyn)!=0 ){
|
|
@@ -70457,6 +74193,20 @@
|
|
}
|
|
|
|
/*
|
|
+** It is already known that pMem contains an unterminated string.
|
|
+** Add the zero terminator.
|
|
+*/
|
|
+static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
|
|
+ if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
|
|
+ return SQLITE_NOMEM_BKPT;
|
|
+ }
|
|
+ pMem->z[pMem->n] = 0;
|
|
+ pMem->z[pMem->n+1] = 0;
|
|
+ pMem->flags |= MEM_Term;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
** Change pMem so that its MEM_Str or MEM_Blob value is stored in
|
|
** MEM.zMalloc, where it can be safely written.
|
|
**
|
|
@@ -70464,16 +74214,12 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
- assert( (pMem->flags&MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
|
|
if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
|
|
if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
|
|
- if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
|
|
- return SQLITE_NOMEM_BKPT;
|
|
- }
|
|
- pMem->z[pMem->n] = 0;
|
|
- pMem->z[pMem->n+1] = 0;
|
|
- pMem->flags |= MEM_Term;
|
|
+ int rc = vdbeMemAddTerminator(pMem);
|
|
+ if( rc ) return rc;
|
|
}
|
|
}
|
|
pMem->flags &= ~MEM_Ephem;
|
|
@@ -70493,7 +74239,7 @@
|
|
int nByte;
|
|
assert( pMem->flags & MEM_Zero );
|
|
assert( pMem->flags&MEM_Blob );
|
|
- assert( (pMem->flags&MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
|
|
/* Set nByte to the number of bytes required to store the expanded blob. */
|
|
@@ -70513,20 +74259,6 @@
|
|
#endif
|
|
|
|
/*
|
|
-** It is already known that pMem contains an unterminated string.
|
|
-** Add the zero terminator.
|
|
-*/
|
|
-static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
|
|
- if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
|
|
- return SQLITE_NOMEM_BKPT;
|
|
- }
|
|
- pMem->z[pMem->n] = 0;
|
|
- pMem->z[pMem->n+1] = 0;
|
|
- pMem->flags |= MEM_Term;
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/*
|
|
** Make sure the given Mem is \u0000 terminated.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
|
|
@@ -70562,7 +74294,7 @@
|
|
assert( !(fg&MEM_Zero) );
|
|
assert( !(fg&(MEM_Str|MEM_Blob)) );
|
|
assert( fg&(MEM_Int|MEM_Real) );
|
|
- assert( (pMem->flags&MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
|
|
|
|
|
@@ -70583,7 +74315,8 @@
|
|
assert( fg & MEM_Real );
|
|
sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
|
|
}
|
|
- pMem->n = sqlite3Strlen30(pMem->z);
|
|
+ assert( pMem->z!=0 );
|
|
+ pMem->n = sqlite3Strlen30NN(pMem->z);
|
|
pMem->enc = SQLITE_UTF8;
|
|
pMem->flags |= MEM_Str|MEM_Term;
|
|
if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
|
|
@@ -70600,29 +74333,56 @@
|
|
** otherwise.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
|
- int rc = SQLITE_OK;
|
|
- if( ALWAYS(pFunc && pFunc->xFinalize) ){
|
|
- sqlite3_context ctx;
|
|
- Mem t;
|
|
- assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
|
|
- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
- memset(&ctx, 0, sizeof(ctx));
|
|
- memset(&t, 0, sizeof(t));
|
|
- t.flags = MEM_Null;
|
|
- t.db = pMem->db;
|
|
- ctx.pOut = &t;
|
|
- ctx.pMem = pMem;
|
|
- ctx.pFunc = pFunc;
|
|
- pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
|
|
- assert( (pMem->flags & MEM_Dyn)==0 );
|
|
- if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
|
|
- memcpy(pMem, &t, sizeof(t));
|
|
- rc = ctx.isError;
|
|
- }
|
|
- return rc;
|
|
+ sqlite3_context ctx;
|
|
+ Mem t;
|
|
+ assert( pFunc!=0 );
|
|
+ assert( pFunc->xFinalize!=0 );
|
|
+ assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
|
|
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
+ memset(&ctx, 0, sizeof(ctx));
|
|
+ memset(&t, 0, sizeof(t));
|
|
+ t.flags = MEM_Null;
|
|
+ t.db = pMem->db;
|
|
+ ctx.pOut = &t;
|
|
+ ctx.pMem = pMem;
|
|
+ ctx.pFunc = pFunc;
|
|
+ pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
|
|
+ assert( (pMem->flags & MEM_Dyn)==0 );
|
|
+ if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
|
|
+ memcpy(pMem, &t, sizeof(t));
|
|
+ return ctx.isError;
|
|
}
|
|
|
|
/*
|
|
+** Memory cell pAccum contains the context of an aggregate function.
|
|
+** This routine calls the xValue method for that function and stores
|
|
+** the results in memory cell pMem.
|
|
+**
|
|
+** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK
|
|
+** otherwise.
|
|
+*/
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){
|
|
+ sqlite3_context ctx;
|
|
+ Mem t;
|
|
+ assert( pFunc!=0 );
|
|
+ assert( pFunc->xValue!=0 );
|
|
+ assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef );
|
|
+ assert( pAccum->db==0 || sqlite3_mutex_held(pAccum->db->mutex) );
|
|
+ memset(&ctx, 0, sizeof(ctx));
|
|
+ memset(&t, 0, sizeof(t));
|
|
+ t.flags = MEM_Null;
|
|
+ t.db = pAccum->db;
|
|
+ sqlite3VdbeMemSetNull(pOut);
|
|
+ ctx.pOut = pOut;
|
|
+ ctx.pMem = pAccum;
|
|
+ ctx.pFunc = pFunc;
|
|
+ pFunc->xValue(&ctx);
|
|
+ return ctx.isError;
|
|
+}
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
+/*
|
|
** If the memory cell contains a value that must be freed by
|
|
** invoking the external callback in Mem.xDel, then this routine
|
|
** will free that value. It also sets Mem.flags to MEM_Null.
|
|
@@ -70640,15 +74400,8 @@
|
|
testcase( p->flags & MEM_Dyn );
|
|
}
|
|
if( p->flags&MEM_Dyn ){
|
|
- assert( (p->flags&MEM_RowSet)==0 );
|
|
assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
|
|
p->xDel((void *)p->z);
|
|
- }else if( p->flags&MEM_RowSet ){
|
|
- sqlite3RowSetClear(p->u.pRowSet);
|
|
- }else if( p->flags&MEM_Frame ){
|
|
- VdbeFrame *pFrame = p->u.pFrame;
|
|
- pFrame->pParent = pFrame->v->pDelFrame;
|
|
- pFrame->v->pDelFrame = pFrame;
|
|
}
|
|
p->flags = MEM_Null;
|
|
}
|
|
@@ -70780,6 +74533,16 @@
|
|
}
|
|
|
|
/*
|
|
+** Return 1 if pMem represents true, and return 0 if pMem represents false.
|
|
+** Return the value ifNull if pMem is NULL.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){
|
|
+ if( pMem->flags & MEM_Int ) return pMem->u.i!=0;
|
|
+ if( pMem->flags & MEM_Null ) return ifNull;
|
|
+ return sqlite3VdbeRealValue(pMem)!=0.0;
|
|
+}
|
|
+
|
|
+/*
|
|
** The MEM structure is already a MEM_Real. Try to also make it a
|
|
** MEM_Int if we can.
|
|
*/
|
|
@@ -70786,7 +74549,7 @@
|
|
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
|
|
i64 ix;
|
|
assert( pMem->flags & MEM_Real );
|
|
- assert( (pMem->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
|
|
|
@@ -70813,7 +74576,7 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
- assert( (pMem->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
|
|
|
pMem->u.i = sqlite3VdbeIntValue(pMem);
|
|
@@ -70834,6 +74597,18 @@
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
+/* Compare a floating point value to an integer. Return true if the two
|
|
+** values are the same within the precision of the floating point value.
|
|
+**
|
|
+** For some versions of GCC on 32-bit machines, if you do the more obvious
|
|
+** comparison of "r1==(double)i" you sometimes get an answer of false even
|
|
+** though the r1 and (double)i values are bit-for-bit the same.
|
|
+*/
|
|
+static int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
|
|
+ double r2 = (double)i;
|
|
+ return memcmp(&r1, &r2, sizeof(r1))==0;
|
|
+}
|
|
+
|
|
/*
|
|
** Convert pMem so that it has types MEM_Real or MEM_Int or both.
|
|
** Invalidate any prior representations.
|
|
@@ -70844,14 +74619,21 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
|
|
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
|
|
+ int rc;
|
|
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
- if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
|
|
+ rc = sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc);
|
|
+ if( rc==0 ){
|
|
MemSetTypeFlag(pMem, MEM_Int);
|
|
}else{
|
|
- pMem->u.r = sqlite3VdbeRealValue(pMem);
|
|
- MemSetTypeFlag(pMem, MEM_Real);
|
|
- sqlite3VdbeIntegerAffinity(pMem);
|
|
+ i64 i = pMem->u.i;
|
|
+ sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
|
|
+ if( rc==1 && sqlite3RealSameAsInt(pMem->u.r, i) ){
|
|
+ pMem->u.i = i;
|
|
+ MemSetTypeFlag(pMem, MEM_Int);
|
|
+ }else{
|
|
+ MemSetTypeFlag(pMem, MEM_Real);
|
|
+ }
|
|
}
|
|
}
|
|
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
|
|
@@ -70978,7 +74760,7 @@
|
|
}
|
|
|
|
/* A no-op destructor */
|
|
-static void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
|
|
+SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
|
|
|
|
/*
|
|
** Set the value stored in *pMem should already be a NULL.
|
|
@@ -71012,26 +74794,36 @@
|
|
}
|
|
#endif
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
/*
|
|
+** Return true if the Mem holds a RowSet object. This routine is intended
|
|
+** for use inside of assert() statements.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem *pMem){
|
|
+ return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn)
|
|
+ && pMem->xDel==sqlite3RowSetDelete;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Delete any previous value and set the value of pMem to be an
|
|
** empty boolean index.
|
|
+**
|
|
+** Return SQLITE_OK on success and SQLITE_NOMEM if a memory allocation
|
|
+** error occurs.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem *pMem){
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem *pMem){
|
|
sqlite3 *db = pMem->db;
|
|
+ RowSet *p;
|
|
assert( db!=0 );
|
|
- assert( (pMem->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
sqlite3VdbeMemRelease(pMem);
|
|
- pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
|
|
- if( db->mallocFailed ){
|
|
- pMem->flags = MEM_Null;
|
|
- pMem->szMalloc = 0;
|
|
- }else{
|
|
- assert( pMem->zMalloc );
|
|
- pMem->szMalloc = sqlite3DbMallocSize(db, pMem->zMalloc);
|
|
- pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, pMem->szMalloc);
|
|
- assert( pMem->u.pRowSet!=0 );
|
|
- pMem->flags = MEM_RowSet;
|
|
- }
|
|
+ p = sqlite3RowSetInit(db);
|
|
+ if( p==0 ) return SQLITE_NOMEM;
|
|
+ pMem->z = (char*)p;
|
|
+ pMem->flags = MEM_Blob|MEM_Dyn;
|
|
+ pMem->xDel = sqlite3RowSetDelete;
|
|
+ return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
@@ -71064,7 +74856,21 @@
|
|
Mem *pX;
|
|
for(i=0, pX=pVdbe->aMem; i<pVdbe->nMem; i++, pX++){
|
|
if( pX->pScopyFrom==pMem ){
|
|
- pX->flags |= MEM_Undefined;
|
|
+ /* If pX is marked as a shallow copy of pMem, then verify that
|
|
+ ** no significant changes have been made to pX since the OP_SCopy.
|
|
+ ** A significant change would indicated a missed call to this
|
|
+ ** function for pX. Minor changes, such as adding or removing a
|
|
+ ** dual type, are allowed, as long as the underlying value is the
|
|
+ ** same. */
|
|
+ u16 mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
|
|
+ assert( (mFlags&MEM_Int)==0 || pMem->u.i==pX->u.i );
|
|
+ assert( (mFlags&MEM_Real)==0 || pMem->u.r==pX->u.r );
|
|
+ assert( (mFlags&MEM_Str)==0 || (pMem->n==pX->n && pMem->z==pX->z) );
|
|
+ assert( (mFlags&MEM_Blob)==0 || sqlite3BlobCompare(pMem,pX)==0 );
|
|
+
|
|
+ /* pMem is the register that is changing. But also mark pX as
|
|
+ ** undefined so that we can quickly detect the shallow-copy error */
|
|
+ pX->flags = MEM_Undefined;
|
|
pX->pScopyFrom = 0;
|
|
}
|
|
}
|
|
@@ -71085,7 +74891,7 @@
|
|
sqlite3VdbeMemShallowCopy(pTo, pFrom, eType);
|
|
}
|
|
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
|
|
- assert( (pFrom->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pFrom) );
|
|
assert( pTo->db==pFrom->db );
|
|
if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; }
|
|
memcpy(pTo, pFrom, MEMCELLSIZE);
|
|
@@ -71103,7 +74909,7 @@
|
|
SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
|
|
int rc = SQLITE_OK;
|
|
|
|
- assert( (pFrom->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pFrom) );
|
|
if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
|
|
memcpy(pTo, pFrom, MEMCELLSIZE);
|
|
pTo->flags &= ~MEM_Dyn;
|
|
@@ -71161,7 +74967,7 @@
|
|
u16 flags = 0; /* New value for pMem->flags */
|
|
|
|
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
|
- assert( (pMem->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
|
|
/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
|
|
if( !z ){
|
|
@@ -71178,7 +74984,7 @@
|
|
if( nByte<0 ){
|
|
assert( enc!=0 );
|
|
if( enc==SQLITE_UTF8 ){
|
|
- nByte = sqlite3Strlen30(z);
|
|
+ nByte = 0x7fffffff & (int)strlen(z);
|
|
if( nByte>iLimit ) nByte = iLimit+1;
|
|
}else{
|
|
for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
|
|
@@ -71256,12 +75062,11 @@
|
|
){
|
|
int rc;
|
|
pMem->flags = MEM_Null;
|
|
- if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+2)) ){
|
|
+ if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){
|
|
rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z);
|
|
if( rc==SQLITE_OK ){
|
|
- pMem->z[amt] = 0;
|
|
- pMem->z[amt+1] = 0;
|
|
- pMem->flags = MEM_Blob|MEM_Term;
|
|
+ pMem->z[amt] = 0; /* Overrun area used when reading malformed records */
|
|
+ pMem->flags = MEM_Blob;
|
|
pMem->n = (int)amt;
|
|
}else{
|
|
sqlite3VdbeMemRelease(pMem);
|
|
@@ -71284,7 +75089,7 @@
|
|
|
|
/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
|
|
** that both the BtShared and database handle mutexes are held. */
|
|
- assert( (pMem->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
|
zData = (char *)sqlite3BtreePayloadFetch(pCur, &available);
|
|
assert( zData!=0 );
|
|
|
|
@@ -71308,7 +75113,7 @@
|
|
assert( pVal!=0 );
|
|
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
|
|
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
|
|
- assert( (pVal->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pVal) );
|
|
assert( (pVal->flags & (MEM_Null))==0 );
|
|
if( pVal->flags & (MEM_Blob|MEM_Str) ){
|
|
if( ExpandBlob(pVal) ) return 0;
|
|
@@ -71330,6 +75135,7 @@
|
|
assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0
|
|
|| pVal->db->mallocFailed );
|
|
if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){
|
|
+ assert( sqlite3VdbeMemConsistentDualRep(pVal) );
|
|
return pVal->z;
|
|
}else{
|
|
return 0;
|
|
@@ -71350,8 +75156,9 @@
|
|
if( !pVal ) return 0;
|
|
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
|
|
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
|
|
- assert( (pVal->flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pVal) );
|
|
if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
|
|
+ assert( sqlite3VdbeMemConsistentDualRep(pVal) );
|
|
return pVal->z;
|
|
}
|
|
if( pVal->flags&MEM_Null ){
|
|
@@ -71410,7 +75217,7 @@
|
|
if( pRec ){
|
|
pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
|
|
if( pRec->pKeyInfo ){
|
|
- assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
|
|
+ assert( pRec->pKeyInfo->nAllField==nCol );
|
|
assert( pRec->pKeyInfo->enc==ENC(db) );
|
|
pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
|
|
for(i=0; i<nCol; i++){
|
|
@@ -71567,7 +75374,11 @@
|
|
|
|
assert( pExpr!=0 );
|
|
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
|
|
+#if defined(SQLITE_ENABLE_STAT3_OR_STAT4)
|
|
+ if( op==TK_REGISTER ) op = pExpr->op2;
|
|
+#else
|
|
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
|
+#endif
|
|
|
|
/* Compressed expressions only appear when parsing the DEFAULT clause
|
|
** on a table column definition, and hence only when pCtx==0. This
|
|
@@ -71651,18 +75462,25 @@
|
|
0, SQLITE_DYNAMIC);
|
|
}
|
|
#endif
|
|
-
|
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
else if( op==TK_FUNCTION && pCtx!=0 ){
|
|
rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
|
|
}
|
|
#endif
|
|
+ else if( op==TK_TRUEFALSE ){
|
|
+ pVal = valueNew(db, pCtx);
|
|
+ pVal->flags = MEM_Int;
|
|
+ pVal->u.i = pExpr->u.zToken[4]==0;
|
|
+ }
|
|
|
|
*ppVal = pVal;
|
|
return rc;
|
|
|
|
no_mem:
|
|
- sqlite3OomFault(db);
|
|
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
+ if( pCtx==0 || pCtx->pParse->nErr==0 )
|
|
+#endif
|
|
+ sqlite3OomFault(db);
|
|
sqlite3DbFree(db, zVal);
|
|
assert( *ppVal==0 );
|
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
@@ -71905,11 +75723,11 @@
|
|
int iCol, /* Column to extract */
|
|
sqlite3_value **ppVal /* OUT: Extracted value */
|
|
){
|
|
- u32 t; /* a column type code */
|
|
+ u32 t = 0; /* a column type code */
|
|
int nHdr; /* Size of the header in the record */
|
|
int iHdr; /* Next unread header byte */
|
|
int iField; /* Next unread data byte */
|
|
- int szField; /* Size of the current data field */
|
|
+ int szField = 0; /* Size of the current data field */
|
|
int i; /* Column index */
|
|
u8 *a = (u8*)pRec; /* Typecast byte array */
|
|
Mem *pMem = *ppVal; /* Write result into this Mem object */
|
|
@@ -71946,7 +75764,7 @@
|
|
SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
|
|
if( pRec ){
|
|
int i;
|
|
- int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
|
|
+ int nCol = pRec->pKeyInfo->nAllField;
|
|
Mem *aMem = pRec->aMem;
|
|
sqlite3 *db = aMem[0].db;
|
|
for(i=0; i<nCol; i++){
|
|
@@ -72042,10 +75860,12 @@
|
|
db->pVdbe = p;
|
|
p->magic = VDBE_MAGIC_INIT;
|
|
p->pParse = pParse;
|
|
+ pParse->pVdbe = p;
|
|
assert( pParse->aLabel==0 );
|
|
assert( pParse->nLabel==0 );
|
|
assert( pParse->nOpAlloc==0 );
|
|
assert( pParse->szOpAlloc==0 );
|
|
+ sqlite3VdbeAddOp2(p, OP_Init, 0, 1);
|
|
return p;
|
|
}
|
|
|
|
@@ -72071,6 +75891,13 @@
|
|
}
|
|
assert( p->zSql==0 );
|
|
p->zSql = sqlite3DbStrNDup(p->db, z, n);
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ assert( p->zNormSql==0 );
|
|
+ if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){
|
|
+ sqlite3Normalize(p, p->zSql, n, prepFlags);
|
|
+ assert( p->zNormSql!=0 || p->db->mallocFailed );
|
|
+ }
|
|
+#endif
|
|
}
|
|
|
|
/*
|
|
@@ -72092,6 +75919,11 @@
|
|
zTmp = pA->zSql;
|
|
pA->zSql = pB->zSql;
|
|
pB->zSql = zTmp;
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ zTmp = pA->zNormSql;
|
|
+ pA->zNormSql = pB->zNormSql;
|
|
+ pB->zNormSql = zTmp;
|
|
+#endif
|
|
pB->expmask = pA->expmask;
|
|
pB->prepFlags = pA->prepFlags;
|
|
memcpy(pB->aCounter, pA->aCounter, sizeof(pB->aCounter));
|
|
@@ -72200,14 +76032,6 @@
|
|
#endif
|
|
#ifdef SQLITE_DEBUG
|
|
if( p->db->flags & SQLITE_VdbeAddopTrace ){
|
|
- int jj, kk;
|
|
- Parse *pParse = p->pParse;
|
|
- for(jj=kk=0; jj<pParse->nColCache; jj++){
|
|
- struct yColCache *x = pParse->aColCache + jj;
|
|
- printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn);
|
|
- kk++;
|
|
- }
|
|
- if( kk ) printf("\n");
|
|
sqlite3VdbePrintOp(0, i, &p->aOp[i]);
|
|
test_addop_breakpoint();
|
|
}
|
|
@@ -72310,7 +76134,50 @@
|
|
return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
|
|
}
|
|
|
|
+#ifndef SQLITE_OMIT_EXPLAIN
|
|
/*
|
|
+** Return the address of the current EXPLAIN QUERY PLAN baseline.
|
|
+** 0 means "none".
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse *pParse){
|
|
+ VdbeOp *pOp;
|
|
+ if( pParse->addrExplain==0 ) return 0;
|
|
+ pOp = sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrExplain);
|
|
+ return pOp->p2;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add a new OP_Explain opcode.
|
|
+**
|
|
+** If the bPush flag is true, then make this opcode the parent for
|
|
+** subsequent Explains until sqlite3VdbeExplainPop() is called.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
|
|
+ if( pParse->explain==2 ){
|
|
+ char *zMsg;
|
|
+ Vdbe *v;
|
|
+ va_list ap;
|
|
+ int iThis;
|
|
+ va_start(ap, zFmt);
|
|
+ zMsg = sqlite3VMPrintf(pParse->db, zFmt, ap);
|
|
+ va_end(ap);
|
|
+ v = pParse->pVdbe;
|
|
+ iThis = v->nOp;
|
|
+ sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
|
|
+ zMsg, P4_DYNAMIC);
|
|
+ if( bPush) pParse->addrExplain = iThis;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Pop the EXPLAIN QUERY PLAN stack one level.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){
|
|
+ pParse->addrExplain = sqlite3VdbeExplainParent(pParse);
|
|
+}
|
|
+#endif /* SQLITE_OMIT_EXPLAIN */
|
|
+
|
|
+/*
|
|
** Add an OP_ParseSchema opcode. This routine is broken out from
|
|
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
|
|
** as having been used.
|
|
@@ -72399,6 +76266,12 @@
|
|
assert( j<p->nLabel );
|
|
assert( j>=0 );
|
|
if( p->aLabel ){
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
|
|
+ printf("RESOLVE LABEL %d to %d\n", x, v->nOp);
|
|
+ }
|
|
+#endif
|
|
+ assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */
|
|
p->aLabel[j] = v->nOp;
|
|
}
|
|
}
|
|
@@ -72499,7 +76372,8 @@
|
|
** * OP_VUpdate
|
|
** * OP_VRename
|
|
** * OP_FkCounter with P2==0 (immediate foreign key constraint)
|
|
-** * OP_CreateTable and OP_InitCoroutine (for CREATE TABLE AS SELECT ...)
|
|
+** * OP_CreateBtree/BTREE_INTKEY and OP_InitCoroutine
|
|
+** (for CREATE TABLE AS SELECT ...)
|
|
**
|
|
** Then check that the value of Parse.mayAbort is true if an
|
|
** ABORT may be thrown, or false otherwise. Return true if it does
|
|
@@ -72527,7 +76401,7 @@
|
|
hasAbort = 1;
|
|
break;
|
|
}
|
|
- if( opcode==OP_CreateTable ) hasCreateTable = 1;
|
|
+ if( opcode==OP_CreateBtree && pOp->p3==BTREE_INTKEY ) hasCreateTable = 1;
|
|
if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1;
|
|
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
|
if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){
|
|
@@ -72547,7 +76421,33 @@
|
|
}
|
|
#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
/*
|
|
+** Increment the nWrite counter in the VDBE if the cursor is not an
|
|
+** ephemeral cursor, or if the cursor argument is NULL.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCursor *pC){
|
|
+ if( pC==0
|
|
+ || (pC->eCurType!=CURTYPE_SORTER
|
|
+ && pC->eCurType!=CURTYPE_PSEUDO
|
|
+ && !pC->isEphemeral)
|
|
+ ){
|
|
+ p->nWrite++;
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+/*
|
|
+** Assert if an Abort at this point in time might result in a corrupt
|
|
+** database.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){
|
|
+ assert( p->nWrite==0 || p->usesStmtJournal );
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** This routine is called after all opcodes have been inserted. It loops
|
|
** through all the opcodes and fixes up some details.
|
|
**
|
|
@@ -72606,6 +76506,25 @@
|
|
p->bIsReader = 1;
|
|
break;
|
|
}
|
|
+ case OP_Next:
|
|
+ case OP_SorterNext: {
|
|
+ pOp->p4.xAdvance = sqlite3BtreeNext;
|
|
+ pOp->p4type = P4_ADVANCE;
|
|
+ /* The code generator never codes any of these opcodes as a jump
|
|
+ ** to a label. They are always coded as a jump backwards to a
|
|
+ ** known address */
|
|
+ assert( pOp->p2>=0 );
|
|
+ break;
|
|
+ }
|
|
+ case OP_Prev: {
|
|
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
|
|
+ pOp->p4type = P4_ADVANCE;
|
|
+ /* The code generator never codes any of these opcodes as a jump
|
|
+ ** to a label. They are always coded as a jump backwards to a
|
|
+ ** known address */
|
|
+ assert( pOp->p2>=0 );
|
|
+ break;
|
|
+ }
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
case OP_VUpdate: {
|
|
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
|
|
@@ -72617,27 +76536,25 @@
|
|
assert( pOp[-1].opcode==OP_Integer );
|
|
n = pOp[-1].p1;
|
|
if( n>nMaxArgs ) nMaxArgs = n;
|
|
- break;
|
|
+ /* Fall through into the default case */
|
|
}
|
|
#endif
|
|
- case OP_Next:
|
|
- case OP_NextIfOpen:
|
|
- case OP_SorterNext: {
|
|
- pOp->p4.xAdvance = sqlite3BtreeNext;
|
|
- pOp->p4type = P4_ADVANCE;
|
|
+ default: {
|
|
+ if( pOp->p2<0 ){
|
|
+ /* The mkopcodeh.tcl script has so arranged things that the only
|
|
+ ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
|
|
+ ** have non-negative values for P2. */
|
|
+ assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
|
|
+ assert( ADDR(pOp->p2)<pParse->nLabel );
|
|
+ pOp->p2 = aLabel[ADDR(pOp->p2)];
|
|
+ }
|
|
break;
|
|
}
|
|
- case OP_Prev:
|
|
- case OP_PrevIfOpen: {
|
|
- pOp->p4.xAdvance = sqlite3BtreePrevious;
|
|
- pOp->p4type = P4_ADVANCE;
|
|
- break;
|
|
- }
|
|
}
|
|
- if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 && pOp->p2<0 ){
|
|
- assert( ADDR(pOp->p2)<pParse->nLabel );
|
|
- pOp->p2 = aLabel[ADDR(pOp->p2)];
|
|
- }
|
|
+ /* The mkopcodeh.tcl script has so arranged things that the only
|
|
+ ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
|
|
+ ** have non-negative values for P2. */
|
|
+ assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0);
|
|
}
|
|
if( pOp==p->aOp ) break;
|
|
pOp--;
|
|
@@ -72688,6 +76605,17 @@
|
|
#endif
|
|
|
|
/*
|
|
+** Generate code (a single OP_Abortable opcode) that will
|
|
+** verify that the VDBE program can safely call Abort in the current
|
|
+** context.
|
|
+*/
|
|
+#if defined(SQLITE_DEBUG)
|
|
+SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){
|
|
+ if( onError==OE_Abort ) sqlite3VdbeAddOp0(p, OP_Abortable);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** This function returns a pointer to the array of opcodes associated with
|
|
** the Vdbe passed as the first argument. It is the callers responsibility
|
|
** to arrange for the returned array to be eventually freed using the
|
|
@@ -72853,6 +76781,7 @@
|
|
case P4_REAL:
|
|
case P4_INT64:
|
|
case P4_DYNAMIC:
|
|
+ case P4_DYNBLOB:
|
|
case P4_INTARRAY: {
|
|
sqlite3DbFree(db, p4);
|
|
break;
|
|
@@ -73230,23 +77159,23 @@
|
|
const char *zOp = 0;
|
|
switch( pExpr->op ){
|
|
case TK_STRING:
|
|
- sqlite3XPrintf(p, "%Q", pExpr->u.zToken);
|
|
+ sqlite3_str_appendf(p, "%Q", pExpr->u.zToken);
|
|
break;
|
|
case TK_INTEGER:
|
|
- sqlite3XPrintf(p, "%d", pExpr->u.iValue);
|
|
+ sqlite3_str_appendf(p, "%d", pExpr->u.iValue);
|
|
break;
|
|
case TK_NULL:
|
|
- sqlite3XPrintf(p, "NULL");
|
|
+ sqlite3_str_appendf(p, "NULL");
|
|
break;
|
|
case TK_REGISTER: {
|
|
- sqlite3XPrintf(p, "r[%d]", pExpr->iTable);
|
|
+ sqlite3_str_appendf(p, "r[%d]", pExpr->iTable);
|
|
break;
|
|
}
|
|
case TK_COLUMN: {
|
|
if( pExpr->iColumn<0 ){
|
|
- sqlite3XPrintf(p, "rowid");
|
|
+ sqlite3_str_appendf(p, "rowid");
|
|
}else{
|
|
- sqlite3XPrintf(p, "c%d", (int)pExpr->iColumn);
|
|
+ sqlite3_str_appendf(p, "c%d", (int)pExpr->iColumn);
|
|
}
|
|
break;
|
|
}
|
|
@@ -73278,18 +77207,18 @@
|
|
case TK_NOTNULL: zOp = "NOTNULL"; break;
|
|
|
|
default:
|
|
- sqlite3XPrintf(p, "%s", "expr");
|
|
+ sqlite3_str_appendf(p, "%s", "expr");
|
|
break;
|
|
}
|
|
|
|
if( zOp ){
|
|
- sqlite3XPrintf(p, "%s(", zOp);
|
|
+ sqlite3_str_appendf(p, "%s(", zOp);
|
|
displayP4Expr(p, pExpr->pLeft);
|
|
if( pExpr->pRight ){
|
|
- sqlite3StrAccumAppend(p, ",", 1);
|
|
+ sqlite3_str_append(p, ",", 1);
|
|
displayP4Expr(p, pExpr->pRight);
|
|
}
|
|
- sqlite3StrAccumAppend(p, ")", 1);
|
|
+ sqlite3_str_append(p, ")", 1);
|
|
}
|
|
}
|
|
#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
|
|
@@ -73310,14 +77239,15 @@
|
|
int j;
|
|
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
|
|
assert( pKeyInfo->aSortOrder!=0 );
|
|
- sqlite3XPrintf(&x, "k(%d", pKeyInfo->nField);
|
|
- for(j=0; j<pKeyInfo->nField; j++){
|
|
+ sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
|
|
+ for(j=0; j<pKeyInfo->nKeyField; j++){
|
|
CollSeq *pColl = pKeyInfo->aColl[j];
|
|
const char *zColl = pColl ? pColl->zName : "";
|
|
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
|
|
- sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
|
|
+ sqlite3_str_appendf(&x, ",%s%s",
|
|
+ pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
|
|
}
|
|
- sqlite3StrAccumAppend(&x, ")", 1);
|
|
+ sqlite3_str_append(&x, ")", 1);
|
|
break;
|
|
}
|
|
#ifdef SQLITE_ENABLE_CURSOR_HINTS
|
|
@@ -73328,31 +77258,31 @@
|
|
#endif
|
|
case P4_COLLSEQ: {
|
|
CollSeq *pColl = pOp->p4.pColl;
|
|
- sqlite3XPrintf(&x, "(%.20s)", pColl->zName);
|
|
+ sqlite3_str_appendf(&x, "(%.20s)", pColl->zName);
|
|
break;
|
|
}
|
|
case P4_FUNCDEF: {
|
|
FuncDef *pDef = pOp->p4.pFunc;
|
|
- sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
|
+ sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
|
break;
|
|
}
|
|
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
|
case P4_FUNCCTX: {
|
|
FuncDef *pDef = pOp->p4.pCtx->pFunc;
|
|
- sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
|
+ sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
|
break;
|
|
}
|
|
#endif
|
|
case P4_INT64: {
|
|
- sqlite3XPrintf(&x, "%lld", *pOp->p4.pI64);
|
|
+ sqlite3_str_appendf(&x, "%lld", *pOp->p4.pI64);
|
|
break;
|
|
}
|
|
case P4_INT32: {
|
|
- sqlite3XPrintf(&x, "%d", pOp->p4.i);
|
|
+ sqlite3_str_appendf(&x, "%d", pOp->p4.i);
|
|
break;
|
|
}
|
|
case P4_REAL: {
|
|
- sqlite3XPrintf(&x, "%.16g", *pOp->p4.pReal);
|
|
+ sqlite3_str_appendf(&x, "%.16g", *pOp->p4.pReal);
|
|
break;
|
|
}
|
|
case P4_MEM: {
|
|
@@ -73360,9 +77290,9 @@
|
|
if( pMem->flags & MEM_Str ){
|
|
zP4 = pMem->z;
|
|
}else if( pMem->flags & MEM_Int ){
|
|
- sqlite3XPrintf(&x, "%lld", pMem->u.i);
|
|
+ sqlite3_str_appendf(&x, "%lld", pMem->u.i);
|
|
}else if( pMem->flags & MEM_Real ){
|
|
- sqlite3XPrintf(&x, "%.16g", pMem->u.r);
|
|
+ sqlite3_str_appendf(&x, "%.16g", pMem->u.r);
|
|
}else if( pMem->flags & MEM_Null ){
|
|
zP4 = "NULL";
|
|
}else{
|
|
@@ -73374,7 +77304,7 @@
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
case P4_VTAB: {
|
|
sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab;
|
|
- sqlite3XPrintf(&x, "vtab:%p", pVtab);
|
|
+ sqlite3_str_appendf(&x, "vtab:%p", pVtab);
|
|
break;
|
|
}
|
|
#endif
|
|
@@ -73383,23 +77313,24 @@
|
|
int *ai = pOp->p4.ai;
|
|
int n = ai[0]; /* The first element of an INTARRAY is always the
|
|
** count of the number of elements to follow */
|
|
- for(i=1; i<n; i++){
|
|
- sqlite3XPrintf(&x, ",%d", ai[i]);
|
|
+ for(i=1; i<=n; i++){
|
|
+ sqlite3_str_appendf(&x, ",%d", ai[i]);
|
|
}
|
|
zTemp[0] = '[';
|
|
- sqlite3StrAccumAppend(&x, "]", 1);
|
|
+ sqlite3_str_append(&x, "]", 1);
|
|
break;
|
|
}
|
|
case P4_SUBPROGRAM: {
|
|
- sqlite3XPrintf(&x, "program");
|
|
+ sqlite3_str_appendf(&x, "program");
|
|
break;
|
|
}
|
|
+ case P4_DYNBLOB:
|
|
case P4_ADVANCE: {
|
|
zTemp[0] = 0;
|
|
break;
|
|
}
|
|
case P4_TABLE: {
|
|
- sqlite3XPrintf(&x, "%s", pOp->p4.pTab->zName);
|
|
+ sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
|
|
break;
|
|
}
|
|
default: {
|
|
@@ -73500,7 +77431,7 @@
|
|
/*
|
|
** Print a single opcode. This routine is used for debugging only.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
|
|
+SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
|
|
char *zP4;
|
|
char zPtr[50];
|
|
char zCom[100];
|
|
@@ -73569,9 +77500,8 @@
|
|
*/
|
|
testcase( p->flags & MEM_Agg );
|
|
testcase( p->flags & MEM_Dyn );
|
|
- testcase( p->flags & MEM_Frame );
|
|
- testcase( p->flags & MEM_RowSet );
|
|
- if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
|
|
+ testcase( p->xDel==sqlite3VdbeFrameMemDel );
|
|
+ if( p->flags&(MEM_Agg|MEM_Dyn) ){
|
|
sqlite3VdbeMemRelease(p);
|
|
}else if( p->szMalloc ){
|
|
sqlite3DbFreeNN(db, p->zMalloc);
|
|
@@ -73583,7 +77513,36 @@
|
|
}
|
|
}
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
/*
|
|
+** Verify that pFrame is a valid VdbeFrame pointer. Return true if it is
|
|
+** and false if something is wrong.
|
|
+**
|
|
+** This routine is intended for use inside of assert() statements only.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3VdbeFrameIsValid(VdbeFrame *pFrame){
|
|
+ if( pFrame->iFrameMagic!=SQLITE_FRAME_MAGIC ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+** This is a destructor on a Mem object (which is really an sqlite3_value)
|
|
+** that deletes the Frame object that is attached to it as a blob.
|
|
+**
|
|
+** This routine does not delete the Frame right away. It merely adds the
|
|
+** frame to a list of frames to be deleted when the Vdbe halts.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void *pArg){
|
|
+ VdbeFrame *pFrame = (VdbeFrame*)pArg;
|
|
+ assert( sqlite3VdbeFrameIsValid(pFrame) );
|
|
+ pFrame->pParent = pFrame->v->pDelFrame;
|
|
+ pFrame->v->pDelFrame = pFrame;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
** Delete a VdbeFrame object and its contents. VdbeFrame objects are
|
|
** allocated by the OP_Program opcode in sqlite3VdbeExec().
|
|
*/
|
|
@@ -73591,6 +77550,7 @@
|
|
int i;
|
|
Mem *aMem = VdbeFrameMem(p);
|
|
VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem];
|
|
+ assert( sqlite3VdbeFrameIsValid(p) );
|
|
for(i=0; i<p->nChildCsr; i++){
|
|
sqlite3VdbeFreeCursor(p->v, apCsr[i]);
|
|
}
|
|
@@ -73611,6 +77571,9 @@
|
|
** p->explain==2, only OP_Explain instructions are listed and these
|
|
** are shown in a different format. p->explain==2 is used to implement
|
|
** EXPLAIN QUERY PLAN.
|
|
+** 2018-04-24: In p->explain==2 mode, the OP_Init opcodes of triggers
|
|
+** are also shown, so that the boundaries between the main program and
|
|
+** each trigger are clear.
|
|
**
|
|
** When p->explain==1, first the main program is listed, then each of
|
|
** the trigger subprograms are listed one by one.
|
|
@@ -73626,6 +77589,8 @@
|
|
int i; /* Loop counter */
|
|
int rc = SQLITE_OK; /* Return code */
|
|
Mem *pMem = &p->aMem[1]; /* First Mem of result set */
|
|
+ int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
|
|
+ Op *pOp = 0;
|
|
|
|
assert( p->explain );
|
|
assert( p->magic==VDBE_MAGIC_RUN );
|
|
@@ -73638,7 +77603,7 @@
|
|
releaseMemArray(pMem, 8);
|
|
p->pResultSet = 0;
|
|
|
|
- if( p->rc==SQLITE_NOMEM_BKPT ){
|
|
+ if( p->rc==SQLITE_NOMEM ){
|
|
/* This happens if a malloc() inside a call to sqlite3_column_text() or
|
|
** sqlite3_column_text16() failed. */
|
|
sqlite3OomFault(db);
|
|
@@ -73653,7 +77618,7 @@
|
|
** encountered, but p->pc will eventually catch up to nRow.
|
|
*/
|
|
nRow = p->nOp;
|
|
- if( p->explain==1 ){
|
|
+ if( bListSubprogs ){
|
|
/* The first 8 memory cells are used for the result set. So we will
|
|
** commandeer the 9th cell to use as storage for an array of pointers
|
|
** to trigger subprograms. The VDBE is guaranteed to have at least 9
|
|
@@ -73671,19 +77636,13 @@
|
|
}
|
|
}
|
|
|
|
- do{
|
|
+ while(1){ /* Loop exits via break */
|
|
i = p->pc++;
|
|
- }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
|
|
- if( i>=nRow ){
|
|
- p->rc = SQLITE_OK;
|
|
- rc = SQLITE_DONE;
|
|
- }else if( db->u1.isInterrupted ){
|
|
- p->rc = SQLITE_INTERRUPT;
|
|
- rc = SQLITE_ERROR;
|
|
- sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
|
|
- }else{
|
|
- char *zP4;
|
|
- Op *pOp;
|
|
+ if( i>=nRow ){
|
|
+ p->rc = SQLITE_OK;
|
|
+ rc = SQLITE_DONE;
|
|
+ break;
|
|
+ }
|
|
if( i<p->nOp ){
|
|
/* The output line number is small enough that we are still in the
|
|
** main program. */
|
|
@@ -73698,94 +77657,113 @@
|
|
}
|
|
pOp = &apSub[j]->aOp[i];
|
|
}
|
|
- if( p->explain==1 ){
|
|
- pMem->flags = MEM_Int;
|
|
- pMem->u.i = i; /* Program counter */
|
|
- pMem++;
|
|
-
|
|
- pMem->flags = MEM_Static|MEM_Str|MEM_Term;
|
|
- pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
|
|
- assert( pMem->z!=0 );
|
|
- pMem->n = sqlite3Strlen30(pMem->z);
|
|
- pMem->enc = SQLITE_UTF8;
|
|
- pMem++;
|
|
|
|
- /* When an OP_Program opcode is encounter (the only opcode that has
|
|
- ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
|
|
- ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
|
|
- ** has not already been seen.
|
|
- */
|
|
- if( pOp->p4type==P4_SUBPROGRAM ){
|
|
- int nByte = (nSub+1)*sizeof(SubProgram*);
|
|
- int j;
|
|
- for(j=0; j<nSub; j++){
|
|
- if( apSub[j]==pOp->p4.pProgram ) break;
|
|
+ /* When an OP_Program opcode is encounter (the only opcode that has
|
|
+ ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
|
|
+ ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
|
|
+ ** has not already been seen.
|
|
+ */
|
|
+ if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
|
|
+ int nByte = (nSub+1)*sizeof(SubProgram*);
|
|
+ int j;
|
|
+ for(j=0; j<nSub; j++){
|
|
+ if( apSub[j]==pOp->p4.pProgram ) break;
|
|
+ }
|
|
+ if( j==nSub ){
|
|
+ p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
|
|
+ if( p->rc!=SQLITE_OK ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ break;
|
|
}
|
|
- if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, nSub!=0) ){
|
|
- apSub = (SubProgram **)pSub->z;
|
|
- apSub[nSub++] = pOp->p4.pProgram;
|
|
- pSub->flags |= MEM_Blob;
|
|
- pSub->n = nSub*sizeof(SubProgram*);
|
|
- }
|
|
+ apSub = (SubProgram **)pSub->z;
|
|
+ apSub[nSub++] = pOp->p4.pProgram;
|
|
+ pSub->flags |= MEM_Blob;
|
|
+ pSub->n = nSub*sizeof(SubProgram*);
|
|
+ nRow += pOp->p4.pProgram->nOp;
|
|
}
|
|
}
|
|
+ if( p->explain<2 ) break;
|
|
+ if( pOp->opcode==OP_Explain ) break;
|
|
+ if( pOp->opcode==OP_Init && p->pc>1 ) break;
|
|
+ }
|
|
|
|
- pMem->flags = MEM_Int;
|
|
- pMem->u.i = pOp->p1; /* P1 */
|
|
- pMem++;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( db->u1.isInterrupted ){
|
|
+ p->rc = SQLITE_INTERRUPT;
|
|
+ rc = SQLITE_ERROR;
|
|
+ sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
|
|
+ }else{
|
|
+ char *zP4;
|
|
+ if( p->explain==1 ){
|
|
+ pMem->flags = MEM_Int;
|
|
+ pMem->u.i = i; /* Program counter */
|
|
+ pMem++;
|
|
+
|
|
+ pMem->flags = MEM_Static|MEM_Str|MEM_Term;
|
|
+ pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
|
|
+ assert( pMem->z!=0 );
|
|
+ pMem->n = sqlite3Strlen30(pMem->z);
|
|
+ pMem->enc = SQLITE_UTF8;
|
|
+ pMem++;
|
|
+ }
|
|
|
|
- pMem->flags = MEM_Int;
|
|
- pMem->u.i = pOp->p2; /* P2 */
|
|
- pMem++;
|
|
+ pMem->flags = MEM_Int;
|
|
+ pMem->u.i = pOp->p1; /* P1 */
|
|
+ pMem++;
|
|
|
|
- pMem->flags = MEM_Int;
|
|
- pMem->u.i = pOp->p3; /* P3 */
|
|
- pMem++;
|
|
+ pMem->flags = MEM_Int;
|
|
+ pMem->u.i = pOp->p2; /* P2 */
|
|
+ pMem++;
|
|
|
|
- if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
|
|
- assert( p->db->mallocFailed );
|
|
- return SQLITE_ERROR;
|
|
- }
|
|
- pMem->flags = MEM_Str|MEM_Term;
|
|
- zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
|
|
- if( zP4!=pMem->z ){
|
|
- pMem->n = 0;
|
|
- sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
|
|
- }else{
|
|
- assert( pMem->z!=0 );
|
|
- pMem->n = sqlite3Strlen30(pMem->z);
|
|
- pMem->enc = SQLITE_UTF8;
|
|
- }
|
|
- pMem++;
|
|
+ pMem->flags = MEM_Int;
|
|
+ pMem->u.i = pOp->p3; /* P3 */
|
|
+ pMem++;
|
|
|
|
- if( p->explain==1 ){
|
|
- if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
|
|
+ if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
|
|
assert( p->db->mallocFailed );
|
|
return SQLITE_ERROR;
|
|
}
|
|
pMem->flags = MEM_Str|MEM_Term;
|
|
- pMem->n = 2;
|
|
- sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
|
|
- pMem->enc = SQLITE_UTF8;
|
|
+ zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
|
|
+ if( zP4!=pMem->z ){
|
|
+ pMem->n = 0;
|
|
+ sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
|
|
+ }else{
|
|
+ assert( pMem->z!=0 );
|
|
+ pMem->n = sqlite3Strlen30(pMem->z);
|
|
+ pMem->enc = SQLITE_UTF8;
|
|
+ }
|
|
pMem++;
|
|
-
|
|
+
|
|
+ if( p->explain==1 ){
|
|
+ if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
|
|
+ assert( p->db->mallocFailed );
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ pMem->flags = MEM_Str|MEM_Term;
|
|
+ pMem->n = 2;
|
|
+ sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
|
|
+ pMem->enc = SQLITE_UTF8;
|
|
+ pMem++;
|
|
+
|
|
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
- if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
|
|
- assert( p->db->mallocFailed );
|
|
- return SQLITE_ERROR;
|
|
- }
|
|
- pMem->flags = MEM_Str|MEM_Term;
|
|
- pMem->n = displayComment(pOp, zP4, pMem->z, 500);
|
|
- pMem->enc = SQLITE_UTF8;
|
|
+ if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
|
|
+ assert( p->db->mallocFailed );
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ pMem->flags = MEM_Str|MEM_Term;
|
|
+ pMem->n = displayComment(pOp, zP4, pMem->z, 500);
|
|
+ pMem->enc = SQLITE_UTF8;
|
|
#else
|
|
- pMem->flags = MEM_Null; /* Comment */
|
|
+ pMem->flags = MEM_Null; /* Comment */
|
|
#endif
|
|
+ }
|
|
+
|
|
+ p->nResColumn = 8 - 4*(p->explain-1);
|
|
+ p->pResultSet = &p->aMem[1];
|
|
+ p->rc = SQLITE_OK;
|
|
+ rc = SQLITE_ROW;
|
|
}
|
|
-
|
|
- p->nResColumn = 8 - 4*(p->explain-1);
|
|
- p->pResultSet = &p->aMem[1];
|
|
- p->rc = SQLITE_OK;
|
|
- rc = SQLITE_ROW;
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -74148,27 +78126,6 @@
|
|
}
|
|
|
|
/*
|
|
-** Clean up the VM after a single run.
|
|
-*/
|
|
-static void Cleanup(Vdbe *p){
|
|
- sqlite3 *db = p->db;
|
|
-
|
|
-#ifdef SQLITE_DEBUG
|
|
- /* Execute assert() statements to ensure that the Vdbe.apCsr[] and
|
|
- ** Vdbe.aMem[] arrays have already been cleaned up. */
|
|
- int i;
|
|
- if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
|
|
- if( p->aMem ){
|
|
- for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
|
|
- }
|
|
-#endif
|
|
-
|
|
- sqlite3DbFree(db, p->zErrMsg);
|
|
- p->zErrMsg = 0;
|
|
- p->pResultSet = 0;
|
|
-}
|
|
-
|
|
-/*
|
|
** Set the number of result columns that will be returned by this SQL
|
|
** statement. This is now set at compile time, rather than during
|
|
** execution of the vdbe program so that sqlite3_column_count() can
|
|
@@ -74276,6 +78233,7 @@
|
|
pPager = sqlite3BtreePager(pBt);
|
|
if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF
|
|
&& aMJNeeded[sqlite3PagerGetJournalMode(pPager)]
|
|
+ && sqlite3PagerIsMemdb(pPager)==0
|
|
){
|
|
assert( i!=1 );
|
|
nTrans++;
|
|
@@ -74876,6 +78834,10 @@
|
|
** VDBE_MAGIC_INIT.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
|
|
+#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
|
+ int i;
|
|
+#endif
|
|
+
|
|
sqlite3 *db;
|
|
db = p->db;
|
|
|
|
@@ -74885,7 +78847,7 @@
|
|
*/
|
|
sqlite3VdbeHalt(p);
|
|
|
|
- /* If the VDBE has be run even partially, then transfer the error code
|
|
+ /* If the VDBE has been run even partially, then transfer the error code
|
|
** and error message from the VDBE into the main database structure. But
|
|
** if the VDBE has just been set to run but has not actually executed any
|
|
** instructions yet, leave the main database error information unchanged.
|
|
@@ -74893,8 +78855,6 @@
|
|
if( p->pc>=0 ){
|
|
vdbeInvokeSqllog(p);
|
|
sqlite3VdbeTransferError(p);
|
|
- sqlite3DbFree(db, p->zErrMsg);
|
|
- p->zErrMsg = 0;
|
|
if( p->runOnlyOnce ) p->expired = 1;
|
|
}else if( p->rc && p->expired ){
|
|
/* The expired flag was set on the VDBE before the first call
|
|
@@ -74902,13 +78862,24 @@
|
|
** called), set the database error in this case as well.
|
|
*/
|
|
sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
|
|
- sqlite3DbFree(db, p->zErrMsg);
|
|
- p->zErrMsg = 0;
|
|
}
|
|
|
|
- /* Reclaim all memory used by the VDBE
|
|
+ /* Reset register contents and reclaim error message memory.
|
|
*/
|
|
- Cleanup(p);
|
|
+#ifdef SQLITE_DEBUG
|
|
+ /* Execute assert() statements to ensure that the Vdbe.apCsr[] and
|
|
+ ** Vdbe.aMem[] arrays have already been cleaned up. */
|
|
+ if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
|
|
+ if( p->aMem ){
|
|
+ for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
|
|
+ }
|
|
+#endif
|
|
+ sqlite3DbFree(db, p->zErrMsg);
|
|
+ p->zErrMsg = 0;
|
|
+ p->pResultSet = 0;
|
|
+#ifdef SQLITE_DEBUG
|
|
+ p->nWrite = 0;
|
|
+#endif
|
|
|
|
/* Save profiling information from this VDBE run.
|
|
*/
|
|
@@ -74916,7 +78887,6 @@
|
|
{
|
|
FILE *out = fopen("vdbe_profile.out", "a");
|
|
if( out ){
|
|
- int i;
|
|
fprintf(out, "---- ");
|
|
for(i=0; i<p->nOp; i++){
|
|
fprintf(out, "%02x", p->aOp[i].opcode);
|
|
@@ -75025,6 +78995,9 @@
|
|
vdbeFreeOpArray(db, p->aOp, p->nOp);
|
|
sqlite3DbFree(db, p->aColName);
|
|
sqlite3DbFree(db, p->zSql);
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ sqlite3DbFree(db, p->zNormSql);
|
|
+#endif
|
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
|
{
|
|
int i;
|
|
@@ -75042,7 +79015,7 @@
|
|
SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
|
|
sqlite3 *db;
|
|
|
|
- if( NEVER(p==0) ) return;
|
|
+ assert( p!=0 );
|
|
db = p->db;
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
sqlite3VdbeClearObject(db, p);
|
|
@@ -75129,20 +79102,19 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
|
|
VdbeCursor *p = *pp;
|
|
- if( p->eCurType==CURTYPE_BTREE ){
|
|
- if( p->deferredMoveto ){
|
|
- int iMap;
|
|
- if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
|
|
- *pp = p->pAltCursor;
|
|
- *piCol = iMap - 1;
|
|
- return SQLITE_OK;
|
|
- }
|
|
- return handleDeferredMoveto(p);
|
|
+ assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO );
|
|
+ if( p->deferredMoveto ){
|
|
+ int iMap;
|
|
+ if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
|
|
+ *pp = p->pAltCursor;
|
|
+ *piCol = iMap - 1;
|
|
+ return SQLITE_OK;
|
|
}
|
|
- if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
|
|
- return handleMovedCursor(p);
|
|
- }
|
|
+ return handleDeferredMoveto(p);
|
|
}
|
|
+ if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
|
|
+ return handleMovedCursor(p);
|
|
+ }
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -75439,7 +79411,13 @@
|
|
Mem *pMem /* Memory cell to write value into */
|
|
){
|
|
switch( serial_type ){
|
|
- case 10: /* Reserved for future use */
|
|
+ case 10: { /* Internal use only: NULL with virtual table
|
|
+ ** UPDATE no-change flag set */
|
|
+ pMem->flags = MEM_Null|MEM_Zero;
|
|
+ pMem->n = 0;
|
|
+ pMem->u.nZero = 0;
|
|
+ break;
|
|
+ }
|
|
case 11: /* Reserved for future use */
|
|
case 0: { /* Null */
|
|
/* EVIDENCE-OF: R-24078-09375 Value is a NULL. */
|
|
@@ -75537,13 +79515,13 @@
|
|
){
|
|
UnpackedRecord *p; /* Unpacked record to return */
|
|
int nByte; /* Number of bytes required for *p */
|
|
- nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
|
|
+ nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
|
|
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
|
|
if( !p ) return 0;
|
|
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
|
|
assert( pKeyInfo->aSortOrder!=0 );
|
|
p->pKeyInfo = pKeyInfo;
|
|
- p->nField = pKeyInfo->nField + 1;
|
|
+ p->nField = pKeyInfo->nKeyField + 1;
|
|
return p;
|
|
}
|
|
|
|
@@ -75583,7 +79561,7 @@
|
|
pMem++;
|
|
if( (++u)>=p->nField ) break;
|
|
}
|
|
- assert( u<=pKeyInfo->nField + 1 );
|
|
+ assert( u<=pKeyInfo->nKeyField + 1 );
|
|
p->nField = u;
|
|
}
|
|
|
|
@@ -75632,9 +79610,9 @@
|
|
idx1 = getVarint32(aKey1, szHdr1);
|
|
if( szHdr1>98307 ) return SQLITE_CORRUPT;
|
|
d1 = szHdr1;
|
|
- assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
|
|
+ assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
|
|
assert( pKeyInfo->aSortOrder!=0 );
|
|
- assert( pKeyInfo->nField>0 );
|
|
+ assert( pKeyInfo->nKeyField>0 );
|
|
assert( idx1<=szHdr1 || CORRUPT_DB );
|
|
do{
|
|
u32 serial_type1;
|
|
@@ -75696,12 +79674,12 @@
|
|
/*
|
|
** Count the number of fields (a.k.a. columns) in the record given by
|
|
** pKey,nKey. The verify that this count is less than or equal to the
|
|
-** limit given by pKeyInfo->nField + pKeyInfo->nXField.
|
|
+** limit given by pKeyInfo->nAllField.
|
|
**
|
|
** If this constraint is not satisfied, it means that the high-speed
|
|
** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will
|
|
** not work correctly. If this assert() ever fires, it probably means
|
|
-** that the KeyInfo.nField or KeyInfo.nXField values were computed
|
|
+** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed
|
|
** incorrectly.
|
|
*/
|
|
static void vdbeAssertFieldCountWithinLimits(
|
|
@@ -75722,7 +79700,7 @@
|
|
idx += getVarint32(aKey+idx, notUsed);
|
|
nField++;
|
|
}
|
|
- assert( nField <= pKeyInfo->nField+pKeyInfo->nXField );
|
|
+ assert( nField <= pKeyInfo->nAllField );
|
|
}
|
|
#else
|
|
# define vdbeAssertFieldCountWithinLimits(A,B,C)
|
|
@@ -75784,7 +79762,7 @@
|
|
** is less than, equal to, or greater than the second, respectively.
|
|
** If one blob is a prefix of the other, then the shorter is the lessor.
|
|
*/
|
|
-static SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
|
|
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
|
|
int c;
|
|
int n1 = pB1->n;
|
|
int n2 = pB2->n;
|
|
@@ -75827,13 +79805,10 @@
|
|
i64 y;
|
|
double s;
|
|
if( r<-9223372036854775808.0 ) return +1;
|
|
- if( r>9223372036854775807.0 ) return -1;
|
|
+ if( r>=9223372036854775808.0 ) return -1;
|
|
y = (i64)r;
|
|
if( i<y ) return -1;
|
|
- if( i>y ){
|
|
- if( y==SMALLEST_INT64 && r>0.0 ) return -1;
|
|
- return +1;
|
|
- }
|
|
+ if( i>y ) return +1;
|
|
s = (double)i;
|
|
if( s<r ) return -1;
|
|
if( s>r ) return +1;
|
|
@@ -75857,7 +79832,7 @@
|
|
f1 = pMem1->flags;
|
|
f2 = pMem2->flags;
|
|
combined_flags = f1|f2;
|
|
- assert( (combined_flags & MEM_RowSet)==0 );
|
|
+ assert( !sqlite3VdbeMemIsRowSet(pMem1) && !sqlite3VdbeMemIsRowSet(pMem2) );
|
|
|
|
/* If one value is NULL, it is less than the other. If both values
|
|
** are NULL, return 0.
|
|
@@ -76002,7 +79977,7 @@
|
|
u32 idx1; /* Offset of first type in header */
|
|
int rc = 0; /* Return value */
|
|
Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */
|
|
- KeyInfo *pKeyInfo = pPKey2->pKeyInfo;
|
|
+ KeyInfo *pKeyInfo;
|
|
const unsigned char *aKey1 = (const unsigned char *)pKey1;
|
|
Mem mem1;
|
|
|
|
@@ -76027,10 +80002,10 @@
|
|
}
|
|
|
|
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
|
|
- assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField
|
|
+ assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|
|
|| CORRUPT_DB );
|
|
assert( pPKey2->pKeyInfo->aSortOrder!=0 );
|
|
- assert( pPKey2->pKeyInfo->nField>0 );
|
|
+ assert( pPKey2->pKeyInfo->nKeyField>0 );
|
|
assert( idx1<=szHdr1 || CORRUPT_DB );
|
|
do{
|
|
u32 serial_type;
|
|
@@ -76097,7 +80072,7 @@
|
|
if( (d1+mem1.n) > (unsigned)nKey1 ){
|
|
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
|
return 0; /* Corruption */
|
|
- }else if( pKeyInfo->aColl[i] ){
|
|
+ }else if( (pKeyInfo = pPKey2->pKeyInfo)->aColl[i] ){
|
|
mem1.enc = pKeyInfo->enc;
|
|
mem1.db = pKeyInfo->db;
|
|
mem1.flags = MEM_Str;
|
|
@@ -76148,7 +80123,7 @@
|
|
}
|
|
|
|
if( rc!=0 ){
|
|
- if( pKeyInfo->aSortOrder[i] ){
|
|
+ if( pPKey2->pKeyInfo->aSortOrder[i] ){
|
|
rc = -rc;
|
|
}
|
|
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
|
|
@@ -76157,10 +80132,11 @@
|
|
}
|
|
|
|
i++;
|
|
+ if( i==pPKey2->nField ) break;
|
|
pRhs++;
|
|
d1 += sqlite3VdbeSerialTypeLen(serial_type);
|
|
idx1 += sqlite3VarintLen(serial_type);
|
|
- }while( idx1<(unsigned)szHdr1 && i<pPKey2->nField && d1<=(unsigned)nKey1 );
|
|
+ }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 );
|
|
|
|
/* No memory allocation is ever used on mem1. Prove this using
|
|
** the following assert(). If the assert() fails, it indicates a
|
|
@@ -76172,7 +80148,7 @@
|
|
** value. */
|
|
assert( CORRUPT_DB
|
|
|| vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
|
|
- || pKeyInfo->db->mallocFailed
|
|
+ || pPKey2->pKeyInfo->db->mallocFailed
|
|
);
|
|
pPKey2->eqSeen = 1;
|
|
return pPKey2->default_rc;
|
|
@@ -76363,7 +80339,7 @@
|
|
** The easiest way to enforce this limit is to consider only records with
|
|
** 13 fields or less. If the first field is an integer, the maximum legal
|
|
** header size is (12*5 + 1 + 1) bytes. */
|
|
- if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){
|
|
+ if( p->pKeyInfo->nAllField<=13 ){
|
|
int flags = p->aMem[0].flags;
|
|
if( p->pKeyInfo->aSortOrder[0] ){
|
|
p->r1 = 1;
|
|
@@ -76423,7 +80399,9 @@
|
|
(void)getVarint32((u8*)m.z, szHdr);
|
|
testcase( szHdr==3 );
|
|
testcase( szHdr==m.n );
|
|
- if( unlikely(szHdr<3 || (int)szHdr>m.n) ){
|
|
+ testcase( szHdr>0x7fffffff );
|
|
+ assert( m.n>=0 );
|
|
+ if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){
|
|
goto idx_rowid_corruption;
|
|
}
|
|
|
|
@@ -76498,7 +80476,7 @@
|
|
if( rc ){
|
|
return rc;
|
|
}
|
|
- *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked);
|
|
+ *res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0);
|
|
sqlite3VdbeMemRelease(&m);
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -76530,11 +80508,19 @@
|
|
** programs obsolete. Removing user-defined functions or collating
|
|
** sequences, or changing an authorization function are the types of
|
|
** things that make prepared statements obsolete.
|
|
+**
|
|
+** If iCode is 1, then expiration is advisory. The statement should
|
|
+** be reprepared before being restarted, but if it is already running
|
|
+** it is allowed to run to completion.
|
|
+**
|
|
+** Internally, this function just sets the Vdbe.expired flag on all
|
|
+** prepared statements. The flag is set to 1 for an immediate expiration
|
|
+** and set to 2 for an advisory expiration.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db){
|
|
+SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){
|
|
Vdbe *p;
|
|
for(p = db->pVdbe; p; p=p->pNext){
|
|
- p->expired = 1;
|
|
+ p->expired = iCode+1;
|
|
}
|
|
}
|
|
|
|
@@ -76698,7 +80684,7 @@
|
|
preupdate.iNewReg = iReg;
|
|
preupdate.keyinfo.db = db;
|
|
preupdate.keyinfo.enc = ENC(db);
|
|
- preupdate.keyinfo.nField = pTab->nCol;
|
|
+ preupdate.keyinfo.nKeyField = pTab->nCol;
|
|
preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
|
|
preupdate.iKey1 = iKey1;
|
|
preupdate.iKey2 = iKey2;
|
|
@@ -76708,8 +80694,8 @@
|
|
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
|
|
db->pPreUpdate = 0;
|
|
sqlite3DbFree(db, preupdate.aRecord);
|
|
- vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pUnpacked);
|
|
- vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pNewUnpacked);
|
|
+ vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
|
|
+ vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
|
|
if( preupdate.aNew ){
|
|
int i;
|
|
for(i=0; i<pCsr->nField; i++){
|
|
@@ -76992,6 +80978,11 @@
|
|
return aType[pVal->flags&MEM_AffMask];
|
|
}
|
|
|
|
+/* Return true if a parameter to xUpdate represents an unchanged column */
|
|
+SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){
|
|
+ return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero);
|
|
+}
|
|
+
|
|
/* Make a copy of an sqlite3_value object
|
|
*/
|
|
SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
|
|
@@ -77091,7 +81082,6 @@
|
|
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
pCtx->isError = SQLITE_ERROR;
|
|
- pCtx->fErrorOrAux = 1;
|
|
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
|
|
}
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
@@ -77098,7 +81088,6 @@
|
|
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
pCtx->isError = SQLITE_ERROR;
|
|
- pCtx->fErrorOrAux = 1;
|
|
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
|
|
}
|
|
#endif
|
|
@@ -77122,7 +81111,8 @@
|
|
){
|
|
Mem *pOut = pCtx->pOut;
|
|
assert( sqlite3_mutex_held(pOut->db->mutex) );
|
|
- sqlite3VdbeMemSetNull(pOut);
|
|
+ sqlite3VdbeMemRelease(pOut);
|
|
+ pOut->flags = MEM_Null;
|
|
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
|
|
}
|
|
SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
|
|
@@ -77203,8 +81193,7 @@
|
|
return SQLITE_OK;
|
|
}
|
|
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
|
|
- pCtx->isError = errCode;
|
|
- pCtx->fErrorOrAux = 1;
|
|
+ pCtx->isError = errCode ? errCode : -1;
|
|
#ifdef SQLITE_DEBUG
|
|
if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
|
|
#endif
|
|
@@ -77218,7 +81207,6 @@
|
|
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
pCtx->isError = SQLITE_TOOBIG;
|
|
- pCtx->fErrorOrAux = 1;
|
|
sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
|
|
SQLITE_UTF8, SQLITE_STATIC);
|
|
}
|
|
@@ -77228,7 +81216,6 @@
|
|
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
|
|
sqlite3VdbeMemSetNull(pCtx->pOut);
|
|
pCtx->isError = SQLITE_NOMEM_BKPT;
|
|
- pCtx->fErrorOrAux = 1;
|
|
sqlite3OomFault(pCtx->pOut->db);
|
|
}
|
|
|
|
@@ -77247,7 +81234,7 @@
|
|
sqlite3BtreeEnter(pBt);
|
|
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
|
|
sqlite3BtreeLeave(pBt);
|
|
- if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
|
|
+ if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){
|
|
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
|
|
}
|
|
}
|
|
@@ -77357,7 +81344,7 @@
|
|
if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
|
|
#endif
|
|
|
|
- if( rc==SQLITE_DONE ){
|
|
+ if( rc==SQLITE_DONE && db->autoCommit ){
|
|
assert( p->rc==SQLITE_OK );
|
|
p->rc = doWalCallbacks(db);
|
|
if( p->rc!=SQLITE_OK ){
|
|
@@ -77401,7 +81388,6 @@
|
|
*/
|
|
SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
|
|
int rc = SQLITE_OK; /* Result from sqlite3Step() */
|
|
- int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */
|
|
Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */
|
|
int cnt = 0; /* Counter to prevent infinite loop of reprepares */
|
|
sqlite3 *db; /* The database connection */
|
|
@@ -77415,32 +81401,31 @@
|
|
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
|
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
|
|
int savedPc = v->pc;
|
|
- rc2 = rc = sqlite3Reprepare(v);
|
|
- if( rc!=SQLITE_OK) break;
|
|
+ rc = sqlite3Reprepare(v);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ /* This case occurs after failing to recompile an sql statement.
|
|
+ ** The error message from the SQL compiler has already been loaded
|
|
+ ** into the database handle. This block copies the error message
|
|
+ ** from the database handle into the statement and sets the statement
|
|
+ ** program counter to 0 to ensure that when the statement is
|
|
+ ** finalized or reset the parser error message is available via
|
|
+ ** sqlite3_errmsg() and sqlite3_errcode().
|
|
+ */
|
|
+ const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
|
+ sqlite3DbFree(db, v->zErrMsg);
|
|
+ if( !db->mallocFailed ){
|
|
+ v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
|
+ v->rc = rc = sqlite3ApiExit(db, rc);
|
|
+ } else {
|
|
+ v->zErrMsg = 0;
|
|
+ v->rc = rc = SQLITE_NOMEM_BKPT;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
sqlite3_reset(pStmt);
|
|
if( savedPc>=0 ) v->doingRerun = 1;
|
|
assert( v->expired==0 );
|
|
}
|
|
- if( rc2!=SQLITE_OK ){
|
|
- /* This case occurs after failing to recompile an sql statement.
|
|
- ** The error message from the SQL compiler has already been loaded
|
|
- ** into the database handle. This block copies the error message
|
|
- ** from the database handle into the statement and sets the statement
|
|
- ** program counter to 0 to ensure that when the statement is
|
|
- ** finalized or reset the parser error message is available via
|
|
- ** sqlite3_errmsg() and sqlite3_errcode().
|
|
- */
|
|
- const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
|
- sqlite3DbFree(db, v->zErrMsg);
|
|
- if( !db->mallocFailed ){
|
|
- v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
|
- v->rc = rc2;
|
|
- } else {
|
|
- v->zErrMsg = 0;
|
|
- v->rc = rc = SQLITE_NOMEM_BKPT;
|
|
- }
|
|
- }
|
|
- rc = sqlite3ApiExit(db, rc);
|
|
sqlite3_mutex_leave(db->mutex);
|
|
return rc;
|
|
}
|
|
@@ -77471,6 +81456,25 @@
|
|
}
|
|
|
|
/*
|
|
+** If this routine is invoked from within an xColumn method of a virtual
|
|
+** table, then it returns true if and only if the the call is during an
|
|
+** UPDATE operation and the value of the column will not be modified
|
|
+** by the UPDATE.
|
|
+**
|
|
+** If this routine is called from any context other than within the
|
|
+** xColumn method of a virtual table, then the return value is meaningless
|
|
+** and arbitrary.
|
|
+**
|
|
+** Virtual table implements might use this routine to optimize their
|
|
+** performance by substituting a NULL result, or some other light-weight
|
|
+** value, as a signal to the xUpdate routine that the column is unchanged.
|
|
+*/
|
|
+SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
|
|
+ assert( p );
|
|
+ return sqlite3_value_nochange(p->pOut);
|
|
+}
|
|
+
|
|
+/*
|
|
** Return the current time for a statement. If the current time
|
|
** is requested more than once within the same run of a single prepared
|
|
** statement, the exact same time is returned for each invocation regardless
|
|
@@ -77494,28 +81498,6 @@
|
|
}
|
|
|
|
/*
|
|
-** The following is the implementation of an SQL function that always
|
|
-** fails with an error message stating that the function is used in the
|
|
-** wrong context. The sqlite3_overload_function() API might construct
|
|
-** SQL function that use this routine so that the functions will exist
|
|
-** for name resolution but are actually overloaded by the xFindFunction
|
|
-** method of virtual tables.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3InvalidFunction(
|
|
- sqlite3_context *context, /* The function calling context */
|
|
- int NotUsed, /* Number of arguments to the function */
|
|
- sqlite3_value **NotUsed2 /* Value of each argument */
|
|
-){
|
|
- const char *zName = context->pFunc->zName;
|
|
- char *zErr;
|
|
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
- zErr = sqlite3_mprintf(
|
|
- "unable to use function %s in the requested context", zName);
|
|
- sqlite3_result_error(context, zErr, -1);
|
|
- sqlite3_free(zErr);
|
|
-}
|
|
-
|
|
-/*
|
|
** Create a new aggregate context for p and return a pointer to
|
|
** its pMem->z element.
|
|
*/
|
|
@@ -77618,10 +81600,7 @@
|
|
pAuxData->iAuxArg = iArg;
|
|
pAuxData->pNextAux = pVdbe->pAuxData;
|
|
pVdbe->pAuxData = pAuxData;
|
|
- if( pCtx->fErrorOrAux==0 ){
|
|
- pCtx->isError = 0;
|
|
- pCtx->fErrorOrAux = 1;
|
|
- }
|
|
+ if( pCtx->isError==0 ) pCtx->isError = -1;
|
|
}else if( pAuxData->xDeleteAux ){
|
|
pAuxData->xDeleteAux(pAuxData->pAux);
|
|
}
|
|
@@ -77701,7 +81680,7 @@
|
|
/* .xDel = */ (void(*)(void*))0,
|
|
#ifdef SQLITE_DEBUG
|
|
/* .pScopyFrom = */ (Mem*)0,
|
|
- /* .pFiller = */ (void*)0,
|
|
+ /* .mScopyFlags= */ 0,
|
|
#endif
|
|
};
|
|
return &nullMem;
|
|
@@ -78377,7 +82356,9 @@
|
|
Vdbe *pVdbe = (Vdbe*)pStmt;
|
|
u32 v;
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
- if( !pStmt ){
|
|
+ if( !pStmt
|
|
+ || (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter)))
|
|
+ ){
|
|
(void)SQLITE_MISUSE_BKPT;
|
|
return 0;
|
|
}
|
|
@@ -78431,6 +82412,16 @@
|
|
#endif
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+/*
|
|
+** Return the normalized SQL associated with a prepared statement.
|
|
+*/
|
|
+SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){
|
|
+ Vdbe *p = (Vdbe *)pStmt;
|
|
+ return p ? p->zNormSql : 0;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
+
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
/*
|
|
** Allocate and populate an UnpackedRecord structure based on the serialized
|
|
@@ -78446,7 +82437,7 @@
|
|
|
|
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
|
|
if( pRet ){
|
|
- memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
|
|
+ memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
|
|
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
|
|
}
|
|
return pRet;
|
|
@@ -78519,7 +82510,7 @@
|
|
*/
|
|
SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
|
|
PreUpdate *p = db->pPreUpdate;
|
|
- return (p ? p->keyinfo.nField : 0);
|
|
+ return (p ? p->keyinfo.nKeyField : 0);
|
|
}
|
|
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
|
|
|
@@ -78772,7 +82763,7 @@
|
|
Mem *pVar; /* Value of a host parameter */
|
|
StrAccum out; /* Accumulate the output here */
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
- Mem utf8; /* Used to convert UTF16 parameters into UTF8 for display */
|
|
+ Mem utf8; /* Used to convert UTF16 into UTF8 for display */
|
|
#endif
|
|
char zBase[100]; /* Initial working space */
|
|
|
|
@@ -78783,17 +82774,17 @@
|
|
while( *zRawSql ){
|
|
const char *zStart = zRawSql;
|
|
while( *(zRawSql++)!='\n' && *zRawSql );
|
|
- sqlite3StrAccumAppend(&out, "-- ", 3);
|
|
+ sqlite3_str_append(&out, "-- ", 3);
|
|
assert( (zRawSql - zStart) > 0 );
|
|
- sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
|
|
+ sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart));
|
|
}
|
|
}else if( p->nVar==0 ){
|
|
- sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql));
|
|
+ sqlite3_str_append(&out, zRawSql, sqlite3Strlen30(zRawSql));
|
|
}else{
|
|
while( zRawSql[0] ){
|
|
n = findNextHostParameter(zRawSql, &nToken);
|
|
assert( n>0 );
|
|
- sqlite3StrAccumAppend(&out, zRawSql, n);
|
|
+ sqlite3_str_append(&out, zRawSql, n);
|
|
zRawSql += n;
|
|
assert( zRawSql[0] || nToken==0 );
|
|
if( nToken==0 ) break;
|
|
@@ -78819,11 +82810,11 @@
|
|
assert( idx>0 && idx<=p->nVar );
|
|
pVar = &p->aVar[idx-1];
|
|
if( pVar->flags & MEM_Null ){
|
|
- sqlite3StrAccumAppend(&out, "NULL", 4);
|
|
+ sqlite3_str_append(&out, "NULL", 4);
|
|
}else if( pVar->flags & MEM_Int ){
|
|
- sqlite3XPrintf(&out, "%lld", pVar->u.i);
|
|
+ sqlite3_str_appendf(&out, "%lld", pVar->u.i);
|
|
}else if( pVar->flags & MEM_Real ){
|
|
- sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
|
|
+ sqlite3_str_appendf(&out, "%!.15g", pVar->u.r);
|
|
}else if( pVar->flags & MEM_Str ){
|
|
int nOut; /* Number of bytes of the string text to include in output */
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
@@ -78833,7 +82824,7 @@
|
|
utf8.db = db;
|
|
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
|
|
if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){
|
|
- out.accError = STRACCUM_NOMEM;
|
|
+ out.accError = SQLITE_NOMEM;
|
|
out.nAlloc = 0;
|
|
}
|
|
pVar = &utf8;
|
|
@@ -78846,10 +82837,10 @@
|
|
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
|
|
}
|
|
#endif
|
|
- sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
|
|
+ sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z);
|
|
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
|
if( nOut<pVar->n ){
|
|
- sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
|
+ sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
|
}
|
|
#endif
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
@@ -78856,28 +82847,28 @@
|
|
if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
|
|
#endif
|
|
}else if( pVar->flags & MEM_Zero ){
|
|
- sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
|
|
+ sqlite3_str_appendf(&out, "zeroblob(%d)", pVar->u.nZero);
|
|
}else{
|
|
int nOut; /* Number of bytes of the blob to include in output */
|
|
assert( pVar->flags & MEM_Blob );
|
|
- sqlite3StrAccumAppend(&out, "x'", 2);
|
|
+ sqlite3_str_append(&out, "x'", 2);
|
|
nOut = pVar->n;
|
|
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
|
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
|
|
#endif
|
|
for(i=0; i<nOut; i++){
|
|
- sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
|
|
+ sqlite3_str_appendf(&out, "%02x", pVar->z[i]&0xff);
|
|
}
|
|
- sqlite3StrAccumAppend(&out, "'", 1);
|
|
+ sqlite3_str_append(&out, "'", 1);
|
|
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
|
if( nOut<pVar->n ){
|
|
- sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
|
+ sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
- if( out.accError ) sqlite3StrAccumReset(&out);
|
|
+ if( out.accError ) sqlite3_str_reset(&out);
|
|
return sqlite3StrAccumFinish(&out);
|
|
}
|
|
|
|
@@ -79009,32 +83000,56 @@
|
|
** feature is used for test suite validation only and does not appear an
|
|
** production builds.
|
|
**
|
|
-** M is an integer, 2 or 3, that indices how many different ways the
|
|
-** branch can go. It is usually 2. "I" is the direction the branch
|
|
-** goes. 0 means falls through. 1 means branch is taken. 2 means the
|
|
-** second alternative branch is taken.
|
|
+** M is an integer between 2 and 4. 2 indicates a ordinary two-way
|
|
+** branch (I=0 means fall through and I=1 means taken). 3 indicates
|
|
+** a 3-way branch where the third way is when one of the operands is
|
|
+** NULL. 4 indicates the OP_Jump instruction which has three destinations
|
|
+** depending on whether the first operand is less than, equal to, or greater
|
|
+** than the second.
|
|
**
|
|
** iSrcLine is the source code line (from the __LINE__ macro) that
|
|
-** generated the VDBE instruction. This instrumentation assumes that all
|
|
-** source code is in a single file (the amalgamation). Special values 1
|
|
-** and 2 for the iSrcLine parameter mean that this particular branch is
|
|
-** always taken or never taken, respectively.
|
|
+** generated the VDBE instruction combined with flag bits. The source
|
|
+** code line number is in the lower 24 bits of iSrcLine and the upper
|
|
+** 8 bytes are flags. The lower three bits of the flags indicate
|
|
+** values for I that should never occur. For example, if the branch is
|
|
+** always taken, the flags should be 0x05 since the fall-through and
|
|
+** alternate branch are never taken. If a branch is never taken then
|
|
+** flags should be 0x06 since only the fall-through approach is allowed.
|
|
+**
|
|
+** Bit 0x04 of the flags indicates an OP_Jump opcode that is only
|
|
+** interested in equal or not-equal. In other words, I==0 and I==2
|
|
+** should be treated the same.
|
|
+**
|
|
+** Since only a line number is retained, not the filename, this macro
|
|
+** only works for amalgamation builds. But that is ok, since these macros
|
|
+** should be no-ops except for special builds used to measure test coverage.
|
|
*/
|
|
#if !defined(SQLITE_VDBE_COVERAGE)
|
|
# define VdbeBranchTaken(I,M)
|
|
#else
|
|
# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M)
|
|
- static void vdbeTakeBranch(int iSrcLine, u8 I, u8 M){
|
|
- if( iSrcLine<=2 && ALWAYS(iSrcLine>0) ){
|
|
- M = iSrcLine;
|
|
- /* Assert the truth of VdbeCoverageAlwaysTaken() and
|
|
- ** VdbeCoverageNeverTaken() */
|
|
- assert( (M & I)==I );
|
|
- }else{
|
|
- if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
|
|
- sqlite3GlobalConfig.xVdbeBranch(sqlite3GlobalConfig.pVdbeBranchArg,
|
|
- iSrcLine,I,M);
|
|
+ static void vdbeTakeBranch(u32 iSrcLine, u8 I, u8 M){
|
|
+ u8 mNever;
|
|
+ assert( I<=2 ); /* 0: fall through, 1: taken, 2: alternate taken */
|
|
+ assert( M<=4 ); /* 2: two-way branch, 3: three-way branch, 4: OP_Jump */
|
|
+ assert( I<M ); /* I can only be 2 if M is 3 or 4 */
|
|
+ /* Transform I from a integer [0,1,2] into a bitmask of [1,2,4] */
|
|
+ I = 1<<I;
|
|
+ /* The upper 8 bits of iSrcLine are flags. The lower three bits of
|
|
+ ** the flags indicate directions that the branch can never go. If
|
|
+ ** a branch really does go in one of those directions, assert right
|
|
+ ** away. */
|
|
+ mNever = iSrcLine >> 24;
|
|
+ assert( (I & mNever)==0 );
|
|
+ if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
|
|
+ I |= mNever;
|
|
+ if( M==2 ) I |= 0x04;
|
|
+ if( M==4 ){
|
|
+ I |= 0x08;
|
|
+ if( (mNever&0x08)!=0 && (I&0x05)!=0) I |= 0x05; /*NO_TEST*/
|
|
}
|
|
+ sqlite3GlobalConfig.xVdbeBranch(sqlite3GlobalConfig.pVdbeBranchArg,
|
|
+ iSrcLine&0xffffff, I, M);
|
|
}
|
|
#endif
|
|
|
|
@@ -79151,6 +83166,11 @@
|
|
pRec->flags |= MEM_Real;
|
|
if( bTryForInt ) sqlite3VdbeIntegerAffinity(pRec);
|
|
}
|
|
+ /* TEXT->NUMERIC is many->one. Hence, it is important to invalidate the
|
|
+ ** string representation after computing a numeric equivalent, because the
|
|
+ ** string representation might not be the canonical representation for the
|
|
+ ** numeric value. Ticket [343634942dd54ab57b7024] 2018-01-31. */
|
|
+ pRec->flags &= ~MEM_Str;
|
|
}
|
|
|
|
/*
|
|
@@ -79241,7 +83261,7 @@
|
|
if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){
|
|
return 0;
|
|
}
|
|
- if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==SQLITE_OK ){
|
|
+ if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==0 ){
|
|
return MEM_Int;
|
|
}
|
|
return MEM_Real;
|
|
@@ -79351,7 +83371,7 @@
|
|
if( p->flags & MEM_Undefined ){
|
|
printf(" undefined");
|
|
}else if( p->flags & MEM_Null ){
|
|
- printf(" NULL");
|
|
+ printf(p->flags & MEM_Zero ? " NULL-nochng" : " NULL");
|
|
}else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
|
|
printf(" si:%lld", p->u.i);
|
|
}else if( p->flags & MEM_Int ){
|
|
@@ -79360,7 +83380,7 @@
|
|
}else if( p->flags & MEM_Real ){
|
|
printf(" r:%g", p->u.r);
|
|
#endif
|
|
- }else if( p->flags & MEM_RowSet ){
|
|
+ }else if( sqlite3VdbeMemIsRowSet(p) ){
|
|
printf(" (rowset)");
|
|
}else{
|
|
char zBuf[200];
|
|
@@ -79619,7 +83639,7 @@
|
|
|
|
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
|
|
#ifdef VDBE_PROFILE
|
|
- start = sqlite3Hwtime();
|
|
+ start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
|
|
#endif
|
|
nVmStep++;
|
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
|
@@ -79886,6 +83906,9 @@
|
|
*/
|
|
case OP_HaltIfNull: { /* in3 */
|
|
pIn3 = &aMem[pOp->p3];
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
|
|
+#endif
|
|
if( (pIn3->flags & MEM_Null)==0 ) break;
|
|
/* Fall through into OP_Halt */
|
|
}
|
|
@@ -79925,6 +83948,9 @@
|
|
int pcx;
|
|
|
|
pcx = (int)(pOp - aOp);
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
|
|
+#endif
|
|
if( pOp->p1==SQLITE_OK && p->pFrame ){
|
|
/* Halt the sub-program. Return control to the parent frame. */
|
|
pFrame = p->pFrame;
|
|
@@ -80108,6 +84134,9 @@
|
|
assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
|
|
pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
|
|
pOut->n = 0;
|
|
+#ifdef SQLITE_DEBUG
|
|
+ pOut->uTemp = 0;
|
|
+#endif
|
|
while( cnt>0 ){
|
|
pOut++;
|
|
memAboutToChange(p, pOut);
|
|
@@ -80229,6 +84258,7 @@
|
|
pOut = &aMem[pOp->p2];
|
|
assert( pOut!=pIn1 );
|
|
while( 1 ){
|
|
+ memAboutToChange(p, pOut);
|
|
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
|
|
Deephemeralize(pOut);
|
|
#ifdef SQLITE_DEBUG
|
|
@@ -80261,7 +84291,8 @@
|
|
assert( pOut!=pIn1 );
|
|
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
|
|
#ifdef SQLITE_DEBUG
|
|
- if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
|
|
+ pOut->pScopyFrom = pIn1;
|
|
+ pOut->mScopyFlags = pIn1->flags;
|
|
#endif
|
|
break;
|
|
}
|
|
@@ -80895,7 +84926,12 @@
|
|
if( (flags1 | flags3)&MEM_Str ){
|
|
if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
|
|
applyNumericAffinity(pIn1,0);
|
|
- testcase( flags3!=pIn3->flags ); /* Possible if pIn1==pIn3 */
|
|
+ assert( flags3==pIn3->flags );
|
|
+ /* testcase( flags3!=pIn3->flags );
|
|
+ ** this used to be possible with pIn1==pIn3, but not since
|
|
+ ** the column cache was removed. The following assignment
|
|
+ ** is essentially a no-op. But, it provides defense-in-depth
|
|
+ ** in case our analysis is incorrect, so it is left in. */
|
|
flags3 = pIn3->flags;
|
|
}
|
|
if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
|
|
@@ -80931,13 +84967,23 @@
|
|
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
|
|
}
|
|
compare_op:
|
|
- switch( pOp->opcode ){
|
|
- case OP_Eq: res2 = res==0; break;
|
|
- case OP_Ne: res2 = res; break;
|
|
- case OP_Lt: res2 = res<0; break;
|
|
- case OP_Le: res2 = res<=0; break;
|
|
- case OP_Gt: res2 = res>0; break;
|
|
- default: res2 = res>=0; break;
|
|
+ /* At this point, res is negative, zero, or positive if reg[P1] is
|
|
+ ** less than, equal to, or greater than reg[P3], respectively. Compute
|
|
+ ** the answer to this operator in res2, depending on what the comparison
|
|
+ ** operator actually is. The next block of code depends on the fact
|
|
+ ** that the 6 comparison operators are consecutive integers in this
|
|
+ ** order: NE, EQ, GT, LE, LT, GE */
|
|
+ assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 );
|
|
+ assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 );
|
|
+ if( res<0 ){ /* ne, eq, gt, le, lt, ge */
|
|
+ static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 };
|
|
+ res2 = aLTb[pOp->opcode - OP_Ne];
|
|
+ }else if( res==0 ){
|
|
+ static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 };
|
|
+ res2 = aEQb[pOp->opcode - OP_Ne];
|
|
+ }else{
|
|
+ static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 };
|
|
+ res2 = aGTb[pOp->opcode - OP_Ne];
|
|
}
|
|
|
|
/* Undo any changes made by applyAffinity() to the input registers. */
|
|
@@ -80949,7 +84995,6 @@
|
|
if( pOp->p5 & SQLITE_STOREP2 ){
|
|
pOut = &aMem[pOp->p2];
|
|
iCompare = res;
|
|
- res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
|
|
if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
|
|
/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
|
|
** and prevents OP_Ne from overwriting NULL with 0. This flag
|
|
@@ -81080,7 +85125,7 @@
|
|
assert( memIsValid(&aMem[p2+idx]) );
|
|
REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
|
|
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
|
|
- assert( i<pKeyInfo->nField );
|
|
+ assert( i<pKeyInfo->nKeyField );
|
|
pColl = pKeyInfo->aColl[i];
|
|
bRev = pKeyInfo->aSortOrder[i];
|
|
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
|
|
@@ -81100,11 +85145,11 @@
|
|
*/
|
|
case OP_Jump: { /* jump */
|
|
if( iCompare<0 ){
|
|
- VdbeBranchTaken(0,3); pOp = &aOp[pOp->p1 - 1];
|
|
+ VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1];
|
|
}else if( iCompare==0 ){
|
|
- VdbeBranchTaken(1,3); pOp = &aOp[pOp->p2 - 1];
|
|
+ VdbeBranchTaken(1,4); pOp = &aOp[pOp->p2 - 1];
|
|
}else{
|
|
- VdbeBranchTaken(2,3); pOp = &aOp[pOp->p3 - 1];
|
|
+ VdbeBranchTaken(2,4); pOp = &aOp[pOp->p3 - 1];
|
|
}
|
|
break;
|
|
}
|
|
@@ -81134,18 +85179,8 @@
|
|
int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
|
|
int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
|
|
|
|
- pIn1 = &aMem[pOp->p1];
|
|
- if( pIn1->flags & MEM_Null ){
|
|
- v1 = 2;
|
|
- }else{
|
|
- v1 = sqlite3VdbeIntValue(pIn1)!=0;
|
|
- }
|
|
- pIn2 = &aMem[pOp->p2];
|
|
- if( pIn2->flags & MEM_Null ){
|
|
- v2 = 2;
|
|
- }else{
|
|
- v2 = sqlite3VdbeIntValue(pIn2)!=0;
|
|
- }
|
|
+ v1 = sqlite3VdbeBooleanValue(&aMem[pOp->p1], 2);
|
|
+ v2 = sqlite3VdbeBooleanValue(&aMem[pOp->p2], 2);
|
|
if( pOp->opcode==OP_And ){
|
|
static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
|
|
v1 = and_logic[v1*3+v2];
|
|
@@ -81163,6 +85198,35 @@
|
|
break;
|
|
}
|
|
|
|
+/* Opcode: IsTrue P1 P2 P3 P4 *
|
|
+** Synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4
|
|
+**
|
|
+** This opcode implements the IS TRUE, IS FALSE, IS NOT TRUE, and
|
|
+** IS NOT FALSE operators.
|
|
+**
|
|
+** Interpret the value in register P1 as a boolean value. Store that
|
|
+** boolean (a 0 or 1) in register P2. Or if the value in register P1 is
|
|
+** NULL, then the P3 is stored in register P2. Invert the answer if P4
|
|
+** is 1.
|
|
+**
|
|
+** The logic is summarized like this:
|
|
+**
|
|
+** <ul>
|
|
+** <li> If P3==0 and P4==0 then r[P2] := r[P1] IS TRUE
|
|
+** <li> If P3==1 and P4==1 then r[P2] := r[P1] IS FALSE
|
|
+** <li> If P3==0 and P4==1 then r[P2] := r[P1] IS NOT TRUE
|
|
+** <li> If P3==1 and P4==0 then r[P2] := r[P1] IS NOT FALSE
|
|
+** </ul>
|
|
+*/
|
|
+case OP_IsTrue: { /* in1, out2 */
|
|
+ assert( pOp->p4type==P4_INT32 );
|
|
+ assert( pOp->p4.i==0 || pOp->p4.i==1 );
|
|
+ assert( pOp->p3==0 || pOp->p3==1 );
|
|
+ sqlite3VdbeMemSetInt64(&aMem[pOp->p2],
|
|
+ sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3) ^ pOp->p4.i);
|
|
+ break;
|
|
+}
|
|
+
|
|
/* Opcode: Not P1 P2 * * *
|
|
** Synopsis: r[P2]= !r[P1]
|
|
**
|
|
@@ -81173,16 +85237,16 @@
|
|
case OP_Not: { /* same as TK_NOT, in1, out2 */
|
|
pIn1 = &aMem[pOp->p1];
|
|
pOut = &aMem[pOp->p2];
|
|
- sqlite3VdbeMemSetNull(pOut);
|
|
if( (pIn1->flags & MEM_Null)==0 ){
|
|
- pOut->flags = MEM_Int;
|
|
- pOut->u.i = !sqlite3VdbeIntValue(pIn1);
|
|
+ sqlite3VdbeMemSetInt64(pOut, !sqlite3VdbeBooleanValue(pIn1,0));
|
|
+ }else{
|
|
+ sqlite3VdbeMemSetNull(pOut);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Opcode: BitNot P1 P2 * * *
|
|
-** Synopsis: r[P1]= ~r[P1]
|
|
+** Synopsis: r[P2]= ~r[P1]
|
|
**
|
|
** Interpret the content of register P1 as an integer. Store the
|
|
** ones-complement of the P1 value into register P2. If P1 holds
|
|
@@ -81243,6 +85307,14 @@
|
|
** is considered true if it is numeric and non-zero. If the value
|
|
** in P1 is NULL then take the jump if and only if P3 is non-zero.
|
|
*/
|
|
+case OP_If: { /* jump, in1 */
|
|
+ int c;
|
|
+ c = sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3);
|
|
+ VdbeBranchTaken(c!=0, 2);
|
|
+ if( c ) goto jump_to_p2;
|
|
+ break;
|
|
+}
|
|
+
|
|
/* Opcode: IfNot P1 P2 P3 * *
|
|
**
|
|
** Jump to P2 if the value in register P1 is False. The value
|
|
@@ -81249,24 +85321,11 @@
|
|
** is considered false if it has a numeric value of zero. If the value
|
|
** in P1 is NULL then take the jump if and only if P3 is non-zero.
|
|
*/
|
|
-case OP_If: /* jump, in1 */
|
|
case OP_IfNot: { /* jump, in1 */
|
|
int c;
|
|
- pIn1 = &aMem[pOp->p1];
|
|
- if( pIn1->flags & MEM_Null ){
|
|
- c = pOp->p3;
|
|
- }else{
|
|
-#ifdef SQLITE_OMIT_FLOATING_POINT
|
|
- c = sqlite3VdbeIntValue(pIn1)!=0;
|
|
-#else
|
|
- c = sqlite3VdbeRealValue(pIn1)!=0.0;
|
|
-#endif
|
|
- if( pOp->opcode==OP_IfNot ) c = !c;
|
|
- }
|
|
+ c = !sqlite3VdbeBooleanValue(&aMem[pOp->p1], !pOp->p3);
|
|
VdbeBranchTaken(c!=0, 2);
|
|
- if( c ){
|
|
- goto jump_to_p2;
|
|
- }
|
|
+ if( c ) goto jump_to_p2;
|
|
break;
|
|
}
|
|
|
|
@@ -81316,6 +85375,36 @@
|
|
break;
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
|
+/* Opcode: Offset P1 P2 P3 * *
|
|
+** Synopsis: r[P3] = sqlite_offset(P1)
|
|
+**
|
|
+** Store in register r[P3] the byte offset into the database file that is the
|
|
+** start of the payload for the record at which that cursor P1 is currently
|
|
+** pointing.
|
|
+**
|
|
+** P2 is the column number for the argument to the sqlite_offset() function.
|
|
+** This opcode does not use P2 itself, but the P2 value is used by the
|
|
+** code generator. The P1, P2, and P3 operands to this opcode are the
|
|
+** same as for OP_Column.
|
|
+**
|
|
+** This opcode is only available if SQLite is compiled with the
|
|
+** -DSQLITE_ENABLE_OFFSET_SQL_FUNC option.
|
|
+*/
|
|
+case OP_Offset: { /* out3 */
|
|
+ VdbeCursor *pC; /* The VDBE cursor */
|
|
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
+ pC = p->apCsr[pOp->p1];
|
|
+ pOut = &p->aMem[pOp->p3];
|
|
+ if( NEVER(pC==0) || pC->eCurType!=CURTYPE_BTREE ){
|
|
+ sqlite3VdbeMemSetNull(pOut);
|
|
+ }else{
|
|
+ sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor));
|
|
+ }
|
|
+ break;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */
|
|
+
|
|
/* Opcode: Column P1 P2 P3 P4 P5
|
|
** Synopsis: r[P3]=PX
|
|
**
|
|
@@ -81353,9 +85442,7 @@
|
|
const u8 *zData; /* Part of the record being decoded */
|
|
const u8 *zHdr; /* Next unparsed byte of the header */
|
|
const u8 *zEndHdr; /* Pointer to first byte after the header */
|
|
- u32 offset; /* Offset into the data */
|
|
u64 offset64; /* 64-bit offset */
|
|
- u32 avail; /* Number of bytes of available data */
|
|
u32 t; /* A type code from the record header */
|
|
Mem *pReg; /* PseudoTable input register */
|
|
|
|
@@ -81382,11 +85469,13 @@
|
|
if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/
|
|
if( pC->nullRow ){
|
|
if( pC->eCurType==CURTYPE_PSEUDO ){
|
|
- assert( pC->uc.pseudoTableReg>0 );
|
|
- pReg = &aMem[pC->uc.pseudoTableReg];
|
|
+ /* For the special case of as pseudo-cursor, the seekResult field
|
|
+ ** identifies the register that holds the record */
|
|
+ assert( pC->seekResult>0 );
|
|
+ pReg = &aMem[pC->seekResult];
|
|
assert( pReg->flags & MEM_Blob );
|
|
assert( memIsValid(pReg) );
|
|
- pC->payloadSize = pC->szRow = avail = pReg->n;
|
|
+ pC->payloadSize = pC->szRow = pReg->n;
|
|
pC->aRow = (u8*)pReg->z;
|
|
}else{
|
|
sqlite3VdbeMemSetNull(pDest);
|
|
@@ -81398,23 +85487,19 @@
|
|
assert( pCrsr );
|
|
assert( sqlite3BtreeCursorIsValid(pCrsr) );
|
|
pC->payloadSize = sqlite3BtreePayloadSize(pCrsr);
|
|
- pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &avail);
|
|
- assert( avail<=65536 ); /* Maximum page size is 64KiB */
|
|
- if( pC->payloadSize <= (u32)avail ){
|
|
- pC->szRow = pC->payloadSize;
|
|
- }else if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
+ pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &pC->szRow);
|
|
+ assert( pC->szRow<=pC->payloadSize );
|
|
+ assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */
|
|
+ if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
goto too_big;
|
|
- }else{
|
|
- pC->szRow = avail;
|
|
}
|
|
}
|
|
pC->cacheStatus = p->cacheCtr;
|
|
- pC->iHdrOffset = getVarint32(pC->aRow, offset);
|
|
+ pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]);
|
|
pC->nHdrParsed = 0;
|
|
- aOffset[0] = offset;
|
|
|
|
|
|
- if( avail<offset ){ /*OPTIMIZATION-IF-FALSE*/
|
|
+ if( pC->szRow<aOffset[0] ){ /*OPTIMIZATION-IF-FALSE*/
|
|
/* pC->aRow does not have to hold the entire row, but it does at least
|
|
** need to cover the header of the record. If pC->aRow does not contain
|
|
** the complete header, then set it to zero, forcing the header to be
|
|
@@ -81431,17 +85516,26 @@
|
|
** 3-byte type for each of the maximum of 32768 columns plus three
|
|
** extra bytes for the header length itself. 32768*3 + 3 = 98307.
|
|
*/
|
|
- if( offset > 98307 || offset > pC->payloadSize ){
|
|
- rc = SQLITE_CORRUPT_BKPT;
|
|
- goto abort_due_to_error;
|
|
+ if( aOffset[0] > 98307 || aOffset[0] > pC->payloadSize ){
|
|
+ goto op_column_corrupt;
|
|
}
|
|
- }else if( offset>0 ){ /*OPTIMIZATION-IF-TRUE*/
|
|
- /* The following goto is an optimization. It can be omitted and
|
|
- ** everything will still work. But OP_Column is measurably faster
|
|
- ** by skipping the subsequent conditional, which is always true.
|
|
+ }else{
|
|
+ /* This is an optimization. By skipping over the first few tests
|
|
+ ** (ex: pC->nHdrParsed<=p2) in the next section, we achieve a
|
|
+ ** measurable performance gain.
|
|
+ **
|
|
+ ** This branch is taken even if aOffset[0]==0. Such a record is never
|
|
+ ** generated by SQLite, and could be considered corruption, but we
|
|
+ ** accept it for historical reasons. When aOffset[0]==0, the code this
|
|
+ ** branch jumps to reads past the end of the record, but never more
|
|
+ ** than a few bytes. Even if the record occurs at the end of the page
|
|
+ ** content area, the "page header" comes after the page content and so
|
|
+ ** this overread is harmless. Similar overreads can occur for a corrupt
|
|
+ ** database file.
|
|
*/
|
|
zData = pC->aRow;
|
|
assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
|
|
+ testcase( aOffset[0]==0 );
|
|
goto op_column_read_header;
|
|
}
|
|
}
|
|
@@ -81470,6 +85564,7 @@
|
|
offset64 = aOffset[i];
|
|
zHdr = zData + pC->iHdrOffset;
|
|
zEndHdr = zData + aOffset[0];
|
|
+ testcase( zHdr>=zEndHdr );
|
|
do{
|
|
if( (t = zHdr[0])<0x80 ){
|
|
zHdr++;
|
|
@@ -81490,9 +85585,13 @@
|
|
if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize))
|
|
|| (offset64 > pC->payloadSize)
|
|
){
|
|
- if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
|
|
- rc = SQLITE_CORRUPT_BKPT;
|
|
- goto abort_due_to_error;
|
|
+ if( aOffset[0]==0 ){
|
|
+ i = 0;
|
|
+ zHdr = zEndHdr;
|
|
+ }else{
|
|
+ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
|
|
+ goto op_column_corrupt;
|
|
+ }
|
|
}
|
|
|
|
pC->nHdrParsed = i;
|
|
@@ -81586,6 +85685,15 @@
|
|
UPDATE_MAX_BLOBSIZE(pDest);
|
|
REGISTER_TRACE(pOp->p3, pDest);
|
|
break;
|
|
+
|
|
+op_column_corrupt:
|
|
+ if( aOp[0].p3>0 ){
|
|
+ pOp = &aOp[aOp[0].p3-1];
|
|
+ break;
|
|
+ }else{
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ goto abort_due_to_error;
|
|
+ }
|
|
}
|
|
|
|
/* Opcode: Affinity P1 P2 * P4 *
|
|
@@ -81710,9 +85818,18 @@
|
|
pRec = pLast;
|
|
do{
|
|
assert( memIsValid(pRec) );
|
|
- pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
|
|
+ serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
|
|
if( pRec->flags & MEM_Zero ){
|
|
- if( nData ){
|
|
+ if( serial_type==0 ){
|
|
+ /* Values with MEM_Null and MEM_Zero are created by xColumn virtual
|
|
+ ** table methods that never invoke sqlite3_result_xxxxx() while
|
|
+ ** computing an unchanging column value in an UPDATE statement.
|
|
+ ** Give such values a special internal-use-only serial-type of 10
|
|
+ ** so that they can be passed through to xUpdate and have
|
|
+ ** a true sqlite3_value_nochange(). */
|
|
+ assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB );
|
|
+ serial_type = 10;
|
|
+ }else if( nData ){
|
|
if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
|
|
}else{
|
|
nZero += pRec->u.nZero;
|
|
@@ -81723,6 +85840,7 @@
|
|
testcase( serial_type==127 );
|
|
testcase( serial_type==128 );
|
|
nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
|
|
+ pRec->uTemp = serial_type;
|
|
if( pRec==pData0 ) break;
|
|
pRec--;
|
|
}while(1);
|
|
@@ -81743,9 +85861,6 @@
|
|
if( nVarint<sqlite3VarintLen(nHdr) ) nHdr++;
|
|
}
|
|
nByte = nHdr+nData;
|
|
- if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
- goto too_big;
|
|
- }
|
|
|
|
/* Make sure the output register has a buffer large enough to store
|
|
** the new record. The output register (pOp->p3) is not allowed to
|
|
@@ -81752,8 +85867,19 @@
|
|
** be one of the input registers (because the following call to
|
|
** sqlite3VdbeMemClearAndResize() could clobber the value before it is used).
|
|
*/
|
|
- if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){
|
|
- goto no_mem;
|
|
+ if( nByte+nZero<=pOut->szMalloc ){
|
|
+ /* The output register is already large enough to hold the record.
|
|
+ ** No error checks or buffer enlargement is required */
|
|
+ pOut->z = pOut->zMalloc;
|
|
+ }else{
|
|
+ /* Need to make sure that the output is not too big and then enlarge
|
|
+ ** the output register to hold the full result */
|
|
+ if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
+ goto too_big;
|
|
+ }
|
|
+ if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){
|
|
+ goto no_mem;
|
|
+ }
|
|
}
|
|
zNewRecord = (u8 *)pOut->z;
|
|
|
|
@@ -81926,7 +86052,7 @@
|
|
int isSchemaChange;
|
|
iSavepoint = db->nSavepoint - iSavepoint - 1;
|
|
if( p1==SAVEPOINT_ROLLBACK ){
|
|
- isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
|
|
+ isSchemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0;
|
|
for(ii=0; ii<db->nDb; ii++){
|
|
rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
|
|
SQLITE_ABORT_ROLLBACK,
|
|
@@ -81943,9 +86069,9 @@
|
|
}
|
|
}
|
|
if( isSchemaChange ){
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
sqlite3ResetAllSchemasOfConnection(db);
|
|
- db->flags = (db->flags | SQLITE_InternChanges);
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
}
|
|
}
|
|
|
|
@@ -82085,8 +86211,7 @@
|
|
*/
|
|
case OP_Transaction: {
|
|
Btree *pBt;
|
|
- int iMeta;
|
|
- int iGen;
|
|
+ int iMeta = 0;
|
|
|
|
assert( p->bIsReader );
|
|
assert( p->readOnly==0 || pOp->p2==0 );
|
|
@@ -82099,7 +86224,7 @@
|
|
pBt = db->aDb[pOp->p1].pBt;
|
|
|
|
if( pBt ){
|
|
- rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
|
|
+ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta);
|
|
testcase( rc==SQLITE_BUSY_SNAPSHOT );
|
|
testcase( rc==SQLITE_BUSY_RECOVERY );
|
|
if( rc!=SQLITE_OK ){
|
|
@@ -82132,19 +86257,17 @@
|
|
p->nStmtDefCons = db->nDeferredCons;
|
|
p->nStmtDefImmCons = db->nDeferredImmCons;
|
|
}
|
|
-
|
|
- /* Gather the schema version number for checking:
|
|
+ }
|
|
+ assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
|
|
+ if( pOp->p5
|
|
+ && (iMeta!=pOp->p3
|
|
+ || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i)
|
|
+ ){
|
|
+ /*
|
|
** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
|
|
** version is checked to ensure that the schema has not changed since the
|
|
** SQL statement was prepared.
|
|
*/
|
|
- sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
|
|
- iGen = db->aDb[pOp->p1].pSchema->iGeneration;
|
|
- }else{
|
|
- iGen = iMeta = 0;
|
|
- }
|
|
- assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
|
|
- if( pOp->p5 && (iMeta!=pOp->p3 || iGen!=pOp->p4.i) ){
|
|
sqlite3DbFree(db, p->zErrMsg);
|
|
p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
|
|
/* If the schema-cookie from the database file matches the cookie
|
|
@@ -82213,6 +86336,8 @@
|
|
*/
|
|
case OP_SetCookie: {
|
|
Db *pDb;
|
|
+
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
assert( pOp->p2<SQLITE_N_BTREE_META );
|
|
assert( pOp->p1>=0 && pOp->p1<db->nDb );
|
|
assert( DbMaskTest(p->btreeMask, pOp->p1) );
|
|
@@ -82225,7 +86350,7 @@
|
|
if( pOp->p2==BTREE_SCHEMA_VERSION ){
|
|
/* When the schema cookie changes, record the new cookie internally */
|
|
pDb->pSchema->schema_cookie = pOp->p3;
|
|
- db->flags |= SQLITE_InternChanges;
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
}else if( pOp->p2==BTREE_FILE_FORMAT ){
|
|
/* Record changes in the file format */
|
|
pDb->pSchema->file_format = pOp->p3;
|
|
@@ -82233,7 +86358,7 @@
|
|
if( pOp->p1==1 ){
|
|
/* Invalidate all prepared statements whenever the TEMP database
|
|
** schema is changed. Ticket #1644 */
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
p->expired = 0;
|
|
}
|
|
if( rc ) goto abort_due_to_error;
|
|
@@ -82251,23 +86376,20 @@
|
|
** values need not be contiguous but all P1 values should be small integers.
|
|
** It is an error for P1 to be negative.
|
|
**
|
|
-** If P5!=0 then use the content of register P2 as the root page, not
|
|
-** the value of P2 itself.
|
|
+** Allowed P5 bits:
|
|
+** <ul>
|
|
+** <li> <b>0x02 OPFLAG_SEEKEQ</b>: This cursor will only be used for
|
|
+** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT
|
|
+** of OP_SeekLE/OP_IdxGT)
|
|
+** </ul>
|
|
**
|
|
-** There will be a read lock on the database whenever there is an
|
|
-** open cursor. If the database was unlocked prior to this instruction
|
|
-** then a read lock is acquired as part of this instruction. A read
|
|
-** lock allows other processes to read the database but prohibits
|
|
-** any other process from modifying the database. The read lock is
|
|
-** released when all cursors are closed. If this instruction attempts
|
|
-** to get a read lock but fails, the script terminates with an
|
|
-** SQLITE_BUSY error code.
|
|
-**
|
|
** The P4 value may be either an integer (P4_INT32) or a pointer to
|
|
** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
|
|
-** structure, then said structure defines the content and collating
|
|
-** sequence of the index being opened. Otherwise, if P4 is an integer
|
|
-** value, it is set to the number of columns in the table.
|
|
+** object, then table being opened must be an [index b-tree] where the
|
|
+** KeyInfo object defines the content and collating
|
|
+** sequence of that index b-tree. Otherwise, if P4 is an integer
|
|
+** value, then the table being opened must be a [table b-tree] with a
|
|
+** number of columns no less than the value of P4.
|
|
**
|
|
** See also: OpenWrite, ReopenIdx
|
|
*/
|
|
@@ -82274,36 +86396,58 @@
|
|
/* Opcode: ReopenIdx P1 P2 P3 P4 P5
|
|
** Synopsis: root=P2 iDb=P3
|
|
**
|
|
-** The ReopenIdx opcode works exactly like ReadOpen except that it first
|
|
-** checks to see if the cursor on P1 is already open with a root page
|
|
-** number of P2 and if it is this opcode becomes a no-op. In other words,
|
|
+** The ReopenIdx opcode works like OP_OpenRead except that it first
|
|
+** checks to see if the cursor on P1 is already open on the same
|
|
+** b-tree and if it is this opcode becomes a no-op. In other words,
|
|
** if the cursor is already open, do not reopen it.
|
|
**
|
|
-** The ReopenIdx opcode may only be used with P5==0 and with P4 being
|
|
-** a P4_KEYINFO object. Furthermore, the P3 value must be the same as
|
|
-** every other ReopenIdx or OpenRead for the same cursor number.
|
|
+** The ReopenIdx opcode may only be used with P5==0 or P5==OPFLAG_SEEKEQ
|
|
+** and with P4 being a P4_KEYINFO object. Furthermore, the P3 value must
|
|
+** be the same as every other ReopenIdx or OpenRead for the same cursor
|
|
+** number.
|
|
**
|
|
-** See the OpenRead opcode documentation for additional information.
|
|
+** Allowed P5 bits:
|
|
+** <ul>
|
|
+** <li> <b>0x02 OPFLAG_SEEKEQ</b>: This cursor will only be used for
|
|
+** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT
|
|
+** of OP_SeekLE/OP_IdxGT)
|
|
+** </ul>
|
|
+**
|
|
+** See also: OP_OpenRead, OP_OpenWrite
|
|
*/
|
|
/* Opcode: OpenWrite P1 P2 P3 P4 P5
|
|
** Synopsis: root=P2 iDb=P3
|
|
**
|
|
** Open a read/write cursor named P1 on the table or index whose root
|
|
-** page is P2. Or if P5!=0 use the content of register P2 to find the
|
|
-** root page.
|
|
+** page is P2 (or whose root page is held in register P2 if the
|
|
+** OPFLAG_P2ISREG bit is set in P5 - see below).
|
|
**
|
|
** The P4 value may be either an integer (P4_INT32) or a pointer to
|
|
** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
|
|
-** structure, then said structure defines the content and collating
|
|
-** sequence of the index being opened. Otherwise, if P4 is an integer
|
|
-** value, it is set to the number of columns in the table, or to the
|
|
-** largest index of any column of the table that is actually used.
|
|
+** object, then table being opened must be an [index b-tree] where the
|
|
+** KeyInfo object defines the content and collating
|
|
+** sequence of that index b-tree. Otherwise, if P4 is an integer
|
|
+** value, then the table being opened must be a [table b-tree] with a
|
|
+** number of columns no less than the value of P4.
|
|
**
|
|
-** This instruction works just like OpenRead except that it opens the cursor
|
|
-** in read/write mode. For a given table, there can be one or more read-only
|
|
-** cursors or a single read/write cursor but not both.
|
|
+** Allowed P5 bits:
|
|
+** <ul>
|
|
+** <li> <b>0x02 OPFLAG_SEEKEQ</b>: This cursor will only be used for
|
|
+** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT
|
|
+** of OP_SeekLE/OP_IdxGT)
|
|
+** <li> <b>0x08 OPFLAG_FORDELETE</b>: This cursor is used only to seek
|
|
+** and subsequently delete entries in an index btree. This is a
|
|
+** hint to the storage engine that the storage engine is allowed to
|
|
+** ignore. The hint is not used by the official SQLite b*tree storage
|
|
+** engine, but is used by COMDB2.
|
|
+** <li> <b>0x10 OPFLAG_P2ISREG</b>: Use the content of register P2
|
|
+** as the root page, not the value of P2 itself.
|
|
+** </ul>
|
|
**
|
|
-** See also OpenRead.
|
|
+** This instruction works like OpenRead except that it opens the cursor
|
|
+** in read/write mode.
|
|
+**
|
|
+** See also: OP_OpenRead, OP_ReopenIdx
|
|
*/
|
|
case OP_ReopenIdx: {
|
|
int nField;
|
|
@@ -82332,7 +86476,7 @@
|
|
assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
|
|
|| p->readOnly==0 );
|
|
|
|
- if( p->expired ){
|
|
+ if( p->expired==1 ){
|
|
rc = SQLITE_ABORT_ROLLBACK;
|
|
goto abort_due_to_error;
|
|
}
|
|
@@ -82359,12 +86503,13 @@
|
|
if( pOp->p5 & OPFLAG_P2ISREG ){
|
|
assert( p2>0 );
|
|
assert( p2<=(p->nMem+1 - p->nCursor) );
|
|
+ assert( pOp->opcode==OP_OpenWrite );
|
|
pIn2 = &aMem[p2];
|
|
assert( memIsValid(pIn2) );
|
|
assert( (pIn2->flags & MEM_Int)!=0 );
|
|
sqlite3VdbeMemIntegerify(pIn2);
|
|
p2 = (int)pIn2->u.i;
|
|
- /* The p2 value always comes from a prior OP_CreateTable opcode and
|
|
+ /* The p2 value always comes from a prior OP_CreateBtree opcode and
|
|
** that opcode will always set the p2 value to 2 or more or else fail.
|
|
** If there were a failure, the prepared statement would have halted
|
|
** before reaching this instruction. */
|
|
@@ -82374,7 +86519,7 @@
|
|
pKeyInfo = pOp->p4.pKeyInfo;
|
|
assert( pKeyInfo->enc==ENC(db) );
|
|
assert( pKeyInfo->db==db );
|
|
- nField = pKeyInfo->nField+pKeyInfo->nXField;
|
|
+ nField = pKeyInfo->nAllField;
|
|
}else if( pOp->p4type==P4_INT32 ){
|
|
nField = pOp->p4.i;
|
|
}
|
|
@@ -82487,7 +86632,7 @@
|
|
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx,
|
|
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1);
|
|
+ rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0);
|
|
}
|
|
if( rc==SQLITE_OK ){
|
|
/* If a transient index is required, create it by calling
|
|
@@ -82585,8 +86730,13 @@
|
|
pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO);
|
|
if( pCx==0 ) goto no_mem;
|
|
pCx->nullRow = 1;
|
|
- pCx->uc.pseudoTableReg = pOp->p2;
|
|
+ pCx->seekResult = pOp->p2;
|
|
pCx->isTable = 1;
|
|
+ /* Give this pseudo-cursor a fake BtCursor pointer so that pCx
|
|
+ ** can be safely passed to sqlite3VdbeCursorMoveto(). This avoids a test
|
|
+ ** for pCx->eCurType==CURTYPE_BTREE inside of sqlite3VdbeCursorMoveto()
|
|
+ ** which is a performance optimization */
|
|
+ pCx->uc.pCursor = sqlite3BtreeFakeValidCursor();
|
|
assert( pOp->p5==0 );
|
|
break;
|
|
}
|
|
@@ -82709,10 +86859,10 @@
|
|
**
|
|
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
|
|
*/
|
|
-case OP_SeekLT: /* jump, in3 */
|
|
-case OP_SeekLE: /* jump, in3 */
|
|
-case OP_SeekGE: /* jump, in3 */
|
|
-case OP_SeekGT: { /* jump, in3 */
|
|
+case OP_SeekLT: /* jump, in3, group */
|
|
+case OP_SeekLE: /* jump, in3, group */
|
|
+case OP_SeekGE: /* jump, in3, group */
|
|
+case OP_SeekGT: { /* jump, in3, group */
|
|
int res; /* Comparison result */
|
|
int oc; /* Opcode */
|
|
VdbeCursor *pC; /* The cursor to seek */
|
|
@@ -82890,6 +87040,25 @@
|
|
break;
|
|
}
|
|
|
|
+/* Opcode: SeekHit P1 P2 * * *
|
|
+** Synopsis: seekHit=P2
|
|
+**
|
|
+** Set the seekHit flag on cursor P1 to the value in P2.
|
|
+** The seekHit flag is used by the IfNoHope opcode.
|
|
+**
|
|
+** P1 must be a valid b-tree cursor. P2 must be a boolean value,
|
|
+** either 0 or 1.
|
|
+*/
|
|
+case OP_SeekHit: {
|
|
+ VdbeCursor *pC;
|
|
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
+ pC = p->apCsr[pOp->p1];
|
|
+ assert( pC!=0 );
|
|
+ assert( pOp->p2==0 || pOp->p2==1 );
|
|
+ pC->seekHit = pOp->p2 & 1;
|
|
+ break;
|
|
+}
|
|
+
|
|
/* Opcode: Found P1 P2 P3 P4 *
|
|
** Synopsis: key=r[P3@P4]
|
|
**
|
|
@@ -82924,8 +87093,35 @@
|
|
** advanced in either direction. In other words, the Next and Prev
|
|
** opcodes do not work after this operation.
|
|
**
|
|
-** See also: Found, NotExists, NoConflict
|
|
+** See also: Found, NotExists, NoConflict, IfNoHope
|
|
*/
|
|
+/* Opcode: IfNoHope P1 P2 P3 P4 *
|
|
+** Synopsis: key=r[P3@P4]
|
|
+**
|
|
+** Register P3 is the first of P4 registers that form an unpacked
|
|
+** record.
|
|
+**
|
|
+** Cursor P1 is on an index btree. If the seekHit flag is set on P1, then
|
|
+** this opcode is a no-op. But if the seekHit flag of P1 is clear, then
|
|
+** check to see if there is any entry in P1 that matches the
|
|
+** prefix identified by P3 and P4. If no entry matches the prefix,
|
|
+** jump to P2. Otherwise fall through.
|
|
+**
|
|
+** This opcode behaves like OP_NotFound if the seekHit
|
|
+** flag is clear and it behaves like OP_Noop if the seekHit flag is set.
|
|
+**
|
|
+** This opcode is used in IN clause processing for a multi-column key.
|
|
+** If an IN clause is attached to an element of the key other than the
|
|
+** left-most element, and if there are no matches on the most recent
|
|
+** seek over the whole key, then it might be that one of the key element
|
|
+** to the left is prohibiting a match, and hence there is "no hope" of
|
|
+** any match regardless of how many IN clause elements are checked.
|
|
+** In such a case, we abandon the IN clause search early, using this
|
|
+** opcode. The opcode name comes from the fact that the
|
|
+** jump is taken if there is "no hope" of achieving a match.
|
|
+**
|
|
+** See also: NotFound, SeekHit
|
|
+*/
|
|
/* Opcode: NoConflict P1 P2 P3 P4 *
|
|
** Synopsis: key=r[P3@P4]
|
|
**
|
|
@@ -82949,6 +87145,14 @@
|
|
**
|
|
** See also: NotFound, Found, NotExists
|
|
*/
|
|
+case OP_IfNoHope: { /* jump, in3 */
|
|
+ VdbeCursor *pC;
|
|
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
+ pC = p->apCsr[pOp->p1];
|
|
+ assert( pC!=0 );
|
|
+ if( pC->seekHit ) break;
|
|
+ /* Fall through into OP_NotFound */
|
|
+}
|
|
case OP_NoConflict: /* jump, in3 */
|
|
case OP_NotFound: /* jump, in3 */
|
|
case OP_Found: { /* jump, in3 */
|
|
@@ -83086,18 +87290,26 @@
|
|
|
|
pIn3 = &aMem[pOp->p3];
|
|
if( (pIn3->flags & MEM_Int)==0 ){
|
|
+ /* Make sure pIn3->u.i contains a valid integer representation of
|
|
+ ** the key value, but do not change the datatype of the register, as
|
|
+ ** other parts of the perpared statement might be depending on the
|
|
+ ** current datatype. */
|
|
+ u16 origFlags = pIn3->flags;
|
|
+ int isNotInt;
|
|
applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding);
|
|
- if( (pIn3->flags & MEM_Int)==0 ) goto jump_to_p2;
|
|
+ isNotInt = (pIn3->flags & MEM_Int)==0;
|
|
+ pIn3->flags = origFlags;
|
|
+ if( isNotInt ) goto jump_to_p2;
|
|
}
|
|
/* Fall through into OP_NotExists */
|
|
case OP_NotExists: /* jump, in3 */
|
|
pIn3 = &aMem[pOp->p3];
|
|
- assert( pIn3->flags & MEM_Int );
|
|
+ assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
|
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
pC = p->apCsr[pOp->p1];
|
|
assert( pC!=0 );
|
|
#ifdef SQLITE_DEBUG
|
|
- pC->seekOp = 0;
|
|
+ pC->seekOp = OP_SeekRowid;
|
|
#endif
|
|
assert( pC->isTable );
|
|
assert( pC->eCurType==CURTYPE_BTREE );
|
|
@@ -83172,6 +87384,7 @@
|
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
pC = p->apCsr[pOp->p1];
|
|
assert( pC!=0 );
|
|
+ assert( pC->isTable );
|
|
assert( pC->eCurType==CURTYPE_BTREE );
|
|
assert( pC->uc.pCursor!=0 );
|
|
{
|
|
@@ -83328,10 +87541,8 @@
|
|
int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
|
|
const char *zDb; /* database name - used by the update hook */
|
|
Table *pTab; /* Table structure - used by update and pre-update hooks */
|
|
- int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
|
|
BtreePayload x; /* Payload to be inserted */
|
|
|
|
- op = 0;
|
|
pData = &aMem[pOp->p2];
|
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
assert( memIsValid(pData) );
|
|
@@ -83342,6 +87553,7 @@
|
|
assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
|
|
assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
|
|
REGISTER_TRACE(pOp->p2, pData);
|
|
+ sqlite3VdbeIncrWriteCounter(p, pC);
|
|
|
|
if( pOp->opcode==OP_Insert ){
|
|
pKey = &aMem[pOp->p3];
|
|
@@ -83359,19 +87571,21 @@
|
|
zDb = db->aDb[pC->iDb].zDbSName;
|
|
pTab = pOp->p4.pTab;
|
|
assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
|
|
- op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
|
|
}else{
|
|
- pTab = 0; /* Not needed. Silence a compiler warning. */
|
|
+ pTab = 0;
|
|
zDb = 0; /* Not needed. Silence a compiler warning. */
|
|
}
|
|
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
/* Invoke the pre-update hook, if any */
|
|
- if( db->xPreUpdateCallback
|
|
- && pOp->p4type==P4_TABLE
|
|
- && !(pOp->p5 & OPFLAG_ISUPDATE)
|
|
- ){
|
|
- sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2);
|
|
+ if( pTab ){
|
|
+ if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){
|
|
+ sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2);
|
|
+ }
|
|
+ if( db->xUpdateCallback==0 || pTab->aCol==0 ){
|
|
+ /* Prevent post-update hook from running in cases when it should not */
|
|
+ pTab = 0;
|
|
+ }
|
|
}
|
|
if( pOp->p5 & OPFLAG_ISNOOP ) break;
|
|
#endif
|
|
@@ -83378,14 +87592,9 @@
|
|
|
|
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
|
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
|
|
- if( pData->flags & MEM_Null ){
|
|
- x.pData = 0;
|
|
- x.nData = 0;
|
|
- }else{
|
|
- assert( pData->flags & (MEM_Blob|MEM_Str) );
|
|
- x.pData = pData->z;
|
|
- x.nData = pData->n;
|
|
- }
|
|
+ assert( pData->flags & (MEM_Blob|MEM_Str) );
|
|
+ x.pData = pData->z;
|
|
+ x.nData = pData->n;
|
|
seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
|
|
if( pData->flags & MEM_Zero ){
|
|
x.nZero = pData->u.nZero;
|
|
@@ -83401,8 +87610,12 @@
|
|
|
|
/* Invoke the update-hook if required. */
|
|
if( rc ) goto abort_due_to_error;
|
|
- if( db->xUpdateCallback && op ){
|
|
- db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, x.nKey);
|
|
+ if( pTab ){
|
|
+ assert( db->xUpdateCallback!=0 );
|
|
+ assert( pTab->aCol!=0 );
|
|
+ db->xUpdateCallback(db->pUpdateArg,
|
|
+ (pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT,
|
|
+ zDb, pTab->zName, x.nKey);
|
|
}
|
|
break;
|
|
}
|
|
@@ -83455,6 +87668,7 @@
|
|
assert( pC->eCurType==CURTYPE_BTREE );
|
|
assert( pC->uc.pCursor!=0 );
|
|
assert( pC->deferredMoveto==0 );
|
|
+ sqlite3VdbeIncrWriteCounter(p, pC);
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) && pOp->p5==0 ){
|
|
@@ -83623,10 +87837,10 @@
|
|
** If the P1 cursor must be pointing to a valid row (not a NULL row)
|
|
** of a real table, not a pseudo-table.
|
|
**
|
|
-** If P3!=0 then this opcode is allowed to make an ephermeral pointer
|
|
+** If P3!=0 then this opcode is allowed to make an ephemeral pointer
|
|
** into the database page. That means that the content of the output
|
|
** register will be invalidated as soon as the cursor moves - including
|
|
-** moves caused by other cursors that "save" the the current cursors
|
|
+** moves caused by other cursors that "save" the current cursors
|
|
** position in order that they can write to the same table. If P3==0
|
|
** then a copy of the data is made into memory. P3!=0 is faster, but
|
|
** P3==0 is safer.
|
|
@@ -83749,11 +87963,24 @@
|
|
assert( pC->uc.pCursor!=0 );
|
|
sqlite3BtreeClearCursor(pC->uc.pCursor);
|
|
}
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( pC->seekOp==0 ) pC->seekOp = OP_NullRow;
|
|
+#endif
|
|
break;
|
|
}
|
|
|
|
-/* Opcode: Last P1 P2 P3 * *
|
|
+/* Opcode: SeekEnd P1 * * * *
|
|
**
|
|
+** Position cursor P1 at the end of the btree for the purpose of
|
|
+** appending a new entry onto the btree.
|
|
+**
|
|
+** It is assumed that the cursor is used only for appending and so
|
|
+** if the cursor is valid, then the cursor must already be pointing
|
|
+** at the end of the btree and so no changes are made to
|
|
+** the cursor.
|
|
+*/
|
|
+/* Opcode: Last P1 P2 * * *
|
|
+**
|
|
** The next use of the Rowid or Column or Prev instruction for P1
|
|
** will refer to the last entry in the database table or index.
|
|
** If the table or index is empty and P2>0, then jump immediately to P2.
|
|
@@ -83763,14 +87990,8 @@
|
|
** This opcode leaves the cursor configured to move in reverse order,
|
|
** from the end toward the beginning. In other words, the cursor is
|
|
** configured to use Prev, not Next.
|
|
-**
|
|
-** If P3 is -1, then the cursor is positioned at the end of the btree
|
|
-** for the purpose of appending a new entry onto the btree. In that
|
|
-** case P2 must be 0. It is assumed that the cursor is used only for
|
|
-** appending and so if the cursor is valid, then the cursor must already
|
|
-** be pointing at the end of the btree and so no changes are made to
|
|
-** the cursor.
|
|
*/
|
|
+case OP_SeekEnd:
|
|
case OP_Last: { /* jump */
|
|
VdbeCursor *pC;
|
|
BtCursor *pCrsr;
|
|
@@ -83783,23 +88004,25 @@
|
|
pCrsr = pC->uc.pCursor;
|
|
res = 0;
|
|
assert( pCrsr!=0 );
|
|
- pC->seekResult = pOp->p3;
|
|
#ifdef SQLITE_DEBUG
|
|
- pC->seekOp = OP_Last;
|
|
+ pC->seekOp = pOp->opcode;
|
|
#endif
|
|
- if( pOp->p3==0 || !sqlite3BtreeCursorIsValidNN(pCrsr) ){
|
|
- rc = sqlite3BtreeLast(pCrsr, &res);
|
|
- pC->nullRow = (u8)res;
|
|
- pC->deferredMoveto = 0;
|
|
- pC->cacheStatus = CACHE_STALE;
|
|
- if( rc ) goto abort_due_to_error;
|
|
- if( pOp->p2>0 ){
|
|
- VdbeBranchTaken(res!=0,2);
|
|
- if( res ) goto jump_to_p2;
|
|
+ if( pOp->opcode==OP_SeekEnd ){
|
|
+ assert( pOp->p2==0 );
|
|
+ pC->seekResult = -1;
|
|
+ if( sqlite3BtreeCursorIsValidNN(pCrsr) ){
|
|
+ break;
|
|
}
|
|
- }else{
|
|
- assert( pOp->p2==0 );
|
|
}
|
|
+ rc = sqlite3BtreeLast(pCrsr, &res);
|
|
+ pC->nullRow = (u8)res;
|
|
+ pC->deferredMoveto = 0;
|
|
+ pC->cacheStatus = CACHE_STALE;
|
|
+ if( rc ) goto abort_due_to_error;
|
|
+ if( pOp->p2>0 ){
|
|
+ VdbeBranchTaken(res!=0,2);
|
|
+ if( res ) goto jump_to_p2;
|
|
+ }
|
|
break;
|
|
}
|
|
|
|
@@ -83861,7 +88084,7 @@
|
|
p->aCounter[SQLITE_STMTSTATUS_SORT]++;
|
|
/* Fall through into OP_Rewind */
|
|
}
|
|
-/* Opcode: Rewind P1 P2 * * *
|
|
+/* Opcode: Rewind P1 P2 * * P5
|
|
**
|
|
** The next use of the Rowid or Column or Next instruction for P1
|
|
** will refer to the first entry in the database table or index.
|
|
@@ -83869,6 +88092,10 @@
|
|
** If the table or index is not empty, fall through to the following
|
|
** instruction.
|
|
**
|
|
+** If P5 is non-zero and the table is not empty, then the "skip-next"
|
|
+** flag is set on the cursor so that the next OP_Next instruction
|
|
+** executed on it is a no-op.
|
|
+**
|
|
** This opcode leaves the cursor configured to move in forward order,
|
|
** from the beginning toward the end. In other words, the cursor is
|
|
** configured to use Next, not Prev.
|
|
@@ -83893,6 +88120,9 @@
|
|
pCrsr = pC->uc.pCursor;
|
|
assert( pCrsr );
|
|
rc = sqlite3BtreeFirst(pCrsr, &res);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr);
|
|
+#endif
|
|
pC->deferredMoveto = 0;
|
|
pC->cacheStatus = CACHE_STALE;
|
|
}
|
|
@@ -83929,13 +88159,8 @@
|
|
** If P5 is positive and the jump is taken, then event counter
|
|
** number P5-1 in the prepared statement is incremented.
|
|
**
|
|
-** See also: Prev, NextIfOpen
|
|
+** See also: Prev
|
|
*/
|
|
-/* Opcode: NextIfOpen P1 P2 P3 P4 P5
|
|
-**
|
|
-** This opcode works just like Next except that if cursor P1 is not
|
|
-** open it behaves a no-op.
|
|
-*/
|
|
/* Opcode: Prev P1 P2 P3 P4 P5
|
|
**
|
|
** Back up cursor P1 so that it points to the previous key/data pair in its
|
|
@@ -83962,11 +88187,6 @@
|
|
** If P5 is positive and the jump is taken, then event counter
|
|
** number P5-1 in the prepared statement is incremented.
|
|
*/
|
|
-/* Opcode: PrevIfOpen P1 P2 P3 P4 P5
|
|
-**
|
|
-** This opcode works just like Prev except that if cursor P1 is not
|
|
-** open it behaves a no-op.
|
|
-*/
|
|
/* Opcode: SorterNext P1 P2 * * P5
|
|
**
|
|
** This opcode works just like OP_Next except that P1 must be a
|
|
@@ -83981,10 +88201,6 @@
|
|
assert( isSorter(pC) );
|
|
rc = sqlite3VdbeSorterNext(db, pC);
|
|
goto next_tail;
|
|
-case OP_PrevIfOpen: /* jump */
|
|
-case OP_NextIfOpen: /* jump */
|
|
- if( p->apCsr[pOp->p1]==0 ) break;
|
|
- /* Fall through */
|
|
case OP_Prev: /* jump */
|
|
case OP_Next: /* jump */
|
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
@@ -83995,17 +88211,17 @@
|
|
assert( pC->eCurType==CURTYPE_BTREE );
|
|
assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
|
|
assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
|
|
- assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext );
|
|
- assert( pOp->opcode!=OP_PrevIfOpen || pOp->p4.xAdvance==sqlite3BtreePrevious);
|
|
|
|
- /* The Next opcode is only used after SeekGT, SeekGE, and Rewind.
|
|
+ /* The Next opcode is only used after SeekGT, SeekGE, Rewind, and Found.
|
|
** The Prev opcode is only used after SeekLT, SeekLE, and Last. */
|
|
- assert( pOp->opcode!=OP_Next || pOp->opcode!=OP_NextIfOpen
|
|
+ assert( pOp->opcode!=OP_Next
|
|
|| pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
|
|
- || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found);
|
|
- assert( pOp->opcode!=OP_Prev || pOp->opcode!=OP_PrevIfOpen
|
|
+ || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
|
|
+ || pC->seekOp==OP_NullRow);
|
|
+ assert( pOp->opcode!=OP_Prev
|
|
|| pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
|
|
- || pC->seekOp==OP_Last );
|
|
+ || pC->seekOp==OP_Last
|
|
+ || pC->seekOp==OP_NullRow);
|
|
|
|
rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3);
|
|
next_tail:
|
|
@@ -84067,6 +88283,7 @@
|
|
|
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
|
pC = p->apCsr[pOp->p1];
|
|
+ sqlite3VdbeIncrWriteCounter(p, pC);
|
|
assert( pC!=0 );
|
|
assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) );
|
|
pIn2 = &aMem[pOp->p2];
|
|
@@ -84113,6 +88330,7 @@
|
|
pC = p->apCsr[pOp->p1];
|
|
assert( pC!=0 );
|
|
assert( pC->eCurType==CURTYPE_BTREE );
|
|
+ sqlite3VdbeIncrWriteCounter(p, pC);
|
|
pCrsr = pC->uc.pCursor;
|
|
assert( pCrsr!=0 );
|
|
assert( pOp->p5==0 );
|
|
@@ -84286,7 +88504,13 @@
|
|
}
|
|
r.aMem = &aMem[pOp->p3];
|
|
#ifdef SQLITE_DEBUG
|
|
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
|
+ {
|
|
+ int i;
|
|
+ for(i=0; i<r.nField; i++){
|
|
+ assert( memIsValid(&r.aMem[i]) );
|
|
+ REGISTER_TRACE(pOp->p3+i, &aMem[pOp->p3+i]);
|
|
+ }
|
|
+ }
|
|
#endif
|
|
res = 0; /* Not needed. Only used to silence a warning. */
|
|
rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
|
|
@@ -84335,6 +88559,7 @@
|
|
int iMoved;
|
|
int iDb;
|
|
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
assert( p->readOnly==0 );
|
|
assert( pOp->p1>1 );
|
|
pOut = out2Prerelease(p, pOp);
|
|
@@ -84384,6 +88609,7 @@
|
|
case OP_Clear: {
|
|
int nChange;
|
|
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
nChange = 0;
|
|
assert( p->readOnly==0 );
|
|
assert( DbMaskTest(p->btreeMask, pOp->p2) );
|
|
@@ -84427,50 +88653,29 @@
|
|
break;
|
|
}
|
|
|
|
-/* Opcode: CreateTable P1 P2 * * *
|
|
-** Synopsis: r[P2]=root iDb=P1
|
|
+/* Opcode: CreateBtree P1 P2 P3 * *
|
|
+** Synopsis: r[P2]=root iDb=P1 flags=P3
|
|
**
|
|
-** Allocate a new table in the main database file if P1==0 or in the
|
|
-** auxiliary database file if P1==1 or in an attached database if
|
|
-** P1>1. Write the root page number of the new table into
|
|
-** register P2
|
|
-**
|
|
-** The difference between a table and an index is this: A table must
|
|
-** have a 4-byte integer key and can have arbitrary data. An index
|
|
-** has an arbitrary key but no data.
|
|
-**
|
|
-** See also: CreateIndex
|
|
+** Allocate a new b-tree in the main database file if P1==0 or in the
|
|
+** TEMP database file if P1==1 or in an attached database if
|
|
+** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table
|
|
+** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table.
|
|
+** The root page number of the new b-tree is stored in register P2.
|
|
*/
|
|
-/* Opcode: CreateIndex P1 P2 * * *
|
|
-** Synopsis: r[P2]=root iDb=P1
|
|
-**
|
|
-** Allocate a new index in the main database file if P1==0 or in the
|
|
-** auxiliary database file if P1==1 or in an attached database if
|
|
-** P1>1. Write the root page number of the new table into
|
|
-** register P2.
|
|
-**
|
|
-** See documentation on OP_CreateTable for additional information.
|
|
-*/
|
|
-case OP_CreateIndex: /* out2 */
|
|
-case OP_CreateTable: { /* out2 */
|
|
+case OP_CreateBtree: { /* out2 */
|
|
int pgno;
|
|
- int flags;
|
|
Db *pDb;
|
|
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
pOut = out2Prerelease(p, pOp);
|
|
pgno = 0;
|
|
+ assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY );
|
|
assert( pOp->p1>=0 && pOp->p1<db->nDb );
|
|
assert( DbMaskTest(p->btreeMask, pOp->p1) );
|
|
assert( p->readOnly==0 );
|
|
pDb = &db->aDb[pOp->p1];
|
|
assert( pDb->pBt!=0 );
|
|
- if( pOp->opcode==OP_CreateTable ){
|
|
- /* flags = BTREE_INTKEY; */
|
|
- flags = BTREE_INTKEY;
|
|
- }else{
|
|
- flags = BTREE_BLOBKEY;
|
|
- }
|
|
- rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
|
|
+ rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, pOp->p3);
|
|
if( rc ) goto abort_due_to_error;
|
|
pOut->u.i = pgno;
|
|
break;
|
|
@@ -84481,6 +88686,7 @@
|
|
** Run the SQL statement or statements specified in the P4 string.
|
|
*/
|
|
case OP_SqlExec: {
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
db->nSqlExec++;
|
|
rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0);
|
|
db->nSqlExec--;
|
|
@@ -84491,7 +88697,8 @@
|
|
/* Opcode: ParseSchema P1 * * P4 *
|
|
**
|
|
** Read and parse all entries from the SQLITE_MASTER table of database P1
|
|
-** that match the WHERE clause P4.
|
|
+** that match the WHERE clause P4. If P4 is a NULL pointer, then the
|
|
+** entire schema for P1 is reparsed.
|
|
**
|
|
** This opcode invokes the parser to create a new virtual machine,
|
|
** then runs the new virtual machine. It is thus a re-entrant opcode.
|
|
@@ -84515,11 +88722,22 @@
|
|
iDb = pOp->p1;
|
|
assert( iDb>=0 && iDb<db->nDb );
|
|
assert( DbHasProperty(db, iDb, DB_SchemaLoaded) );
|
|
- /* Used to be a conditional */ {
|
|
+
|
|
+#ifndef SQLITE_OMIT_ALTERTABLE
|
|
+ if( pOp->p4.z==0 ){
|
|
+ sqlite3SchemaClear(db->aDb[iDb].pSchema);
|
|
+ db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
|
|
+ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
+ p->expired = 0;
|
|
+ }else
|
|
+#endif
|
|
+ {
|
|
zMaster = MASTER_NAME;
|
|
initData.db = db;
|
|
- initData.iDb = pOp->p1;
|
|
+ initData.iDb = iDb;
|
|
initData.pzErrMsg = &p->zErrMsg;
|
|
+ initData.mInitFlags = 0;
|
|
zSql = sqlite3MPrintf(db,
|
|
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
|
|
db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
|
|
@@ -84570,6 +88788,7 @@
|
|
** schema consistent with what is on disk.
|
|
*/
|
|
case OP_DropTable: {
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p4.z);
|
|
break;
|
|
}
|
|
@@ -84583,6 +88802,7 @@
|
|
** schema consistent with what is on disk.
|
|
*/
|
|
case OP_DropIndex: {
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p4.z);
|
|
break;
|
|
}
|
|
@@ -84596,6 +88816,7 @@
|
|
** schema consistent with what is on disk.
|
|
*/
|
|
case OP_DropTrigger: {
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p4.z);
|
|
break;
|
|
}
|
|
@@ -84632,7 +88853,7 @@
|
|
nRoot = pOp->p2;
|
|
aRoot = pOp->p4.ai;
|
|
assert( nRoot>0 );
|
|
- assert( aRoot[nRoot]==0 );
|
|
+ assert( aRoot[0]==nRoot );
|
|
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
|
pnErr = &aMem[pOp->p3];
|
|
assert( (pnErr->flags & MEM_Int)!=0 );
|
|
@@ -84640,7 +88861,7 @@
|
|
pIn1 = &aMem[pOp->p1];
|
|
assert( pOp->p5<db->nDb );
|
|
assert( DbMaskTest(p->btreeMask, pOp->p5) );
|
|
- z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot,
|
|
+ z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
|
|
(int)pnErr->u.i+1, &nErr);
|
|
sqlite3VdbeMemSetNull(pIn1);
|
|
if( nErr==0 ){
|
|
@@ -84669,11 +88890,11 @@
|
|
pIn1 = &aMem[pOp->p1];
|
|
pIn2 = &aMem[pOp->p2];
|
|
assert( (pIn2->flags & MEM_Int)!=0 );
|
|
- if( (pIn1->flags & MEM_RowSet)==0 ){
|
|
- sqlite3VdbeMemSetRowSet(pIn1);
|
|
- if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
|
|
+ if( (pIn1->flags & MEM_Blob)==0 ){
|
|
+ if( sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem;
|
|
}
|
|
- sqlite3RowSetInsert(pIn1->u.pRowSet, pIn2->u.i);
|
|
+ assert( sqlite3VdbeMemIsRowSet(pIn1) );
|
|
+ sqlite3RowSetInsert((RowSet*)pIn1->z, pIn2->u.i);
|
|
break;
|
|
}
|
|
|
|
@@ -84689,8 +88910,9 @@
|
|
i64 val;
|
|
|
|
pIn1 = &aMem[pOp->p1];
|
|
- if( (pIn1->flags & MEM_RowSet)==0
|
|
- || sqlite3RowSetNext(pIn1->u.pRowSet, &val)==0
|
|
+ assert( (pIn1->flags & MEM_Blob)==0 || sqlite3VdbeMemIsRowSet(pIn1) );
|
|
+ if( (pIn1->flags & MEM_Blob)==0
|
|
+ || sqlite3RowSetNext((RowSet*)pIn1->z, &val)==0
|
|
){
|
|
/* The boolean index is empty */
|
|
sqlite3VdbeMemSetNull(pIn1);
|
|
@@ -84739,20 +88961,19 @@
|
|
/* If there is anything other than a rowset object in memory cell P1,
|
|
** delete it now and initialize P1 with an empty rowset
|
|
*/
|
|
- if( (pIn1->flags & MEM_RowSet)==0 ){
|
|
- sqlite3VdbeMemSetRowSet(pIn1);
|
|
- if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
|
|
+ if( (pIn1->flags & MEM_Blob)==0 ){
|
|
+ if( sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem;
|
|
}
|
|
-
|
|
+ assert( sqlite3VdbeMemIsRowSet(pIn1) );
|
|
assert( pOp->p4type==P4_INT32 );
|
|
assert( iSet==-1 || iSet>=0 );
|
|
if( iSet ){
|
|
- exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i);
|
|
+ exists = sqlite3RowSetTest((RowSet*)pIn1->z, iSet, pIn3->u.i);
|
|
VdbeBranchTaken(exists!=0,2);
|
|
if( exists ) goto jump_to_p2;
|
|
}
|
|
if( iSet>=0 ){
|
|
- sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i);
|
|
+ sqlite3RowSetInsert((RowSet*)pIn1->z, pIn3->u.i);
|
|
}
|
|
break;
|
|
}
|
|
@@ -84816,7 +89037,7 @@
|
|
** of the current program, and the memory required at runtime to execute
|
|
** the trigger program. If this trigger has been fired before, then pRt
|
|
** is already allocated. Otherwise, it must be initialized. */
|
|
- if( (pRt->flags&MEM_Frame)==0 ){
|
|
+ if( (pRt->flags&MEM_Blob)==0 ){
|
|
/* SubProgram.nMem is set to the number of memory cells used by the
|
|
** program stored in SubProgram.aOp. As well as these, one memory
|
|
** cell is required for each cursor used by the program. Set local
|
|
@@ -84834,8 +89055,10 @@
|
|
goto no_mem;
|
|
}
|
|
sqlite3VdbeMemRelease(pRt);
|
|
- pRt->flags = MEM_Frame;
|
|
- pRt->u.pFrame = pFrame;
|
|
+ pRt->flags = MEM_Blob|MEM_Dyn;
|
|
+ pRt->z = (char*)pFrame;
|
|
+ pRt->n = nByte;
|
|
+ pRt->xDel = sqlite3VdbeFrameMemDel;
|
|
|
|
pFrame->v = p;
|
|
pFrame->nChildMem = nMem;
|
|
@@ -84851,6 +89074,9 @@
|
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
|
pFrame->anExec = p->anExec;
|
|
#endif
|
|
+#ifdef SQLITE_DEBUG
|
|
+ pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
|
|
+#endif
|
|
|
|
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
|
|
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
|
|
@@ -84858,7 +89084,8 @@
|
|
pMem->db = db;
|
|
}
|
|
}else{
|
|
- pFrame = pRt->u.pFrame;
|
|
+ pFrame = (VdbeFrame*)pRt->z;
|
|
+ assert( pRt->xDel==sqlite3VdbeFrameMemDel );
|
|
assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem
|
|
|| (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) );
|
|
assert( pProgram->nCsr==pFrame->nChildCsr );
|
|
@@ -85087,24 +89314,35 @@
|
|
}
|
|
|
|
|
|
-/* Opcode: AggStep0 * P2 P3 P4 P5
|
|
+/* Opcode: AggStep * P2 P3 P4 P5
|
|
** Synopsis: accum=r[P3] step(r[P2@P5])
|
|
**
|
|
-** Execute the step function for an aggregate. The
|
|
-** function has P5 arguments. P4 is a pointer to the FuncDef
|
|
-** structure that specifies the function. Register P3 is the
|
|
+** Execute the xStep function for an aggregate.
|
|
+** The function has P5 arguments. P4 is a pointer to the
|
|
+** FuncDef structure that specifies the function. Register P3 is the
|
|
** accumulator.
|
|
**
|
|
** The P5 arguments are taken from register P2 and its
|
|
** successors.
|
|
*/
|
|
-/* Opcode: AggStep * P2 P3 P4 P5
|
|
+/* Opcode: AggInverse * P2 P3 P4 P5
|
|
+** Synopsis: accum=r[P3] inverse(r[P2@P5])
|
|
+**
|
|
+** Execute the xInverse function for an aggregate.
|
|
+** The function has P5 arguments. P4 is a pointer to the
|
|
+** FuncDef structure that specifies the function. Register P3 is the
|
|
+** accumulator.
|
|
+**
|
|
+** The P5 arguments are taken from register P2 and its
|
|
+** successors.
|
|
+*/
|
|
+/* Opcode: AggStep1 P1 P2 P3 P4 P5
|
|
** Synopsis: accum=r[P3] step(r[P2@P5])
|
|
**
|
|
-** Execute the step function for an aggregate. The
|
|
-** function has P5 arguments. P4 is a pointer to an sqlite3_context
|
|
-** object that is used to run the function. Register P3 is
|
|
-** as the accumulator.
|
|
+** Execute the xStep (if P1==0) or xInverse (if P1!=0) function for an
|
|
+** aggregate. The function has P5 arguments. P4 is a pointer to the
|
|
+** FuncDef structure that specifies the function. Register P3 is the
|
|
+** accumulator.
|
|
**
|
|
** The P5 arguments are taken from register P2 and its
|
|
** successors.
|
|
@@ -85115,7 +89353,8 @@
|
|
** sqlite3_context only happens once, instead of on each call to the
|
|
** step function.
|
|
*/
|
|
-case OP_AggStep0: {
|
|
+case OP_AggInverse:
|
|
+case OP_AggStep: {
|
|
int n;
|
|
sqlite3_context *pCtx;
|
|
|
|
@@ -85124,28 +89363,47 @@
|
|
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
|
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
|
|
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
|
|
- pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
|
|
+ pCtx = sqlite3DbMallocRawNN(db, n*sizeof(sqlite3_value*) +
|
|
+ (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlite3_value*)));
|
|
if( pCtx==0 ) goto no_mem;
|
|
pCtx->pMem = 0;
|
|
+ pCtx->pOut = (Mem*)&(pCtx->argv[n]);
|
|
+ sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null);
|
|
pCtx->pFunc = pOp->p4.pFunc;
|
|
pCtx->iOp = (int)(pOp - aOp);
|
|
pCtx->pVdbe = p;
|
|
+ pCtx->skipFlag = 0;
|
|
+ pCtx->isError = 0;
|
|
pCtx->argc = n;
|
|
pOp->p4type = P4_FUNCCTX;
|
|
pOp->p4.pCtx = pCtx;
|
|
- pOp->opcode = OP_AggStep;
|
|
+
|
|
+ /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */
|
|
+ assert( pOp->p1==(pOp->opcode==OP_AggInverse) );
|
|
+
|
|
+ pOp->opcode = OP_AggStep1;
|
|
/* Fall through into OP_AggStep */
|
|
}
|
|
-case OP_AggStep: {
|
|
+case OP_AggStep1: {
|
|
int i;
|
|
sqlite3_context *pCtx;
|
|
Mem *pMem;
|
|
- Mem t;
|
|
|
|
assert( pOp->p4type==P4_FUNCCTX );
|
|
pCtx = pOp->p4.pCtx;
|
|
pMem = &aMem[pOp->p3];
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( pOp->p1 ){
|
|
+ /* This is an OP_AggInverse call. Verify that xStep has always
|
|
+ ** been called at least once prior to any xInverse call. */
|
|
+ assert( pMem->uTemp==0x1122e0e3 );
|
|
+ }else{
|
|
+ /* This is an OP_AggStep call. Mark it as such. */
|
|
+ pMem->uTemp = 0x1122e0e3;
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* If this function is inside of a trigger, the register array in aMem[]
|
|
** might change from one evaluation to the next. The next block of code
|
|
** checks to see if the register array has changed, and if so it
|
|
@@ -85163,26 +89421,34 @@
|
|
#endif
|
|
|
|
pMem->n++;
|
|
- sqlite3VdbeMemInit(&t, db, MEM_Null);
|
|
- pCtx->pOut = &t;
|
|
- pCtx->fErrorOrAux = 0;
|
|
- pCtx->skipFlag = 0;
|
|
+ assert( pCtx->pOut->flags==MEM_Null );
|
|
+ assert( pCtx->isError==0 );
|
|
+ assert( pCtx->skipFlag==0 );
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pOp->p1 ){
|
|
+ (pCtx->pFunc->xInverse)(pCtx,pCtx->argc,pCtx->argv);
|
|
+ }else
|
|
+#endif
|
|
(pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
|
|
- if( pCtx->fErrorOrAux ){
|
|
- if( pCtx->isError ){
|
|
- sqlite3VdbeError(p, "%s", sqlite3_value_text(&t));
|
|
+
|
|
+ if( pCtx->isError ){
|
|
+ if( pCtx->isError>0 ){
|
|
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut));
|
|
rc = pCtx->isError;
|
|
}
|
|
- sqlite3VdbeMemRelease(&t);
|
|
+ if( pCtx->skipFlag ){
|
|
+ assert( pOp[-1].opcode==OP_CollSeq );
|
|
+ i = pOp[-1].p1;
|
|
+ if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1);
|
|
+ pCtx->skipFlag = 0;
|
|
+ }
|
|
+ sqlite3VdbeMemRelease(pCtx->pOut);
|
|
+ pCtx->pOut->flags = MEM_Null;
|
|
+ pCtx->isError = 0;
|
|
if( rc ) goto abort_due_to_error;
|
|
- }else{
|
|
- assert( t.flags==MEM_Null );
|
|
}
|
|
- if( pCtx->skipFlag ){
|
|
- assert( pOp[-1].opcode==OP_CollSeq );
|
|
- i = pOp[-1].p1;
|
|
- if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1);
|
|
- }
|
|
+ assert( pCtx->pOut->flags==MEM_Null );
|
|
+ assert( pCtx->skipFlag==0 );
|
|
break;
|
|
}
|
|
|
|
@@ -85189,22 +89455,46 @@
|
|
/* Opcode: AggFinal P1 P2 * P4 *
|
|
** Synopsis: accum=r[P1] N=P2
|
|
**
|
|
-** Execute the finalizer function for an aggregate. P1 is
|
|
-** the memory location that is the accumulator for the aggregate.
|
|
+** P1 is the memory location that is the accumulator for an aggregate
|
|
+** or window function. Execute the finalizer function
|
|
+** for an aggregate and store the result in P1.
|
|
**
|
|
** P2 is the number of arguments that the step function takes and
|
|
** P4 is a pointer to the FuncDef for this function. The P2
|
|
** argument is not used by this opcode. It is only there to disambiguate
|
|
** functions that can take varying numbers of arguments. The
|
|
-** P4 argument is only needed for the degenerate case where
|
|
+** P4 argument is only needed for the case where
|
|
** the step function was not previously called.
|
|
*/
|
|
+/* Opcode: AggValue * P2 P3 P4 *
|
|
+** Synopsis: r[P3]=value N=P2
|
|
+**
|
|
+** Invoke the xValue() function and store the result in register P3.
|
|
+**
|
|
+** P2 is the number of arguments that the step function takes and
|
|
+** P4 is a pointer to the FuncDef for this function. The P2
|
|
+** argument is not used by this opcode. It is only there to disambiguate
|
|
+** functions that can take varying numbers of arguments. The
|
|
+** P4 argument is only needed for the case where
|
|
+** the step function was not previously called.
|
|
+*/
|
|
+case OP_AggValue:
|
|
case OP_AggFinal: {
|
|
Mem *pMem;
|
|
assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
|
|
+ assert( pOp->p3==0 || pOp->opcode==OP_AggValue );
|
|
pMem = &aMem[pOp->p1];
|
|
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
|
|
- rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pOp->p3 ){
|
|
+ rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc);
|
|
+ pMem = &aMem[pOp->p3];
|
|
+ }else
|
|
+#endif
|
|
+ {
|
|
+ rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
|
|
+ }
|
|
+
|
|
if( rc ){
|
|
sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
|
|
goto abort_due_to_error;
|
|
@@ -85399,7 +89689,7 @@
|
|
}
|
|
#endif
|
|
|
|
-/* Opcode: Expire P1 * * * *
|
|
+/* Opcode: Expire P1 P2 * * *
|
|
**
|
|
** Cause precompiled statements to expire. When an expired statement
|
|
** is executed using sqlite3_step() it will either automatically
|
|
@@ -85408,12 +89698,19 @@
|
|
**
|
|
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
|
|
** then only the currently executing statement is expired.
|
|
+**
|
|
+** If P2 is 0, then SQL statements are expired immediately. If P2 is 1,
|
|
+** then running SQL statements are allowed to continue to run to completion.
|
|
+** The P2==1 case occurs when a CREATE INDEX or similar schema change happens
|
|
+** that might help the statement run faster but which does not affect the
|
|
+** correctness of operation.
|
|
*/
|
|
case OP_Expire: {
|
|
+ assert( pOp->p2==0 || pOp->p2==1 );
|
|
if( !pOp->p1 ){
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, pOp->p2);
|
|
}else{
|
|
- p->expired = 1;
|
|
+ p->expired = pOp->p2+1;
|
|
}
|
|
break;
|
|
}
|
|
@@ -85627,12 +89924,19 @@
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
-/* Opcode: VColumn P1 P2 P3 * *
|
|
+/* Opcode: VColumn P1 P2 P3 * P5
|
|
** Synopsis: r[P3]=vcolumn(P2)
|
|
**
|
|
-** Store the value of the P2-th column of
|
|
-** the row of the virtual-table that the
|
|
-** P1 cursor is pointing to into register P3.
|
|
+** Store in register P3 the value of the P2-th column of
|
|
+** the current row of the virtual-table of cursor P1.
|
|
+**
|
|
+** If the VColumn opcode is being used to fetch the value of
|
|
+** an unchanging column during an UPDATE operation, then the P5
|
|
+** value is OPFLAG_NOCHNG. This will cause the sqlite3_vtab_nochange()
|
|
+** function to return true inside the xColumn method of the virtual
|
|
+** table implementation. The P5 column might also contain other
|
|
+** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are
|
|
+** unused by OP_VColumn.
|
|
*/
|
|
case OP_VColumn: {
|
|
sqlite3_vtab *pVtab;
|
|
@@ -85654,10 +89958,18 @@
|
|
assert( pModule->xColumn );
|
|
memset(&sContext, 0, sizeof(sContext));
|
|
sContext.pOut = pDest;
|
|
- MemSetTypeFlag(pDest, MEM_Null);
|
|
+ testcase( (pOp->p5 & OPFLAG_NOCHNG)==0 && pOp->p5!=0 );
|
|
+ if( pOp->p5 & OPFLAG_NOCHNG ){
|
|
+ sqlite3VdbeMemSetNull(pDest);
|
|
+ pDest->flags = MEM_Null|MEM_Zero;
|
|
+ pDest->u.nZero = 0;
|
|
+ }else{
|
|
+ MemSetTypeFlag(pDest, MEM_Null);
|
|
+ }
|
|
rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2);
|
|
sqlite3VtabImportErrmsg(p, pVtab);
|
|
- if( sContext.isError ){
|
|
+ if( sContext.isError>0 ){
|
|
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(pDest));
|
|
rc = sContext.isError;
|
|
}
|
|
sqlite3VdbeChangeEncoding(pDest, encoding);
|
|
@@ -85724,7 +90036,10 @@
|
|
case OP_VRename: {
|
|
sqlite3_vtab *pVtab;
|
|
Mem *pName;
|
|
-
|
|
+ int isLegacy;
|
|
+
|
|
+ isLegacy = (db->flags & SQLITE_LegacyAlter);
|
|
+ db->flags |= SQLITE_LegacyAlter;
|
|
pVtab = pOp->p4.pVtab->pVtab;
|
|
pName = &aMem[pOp->p1];
|
|
assert( pVtab->pModule->xRename );
|
|
@@ -85738,6 +90053,7 @@
|
|
rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8);
|
|
if( rc ) goto abort_due_to_error;
|
|
rc = pVtab->pModule->xRename(pVtab, pName->z);
|
|
+ if( isLegacy==0 ) db->flags &= ~SQLITE_LegacyAlter;
|
|
sqlite3VtabImportErrmsg(p, pVtab);
|
|
p->expired = 0;
|
|
if( rc ) goto abort_due_to_error;
|
|
@@ -85786,6 +90102,8 @@
|
|
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
|
|
);
|
|
assert( p->readOnly==0 );
|
|
+ if( db->mallocFailed ) goto no_mem;
|
|
+ sqlite3VdbeIncrWriteCounter(p, 0);
|
|
pVtab = pOp->p4.pVtab->pVtab;
|
|
if( pVtab==0 || NEVER(pVtab->pModule==0) ){
|
|
rc = SQLITE_LOCKED;
|
|
@@ -85906,8 +90224,8 @@
|
|
**
|
|
** See also: Function0, AggStep, AggFinal
|
|
*/
|
|
-case OP_PureFunc0:
|
|
-case OP_Function0: {
|
|
+case OP_PureFunc0: /* group */
|
|
+case OP_Function0: { /* group */
|
|
int n;
|
|
sqlite3_context *pCtx;
|
|
|
|
@@ -85922,6 +90240,7 @@
|
|
pCtx->pFunc = pOp->p4.pFunc;
|
|
pCtx->iOp = (int)(pOp - aOp);
|
|
pCtx->pVdbe = p;
|
|
+ pCtx->isError = 0;
|
|
pCtx->argc = n;
|
|
pOp->p4type = P4_FUNCCTX;
|
|
pOp->p4.pCtx = pCtx;
|
|
@@ -85930,8 +90249,8 @@
|
|
pOp->opcode += 2;
|
|
/* Fall through into OP_Function */
|
|
}
|
|
-case OP_PureFunc:
|
|
-case OP_Function: {
|
|
+case OP_PureFunc: /* group */
|
|
+case OP_Function: { /* group */
|
|
int i;
|
|
sqlite3_context *pCtx;
|
|
|
|
@@ -85956,16 +90275,17 @@
|
|
}
|
|
#endif
|
|
MemSetTypeFlag(pOut, MEM_Null);
|
|
- pCtx->fErrorOrAux = 0;
|
|
+ assert( pCtx->isError==0 );
|
|
(*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
|
|
|
|
/* If the function returned an error, throw an exception */
|
|
- if( pCtx->fErrorOrAux ){
|
|
- if( pCtx->isError ){
|
|
+ if( pCtx->isError ){
|
|
+ if( pCtx->isError>0 ){
|
|
sqlite3VdbeError(p, "%s", sqlite3_value_text(pOut));
|
|
rc = pCtx->isError;
|
|
}
|
|
sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1);
|
|
+ pCtx->isError = 0;
|
|
if( rc ) goto abort_due_to_error;
|
|
}
|
|
|
|
@@ -85980,8 +90300,14 @@
|
|
break;
|
|
}
|
|
|
|
-
|
|
-/* Opcode: Init P1 P2 * P4 *
|
|
+/* Opcode: Trace P1 P2 * P4 *
|
|
+**
|
|
+** Write P4 on the statement trace output if statement tracing is
|
|
+** enabled.
|
|
+**
|
|
+** Operand P1 must be 0x7fffffff and P2 must positive.
|
|
+*/
|
|
+/* Opcode: Init P1 P2 P3 P4 *
|
|
** Synopsis: Start at P2
|
|
**
|
|
** Programs contain a single instance of this opcode as the very first
|
|
@@ -85995,10 +90321,16 @@
|
|
**
|
|
** Increment the value of P1 so that OP_Once opcodes will jump the
|
|
** first time they are evaluated for this run.
|
|
+**
|
|
+** If P3 is not zero, then it is an address to jump to if an SQLITE_CORRUPT
|
|
+** error is encountered.
|
|
*/
|
|
+case OP_Trace:
|
|
case OP_Init: { /* jump */
|
|
+ int i;
|
|
+#ifndef SQLITE_OMIT_TRACE
|
|
char *zTrace;
|
|
- int i;
|
|
+#endif
|
|
|
|
/* If the P4 argument is not NULL, then it must be an SQL comment string.
|
|
** The "--" string is broken up to prevent false-positives with srcck1.c.
|
|
@@ -86010,8 +90342,10 @@
|
|
** sqlite3_expanded_sql(P) otherwise.
|
|
*/
|
|
assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 );
|
|
- assert( pOp==p->aOp ); /* Always instruction 0 */
|
|
|
|
+ /* OP_Init is always instruction 0 */
|
|
+ assert( pOp==p->aOp || pOp->opcode==OP_Trace );
|
|
+
|
|
#ifndef SQLITE_OMIT_TRACE
|
|
if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0
|
|
&& !p->doingRerun
|
|
@@ -86053,6 +90387,7 @@
|
|
#endif /* SQLITE_OMIT_TRACE */
|
|
assert( pOp->p2>0 );
|
|
if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){
|
|
+ if( pOp->opcode==OP_Trace ) break;
|
|
for(i=1; i<p->nOp; i++){
|
|
if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0;
|
|
}
|
|
@@ -86086,6 +90421,22 @@
|
|
}
|
|
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
+/* Opcode: Abortable * * * * *
|
|
+**
|
|
+** Verify that an Abort can happen. Assert if an Abort at this point
|
|
+** might cause database corruption. This opcode only appears in debugging
|
|
+** builds.
|
|
+**
|
|
+** An Abort is safe if either there have been no writes, or if there is
|
|
+** an active statement journal.
|
|
+*/
|
|
+case OP_Abortable: {
|
|
+ sqlite3VdbeAssertAbortable(p);
|
|
+ break;
|
|
+}
|
|
+#endif
|
|
+
|
|
/* Opcode: Noop * * * * *
|
|
**
|
|
** Do nothing. This instruction is often useful as a jump
|
|
@@ -86097,8 +90448,9 @@
|
|
** This opcode records information from the optimizer. It is the
|
|
** the same as a no-op. This opcodesnever appears in a real VM program.
|
|
*/
|
|
-default: { /* This is really OP_Noop and OP_Explain */
|
|
+default: { /* This is really OP_Noop, OP_Explain */
|
|
assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain );
|
|
+
|
|
break;
|
|
}
|
|
|
|
@@ -86112,7 +90464,7 @@
|
|
|
|
#ifdef VDBE_PROFILE
|
|
{
|
|
- u64 endTime = sqlite3Hwtime();
|
|
+ u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
|
|
if( endTime>start ) pOrigOp->cycles += endTime - start;
|
|
pOrigOp->cnt++;
|
|
}
|
|
@@ -86269,11 +90621,12 @@
|
|
v->aMem[1].u.i = iRow;
|
|
|
|
/* If the statement has been run before (and is paused at the OP_ResultRow)
|
|
- ** then back it up to the point where it does the OP_SeekRowid. This could
|
|
+ ** then back it up to the point where it does the OP_NotExists. This could
|
|
** have been down with an extra OP_Goto, but simply setting the program
|
|
** counter is faster. */
|
|
- if( v->pc>3 ){
|
|
- v->pc = 3;
|
|
+ if( v->pc>4 ){
|
|
+ v->pc = 4;
|
|
+ assert( v->aOp[v->pc].opcode==OP_NotExists );
|
|
rc = sqlite3VdbeExec(v);
|
|
}else{
|
|
rc = sqlite3_step(p->pStmt);
|
|
@@ -86335,8 +90688,8 @@
|
|
int rc = SQLITE_OK;
|
|
char *zErr = 0;
|
|
Table *pTab;
|
|
- Parse *pParse = 0;
|
|
Incrblob *pBlob = 0;
|
|
+ Parse sParse;
|
|
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( ppBlob==0 ){
|
|
@@ -86354,37 +90707,34 @@
|
|
sqlite3_mutex_enter(db->mutex);
|
|
|
|
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
|
|
- if( !pBlob ) goto blob_open_out;
|
|
- pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
|
|
- if( !pParse ) goto blob_open_out;
|
|
-
|
|
do {
|
|
- memset(pParse, 0, sizeof(Parse));
|
|
- pParse->db = db;
|
|
+ memset(&sParse, 0, sizeof(Parse));
|
|
+ if( !pBlob ) goto blob_open_out;
|
|
+ sParse.db = db;
|
|
sqlite3DbFree(db, zErr);
|
|
zErr = 0;
|
|
|
|
sqlite3BtreeEnterAll(db);
|
|
- pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
|
|
+ pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);
|
|
if( pTab && IsVirtual(pTab) ){
|
|
pTab = 0;
|
|
- sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
|
|
+ sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);
|
|
}
|
|
if( pTab && !HasRowid(pTab) ){
|
|
pTab = 0;
|
|
- sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
|
|
+ sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
|
|
}
|
|
#ifndef SQLITE_OMIT_VIEW
|
|
if( pTab && pTab->pSelect ){
|
|
pTab = 0;
|
|
- sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable);
|
|
+ sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
|
|
}
|
|
#endif
|
|
if( !pTab ){
|
|
- if( pParse->zErrMsg ){
|
|
+ if( sParse.zErrMsg ){
|
|
sqlite3DbFree(db, zErr);
|
|
- zErr = pParse->zErrMsg;
|
|
- pParse->zErrMsg = 0;
|
|
+ zErr = sParse.zErrMsg;
|
|
+ sParse.zErrMsg = 0;
|
|
}
|
|
rc = SQLITE_ERROR;
|
|
sqlite3BtreeLeaveAll(db);
|
|
@@ -86448,7 +90798,7 @@
|
|
}
|
|
}
|
|
|
|
- pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
|
|
+ pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);
|
|
assert( pBlob->pStmt || db->mallocFailed );
|
|
if( pBlob->pStmt ){
|
|
|
|
@@ -86484,7 +90834,8 @@
|
|
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
|
|
pTab->pSchema->schema_cookie,
|
|
pTab->pSchema->iGeneration);
|
|
- sqlite3VdbeChangeP5(v, 1);
|
|
+ sqlite3VdbeChangeP5(v, 1);
|
|
+ assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );
|
|
aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
|
|
|
|
/* Make sure a mutex is held on the table to be accessed */
|
|
@@ -86499,7 +90850,7 @@
|
|
aOp[0].p1 = iDb;
|
|
aOp[0].p2 = pTab->tnum;
|
|
aOp[0].p3 = wrFlag;
|
|
- sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
|
+ sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
|
|
}
|
|
if( db->mallocFailed==0 ){
|
|
#endif
|
|
@@ -86521,10 +90872,10 @@
|
|
aOp[1].p4.i = pTab->nCol+1;
|
|
aOp[3].p2 = pTab->nCol;
|
|
|
|
- pParse->nVar = 0;
|
|
- pParse->nMem = 1;
|
|
- pParse->nTab = 1;
|
|
- sqlite3VdbeMakeReady(v, pParse);
|
|
+ sParse.nVar = 0;
|
|
+ sParse.nMem = 1;
|
|
+ sParse.nTab = 1;
|
|
+ sqlite3VdbeMakeReady(v, &sParse);
|
|
}
|
|
}
|
|
|
|
@@ -86546,8 +90897,7 @@
|
|
}
|
|
sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
|
|
sqlite3DbFree(db, zErr);
|
|
- sqlite3ParserReset(pParse);
|
|
- sqlite3StackFree(db, pParse);
|
|
+ sqlite3ParserReset(&sParse);
|
|
rc = sqlite3ApiExit(db, rc);
|
|
sqlite3_mutex_leave(db->mutex);
|
|
return rc;
|
|
@@ -87541,7 +91891,7 @@
|
|
}
|
|
|
|
if( res==0 ){
|
|
- if( pTask->pSorter->pKeyInfo->nField>1 ){
|
|
+ if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
|
res = vdbeSorterCompareTail(
|
|
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
|
|
);
|
|
@@ -87610,7 +91960,7 @@
|
|
}
|
|
|
|
if( res==0 ){
|
|
- if( pTask->pSorter->pKeyInfo->nField>1 ){
|
|
+ if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
|
res = vdbeSorterCompareTail(
|
|
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
|
|
);
|
|
@@ -87625,7 +91975,7 @@
|
|
/*
|
|
** Initialize the temporary index cursor just opened as a sorter cursor.
|
|
**
|
|
-** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField)
|
|
+** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField)
|
|
** to determine the number of fields that should be compared from the
|
|
** records being sorted. However, if the value passed as argument nField
|
|
** is non-zero and the sorter is able to guarantee a stable sort, nField
|
|
@@ -87678,7 +92028,7 @@
|
|
|
|
assert( pCsr->pKeyInfo && pCsr->pBtx==0 );
|
|
assert( pCsr->eCurType==CURTYPE_SORTER );
|
|
- szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*);
|
|
+ szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
|
|
sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
|
|
|
|
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
|
|
@@ -87690,8 +92040,7 @@
|
|
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
|
|
pKeyInfo->db = 0;
|
|
if( nField && nWorker==0 ){
|
|
- pKeyInfo->nXField += (pKeyInfo->nField - nField);
|
|
- pKeyInfo->nField = nField;
|
|
+ pKeyInfo->nKeyField = nField;
|
|
}
|
|
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
|
|
pSorter->nTask = nWorker + 1;
|
|
@@ -87719,11 +92068,9 @@
|
|
mxCache = MIN(mxCache, SQLITE_MAX_PMASZ);
|
|
pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache);
|
|
|
|
- /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
|
|
- ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
|
|
- ** large heap allocations.
|
|
- */
|
|
- if( sqlite3GlobalConfig.pScratch==0 ){
|
|
+ /* Avoid large memory allocations if the application has requested
|
|
+ ** SQLITE_CONFIG_SMALL_MALLOC. */
|
|
+ if( sqlite3GlobalConfig.bSmallMalloc==0 ){
|
|
assert( pSorter->iMemory==0 );
|
|
pSorter->nMemory = pgsz;
|
|
pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz);
|
|
@@ -87731,7 +92078,7 @@
|
|
}
|
|
}
|
|
|
|
- if( (pKeyInfo->nField+pKeyInfo->nXField)<13
|
|
+ if( pKeyInfo->nAllField<13
|
|
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
|
|
){
|
|
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
|
|
@@ -88046,7 +92393,7 @@
|
|
if( pTask->pUnpacked==0 ){
|
|
pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo);
|
|
if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT;
|
|
- pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
|
|
+ pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField;
|
|
pTask->pUnpacked->errCode = 0;
|
|
}
|
|
return SQLITE_OK;
|
|
@@ -88828,8 +93175,12 @@
|
|
){
|
|
int rc = SQLITE_OK; /* Return code */
|
|
int i; /* For looping over PmaReader objects */
|
|
- int nTree = pMerger->nTree;
|
|
+ int nTree; /* Number of subtrees to merge */
|
|
|
|
+ /* Failure to allocate the merge would have been detected prior to
|
|
+ ** invoking this routine */
|
|
+ assert( pMerger!=0 );
|
|
+
|
|
/* eMode is always INCRINIT_NORMAL in single-threaded mode */
|
|
assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL );
|
|
|
|
@@ -88837,6 +93188,7 @@
|
|
assert( pMerger->pTask==0 );
|
|
pMerger->pTask = pTask;
|
|
|
|
+ nTree = pMerger->nTree;
|
|
for(i=0; i<nTree; i++){
|
|
if( SQLITE_MAX_WORKER_THREADS>0 && eMode==INCRINIT_ROOT ){
|
|
/* PmaReaders should be normally initialized in order, as if they are
|
|
@@ -89570,7 +93922,8 @@
|
|
int iChunkOffset;
|
|
FileChunk *pChunk;
|
|
|
|
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
+#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
|
+ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
if( (iAmt+iOfst)>p->endpoint.iOffset ){
|
|
return SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
@@ -89689,7 +94042,8 @@
|
|
** atomic-write optimization. In this case the first 28 bytes of the
|
|
** journal file may be written as part of committing the transaction. */
|
|
assert( iOfst==p->endpoint.iOffset || iOfst==0 );
|
|
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
+#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
|
+ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
if( iOfst==0 && p->pFirst ){
|
|
assert( p->nChunkSize>iAmt );
|
|
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
|
|
@@ -89858,17 +94212,31 @@
|
|
sqlite3JournalOpen(0, 0, pJfd, 0, -1);
|
|
}
|
|
|
|
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
+#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
|
+ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
|
/*
|
|
** If the argument p points to a MemJournal structure that is not an
|
|
** in-memory-only journal file (i.e. is one that was opened with a +ve
|
|
-** nSpill parameter), and the underlying file has not yet been created,
|
|
-** create it now.
|
|
+** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying
|
|
+** file has not yet been created, create it now.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
|
|
+SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){
|
|
int rc = SQLITE_OK;
|
|
- if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
|
|
- rc = memjrnlCreateFile((MemJournal*)p);
|
|
+ MemJournal *p = (MemJournal*)pJfd;
|
|
+ if( p->pMethod==&MemJournalMethods && (
|
|
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
|
+ p->nSpill>0
|
|
+#else
|
|
+ /* While this appears to not be possible without ATOMIC_WRITE, the
|
|
+ ** paths are complex, so it seems prudent to leave the test in as
|
|
+ ** a NEVER(), in case our analysis is subtly flawed. */
|
|
+ NEVER(p->nSpill>0)
|
|
+#endif
|
|
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
|
+ || (p->flags & SQLITE_OPEN_MAIN_JOURNAL)
|
|
+#endif
|
|
+ )){
|
|
+ rc = memjrnlCreateFile(p);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -89935,18 +94303,30 @@
|
|
int rc;
|
|
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
|
|
testcase( ExprHasProperty(pExpr, EP_Reduced) );
|
|
- rc = pWalker->xExprCallback(pWalker, pExpr);
|
|
- if( rc ) return rc & WRC_Abort;
|
|
- if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
|
|
- if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
|
- assert( pExpr->x.pList==0 || pExpr->pRight==0 );
|
|
- if( pExpr->pRight ){
|
|
- if( walkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
|
|
- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
|
- if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
|
- }else if( pExpr->x.pList ){
|
|
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
|
+ while(1){
|
|
+ rc = pWalker->xExprCallback(pWalker, pExpr);
|
|
+ if( rc ) return rc & WRC_Abort;
|
|
+ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
|
|
+ if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
|
+ assert( pExpr->x.pList==0 || pExpr->pRight==0 );
|
|
+ if( pExpr->pRight ){
|
|
+ pExpr = pExpr->pRight;
|
|
+ continue;
|
|
+ }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
|
+ if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
|
+ }else if( pExpr->x.pList ){
|
|
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
|
+ }
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
|
+ Window *pWin = pExpr->y.pWin;
|
|
+ if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort;
|
|
+ if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort;
|
|
+ if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort;
|
|
+ }
|
|
+#endif
|
|
}
|
|
+ break;
|
|
}
|
|
return WRC_Continue;
|
|
}
|
|
@@ -89982,7 +94362,6 @@
|
|
if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort;
|
|
if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort;
|
|
if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort;
|
|
- if( sqlite3WalkExpr(pWalker, p->pOffset) ) return WRC_Abort;
|
|
return WRC_Continue;
|
|
}
|
|
|
|
@@ -89999,17 +94378,16 @@
|
|
struct SrcList_item *pItem;
|
|
|
|
pSrc = p->pSrc;
|
|
- if( ALWAYS(pSrc) ){
|
|
- for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
|
- if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
|
|
- return WRC_Abort;
|
|
- }
|
|
- if( pItem->fg.isTabFunc
|
|
- && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
|
|
- ){
|
|
- return WRC_Abort;
|
|
- }
|
|
+ assert( pSrc!=0 );
|
|
+ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
|
+ if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
|
|
+ return WRC_Abort;
|
|
}
|
|
+ if( pItem->fg.isTabFunc
|
|
+ && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
|
|
+ ){
|
|
+ return WRC_Abort;
|
|
+ }
|
|
}
|
|
return WRC_Continue;
|
|
}
|
|
@@ -90130,29 +94508,31 @@
|
|
assert( pOrig!=0 );
|
|
db = pParse->db;
|
|
pDup = sqlite3ExprDup(db, pOrig, 0);
|
|
- if( pDup==0 ) return;
|
|
- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
|
|
- if( pExpr->op==TK_COLLATE ){
|
|
- pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
|
|
- }
|
|
- ExprSetProperty(pDup, EP_Alias);
|
|
+ if( pDup!=0 ){
|
|
+ if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
|
|
+ if( pExpr->op==TK_COLLATE ){
|
|
+ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
|
|
+ }
|
|
+ ExprSetProperty(pDup, EP_Alias);
|
|
|
|
- /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
|
|
- ** prevents ExprDelete() from deleting the Expr structure itself,
|
|
- ** allowing it to be repopulated by the memcpy() on the following line.
|
|
- ** The pExpr->u.zToken might point into memory that will be freed by the
|
|
- ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
|
|
- ** make a copy of the token before doing the sqlite3DbFree().
|
|
- */
|
|
- ExprSetProperty(pExpr, EP_Static);
|
|
- sqlite3ExprDelete(db, pExpr);
|
|
- memcpy(pExpr, pDup, sizeof(*pExpr));
|
|
- if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
|
|
- assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
|
|
- pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
|
|
- pExpr->flags |= EP_MemToken;
|
|
+ /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
|
|
+ ** prevents ExprDelete() from deleting the Expr structure itself,
|
|
+ ** allowing it to be repopulated by the memcpy() on the following line.
|
|
+ ** The pExpr->u.zToken might point into memory that will be freed by the
|
|
+ ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
|
|
+ ** make a copy of the token before doing the sqlite3DbFree().
|
|
+ */
|
|
+ ExprSetProperty(pExpr, EP_Static);
|
|
+ sqlite3ExprDelete(db, pExpr);
|
|
+ memcpy(pExpr, pDup, sizeof(*pExpr));
|
|
+ if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
|
|
+ assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
|
|
+ pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
|
|
+ pExpr->flags |= EP_MemToken;
|
|
+ }
|
|
+ sqlite3DbFree(db, pDup);
|
|
}
|
|
- sqlite3DbFree(db, pDup);
|
|
+ ExprSetProperty(pExpr, EP_Alias);
|
|
}
|
|
|
|
|
|
@@ -90212,7 +94592,7 @@
|
|
** (even if X is implied).
|
|
** pExpr->iTable Set to the cursor number for the table obtained
|
|
** from pSrcList.
|
|
-** pExpr->pTab Points to the Table structure of X.Y (even if
|
|
+** pExpr->y.pTab Points to the Table structure of X.Y (even if
|
|
** X and/or Y are implied.)
|
|
** pExpr->iColumn Set to the column number within the table.
|
|
** pExpr->op Set to TK_COLUMN.
|
|
@@ -90246,7 +94626,7 @@
|
|
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
|
|
NameContext *pTopNC = pNC; /* First namecontext in the list */
|
|
Schema *pSchema = 0; /* Schema of the expression */
|
|
- int isTrigger = 0; /* True if resolved to a trigger column */
|
|
+ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
|
|
Table *pTab = 0; /* Table hold the row */
|
|
Column *pCol; /* A column of pTab */
|
|
|
|
@@ -90256,7 +94636,6 @@
|
|
|
|
/* Initialize the node to no-match */
|
|
pExpr->iTable = -1;
|
|
- pExpr->pTab = 0;
|
|
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
|
|
|
/* Translate the schema name in zDb into a pointer to the corresponding
|
|
@@ -90317,6 +94696,9 @@
|
|
if( sqlite3StrICmp(zTabName, zTab)!=0 ){
|
|
continue;
|
|
}
|
|
+ if( IN_RENAME_OBJECT && pItem->zAlias ){
|
|
+ sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab);
|
|
+ }
|
|
}
|
|
if( 0==(cntTab++) ){
|
|
pMatch = pItem;
|
|
@@ -90341,32 +94723,45 @@
|
|
}
|
|
if( pMatch ){
|
|
pExpr->iTable = pMatch->iCursor;
|
|
- pExpr->pTab = pMatch->pTab;
|
|
+ pExpr->y.pTab = pMatch->pTab;
|
|
/* RIGHT JOIN not (yet) supported */
|
|
assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
|
|
if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
|
|
ExprSetProperty(pExpr, EP_CanBeNull);
|
|
}
|
|
- pSchema = pExpr->pTab->pSchema;
|
|
+ pSchema = pExpr->y.pTab->pSchema;
|
|
}
|
|
} /* if( pSrcList ) */
|
|
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
+#if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT)
|
|
/* If we have not already resolved the name, then maybe
|
|
- ** it is a new.* or old.* trigger argument reference
|
|
+ ** it is a new.* or old.* trigger argument reference. Or
|
|
+ ** maybe it is an excluded.* from an upsert.
|
|
*/
|
|
- if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
|
|
- int op = pParse->eTriggerOp;
|
|
- assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
|
- if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
|
- pExpr->iTable = 1;
|
|
- pTab = pParse->pTriggerTab;
|
|
- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
|
- pExpr->iTable = 0;
|
|
- pTab = pParse->pTriggerTab;
|
|
- }else{
|
|
- pTab = 0;
|
|
+ if( zDb==0 && zTab!=0 && cntTab==0 ){
|
|
+ pTab = 0;
|
|
+#ifndef SQLITE_OMIT_TRIGGER
|
|
+ if( pParse->pTriggerTab!=0 ){
|
|
+ int op = pParse->eTriggerOp;
|
|
+ assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
|
+ if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
|
+ pExpr->iTable = 1;
|
|
+ pTab = pParse->pTriggerTab;
|
|
+ }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
|
+ pExpr->iTable = 0;
|
|
+ pTab = pParse->pTriggerTab;
|
|
+ }
|
|
}
|
|
+#endif /* SQLITE_OMIT_TRIGGER */
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+ if( (pNC->ncFlags & NC_UUpsert)!=0 ){
|
|
+ Upsert *pUpsert = pNC->uNC.pUpsert;
|
|
+ if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
|
+ pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
|
+ pExpr->iTable = 2;
|
|
+ }
|
|
+ }
|
|
+#endif /* SQLITE_OMIT_UPSERT */
|
|
|
|
if( pTab ){
|
|
int iCol;
|
|
@@ -90386,24 +94781,42 @@
|
|
}
|
|
if( iCol<pTab->nCol ){
|
|
cnt++;
|
|
- if( iCol<0 ){
|
|
- pExpr->affinity = SQLITE_AFF_INTEGER;
|
|
- }else if( pExpr->iTable==0 ){
|
|
- testcase( iCol==31 );
|
|
- testcase( iCol==32 );
|
|
- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
|
- }else{
|
|
- testcase( iCol==31 );
|
|
- testcase( iCol==32 );
|
|
- pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+ if( pExpr->iTable==2 ){
|
|
+ testcase( iCol==(-1) );
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ pExpr->iColumn = iCol;
|
|
+ pExpr->y.pTab = pTab;
|
|
+ eNewExprOp = TK_COLUMN;
|
|
+ }else{
|
|
+ pExpr->iTable = pNC->uNC.pUpsert->regData + iCol;
|
|
+ eNewExprOp = TK_REGISTER;
|
|
+ ExprSetProperty(pExpr, EP_Alias);
|
|
+ }
|
|
+ }else
|
|
+#endif /* SQLITE_OMIT_UPSERT */
|
|
+ {
|
|
+#ifndef SQLITE_OMIT_TRIGGER
|
|
+ if( iCol<0 ){
|
|
+ pExpr->affinity = SQLITE_AFF_INTEGER;
|
|
+ }else if( pExpr->iTable==0 ){
|
|
+ testcase( iCol==31 );
|
|
+ testcase( iCol==32 );
|
|
+ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
|
+ }else{
|
|
+ testcase( iCol==31 );
|
|
+ testcase( iCol==32 );
|
|
+ pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
|
+ }
|
|
+ pExpr->y.pTab = pTab;
|
|
+ pExpr->iColumn = (i16)iCol;
|
|
+ eNewExprOp = TK_TRIGGER;
|
|
+#endif /* SQLITE_OMIT_TRIGGER */
|
|
}
|
|
- pExpr->iColumn = (i16)iCol;
|
|
- pExpr->pTab = pTab;
|
|
- isTrigger = 1;
|
|
}
|
|
}
|
|
}
|
|
-#endif /* !defined(SQLITE_OMIT_TRIGGER) */
|
|
+#endif /* !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) */
|
|
|
|
/*
|
|
** Perhaps the name is a reference to the ROWID
|
|
@@ -90438,10 +94851,12 @@
|
|
** is supported for backwards compatibility only. Hence, we issue a warning
|
|
** on sqlite3_log() whenever the capability is used.
|
|
*/
|
|
- if( (pEList = pNC->pEList)!=0
|
|
+ if( (pNC->ncFlags & NC_UEList)!=0
|
|
+ && cnt==0
|
|
&& zTab==0
|
|
- && cnt==0
|
|
){
|
|
+ pEList = pNC->uNC.pEList;
|
|
+ assert( pEList!=0 );
|
|
for(j=0; j<pEList->nExpr; j++){
|
|
char *zAs = pEList->a[j].zName;
|
|
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
|
|
@@ -90462,6 +94877,9 @@
|
|
cnt = 1;
|
|
pMatch = 0;
|
|
assert( zTab==0 && zDb==0 );
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
|
|
+ }
|
|
goto lookupname_end;
|
|
}
|
|
}
|
|
@@ -90486,10 +94904,16 @@
|
|
** Because no reference was made to outer contexts, the pNC->nRef
|
|
** fields are not changed in any context.
|
|
*/
|
|
- if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){
|
|
- pExpr->op = TK_STRING;
|
|
- pExpr->pTab = 0;
|
|
- return WRC_Prune;
|
|
+ if( cnt==0 && zTab==0 ){
|
|
+ assert( pExpr->op==TK_ID );
|
|
+ if( ExprHasProperty(pExpr,EP_DblQuoted) ){
|
|
+ pExpr->op = TK_STRING;
|
|
+ pExpr->y.pTab = 0;
|
|
+ return WRC_Prune;
|
|
+ }
|
|
+ if( sqlite3ExprIdToTrueFalse(pExpr) ){
|
|
+ return WRC_Prune;
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
@@ -90532,7 +94956,7 @@
|
|
pExpr->pLeft = 0;
|
|
sqlite3ExprDelete(db, pExpr->pRight);
|
|
pExpr->pRight = 0;
|
|
- pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN);
|
|
+ pExpr->op = eNewExprOp;
|
|
ExprSetProperty(pExpr, EP_Leaf);
|
|
lookupname_end:
|
|
if( cnt==1 ){
|
|
@@ -90562,9 +94986,9 @@
|
|
Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
|
|
if( p ){
|
|
struct SrcList_item *pItem = &pSrc->a[iSrc];
|
|
- p->pTab = pItem->pTab;
|
|
+ p->y.pTab = pItem->pTab;
|
|
p->iTable = pItem->iCursor;
|
|
- if( p->pTab->iPKey==iCol ){
|
|
+ if( p->y.pTab->iPKey==iCol ){
|
|
p->iColumn = -1;
|
|
}else{
|
|
p->iColumn = (ynVar)iCol;
|
|
@@ -90651,9 +95075,10 @@
|
|
SrcList *pSrcList = pNC->pSrcList;
|
|
struct SrcList_item *pItem;
|
|
assert( pSrcList && pSrcList->nSrc==1 );
|
|
- pItem = pSrcList->a;
|
|
+ pItem = pSrcList->a;
|
|
+ assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
|
|
pExpr->op = TK_COLUMN;
|
|
- pExpr->pTab = pItem->pTab;
|
|
+ pExpr->y.pTab = pItem->pTab;
|
|
pExpr->iTable = pItem->iCursor;
|
|
pExpr->iColumn = -1;
|
|
pExpr->affinity = SQLITE_AFF_INTEGER;
|
|
@@ -90682,18 +95107,23 @@
|
|
zTable = 0;
|
|
zColumn = pExpr->u.zToken;
|
|
}else{
|
|
+ Expr *pLeft = pExpr->pLeft;
|
|
notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
|
|
pRight = pExpr->pRight;
|
|
if( pRight->op==TK_ID ){
|
|
zDb = 0;
|
|
- zTable = pExpr->pLeft->u.zToken;
|
|
- zColumn = pRight->u.zToken;
|
|
}else{
|
|
assert( pRight->op==TK_DOT );
|
|
- zDb = pExpr->pLeft->u.zToken;
|
|
- zTable = pRight->pLeft->u.zToken;
|
|
- zColumn = pRight->pRight->u.zToken;
|
|
+ zDb = pLeft->u.zToken;
|
|
+ pLeft = pRight->pLeft;
|
|
+ pRight = pRight->pRight;
|
|
}
|
|
+ zTable = pLeft->u.zToken;
|
|
+ zColumn = pRight->u.zToken;
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
|
|
+ sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft);
|
|
+ }
|
|
}
|
|
return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
|
|
}
|
|
@@ -90774,41 +95204,105 @@
|
|
notValid(pParse, pNC, "non-deterministic functions",
|
|
NC_IdxExpr|NC_PartIdx);
|
|
}
|
|
+ if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
|
|
+ && pParse->nested==0
|
|
+ && sqlite3Config.bInternalFunctions==0
|
|
+ ){
|
|
+ /* Internal-use-only functions are disallowed unless the
|
|
+ ** SQL is being compiled using sqlite3NestedParse() */
|
|
+ no_such_func = 1;
|
|
+ pDef = 0;
|
|
+ }
|
|
}
|
|
- if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
|
|
- sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
|
|
- pNC->nErr++;
|
|
- is_agg = 0;
|
|
- }else if( no_such_func && pParse->db->init.busy==0
|
|
+
|
|
+ if( 0==IN_RENAME_OBJECT ){
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
|
|
+ || (pDef->xValue==0 && pDef->xInverse==0)
|
|
+ || (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
|
|
+ );
|
|
+ if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
|
|
+ sqlite3ErrorMsg(pParse,
|
|
+ "%.*s() may not be used as a window function", nId, zId
|
|
+ );
|
|
+ pNC->nErr++;
|
|
+ }else if(
|
|
+ (is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
|
|
+ || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
|
|
+ || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
|
|
+ ){
|
|
+ const char *zType;
|
|
+ if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
|
|
+ zType = "window";
|
|
+ }else{
|
|
+ zType = "aggregate";
|
|
+ }
|
|
+ sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
|
|
+ pNC->nErr++;
|
|
+ is_agg = 0;
|
|
+ }
|
|
+#else
|
|
+ if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
|
|
+ sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId);
|
|
+ pNC->nErr++;
|
|
+ is_agg = 0;
|
|
+ }
|
|
+#endif
|
|
+ else if( no_such_func && pParse->db->init.busy==0
|
|
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
|
- && pParse->explain==0
|
|
+ && pParse->explain==0
|
|
#endif
|
|
- ){
|
|
- sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
|
|
- pNC->nErr++;
|
|
- }else if( wrong_num_args ){
|
|
- sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
|
|
- nId, zId);
|
|
- pNC->nErr++;
|
|
+ ){
|
|
+ sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
|
|
+ pNC->nErr++;
|
|
+ }else if( wrong_num_args ){
|
|
+ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
|
|
+ nId, zId);
|
|
+ pNC->nErr++;
|
|
+ }
|
|
+ if( is_agg ){
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg);
|
|
+#else
|
|
+ pNC->ncFlags &= ~NC_AllowAgg;
|
|
+#endif
|
|
+ }
|
|
}
|
|
- if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg;
|
|
sqlite3WalkExprList(pWalker, pList);
|
|
if( is_agg ){
|
|
- NameContext *pNC2 = pNC;
|
|
- pExpr->op = TK_AGG_FUNCTION;
|
|
- pExpr->op2 = 0;
|
|
- while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
|
|
- pExpr->op2++;
|
|
- pNC2 = pNC2->pNext;
|
|
- }
|
|
- assert( pDef!=0 );
|
|
- if( pNC2 ){
|
|
- assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
|
|
- testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
|
|
- pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pExpr->y.pWin ){
|
|
+ Select *pSel = pNC->pWinSelect;
|
|
+ sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition);
|
|
+ sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy);
|
|
+ sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
|
|
+ sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
|
|
+ if( 0==pSel->pWin
|
|
+ || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin)
|
|
+ ){
|
|
+ pExpr->y.pWin->pNextWin = pSel->pWin;
|
|
+ pSel->pWin = pExpr->y.pWin;
|
|
+ }
|
|
+ pNC->ncFlags |= NC_AllowWin;
|
|
+ }else
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+ {
|
|
+ NameContext *pNC2 = pNC;
|
|
+ pExpr->op = TK_AGG_FUNCTION;
|
|
+ pExpr->op2 = 0;
|
|
+ while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
|
|
+ pExpr->op2++;
|
|
+ pNC2 = pNC2->pNext;
|
|
+ }
|
|
+ assert( pDef!=0 );
|
|
+ if( pNC2 ){
|
|
+ assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
|
|
+ testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
|
|
+ pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
|
|
|
|
+ }
|
|
+ pNC->ncFlags |= NC_AllowAgg;
|
|
}
|
|
- pNC->ncFlags |= NC_AllowAgg;
|
|
}
|
|
/* FIX ME: Compute pExpr->affinity based on the expected return
|
|
** type of the function
|
|
@@ -90837,6 +95331,23 @@
|
|
notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
|
|
break;
|
|
}
|
|
+ case TK_IS:
|
|
+ case TK_ISNOT: {
|
|
+ Expr *pRight;
|
|
+ assert( !ExprHasProperty(pExpr, EP_Reduced) );
|
|
+ /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
|
|
+ ** and "x IS NOT FALSE". */
|
|
+ if( (pRight = pExpr->pRight)->op==TK_ID ){
|
|
+ int rc = resolveExprStep(pWalker, pRight);
|
|
+ if( rc==WRC_Abort ) return WRC_Abort;
|
|
+ if( pRight->op==TK_TRUEFALSE ){
|
|
+ pExpr->op2 = pExpr->op;
|
|
+ pExpr->op = TK_TRUTH;
|
|
+ return WRC_Continue;
|
|
+ }
|
|
+ }
|
|
+ /* Fall thru */
|
|
+ }
|
|
case TK_BETWEEN:
|
|
case TK_EQ:
|
|
case TK_NE:
|
|
@@ -90843,9 +95354,7 @@
|
|
case TK_LT:
|
|
case TK_LE:
|
|
case TK_GT:
|
|
- case TK_GE:
|
|
- case TK_IS:
|
|
- case TK_ISNOT: {
|
|
+ case TK_GE: {
|
|
int nLeft, nRight;
|
|
if( pParse->db->mallocFailed ) break;
|
|
assert( pExpr->pLeft!=0 );
|
|
@@ -90948,8 +95457,8 @@
|
|
memset(&nc, 0, sizeof(nc));
|
|
nc.pParse = pParse;
|
|
nc.pSrcList = pSelect->pSrc;
|
|
- nc.pEList = pEList;
|
|
- nc.ncFlags = NC_AllowAgg;
|
|
+ nc.uNC.pEList = pEList;
|
|
+ nc.ncFlags = NC_AllowAgg|NC_UEList;
|
|
nc.nErr = 0;
|
|
db = pParse->db;
|
|
savedSuppErr = db->suppressErr;
|
|
@@ -91014,12 +95523,10 @@
|
|
pOrderBy = pSelect->pOrderBy;
|
|
if( pOrderBy==0 ) return 0;
|
|
db = pParse->db;
|
|
-#if SQLITE_MAX_COLUMN
|
|
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
|
sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
|
|
return 1;
|
|
}
|
|
-#endif
|
|
for(i=0; i<pOrderBy->nExpr; i++){
|
|
pOrderBy->a[i].done = 0;
|
|
}
|
|
@@ -91111,12 +95618,10 @@
|
|
struct ExprList_item *pItem;
|
|
|
|
if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
|
|
-#if SQLITE_MAX_COLUMN
|
|
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
|
sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
|
|
return 1;
|
|
}
|
|
-#endif
|
|
pEList = pSelect->pEList;
|
|
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
|
|
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
|
|
@@ -91198,6 +95703,19 @@
|
|
}
|
|
for(j=0; j<pSelect->pEList->nExpr; j++){
|
|
if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( ExprHasProperty(pE, EP_WinFunc) ){
|
|
+ /* Since this window function is being changed into a reference
|
|
+ ** to the same window function the result set, remove the instance
|
|
+ ** of this window function from the Select.pWin list. */
|
|
+ Window **pp;
|
|
+ for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
|
|
+ if( *pp==pE->y.pWin ){
|
|
+ *pp = (*pp)->pNextWin;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
pItem->u.x.iOrderByCol = j+1;
|
|
}
|
|
}
|
|
@@ -91254,8 +95772,8 @@
|
|
*/
|
|
memset(&sNC, 0, sizeof(sNC));
|
|
sNC.pParse = pParse;
|
|
- if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
|
|
- sqlite3ResolveExprNames(&sNC, p->pOffset) ){
|
|
+ sNC.pWinSelect = p;
|
|
+ if( sqlite3ResolveExprNames(&sNC, p->pLimit) ){
|
|
return WRC_Abort;
|
|
}
|
|
|
|
@@ -91303,12 +95821,13 @@
|
|
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
|
|
** resolve the result-set expression list.
|
|
*/
|
|
- sNC.ncFlags = NC_AllowAgg;
|
|
+ sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
|
|
sNC.pSrcList = p->pSrc;
|
|
sNC.pNext = pOuterNC;
|
|
|
|
/* Resolve names in the result set. */
|
|
if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
|
|
+ sNC.ncFlags &= ~NC_AllowWin;
|
|
|
|
/* If there are no aggregate functions in the result-set, and no GROUP BY
|
|
** expression, do not allow aggregates in any of the other expressions.
|
|
@@ -91337,7 +95856,9 @@
|
|
** Minor point: If this is the case, then the expression will be
|
|
** re-evaluated for each reference to it.
|
|
*/
|
|
- sNC.pEList = p->pEList;
|
|
+ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
|
|
+ sNC.uNC.pEList = p->pEList;
|
|
+ sNC.ncFlags |= NC_UEList;
|
|
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
|
|
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
|
|
|
|
@@ -91355,7 +95876,7 @@
|
|
** outer queries
|
|
*/
|
|
sNC.pNext = 0;
|
|
- sNC.ncFlags |= NC_AllowAgg;
|
|
+ sNC.ncFlags |= NC_AllowAgg|NC_AllowWin;
|
|
|
|
/* If this is a converted compound query, move the ORDER BY clause from
|
|
** the sub-query back to the parent query. At this point each term
|
|
@@ -91386,6 +95907,7 @@
|
|
if( db->mallocFailed ){
|
|
return WRC_Abort;
|
|
}
|
|
+ sNC.ncFlags &= ~NC_AllowWin;
|
|
|
|
/* Resolve the GROUP BY clause. At the same time, make sure
|
|
** the GROUP BY clause does not contain aggregate functions.
|
|
@@ -91570,7 +96092,7 @@
|
|
Table *pTab, /* The table being referenced */
|
|
int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
|
|
Expr *pExpr, /* Expression to resolve. May be NULL. */
|
|
- ExprList *pList /* Expression list to resolve. May be NUL. */
|
|
+ ExprList *pList /* Expression list to resolve. May be NULL. */
|
|
){
|
|
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
|
|
NameContext sNC; /* Name context for pParse->pNewTable */
|
|
@@ -91651,8 +96173,8 @@
|
|
return sqlite3AffinityType(pExpr->u.zToken, 0);
|
|
}
|
|
#endif
|
|
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->pTab ){
|
|
- return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);
|
|
+ if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){
|
|
+ return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
|
|
}
|
|
if( op==TK_SELECT_COLUMN ){
|
|
assert( pExpr->pLeft->flags&EP_xIsSelect );
|
|
@@ -91717,6 +96239,11 @@
|
|
** Return the collation sequence for the expression pExpr. If
|
|
** there is no defined collating sequence, return NULL.
|
|
**
|
|
+** See also: sqlite3ExprNNCollSeq()
|
|
+**
|
|
+** The sqlite3ExprNNCollSeq() works the same exact that it returns the
|
|
+** default collation if pExpr has no defined collation.
|
|
+**
|
|
** The collating sequence might be determined by a COLLATE operator
|
|
** or by the presence of a column with a defined collating sequence.
|
|
** COLLATE operators take first precedence. Left operands take
|
|
@@ -91729,27 +96256,27 @@
|
|
while( p ){
|
|
int op = p->op;
|
|
if( p->flags & EP_Generic ) break;
|
|
- if( op==TK_CAST || op==TK_UPLUS ){
|
|
- p = p->pLeft;
|
|
- continue;
|
|
- }
|
|
- if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){
|
|
- pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
|
|
- break;
|
|
- }
|
|
if( (op==TK_AGG_COLUMN || op==TK_COLUMN
|
|
|| op==TK_REGISTER || op==TK_TRIGGER)
|
|
- && p->pTab!=0
|
|
+ && p->y.pTab!=0
|
|
){
|
|
- /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
|
|
+ /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally
|
|
** a TK_COLUMN but was previously evaluated and cached in a register */
|
|
int j = p->iColumn;
|
|
if( j>=0 ){
|
|
- const char *zColl = p->pTab->aCol[j].zColl;
|
|
+ const char *zColl = p->y.pTab->aCol[j].zColl;
|
|
pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
|
|
}
|
|
break;
|
|
}
|
|
+ if( op==TK_CAST || op==TK_UPLUS ){
|
|
+ p = p->pLeft;
|
|
+ continue;
|
|
+ }
|
|
+ if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){
|
|
+ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
|
|
+ break;
|
|
+ }
|
|
if( p->flags & EP_Collate ){
|
|
if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){
|
|
p = p->pLeft;
|
|
@@ -91782,6 +96309,32 @@
|
|
}
|
|
|
|
/*
|
|
+** Return the collation sequence for the expression pExpr. If
|
|
+** there is no defined collating sequence, return a pointer to the
|
|
+** defautl collation sequence.
|
|
+**
|
|
+** See also: sqlite3ExprCollSeq()
|
|
+**
|
|
+** The sqlite3ExprCollSeq() routine works the same except that it
|
|
+** returns NULL if there is no defined collation.
|
|
+*/
|
|
+SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
|
|
+ CollSeq *p = sqlite3ExprCollSeq(pParse, pExpr);
|
|
+ if( p==0 ) p = pParse->db->pDfltColl;
|
|
+ assert( p!=0 );
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return TRUE if the two expressions have equivalent collating sequences.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
|
|
+ CollSeq *pColl1 = sqlite3ExprNNCollSeq(pParse, pE1);
|
|
+ CollSeq *pColl2 = sqlite3ExprNNCollSeq(pParse, pE2);
|
|
+ return sqlite3StrICmp(pColl1->zName, pColl2->zName)==0;
|
|
+}
|
|
+
|
|
+/*
|
|
** pExpr is an operand of a comparison operator. aff2 is the
|
|
** type affinity of the other operand. This routine returns the
|
|
** type affinity that should be used for the comparison operator.
|
|
@@ -92143,7 +96696,6 @@
|
|
Expr *pL, *pR;
|
|
int r1, r2;
|
|
assert( i>=0 && i<nLeft );
|
|
- if( i>0 ) sqlite3ExprCachePush(pParse);
|
|
r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1);
|
|
r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2);
|
|
codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5);
|
|
@@ -92155,7 +96707,6 @@
|
|
testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
|
|
sqlite3ReleaseTempReg(pParse, regFree1);
|
|
sqlite3ReleaseTempReg(pParse, regFree2);
|
|
- if( i>0 ) sqlite3ExprCachePop(pParse);
|
|
if( i==nLeft-1 ){
|
|
break;
|
|
}
|
|
@@ -92220,16 +96771,15 @@
|
|
}
|
|
}
|
|
}
|
|
-static void heightOfSelect(Select *p, int *pnHeight){
|
|
- if( p ){
|
|
+static void heightOfSelect(Select *pSelect, int *pnHeight){
|
|
+ Select *p;
|
|
+ for(p=pSelect; p; p=p->pPrior){
|
|
heightOfExpr(p->pWhere, pnHeight);
|
|
heightOfExpr(p->pHaving, pnHeight);
|
|
heightOfExpr(p->pLimit, pnHeight);
|
|
- heightOfExpr(p->pOffset, pnHeight);
|
|
heightOfExprList(p->pEList, pnHeight);
|
|
heightOfExprList(p->pGroupBy, pnHeight);
|
|
heightOfExprList(p->pOrderBy, pnHeight);
|
|
- heightOfSelect(p->pPrior, pnHeight);
|
|
}
|
|
}
|
|
|
|
@@ -92368,7 +96918,7 @@
|
|
){
|
|
Token x;
|
|
x.z = zToken;
|
|
- x.n = zToken ? sqlite3Strlen30(zToken) : 0;
|
|
+ x.n = sqlite3Strlen30(zToken);
|
|
return sqlite3ExprAlloc(db, op, &x, 0);
|
|
}
|
|
|
|
@@ -92504,7 +97054,12 @@
|
|
** Construct a new expression node for a function with multiple
|
|
** arguments.
|
|
*/
|
|
-SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
|
|
+SQLITE_PRIVATE Expr *sqlite3ExprFunction(
|
|
+ Parse *pParse, /* Parsing context */
|
|
+ ExprList *pList, /* Argument list */
|
|
+ Token *pToken, /* Name of the function */
|
|
+ int eDistinct /* SF_Distinct or SF_ALL or 0 */
|
|
+){
|
|
Expr *pNew;
|
|
sqlite3 *db = pParse->db;
|
|
assert( pToken );
|
|
@@ -92513,9 +97068,14 @@
|
|
sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */
|
|
return 0;
|
|
}
|
|
+ if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
|
|
+ sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken);
|
|
+ }
|
|
pNew->x.pList = pList;
|
|
+ ExprSetProperty(pNew, EP_HasFunc);
|
|
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
|
|
sqlite3ExprSetHeightAndFlags(pParse, pNew);
|
|
+ if( eDistinct==SF_Distinct ) ExprSetProperty(pNew, EP_Distinct);
|
|
return pNew;
|
|
}
|
|
|
|
@@ -92607,6 +97167,10 @@
|
|
assert( p!=0 );
|
|
/* Sanity check: Assert that the IntValue is non-negative if it exists */
|
|
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
|
|
+
|
|
+ assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed );
|
|
+ assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced)
|
|
+ || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) );
|
|
#ifdef SQLITE_DEBUG
|
|
if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
|
|
assert( p->pLeft==0 );
|
|
@@ -92625,6 +97189,10 @@
|
|
}else{
|
|
sqlite3ExprListDelete(db, p->x.pList);
|
|
}
|
|
+ if( ExprHasProperty(p, EP_WinFunc) ){
|
|
+ assert( p->op==TK_FUNCTION );
|
|
+ sqlite3WindowDelete(db, p->y.pWin);
|
|
+ }
|
|
}
|
|
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
|
|
if( !ExprHasProperty(p, EP_Static) ){
|
|
@@ -92673,7 +97241,7 @@
|
|
** Note that with flags==EXPRDUP_REDUCE, this routines works on full-size
|
|
** (unreduced) Expr objects as they or originally constructed by the parser.
|
|
** During expression analysis, extra information is computed and moved into
|
|
-** later parts of teh Expr object and that extra information might get chopped
|
|
+** later parts of the Expr object and that extra information might get chopped
|
|
** off if the expression is reduced. Note also that it does not work to
|
|
** make an EXPRDUP_REDUCE copy of a reduced expression. It is only legal
|
|
** to reduce a pristine expression tree from the parser. The implementation
|
|
@@ -92685,7 +97253,11 @@
|
|
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
|
|
assert( EXPR_FULLSIZE<=0xfff );
|
|
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
|
|
- if( 0==flags || p->op==TK_SELECT_COLUMN ){
|
|
+ if( 0==flags || p->op==TK_SELECT_COLUMN
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ || ExprHasProperty(p, EP_WinFunc)
|
|
+#endif
|
|
+ ){
|
|
nSize = EXPR_FULLSIZE;
|
|
}else{
|
|
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
|
|
@@ -92710,7 +97282,7 @@
|
|
static int dupedExprNodeSize(Expr *p, int flags){
|
|
int nByte = dupedExprStructSize(p, flags) & 0xfff;
|
|
if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
|
|
- nByte += sqlite3Strlen30(p->u.zToken)+1;
|
|
+ nByte += sqlite3Strlen30NN(p->u.zToken)+1;
|
|
}
|
|
return ROUND8(nByte);
|
|
}
|
|
@@ -92813,7 +97385,7 @@
|
|
}
|
|
|
|
/* Fill in pNew->pLeft and pNew->pRight. */
|
|
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
|
|
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
|
|
zAlloc += dupedExprNodeSize(p, dupFlags);
|
|
if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
|
|
pNew->pLeft = p->pLeft ?
|
|
@@ -92821,6 +97393,12 @@
|
|
pNew->pRight = p->pRight ?
|
|
exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( ExprHasProperty(p, EP_WinFunc) ){
|
|
+ pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
|
|
+ assert( ExprHasProperty(pNew, EP_WinFunc) );
|
|
+ }
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
if( pzBuffer ){
|
|
*pzBuffer = zAlloc;
|
|
}
|
|
@@ -92895,10 +97473,9 @@
|
|
Expr *pPriorSelectCol = 0;
|
|
assert( db!=0 );
|
|
if( p==0 ) return 0;
|
|
- pNew = sqlite3DbMallocRawNN(db,
|
|
- sizeof(*pNew)+sizeof(pNew->a[0])*(p->nExpr-1) );
|
|
+ pNew = sqlite3DbMallocRawNN(db, sqlite3DbMallocSize(db, p));
|
|
if( pNew==0 ) return 0;
|
|
- pNew->nAlloc = pNew->nExpr = p->nExpr;
|
|
+ pNew->nExpr = p->nExpr;
|
|
pItem = pNew->a;
|
|
pOldItem = p->a;
|
|
for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
|
|
@@ -92926,6 +97503,7 @@
|
|
pItem->sortOrder = pOldItem->sortOrder;
|
|
pItem->done = 0;
|
|
pItem->bSpanIsTab = pOldItem->bSpanIsTab;
|
|
+ pItem->bSorterRef = pOldItem->bSorterRef;
|
|
pItem->u = pOldItem->u;
|
|
}
|
|
return pNew;
|
|
@@ -93024,7 +97602,6 @@
|
|
pNew->pNext = pNext;
|
|
pNew->pPrior = 0;
|
|
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
|
|
- pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
|
|
pNew->iLimit = 0;
|
|
pNew->iOffset = 0;
|
|
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
|
|
@@ -93032,7 +97609,11 @@
|
|
pNew->addrOpenEphm[1] = -1;
|
|
pNew->nSelectRow = p->nSelectRow;
|
|
pNew->pWith = withDup(db, p->pWith);
|
|
- sqlite3SelectSetName(pNew, p->zSelName);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ pNew->pWin = 0;
|
|
+ pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn);
|
|
+#endif
|
|
+ pNew->selId = p->selId;
|
|
*pp = pNew;
|
|
pp = &pNew->pPrior;
|
|
pNext = pNew;
|
|
@@ -93052,6 +97633,13 @@
|
|
** Add a new element to the end of an expression list. If pList is
|
|
** initially NULL, then create a new expression list.
|
|
**
|
|
+** The pList argument must be either NULL or a pointer to an ExprList
|
|
+** obtained from a prior call to sqlite3ExprListAppend(). This routine
|
|
+** may not be used with an ExprList obtained from sqlite3ExprListDup().
|
|
+** Reason: This routine assumes that the number of slots in pList->a[]
|
|
+** is a power of two. That is true for sqlite3ExprListAppend() returns
|
|
+** but is not necessarily true from the return value of sqlite3ExprListDup().
|
|
+**
|
|
** If a memory allocation error occurs, the entire list is freed and
|
|
** NULL is returned. If non-NULL is returned, then it is guaranteed
|
|
** that the new entry was successfully appended.
|
|
@@ -93070,16 +97658,14 @@
|
|
goto no_mem;
|
|
}
|
|
pList->nExpr = 0;
|
|
- pList->nAlloc = 1;
|
|
- }else if( pList->nExpr==pList->nAlloc ){
|
|
+ }else if( (pList->nExpr & (pList->nExpr-1))==0 ){
|
|
ExprList *pNew;
|
|
pNew = sqlite3DbRealloc(db, pList,
|
|
- sizeof(*pList)+(2*pList->nAlloc - 1)*sizeof(pList->a[0]));
|
|
+ sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0]));
|
|
if( pNew==0 ){
|
|
goto no_mem;
|
|
}
|
|
pList = pNew;
|
|
- pList->nAlloc *= 2;
|
|
}
|
|
pItem = &pList->a[pList->nExpr++];
|
|
assert( offsetof(struct ExprList_item,zName)==sizeof(pItem->pExpr) );
|
|
@@ -93199,6 +97785,9 @@
|
|
assert( pItem->zName==0 );
|
|
pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
|
|
if( dequote ) sqlite3Dequote(pItem->zName);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenMap(pParse, (void*)pItem->zName, pName);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -93213,7 +97802,8 @@
|
|
SQLITE_PRIVATE void sqlite3ExprListSetSpan(
|
|
Parse *pParse, /* Parsing context */
|
|
ExprList *pList, /* List to which to add the span. */
|
|
- ExprSpan *pSpan /* The span to be added */
|
|
+ const char *zStart, /* Start of the span */
|
|
+ const char *zEnd /* End of the span */
|
|
){
|
|
sqlite3 *db = pParse->db;
|
|
assert( pList!=0 || db->mallocFailed!=0 );
|
|
@@ -93220,10 +97810,8 @@
|
|
if( pList ){
|
|
struct ExprList_item *pItem = &pList->a[pList->nExpr-1];
|
|
assert( pList->nExpr>0 );
|
|
- assert( db->mallocFailed || pItem->pExpr==pSpan->pExpr );
|
|
sqlite3DbFree(db, pItem->zSpan);
|
|
- pItem->zSpan = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
|
- (int)(pSpan->zEnd - pSpan->zStart));
|
|
+ pItem->zSpan = sqlite3DbSpanDup(db, zStart, zEnd);
|
|
}
|
|
}
|
|
|
|
@@ -93270,17 +97858,57 @@
|
|
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
|
|
int i;
|
|
u32 m = 0;
|
|
- if( pList ){
|
|
- for(i=0; i<pList->nExpr; i++){
|
|
- Expr *pExpr = pList->a[i].pExpr;
|
|
- assert( pExpr!=0 );
|
|
- m |= pExpr->flags;
|
|
- }
|
|
+ assert( pList!=0 );
|
|
+ for(i=0; i<pList->nExpr; i++){
|
|
+ Expr *pExpr = pList->a[i].pExpr;
|
|
+ assert( pExpr!=0 );
|
|
+ m |= pExpr->flags;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
/*
|
|
+** This is a SELECT-node callback for the expression walker that
|
|
+** always "fails". By "fail" in this case, we mean set
|
|
+** pWalker->eCode to zero and abort.
|
|
+**
|
|
+** This callback is used by multiple expression walkers.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker *pWalker, Select *NotUsed){
|
|
+ UNUSED_PARAMETER(NotUsed);
|
|
+ pWalker->eCode = 0;
|
|
+ return WRC_Abort;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If the input expression is an ID with the name "true" or "false"
|
|
+** then convert it into an TK_TRUEFALSE term. Return non-zero if
|
|
+** the conversion happened, and zero if the expression is unaltered.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){
|
|
+ assert( pExpr->op==TK_ID || pExpr->op==TK_STRING );
|
|
+ if( sqlite3StrICmp(pExpr->u.zToken, "true")==0
|
|
+ || sqlite3StrICmp(pExpr->u.zToken, "false")==0
|
|
+ ){
|
|
+ pExpr->op = TK_TRUEFALSE;
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The argument must be a TK_TRUEFALSE Expr node. Return 1 if it is TRUE
|
|
+** and 0 if it is FALSE.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){
|
|
+ assert( pExpr->op==TK_TRUEFALSE );
|
|
+ assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0
|
|
+ || sqlite3StrICmp(pExpr->u.zToken,"false")==0 );
|
|
+ return pExpr->u.zToken[4]==0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
** These routines are Walker callbacks used to check expressions to
|
|
** see if they are "constant" for some definition of constant. The
|
|
** Walker.eCode value determines the type of "constant" we are looking
|
|
@@ -93327,6 +97955,12 @@
|
|
return WRC_Abort;
|
|
}
|
|
case TK_ID:
|
|
+ /* Convert "true" or "false" in a DEFAULT clause into the
|
|
+ ** appropriate TK_TRUEFALSE operator */
|
|
+ if( sqlite3ExprIdToTrueFalse(pExpr) ){
|
|
+ return WRC_Prune;
|
|
+ }
|
|
+ /* Fall thru */
|
|
case TK_COLUMN:
|
|
case TK_AGG_FUNCTION:
|
|
case TK_AGG_COLUMN:
|
|
@@ -93334,11 +97968,16 @@
|
|
testcase( pExpr->op==TK_COLUMN );
|
|
testcase( pExpr->op==TK_AGG_FUNCTION );
|
|
testcase( pExpr->op==TK_AGG_COLUMN );
|
|
+ if( ExprHasProperty(pExpr, EP_FixedCol) && pWalker->eCode!=2 ){
|
|
+ return WRC_Continue;
|
|
+ }
|
|
if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
|
|
return WRC_Continue;
|
|
}
|
|
/* Fall through */
|
|
case TK_IF_NULL_ROW:
|
|
+ case TK_REGISTER:
|
|
+ testcase( pExpr->op==TK_REGISTER );
|
|
testcase( pExpr->op==TK_IF_NULL_ROW );
|
|
pWalker->eCode = 0;
|
|
return WRC_Abort;
|
|
@@ -93356,21 +97995,16 @@
|
|
}
|
|
/* Fall through */
|
|
default:
|
|
- testcase( pExpr->op==TK_SELECT ); /* selectNodeIsConstant will disallow */
|
|
- testcase( pExpr->op==TK_EXISTS ); /* selectNodeIsConstant will disallow */
|
|
+ testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
|
|
+ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
|
|
return WRC_Continue;
|
|
}
|
|
}
|
|
-static int selectNodeIsConstant(Walker *pWalker, Select *NotUsed){
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
- pWalker->eCode = 0;
|
|
- return WRC_Abort;
|
|
-}
|
|
static int exprIsConst(Expr *p, int initFlag, int iCur){
|
|
Walker w;
|
|
w.eCode = initFlag;
|
|
w.xExprCallback = exprNodeIsConstant;
|
|
- w.xSelectCallback = selectNodeIsConstant;
|
|
+ w.xSelectCallback = sqlite3SelectWalkFail;
|
|
#ifdef SQLITE_DEBUG
|
|
w.xSelectCallback2 = sqlite3SelectWalkAssert2;
|
|
#endif
|
|
@@ -93392,10 +98026,17 @@
|
|
}
|
|
|
|
/*
|
|
-** Walk an expression tree. Return non-zero if the expression is constant
|
|
-** that does no originate from the ON or USING clauses of a join.
|
|
-** Return 0 if it involves variables or function calls or terms from
|
|
-** an ON or USING clause.
|
|
+** Walk an expression tree. Return non-zero if
|
|
+**
|
|
+** (1) the expression is constant, and
|
|
+** (2) the expression does originate in the ON or USING clause
|
|
+** of a LEFT JOIN, and
|
|
+** (3) the expression does not contain any EP_FixedCol TK_COLUMN
|
|
+** operands created by the constant propagation optimization.
|
|
+**
|
|
+** When this routine returns true, it indicates that the expression
|
|
+** can be added to the pParse->pConstExpr list and evaluated once when
|
|
+** the prepared statement starts up. See sqlite3ExprCodeAtInit().
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
|
|
return exprIsConst(p, 2, 0);
|
|
@@ -93424,8 +98065,8 @@
|
|
for(i=0; i<pGroupBy->nExpr; i++){
|
|
Expr *p = pGroupBy->a[i].pExpr;
|
|
if( sqlite3ExprCompare(0, pExpr, p, -1)<2 ){
|
|
- CollSeq *pColl = sqlite3ExprCollSeq(pWalker->pParse, p);
|
|
- if( pColl==0 || sqlite3_stricmp("BINARY", pColl->zName)==0 ){
|
|
+ CollSeq *pColl = sqlite3ExprNNCollSeq(pWalker->pParse, p);
|
|
+ if( sqlite3IsBinary(pColl) ){
|
|
return WRC_Prune;
|
|
}
|
|
}
|
|
@@ -93493,7 +98134,7 @@
|
|
Walker w;
|
|
w.eCode = 1;
|
|
w.xExprCallback = sqlite3ExprWalkNoop;
|
|
- w.xSelectCallback = selectNodeIsConstant;
|
|
+ w.xSelectCallback = sqlite3SelectWalkFail;
|
|
#ifdef SQLITE_DEBUG
|
|
w.xSelectCallback2 = sqlite3SelectWalkAssert2;
|
|
#endif
|
|
@@ -93566,9 +98207,9 @@
|
|
case TK_BLOB:
|
|
return 0;
|
|
case TK_COLUMN:
|
|
- assert( p->pTab!=0 );
|
|
return ExprHasProperty(p, EP_CanBeNull) ||
|
|
- (p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0);
|
|
+ p->y.pTab==0 || /* Reference to column of index on expression */
|
|
+ (p->iColumn>=0 && p->y.pTab->aCol[p->iColumn].notNull==0);
|
|
default:
|
|
return 1;
|
|
}
|
|
@@ -93623,6 +98264,14 @@
|
|
if( sqlite3StrICmp(z, "OID")==0 ) return 1;
|
|
return 0;
|
|
}
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE int sqlite3IsRowidN(const char *z, int n){
|
|
+ if( sqlite3StrNICmp(z, "_ROWID_", n)==0 ) return 1;
|
|
+ if( sqlite3StrNICmp(z, "ROWID", n)==0 ) return 1;
|
|
+ if( sqlite3StrNICmp(z, "OID", n)==0 ) return 1;
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
|
|
/*
|
|
** pX is the RHS of an IN operator. If pX is a SELECT statement
|
|
@@ -93649,7 +98298,6 @@
|
|
}
|
|
assert( p->pGroupBy==0 ); /* Has no GROUP BY clause */
|
|
if( p->pLimit ) return 0; /* Has no LIMIT clause */
|
|
- assert( p->pOffset==0 ); /* No LIMIT means no OFFSET */
|
|
if( p->pWhere ) return 0; /* Has no WHERE clause */
|
|
pSrc = p->pSrc;
|
|
assert( pSrc!=0 );
|
|
@@ -93739,16 +98387,15 @@
|
|
** pX->iTable made to point to the ephemeral table instead of an
|
|
** existing table.
|
|
**
|
|
-** The inFlags parameter must contain exactly one of the bits
|
|
-** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP. If inFlags contains
|
|
-** IN_INDEX_MEMBERSHIP, then the generated table will be used for a
|
|
-** fast membership test. When the IN_INDEX_LOOP bit is set, the
|
|
-** IN index will be used to loop over all values of the RHS of the
|
|
-** IN operator.
|
|
+** The inFlags parameter must contain, at a minimum, one of the bits
|
|
+** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP but not both. If inFlags contains
|
|
+** IN_INDEX_MEMBERSHIP, then the generated table will be used for a fast
|
|
+** membership test. When the IN_INDEX_LOOP bit is set, the IN index will
|
|
+** be used to loop over all values of the RHS of the IN operator.
|
|
**
|
|
** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate
|
|
** through the set members) then the b-tree must not contain duplicates.
|
|
-** An epheremal table must be used unless the selected columns are guaranteed
|
|
+** An epheremal table will be created unless the selected columns are guaranteed
|
|
** to be unique - either because it is an INTEGER PRIMARY KEY or due to
|
|
** a UNIQUE constraint or index.
|
|
**
|
|
@@ -93849,7 +98496,8 @@
|
|
|
|
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
|
eType = IN_INDEX_ROWID;
|
|
-
|
|
+ ExplainQueryPlan((pParse, 0,
|
|
+ "USING ROWID SEARCH ON TABLE %s FOR IN-OPERATOR",pTab->zName));
|
|
sqlite3VdbeJumpHere(v, iAddr);
|
|
}else{
|
|
Index *pIdx; /* Iterator variable */
|
|
@@ -93928,11 +98576,8 @@
|
|
if( colUsed==(MASKBIT(nExpr)-1) ){
|
|
/* If we reach this point, that means the index pIdx is usable */
|
|
int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
|
-#ifndef SQLITE_OMIT_EXPLAIN
|
|
- sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
|
|
- sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
|
|
- P4_DYNAMIC);
|
|
-#endif
|
|
+ ExplainQueryPlan((pParse, 0,
|
|
+ "USING INDEX %s FOR IN-OPERATOR",pIdx->zName));
|
|
sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
|
|
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
|
VdbeComment((v, "%s", pIdx->zName));
|
|
@@ -94111,7 +98756,6 @@
|
|
int rReg = 0; /* Register storing resulting */
|
|
Vdbe *v = sqlite3GetVdbe(pParse);
|
|
if( NEVER(v==0) ) return 0;
|
|
- sqlite3ExprCachePush(pParse);
|
|
|
|
/* The evaluation of the IN/EXISTS/SELECT must be repeated every time it
|
|
** is encountered if any of the following is true:
|
|
@@ -94127,17 +98771,6 @@
|
|
jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
|
}
|
|
|
|
-#ifndef SQLITE_OMIT_EXPLAIN
|
|
- if( pParse->explain==2 ){
|
|
- char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %s%s SUBQUERY %d",
|
|
- jmpIfDynamic>=0?"":"CORRELATED ",
|
|
- pExpr->op==TK_IN?"LIST":"SCALAR",
|
|
- pParse->iNextSelectId
|
|
- );
|
|
- sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
|
- }
|
|
-#endif
|
|
-
|
|
switch( pExpr->op ){
|
|
case TK_IN: {
|
|
int addr; /* Address of OP_OpenEphemeral instruction */
|
|
@@ -94175,6 +98808,9 @@
|
|
Select *pSelect = pExpr->x.pSelect;
|
|
ExprList *pEList = pSelect->pEList;
|
|
|
|
+ ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
|
|
+ jmpIfDynamic>=0?"":"CORRELATED "
|
|
+ ));
|
|
assert( !isRowid );
|
|
/* If the LHS and RHS of the IN operator do not match, that
|
|
** error will have been caught long before we reach this point. */
|
|
@@ -94216,7 +98852,6 @@
|
|
ExprList *pList = pExpr->x.pList;
|
|
struct ExprList_item *pItem;
|
|
int r1, r2, r3;
|
|
-
|
|
affinity = sqlite3ExprAffinity(pLeft);
|
|
if( !affinity ){
|
|
affinity = SQLITE_AFF_BLOB;
|
|
@@ -94229,7 +98864,7 @@
|
|
/* Loop through each expression in <exprlist>. */
|
|
r1 = sqlite3GetTempReg(pParse);
|
|
r2 = sqlite3GetTempReg(pParse);
|
|
- if( isRowid ) sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
|
|
+ if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC);
|
|
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
|
|
Expr *pE2 = pItem->pExpr;
|
|
int iValToIns;
|
|
@@ -94256,7 +98891,6 @@
|
|
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
|
|
}else{
|
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
|
|
- sqlite3ExprCacheAffinityChange(pParse, r3, 1);
|
|
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1);
|
|
}
|
|
}
|
|
@@ -94289,6 +98923,7 @@
|
|
Select *pSel; /* SELECT statement to encode */
|
|
SelectDest dest; /* How to deal with SELECT result */
|
|
int nReg; /* Registers to allocate */
|
|
+ Expr *pLimit; /* New limit expression */
|
|
|
|
testcase( pExpr->op==TK_EXISTS );
|
|
testcase( pExpr->op==TK_SELECT );
|
|
@@ -94296,6 +98931,8 @@
|
|
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
|
|
|
pSel = pExpr->x.pSelect;
|
|
+ ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
|
|
+ jmpIfDynamic>=0?"":"CORRELATED "));
|
|
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
|
|
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
|
|
pParse->nMem += nReg;
|
|
@@ -94310,11 +98947,14 @@
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
|
|
VdbeComment((v, "Init EXISTS result"));
|
|
}
|
|
- sqlite3ExprDelete(pParse->db, pSel->pLimit);
|
|
- pSel->pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,
|
|
- &sqlite3IntTokens[1], 0);
|
|
+ pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0);
|
|
+ if( pSel->pLimit ){
|
|
+ sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft);
|
|
+ pSel->pLimit->pLeft = pLimit;
|
|
+ }else{
|
|
+ pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
|
|
+ }
|
|
pSel->iLimit = 0;
|
|
- pSel->selFlags &= ~SF_MultiValue;
|
|
if( sqlite3Select(pParse, pSel, &dest) ){
|
|
return 0;
|
|
}
|
|
@@ -94331,7 +98971,6 @@
|
|
if( jmpIfDynamic>=0 ){
|
|
sqlite3VdbeJumpHere(v, jmpIfDynamic);
|
|
}
|
|
- sqlite3ExprCachePop(pParse);
|
|
|
|
return rReg;
|
|
}
|
|
@@ -94450,7 +99089,6 @@
|
|
** aiMap[] array contains a mapping from the original LHS field order to
|
|
** the field order that matches the RHS index.
|
|
*/
|
|
- sqlite3ExprCachePush(pParse);
|
|
rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
|
|
for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
|
|
if( i==nVector ){
|
|
@@ -94609,7 +99247,6 @@
|
|
|
|
sqlite3ExprCodeIN_finished:
|
|
if( rLhs!=rLhsOrig ) sqlite3ReleaseTempReg(pParse, rLhs);
|
|
- sqlite3ExprCachePop(pParse);
|
|
VdbeComment((v, "end IN expr"));
|
|
sqlite3ExprCodeIN_oom_error:
|
|
sqlite3DbFree(pParse->db, aiMap);
|
|
@@ -94657,7 +99294,7 @@
|
|
const char *z = pExpr->u.zToken;
|
|
assert( z!=0 );
|
|
c = sqlite3DecOrHexToI64(z, &value);
|
|
- if( c==1 || (c==2 && !negFlag) || (negFlag && value==SMALLEST_INT64)){
|
|
+ if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){
|
|
#ifdef SQLITE_OMIT_FLOATING_POINT
|
|
sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
|
|
#else
|
|
@@ -94671,152 +99308,13 @@
|
|
}
|
|
#endif
|
|
}else{
|
|
- if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; }
|
|
+ if( negFlag ){ value = c==3 ? SMALLEST_INT64 : -value; }
|
|
sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, iMem, 0, (u8*)&value, P4_INT64);
|
|
}
|
|
}
|
|
}
|
|
|
|
-/*
|
|
-** Erase column-cache entry number i
|
|
-*/
|
|
-static void cacheEntryClear(Parse *pParse, int i){
|
|
- if( pParse->aColCache[i].tempReg ){
|
|
- if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
|
|
- pParse->aTempReg[pParse->nTempReg++] = pParse->aColCache[i].iReg;
|
|
- }
|
|
- }
|
|
- pParse->nColCache--;
|
|
- if( i<pParse->nColCache ){
|
|
- pParse->aColCache[i] = pParse->aColCache[pParse->nColCache];
|
|
- }
|
|
-}
|
|
|
|
-
|
|
-/*
|
|
-** Record in the column cache that a particular column from a
|
|
-** particular table is stored in a particular register.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){
|
|
- int i;
|
|
- int minLru;
|
|
- int idxLru;
|
|
- struct yColCache *p;
|
|
-
|
|
- /* Unless an error has occurred, register numbers are always positive. */
|
|
- assert( iReg>0 || pParse->nErr || pParse->db->mallocFailed );
|
|
- assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */
|
|
-
|
|
- /* The SQLITE_ColumnCache flag disables the column cache. This is used
|
|
- ** for testing only - to verify that SQLite always gets the same answer
|
|
- ** with and without the column cache.
|
|
- */
|
|
- if( OptimizationDisabled(pParse->db, SQLITE_ColumnCache) ) return;
|
|
-
|
|
- /* First replace any existing entry.
|
|
- **
|
|
- ** Actually, the way the column cache is currently used, we are guaranteed
|
|
- ** that the object will never already be in cache. Verify this guarantee.
|
|
- */
|
|
-#ifndef NDEBUG
|
|
- for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
|
|
- assert( p->iTable!=iTab || p->iColumn!=iCol );
|
|
- }
|
|
-#endif
|
|
-
|
|
- /* If the cache is already full, delete the least recently used entry */
|
|
- if( pParse->nColCache>=SQLITE_N_COLCACHE ){
|
|
- minLru = 0x7fffffff;
|
|
- idxLru = -1;
|
|
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
|
- if( p->lru<minLru ){
|
|
- idxLru = i;
|
|
- minLru = p->lru;
|
|
- }
|
|
- }
|
|
- p = &pParse->aColCache[idxLru];
|
|
- }else{
|
|
- p = &pParse->aColCache[pParse->nColCache++];
|
|
- }
|
|
-
|
|
- /* Add the new entry to the end of the cache */
|
|
- p->iLevel = pParse->iCacheLevel;
|
|
- p->iTable = iTab;
|
|
- p->iColumn = iCol;
|
|
- p->iReg = iReg;
|
|
- p->tempReg = 0;
|
|
- p->lru = pParse->iCacheCnt++;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Indicate that registers between iReg..iReg+nReg-1 are being overwritten.
|
|
-** Purge the range of registers from the column cache.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){
|
|
- int i = 0;
|
|
- while( i<pParse->nColCache ){
|
|
- struct yColCache *p = &pParse->aColCache[i];
|
|
- if( p->iReg >= iReg && p->iReg < iReg+nReg ){
|
|
- cacheEntryClear(pParse, i);
|
|
- }else{
|
|
- i++;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
-** Remember the current column cache context. Any new entries added
|
|
-** added to the column cache after this call are removed when the
|
|
-** corresponding pop occurs.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){
|
|
- pParse->iCacheLevel++;
|
|
-#ifdef SQLITE_DEBUG
|
|
- if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
|
|
- printf("PUSH to %d\n", pParse->iCacheLevel);
|
|
- }
|
|
-#endif
|
|
-}
|
|
-
|
|
-/*
|
|
-** Remove from the column cache any entries that were added since the
|
|
-** the previous sqlite3ExprCachePush operation. In other words, restore
|
|
-** the cache to the state it was in prior the most recent Push.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse){
|
|
- int i = 0;
|
|
- assert( pParse->iCacheLevel>=1 );
|
|
- pParse->iCacheLevel--;
|
|
-#ifdef SQLITE_DEBUG
|
|
- if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
|
|
- printf("POP to %d\n", pParse->iCacheLevel);
|
|
- }
|
|
-#endif
|
|
- while( i<pParse->nColCache ){
|
|
- if( pParse->aColCache[i].iLevel>pParse->iCacheLevel ){
|
|
- cacheEntryClear(pParse, i);
|
|
- }else{
|
|
- i++;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
-** When a cached column is reused, make sure that its register is
|
|
-** no longer available as a temp register. ticket #3879: that same
|
|
-** register might be in the cache in multiple places, so be sure to
|
|
-** get them all.
|
|
-*/
|
|
-static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){
|
|
- int i;
|
|
- struct yColCache *p;
|
|
- for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
|
|
- if( p->iReg==iReg ){
|
|
- p->tempReg = 0;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
/* Generate code that will load into register regOut a value that is
|
|
** appropriate for the iIdxCol-th column of index pIdx.
|
|
*/
|
|
@@ -94871,13 +99369,8 @@
|
|
|
|
/*
|
|
** Generate code that will extract the iColumn-th column from
|
|
-** table pTab and store the column value in a register.
|
|
+** table pTab and store the column value in register iReg.
|
|
**
|
|
-** An effort is made to store the column value in register iReg. This
|
|
-** is not garanteeed for GetColumn() - the result can be stored in
|
|
-** any register. But the result is guaranteed to land in register iReg
|
|
-** for GetColumnToReg().
|
|
-**
|
|
** There must be an open cursor to pTab in iTable when this routine
|
|
** is called. If iColumn<0 then code is generated that extracts the rowid.
|
|
*/
|
|
@@ -94890,97 +99383,24 @@
|
|
u8 p5 /* P5 value for OP_Column + FLAGS */
|
|
){
|
|
Vdbe *v = pParse->pVdbe;
|
|
- int i;
|
|
- struct yColCache *p;
|
|
-
|
|
- for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
|
|
- if( p->iTable==iTable && p->iColumn==iColumn ){
|
|
- p->lru = pParse->iCacheCnt++;
|
|
- sqlite3ExprCachePinRegister(pParse, p->iReg);
|
|
- return p->iReg;
|
|
- }
|
|
- }
|
|
assert( v!=0 );
|
|
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
|
|
if( p5 ){
|
|
sqlite3VdbeChangeP5(v, p5);
|
|
- }else{
|
|
- sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
|
|
}
|
|
return iReg;
|
|
}
|
|
-SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(
|
|
- Parse *pParse, /* Parsing and code generating context */
|
|
- Table *pTab, /* Description of the table we are reading from */
|
|
- int iColumn, /* Index of the table column */
|
|
- int iTable, /* The cursor pointing to the table */
|
|
- int iReg /* Store results here */
|
|
-){
|
|
- int r1 = sqlite3ExprCodeGetColumn(pParse, pTab, iColumn, iTable, iReg, 0);
|
|
- if( r1!=iReg ) sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, r1, iReg);
|
|
-}
|
|
|
|
-
|
|
/*
|
|
-** Clear all column cache entries.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){
|
|
- int i;
|
|
-
|
|
-#ifdef SQLITE_DEBUG
|
|
- if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
|
|
- printf("CLEAR\n");
|
|
- }
|
|
-#endif
|
|
- for(i=0; i<pParse->nColCache; i++){
|
|
- if( pParse->aColCache[i].tempReg
|
|
- && pParse->nTempReg<ArraySize(pParse->aTempReg)
|
|
- ){
|
|
- pParse->aTempReg[pParse->nTempReg++] = pParse->aColCache[i].iReg;
|
|
- }
|
|
- }
|
|
- pParse->nColCache = 0;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Record the fact that an affinity change has occurred on iCount
|
|
-** registers starting with iStart.
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){
|
|
- sqlite3ExprCacheRemove(pParse, iStart, iCount);
|
|
-}
|
|
-
|
|
-/*
|
|
** Generate code to move content from registers iFrom...iFrom+nReg-1
|
|
-** over to iTo..iTo+nReg-1. Keep the column cache up-to-date.
|
|
+** over to iTo..iTo+nReg-1.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
|
|
assert( iFrom>=iTo+nReg || iFrom+nReg<=iTo );
|
|
sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
|
|
- sqlite3ExprCacheRemove(pParse, iFrom, nReg);
|
|
}
|
|
|
|
-#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
|
|
/*
|
|
-** Return true if any register in the range iFrom..iTo (inclusive)
|
|
-** is used as part of the column cache.
|
|
-**
|
|
-** This routine is used within assert() and testcase() macros only
|
|
-** and does not appear in a normal build.
|
|
-*/
|
|
-static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
|
|
- int i;
|
|
- struct yColCache *p;
|
|
- for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
|
|
- int r = p->iReg;
|
|
- if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
|
|
-
|
|
-
|
|
-/*
|
|
** Convert a scalar expression node to a TK_REGISTER referencing
|
|
** register iReg. The caller must ensure that iReg already contains
|
|
** the correct value for the expression.
|
|
@@ -95055,6 +99475,7 @@
|
|
return 0;
|
|
}
|
|
|
|
+expr_code_doover:
|
|
if( pExpr==0 ){
|
|
op = TK_NULL;
|
|
}else{
|
|
@@ -95076,6 +99497,28 @@
|
|
}
|
|
case TK_COLUMN: {
|
|
int iTab = pExpr->iTable;
|
|
+ if( ExprHasProperty(pExpr, EP_FixedCol) ){
|
|
+ /* This COLUMN expression is really a constant due to WHERE clause
|
|
+ ** constraints, and that constant is coded by the pExpr->pLeft
|
|
+ ** expresssion. However, make sure the constant has the correct
|
|
+ ** datatype by applying the Affinity of the table column to the
|
|
+ ** constant.
|
|
+ */
|
|
+ int iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
|
|
+ int aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
|
|
+ if( aff!=SQLITE_AFF_BLOB ){
|
|
+ static const char zAff[] = "B\000C\000D\000E";
|
|
+ assert( SQLITE_AFF_BLOB=='A' );
|
|
+ assert( SQLITE_AFF_TEXT=='B' );
|
|
+ if( iReg!=target ){
|
|
+ sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target);
|
|
+ iReg = target;
|
|
+ }
|
|
+ sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
|
|
+ &zAff[(aff-'B')*2], P4_STATIC);
|
|
+ }
|
|
+ return iReg;
|
|
+ }
|
|
if( iTab<0 ){
|
|
if( pParse->iSelfTab<0 ){
|
|
/* Generating CHECK constraints or inserting into partial index */
|
|
@@ -95086,7 +99529,7 @@
|
|
iTab = pParse->iSelfTab - 1;
|
|
}
|
|
}
|
|
- return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
|
|
+ return sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
|
|
pExpr->iColumn, iTab, target,
|
|
pExpr->op2);
|
|
}
|
|
@@ -95094,6 +99537,10 @@
|
|
codeInteger(pParse, pExpr, 0, target);
|
|
return target;
|
|
}
|
|
+ case TK_TRUEFALSE: {
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, sqlite3ExprTruthValue(pExpr), target);
|
|
+ return target;
|
|
+ }
|
|
#ifndef SQLITE_OMIT_FLOATING_POINT
|
|
case TK_FLOAT: {
|
|
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
|
@@ -95152,8 +99599,6 @@
|
|
}
|
|
sqlite3VdbeAddOp2(v, OP_Cast, target,
|
|
sqlite3AffinityType(pExpr->u.zToken, 0));
|
|
- testcase( usedAsColumnCache(pParse, inReg, inReg) );
|
|
- sqlite3ExprCacheAffinityChange(pParse, inReg, 1);
|
|
return inReg;
|
|
}
|
|
#endif /* SQLITE_OMIT_CAST */
|
|
@@ -95249,6 +99694,18 @@
|
|
sqlite3VdbeAddOp2(v, op, r1, inReg);
|
|
break;
|
|
}
|
|
+ case TK_TRUTH: {
|
|
+ int isTrue; /* IS TRUE or IS NOT TRUE */
|
|
+ int bNormal; /* IS TRUE or IS FALSE */
|
|
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
|
+ testcase( regFree1==0 );
|
|
+ isTrue = sqlite3ExprTruthValue(pExpr->pRight);
|
|
+ bNormal = pExpr->op2==TK_IS;
|
|
+ testcase( isTrue && bNormal);
|
|
+ testcase( !isTrue && bNormal);
|
|
+ sqlite3VdbeAddOp4Int(v, OP_IsTrue, r1, inReg, !isTrue, isTrue ^ bNormal);
|
|
+ break;
|
|
+ }
|
|
case TK_ISNULL:
|
|
case TK_NOTNULL: {
|
|
int addr;
|
|
@@ -95285,6 +99742,12 @@
|
|
u8 enc = ENC(db); /* The text encoding used by this database */
|
|
CollSeq *pColl = 0; /* A collating sequence */
|
|
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
|
+ return pExpr->y.pWin->regResult;
|
|
+ }
|
|
+#endif
|
|
+
|
|
if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
|
|
/* SQL functions can be expensive. So try to move constant functions
|
|
** out of the inner loop, even if that means an extra OP_Copy. */
|
|
@@ -95321,10 +99784,7 @@
|
|
for(i=1; i<nFarg; i++){
|
|
sqlite3VdbeAddOp2(v, OP_NotNull, target, endCoalesce);
|
|
VdbeCoverage(v);
|
|
- sqlite3ExprCacheRemove(pParse, target, 1);
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target);
|
|
- sqlite3ExprCachePop(pParse);
|
|
}
|
|
sqlite3VdbeResolveLabel(v, endCoalesce);
|
|
break;
|
|
@@ -95390,10 +99850,8 @@
|
|
}
|
|
}
|
|
|
|
- sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
|
|
sqlite3ExprCodeExprList(pParse, pFarg, r1, 0,
|
|
SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
|
|
- sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */
|
|
}else{
|
|
r1 = 0;
|
|
}
|
|
@@ -95410,7 +99868,7 @@
|
|
** "glob(B,A). We want to use the A in "A glob B" to test
|
|
** for function overloading. But we use the B term in "glob(B,A)".
|
|
*/
|
|
- if( nFarg>=2 && (pExpr->flags & EP_InfixFunc) ){
|
|
+ if( nFarg>=2 && ExprHasProperty(pExpr, EP_InfixFunc) ){
|
|
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[1].pExpr);
|
|
}else if( nFarg>0 ){
|
|
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr);
|
|
@@ -95420,9 +99878,21 @@
|
|
if( !pColl ) pColl = db->pDfltColl;
|
|
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
|
|
}
|
|
- sqlite3VdbeAddOp4(v, pParse->iSelfTab ? OP_PureFunc0 : OP_Function0,
|
|
- constMask, r1, target, (char*)pDef, P4_FUNCDEF);
|
|
- sqlite3VdbeChangeP5(v, (u8)nFarg);
|
|
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
|
+ if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){
|
|
+ Expr *pArg = pFarg->a[0].pExpr;
|
|
+ if( pArg->op==TK_COLUMN ){
|
|
+ sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
|
+ }
|
|
+ }else
|
|
+#endif
|
|
+ {
|
|
+ sqlite3VdbeAddOp4(v, pParse->iSelfTab ? OP_PureFunc0 : OP_Function0,
|
|
+ constMask, r1, target, (char*)pDef, P4_FUNCDEF);
|
|
+ sqlite3VdbeChangeP5(v, (u8)nFarg);
|
|
+ }
|
|
if( nFarg && constMask==0 ){
|
|
sqlite3ReleaseTempRange(pParse, r1, nFarg);
|
|
}
|
|
@@ -95487,7 +99957,8 @@
|
|
case TK_SPAN:
|
|
case TK_COLLATE:
|
|
case TK_UPLUS: {
|
|
- return sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
|
|
+ pExpr = pExpr->pLeft;
|
|
+ goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */
|
|
}
|
|
|
|
case TK_TRIGGER: {
|
|
@@ -95516,7 +99987,7 @@
|
|
** p1==1 -> old.a p1==4 -> new.a
|
|
** p1==2 -> old.b p1==5 -> new.b
|
|
*/
|
|
- Table *pTab = pExpr->pTab;
|
|
+ Table *pTab = pExpr->y.pTab;
|
|
int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn;
|
|
|
|
assert( pExpr->iTable==0 || pExpr->iTable==1 );
|
|
@@ -95525,10 +99996,9 @@
|
|
assert( p1>=0 && p1<(pTab->nCol*2+2) );
|
|
|
|
sqlite3VdbeAddOp2(v, OP_Param, p1, target);
|
|
- VdbeComment((v, "%s.%s -> $%d",
|
|
+ VdbeComment((v, "r[%d]=%s.%s", target,
|
|
(pExpr->iTable ? "new" : "old"),
|
|
- (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName),
|
|
- target
|
|
+ (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[pExpr->iColumn].zName)
|
|
));
|
|
|
|
#ifndef SQLITE_OMIT_FLOATING_POINT
|
|
@@ -95554,9 +100024,7 @@
|
|
case TK_IF_NULL_ROW: {
|
|
int addrINR;
|
|
addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable);
|
|
- sqlite3ExprCachePush(pParse);
|
|
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
|
|
- sqlite3ExprCachePop(pParse);
|
|
sqlite3VdbeJumpHere(v, addrINR);
|
|
sqlite3VdbeChangeP3(v, addrINR, inReg);
|
|
break;
|
|
@@ -95593,7 +100061,6 @@
|
|
Expr opCompare; /* The X==Ei expression */
|
|
Expr *pX; /* The X expression */
|
|
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
|
|
- VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; )
|
|
|
|
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
|
|
assert(pExpr->x.pList->nExpr > 0);
|
|
@@ -95617,7 +100084,6 @@
|
|
regFree1 = 0;
|
|
}
|
|
for(i=0; i<nExpr-1; i=i+2){
|
|
- sqlite3ExprCachePush(pParse);
|
|
if( pX ){
|
|
assert( pTest!=0 );
|
|
opCompare.pRight = aListelem[i].pExpr;
|
|
@@ -95630,18 +100096,13 @@
|
|
testcase( aListelem[i+1].pExpr->op==TK_COLUMN );
|
|
sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target);
|
|
sqlite3VdbeGoto(v, endLabel);
|
|
- sqlite3ExprCachePop(pParse);
|
|
sqlite3VdbeResolveLabel(v, nextCase);
|
|
}
|
|
if( (nExpr&1)!=0 ){
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprCode(pParse, pEList->a[nExpr-1].pExpr, target);
|
|
- sqlite3ExprCachePop(pParse);
|
|
}else{
|
|
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
|
}
|
|
- assert( pParse->db->mallocFailed || pParse->nErr>0
|
|
- || pParse->iCacheLevel==iCacheLevel );
|
|
sqlite3VdbeResolveLabel(v, endLabel);
|
|
break;
|
|
}
|
|
@@ -95791,7 +100252,7 @@
|
|
** might choose to code the expression at initialization time.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
|
|
- if( pParse->okConstFactor && sqlite3ExprIsConstant(pExpr) ){
|
|
+ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){
|
|
sqlite3ExprCodeAtInit(pParse, pExpr, target);
|
|
}else{
|
|
sqlite3ExprCode(pParse, pExpr, target);
|
|
@@ -95826,7 +100287,9 @@
|
|
** Generate code that pushes the value of every element of the given
|
|
** expression list into a sequence of registers beginning at target.
|
|
**
|
|
-** Return the number of elements evaluated.
|
|
+** Return the number of elements evaluated. The number returned will
|
|
+** usually be pList->nExpr but might be reduced if SQLITE_ECEL_OMITREF
|
|
+** is defined.
|
|
**
|
|
** The SQLITE_ECEL_DUP flag prevents the arguments from being
|
|
** filled using OP_SCopy. OP_Copy must be used instead.
|
|
@@ -95837,6 +100300,8 @@
|
|
** The SQLITE_ECEL_REF flag means that expressions in the list with
|
|
** ExprList.a[].u.x.iOrderByCol>0 have already been evaluated and stored
|
|
** in registers at srcReg, and so the value can be copied from there.
|
|
+** If SQLITE_ECEL_OMITREF is also set, then the values with u.x.iOrderByCol>0
|
|
+** are simply omitted rather than being copied from srcReg.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3ExprCodeExprList(
|
|
Parse *pParse, /* Parsing context */
|
|
@@ -95856,6 +100321,12 @@
|
|
if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
|
|
for(pItem=pList->a, i=0; i<n; i++, pItem++){
|
|
Expr *pExpr = pItem->pExpr;
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( pItem->bSorterRef ){
|
|
+ i--;
|
|
+ n--;
|
|
+ }else
|
|
+#endif
|
|
if( (flags & SQLITE_ECEL_REF)!=0 && (j = pItem->u.x.iOrderByCol)>0 ){
|
|
if( flags & SQLITE_ECEL_OMITREF ){
|
|
i--;
|
|
@@ -95863,7 +100334,9 @@
|
|
}else{
|
|
sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
|
|
}
|
|
- }else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){
|
|
+ }else if( (flags & SQLITE_ECEL_FACTOR)!=0
|
|
+ && sqlite3ExprIsConstantNotJoin(pExpr)
|
|
+ ){
|
|
sqlite3ExprCodeAtInit(pParse, pExpr, target+i);
|
|
}else{
|
|
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
|
|
@@ -95989,18 +100462,14 @@
|
|
int d2 = sqlite3VdbeMakeLabel(v);
|
|
testcase( jumpIfNull==0 );
|
|
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
|
sqlite3VdbeResolveLabel(v, d2);
|
|
- sqlite3ExprCachePop(pParse);
|
|
break;
|
|
}
|
|
case TK_OR: {
|
|
testcase( jumpIfNull==0 );
|
|
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
|
- sqlite3ExprCachePop(pParse);
|
|
break;
|
|
}
|
|
case TK_NOT: {
|
|
@@ -96008,6 +100477,23 @@
|
|
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
|
break;
|
|
}
|
|
+ case TK_TRUTH: {
|
|
+ int isNot; /* IS NOT TRUE or IS NOT FALSE */
|
|
+ int isTrue; /* IS TRUE or IS NOT TRUE */
|
|
+ testcase( jumpIfNull==0 );
|
|
+ isNot = pExpr->op2==TK_ISNOT;
|
|
+ isTrue = sqlite3ExprTruthValue(pExpr->pRight);
|
|
+ testcase( isTrue && isNot );
|
|
+ testcase( !isTrue && isNot );
|
|
+ if( isTrue ^ isNot ){
|
|
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest,
|
|
+ isNot ? SQLITE_JUMPIFNULL : 0);
|
|
+ }else{
|
|
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest,
|
|
+ isNot ? SQLITE_JUMPIFNULL : 0);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
case TK_IS:
|
|
case TK_ISNOT:
|
|
testcase( op==TK_IS );
|
|
@@ -96142,9 +100628,7 @@
|
|
case TK_AND: {
|
|
testcase( jumpIfNull==0 );
|
|
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
|
- sqlite3ExprCachePop(pParse);
|
|
break;
|
|
}
|
|
case TK_OR: {
|
|
@@ -96151,10 +100635,8 @@
|
|
int d2 = sqlite3VdbeMakeLabel(v);
|
|
testcase( jumpIfNull==0 );
|
|
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
|
sqlite3VdbeResolveLabel(v, d2);
|
|
- sqlite3ExprCachePop(pParse);
|
|
break;
|
|
}
|
|
case TK_NOT: {
|
|
@@ -96162,6 +100644,26 @@
|
|
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
|
|
break;
|
|
}
|
|
+ case TK_TRUTH: {
|
|
+ int isNot; /* IS NOT TRUE or IS NOT FALSE */
|
|
+ int isTrue; /* IS TRUE or IS NOT TRUE */
|
|
+ testcase( jumpIfNull==0 );
|
|
+ isNot = pExpr->op2==TK_ISNOT;
|
|
+ isTrue = sqlite3ExprTruthValue(pExpr->pRight);
|
|
+ testcase( isTrue && isNot );
|
|
+ testcase( !isTrue && isNot );
|
|
+ if( isTrue ^ isNot ){
|
|
+ /* IS TRUE and IS NOT FALSE */
|
|
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest,
|
|
+ isNot ? 0 : SQLITE_JUMPIFNULL);
|
|
+
|
|
+ }else{
|
|
+ /* IS FALSE and IS NOT TRUE */
|
|
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest,
|
|
+ isNot ? 0 : SQLITE_JUMPIFNULL);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
case TK_IS:
|
|
case TK_ISNOT:
|
|
testcase( pExpr->op==TK_IS );
|
|
@@ -96347,17 +100849,35 @@
|
|
if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
|
|
if( pA->op==TK_FUNCTION ){
|
|
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ /* Justification for the assert():
|
|
+ ** window functions have p->op==TK_FUNCTION but aggregate functions
|
|
+ ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate
|
|
+ ** function and a window function should have failed before reaching
|
|
+ ** this point. And, it is not possible to have a window function and
|
|
+ ** a scalar function with the same name and number of arguments. So
|
|
+ ** if we reach this point, either A and B both window functions or
|
|
+ ** neither are a window functions. */
|
|
+ assert( ExprHasProperty(pA,EP_WinFunc)==ExprHasProperty(pB,EP_WinFunc) );
|
|
+ if( ExprHasProperty(pA,EP_WinFunc) ){
|
|
+ if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2;
|
|
+ }
|
|
+#endif
|
|
+ }else if( pA->op==TK_COLLATE ){
|
|
+ if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
|
|
}else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
|
|
- return pA->op==TK_COLLATE ? 1 : 2;
|
|
+ return 2;
|
|
}
|
|
}
|
|
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
|
|
if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
|
|
if( combinedFlags & EP_xIsSelect ) return 2;
|
|
- if( sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
|
|
+ if( (combinedFlags & EP_FixedCol)==0
|
|
+ && sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
|
|
if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2;
|
|
if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
|
|
- if( ALWAYS((combinedFlags & EP_Reduced)==0) && pA->op!=TK_STRING ){
|
|
+ assert( (combinedFlags & EP_Reduced)==0 );
|
|
+ if( pA->op!=TK_STRING && pA->op!=TK_TRUEFALSE ){
|
|
if( pA->iColumn!=pB->iColumn ) return 2;
|
|
if( pA->iTable!=pB->iTable
|
|
&& (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
|
|
@@ -96450,6 +100970,102 @@
|
|
}
|
|
|
|
/*
|
|
+** This is the Expr node callback for sqlite3ExprImpliesNotNullRow().
|
|
+** If the expression node requires that the table at pWalker->iCur
|
|
+** have one or more non-NULL column, then set pWalker->eCode to 1 and abort.
|
|
+**
|
|
+** This routine controls an optimization. False positives (setting
|
|
+** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives
|
|
+** (never setting pWalker->eCode) is a harmless missed optimization.
|
|
+*/
|
|
+static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
|
|
+ testcase( pExpr->op==TK_AGG_COLUMN );
|
|
+ testcase( pExpr->op==TK_AGG_FUNCTION );
|
|
+ if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
|
|
+ switch( pExpr->op ){
|
|
+ case TK_ISNOT:
|
|
+ case TK_NOT:
|
|
+ case TK_ISNULL:
|
|
+ case TK_IS:
|
|
+ case TK_OR:
|
|
+ case TK_CASE:
|
|
+ case TK_IN:
|
|
+ case TK_FUNCTION:
|
|
+ testcase( pExpr->op==TK_ISNOT );
|
|
+ testcase( pExpr->op==TK_NOT );
|
|
+ testcase( pExpr->op==TK_ISNULL );
|
|
+ testcase( pExpr->op==TK_IS );
|
|
+ testcase( pExpr->op==TK_OR );
|
|
+ testcase( pExpr->op==TK_CASE );
|
|
+ testcase( pExpr->op==TK_IN );
|
|
+ testcase( pExpr->op==TK_FUNCTION );
|
|
+ return WRC_Prune;
|
|
+ case TK_COLUMN:
|
|
+ if( pWalker->u.iCur==pExpr->iTable ){
|
|
+ pWalker->eCode = 1;
|
|
+ return WRC_Abort;
|
|
+ }
|
|
+ return WRC_Prune;
|
|
+
|
|
+ /* Virtual tables are allowed to use constraints like x=NULL. So
|
|
+ ** a term of the form x=y does not prove that y is not null if x
|
|
+ ** is the column of a virtual table */
|
|
+ case TK_EQ:
|
|
+ case TK_NE:
|
|
+ case TK_LT:
|
|
+ case TK_LE:
|
|
+ case TK_GT:
|
|
+ case TK_GE:
|
|
+ testcase( pExpr->op==TK_EQ );
|
|
+ testcase( pExpr->op==TK_NE );
|
|
+ testcase( pExpr->op==TK_LT );
|
|
+ testcase( pExpr->op==TK_LE );
|
|
+ testcase( pExpr->op==TK_GT );
|
|
+ testcase( pExpr->op==TK_GE );
|
|
+ if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab))
|
|
+ || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab))
|
|
+ ){
|
|
+ return WRC_Prune;
|
|
+ }
|
|
+ default:
|
|
+ return WRC_Continue;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true (non-zero) if expression p can only be true if at least
|
|
+** one column of table iTab is non-null. In other words, return true
|
|
+** if expression p will always be NULL or false if every column of iTab
|
|
+** is NULL.
|
|
+**
|
|
+** False negatives are acceptable. In other words, it is ok to return
|
|
+** zero even if expression p will never be true of every column of iTab
|
|
+** is NULL. A false negative is merely a missed optimization opportunity.
|
|
+**
|
|
+** False positives are not allowed, however. A false positive may result
|
|
+** in an incorrect answer.
|
|
+**
|
|
+** Terms of p that are marked with EP_FromJoin (and hence that come from
|
|
+** the ON or USING clauses of LEFT JOINS) are excluded from the analysis.
|
|
+**
|
|
+** This routine is used to check if a LEFT JOIN can be converted into
|
|
+** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE
|
|
+** clause requires that some column of the right table of the LEFT JOIN
|
|
+** be non-NULL, then the LEFT JOIN can be safely converted into an
|
|
+** ordinary join.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){
|
|
+ Walker w;
|
|
+ w.xExprCallback = impliesNotNullRow;
|
|
+ w.xSelectCallback = 0;
|
|
+ w.xSelectCallback2 = 0;
|
|
+ w.eCode = 0;
|
|
+ w.u.iCur = iTab;
|
|
+ sqlite3WalkExpr(&w, p);
|
|
+ return w.eCode;
|
|
+}
|
|
+
|
|
+/*
|
|
** An instance of the following structure is used by the tree walker
|
|
** to determine if an expression can be evaluated by reference to the
|
|
** index only, without having to do a search for the corresponding
|
|
@@ -96604,8 +101220,9 @@
|
|
NameContext *pNC = pWalker->u.pNC;
|
|
Parse *pParse = pNC->pParse;
|
|
SrcList *pSrcList = pNC->pSrcList;
|
|
- AggInfo *pAggInfo = pNC->pAggInfo;
|
|
+ AggInfo *pAggInfo = pNC->uNC.pAggInfo;
|
|
|
|
+ assert( pNC->ncFlags & NC_UAggInfo );
|
|
switch( pExpr->op ){
|
|
case TK_AGG_COLUMN:
|
|
case TK_COLUMN: {
|
|
@@ -96637,7 +101254,7 @@
|
|
&& (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
|
|
){
|
|
pCol = &pAggInfo->aCol[k];
|
|
- pCol->pTab = pExpr->pTab;
|
|
+ pCol->pTab = pExpr->y.pTab;
|
|
pCol->iTable = pExpr->iTable;
|
|
pCol->iColumn = pExpr->iColumn;
|
|
pCol->iMem = ++pParse->nMem;
|
|
@@ -96783,21 +101400,9 @@
|
|
/*
|
|
** Deallocate a register, making available for reuse for some other
|
|
** purpose.
|
|
-**
|
|
-** If a register is currently being used by the column cache, then
|
|
-** the deallocation is deferred until the column cache line that uses
|
|
-** the register becomes stale.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
|
|
if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
|
|
- int i;
|
|
- struct yColCache *p;
|
|
- for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
|
|
- if( p->iReg==iReg ){
|
|
- p->tempReg = 1;
|
|
- return;
|
|
- }
|
|
- }
|
|
pParse->aTempReg[pParse->nTempReg++] = iReg;
|
|
}
|
|
}
|
|
@@ -96811,7 +101416,6 @@
|
|
i = pParse->iRangeReg;
|
|
n = pParse->nRangeReg;
|
|
if( nReg<=n ){
|
|
- assert( !usedAsColumnCache(pParse, i, i+n-1) );
|
|
pParse->iRangeReg += nReg;
|
|
pParse->nRangeReg -= nReg;
|
|
}else{
|
|
@@ -96825,7 +101429,6 @@
|
|
sqlite3ReleaseTempReg(pParse, iReg);
|
|
return;
|
|
}
|
|
- sqlite3ExprCacheRemove(pParse, iReg, nReg);
|
|
if( nReg>pParse->nRangeReg ){
|
|
pParse->nRangeReg = nReg;
|
|
pParse->iRangeReg = iReg;
|
|
@@ -96887,369 +101490,66 @@
|
|
*/
|
|
#ifndef SQLITE_OMIT_ALTERTABLE
|
|
|
|
-
|
|
/*
|
|
-** This function is used by SQL generated to implement the
|
|
-** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
|
|
-** CREATE INDEX command. The second is a table name. The table name in
|
|
-** the CREATE TABLE or CREATE INDEX statement is replaced with the third
|
|
-** argument and the result returned. Examples:
|
|
+** Parameter zName is the name of a table that is about to be altered
|
|
+** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
|
|
+** If the table is a system table, this function leaves an error message
|
|
+** in pParse->zErr (system tables may not be altered) and returns non-zero.
|
|
**
|
|
-** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
|
|
-** -> 'CREATE TABLE def(a, b, c)'
|
|
-**
|
|
-** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
|
|
-** -> 'CREATE INDEX i ON def(a, b, c)'
|
|
+** Or, if zName is not a system table, zero is returned.
|
|
*/
|
|
-static void renameTableFunc(
|
|
- sqlite3_context *context,
|
|
- int NotUsed,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
|
- unsigned char const *zTableName = sqlite3_value_text(argv[1]);
|
|
-
|
|
- int token;
|
|
- Token tname;
|
|
- unsigned char const *zCsr = zSql;
|
|
- int len = 0;
|
|
- char *zRet;
|
|
-
|
|
- sqlite3 *db = sqlite3_context_db_handle(context);
|
|
-
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
-
|
|
- /* The principle used to locate the table name in the CREATE TABLE
|
|
- ** statement is that the table name is the first non-space token that
|
|
- ** is immediately followed by a TK_LP or TK_USING token.
|
|
- */
|
|
- if( zSql ){
|
|
- do {
|
|
- if( !*zCsr ){
|
|
- /* Ran out of input before finding an opening bracket. Return NULL. */
|
|
- return;
|
|
- }
|
|
-
|
|
- /* Store the token that zCsr points to in tname. */
|
|
- tname.z = (char*)zCsr;
|
|
- tname.n = len;
|
|
-
|
|
- /* Advance zCsr to the next token. Store that token type in 'token',
|
|
- ** and its length in 'len' (to be used next iteration of this loop).
|
|
- */
|
|
- do {
|
|
- zCsr += len;
|
|
- len = sqlite3GetToken(zCsr, &token);
|
|
- } while( token==TK_SPACE );
|
|
- assert( len>0 );
|
|
- } while( token!=TK_LP && token!=TK_USING );
|
|
-
|
|
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
|
|
- zSql, zTableName, tname.z+tname.n);
|
|
- sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
|
|
+static int isSystemTable(Parse *pParse, const char *zName){
|
|
+ if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
|
|
+ sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
|
|
+ return 1;
|
|
}
|
|
+ return 0;
|
|
}
|
|
|
|
/*
|
|
-** This C function implements an SQL user function that is used by SQL code
|
|
-** generated by the ALTER TABLE ... RENAME command to modify the definition
|
|
-** of any foreign key constraints that use the table being renamed as the
|
|
-** parent table. It is passed three arguments:
|
|
-**
|
|
-** 1) The complete text of the CREATE TABLE statement being modified,
|
|
-** 2) The old name of the table being renamed, and
|
|
-** 3) The new name of the table being renamed.
|
|
-**
|
|
-** It returns the new CREATE TABLE statement. For example:
|
|
-**
|
|
-** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
|
|
-** -> 'CREATE TABLE t1(a REFERENCES t3)'
|
|
+** Generate code to verify that the schemas of database zDb and, if
|
|
+** bTemp is not true, database "temp", can still be parsed. This is
|
|
+** called at the end of the generation of an ALTER TABLE ... RENAME ...
|
|
+** statement to ensure that the operation has not rendered any schema
|
|
+** objects unusable.
|
|
*/
|
|
-#ifndef SQLITE_OMIT_FOREIGN_KEY
|
|
-static void renameParentFunc(
|
|
- sqlite3_context *context,
|
|
- int NotUsed,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- sqlite3 *db = sqlite3_context_db_handle(context);
|
|
- char *zOutput = 0;
|
|
- char *zResult;
|
|
- unsigned char const *zInput = sqlite3_value_text(argv[0]);
|
|
- unsigned char const *zOld = sqlite3_value_text(argv[1]);
|
|
- unsigned char const *zNew = sqlite3_value_text(argv[2]);
|
|
+static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
|
+ sqlite3NestedParse(pParse,
|
|
+ "SELECT 1 "
|
|
+ "FROM \"%w\".%s "
|
|
+ "WHERE name NOT LIKE 'sqlite_%%'"
|
|
+ " AND sql NOT LIKE 'create virtual%%'"
|
|
+ " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
|
|
+ zDb, MASTER_NAME,
|
|
+ zDb, bTemp
|
|
+ );
|
|
|
|
- unsigned const char *z; /* Pointer to token */
|
|
- int n; /* Length of token z */
|
|
- int token; /* Type of token */
|
|
-
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
- if( zInput==0 || zOld==0 ) return;
|
|
- for(z=zInput; *z; z=z+n){
|
|
- n = sqlite3GetToken(z, &token);
|
|
- if( token==TK_REFERENCES ){
|
|
- char *zParent;
|
|
- do {
|
|
- z += n;
|
|
- n = sqlite3GetToken(z, &token);
|
|
- }while( token==TK_SPACE );
|
|
-
|
|
- if( token==TK_ILLEGAL ) break;
|
|
- zParent = sqlite3DbStrNDup(db, (const char *)z, n);
|
|
- if( zParent==0 ) break;
|
|
- sqlite3Dequote(zParent);
|
|
- if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
|
|
- char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"",
|
|
- (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
|
|
- );
|
|
- sqlite3DbFree(db, zOutput);
|
|
- zOutput = zOut;
|
|
- zInput = &z[n];
|
|
- }
|
|
- sqlite3DbFree(db, zParent);
|
|
- }
|
|
+ if( bTemp==0 ){
|
|
+ sqlite3NestedParse(pParse,
|
|
+ "SELECT 1 "
|
|
+ "FROM temp.%s "
|
|
+ "WHERE name NOT LIKE 'sqlite_%%'"
|
|
+ " AND sql NOT LIKE 'create virtual%%'"
|
|
+ " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
|
|
+ MASTER_NAME, zDb
|
|
+ );
|
|
}
|
|
-
|
|
- zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput),
|
|
- sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC);
|
|
- sqlite3DbFree(db, zOutput);
|
|
}
|
|
-#endif
|
|
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
-/* This function is used by SQL generated to implement the
|
|
-** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER
|
|
-** statement. The second is a table name. The table name in the CREATE
|
|
-** TRIGGER statement is replaced with the third argument and the result
|
|
-** returned. This is analagous to renameTableFunc() above, except for CREATE
|
|
-** TRIGGER, not CREATE INDEX and CREATE TABLE.
|
|
-*/
|
|
-static void renameTriggerFunc(
|
|
- sqlite3_context *context,
|
|
- int NotUsed,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
|
- unsigned char const *zTableName = sqlite3_value_text(argv[1]);
|
|
-
|
|
- int token;
|
|
- Token tname;
|
|
- int dist = 3;
|
|
- unsigned char const *zCsr = zSql;
|
|
- int len = 0;
|
|
- char *zRet;
|
|
- sqlite3 *db = sqlite3_context_db_handle(context);
|
|
-
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
-
|
|
- /* The principle used to locate the table name in the CREATE TRIGGER
|
|
- ** statement is that the table name is the first token that is immediately
|
|
- ** preceded by either TK_ON or TK_DOT and immediately followed by one
|
|
- ** of TK_WHEN, TK_BEGIN or TK_FOR.
|
|
- */
|
|
- if( zSql ){
|
|
- do {
|
|
-
|
|
- if( !*zCsr ){
|
|
- /* Ran out of input before finding the table name. Return NULL. */
|
|
- return;
|
|
- }
|
|
-
|
|
- /* Store the token that zCsr points to in tname. */
|
|
- tname.z = (char*)zCsr;
|
|
- tname.n = len;
|
|
-
|
|
- /* Advance zCsr to the next token. Store that token type in 'token',
|
|
- ** and its length in 'len' (to be used next iteration of this loop).
|
|
- */
|
|
- do {
|
|
- zCsr += len;
|
|
- len = sqlite3GetToken(zCsr, &token);
|
|
- }while( token==TK_SPACE );
|
|
- assert( len>0 );
|
|
-
|
|
- /* Variable 'dist' stores the number of tokens read since the most
|
|
- ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN
|
|
- ** token is read and 'dist' equals 2, the condition stated above
|
|
- ** to be met.
|
|
- **
|
|
- ** Note that ON cannot be a database, table or column name, so
|
|
- ** there is no need to worry about syntax like
|
|
- ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
|
|
- */
|
|
- dist++;
|
|
- if( token==TK_DOT || token==TK_ON ){
|
|
- dist = 0;
|
|
- }
|
|
- } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) );
|
|
-
|
|
- /* Variable tname now contains the token that is the old table-name
|
|
- ** in the CREATE TRIGGER statement.
|
|
- */
|
|
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
|
|
- zSql, zTableName, tname.z+tname.n);
|
|
- sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
|
|
- }
|
|
-}
|
|
-#endif /* !SQLITE_OMIT_TRIGGER */
|
|
-
|
|
/*
|
|
-** Register built-in functions used to help implement ALTER TABLE
|
|
+** Generate code to reload the schema for database iDb. And, if iDb!=1, for
|
|
+** the temp database as well.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3AlterFunctions(void){
|
|
- static FuncDef aAlterTableFuncs[] = {
|
|
- FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc),
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
|
|
-#endif
|
|
-#ifndef SQLITE_OMIT_FOREIGN_KEY
|
|
- FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc),
|
|
-#endif
|
|
- };
|
|
- sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
|
|
-}
|
|
-
|
|
-/*
|
|
-** This function is used to create the text of expressions of the form:
|
|
-**
|
|
-** name=<constant1> OR name=<constant2> OR ...
|
|
-**
|
|
-** If argument zWhere is NULL, then a pointer string containing the text
|
|
-** "name=<constant>" is returned, where <constant> is the quoted version
|
|
-** of the string passed as argument zConstant. The returned buffer is
|
|
-** allocated using sqlite3DbMalloc(). It is the responsibility of the
|
|
-** caller to ensure that it is eventually freed.
|
|
-**
|
|
-** If argument zWhere is not NULL, then the string returned is
|
|
-** "<where> OR name=<constant>", where <where> is the contents of zWhere.
|
|
-** In this case zWhere is passed to sqlite3DbFree() before returning.
|
|
-**
|
|
-*/
|
|
-static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){
|
|
- char *zNew;
|
|
- if( !zWhere ){
|
|
- zNew = sqlite3MPrintf(db, "name=%Q", zConstant);
|
|
- }else{
|
|
- zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant);
|
|
- sqlite3DbFree(db, zWhere);
|
|
+static void renameReloadSchema(Parse *pParse, int iDb){
|
|
+ Vdbe *v = pParse->pVdbe;
|
|
+ if( v ){
|
|
+ sqlite3ChangeCookie(pParse, iDb);
|
|
+ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
|
|
+ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
|
|
}
|
|
- return zNew;
|
|
}
|
|
|
|
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
|
/*
|
|
-** Generate the text of a WHERE expression which can be used to select all
|
|
-** tables that have foreign key constraints that refer to table pTab (i.e.
|
|
-** constraints for which pTab is the parent table) from the sqlite_master
|
|
-** table.
|
|
-*/
|
|
-static char *whereForeignKeys(Parse *pParse, Table *pTab){
|
|
- FKey *p;
|
|
- char *zWhere = 0;
|
|
- for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
|
- zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName);
|
|
- }
|
|
- return zWhere;
|
|
-}
|
|
-#endif
|
|
-
|
|
-/*
|
|
-** Generate the text of a WHERE expression which can be used to select all
|
|
-** temporary triggers on table pTab from the sqlite_temp_master table. If
|
|
-** table pTab has no temporary triggers, or is itself stored in the
|
|
-** temporary database, NULL is returned.
|
|
-*/
|
|
-static char *whereTempTriggers(Parse *pParse, Table *pTab){
|
|
- Trigger *pTrig;
|
|
- char *zWhere = 0;
|
|
- const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */
|
|
-
|
|
- /* If the table is not located in the temp-db (in which case NULL is
|
|
- ** returned, loop through the tables list of triggers. For each trigger
|
|
- ** that is not part of the temp-db schema, add a clause to the WHERE
|
|
- ** expression being built up in zWhere.
|
|
- */
|
|
- if( pTab->pSchema!=pTempSchema ){
|
|
- sqlite3 *db = pParse->db;
|
|
- for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
|
|
- if( pTrig->pSchema==pTempSchema ){
|
|
- zWhere = whereOrName(db, zWhere, pTrig->zName);
|
|
- }
|
|
- }
|
|
- }
|
|
- if( zWhere ){
|
|
- char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere);
|
|
- sqlite3DbFree(pParse->db, zWhere);
|
|
- zWhere = zNew;
|
|
- }
|
|
- return zWhere;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Generate code to drop and reload the internal representation of table
|
|
-** pTab from the database, including triggers and temporary triggers.
|
|
-** Argument zName is the name of the table in the database schema at
|
|
-** the time the generated code is executed. This can be different from
|
|
-** pTab->zName if this function is being called to code part of an
|
|
-** "ALTER TABLE RENAME TO" statement.
|
|
-*/
|
|
-static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
|
|
- Vdbe *v;
|
|
- char *zWhere;
|
|
- int iDb; /* Index of database containing pTab */
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- Trigger *pTrig;
|
|
-#endif
|
|
-
|
|
- v = sqlite3GetVdbe(pParse);
|
|
- if( NEVER(v==0) ) return;
|
|
- assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
|
|
- iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
|
- assert( iDb>=0 );
|
|
-
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- /* Drop any table triggers from the internal schema. */
|
|
- for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
|
|
- int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
|
|
- assert( iTrigDb==iDb || iTrigDb==1 );
|
|
- sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0);
|
|
- }
|
|
-#endif
|
|
-
|
|
- /* Drop the table and index from the internal schema. */
|
|
- sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
|
|
-
|
|
- /* Reload the table, index and permanent trigger schemas. */
|
|
- zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
|
|
- if( !zWhere ) return;
|
|
- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
|
|
-
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- /* Now, if the table is not stored in the temp database, reload any temp
|
|
- ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
|
|
- */
|
|
- if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
|
|
- sqlite3VdbeAddParseSchemaOp(v, 1, zWhere);
|
|
- }
|
|
-#endif
|
|
-}
|
|
-
|
|
-/*
|
|
-** Parameter zName is the name of a table that is about to be altered
|
|
-** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
|
|
-** If the table is a system table, this function leaves an error message
|
|
-** in pParse->zErr (system tables may not be altered) and returns non-zero.
|
|
-**
|
|
-** Or, if zName is not a system table, zero is returned.
|
|
-*/
|
|
-static int isSystemTable(Parse *pParse, const char *zName){
|
|
- if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
|
|
- sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
|
|
- return 1;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/*
|
|
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
|
|
** command.
|
|
*/
|
|
@@ -97266,13 +101566,10 @@
|
|
int nTabName; /* Number of UTF-8 characters in zTabName */
|
|
const char *zTabName; /* Original name of the table */
|
|
Vdbe *v;
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- char *zWhere = 0; /* Where clause to locate temp triggers */
|
|
-#endif
|
|
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */
|
|
- int savedDbFlags; /* Saved value of db->flags */
|
|
+ u32 savedDbFlags; /* Saved value of db->mDbFlags */
|
|
|
|
- savedDbFlags = db->flags;
|
|
+ savedDbFlags = db->mDbFlags;
|
|
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
|
|
assert( pSrc->nSrc==1 );
|
|
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
|
|
@@ -97281,7 +101578,7 @@
|
|
if( !pTab ) goto exit_rename_table;
|
|
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
|
zDb = db->aDb[iDb].zDbSName;
|
|
- db->flags |= SQLITE_PreferBuiltin;
|
|
+ db->mDbFlags |= DBFLAG_PreferBuiltin;
|
|
|
|
/* Get a NULL terminated version of the new table name. */
|
|
zName = sqlite3NameFromToken(db, pName);
|
|
@@ -97341,52 +101638,25 @@
|
|
if( v==0 ){
|
|
goto exit_rename_table;
|
|
}
|
|
- sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb);
|
|
- sqlite3ChangeCookie(pParse, iDb);
|
|
|
|
- /* If this is a virtual table, invoke the xRename() function if
|
|
- ** one is defined. The xRename() callback will modify the names
|
|
- ** of any resources used by the v-table implementation (including other
|
|
- ** SQLite tables) that are identified by the name of the virtual table.
|
|
- */
|
|
-#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- if( pVTab ){
|
|
- int i = ++pParse->nMem;
|
|
- sqlite3VdbeLoadString(v, i, zName);
|
|
- sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
|
|
- sqlite3MayAbort(pParse);
|
|
- }
|
|
-#endif
|
|
-
|
|
/* figure out how many UTF-8 characters are in zName */
|
|
zTabName = pTab->zName;
|
|
nTabName = sqlite3Utf8CharLen(zTabName, -1);
|
|
|
|
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
|
- if( db->flags&SQLITE_ForeignKeys ){
|
|
- /* If foreign-key support is enabled, rewrite the CREATE TABLE
|
|
- ** statements corresponding to all child tables of foreign key constraints
|
|
- ** for which the renamed table is the parent table. */
|
|
- if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){
|
|
- sqlite3NestedParse(pParse,
|
|
- "UPDATE \"%w\".%s SET "
|
|
- "sql = sqlite_rename_parent(sql, %Q, %Q) "
|
|
- "WHERE %s;", zDb, MASTER_NAME, zTabName, zName, zWhere);
|
|
- sqlite3DbFree(db, zWhere);
|
|
- }
|
|
- }
|
|
-#endif
|
|
+ /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
|
|
+ ** the schema to use the new table name. */
|
|
+ sqlite3NestedParse(pParse,
|
|
+ "UPDATE \"%w\".%s SET "
|
|
+ "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
|
|
+ "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
|
|
+ "AND name NOT LIKE 'sqlite_%%'"
|
|
+ , zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
|
|
+ );
|
|
|
|
- /* Modify the sqlite_master table to use the new table name. */
|
|
+ /* Update the tbl_name and name columns of the sqlite_master table
|
|
+ ** as required. */
|
|
sqlite3NestedParse(pParse,
|
|
"UPDATE %Q.%s SET "
|
|
-#ifdef SQLITE_OMIT_TRIGGER
|
|
- "sql = sqlite_rename_table(sql, %Q), "
|
|
-#else
|
|
- "sql = CASE "
|
|
- "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)"
|
|
- "ELSE sqlite_rename_table(sql, %Q) END, "
|
|
-#endif
|
|
"tbl_name = %Q, "
|
|
"name = CASE "
|
|
"WHEN type='table' THEN %Q "
|
|
@@ -97395,11 +101665,9 @@
|
|
"ELSE name END "
|
|
"WHERE tbl_name=%Q COLLATE nocase AND "
|
|
"(type='table' OR type='index' OR type='trigger');",
|
|
- zDb, MASTER_NAME, zName, zName, zName,
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- zName,
|
|
-#endif
|
|
- zName, nTabName, zTabName
|
|
+ zDb, MASTER_NAME,
|
|
+ zName, zName, zName,
|
|
+ nTabName, zTabName
|
|
);
|
|
|
|
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
|
@@ -97413,40 +101681,42 @@
|
|
}
|
|
#endif
|
|
|
|
-#ifndef SQLITE_OMIT_TRIGGER
|
|
- /* If there are TEMP triggers on this table, modify the sqlite_temp_master
|
|
- ** table. Don't do this if the table being ALTERed is itself located in
|
|
- ** the temp database.
|
|
- */
|
|
- if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
|
|
+ /* If the table being renamed is not itself part of the temp database,
|
|
+ ** edit view and trigger definitions within the temp database
|
|
+ ** as required. */
|
|
+ if( iDb!=1 ){
|
|
sqlite3NestedParse(pParse,
|
|
"UPDATE sqlite_temp_master SET "
|
|
- "sql = sqlite_rename_trigger(sql, %Q), "
|
|
- "tbl_name = %Q "
|
|
- "WHERE %s;", zName, zName, zWhere);
|
|
- sqlite3DbFree(db, zWhere);
|
|
+ "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
|
|
+ "tbl_name = "
|
|
+ "CASE WHEN tbl_name=%Q COLLATE nocase AND "
|
|
+ " sqlite_rename_test(%Q, sql, type, name, 1) "
|
|
+ "THEN %Q ELSE tbl_name END "
|
|
+ "WHERE type IN ('view', 'trigger')"
|
|
+ , zDb, zTabName, zName, zTabName, zDb, zName);
|
|
}
|
|
-#endif
|
|
|
|
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
|
- if( db->flags&SQLITE_ForeignKeys ){
|
|
- FKey *p;
|
|
- for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
|
- Table *pFrom = p->pFrom;
|
|
- if( pFrom!=pTab ){
|
|
- reloadTableSchema(pParse, p->pFrom, pFrom->zName);
|
|
- }
|
|
- }
|
|
+ /* If this is a virtual table, invoke the xRename() function if
|
|
+ ** one is defined. The xRename() callback will modify the names
|
|
+ ** of any resources used by the v-table implementation (including other
|
|
+ ** SQLite tables) that are identified by the name of the virtual table.
|
|
+ */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( pVTab ){
|
|
+ int i = ++pParse->nMem;
|
|
+ sqlite3VdbeLoadString(v, i, zName);
|
|
+ sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
|
|
+ sqlite3MayAbort(pParse);
|
|
}
|
|
#endif
|
|
|
|
- /* Drop and reload the internal table schema. */
|
|
- reloadTableSchema(pParse, pTab, zName);
|
|
+ renameReloadSchema(pParse, iDb);
|
|
+ renameTestSchema(pParse, zDb, iDb==1);
|
|
|
|
exit_rename_table:
|
|
sqlite3SrcListDelete(db, pSrc);
|
|
sqlite3DbFree(db, zName);
|
|
- db->flags = savedDbFlags;
|
|
+ db->mDbFlags = savedDbFlags;
|
|
}
|
|
|
|
/*
|
|
@@ -97467,12 +101737,11 @@
|
|
Column *pCol; /* The new column */
|
|
Expr *pDflt; /* Default value for the new column */
|
|
sqlite3 *db; /* The database connection; */
|
|
- Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
|
|
+ Vdbe *v; /* The prepared statement under construction */
|
|
int r1; /* Temporary registers */
|
|
|
|
db = pParse->db;
|
|
if( pParse->nErr || db->mallocFailed ) return;
|
|
- assert( v!=0 );
|
|
pNew = pParse->pNewTable;
|
|
assert( pNew );
|
|
|
|
@@ -97547,11 +101816,11 @@
|
|
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
|
|
if( zCol ){
|
|
char *zEnd = &zCol[pColDef->n-1];
|
|
- int savedDbFlags = db->flags;
|
|
+ u32 savedDbFlags = db->mDbFlags;
|
|
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
|
|
*zEnd-- = '\0';
|
|
}
|
|
- db->flags |= SQLITE_PreferBuiltin;
|
|
+ db->mDbFlags |= DBFLAG_PreferBuiltin;
|
|
sqlite3NestedParse(pParse,
|
|
"UPDATE \"%w\".%s SET "
|
|
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
|
|
@@ -97560,7 +101829,7 @@
|
|
zTab
|
|
);
|
|
sqlite3DbFree(db, zCol);
|
|
- db->flags = savedDbFlags;
|
|
+ db->mDbFlags = savedDbFlags;
|
|
}
|
|
|
|
/* Make sure the schema version is at least 3. But do not upgrade
|
|
@@ -97567,17 +101836,20 @@
|
|
** from less than 3 to 4, as that will corrupt any preexisting DESC
|
|
** index.
|
|
*/
|
|
- r1 = sqlite3GetTempReg(pParse);
|
|
- sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
|
|
- sqlite3VdbeUsesBtree(v, iDb);
|
|
- sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
|
|
- sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
|
|
- VdbeCoverage(v);
|
|
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
|
|
- sqlite3ReleaseTempReg(pParse, r1);
|
|
+ v = sqlite3GetVdbe(pParse);
|
|
+ if( v ){
|
|
+ r1 = sqlite3GetTempReg(pParse);
|
|
+ sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
|
|
+ sqlite3VdbeUsesBtree(v, iDb);
|
|
+ sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
|
|
+ sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
|
|
+ sqlite3ReleaseTempReg(pParse, r1);
|
|
+ }
|
|
|
|
- /* Reload the schema of the modified table. */
|
|
- reloadTableSchema(pParse, pTab, pTab->zName);
|
|
+ /* Reload the table definition */
|
|
+ renameReloadSchema(pParse, iDb);
|
|
}
|
|
|
|
/*
|
|
@@ -97598,7 +101870,6 @@
|
|
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
|
Table *pNew;
|
|
Table *pTab;
|
|
- Vdbe *v;
|
|
int iDb;
|
|
int i;
|
|
int nAlloc;
|
|
@@ -97662,16 +101933,1146 @@
|
|
pNew->addColOffset = pTab->addColOffset;
|
|
pNew->nTabRef = 1;
|
|
|
|
- /* Begin a transaction and increment the schema cookie. */
|
|
- sqlite3BeginWriteOperation(pParse, 0, iDb);
|
|
- v = sqlite3GetVdbe(pParse);
|
|
- if( !v ) goto exit_begin_add_column;
|
|
- sqlite3ChangeCookie(pParse, iDb);
|
|
-
|
|
exit_begin_add_column:
|
|
sqlite3SrcListDelete(db, pSrc);
|
|
return;
|
|
}
|
|
+
|
|
+/*
|
|
+** Parameter pTab is the subject of an ALTER TABLE ... RENAME COLUMN
|
|
+** command. This function checks if the table is a view or virtual
|
|
+** table (columns of views or virtual tables may not be renamed). If so,
|
|
+** it loads an error message into pParse and returns non-zero.
|
|
+**
|
|
+** Or, if pTab is not a view or virtual table, zero is returned.
|
|
+*/
|
|
+#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
|
+static int isRealTable(Parse *pParse, Table *pTab){
|
|
+ const char *zType = 0;
|
|
+#ifndef SQLITE_OMIT_VIEW
|
|
+ if( pTab->pSelect ){
|
|
+ zType = "view";
|
|
+ }
|
|
+#endif
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ if( IsVirtual(pTab) ){
|
|
+ zType = "virtual table";
|
|
+ }
|
|
+#endif
|
|
+ if( zType ){
|
|
+ sqlite3ErrorMsg(
|
|
+ pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName
|
|
+ );
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+#else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
|
+# define isRealTable(x,y) (0)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Handles the following parser reduction:
|
|
+**
|
|
+** cmd ::= ALTER TABLE pSrc RENAME COLUMN pOld TO pNew
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3AlterRenameColumn(
|
|
+ Parse *pParse, /* Parsing context */
|
|
+ SrcList *pSrc, /* Table being altered. pSrc->nSrc==1 */
|
|
+ Token *pOld, /* Name of column being changed */
|
|
+ Token *pNew /* New column name */
|
|
+){
|
|
+ sqlite3 *db = pParse->db; /* Database connection */
|
|
+ Table *pTab; /* Table being updated */
|
|
+ int iCol; /* Index of column being renamed */
|
|
+ char *zOld = 0; /* Old column name */
|
|
+ char *zNew = 0; /* New column name */
|
|
+ const char *zDb; /* Name of schema containing the table */
|
|
+ int iSchema; /* Index of the schema */
|
|
+ int bQuote; /* True to quote the new name */
|
|
+
|
|
+ /* Locate the table to be altered */
|
|
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
|
+ if( !pTab ) goto exit_rename_column;
|
|
+
|
|
+ /* Cannot alter a system table */
|
|
+ if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ) goto exit_rename_column;
|
|
+ if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column;
|
|
+
|
|
+ /* Which schema holds the table to be altered */
|
|
+ iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
+ assert( iSchema>=0 );
|
|
+ zDb = db->aDb[iSchema].zDbSName;
|
|
+
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ /* Invoke the authorization callback. */
|
|
+ if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
|
|
+ goto exit_rename_column;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* Make sure the old name really is a column name in the table to be
|
|
+ ** altered. Set iCol to be the index of the column being renamed */
|
|
+ zOld = sqlite3NameFromToken(db, pOld);
|
|
+ if( !zOld ) goto exit_rename_column;
|
|
+ for(iCol=0; iCol<pTab->nCol; iCol++){
|
|
+ if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break;
|
|
+ }
|
|
+ if( iCol==pTab->nCol ){
|
|
+ sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld);
|
|
+ goto exit_rename_column;
|
|
+ }
|
|
+
|
|
+ /* Do the rename operation using a recursive UPDATE statement that
|
|
+ ** uses the sqlite_rename_column() SQL function to compute the new
|
|
+ ** CREATE statement text for the sqlite_master table.
|
|
+ */
|
|
+ zNew = sqlite3NameFromToken(db, pNew);
|
|
+ if( !zNew ) goto exit_rename_column;
|
|
+ assert( pNew->n>0 );
|
|
+ bQuote = sqlite3Isquote(pNew->z[0]);
|
|
+ sqlite3NestedParse(pParse,
|
|
+ "UPDATE \"%w\".%s SET "
|
|
+ "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
|
|
+ "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)"
|
|
+ " AND sql NOT LIKE 'create virtual%%'",
|
|
+ zDb, MASTER_NAME,
|
|
+ zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
|
|
+ pTab->zName
|
|
+ );
|
|
+
|
|
+ sqlite3NestedParse(pParse,
|
|
+ "UPDATE temp.%s SET "
|
|
+ "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) "
|
|
+ "WHERE type IN ('trigger', 'view')",
|
|
+ MASTER_NAME,
|
|
+ zDb, pTab->zName, iCol, zNew, bQuote
|
|
+ );
|
|
+
|
|
+ /* Drop and reload the database schema. */
|
|
+ renameReloadSchema(pParse, iSchema);
|
|
+ renameTestSchema(pParse, zDb, iSchema==1);
|
|
+
|
|
+ exit_rename_column:
|
|
+ sqlite3SrcListDelete(db, pSrc);
|
|
+ sqlite3DbFree(db, zOld);
|
|
+ sqlite3DbFree(db, zNew);
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Each RenameToken object maps an element of the parse tree into
|
|
+** the token that generated that element. The parse tree element
|
|
+** might be one of:
|
|
+**
|
|
+** * A pointer to an Expr that represents an ID
|
|
+** * The name of a table column in Column.zName
|
|
+**
|
|
+** A list of RenameToken objects can be constructed during parsing.
|
|
+** Each new object is created by sqlite3RenameTokenMap().
|
|
+** As the parse tree is transformed, the sqlite3RenameTokenRemap()
|
|
+** routine is used to keep the mapping current.
|
|
+**
|
|
+** After the parse finishes, renameTokenFind() routine can be used
|
|
+** to look up the actual token value that created some element in
|
|
+** the parse tree.
|
|
+*/
|
|
+struct RenameToken {
|
|
+ void *p; /* Parse tree element created by token t */
|
|
+ Token t; /* The token that created parse tree element p */
|
|
+ RenameToken *pNext; /* Next is a list of all RenameToken objects */
|
|
+};
|
|
+
|
|
+/*
|
|
+** The context of an ALTER TABLE RENAME COLUMN operation that gets passed
|
|
+** down into the Walker.
|
|
+*/
|
|
+typedef struct RenameCtx RenameCtx;
|
|
+struct RenameCtx {
|
|
+ RenameToken *pList; /* List of tokens to overwrite */
|
|
+ int nList; /* Number of tokens in pList */
|
|
+ int iCol; /* Index of column being renamed */
|
|
+ Table *pTab; /* Table being ALTERed */
|
|
+ const char *zOld; /* Old column name */
|
|
+};
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+/*
|
|
+** This function is only for debugging. It performs two tasks:
|
|
+**
|
|
+** 1. Checks that pointer pPtr does not already appear in the
|
|
+** rename-token list.
|
|
+**
|
|
+** 2. Dereferences each pointer in the rename-token list.
|
|
+**
|
|
+** The second is most effective when debugging under valgrind or
|
|
+** address-sanitizer or similar. If any of these pointers no longer
|
|
+** point to valid objects, an exception is raised by the memory-checking
|
|
+** tool.
|
|
+**
|
|
+** The point of this is to prevent comparisons of invalid pointer values.
|
|
+** Even though this always seems to work, it is undefined according to the
|
|
+** C standard. Example of undefined comparison:
|
|
+**
|
|
+** sqlite3_free(x);
|
|
+** if( x==y ) ...
|
|
+**
|
|
+** Technically, as x no longer points into a valid object or to the byte
|
|
+** following a valid object, it may not be used in comparison operations.
|
|
+*/
|
|
+static void renameTokenCheckAll(Parse *pParse, void *pPtr){
|
|
+ if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){
|
|
+ RenameToken *p;
|
|
+ u8 i = 0;
|
|
+ for(p=pParse->pRename; p; p=p->pNext){
|
|
+ if( p->p ){
|
|
+ assert( p->p!=pPtr );
|
|
+ i += *(u8*)(p->p);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#else
|
|
+# define renameTokenCheckAll(x,y)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Remember that the parser tree element pPtr was created using
|
|
+** the token pToken.
|
|
+**
|
|
+** In other words, construct a new RenameToken object and add it
|
|
+** to the list of RenameToken objects currently being built up
|
|
+** in pParse->pRename.
|
|
+**
|
|
+** The pPtr argument is returned so that this routine can be used
|
|
+** with tail recursion in tokenExpr() routine, for a small performance
|
|
+** improvement.
|
|
+*/
|
|
+SQLITE_PRIVATE 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;
|
|
+ }
|
|
+
|
|
+ return pPtr;
|
|
+}
|
|
+
|
|
+/*
|
|
+** It is assumed that there is already a RenameToken object associated
|
|
+** with parse tree element pFrom. This function remaps the associated token
|
|
+** to parse tree element pTo.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){
|
|
+ RenameToken *p;
|
|
+ renameTokenCheckAll(pParse, pTo);
|
|
+ for(p=pParse->pRename; p; p=p->pNext){
|
|
+ if( p->p==pFrom ){
|
|
+ p->p = pTo;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Walker callback used by sqlite3RenameExprUnmap().
|
|
+*/
|
|
+static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
|
|
+ Parse *pParse = pWalker->pParse;
|
|
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
|
|
+ return WRC_Continue;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Remove all nodes that are part of expression pExpr from the rename list.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
|
|
+ Walker sWalker;
|
|
+ memset(&sWalker, 0, sizeof(Walker));
|
|
+ sWalker.pParse = pParse;
|
|
+ sWalker.xExprCallback = renameUnmapExprCb;
|
|
+ sqlite3WalkExpr(&sWalker, pExpr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Remove all nodes that are part of expression-list pEList from the
|
|
+** rename list.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){
|
|
+ if( pEList ){
|
|
+ int i;
|
|
+ Walker sWalker;
|
|
+ memset(&sWalker, 0, sizeof(Walker));
|
|
+ sWalker.pParse = pParse;
|
|
+ sWalker.xExprCallback = renameUnmapExprCb;
|
|
+ sqlite3WalkExprList(&sWalker, pEList);
|
|
+ for(i=0; i<pEList->nExpr; i++){
|
|
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zName);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the list of RenameToken objects given in the second argument
|
|
+*/
|
|
+static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
|
|
+ RenameToken *pNext;
|
|
+ RenameToken *p;
|
|
+ for(p=pToken; p; p=pNext){
|
|
+ pNext = p->pNext;
|
|
+ sqlite3DbFree(db, p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Search the Parse object passed as the first argument for a RenameToken
|
|
+** object associated with parse tree element pPtr. If found, remove it
|
|
+** from the Parse object and add it to the list maintained by the
|
|
+** RenameCtx object passed as the second argument.
|
|
+*/
|
|
+static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
|
|
+ RenameToken **pp;
|
|
+ assert( pPtr!=0 );
|
|
+ for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
|
|
+ if( (*pp)->p==pPtr ){
|
|
+ RenameToken *pToken = *pp;
|
|
+ *pp = pToken->pNext;
|
|
+ pToken->pNext = pCtx->pList;
|
|
+ pCtx->pList = pToken;
|
|
+ pCtx->nList++;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** 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){
|
|
+ UNUSED_PARAMETER(pWalker);
|
|
+ UNUSED_PARAMETER(p);
|
|
+ return WRC_Continue;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is a Walker expression callback.
|
|
+**
|
|
+** For every TK_COLUMN node in the expression tree, search to see
|
|
+** if the column being references is the column being renamed by an
|
|
+** ALTER TABLE statement. If it is, then attach its associated
|
|
+** RenameToken object to the list of RenameToken objects being
|
|
+** constructed in RenameCtx object at pWalker->u.pRename.
|
|
+*/
|
|
+static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
|
|
+ RenameCtx *p = pWalker->u.pRename;
|
|
+ if( pExpr->op==TK_TRIGGER
|
|
+ && pExpr->iColumn==p->iCol
|
|
+ && pWalker->pParse->pTriggerTab==p->pTab
|
|
+ ){
|
|
+ renameTokenFind(pWalker->pParse, p, (void*)pExpr);
|
|
+ }else if( pExpr->op==TK_COLUMN
|
|
+ && pExpr->iColumn==p->iCol
|
|
+ && p->pTab==pExpr->y.pTab
|
|
+ ){
|
|
+ renameTokenFind(pWalker->pParse, p, (void*)pExpr);
|
|
+ }
|
|
+ return WRC_Continue;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The RenameCtx contains a list of tokens that reference a column that
|
|
+** is being renamed by an ALTER TABLE statement. Return the "last"
|
|
+** RenameToken in the RenameCtx and remove that RenameToken from the
|
|
+** RenameContext. "Last" means the last RenameToken encountered when
|
|
+** the input SQL is parsed from left to right. Repeated calls to this routine
|
|
+** return all column name tokens in the order that they are encountered
|
|
+** in the SQL statement.
|
|
+*/
|
|
+static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
|
|
+ RenameToken *pBest = pCtx->pList;
|
|
+ RenameToken *pToken;
|
|
+ RenameToken **pp;
|
|
+
|
|
+ for(pToken=pBest->pNext; pToken; pToken=pToken->pNext){
|
|
+ if( pToken->t.z>pBest->t.z ) pBest = pToken;
|
|
+ }
|
|
+ for(pp=&pCtx->pList; *pp!=pBest; pp=&(*pp)->pNext);
|
|
+ *pp = pBest->pNext;
|
|
+
|
|
+ return pBest;
|
|
+}
|
|
+
|
|
+/*
|
|
+** An error occured while parsing or otherwise processing a database
|
|
+** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an
|
|
+** ALTER TABLE RENAME COLUMN program. The error message emitted by the
|
|
+** sub-routine is currently stored in pParse->zErrMsg. This function
|
|
+** adds context to the error message and then stores it in pCtx.
|
|
+*/
|
|
+static void renameColumnParseError(
|
|
+ sqlite3_context *pCtx,
|
|
+ int bPost,
|
|
+ sqlite3_value *pType,
|
|
+ sqlite3_value *pObject,
|
|
+ Parse *pParse
|
|
+){
|
|
+ const char *zT = (const char*)sqlite3_value_text(pType);
|
|
+ const char *zN = (const char*)sqlite3_value_text(pObject);
|
|
+ char *zErr;
|
|
+
|
|
+ zErr = sqlite3_mprintf("error in %s %s%s: %s",
|
|
+ zT, zN, (bPost ? " after rename" : ""),
|
|
+ pParse->zErrMsg
|
|
+ );
|
|
+ sqlite3_result_error(pCtx, zErr, -1);
|
|
+ sqlite3_free(zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** For each name in the the expression-list pEList (i.e. each
|
|
+** pEList->a[i].zName) that matches the string in zOld, extract the
|
|
+** corresponding rename-token from Parse object pParse and add it
|
|
+** to the RenameCtx pCtx.
|
|
+*/
|
|
+static void renameColumnElistNames(
|
|
+ Parse *pParse,
|
|
+ RenameCtx *pCtx,
|
|
+ ExprList *pEList,
|
|
+ const char *zOld
|
|
+){
|
|
+ if( pEList ){
|
|
+ int i;
|
|
+ for(i=0; i<pEList->nExpr; i++){
|
|
+ char *zName = pEList->a[i].zName;
|
|
+ if( 0==sqlite3_stricmp(zName, zOld) ){
|
|
+ renameTokenFind(pParse, pCtx, (void*)zName);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** For each name in the the id-list pIdList (i.e. each pIdList->a[i].zName)
|
|
+** that matches the string in zOld, extract the corresponding rename-token
|
|
+** from Parse object pParse and add it to the RenameCtx pCtx.
|
|
+*/
|
|
+static void renameColumnIdlistNames(
|
|
+ Parse *pParse,
|
|
+ RenameCtx *pCtx,
|
|
+ IdList *pIdList,
|
|
+ const char *zOld
|
|
+){
|
|
+ if( pIdList ){
|
|
+ int i;
|
|
+ for(i=0; i<pIdList->nId; i++){
|
|
+ char *zName = pIdList->a[i].zName;
|
|
+ if( 0==sqlite3_stricmp(zName, zOld) ){
|
|
+ renameTokenFind(pParse, pCtx, (void*)zName);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parse the SQL statement zSql using Parse object (*p). The Parse object
|
|
+** is initialized by this function before it is used.
|
|
+*/
|
|
+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 */
|
|
+){
|
|
+ int rc;
|
|
+ char *zErr = 0;
|
|
+
|
|
+ db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
|
|
+
|
|
+ /* Parse the SQL statement passed as the first argument. If no error
|
|
+ ** 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->db = db;
|
|
+ p->nQueryLoop = 1;
|
|
+ rc = sqlite3RunParser(p, zSql, &zErr);
|
|
+ assert( p->zErrMsg==0 );
|
|
+ assert( rc!=SQLITE_OK || zErr==0 );
|
|
+ assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 );
|
|
+ p->zErrMsg = zErr;
|
|
+ if( db->mallocFailed ) rc = SQLITE_NOMEM;
|
|
+ if( rc==SQLITE_OK
|
|
+ && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0
|
|
+ ){
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+ /* Ensure that all mappings in the Parse.pRename list really do map to
|
|
+ ** a part of the input string. */
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int nSql = sqlite3Strlen30(zSql);
|
|
+ RenameToken *pToken;
|
|
+ for(pToken=p->pRename; pToken; pToken=pToken->pNext){
|
|
+ assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ db->init.iDb = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function edits SQL statement zSql, replacing each token identified
|
|
+** by the linked list pRename with the text of zNew. If argument bQuote is
|
|
+** true, then zNew is always quoted first. If no error occurs, the result
|
|
+** is loaded into context object pCtx as the result.
|
|
+**
|
|
+** Or, if an error occurs (i.e. an OOM condition), an error is left in
|
|
+** pCtx and an SQLite error code returned.
|
|
+*/
|
|
+static int renameEditSql(
|
|
+ sqlite3_context *pCtx, /* Return result here */
|
|
+ RenameCtx *pRename, /* Rename context */
|
|
+ const char *zSql, /* SQL statement to edit */
|
|
+ const char *zNew, /* New token text */
|
|
+ int bQuote /* True to always quote token */
|
|
+){
|
|
+ int nNew = sqlite3Strlen30(zNew);
|
|
+ int nSql = sqlite3Strlen30(zSql);
|
|
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zQuot;
|
|
+ char *zOut;
|
|
+ int nQuot;
|
|
+
|
|
+ /* Set zQuot to point to a buffer containing a quoted copy of the
|
|
+ ** identifier zNew. If the corresponding identifier in the original
|
|
+ ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
|
|
+ ** point to zQuot so that all substitutions are made using the
|
|
+ ** quoted version of the new column name. */
|
|
+ zQuot = sqlite3MPrintf(db, "\"%w\"", zNew);
|
|
+ if( zQuot==0 ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }else{
|
|
+ nQuot = sqlite3Strlen30(zQuot);
|
|
+ }
|
|
+ if( bQuote ){
|
|
+ zNew = zQuot;
|
|
+ nNew = nQuot;
|
|
+ }
|
|
+
|
|
+ /* At this point pRename->pList contains a list of RenameToken objects
|
|
+ ** corresponding to all tokens in the input SQL that must be replaced
|
|
+ ** with the new column name. All that remains is to construct and
|
|
+ ** return the edited SQL string. */
|
|
+ assert( nQuot>=nNew );
|
|
+ zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
|
|
+ if( zOut ){
|
|
+ int nOut = nSql;
|
|
+ memcpy(zOut, zSql, nSql);
|
|
+ while( pRename->pList ){
|
|
+ int iOff; /* Offset of token to replace in zOut */
|
|
+ RenameToken *pBest = renameColumnTokenNext(pRename);
|
|
+
|
|
+ u32 nReplace;
|
|
+ const char *zReplace;
|
|
+ if( sqlite3IsIdChar(*pBest->t.z) ){
|
|
+ nReplace = nNew;
|
|
+ zReplace = zNew;
|
|
+ }else{
|
|
+ nReplace = nQuot;
|
|
+ zReplace = zQuot;
|
|
+ }
|
|
+
|
|
+ iOff = pBest->t.z - zSql;
|
|
+ if( pBest->t.n!=nReplace ){
|
|
+ memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
|
|
+ nOut - (iOff + pBest->t.n)
|
|
+ );
|
|
+ nOut += nReplace - pBest->t.n;
|
|
+ zOut[nOut] = '\0';
|
|
+ }
|
|
+ memcpy(&zOut[iOff], zReplace, nReplace);
|
|
+ sqlite3DbFree(db, pBest);
|
|
+ }
|
|
+
|
|
+ sqlite3_result_text(pCtx, zOut, -1, SQLITE_TRANSIENT);
|
|
+ sqlite3DbFree(db, zOut);
|
|
+ }else{
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }
|
|
+
|
|
+ sqlite3_free(zQuot);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
|
|
+** it was read from the schema of database zDb. Return SQLITE_OK if
|
|
+** successful. Otherwise, return an SQLite error code and leave an error
|
|
+** message in the Parse object.
|
|
+*/
|
|
+static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
|
+ sqlite3 *db = pParse->db;
|
|
+ Trigger *pNew = pParse->pNewTrigger;
|
|
+ TriggerStep *pStep;
|
|
+ NameContext sNC;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ memset(&sNC, 0, sizeof(sNC));
|
|
+ sNC.pParse = pParse;
|
|
+ assert( pNew->pTabSchema );
|
|
+ pParse->pTriggerTab = sqlite3FindTable(db, pNew->table,
|
|
+ db->aDb[sqlite3SchemaToIndex(db, pNew->pTabSchema)].zDbSName
|
|
+ );
|
|
+ pParse->eTriggerOp = pNew->op;
|
|
+ /* ALWAYS() because if the table of the trigger does not exist, the
|
|
+ ** error would have been hit before this point */
|
|
+ if( ALWAYS(pParse->pTriggerTab) ){
|
|
+ rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab);
|
|
+ }
|
|
+
|
|
+ /* Resolve symbols in WHEN clause */
|
|
+ if( rc==SQLITE_OK && pNew->pWhen ){
|
|
+ rc = sqlite3ResolveExprNames(&sNC, pNew->pWhen);
|
|
+ }
|
|
+
|
|
+ for(pStep=pNew->step_list; rc==SQLITE_OK && pStep; pStep=pStep->pNext){
|
|
+ if( pStep->pSelect ){
|
|
+ sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
|
|
+ if( pParse->nErr ) rc = pParse->rc;
|
|
+ }
|
|
+ if( rc==SQLITE_OK && pStep->zTarget ){
|
|
+ Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
|
|
+ if( pTarget==0 ){
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){
|
|
+ SrcList sSrc;
|
|
+ memset(&sSrc, 0, sizeof(sSrc));
|
|
+ sSrc.nSrc = 1;
|
|
+ sSrc.a[0].zName = pStep->zTarget;
|
|
+ sSrc.a[0].pTab = pTarget;
|
|
+ sNC.pSrcList = &sSrc;
|
|
+ if( pStep->pWhere ){
|
|
+ rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
|
|
+ }
|
|
+ assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
|
|
+ if( pStep->pUpsert ){
|
|
+ Upsert *pUpsert = pStep->pUpsert;
|
|
+ assert( rc==SQLITE_OK );
|
|
+ pUpsert->pUpsertSrc = &sSrc;
|
|
+ sNC.uNC.pUpsert = pUpsert;
|
|
+ sNC.ncFlags = NC_UUpsert;
|
|
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ ExprList *pUpsertSet = pUpsert->pUpsertSet;
|
|
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
|
+ }
|
|
+ sNC.ncFlags = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Invoke sqlite3WalkExpr() or sqlite3WalkSelect() on all Select or Expr
|
|
+** objects that are part of the trigger passed as the second argument.
|
|
+*/
|
|
+static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
|
|
+ TriggerStep *pStep;
|
|
+
|
|
+ /* Find tokens to edit in WHEN clause */
|
|
+ sqlite3WalkExpr(pWalker, pTrigger->pWhen);
|
|
+
|
|
+ /* Find tokens to edit in trigger steps */
|
|
+ for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
|
|
+ sqlite3WalkSelect(pWalker, pStep->pSelect);
|
|
+ sqlite3WalkExpr(pWalker, pStep->pWhere);
|
|
+ sqlite3WalkExprList(pWalker, pStep->pExprList);
|
|
+ if( pStep->pUpsert ){
|
|
+ Upsert *pUpsert = pStep->pUpsert;
|
|
+ sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget);
|
|
+ sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet);
|
|
+ sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
|
|
+ sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the contents of Parse object (*pParse). Do not free the memory
|
|
+** occupied by the Parse object itself.
|
|
+*/
|
|
+static void renameParseCleanup(Parse *pParse){
|
|
+ sqlite3 *db = pParse->db;
|
|
+ if( pParse->pVdbe ){
|
|
+ sqlite3VdbeFinalize(pParse->pVdbe);
|
|
+ }
|
|
+ sqlite3DeleteTable(db, pParse->pNewTable);
|
|
+ if( pParse->pNewIndex ) sqlite3FreeIndex(db, pParse->pNewIndex);
|
|
+ sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
|
+ sqlite3DbFree(db, pParse->zErrMsg);
|
|
+ renameTokenFree(db, pParse->pRename);
|
|
+ sqlite3ParserReset(pParse);
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function:
|
|
+**
|
|
+** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld)
|
|
+**
|
|
+** 0. zSql: SQL statement to rewrite
|
|
+** 1. type: Type of object ("table", "view" etc.)
|
|
+** 2. object: Name of object
|
|
+** 3. Database: Database name (e.g. "main")
|
|
+** 4. Table: Table name
|
|
+** 5. iCol: Index of column to rename
|
|
+** 6. zNew: New column name
|
|
+** 7. bQuote: Non-zero if the new column name should be quoted.
|
|
+** 8. bTemp: True if zSql comes from temp schema
|
|
+**
|
|
+** Do a column rename operation on the CREATE statement given in zSql.
|
|
+** The iCol-th column (left-most is 0) of table zTable is renamed from zCol
|
|
+** into zNew. The name should be quoted if bQuote is true.
|
|
+**
|
|
+** This function is used internally by the ALTER TABLE RENAME COLUMN command.
|
|
+** It is only accessible to SQL created using sqlite3NestedParse(). It is
|
|
+** not reachable from ordinary SQL passed into sqlite3_prepare().
|
|
+*/
|
|
+static void renameColumnFunc(
|
|
+ sqlite3_context *context,
|
|
+ int NotUsed,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ RenameCtx sCtx;
|
|
+ const char *zSql = (const char*)sqlite3_value_text(argv[0]);
|
|
+ const char *zDb = (const char*)sqlite3_value_text(argv[3]);
|
|
+ const char *zTable = (const char*)sqlite3_value_text(argv[4]);
|
|
+ int iCol = sqlite3_value_int(argv[5]);
|
|
+ const char *zNew = (const char*)sqlite3_value_text(argv[6]);
|
|
+ int bQuote = sqlite3_value_int(argv[7]);
|
|
+ int bTemp = sqlite3_value_int(argv[8]);
|
|
+ const char *zOld;
|
|
+ int rc;
|
|
+ Parse sParse;
|
|
+ Walker sWalker;
|
|
+ Index *pIdx;
|
|
+ int i;
|
|
+ Table *pTab;
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ sqlite3_xauth xAuth = db->xAuth;
|
|
+#endif
|
|
+
|
|
+ UNUSED_PARAMETER(NotUsed);
|
|
+ if( zSql==0 ) return;
|
|
+ if( zTable==0 ) return;
|
|
+ if( zNew==0 ) return;
|
|
+ if( iCol<0 ) return;
|
|
+ sqlite3BtreeEnterAll(db);
|
|
+ pTab = sqlite3FindTable(db, zTable, zDb);
|
|
+ if( pTab==0 || iCol>=pTab->nCol ){
|
|
+ sqlite3BtreeLeaveAll(db);
|
|
+ return;
|
|
+ }
|
|
+ zOld = pTab->aCol[iCol].zName;
|
|
+ memset(&sCtx, 0, sizeof(sCtx));
|
|
+ sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
|
|
+
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ db->xAuth = 0;
|
|
+#endif
|
|
+ rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp);
|
|
+
|
|
+ /* Find tokens that need to be replaced. */
|
|
+ memset(&sWalker, 0, sizeof(Walker));
|
|
+ sWalker.pParse = &sParse;
|
|
+ sWalker.xExprCallback = renameColumnExprCb;
|
|
+ sWalker.xSelectCallback = renameColumnSelectCb;
|
|
+ sWalker.u.pRename = &sCtx;
|
|
+
|
|
+ sCtx.pTab = pTab;
|
|
+ if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
|
|
+ if( sParse.pNewTable ){
|
|
+ Select *pSelect = sParse.pNewTable->pSelect;
|
|
+ if( pSelect ){
|
|
+ sParse.rc = SQLITE_OK;
|
|
+ sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
|
|
+ rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3WalkSelect(&sWalker, pSelect);
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
|
|
+ }else{
|
|
+ /* A regular table */
|
|
+ int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
|
|
+ FKey *pFKey;
|
|
+ assert( sParse.pNewTable->pSelect==0 );
|
|
+ sCtx.pTab = sParse.pNewTable;
|
|
+ if( bFKOnly==0 ){
|
|
+ renameTokenFind(
|
|
+ &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName
|
|
+ );
|
|
+ if( sCtx.iCol<0 ){
|
|
+ renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey);
|
|
+ }
|
|
+ sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
|
|
+ for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
+ sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
|
|
+ for(i=0; i<pFKey->nCol; i++){
|
|
+ if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
|
|
+ renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
|
|
+ }
|
|
+ if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
|
|
+ && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
|
|
+ ){
|
|
+ renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }else if( sParse.pNewIndex ){
|
|
+ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
|
|
+ sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
|
|
+ }else{
|
|
+ /* A trigger */
|
|
+ TriggerStep *pStep;
|
|
+ rc = renameResolveTrigger(&sParse, (bTemp ? 0 : zDb));
|
|
+ if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
|
|
+
|
|
+ for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
|
|
+ if( pStep->zTarget ){
|
|
+ Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
|
|
+ if( pTarget==pTab ){
|
|
+ if( pStep->pUpsert ){
|
|
+ ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
|
|
+ renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
|
|
+ }
|
|
+ renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
|
|
+ renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Find tokens to edit in UPDATE OF clause */
|
|
+ if( sParse.pTriggerTab==pTab ){
|
|
+ renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
|
|
+ }
|
|
+
|
|
+ /* Find tokens to edit in various expressions and selects */
|
|
+ renameWalkTrigger(&sWalker, sParse.pNewTrigger);
|
|
+ }
|
|
+
|
|
+ assert( rc==SQLITE_OK );
|
|
+ rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote);
|
|
+
|
|
+renameColumnFunc_done:
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ if( sParse.zErrMsg ){
|
|
+ renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
|
|
+ }else{
|
|
+ sqlite3_result_error_code(context, rc);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ renameParseCleanup(&sParse);
|
|
+ renameTokenFree(db, sCtx.pList);
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ db->xAuth = xAuth;
|
|
+#endif
|
|
+ sqlite3BtreeLeaveAll(db);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Walker expression callback used by "RENAME TABLE".
|
|
+*/
|
|
+static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
|
|
+ RenameCtx *p = pWalker->u.pRename;
|
|
+ if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){
|
|
+ renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab);
|
|
+ }
|
|
+ return WRC_Continue;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Walker select callback used by "RENAME TABLE".
|
|
+*/
|
|
+static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
|
|
+ int i;
|
|
+ RenameCtx *p = pWalker->u.pRename;
|
|
+ SrcList *pSrc = pSelect->pSrc;
|
|
+ for(i=0; i<pSrc->nSrc; i++){
|
|
+ struct SrcList_item *pItem = &pSrc->a[i];
|
|
+ if( pItem->pTab==p->pTab ){
|
|
+ renameTokenFind(pWalker->pParse, p, pItem->zName);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return WRC_Continue;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** This C function implements an SQL user function that is used by SQL code
|
|
+** generated by the ALTER TABLE ... RENAME command to modify the definition
|
|
+** of any foreign key constraints that use the table being renamed as the
|
|
+** parent table. It is passed three arguments:
|
|
+**
|
|
+** 0: The database containing the table being renamed.
|
|
+** 1. type: Type of object ("table", "view" etc.)
|
|
+** 2. object: Name of object
|
|
+** 3: The complete text of the schema statement being modified,
|
|
+** 4: The old name of the table being renamed, and
|
|
+** 5: The new name of the table being renamed.
|
|
+** 6: True if the schema statement comes from the temp db.
|
|
+**
|
|
+** It returns the new schema statement. For example:
|
|
+**
|
|
+** sqlite_rename_table('main', 'CREATE TABLE t1(a REFERENCES t2)','t2','t3',0)
|
|
+** -> 'CREATE TABLE t1(a REFERENCES t3)'
|
|
+*/
|
|
+static void renameTableFunc(
|
|
+ sqlite3_context *context,
|
|
+ int NotUsed,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ const char *zDb = (const char*)sqlite3_value_text(argv[0]);
|
|
+ const char *zInput = (const char*)sqlite3_value_text(argv[3]);
|
|
+ const char *zOld = (const char*)sqlite3_value_text(argv[4]);
|
|
+ const char *zNew = (const char*)sqlite3_value_text(argv[5]);
|
|
+ int bTemp = sqlite3_value_int(argv[6]);
|
|
+ UNUSED_PARAMETER(NotUsed);
|
|
+
|
|
+ if( zInput && zOld && zNew ){
|
|
+ Parse sParse;
|
|
+ int rc;
|
|
+ int bQuote = 1;
|
|
+ RenameCtx sCtx;
|
|
+ Walker sWalker;
|
|
+
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ sqlite3_xauth xAuth = db->xAuth;
|
|
+ db->xAuth = 0;
|
|
+#endif
|
|
+
|
|
+ sqlite3BtreeEnterAll(db);
|
|
+
|
|
+ memset(&sCtx, 0, sizeof(RenameCtx));
|
|
+ sCtx.pTab = sqlite3FindTable(db, zOld, zDb);
|
|
+ memset(&sWalker, 0, sizeof(Walker));
|
|
+ sWalker.pParse = &sParse;
|
|
+ sWalker.xExprCallback = renameTableExprCb;
|
|
+ sWalker.xSelectCallback = renameTableSelectCb;
|
|
+ sWalker.u.pRename = &sCtx;
|
|
+
|
|
+ rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int isLegacy = (db->flags & SQLITE_LegacyAlter);
|
|
+ if( sParse.pNewTable ){
|
|
+ Table *pTab = sParse.pNewTable;
|
|
+
|
|
+ if( pTab->pSelect ){
|
|
+ if( isLegacy==0 ){
|
|
+ NameContext sNC;
|
|
+ memset(&sNC, 0, sizeof(sNC));
|
|
+ sNC.pParse = &sParse;
|
|
+
|
|
+ sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
|
|
+ if( sParse.nErr ) rc = sParse.rc;
|
|
+ sqlite3WalkSelect(&sWalker, pTab->pSelect);
|
|
+ }
|
|
+ }else{
|
|
+ /* Modify any FK definitions to point to the new table. */
|
|
+#ifndef SQLITE_OMIT_FOREIGN_KEY
|
|
+ if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){
|
|
+ FKey *pFKey;
|
|
+ for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
|
|
+ if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
|
|
+ renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* If this is the table being altered, fix any table refs in CHECK
|
|
+ ** expressions. Also update the name that appears right after the
|
|
+ ** "CREATE [VIRTUAL] TABLE" bit. */
|
|
+ if( sqlite3_stricmp(zOld, pTab->zName)==0 ){
|
|
+ sCtx.pTab = pTab;
|
|
+ if( isLegacy==0 ){
|
|
+ sqlite3WalkExprList(&sWalker, pTab->pCheck);
|
|
+ }
|
|
+ renameTokenFind(&sParse, &sCtx, pTab->zName);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ else if( sParse.pNewIndex ){
|
|
+ renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName);
|
|
+ if( isLegacy==0 ){
|
|
+ sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
|
|
+ }
|
|
+ }
|
|
+
|
|
+#ifndef SQLITE_OMIT_TRIGGER
|
|
+ else{
|
|
+ Trigger *pTrigger = sParse.pNewTrigger;
|
|
+ TriggerStep *pStep;
|
|
+ if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld)
|
|
+ && sCtx.pTab->pSchema==pTrigger->pTabSchema
|
|
+ ){
|
|
+ renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table);
|
|
+ }
|
|
+
|
|
+ if( isLegacy==0 ){
|
|
+ rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ renameWalkTrigger(&sWalker, pTrigger);
|
|
+ for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
|
|
+ if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
|
|
+ renameTokenFind(&sParse, &sCtx, pStep->zTarget);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote);
|
|
+ }
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ if( sParse.zErrMsg ){
|
|
+ renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
|
|
+ }else{
|
|
+ sqlite3_result_error_code(context, rc);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ renameParseCleanup(&sParse);
|
|
+ renameTokenFree(db, sCtx.pList);
|
|
+ sqlite3BtreeLeaveAll(db);
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ db->xAuth = xAuth;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+** An SQL user function that checks that there are no parse or symbol
|
|
+** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement.
|
|
+** After an ALTER TABLE .. RENAME operation is performed and the schema
|
|
+** reloaded, this function is called on each SQL statement in the schema
|
|
+** to ensure that it is still usable.
|
|
+**
|
|
+** 0: Database name ("main", "temp" etc.).
|
|
+** 1: SQL statement.
|
|
+** 2: Object type ("view", "table", "trigger" or "index").
|
|
+** 3: Object name.
|
|
+** 4: True if object is from temp schema.
|
|
+**
|
|
+** Unless it finds an error, this function normally returns NULL. However, it
|
|
+** returns integer value 1 if:
|
|
+**
|
|
+** * the SQL argument creates a trigger, and
|
|
+** * the table that the trigger is attached to is in database zDb.
|
|
+*/
|
|
+static void renameTableTest(
|
|
+ sqlite3_context *context,
|
|
+ int NotUsed,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ char const *zDb = (const char*)sqlite3_value_text(argv[0]);
|
|
+ char const *zInput = (const char*)sqlite3_value_text(argv[1]);
|
|
+ int bTemp = sqlite3_value_int(argv[4]);
|
|
+ int isLegacy = (db->flags & SQLITE_LegacyAlter);
|
|
+
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ sqlite3_xauth xAuth = db->xAuth;
|
|
+ db->xAuth = 0;
|
|
+#endif
|
|
+
|
|
+ UNUSED_PARAMETER(NotUsed);
|
|
+ if( zDb && zInput ){
|
|
+ int rc;
|
|
+ Parse sParse;
|
|
+ rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
|
|
+ NameContext sNC;
|
|
+ memset(&sNC, 0, sizeof(sNC));
|
|
+ sNC.pParse = &sParse;
|
|
+ sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC);
|
|
+ if( sParse.nErr ) rc = sParse.rc;
|
|
+ }
|
|
+
|
|
+ else if( sParse.pNewTrigger ){
|
|
+ if( isLegacy==0 ){
|
|
+ rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema);
|
|
+ int i2 = sqlite3FindDbName(db, zDb);
|
|
+ if( i1==i2 ) sqlite3_result_int(context, 1);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
|
|
+ }
|
|
+ renameParseCleanup(&sParse);
|
|
+ }
|
|
+
|
|
+#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
+ db->xAuth = xAuth;
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** Register built-in functions used to help implement ALTER TABLE
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3AlterFunctions(void){
|
|
+ static FuncDef aAlterTableFuncs[] = {
|
|
+ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
|
|
+ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
|
|
+ INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest),
|
|
+ };
|
|
+ sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
|
|
+}
|
|
#endif /* SQLITE_ALTER_TABLE */
|
|
|
|
/************** End of alter.c ***********************************************/
|
|
@@ -97912,6 +103313,10 @@
|
|
"DELETE FROM %Q.%s WHERE %s=%Q",
|
|
pDb->zDbSName, zTab, zWhereType, zWhere
|
|
);
|
|
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
+ }else if( db->xPreUpdateCallback ){
|
|
+ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab);
|
|
+#endif
|
|
}else{
|
|
/* The sqlite_stat[134] table already exists. Delete all rows. */
|
|
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
|
|
@@ -98159,6 +103564,7 @@
|
|
0, /* pNext */
|
|
statInit, /* xSFunc */
|
|
0, /* xFinalize */
|
|
+ 0, 0, /* xValue, xInverse */
|
|
"stat_init", /* zName */
|
|
{0}
|
|
};
|
|
@@ -98475,6 +103881,7 @@
|
|
0, /* pNext */
|
|
statPush, /* xSFunc */
|
|
0, /* xFinalize */
|
|
+ 0, 0, /* xValue, xInverse */
|
|
"stat_push", /* zName */
|
|
{0}
|
|
};
|
|
@@ -98626,6 +104033,7 @@
|
|
0, /* pNext */
|
|
statGet, /* xSFunc */
|
|
0, /* xFinalize */
|
|
+ 0, 0, /* xValue, xInverse */
|
|
"stat_get", /* zName */
|
|
{0}
|
|
};
|
|
@@ -98676,6 +104084,9 @@
|
|
int regIdxname = iMem++; /* Register containing index name */
|
|
int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
|
|
int regPrev = iMem; /* MUST BE LAST (see below) */
|
|
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
+ Table *pStat1 = 0;
|
|
+#endif
|
|
|
|
pParse->nMem = MAX(pParse->nMem, iMem);
|
|
v = sqlite3GetVdbe(pParse);
|
|
@@ -98686,7 +104097,7 @@
|
|
/* Do not gather statistics on views or virtual tables */
|
|
return;
|
|
}
|
|
- if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){
|
|
+ if( sqlite3_strlike("sqlite\\_%", pTab->zName, '\\')==0 ){
|
|
/* Do not gather statistics on system tables */
|
|
return;
|
|
}
|
|
@@ -98701,6 +104112,18 @@
|
|
}
|
|
#endif
|
|
|
|
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
+ if( db->xPreUpdateCallback ){
|
|
+ pStat1 = (Table*)sqlite3DbMallocZero(db, sizeof(Table) + 13);
|
|
+ if( pStat1==0 ) return;
|
|
+ pStat1->zName = (char*)&pStat1[1];
|
|
+ memcpy(pStat1->zName, "sqlite_stat1", 13);
|
|
+ pStat1->nCol = 3;
|
|
+ pStat1->iPKey = -1;
|
|
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB);
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* Establish a read-lock on the table at the shared-cache level.
|
|
** Open a read-only cursor on the table. Also allocate a cursor number
|
|
** to use for scanning indexes (iIdxCur). No index cursor is opened at
|
|
@@ -98902,6 +104325,9 @@
|
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
|
|
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
|
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
|
|
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
+ sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE);
|
|
+#endif
|
|
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
|
|
|
/* Add the entries to the stat3 or stat4 table. */
|
|
@@ -98927,10 +104353,7 @@
|
|
callStatGet(v, regStat4, STAT_GET_NLT, regLt);
|
|
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
|
|
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
|
|
- /* We know that the regSampleRowid row exists because it was read by
|
|
- ** the previous loop. Thus the not-found jump of seekOp will never
|
|
- ** be taken */
|
|
- VdbeCoverageNeverTaken(v);
|
|
+ VdbeCoverage(v);
|
|
#ifdef SQLITE_ENABLE_STAT3
|
|
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
|
|
#else
|
|
@@ -98965,6 +104388,9 @@
|
|
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
|
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
|
|
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
|
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
+ sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE);
|
|
+#endif
|
|
sqlite3VdbeJumpHere(v, jZeroRows);
|
|
}
|
|
}
|
|
@@ -99567,7 +104993,7 @@
|
|
|
|
/* Load the statistics from the sqlite_stat4 table. */
|
|
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
|
- if( rc==SQLITE_OK && OptimizationEnabled(db, SQLITE_Stat34) ){
|
|
+ if( rc==SQLITE_OK ){
|
|
db->lookaside.bDisable++;
|
|
rc = loadStat4(db, sInfo.zDatabase);
|
|
db->lookaside.bDisable--;
|
|
@@ -99647,6 +105073,10 @@
|
|
**
|
|
** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the
|
|
** third argument.
|
|
+**
|
|
+** If the db->init.reopenMemdb flags is set, then instead of attaching a
|
|
+** new database, close the database on db->init.iDb and reopen it as an
|
|
+** empty MemDB.
|
|
*/
|
|
static void attachFunc(
|
|
sqlite3_context *context,
|
|
@@ -99667,70 +105097,86 @@
|
|
sqlite3_vfs *pVfs;
|
|
|
|
UNUSED_PARAMETER(NotUsed);
|
|
-
|
|
zFile = (const char *)sqlite3_value_text(argv[0]);
|
|
zName = (const char *)sqlite3_value_text(argv[1]);
|
|
if( zFile==0 ) zFile = "";
|
|
if( zName==0 ) zName = "";
|
|
|
|
- /* Check for the following errors:
|
|
- **
|
|
- ** * Too many attached databases,
|
|
- ** * Transaction currently open
|
|
- ** * Specified database name already being used.
|
|
- */
|
|
- if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
|
|
- zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
|
|
- db->aLimit[SQLITE_LIMIT_ATTACHED]
|
|
- );
|
|
- goto attach_error;
|
|
- }
|
|
- if( !db->autoCommit ){
|
|
- zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction");
|
|
- goto attach_error;
|
|
- }
|
|
- for(i=0; i<db->nDb; i++){
|
|
- char *z = db->aDb[i].zDbSName;
|
|
- assert( z && zName );
|
|
- if( sqlite3StrICmp(z, zName)==0 ){
|
|
- zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
+# define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb)
|
|
+#else
|
|
+# define REOPEN_AS_MEMDB(db) (0)
|
|
+#endif
|
|
+
|
|
+ if( REOPEN_AS_MEMDB(db) ){
|
|
+ /* This is not a real ATTACH. Instead, this routine is being called
|
|
+ ** from sqlite3_deserialize() to close database db->init.iDb and
|
|
+ ** reopen it as a MemDB */
|
|
+ pVfs = sqlite3_vfs_find("memdb");
|
|
+ if( pVfs==0 ) return;
|
|
+ pNew = &db->aDb[db->init.iDb];
|
|
+ if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt);
|
|
+ pNew->pBt = 0;
|
|
+ pNew->pSchema = 0;
|
|
+ rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB);
|
|
+ }else{
|
|
+ /* This is a real ATTACH
|
|
+ **
|
|
+ ** Check for the following errors:
|
|
+ **
|
|
+ ** * Too many attached databases,
|
|
+ ** * Transaction currently open
|
|
+ ** * Specified database name already being used.
|
|
+ */
|
|
+ if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
|
|
+ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
|
|
+ db->aLimit[SQLITE_LIMIT_ATTACHED]
|
|
+ );
|
|
goto attach_error;
|
|
}
|
|
+ for(i=0; i<db->nDb; i++){
|
|
+ char *z = db->aDb[i].zDbSName;
|
|
+ assert( z && zName );
|
|
+ if( sqlite3StrICmp(z, zName)==0 ){
|
|
+ zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
|
+ goto attach_error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Allocate the new entry in the db->aDb[] array and initialize the schema
|
|
+ ** hash tables.
|
|
+ */
|
|
+ if( db->aDb==db->aDbStatic ){
|
|
+ aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
|
|
+ if( aNew==0 ) return;
|
|
+ memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
|
|
+ }else{
|
|
+ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
|
|
+ if( aNew==0 ) return;
|
|
+ }
|
|
+ db->aDb = aNew;
|
|
+ pNew = &db->aDb[db->nDb];
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+
|
|
+ /* Open the database file. If the btree is successfully opened, use
|
|
+ ** it to obtain the database schema. At this point the schema may
|
|
+ ** or may not be initialized.
|
|
+ */
|
|
+ flags = db->openFlags;
|
|
+ rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
|
|
+ sqlite3_result_error(context, zErr, -1);
|
|
+ sqlite3_free(zErr);
|
|
+ return;
|
|
+ }
|
|
+ assert( pVfs );
|
|
+ flags |= SQLITE_OPEN_MAIN_DB;
|
|
+ rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags);
|
|
+ sqlite3_free( zPath );
|
|
+ db->nDb++;
|
|
}
|
|
-
|
|
- /* Allocate the new entry in the db->aDb[] array and initialize the schema
|
|
- ** hash tables.
|
|
- */
|
|
- if( db->aDb==db->aDbStatic ){
|
|
- aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
|
|
- if( aNew==0 ) return;
|
|
- memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
|
|
- }else{
|
|
- aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
|
|
- if( aNew==0 ) return;
|
|
- }
|
|
- db->aDb = aNew;
|
|
- pNew = &db->aDb[db->nDb];
|
|
- memset(pNew, 0, sizeof(*pNew));
|
|
-
|
|
- /* Open the database file. If the btree is successfully opened, use
|
|
- ** it to obtain the database schema. At this point the schema may
|
|
- ** or may not be initialized.
|
|
- */
|
|
- flags = db->openFlags;
|
|
- rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
|
|
- if( rc!=SQLITE_OK ){
|
|
- if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
|
|
- sqlite3_result_error(context, zErr, -1);
|
|
- sqlite3_free(zErr);
|
|
- return;
|
|
- }
|
|
- assert( pVfs );
|
|
- flags |= SQLITE_OPEN_MAIN_DB;
|
|
- rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags);
|
|
- sqlite3_free( zPath );
|
|
- db->nDb++;
|
|
- db->skipBtreeMutex = 0;
|
|
+ db->noSharedCache = 0;
|
|
if( rc==SQLITE_CONSTRAINT ){
|
|
rc = SQLITE_ERROR;
|
|
zErrDyn = sqlite3MPrintf(db, "database is already attached");
|
|
@@ -99756,7 +105202,7 @@
|
|
sqlite3BtreeLeave(pNew->pBt);
|
|
}
|
|
pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
|
- pNew->zDbSName = sqlite3DbStrDup(db, zName);
|
|
+ if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName);
|
|
if( rc==SQLITE_OK && pNew->zDbSName==0 ){
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -99796,13 +105242,16 @@
|
|
|
|
/* If the file was opened successfully, read the schema for the new database.
|
|
** If this fails, or if opening the file failed, then close the file and
|
|
- ** remove the entry from the db->aDb[] array. i.e. put everything back the way
|
|
- ** we found it.
|
|
+ ** remove the entry from the db->aDb[] array. i.e. put everything back the
|
|
+ ** way we found it.
|
|
*/
|
|
if( rc==SQLITE_OK ){
|
|
sqlite3BtreeEnterAll(db);
|
|
+ db->init.iDb = 0;
|
|
+ db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
|
|
rc = sqlite3Init(db, &zErrDyn);
|
|
sqlite3BtreeLeaveAll(db);
|
|
+ assert( zErrDyn==0 || rc!=SQLITE_OK );
|
|
}
|
|
#ifdef SQLITE_USER_AUTHENTICATION
|
|
if( rc==SQLITE_OK ){
|
|
@@ -99814,22 +105263,24 @@
|
|
}
|
|
#endif
|
|
if( rc ){
|
|
- int iDb = db->nDb - 1;
|
|
- assert( iDb>=2 );
|
|
- if( db->aDb[iDb].pBt ){
|
|
- sqlite3BtreeClose(db->aDb[iDb].pBt);
|
|
- db->aDb[iDb].pBt = 0;
|
|
- db->aDb[iDb].pSchema = 0;
|
|
+ if( !REOPEN_AS_MEMDB(db) ){
|
|
+ int iDb = db->nDb - 1;
|
|
+ assert( iDb>=2 );
|
|
+ if( db->aDb[iDb].pBt ){
|
|
+ sqlite3BtreeClose(db->aDb[iDb].pBt);
|
|
+ db->aDb[iDb].pBt = 0;
|
|
+ db->aDb[iDb].pSchema = 0;
|
|
+ }
|
|
+ sqlite3ResetAllSchemasOfConnection(db);
|
|
+ db->nDb = iDb;
|
|
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
|
+ sqlite3OomFault(db);
|
|
+ sqlite3DbFree(db, zErrDyn);
|
|
+ zErrDyn = sqlite3MPrintf(db, "out of memory");
|
|
+ }else if( zErrDyn==0 ){
|
|
+ zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile);
|
|
+ }
|
|
}
|
|
- sqlite3ResetAllSchemasOfConnection(db);
|
|
- db->nDb = iDb;
|
|
- if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
|
- sqlite3OomFault(db);
|
|
- sqlite3DbFree(db, zErrDyn);
|
|
- zErrDyn = sqlite3MPrintf(db, "out of memory");
|
|
- }else if( zErrDyn==0 ){
|
|
- zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile);
|
|
- }
|
|
goto attach_error;
|
|
}
|
|
|
|
@@ -99880,11 +105331,6 @@
|
|
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
|
|
goto detach_error;
|
|
}
|
|
- if( !db->autoCommit ){
|
|
- sqlite3_snprintf(sizeof(zErr), zErr,
|
|
- "cannot DETACH database within transaction");
|
|
- goto detach_error;
|
|
- }
|
|
if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
|
|
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
|
|
goto detach_error;
|
|
@@ -99986,6 +105432,7 @@
|
|
0, /* pNext */
|
|
detachFunc, /* xSFunc */
|
|
0, /* xFinalize */
|
|
+ 0, 0, /* xValue, xInverse */
|
|
"sqlite_detach", /* zName */
|
|
{0}
|
|
};
|
|
@@ -100005,6 +105452,7 @@
|
|
0, /* pNext */
|
|
attachFunc, /* xSFunc */
|
|
0, /* xFinalize */
|
|
+ 0, 0, /* xValue, xInverse */
|
|
"sqlite_attach", /* zName */
|
|
{0}
|
|
};
|
|
@@ -100075,6 +105523,9 @@
|
|
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
|
|
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
|
|
#endif
|
|
+ if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
|
|
+ return 1;
|
|
+ }
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -100105,8 +105556,13 @@
|
|
if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
|
|
return 1;
|
|
}
|
|
- if( sqlite3FixExpr(pFix, pSelect->pOffset) ){
|
|
- return 1;
|
|
+ if( pSelect->pWith ){
|
|
+ int i;
|
|
+ for(i=0; i<pSelect->pWith->nCte; i++){
|
|
+ if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
}
|
|
pSelect = pSelect->pPrior;
|
|
}
|
|
@@ -100169,6 +105625,18 @@
|
|
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
|
return 1;
|
|
}
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+ if( pStep->pUpsert ){
|
|
+ Upsert *pUp = pStep->pUpsert;
|
|
+ if( sqlite3FixExprList(pFix, pUp->pUpsertTarget)
|
|
+ || sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere)
|
|
+ || sqlite3FixExprList(pFix, pUp->pUpsertSet)
|
|
+ || sqlite3FixExpr(pFix, pUp->pUpsertWhere)
|
|
+ ){
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
pStep = pStep->pNext;
|
|
}
|
|
return 0;
|
|
@@ -100257,7 +105725,7 @@
|
|
sqlite3_mutex_enter(db->mutex);
|
|
db->xAuth = (sqlite3_xauth)xAuth;
|
|
db->pAuthArg = pArg;
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
sqlite3_mutex_leave(db->mutex);
|
|
return SQLITE_OK;
|
|
}
|
|
@@ -100297,11 +105765,9 @@
|
|
#endif
|
|
);
|
|
if( rc==SQLITE_DENY ){
|
|
- if( db->nDb>2 || iDb!=0 ){
|
|
- sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol);
|
|
- }else{
|
|
- sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol);
|
|
- }
|
|
+ char *z = sqlite3_mprintf("%s.%s", zTab, zCol);
|
|
+ if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z);
|
|
+ sqlite3ErrorMsg(pParse, "access to %z is prohibited", z);
|
|
pParse->rc = SQLITE_AUTH;
|
|
}else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){
|
|
sqliteAuthBadReturnCode(pParse);
|
|
@@ -100331,6 +105797,8 @@
|
|
int iDb; /* The index of the database the expression refers to */
|
|
int iCol; /* Index of column in table */
|
|
|
|
+ assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
|
|
+ assert( !IN_RENAME_OBJECT || db->xAuth==0 );
|
|
if( db->xAuth==0 ) return;
|
|
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
|
|
if( iDb<0 ){
|
|
@@ -100339,7 +105807,6 @@
|
|
return;
|
|
}
|
|
|
|
- assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
|
|
if( pExpr->op==TK_TRIGGER ){
|
|
pTab = pParse->pTriggerTab;
|
|
}else{
|
|
@@ -100388,7 +105855,8 @@
|
|
/* Don't do any authorization checks if the database is initialising
|
|
** or if the parser is being invoked from within sqlite3_declare_vtab.
|
|
*/
|
|
- if( db->init.busy || IN_DECLARE_VTAB ){
|
|
+ assert( !IN_RENAME_OBJECT || db->xAuth==0 );
|
|
+ if( db->init.busy || IN_SPECIAL_PARSE ){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -100680,7 +106148,6 @@
|
|
/* Get the VDBE program ready for execution
|
|
*/
|
|
if( v && pParse->nErr==0 && !db->mallocFailed ){
|
|
- assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
|
|
/* A minimum of one cursor is required if autoincrement is used
|
|
* See ticket [a696379c1f08866] */
|
|
if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
|
|
@@ -100798,29 +106265,30 @@
|
|
const char *zDbase /* Name of the database. Might be NULL */
|
|
){
|
|
Table *p;
|
|
+ sqlite3 *db = pParse->db;
|
|
|
|
/* Read the database schema. If an error occurs, leave an error message
|
|
** and code in pParse and return NULL. */
|
|
- if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
|
+ if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0
|
|
+ && SQLITE_OK!=sqlite3ReadSchema(pParse)
|
|
+ ){
|
|
return 0;
|
|
}
|
|
|
|
- p = sqlite3FindTable(pParse->db, zName, zDbase);
|
|
+ p = sqlite3FindTable(db, zName, zDbase);
|
|
if( p==0 ){
|
|
const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- if( sqlite3FindDbName(pParse->db, zDbase)<1 ){
|
|
- /* If zName is the not the name of a table in the schema created using
|
|
- ** CREATE, then check to see if it is the name of an virtual table that
|
|
- ** can be an eponymous virtual table. */
|
|
- Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
|
|
- if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
|
|
- pMod = sqlite3PragmaVtabRegister(pParse->db, zName);
|
|
- }
|
|
- if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
|
|
- return pMod->pEpoTab;
|
|
- }
|
|
+ /* If zName is the not the name of a table in the schema created using
|
|
+ ** CREATE, then check to see if it is the name of an virtual table that
|
|
+ ** can be an eponymous virtual table. */
|
|
+ Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
|
|
+ if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
|
|
+ pMod = sqlite3PragmaVtabRegister(db, zName);
|
|
}
|
|
+ if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
|
|
+ return pMod->pEpoTab;
|
|
+ }
|
|
#endif
|
|
if( (flags & LOCATE_NOERR)==0 ){
|
|
if( zDbase ){
|
|
@@ -100892,7 +106360,7 @@
|
|
/*
|
|
** Reclaim the memory used by an index
|
|
*/
|
|
-static void freeIndex(sqlite3 *db, Index *p){
|
|
+SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3 *db, Index *p){
|
|
#ifndef SQLITE_OMIT_ANALYZE
|
|
sqlite3DeleteIndexSamples(db, p);
|
|
#endif
|
|
@@ -100932,9 +106400,9 @@
|
|
p->pNext = pIndex->pNext;
|
|
}
|
|
}
|
|
- freeIndex(db, pIndex);
|
|
+ sqlite3FreeIndex(db, pIndex);
|
|
}
|
|
- db->flags |= SQLITE_InternChanges;
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
}
|
|
|
|
/*
|
|
@@ -100969,28 +106437,27 @@
|
|
|
|
/*
|
|
** Reset the schema for the database at index iDb. Also reset the
|
|
-** TEMP schema.
|
|
+** TEMP schema. The reset is deferred if db->nSchemaLock is not zero.
|
|
+** Deferred resets may be run by calling with iDb<0.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
|
|
- Db *pDb;
|
|
+ int i;
|
|
assert( iDb<db->nDb );
|
|
|
|
- /* Case 1: Reset the single schema identified by iDb */
|
|
- pDb = &db->aDb[iDb];
|
|
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
|
- assert( pDb->pSchema!=0 );
|
|
- sqlite3SchemaClear(pDb->pSchema);
|
|
+ if( iDb>=0 ){
|
|
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
|
+ DbSetProperty(db, iDb, DB_ResetWanted);
|
|
+ DbSetProperty(db, 1, DB_ResetWanted);
|
|
+ db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
|
|
+ }
|
|
|
|
- /* If any database other than TEMP is reset, then also reset TEMP
|
|
- ** since TEMP might be holding triggers that reference tables in the
|
|
- ** other database.
|
|
- */
|
|
- if( iDb!=1 ){
|
|
- pDb = &db->aDb[1];
|
|
- assert( pDb->pSchema!=0 );
|
|
- sqlite3SchemaClear(pDb->pSchema);
|
|
+ if( db->nSchemaLock==0 ){
|
|
+ for(i=0; i<db->nDb; i++){
|
|
+ if( DbHasProperty(db, i, DB_ResetWanted) ){
|
|
+ sqlite3SchemaClear(db->aDb[i].pSchema);
|
|
+ }
|
|
+ }
|
|
}
|
|
- return;
|
|
}
|
|
|
|
/*
|
|
@@ -101003,13 +106470,19 @@
|
|
for(i=0; i<db->nDb; i++){
|
|
Db *pDb = &db->aDb[i];
|
|
if( pDb->pSchema ){
|
|
- sqlite3SchemaClear(pDb->pSchema);
|
|
+ if( db->nSchemaLock==0 ){
|
|
+ sqlite3SchemaClear(pDb->pSchema);
|
|
+ }else{
|
|
+ DbSetProperty(db, i, DB_ResetWanted);
|
|
+ }
|
|
}
|
|
}
|
|
- db->flags &= ~SQLITE_InternChanges;
|
|
+ db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk);
|
|
sqlite3VtabUnlockList(db);
|
|
sqlite3BtreeLeaveAll(db);
|
|
- sqlite3CollapseDatabaseArray(db);
|
|
+ if( db->nSchemaLock==0 ){
|
|
+ sqlite3CollapseDatabaseArray(db);
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
@@ -101016,7 +106489,7 @@
|
|
** This routine is called when a commit occurs.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){
|
|
- db->flags &= ~SQLITE_InternChanges;
|
|
+ db->mDbFlags &= ~DBFLAG_SchemaChange;
|
|
}
|
|
|
|
/*
|
|
@@ -101054,13 +106527,16 @@
|
|
*/
|
|
static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
|
|
Index *pIndex, *pNext;
|
|
- TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */
|
|
|
|
+#ifdef SQLITE_DEBUG
|
|
/* Record the number of outstanding lookaside allocations in schema Tables
|
|
** prior to doing any free() operations. Since schema Tables do not use
|
|
** lookaside, this number should not change. */
|
|
- TESTONLY( nLookaside = (db && (pTable->tabFlags & TF_Ephemeral)==0) ?
|
|
- db->lookaside.nOut : 0 );
|
|
+ int nLookaside = 0;
|
|
+ if( db && (pTable->tabFlags & TF_Ephemeral)==0 ){
|
|
+ nLookaside = sqlite3LookasideUsed(db, 0);
|
|
+ }
|
|
+#endif
|
|
|
|
/* Delete all indices associated with this table. */
|
|
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
|
|
@@ -101075,7 +106551,7 @@
|
|
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
|
|
assert( pOld==pIndex || pOld==0 );
|
|
}
|
|
- freeIndex(db, pIndex);
|
|
+ sqlite3FreeIndex(db, pIndex);
|
|
}
|
|
|
|
/* Delete any foreign keys attached to this table. */
|
|
@@ -101083,6 +106559,12 @@
|
|
|
|
/* Delete the Table structure itself.
|
|
*/
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ if( pTable->pColHash ){
|
|
+ sqlite3HashClear(pTable->pColHash);
|
|
+ sqlite3_free(pTable->pColHash);
|
|
+ }
|
|
+#endif
|
|
sqlite3DeleteColumnNames(db, pTable);
|
|
sqlite3DbFree(db, pTable->zName);
|
|
sqlite3DbFree(db, pTable->zColAff);
|
|
@@ -101094,7 +106576,7 @@
|
|
sqlite3DbFree(db, pTable);
|
|
|
|
/* Verify that no lookaside memory was used by schema tables */
|
|
- assert( nLookaside==0 || nLookaside==db->lookaside.nOut );
|
|
+ assert( nLookaside==0 || nLookaside==sqlite3LookasideUsed(db,0) );
|
|
}
|
|
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
|
/* Do not delete the table until the reference count reaches zero. */
|
|
@@ -101120,7 +106602,7 @@
|
|
pDb = &db->aDb[iDb];
|
|
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, 0);
|
|
sqlite3DeleteTable(db, p);
|
|
- db->flags |= SQLITE_InternChanges;
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
}
|
|
|
|
/*
|
|
@@ -101233,7 +106715,8 @@
|
|
return -1;
|
|
}
|
|
}else{
|
|
- assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
|
|
+ assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT
|
|
+ || (db->mDbFlags & DBFLAG_Vacuum)!=0);
|
|
iDb = db->init.iDb;
|
|
*pUnqual = pName1;
|
|
}
|
|
@@ -101241,6 +106724,20 @@
|
|
}
|
|
|
|
/*
|
|
+** True if PRAGMA writable_schema is ON
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3 *db){
|
|
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==0 );
|
|
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==
|
|
+ SQLITE_WriteSchema );
|
|
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==
|
|
+ SQLITE_Defensive );
|
|
+ testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==
|
|
+ (SQLITE_WriteSchema|SQLITE_Defensive) );
|
|
+ return (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==SQLITE_WriteSchema;
|
|
+}
|
|
+
|
|
+/*
|
|
** This routine is used to check if the UTF-8 string zName is a legal
|
|
** unqualified name for a new schema object (table, index, view or
|
|
** trigger). All names are legal except those that begin with the string
|
|
@@ -101249,7 +106746,7 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){
|
|
if( !pParse->db->init.busy && pParse->nested==0
|
|
- && (pParse->db->flags & SQLITE_WriteSchema)==0
|
|
+ && sqlite3WritableSchema(pParse->db)==0
|
|
&& 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
|
|
sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
|
|
return SQLITE_ERROR;
|
|
@@ -101327,6 +106824,9 @@
|
|
}
|
|
if( !OMIT_TEMPDB && isTemp ) iDb = 1;
|
|
zName = sqlite3NameFromToken(db, pName);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenMap(pParse, (void*)zName, pName);
|
|
+ }
|
|
}
|
|
pParse->sNameToken = *pName;
|
|
if( zName==0 ) return;
|
|
@@ -101362,7 +106862,7 @@
|
|
** and types will be used, so there is no need to test for namespace
|
|
** collisions.
|
|
*/
|
|
- if( !IN_DECLARE_VTAB ){
|
|
+ if( !IN_SPECIAL_PARSE ){
|
|
char *zDb = db->aDb[iDb].zDbSName;
|
|
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
|
goto begin_table_error;
|
|
@@ -101465,7 +106965,8 @@
|
|
}else
|
|
#endif
|
|
{
|
|
- pParse->addrCrTab = sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
|
|
+ pParse->addrCrTab =
|
|
+ sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
|
|
}
|
|
sqlite3OpenMasterTable(pParse, iDb);
|
|
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
|
|
@@ -101514,14 +107015,13 @@
|
|
Column *pCol;
|
|
sqlite3 *db = pParse->db;
|
|
if( (p = pParse->pNewTable)==0 ) return;
|
|
-#if SQLITE_MAX_COLUMN
|
|
if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
|
sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
|
|
return;
|
|
}
|
|
-#endif
|
|
z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
|
|
if( z==0 ) return;
|
|
+ if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
|
|
memcpy(z, pName->z, pName->n);
|
|
z[pName->n] = 0;
|
|
sqlite3Dequote(z);
|
|
@@ -101548,15 +107048,20 @@
|
|
|
|
if( pType->n==0 ){
|
|
/* If there is no type specified, columns have the default affinity
|
|
- ** 'BLOB'. */
|
|
+ ** 'BLOB' with a default size of 4 bytes. */
|
|
pCol->affinity = SQLITE_AFF_BLOB;
|
|
pCol->szEst = 1;
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( 4>=sqlite3GlobalConfig.szSorterRef ){
|
|
+ pCol->colFlags |= COLFLAG_SORTERREF;
|
|
+ }
|
|
+#endif
|
|
}else{
|
|
zType = z + sqlite3Strlen30(z) + 1;
|
|
memcpy(zType, pType->z, pType->n);
|
|
zType[pType->n] = 0;
|
|
sqlite3Dequote(zType);
|
|
- pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
|
|
+ pCol->affinity = sqlite3AffinityType(zType, pCol);
|
|
pCol->colFlags |= COLFLAG_HASTYPE;
|
|
}
|
|
p->nCol++;
|
|
@@ -101571,10 +107076,24 @@
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){
|
|
Table *p;
|
|
+ Column *pCol;
|
|
p = pParse->pNewTable;
|
|
if( p==0 || NEVER(p->nCol<1) ) return;
|
|
- p->aCol[p->nCol-1].notNull = (u8)onError;
|
|
+ pCol = &p->aCol[p->nCol-1];
|
|
+ pCol->notNull = (u8)onError;
|
|
p->tabFlags |= TF_HasNotNull;
|
|
+
|
|
+ /* Set the uniqNotNull flag on any UNIQUE or PK indexes already created
|
|
+ ** on this column. */
|
|
+ if( pCol->colFlags & COLFLAG_UNIQUE ){
|
|
+ Index *pIdx;
|
|
+ for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
+ assert( pIdx->nKeyCol==1 && pIdx->onError!=OE_None );
|
|
+ if( pIdx->aiColumn[0]==p->nCol-1 ){
|
|
+ pIdx->uniqNotNull = 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
@@ -101602,7 +107121,7 @@
|
|
** If none of the substrings in the above table are found,
|
|
** SQLITE_AFF_NUMERIC is returned.
|
|
*/
|
|
-SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, u8 *pszEst){
|
|
+SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
|
|
u32 h = 0;
|
|
char aff = SQLITE_AFF_NUMERIC;
|
|
const char *zChar = 0;
|
|
@@ -101639,27 +107158,32 @@
|
|
}
|
|
}
|
|
|
|
- /* If pszEst is not NULL, store an estimate of the field size. The
|
|
+ /* If pCol is not NULL, store an estimate of the field size. The
|
|
** estimate is scaled so that the size of an integer is 1. */
|
|
- if( pszEst ){
|
|
- *pszEst = 1; /* default size is approx 4 bytes */
|
|
+ if( pCol ){
|
|
+ int v = 0; /* default size is approx 4 bytes */
|
|
if( aff<SQLITE_AFF_NUMERIC ){
|
|
if( zChar ){
|
|
while( zChar[0] ){
|
|
if( sqlite3Isdigit(zChar[0]) ){
|
|
- int v = 0;
|
|
+ /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
|
|
sqlite3GetInt32(zChar, &v);
|
|
- v = v/4 + 1;
|
|
- if( v>255 ) v = 255;
|
|
- *pszEst = v; /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
|
|
break;
|
|
}
|
|
zChar++;
|
|
}
|
|
}else{
|
|
- *pszEst = 5; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
|
|
+ v = 16; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
|
|
}
|
|
}
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( v>=sqlite3GlobalConfig.szSorterRef ){
|
|
+ pCol->colFlags |= COLFLAG_SORTERREF;
|
|
+ }
|
|
+#endif
|
|
+ v = v/4 + 1;
|
|
+ if( v>255 ) v = 255;
|
|
+ pCol->szEst = v;
|
|
}
|
|
return aff;
|
|
}
|
|
@@ -101674,7 +107198,12 @@
|
|
** This routine is called by the parser while in the middle of
|
|
** parsing a CREATE TABLE statement.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
|
|
+SQLITE_PRIVATE void sqlite3AddDefaultValue(
|
|
+ Parse *pParse, /* Parsing context */
|
|
+ Expr *pExpr, /* The parsed expression of the default value */
|
|
+ const char *zStart, /* Start of the default value text */
|
|
+ const char *zEnd /* First character past end of defaut value text */
|
|
+){
|
|
Table *p;
|
|
Column *pCol;
|
|
sqlite3 *db = pParse->db;
|
|
@@ -101681,27 +107210,28 @@
|
|
p = pParse->pNewTable;
|
|
if( p!=0 ){
|
|
pCol = &(p->aCol[p->nCol-1]);
|
|
- if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr, db->init.busy) ){
|
|
+ if( !sqlite3ExprIsConstantOrFunction(pExpr, db->init.busy) ){
|
|
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
|
|
pCol->zName);
|
|
}else{
|
|
/* A copy of pExpr is used instead of the original, as pExpr contains
|
|
- ** tokens that point to volatile memory. The 'span' of the expression
|
|
- ** is required by pragma table_info.
|
|
+ ** tokens that point to volatile memory.
|
|
*/
|
|
Expr x;
|
|
sqlite3ExprDelete(db, pCol->pDflt);
|
|
memset(&x, 0, sizeof(x));
|
|
x.op = TK_SPAN;
|
|
- x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
|
- (int)(pSpan->zEnd - pSpan->zStart));
|
|
- x.pLeft = pSpan->pExpr;
|
|
+ x.u.zToken = sqlite3DbSpanDup(db, zStart, zEnd);
|
|
+ x.pLeft = pExpr;
|
|
x.flags = EP_Skip;
|
|
pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
|
|
sqlite3DbFree(db, x.u.zToken);
|
|
}
|
|
}
|
|
- sqlite3ExprDelete(db, pSpan->pExpr);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameExprUnmap(pParse, pExpr);
|
|
+ }
|
|
+ sqlite3ExprDelete(db, pExpr);
|
|
}
|
|
|
|
/*
|
|
@@ -101792,6 +107322,9 @@
|
|
&& sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
|
|
&& sortOrder!=SQLITE_SO_DESC
|
|
){
|
|
+ if( IN_RENAME_OBJECT && pList ){
|
|
+ sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr);
|
|
+ }
|
|
pTab->iPKey = iCol;
|
|
pTab->keyConf = (u8)onError;
|
|
assert( autoInc==0 || autoInc==1 );
|
|
@@ -101932,7 +107465,7 @@
|
|
Vdbe *v = pParse->pVdbe;
|
|
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
|
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
|
|
- db->aDb[iDb].pSchema->schema_cookie+1);
|
|
+ (int)(1+(unsigned)db->aDb[iDb].pSchema->schema_cookie));
|
|
}
|
|
|
|
/*
|
|
@@ -102117,6 +107650,31 @@
|
|
return 0;
|
|
}
|
|
|
|
+/* Recompute the colNotIdxed field of the Index.
|
|
+**
|
|
+** colNotIdxed is a bitmask that has a 0 bit representing each indexed
|
|
+** columns that are within the first 63 columns of the table. The
|
|
+** high-order bit of colNotIdxed is always 1. All unindexed columns
|
|
+** of the table have a 1.
|
|
+**
|
|
+** 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;
|
|
+ for(j=pIdx->nColumn-1; j>=0; j--){
|
|
+ int x = pIdx->aiColumn[j];
|
|
+ if( x>=0 ){
|
|
+ testcase( x==BMS-1 );
|
|
+ testcase( x==BMS-2 );
|
|
+ if( x<BMS-1 ) m |= MASKBIT(x);
|
|
+ }
|
|
+ }
|
|
+ pIdx->colNotIdxed = ~m;
|
|
+ assert( (pIdx->colNotIdxed>>63)==1 );
|
|
+}
|
|
+
|
|
/*
|
|
** This routine runs at the end of parsing a CREATE TABLE statement that
|
|
** has a WITHOUT ROWID clause. The job of this routine is to convert both
|
|
@@ -102125,9 +107683,8 @@
|
|
** Changes include:
|
|
**
|
|
** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
|
|
-** (2) Convert the OP_CreateTable into an OP_CreateIndex. There is
|
|
-** no rowid btree for a WITHOUT ROWID. Instead, the canonical
|
|
-** data storage is a covering index btree.
|
|
+** (2) Convert P3 parameter of the OP_CreateBtree from BTREE_INTKEY
|
|
+** into BTREE_BLOBKEY.
|
|
** (3) Bypass the creation of the sqlite_master table entry
|
|
** for the PRIMARY KEY as the primary key index is now
|
|
** identified by the sqlite_master table entry of the table itself.
|
|
@@ -102135,7 +107692,7 @@
|
|
** schema to the rootpage from the main table.
|
|
** (5) Add all table columns to the PRIMARY KEY Index object
|
|
** so that the PRIMARY KEY is a covering index. The surplus
|
|
-** columns are part of KeyInfo.nXField and are not used for
|
|
+** columns are part of KeyInfo.nAllField and are not used for
|
|
** sorting or lookup or uniqueness checks.
|
|
** (6) Replace the rowid tail on all automatically generated UNIQUE
|
|
** indices with the PRIMARY KEY columns.
|
|
@@ -102160,17 +107717,12 @@
|
|
}
|
|
}
|
|
|
|
- /* The remaining transformations only apply to b-tree tables, not to
|
|
- ** virtual tables */
|
|
- if( IN_DECLARE_VTAB ) return;
|
|
-
|
|
- /* Convert the OP_CreateTable opcode that would normally create the
|
|
- ** root-page for the table into an OP_CreateIndex opcode. The index
|
|
- ** created will become the PRIMARY KEY index.
|
|
+ /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
|
|
+ ** into BTREE_BLOBKEY.
|
|
*/
|
|
if( pParse->addrCrTab ){
|
|
assert( v );
|
|
- sqlite3VdbeChangeOpcode(v, pParse->addrCrTab, OP_CreateIndex);
|
|
+ sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY);
|
|
}
|
|
|
|
/* Locate the PRIMARY KEY index. Or, if this table was originally
|
|
@@ -102187,7 +107739,7 @@
|
|
assert( pParse->pNewTable==pTab );
|
|
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
|
|
SQLITE_IDXTYPE_PRIMARYKEY);
|
|
- if( db->mallocFailed ) return;
|
|
+ if( db->mallocFailed || pParse->nErr ) return;
|
|
pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
pTab->iPKey = -1;
|
|
}else{
|
|
@@ -102267,9 +107819,40 @@
|
|
}else{
|
|
pPk->nColumn = pTab->nCol;
|
|
}
|
|
+ recomputeColumnsNotIndexed(pPk);
|
|
}
|
|
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
/*
|
|
+** Return true if zName is a shadow table name in the current database
|
|
+** connection.
|
|
+**
|
|
+** 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){
|
|
+ char *zTail; /* Pointer to the last "_" in zName */
|
|
+ Table *pTab; /* Table that zName is a shadow of */
|
|
+ Module *pMod; /* Module for the virtual table */
|
|
+
|
|
+ zTail = strrchr(zName, '_');
|
|
+ if( zTail==0 ) return 0;
|
|
+ *zTail = 0;
|
|
+ pTab = sqlite3FindTable(db, zName, 0);
|
|
+ *zTail = '_';
|
|
+ if( pTab==0 ) return 0;
|
|
+ if( !IsVirtual(pTab) ) return 0;
|
|
+ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
|
|
+ if( pMod==0 ) return 0;
|
|
+ if( pMod->pModule->iVersion<3 ) return 0;
|
|
+ if( pMod->pModule->xShadowName==0 ) return 0;
|
|
+ return pMod->pModule->xShadowName(zTail+1);
|
|
+}
|
|
+#else
|
|
+# define isShadowTableName(x,y) 0
|
|
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+/*
|
|
** This routine is called to report the final ")" that terminates
|
|
** a CREATE TABLE statement.
|
|
**
|
|
@@ -102308,7 +107891,9 @@
|
|
p = pParse->pNewTable;
|
|
if( p==0 ) return;
|
|
|
|
- assert( !db->init.busy || !pSelect );
|
|
+ if( pSelect==0 && isShadowTableName(db, p->zName) ){
|
|
+ p->tabFlags |= TF_Shadow;
|
|
+ }
|
|
|
|
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
|
** "sqlite_master" or "sqlite_temp_master" table on the disk.
|
|
@@ -102320,6 +107905,10 @@
|
|
** table itself. So mark it read-only.
|
|
*/
|
|
if( db->init.busy ){
|
|
+ if( pSelect ){
|
|
+ sqlite3ErrorMsg(pParse, "");
|
|
+ return;
|
|
+ }
|
|
p->tnum = db->init.newTnum;
|
|
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
|
|
}
|
|
@@ -102420,10 +108009,6 @@
|
|
pParse->nTab = 2;
|
|
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
|
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
|
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
|
|
- sqlite3Select(pParse, pSelect, &dest);
|
|
- sqlite3VdbeEndCoroutine(v, regYield);
|
|
- sqlite3VdbeJumpHere(v, addrTop - 1);
|
|
if( pParse->nErr ) return;
|
|
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
|
|
if( pSelTab==0 ) return;
|
|
@@ -102433,6 +108018,11 @@
|
|
pSelTab->nCol = 0;
|
|
pSelTab->aCol = 0;
|
|
sqlite3DeleteTable(db, pSelTab);
|
|
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
|
|
+ sqlite3Select(pParse, pSelect, &dest);
|
|
+ if( pParse->nErr ) return;
|
|
+ sqlite3VdbeEndCoroutine(v, regYield);
|
|
+ sqlite3VdbeJumpHere(v, addrTop - 1);
|
|
addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
|
|
VdbeCoverage(v);
|
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
|
|
@@ -102510,7 +108100,7 @@
|
|
return;
|
|
}
|
|
pParse->pNewTable = 0;
|
|
- db->flags |= SQLITE_InternChanges;
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
|
|
#ifndef SQLITE_OMIT_ALTERTABLE
|
|
if( !p->pSelect ){
|
|
@@ -102567,7 +108157,12 @@
|
|
** allocated rather than point to the input string - which means that
|
|
** they will persist after the current sqlite3_exec() call returns.
|
|
*/
|
|
- p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ p->pSelect = pSelect;
|
|
+ pSelect = 0;
|
|
+ }else{
|
|
+ p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
|
+ }
|
|
p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
|
|
if( db->mallocFailed ) goto create_view_fail;
|
|
|
|
@@ -102575,7 +108170,7 @@
|
|
** the end.
|
|
*/
|
|
sEnd = pParse->sLastToken;
|
|
- assert( sEnd.z[0]!=0 );
|
|
+ assert( sEnd.z[0]!=0 || sEnd.n==0 );
|
|
if( sEnd.z[0]!=';' ){
|
|
sEnd.z += sEnd.n;
|
|
}
|
|
@@ -102592,6 +108187,9 @@
|
|
|
|
create_view_fail:
|
|
sqlite3SelectDelete(db, pSelect);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameExprlistUnmap(pParse, pCNames);
|
|
+ }
|
|
sqlite3ExprListDelete(db, pCNames);
|
|
return;
|
|
}
|
|
@@ -102609,6 +108207,9 @@
|
|
int nErr = 0; /* Number of errors encountered */
|
|
int n; /* Temporarily holds the number of cursors assigned */
|
|
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ int rc;
|
|
+#endif
|
|
#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
sqlite3_xauth xAuth; /* Saved xAuth pointer */
|
|
#endif
|
|
@@ -102616,8 +108217,11 @@
|
|
assert( pTable );
|
|
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- if( sqlite3VtabCallConnect(pParse, pTable) ){
|
|
- return SQLITE_ERROR;
|
|
+ db->nSchemaLock++;
|
|
+ rc = sqlite3VtabCallConnect(pParse, pTable);
|
|
+ db->nSchemaLock--;
|
|
+ if( rc ){
|
|
+ return 1;
|
|
}
|
|
if( IsVirtual(pTable) ) return 0;
|
|
#endif
|
|
@@ -102659,6 +108263,10 @@
|
|
assert( pTable->pSelect );
|
|
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
|
if( pSel ){
|
|
+#ifndef SQLITE_OMIT_ALTERTABLE
|
|
+ u8 eParseMode = pParse->eParseMode;
|
|
+ pParse->eParseMode = PARSE_MODE_NORMAL;
|
|
+#endif
|
|
n = pParse->nTab;
|
|
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
|
pTable->nCol = -1;
|
|
@@ -102704,10 +108312,18 @@
|
|
sqlite3DeleteTable(db, pSelTab);
|
|
sqlite3SelectDelete(db, pSel);
|
|
db->lookaside.bDisable--;
|
|
+#ifndef SQLITE_OMIT_ALTERTABLE
|
|
+ pParse->eParseMode = eParseMode;
|
|
+#endif
|
|
} else {
|
|
nErr++;
|
|
}
|
|
pTable->pSchema->schemaFlags |= DB_UnresetViews;
|
|
+ if( db->mallocFailed ){
|
|
+ sqlite3DeleteColumnNames(db, pTable);
|
|
+ pTable->aCol = 0;
|
|
+ pTable->nCol = 0;
|
|
+ }
|
|
#endif /* SQLITE_OMIT_VIEW */
|
|
return nErr;
|
|
}
|
|
@@ -102786,7 +108402,7 @@
|
|
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
|
Vdbe *v = sqlite3GetVdbe(pParse);
|
|
int r1 = sqlite3GetTempReg(pParse);
|
|
- assert( iTable>1 );
|
|
+ if( iTable<2 ) sqlite3ErrorMsg(pParse, "corrupt schema");
|
|
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
|
|
sqlite3MayAbort(pParse);
|
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
|
@@ -102813,14 +108429,6 @@
|
|
** is also added (this can happen with an auto-vacuum database).
|
|
*/
|
|
static void destroyTable(Parse *pParse, Table *pTab){
|
|
-#ifdef SQLITE_OMIT_AUTOVACUUM
|
|
- Index *pIdx;
|
|
- int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
|
- destroyRootPage(pParse, pTab->tnum, iDb);
|
|
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
- destroyRootPage(pParse, pIdx->tnum, iDb);
|
|
- }
|
|
-#else
|
|
/* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
|
|
** is not defined), then it is important to call OP_Destroy on the
|
|
** table and index root-pages in order, starting with the numerically
|
|
@@ -102863,7 +108471,6 @@
|
|
iDestroyed = iLargest;
|
|
}
|
|
}
|
|
-#endif
|
|
}
|
|
|
|
/*
|
|
@@ -103055,8 +108662,10 @@
|
|
v = sqlite3GetVdbe(pParse);
|
|
if( v ){
|
|
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
|
- sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
|
|
- sqlite3FkDropTable(pParse, pName, pTab);
|
|
+ if( !isView ){
|
|
+ sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
|
|
+ sqlite3FkDropTable(pParse, pName, pTab);
|
|
+ }
|
|
sqlite3CodeDropTable(pParse, pTab, iDb, isView);
|
|
}
|
|
|
|
@@ -103131,6 +108740,9 @@
|
|
pFKey->pNextFrom = p->pFKey;
|
|
z = (char*)&pFKey->aCol[nCol];
|
|
pFKey->zTo = z;
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenMap(pParse, (void*)z, pTo);
|
|
+ }
|
|
memcpy(z, pTo->z, pTo->n);
|
|
z[pTo->n] = 0;
|
|
sqlite3Dequote(z);
|
|
@@ -103153,6 +108765,9 @@
|
|
pFromCol->a[i].zName);
|
|
goto fk_end;
|
|
}
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName);
|
|
+ }
|
|
}
|
|
}
|
|
if( pToCol ){
|
|
@@ -103159,6 +108774,9 @@
|
|
for(i=0; i<nCol; i++){
|
|
int n = sqlite3Strlen30(pToCol->a[i].zName);
|
|
pFKey->aCol[i].zCol = z;
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName);
|
|
+ }
|
|
memcpy(z, pToCol->a[i].zName, n);
|
|
z[n] = 0;
|
|
z += n+1;
|
|
@@ -103267,6 +108885,7 @@
|
|
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
|
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); VdbeCoverage(v);
|
|
regRecord = sqlite3GetTempReg(pParse);
|
|
+ sqlite3MultiWrite(pParse);
|
|
|
|
sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0);
|
|
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
|
|
@@ -103280,17 +108899,18 @@
|
|
|
|
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v);
|
|
if( IsUniqueIndex(pIndex) ){
|
|
- int j2 = sqlite3VdbeCurrentAddr(v) + 3;
|
|
- sqlite3VdbeGoto(v, j2);
|
|
+ int j2 = sqlite3VdbeGoto(v, 1);
|
|
addr2 = sqlite3VdbeCurrentAddr(v);
|
|
+ sqlite3VdbeVerifyAbortable(v, OE_Abort);
|
|
sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
|
|
pIndex->nKeyCol); VdbeCoverage(v);
|
|
sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
|
|
+ sqlite3VdbeJumpHere(v, j2);
|
|
}else{
|
|
addr2 = sqlite3VdbeCurrentAddr(v);
|
|
}
|
|
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
|
|
- sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1);
|
|
+ sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
|
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
|
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
|
sqlite3ReleaseTempReg(pParse, regRecord);
|
|
@@ -103448,7 +109068,11 @@
|
|
#if SQLITE_USER_AUTHENTICATION
|
|
&& sqlite3UserAuthTable(pTab->zName)==0
|
|
#endif
|
|
- && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){
|
|
+#ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX
|
|
+ && sqlite3StrICmp(&pTab->zName[7],"master")!=0
|
|
+#endif
|
|
+ && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0
|
|
+ ){
|
|
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
|
|
goto exit_create_index;
|
|
}
|
|
@@ -103485,21 +109109,23 @@
|
|
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
|
goto exit_create_index;
|
|
}
|
|
- if( !db->init.busy ){
|
|
- if( sqlite3FindTable(db, zName, 0)!=0 ){
|
|
- sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
|
|
+ if( !IN_RENAME_OBJECT ){
|
|
+ if( !db->init.busy ){
|
|
+ if( sqlite3FindTable(db, zName, 0)!=0 ){
|
|
+ sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
|
|
+ goto exit_create_index;
|
|
+ }
|
|
+ }
|
|
+ if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
|
|
+ if( !ifNotExist ){
|
|
+ sqlite3ErrorMsg(pParse, "index %s already exists", zName);
|
|
+ }else{
|
|
+ assert( !db->init.busy );
|
|
+ sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ }
|
|
goto exit_create_index;
|
|
}
|
|
}
|
|
- if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
|
|
- if( !ifNotExist ){
|
|
- sqlite3ErrorMsg(pParse, "index %s already exists", zName);
|
|
- }else{
|
|
- assert( !db->init.busy );
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
- }
|
|
- goto exit_create_index;
|
|
- }
|
|
}else{
|
|
int n;
|
|
Index *pLoop;
|
|
@@ -103514,13 +109140,13 @@
|
|
** The following statement converts "sqlite3_autoindex..." into
|
|
** "sqlite3_butoindex..." in order to make the names distinct.
|
|
** The "vtab_err.test" test demonstrates the need of this statement. */
|
|
- if( IN_DECLARE_VTAB ) zName[7]++;
|
|
+ if( IN_SPECIAL_PARSE ) zName[7]++;
|
|
}
|
|
|
|
/* Check for authorization to create an index.
|
|
*/
|
|
#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
- {
|
|
+ if( !IN_RENAME_OBJECT ){
|
|
const char *zDb = pDb->zDbSName;
|
|
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
|
|
goto exit_create_index;
|
|
@@ -103539,7 +109165,9 @@
|
|
*/
|
|
if( pList==0 ){
|
|
Token prevCol;
|
|
- sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol-1].zName);
|
|
+ Column *pCol = &pTab->aCol[pTab->nCol-1];
|
|
+ pCol->colFlags |= COLFLAG_UNIQUE;
|
|
+ sqlite3TokenInit(&prevCol, pCol->zName);
|
|
pList = sqlite3ExprListAppend(pParse, 0,
|
|
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
|
|
if( pList==0 ) goto exit_create_index;
|
|
@@ -103605,7 +109233,12 @@
|
|
** TODO: Issue a warning if the table primary key is used as part of the
|
|
** index key.
|
|
*/
|
|
- for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
|
|
+ pListItem = pList->a;
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ pIndex->aColExpr = pList;
|
|
+ pList = 0;
|
|
+ }
|
|
+ for(i=0; i<pIndex->nKeyCol; i++, pListItem++){
|
|
Expr *pCExpr; /* The i-th index expression */
|
|
int requestedSortOrder; /* ASC or DESC on the i-th expression */
|
|
const char *zColl; /* Collation sequence name */
|
|
@@ -103621,12 +109254,8 @@
|
|
goto exit_create_index;
|
|
}
|
|
if( pIndex->aColExpr==0 ){
|
|
- ExprList *pCopy = sqlite3ExprListDup(db, pList, 0);
|
|
- pIndex->aColExpr = pCopy;
|
|
- if( !db->mallocFailed ){
|
|
- assert( pCopy!=0 );
|
|
- pListItem = &pCopy->a[i];
|
|
- }
|
|
+ pIndex->aColExpr = pList;
|
|
+ pList = 0;
|
|
}
|
|
j = XN_EXPR;
|
|
pIndex->aiColumn[i] = XN_EXPR;
|
|
@@ -103692,6 +109321,7 @@
|
|
** it as a covering index */
|
|
assert( HasRowid(pTab)
|
|
|| pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 );
|
|
+ recomputeColumnsNotIndexed(pIndex);
|
|
if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
|
|
pIndex->isCovering = 1;
|
|
for(j=0; j<pTab->nCol; j++){
|
|
@@ -103764,98 +109394,101 @@
|
|
}
|
|
}
|
|
|
|
- /* Link the new Index structure to its table and to the other
|
|
- ** in-memory database structures.
|
|
- */
|
|
- assert( pParse->nErr==0 );
|
|
- if( db->init.busy ){
|
|
- Index *p;
|
|
- assert( !IN_DECLARE_VTAB );
|
|
- assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
|
|
- p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
|
|
- pIndex->zName, pIndex);
|
|
- if( p ){
|
|
- assert( p==pIndex ); /* Malloc must have failed */
|
|
- sqlite3OomFault(db);
|
|
- goto exit_create_index;
|
|
+ if( !IN_RENAME_OBJECT ){
|
|
+
|
|
+ /* Link the new Index structure to its table and to the other
|
|
+ ** in-memory database structures.
|
|
+ */
|
|
+ assert( pParse->nErr==0 );
|
|
+ if( db->init.busy ){
|
|
+ Index *p;
|
|
+ assert( !IN_SPECIAL_PARSE );
|
|
+ assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
|
|
+ p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
|
|
+ pIndex->zName, pIndex);
|
|
+ if( p ){
|
|
+ assert( p==pIndex ); /* Malloc must have failed */
|
|
+ sqlite3OomFault(db);
|
|
+ goto exit_create_index;
|
|
+ }
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
+ if( pTblName!=0 ){
|
|
+ pIndex->tnum = db->init.newTnum;
|
|
+ }
|
|
}
|
|
- db->flags |= SQLITE_InternChanges;
|
|
- if( pTblName!=0 ){
|
|
- pIndex->tnum = db->init.newTnum;
|
|
- }
|
|
- }
|
|
|
|
- /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
|
|
- ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
|
|
- ** emit code to allocate the index rootpage on disk and make an entry for
|
|
- ** the index in the sqlite_master table and populate the index with
|
|
- ** content. But, do not do this if we are simply reading the sqlite_master
|
|
- ** table to parse the schema, or if this index is the PRIMARY KEY index
|
|
- ** of a WITHOUT ROWID table.
|
|
- **
|
|
- ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
|
|
- ** or UNIQUE index in a CREATE TABLE statement. Since the table
|
|
- ** has just been created, it contains no data and the index initialization
|
|
- ** step can be skipped.
|
|
- */
|
|
- else if( HasRowid(pTab) || pTblName!=0 ){
|
|
- Vdbe *v;
|
|
- char *zStmt;
|
|
- int iMem = ++pParse->nMem;
|
|
+ /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
|
|
+ ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
|
|
+ ** emit code to allocate the index rootpage on disk and make an entry for
|
|
+ ** the index in the sqlite_master table and populate the index with
|
|
+ ** content. But, do not do this if we are simply reading the sqlite_master
|
|
+ ** table to parse the schema, or if this index is the PRIMARY KEY index
|
|
+ ** of a WITHOUT ROWID table.
|
|
+ **
|
|
+ ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
|
|
+ ** or UNIQUE index in a CREATE TABLE statement. Since the table
|
|
+ ** has just been created, it contains no data and the index initialization
|
|
+ ** step can be skipped.
|
|
+ */
|
|
+ else if( HasRowid(pTab) || pTblName!=0 ){
|
|
+ Vdbe *v;
|
|
+ char *zStmt;
|
|
+ int iMem = ++pParse->nMem;
|
|
|
|
- v = sqlite3GetVdbe(pParse);
|
|
- if( v==0 ) goto exit_create_index;
|
|
+ v = sqlite3GetVdbe(pParse);
|
|
+ if( v==0 ) goto exit_create_index;
|
|
|
|
- sqlite3BeginWriteOperation(pParse, 1, iDb);
|
|
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
|
|
|
|
- /* Create the rootpage for the index using CreateIndex. But before
|
|
- ** doing so, code a Noop instruction and store its address in
|
|
- ** Index.tnum. This is required in case this index is actually a
|
|
- ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
|
|
- ** that case the convertToWithoutRowidTable() routine will replace
|
|
- ** the Noop with a Goto to jump over the VDBE code generated below. */
|
|
- pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
|
|
- sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem);
|
|
+ /* Create the rootpage for the index using CreateIndex. But before
|
|
+ ** doing so, code a Noop instruction and store its address in
|
|
+ ** Index.tnum. This is required in case this index is actually a
|
|
+ ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
|
|
+ ** that case the convertToWithoutRowidTable() routine will replace
|
|
+ ** the Noop with a Goto to jump over the VDBE code generated below. */
|
|
+ pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
|
|
+ sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY);
|
|
|
|
- /* Gather the complete text of the CREATE INDEX statement into
|
|
- ** the zStmt variable
|
|
- */
|
|
- if( pStart ){
|
|
- int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
|
|
- if( pName->z[n-1]==';' ) n--;
|
|
- /* A named index with an explicit CREATE INDEX statement */
|
|
- zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
|
|
- onError==OE_None ? "" : " UNIQUE", n, pName->z);
|
|
- }else{
|
|
- /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
|
|
- /* zStmt = sqlite3MPrintf(""); */
|
|
- zStmt = 0;
|
|
- }
|
|
+ /* Gather the complete text of the CREATE INDEX statement into
|
|
+ ** the zStmt variable
|
|
+ */
|
|
+ if( pStart ){
|
|
+ int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
|
|
+ if( pName->z[n-1]==';' ) n--;
|
|
+ /* A named index with an explicit CREATE INDEX statement */
|
|
+ zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
|
|
+ onError==OE_None ? "" : " UNIQUE", n, pName->z);
|
|
+ }else{
|
|
+ /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
|
|
+ /* zStmt = sqlite3MPrintf(""); */
|
|
+ zStmt = 0;
|
|
+ }
|
|
|
|
- /* Add an entry in sqlite_master for this index
|
|
- */
|
|
- sqlite3NestedParse(pParse,
|
|
- "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
|
|
- db->aDb[iDb].zDbSName, MASTER_NAME,
|
|
- pIndex->zName,
|
|
- pTab->zName,
|
|
- iMem,
|
|
- zStmt
|
|
- );
|
|
- sqlite3DbFree(db, zStmt);
|
|
+ /* Add an entry in sqlite_master for this index
|
|
+ */
|
|
+ sqlite3NestedParse(pParse,
|
|
+ "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
|
|
+ db->aDb[iDb].zDbSName, MASTER_NAME,
|
|
+ pIndex->zName,
|
|
+ pTab->zName,
|
|
+ iMem,
|
|
+ zStmt
|
|
+ );
|
|
+ sqlite3DbFree(db, zStmt);
|
|
|
|
- /* Fill the index with data and reparse the schema. Code an OP_Expire
|
|
- ** to invalidate all pre-compiled statements.
|
|
- */
|
|
- if( pTblName ){
|
|
- sqlite3RefillIndex(pParse, pIndex, iMem);
|
|
- sqlite3ChangeCookie(pParse, iDb);
|
|
- sqlite3VdbeAddParseSchemaOp(v, iDb,
|
|
- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
|
- sqlite3VdbeAddOp0(v, OP_Expire);
|
|
+ /* Fill the index with data and reparse the schema. Code an OP_Expire
|
|
+ ** to invalidate all pre-compiled statements.
|
|
+ */
|
|
+ if( pTblName ){
|
|
+ sqlite3RefillIndex(pParse, pIndex, iMem);
|
|
+ sqlite3ChangeCookie(pParse, iDb);
|
|
+ sqlite3VdbeAddParseSchemaOp(v, iDb,
|
|
+ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
|
+ sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
|
|
+ }
|
|
+
|
|
+ sqlite3VdbeJumpHere(v, pIndex->tnum);
|
|
}
|
|
-
|
|
- sqlite3VdbeJumpHere(v, pIndex->tnum);
|
|
}
|
|
|
|
/* When adding an index to the list of indices for a table, make
|
|
@@ -103879,10 +109512,15 @@
|
|
}
|
|
pIndex = 0;
|
|
}
|
|
+ else if( IN_RENAME_OBJECT ){
|
|
+ assert( pParse->pNewIndex==0 );
|
|
+ pParse->pNewIndex = pIndex;
|
|
+ pIndex = 0;
|
|
+ }
|
|
|
|
/* Clean up before exiting */
|
|
exit_create_index:
|
|
- if( pIndex ) freeIndex(db, pIndex);
|
|
+ if( pIndex ) sqlite3FreeIndex(db, pIndex);
|
|
sqlite3ExprDelete(db, pPIWhere);
|
|
sqlite3ExprListDelete(db, pList);
|
|
sqlite3SrcListDelete(db, pTblName);
|
|
@@ -104051,7 +109689,8 @@
|
|
**
|
|
** A new IdList is returned, or NULL if malloc() fails.
|
|
*/
|
|
-SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
|
|
+SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
|
|
+ sqlite3 *db = pParse->db;
|
|
int i;
|
|
if( pList==0 ){
|
|
pList = sqlite3DbMallocZero(db, sizeof(IdList) );
|
|
@@ -104069,6 +109708,9 @@
|
|
return 0;
|
|
}
|
|
pList->a[i].zName = sqlite3NameFromToken(db, pToken);
|
|
+ if( IN_RENAME_OBJECT && pList->a[i].zName ){
|
|
+ sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken);
|
|
+ }
|
|
return pList;
|
|
}
|
|
|
|
@@ -104310,10 +109952,17 @@
|
|
goto append_from_error;
|
|
}
|
|
p = sqlite3SrcListAppend(db, p, pTable, pDatabase);
|
|
- if( p==0 || NEVER(p->nSrc==0) ){
|
|
+ if( p==0 ){
|
|
goto append_from_error;
|
|
}
|
|
+ assert( p->nSrc>0 );
|
|
pItem = &p->a[p->nSrc-1];
|
|
+ assert( (pTable==0)==(pDatabase==0) );
|
|
+ assert( pItem->zName==0 || pDatabase!=0 );
|
|
+ if( IN_RENAME_OBJECT && pItem->zName ){
|
|
+ Token *pToken = (ALWAYS(pDatabase) && pDatabase->z) ? pDatabase : pTable;
|
|
+ sqlite3RenameTokenMap(pParse, pItem->zName, pToken);
|
|
+ }
|
|
assert( pAlias!=0 );
|
|
if( pAlias->n ){
|
|
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
|
|
@@ -104337,8 +109986,10 @@
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
|
|
assert( pIndexedBy!=0 );
|
|
- if( p && ALWAYS(p->nSrc>0) ){
|
|
- struct SrcList_item *pItem = &p->a[p->nSrc-1];
|
|
+ if( p && pIndexedBy->n>0 ){
|
|
+ struct SrcList_item *pItem;
|
|
+ assert( p->nSrc>0 );
|
|
+ pItem = &p->a[p->nSrc-1];
|
|
assert( pItem->fg.notIndexed==0 );
|
|
assert( pItem->fg.isIndexedBy==0 );
|
|
assert( pItem->fg.isTabFunc==0 );
|
|
@@ -104348,7 +109999,7 @@
|
|
pItem->fg.notIndexed = 1;
|
|
}else{
|
|
pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy);
|
|
- pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy!=0);
|
|
+ pItem->fg.isIndexedBy = 1;
|
|
}
|
|
}
|
|
}
|
|
@@ -104622,16 +110273,16 @@
|
|
|
|
sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
|
|
if( pIdx->aColExpr ){
|
|
- sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
|
|
+ sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName);
|
|
}else{
|
|
for(j=0; j<pIdx->nKeyCol; j++){
|
|
char *zCol;
|
|
assert( pIdx->aiColumn[j]>=0 );
|
|
zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
|
|
- if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
|
|
- sqlite3StrAccumAppendAll(&errMsg, pTab->zName);
|
|
- sqlite3StrAccumAppend(&errMsg, ".", 1);
|
|
- sqlite3StrAccumAppendAll(&errMsg, zCol);
|
|
+ if( j ) sqlite3_str_append(&errMsg, ", ", 2);
|
|
+ sqlite3_str_appendall(&errMsg, pTab->zName);
|
|
+ sqlite3_str_append(&errMsg, ".", 1);
|
|
+ sqlite3_str_appendall(&errMsg, zCol);
|
|
}
|
|
}
|
|
zErr = sqlite3StrAccumFinish(&errMsg);
|
|
@@ -104819,6 +110470,18 @@
|
|
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
|
}
|
|
if( pParse->nErr ){
|
|
+ assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
|
|
+ if( pIdx->bNoQuery==0 ){
|
|
+ /* Deactivate the index because it contains an unknown collating
|
|
+ ** sequence. The only way to reactive the index is to reload the
|
|
+ ** schema. Adding the missing collating sequence later does not
|
|
+ ** reactive the index. The application had the chance to register
|
|
+ ** the missing index using the collation-needed callback. For
|
|
+ ** simplicity, SQLite will not give the application a second chance.
|
|
+ */
|
|
+ pIdx->bNoQuery = 1;
|
|
+ pParse->rc = SQLITE_ERROR_RETRY;
|
|
+ }
|
|
sqlite3KeyInfoUnref(pKey);
|
|
pKey = 0;
|
|
}
|
|
@@ -105004,6 +110667,7 @@
|
|
assert( !p || p->xCmp );
|
|
if( p==0 ){
|
|
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
|
+ pParse->rc = SQLITE_ERROR_MISSING_COLLSEQ;
|
|
}
|
|
return p;
|
|
}
|
|
@@ -105193,6 +110857,21 @@
|
|
}
|
|
return 0;
|
|
}
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(
|
|
+ int h, /* Hash of the name */
|
|
+ const char *zFunc, /* Name of function */
|
|
+ int nFunc /* Length of the name */
|
|
+){
|
|
+ FuncDef *p;
|
|
+ for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
|
|
+ if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 ){
|
|
+ return p;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
|
|
/*
|
|
** Insert a new FuncDef into a FuncDefHash hash table.
|
|
@@ -105206,7 +110885,7 @@
|
|
FuncDef *pOther;
|
|
const char *zName = aDef[i].zName;
|
|
int nName = sqlite3Strlen30(zName);
|
|
- int h = (zName[0] + nName) % SQLITE_FUNC_HASH_SZ;
|
|
+ int h = SQLITE_FUNC_HASH(zName[0], nName);
|
|
assert( zName[0]>='a' && zName[0]<='z' );
|
|
pOther = functionSearch(h, zName);
|
|
if( pOther ){
|
|
@@ -105273,7 +110952,7 @@
|
|
|
|
/* If no match is found, search the built-in functions.
|
|
**
|
|
- ** If the SQLITE_PreferBuiltin flag is set, then search the built-in
|
|
+ ** If the DBFLAG_PreferBuiltin flag is set, then search the built-in
|
|
** functions even if a prior app-defined function was found. And give
|
|
** priority to built-in functions.
|
|
**
|
|
@@ -105283,9 +110962,9 @@
|
|
** new function. But the FuncDefs for built-in functions are read-only.
|
|
** So we must not search for built-ins when creating a new function.
|
|
*/
|
|
- if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
|
|
+ if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){
|
|
bestScore = 0;
|
|
- h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
|
|
+ h = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zName[0]], nName);
|
|
p = functionSearch(h, zName);
|
|
while( p ){
|
|
int score = matchQuality(p, nArg, enc);
|
|
@@ -105304,10 +110983,12 @@
|
|
if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
|
|
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
|
|
FuncDef *pOther;
|
|
+ u8 *z;
|
|
pBest->zName = (const char*)&pBest[1];
|
|
pBest->nArg = (u16)nArg;
|
|
pBest->funcFlags = enc;
|
|
memcpy((char*)&pBest[1], zName, nName+1);
|
|
+ for(z=(u8*)pBest->zName; *z; z++) *z = sqlite3UpperToLower[*z];
|
|
pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest);
|
|
if( pOther==pBest ){
|
|
sqlite3DbFree(db, pBest);
|
|
@@ -105356,8 +111037,8 @@
|
|
pSchema->pSeqTab = 0;
|
|
if( pSchema->schemaFlags & DB_SchemaLoaded ){
|
|
pSchema->iGeneration++;
|
|
- pSchema->schemaFlags &= ~DB_SchemaLoaded;
|
|
}
|
|
+ pSchema->schemaFlags &= ~(DB_SchemaLoaded|DB_ResetWanted);
|
|
}
|
|
|
|
/*
|
|
@@ -105431,6 +111112,39 @@
|
|
return pTab;
|
|
}
|
|
|
|
+/* Return true if table pTab is read-only.
|
|
+**
|
|
+** A table is read-only if any of the following are true:
|
|
+**
|
|
+** 1) It is a virtual table and no implementation of the xUpdate method
|
|
+** has been provided
|
|
+**
|
|
+** 2) It is a system table (i.e. sqlite_master), this call is not
|
|
+** part of a nested parse and writable_schema pragma has not
|
|
+** been specified
|
|
+**
|
|
+** 3) The table is a shadow table, the database connection is in
|
|
+** defensive mode, and the current sqlite3_prepare()
|
|
+** is for a top-level SQL statement.
|
|
+*/
|
|
+static int tabIsReadOnly(Parse *pParse, Table *pTab){
|
|
+ sqlite3 *db;
|
|
+ if( IsVirtual(pTab) ){
|
|
+ return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0;
|
|
+ }
|
|
+ if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0;
|
|
+ db = pParse->db;
|
|
+ if( (pTab->tabFlags & TF_Readonly)!=0 ){
|
|
+ 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;
|
|
+}
|
|
+
|
|
/*
|
|
** Check to make sure the given table is writable. If it is not
|
|
** writable, generate an error message and return 1. If it is
|
|
@@ -105437,26 +111151,10 @@
|
|
** writable return 0;
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
|
|
- /* A table is not writable under the following circumstances:
|
|
- **
|
|
- ** 1) It is a virtual table and no implementation of the xUpdate method
|
|
- ** has been provided, or
|
|
- ** 2) It is a system table (i.e. sqlite_master), this call is not
|
|
- ** part of a nested parse and writable_schema pragma has not
|
|
- ** been specified.
|
|
- **
|
|
- ** In either case leave an error message in pParse and return non-zero.
|
|
- */
|
|
- if( ( IsVirtual(pTab)
|
|
- && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 )
|
|
- || ( (pTab->tabFlags & TF_Readonly)!=0
|
|
- && (pParse->db->flags & SQLITE_WriteSchema)==0
|
|
- && pParse->nested==0 )
|
|
- ){
|
|
+ if( tabIsReadOnly(pParse, pTab) ){
|
|
sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
|
|
return 1;
|
|
}
|
|
-
|
|
#ifndef SQLITE_OMIT_VIEW
|
|
if( !viewOk && pTab->pSelect ){
|
|
sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
|
|
@@ -105477,6 +111175,8 @@
|
|
Parse *pParse, /* Parsing context */
|
|
Table *pView, /* View definition */
|
|
Expr *pWhere, /* Optional WHERE clause to be added */
|
|
+ ExprList *pOrderBy, /* Optional ORDER BY clause */
|
|
+ Expr *pLimit, /* Optional LIMIT clause */
|
|
int iCur /* Cursor number for ephemeral table */
|
|
){
|
|
SelectDest dest;
|
|
@@ -105493,8 +111193,8 @@
|
|
assert( pFrom->a[0].pOn==0 );
|
|
assert( pFrom->a[0].pUsing==0 );
|
|
}
|
|
- pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0,
|
|
- SF_IncludeHidden, 0, 0);
|
|
+ pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,
|
|
+ SF_IncludeHidden, pLimit);
|
|
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
|
|
sqlite3Select(pParse, pSel, &dest);
|
|
sqlite3SelectDelete(db, pSel);
|
|
@@ -105516,21 +111216,23 @@
|
|
Expr *pWhere, /* The WHERE clause. May be null */
|
|
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
|
|
Expr *pLimit, /* The LIMIT clause. May be null */
|
|
- Expr *pOffset, /* The OFFSET clause. May be null */
|
|
char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
|
){
|
|
- Expr *pWhereRowid = NULL; /* WHERE rowid .. */
|
|
+ sqlite3 *db = pParse->db;
|
|
+ Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */
|
|
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
|
|
- Expr *pSelectRowid = NULL; /* SELECT rowid ... */
|
|
ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */
|
|
SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */
|
|
Select *pSelect = NULL; /* Complete SELECT tree */
|
|
+ Table *pTab;
|
|
|
|
/* Check that there isn't an ORDER BY without a LIMIT clause.
|
|
*/
|
|
- if( pOrderBy && (pLimit == 0) ) {
|
|
+ if( pOrderBy && pLimit==0 ) {
|
|
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
|
- goto limit_where_cleanup;
|
|
+ sqlite3ExprDelete(pParse->db, pWhere);
|
|
+ sqlite3ExprListDelete(pParse->db, pOrderBy);
|
|
+ return 0;
|
|
}
|
|
|
|
/* We only need to generate a select expression if there
|
|
@@ -105537,8 +111239,6 @@
|
|
** is a limit/offset term to enforce.
|
|
*/
|
|
if( pLimit == 0 ) {
|
|
- /* if pLimit is null, pOffset will always be null as well. */
|
|
- assert( pOffset == 0 );
|
|
return pWhere;
|
|
}
|
|
|
|
@@ -105551,36 +111251,47 @@
|
|
** );
|
|
*/
|
|
|
|
- pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
|
- if( pSelectRowid == 0 ) goto limit_where_cleanup;
|
|
- pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
|
|
- if( pEList == 0 ) goto limit_where_cleanup;
|
|
+ pTab = pSrc->a[0].pTab;
|
|
+ if( HasRowid(pTab) ){
|
|
+ pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
|
+ pEList = sqlite3ExprListAppend(
|
|
+ pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0)
|
|
+ );
|
|
+ }else{
|
|
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
+ if( pPk->nKeyCol==1 ){
|
|
+ const char *zName = pTab->aCol[pPk->aiColumn[0]].zName;
|
|
+ pLhs = sqlite3Expr(db, TK_ID, zName);
|
|
+ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName));
|
|
+ }else{
|
|
+ int i;
|
|
+ for(i=0; i<pPk->nKeyCol; i++){
|
|
+ Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName);
|
|
+ pEList = sqlite3ExprListAppend(pParse, pEList, p);
|
|
+ }
|
|
+ pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
|
+ if( pLhs ){
|
|
+ pLhs->x.pList = sqlite3ExprListDup(db, pEList, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
|
|
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
|
** and the SELECT subtree. */
|
|
+ pSrc->a[0].pTab = 0;
|
|
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
|
|
- if( pSelectSrc == 0 ) {
|
|
- sqlite3ExprListDelete(pParse->db, pEList);
|
|
- goto limit_where_cleanup;
|
|
- }
|
|
+ pSrc->a[0].pTab = pTab;
|
|
+ pSrc->a[0].pIBIndex = 0;
|
|
|
|
/* generate the SELECT expression tree. */
|
|
- pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
|
|
- pOrderBy,0,pLimit,pOffset);
|
|
- if( pSelect == 0 ) return 0;
|
|
+ pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
|
|
+ pOrderBy,0,pLimit
|
|
+ );
|
|
|
|
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
|
- pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
|
- pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0) : 0;
|
|
+ pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0);
|
|
sqlite3PExprAddSelect(pParse, pInClause, pSelect);
|
|
return pInClause;
|
|
-
|
|
-limit_where_cleanup:
|
|
- sqlite3ExprDelete(pParse->db, pWhere);
|
|
- sqlite3ExprListDelete(pParse->db, pOrderBy);
|
|
- sqlite3ExprDelete(pParse->db, pLimit);
|
|
- sqlite3ExprDelete(pParse->db, pOffset);
|
|
- return 0;
|
|
}
|
|
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
|
|
/* && !defined(SQLITE_OMIT_SUBQUERY) */
|
|
@@ -105595,7 +111306,9 @@
|
|
SQLITE_PRIVATE void sqlite3DeleteFrom(
|
|
Parse *pParse, /* The parser context */
|
|
SrcList *pTabList, /* The table from which we should delete things */
|
|
- Expr *pWhere /* The WHERE clause. May be null */
|
|
+ Expr *pWhere, /* The WHERE clause. May be null */
|
|
+ ExprList *pOrderBy, /* ORDER BY clause. May be null */
|
|
+ Expr *pLimit /* LIMIT clause. May be null */
|
|
){
|
|
Vdbe *v; /* The virtual database engine */
|
|
Table *pTab; /* The table from which records will be deleted */
|
|
@@ -105610,7 +111323,7 @@
|
|
AuthContext sContext; /* Authorization context */
|
|
NameContext sNC; /* Name context to resolve expressions in */
|
|
int iDb; /* Database number */
|
|
- int memCnt = -1; /* Memory cell used for change counting */
|
|
+ int memCnt = 0; /* Memory cell used for change counting */
|
|
int rcauth; /* Value returned by authorization callback */
|
|
int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */
|
|
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
|
@@ -105640,6 +111353,7 @@
|
|
}
|
|
assert( pTabList->nSrc==1 );
|
|
|
|
+
|
|
/* Locate the table which we want to delete. This table has to be
|
|
** put in an SrcList structure because some of the subroutines we
|
|
** will be calling are designed to work with multiple tables and expect
|
|
@@ -105654,16 +111368,26 @@
|
|
#ifndef SQLITE_OMIT_TRIGGER
|
|
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
|
isView = pTab->pSelect!=0;
|
|
- bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
|
#else
|
|
# define pTrigger 0
|
|
# define isView 0
|
|
#endif
|
|
+ bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
|
#ifdef SQLITE_OMIT_VIEW
|
|
# undef isView
|
|
# define isView 0
|
|
#endif
|
|
|
|
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
|
+ if( !isView ){
|
|
+ pWhere = sqlite3LimitWhere(
|
|
+ pParse, pTabList, pWhere, pOrderBy, pLimit, "DELETE"
|
|
+ );
|
|
+ pOrderBy = 0;
|
|
+ pLimit = 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* If pTab is really a view, make sure it has been initialized.
|
|
*/
|
|
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
|
@@ -105704,7 +111428,7 @@
|
|
goto delete_from_cleanup;
|
|
}
|
|
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
|
- sqlite3BeginWriteOperation(pParse, 1, iDb);
|
|
+ sqlite3BeginWriteOperation(pParse, bComplex, iDb);
|
|
|
|
/* If we are trying to delete from a view, realize that view into
|
|
** an ephemeral table.
|
|
@@ -105711,8 +111435,12 @@
|
|
*/
|
|
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
|
if( isView ){
|
|
- sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
|
|
+ sqlite3MaterializeView(pParse, pTab,
|
|
+ pWhere, pOrderBy, pLimit, iTabCur
|
|
+ );
|
|
iDataCur = iIdxCur = iTabCur;
|
|
+ pOrderBy = 0;
|
|
+ pLimit = 0;
|
|
}
|
|
#endif
|
|
|
|
@@ -105728,7 +111456,10 @@
|
|
/* Initialize the counter of the number of rows deleted, if
|
|
** we are counting rows.
|
|
*/
|
|
- if( db->flags & SQLITE_CountRows ){
|
|
+ if( (db->flags & SQLITE_CountRows)!=0
|
|
+ && !pParse->nested
|
|
+ && !pParse->pTriggerTab
|
|
+ ){
|
|
memCnt = ++pParse->nMem;
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
|
|
}
|
|
@@ -105756,7 +111487,7 @@
|
|
assert( !isView );
|
|
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
|
if( HasRowid(pTab) ){
|
|
- sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
|
+ sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt ? memCnt : -1,
|
|
pTab->zName, P4_STATIC);
|
|
}
|
|
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
@@ -105801,9 +111532,10 @@
|
|
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
|
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
|
|
assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
|
|
+ if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse);
|
|
|
|
/* Keep track of the number of rows to be deleted */
|
|
- if( db->flags & SQLITE_CountRows ){
|
|
+ if( memCnt ){
|
|
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
|
}
|
|
|
|
@@ -105816,9 +111548,8 @@
|
|
}
|
|
iKey = iPk;
|
|
}else{
|
|
- iKey = pParse->nMem + 1;
|
|
- iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0);
|
|
- if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
|
+ iKey = ++pParse->nMem;
|
|
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, -1, iKey);
|
|
}
|
|
|
|
if( eOnePass!=ONEPASS_OFF ){
|
|
@@ -105889,7 +111620,11 @@
|
|
}
|
|
}else if( pPk ){
|
|
addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
|
|
- sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
|
+ if( IsVirtual(pTab) ){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
|
+ }
|
|
assert( nKey==0 ); /* OP_Found will use a composite key */
|
|
}else{
|
|
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
|
|
@@ -105902,13 +111637,16 @@
|
|
if( IsVirtual(pTab) ){
|
|
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
|
|
sqlite3VtabMakeWritable(pParse, pTab);
|
|
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
|
- sqlite3VdbeChangeP5(v, OE_Abort);
|
|
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
|
sqlite3MayAbort(pParse);
|
|
- if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
|
|
- pParse->isMultiWrite = 0;
|
|
+ if( eOnePass==ONEPASS_SINGLE ){
|
|
+ sqlite3VdbeAddOp1(v, OP_Close, iTabCur);
|
|
+ if( sqlite3IsToplevel(pParse) ){
|
|
+ pParse->isMultiWrite = 0;
|
|
+ }
|
|
}
|
|
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
|
+ sqlite3VdbeChangeP5(v, OE_Abort);
|
|
}else
|
|
#endif
|
|
{
|
|
@@ -105942,7 +111680,7 @@
|
|
** generating code because of a call to sqlite3NestedParse(), do not
|
|
** invoke the callback function.
|
|
*/
|
|
- if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
|
|
+ if( memCnt ){
|
|
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
|
|
sqlite3VdbeSetNumCols(v, 1);
|
|
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
|
|
@@ -105952,6 +111690,10 @@
|
|
sqlite3AuthContextPop(&sContext);
|
|
sqlite3SrcListDelete(db, pTabList);
|
|
sqlite3ExprDelete(db, pWhere);
|
|
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
|
+ sqlite3ExprListDelete(db, pOrderBy);
|
|
+ sqlite3ExprDelete(db, pLimit);
|
|
+#endif
|
|
sqlite3DbFree(db, aToOpen);
|
|
return;
|
|
}
|
|
@@ -106109,7 +111851,7 @@
|
|
u8 p5 = 0;
|
|
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
|
|
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
|
- if( pParse->nested==0 ){
|
|
+ if( pParse->nested==0 || 0==sqlite3_stricmp(pTab->zName, "sqlite_stat1") ){
|
|
sqlite3VdbeAppendP4(v, (char*)pTab, P4_TABLE);
|
|
}
|
|
if( eMode!=ONEPASS_OFF ){
|
|
@@ -106240,7 +111982,6 @@
|
|
if( pIdx->pPartIdxWhere ){
|
|
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
|
|
pParse->iSelfTab = iDataCur + 1;
|
|
- sqlite3ExprCachePush(pParse);
|
|
sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
|
|
SQLITE_JUMPIFNULL);
|
|
pParse->iSelfTab = 0;
|
|
@@ -106287,7 +112028,6 @@
|
|
SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
|
|
if( iLabel ){
|
|
sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel);
|
|
- sqlite3ExprCachePop(pParse);
|
|
}
|
|
}
|
|
|
|
@@ -106330,6 +112070,8 @@
|
|
** iteration of the aggregate loop.
|
|
*/
|
|
static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){
|
|
+ assert( context->isError<=0 );
|
|
+ context->isError = -1;
|
|
context->skipFlag = 1;
|
|
}
|
|
|
|
@@ -106396,8 +112138,6 @@
|
|
int argc,
|
|
sqlite3_value **argv
|
|
){
|
|
- int len;
|
|
-
|
|
assert( argc==1 );
|
|
UNUSED_PARAMETER(argc);
|
|
switch( sqlite3_value_type(argv[0]) ){
|
|
@@ -106409,13 +112149,17 @@
|
|
}
|
|
case SQLITE_TEXT: {
|
|
const unsigned char *z = sqlite3_value_text(argv[0]);
|
|
+ const unsigned char *z0;
|
|
+ unsigned char c;
|
|
if( z==0 ) return;
|
|
- len = 0;
|
|
- while( *z ){
|
|
- len++;
|
|
- SQLITE_SKIP_UTF8(z);
|
|
+ z0 = z;
|
|
+ while( (c = *z)!=0 ){
|
|
+ z++;
|
|
+ if( c>=0xc0 ){
|
|
+ while( (*z & 0xc0)==0x80 ){ z++; z0++; }
|
|
+ }
|
|
}
|
|
- sqlite3_result_int(context, len);
|
|
+ sqlite3_result_int(context, (int)(z-z0));
|
|
break;
|
|
}
|
|
default: {
|
|
@@ -106542,7 +112286,7 @@
|
|
x.apArg = argv+1;
|
|
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
|
|
str.printfFlags = SQLITE_PRINTF_SQLFUNC;
|
|
- sqlite3XPrintf(&str, zFormat, &x);
|
|
+ sqlite3_str_appendf(&str, zFormat, &x);
|
|
n = str.nChar;
|
|
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
|
|
SQLITE_DYNAMIC);
|
|
@@ -106993,16 +112737,20 @@
|
|
** c or cx.
|
|
*/
|
|
if( c<=0x80 ){
|
|
- u32 cx;
|
|
+ char zStop[3];
|
|
int bMatch;
|
|
if( noCase ){
|
|
- cx = sqlite3Toupper(c);
|
|
- c = sqlite3Tolower(c);
|
|
+ zStop[0] = sqlite3Toupper(c);
|
|
+ zStop[1] = sqlite3Tolower(c);
|
|
+ zStop[2] = 0;
|
|
}else{
|
|
- cx = c;
|
|
+ zStop[0] = c;
|
|
+ zStop[1] = 0;
|
|
}
|
|
- while( (c2 = *(zString++))!=0 ){
|
|
- if( c2!=c && c2!=cx ) continue;
|
|
+ while(1){
|
|
+ zString += strcspn((const char*)zString, zStop);
|
|
+ if( zString[0]==0 ) break;
|
|
+ zString++;
|
|
bMatch = patternCompare(zPattern,zString,pInfo,matchOther);
|
|
if( bMatch!=SQLITE_NOMATCH ) return bMatch;
|
|
}
|
|
@@ -107160,7 +112908,8 @@
|
|
#ifdef SQLITE_TEST
|
|
sqlite3_like_count++;
|
|
#endif
|
|
- sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)==SQLITE_MATCH);
|
|
+ sqlite3_result_int(context,
|
|
+ patternCompare(zB, zA, pInfo, escape)==SQLITE_MATCH);
|
|
}
|
|
}
|
|
|
|
@@ -107485,6 +113234,8 @@
|
|
i64 nOut; /* Maximum size of zOut */
|
|
int loopLimit; /* Last zStr[] that might match zPattern[] */
|
|
int i, j; /* Loop counters */
|
|
+ unsigned cntExpand; /* Number zOut expansions */
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
|
|
assert( argc==3 );
|
|
UNUSED_PARAMETER(argc);
|
|
@@ -107516,33 +113267,40 @@
|
|
return;
|
|
}
|
|
loopLimit = nStr - nPattern;
|
|
+ cntExpand = 0;
|
|
for(i=j=0; i<=loopLimit; i++){
|
|
if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){
|
|
zOut[j++] = zStr[i];
|
|
}else{
|
|
- u8 *zOld;
|
|
- sqlite3 *db = sqlite3_context_db_handle(context);
|
|
- nOut += nRep - nPattern;
|
|
- testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
|
- testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
|
- if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
- sqlite3_result_error_toobig(context);
|
|
- sqlite3_free(zOut);
|
|
- return;
|
|
+ if( nRep>nPattern ){
|
|
+ nOut += nRep - nPattern;
|
|
+ testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
|
+ testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
|
+ if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
|
+ sqlite3_result_error_toobig(context);
|
|
+ sqlite3_free(zOut);
|
|
+ return;
|
|
+ }
|
|
+ cntExpand++;
|
|
+ if( (cntExpand&(cntExpand-1))==0 ){
|
|
+ /* Grow the size of the output buffer only on substitutions
|
|
+ ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */
|
|
+ u8 *zOld;
|
|
+ zOld = zOut;
|
|
+ zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1));
|
|
+ if( zOut==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ sqlite3_free(zOld);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
}
|
|
- zOld = zOut;
|
|
- zOut = sqlite3_realloc64(zOut, (int)nOut);
|
|
- if( zOut==0 ){
|
|
- sqlite3_result_error_nomem(context);
|
|
- sqlite3_free(zOld);
|
|
- return;
|
|
- }
|
|
memcpy(&zOut[j], zRep, nRep);
|
|
j += nRep;
|
|
i += nPattern-1;
|
|
}
|
|
}
|
|
- assert( j+nStr-i+1==nOut );
|
|
+ assert( j+nStr-i+1<=nOut );
|
|
memcpy(&zOut[j], &zStr[i], nStr-i);
|
|
j += nStr - i;
|
|
assert( j<=nOut );
|
|
@@ -107782,7 +113540,7 @@
|
|
i64 v = sqlite3_value_int64(argv[0]);
|
|
p->rSum += v;
|
|
if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){
|
|
- p->overflow = 1;
|
|
+ p->approx = p->overflow = 1;
|
|
}
|
|
}else{
|
|
p->rSum += sqlite3_value_double(argv[0]);
|
|
@@ -107790,6 +113548,32 @@
|
|
}
|
|
}
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
|
|
+ SumCtx *p;
|
|
+ int type;
|
|
+ assert( argc==1 );
|
|
+ UNUSED_PARAMETER(argc);
|
|
+ p = sqlite3_aggregate_context(context, sizeof(*p));
|
|
+ type = sqlite3_value_numeric_type(argv[0]);
|
|
+ /* p is always non-NULL because sumStep() will have been called first
|
|
+ ** to initialize it */
|
|
+ if( ALWAYS(p) && type!=SQLITE_NULL ){
|
|
+ assert( p->cnt>0 );
|
|
+ p->cnt--;
|
|
+ assert( type==SQLITE_INTEGER || p->approx );
|
|
+ if( type==SQLITE_INTEGER && p->approx==0 ){
|
|
+ i64 v = sqlite3_value_int64(argv[0]);
|
|
+ p->rSum -= v;
|
|
+ p->iSum -= v;
|
|
+ }else{
|
|
+ p->rSum -= sqlite3_value_double(argv[0]);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#else
|
|
+# define sumInverse 0
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
static void sumFinalize(sqlite3_context *context){
|
|
SumCtx *p;
|
|
p = sqlite3_aggregate_context(context, 0);
|
|
@@ -107824,6 +113608,9 @@
|
|
typedef struct CountCtx CountCtx;
|
|
struct CountCtx {
|
|
i64 n;
|
|
+#ifdef SQLITE_DEBUG
|
|
+ int bInverse; /* True if xInverse() ever called */
|
|
+#endif
|
|
};
|
|
|
|
/*
|
|
@@ -107841,7 +113628,7 @@
|
|
** sure it still operates correctly, verify that its count agrees with our
|
|
** internal count when using count(*) and when the total count can be
|
|
** expressed as a 32-bit integer. */
|
|
- assert( argc==1 || p==0 || p->n>0x7fffffff
|
|
+ assert( argc==1 || p==0 || p->n>0x7fffffff || p->bInverse
|
|
|| p->n==sqlite3_aggregate_count(context) );
|
|
#endif
|
|
}
|
|
@@ -107850,6 +113637,21 @@
|
|
p = sqlite3_aggregate_context(context, 0);
|
|
sqlite3_result_int64(context, p ? p->n : 0);
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+static void countInverse(sqlite3_context *ctx, int argc, sqlite3_value **argv){
|
|
+ CountCtx *p;
|
|
+ p = sqlite3_aggregate_context(ctx, sizeof(*p));
|
|
+ /* p is always non-NULL since countStep() will have been called first */
|
|
+ if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && ALWAYS(p) ){
|
|
+ p->n--;
|
|
+#ifdef SQLITE_DEBUG
|
|
+ p->bInverse = 1;
|
|
+#endif
|
|
+ }
|
|
+}
|
|
+#else
|
|
+# define countInverse 0
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
|
|
/*
|
|
** Routines to implement min() and max() aggregate functions.
|
|
@@ -107866,7 +113668,7 @@
|
|
pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
|
|
if( !pBest ) return;
|
|
|
|
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
|
+ if( sqlite3_value_type(pArg)==SQLITE_NULL ){
|
|
if( pBest->flags ) sqlite3SkipAccumulatorLoad(context);
|
|
}else if( pBest->flags ){
|
|
int max;
|
|
@@ -107892,7 +113694,7 @@
|
|
sqlite3VdbeMemCopy(pBest, pArg);
|
|
}
|
|
}
|
|
-static void minMaxFinalize(sqlite3_context *context){
|
|
+static void minMaxValueFinalize(sqlite3_context *context, int bValue){
|
|
sqlite3_value *pRes;
|
|
pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0);
|
|
if( pRes ){
|
|
@@ -107899,9 +113701,19 @@
|
|
if( pRes->flags ){
|
|
sqlite3_result_value(context, pRes);
|
|
}
|
|
- sqlite3VdbeMemRelease(pRes);
|
|
+ if( bValue==0 ) sqlite3VdbeMemRelease(pRes);
|
|
}
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+static void minMaxValue(sqlite3_context *context){
|
|
+ minMaxValueFinalize(context, 1);
|
|
+}
|
|
+#else
|
|
+# define minMaxValue 0
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+static void minMaxFinalize(sqlite3_context *context){
|
|
+ minMaxValueFinalize(context, 0);
|
|
+}
|
|
|
|
/*
|
|
** group_concat(EXPR, ?SEPARATOR?)
|
|
@@ -107931,20 +113743,52 @@
|
|
zSep = ",";
|
|
nSep = 1;
|
|
}
|
|
- if( zSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
|
|
+ if( zSep ) sqlite3_str_append(pAccum, zSep, nSep);
|
|
}
|
|
zVal = (char*)sqlite3_value_text(argv[0]);
|
|
nVal = sqlite3_value_bytes(argv[0]);
|
|
- if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
|
+ if( zVal ) sqlite3_str_append(pAccum, zVal, nVal);
|
|
}
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+static void groupConcatInverse(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int n;
|
|
+ StrAccum *pAccum;
|
|
+ assert( argc==1 || argc==2 );
|
|
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
|
+ pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
|
|
+ /* pAccum is always non-NULL since groupConcatStep() will have always
|
|
+ ** run frist to initialize it */
|
|
+ if( ALWAYS(pAccum) ){
|
|
+ n = sqlite3_value_bytes(argv[0]);
|
|
+ if( argc==2 ){
|
|
+ n += sqlite3_value_bytes(argv[1]);
|
|
+ }else{
|
|
+ n++;
|
|
+ }
|
|
+ if( n>=(int)pAccum->nChar ){
|
|
+ pAccum->nChar = 0;
|
|
+ }else{
|
|
+ pAccum->nChar -= n;
|
|
+ memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar);
|
|
+ }
|
|
+ if( pAccum->nChar==0 ) pAccum->mxAlloc = 0;
|
|
+ }
|
|
+}
|
|
+#else
|
|
+# define groupConcatInverse 0
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
static void groupConcatFinalize(sqlite3_context *context){
|
|
StrAccum *pAccum;
|
|
pAccum = sqlite3_aggregate_context(context, 0);
|
|
if( pAccum ){
|
|
- if( pAccum->accError==STRACCUM_TOOBIG ){
|
|
+ if( pAccum->accError==SQLITE_TOOBIG ){
|
|
sqlite3_result_error_toobig(context);
|
|
- }else if( pAccum->accError==STRACCUM_NOMEM ){
|
|
+ }else if( pAccum->accError==SQLITE_NOMEM ){
|
|
sqlite3_result_error_nomem(context);
|
|
}else{
|
|
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
|
|
@@ -107952,6 +113796,24 @@
|
|
}
|
|
}
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+static void groupConcatValue(sqlite3_context *context){
|
|
+ sqlite3_str *pAccum;
|
|
+ pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0);
|
|
+ if( pAccum ){
|
|
+ if( pAccum->accError==SQLITE_TOOBIG ){
|
|
+ sqlite3_result_error_toobig(context);
|
|
+ }else if( pAccum->accError==SQLITE_NOMEM ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ const char *zText = sqlite3_str_value(pAccum);
|
|
+ sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#else
|
|
+# define groupConcatValue 0
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
|
|
/*
|
|
** This routine does per-connection function registration. Most
|
|
@@ -107989,10 +113851,10 @@
|
|
}else{
|
|
pInfo = (struct compareInfo*)&likeInfoNorm;
|
|
}
|
|
- sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
|
|
- sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
|
|
+ sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
|
|
+ sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
|
|
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
|
|
- (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0);
|
|
+ (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0, 0, 0);
|
|
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
|
|
setLikeOptFlag(db, "like",
|
|
caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
|
|
@@ -108001,10 +113863,15 @@
|
|
/*
|
|
** pExpr points to an expression which implements a function. If
|
|
** it is appropriate to apply the LIKE optimization to that function
|
|
-** then set aWc[0] through aWc[2] to the wildcard characters and
|
|
-** return TRUE. If the function is not a LIKE-style function then
|
|
-** return FALSE.
|
|
+** then set aWc[0] through aWc[2] to the wildcard characters and the
|
|
+** escape character and then return TRUE. If the function is not a
|
|
+** LIKE-style function then return FALSE.
|
|
**
|
|
+** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE
|
|
+** operator if c is a string literal that is exactly one byte in length.
|
|
+** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is
|
|
+** no ESCAPE clause.
|
|
+**
|
|
** *pIsNocase is set to true if uppercase and lowercase are equivalent for
|
|
** the function (default for LIKE). If the function makes the distinction
|
|
** between uppercase and lowercase (as does GLOB) then *pIsNocase is set to
|
|
@@ -108012,17 +113879,26 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
|
FuncDef *pDef;
|
|
- if( pExpr->op!=TK_FUNCTION
|
|
- || !pExpr->x.pList
|
|
- || pExpr->x.pList->nExpr!=2
|
|
- ){
|
|
+ int nExpr;
|
|
+ if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
|
|
return 0;
|
|
}
|
|
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
|
- pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
|
|
+ nExpr = pExpr->x.pList->nExpr;
|
|
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0);
|
|
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
|
|
return 0;
|
|
}
|
|
+ if( nExpr<3 ){
|
|
+ aWc[3] = 0;
|
|
+ }else{
|
|
+ Expr *pEscape = pExpr->x.pList->a[2].pExpr;
|
|
+ char *zEscape;
|
|
+ if( pEscape->op!=TK_STRING ) return 0;
|
|
+ zEscape = pEscape->u.zToken;
|
|
+ if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
|
|
+ aWc[3] = zEscape[0];
|
|
+ }
|
|
|
|
/* The memcpy() statement assumes that the wildcard characters are
|
|
** the first three statements in the compareInfo structure. The
|
|
@@ -108075,6 +113951,10 @@
|
|
#ifdef SQLITE_DEBUG
|
|
FUNCTION2(affinity, 1, 0, 0, noopFunc, SQLITE_FUNC_AFFINITY),
|
|
#endif
|
|
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
|
+ FUNCTION2(sqlite_offset, 1, 0, 0, noopFunc, SQLITE_FUNC_OFFSET|
|
|
+ SQLITE_FUNC_TYPEOF),
|
|
+#endif
|
|
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
|
|
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
|
|
FUNCTION(rtrim, 1, 2, 0, trimFunc ),
|
|
@@ -108083,11 +113963,11 @@
|
|
FUNCTION(trim, 2, 3, 0, trimFunc ),
|
|
FUNCTION(min, -1, 0, 1, minmaxFunc ),
|
|
FUNCTION(min, 0, 0, 1, 0 ),
|
|
- AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize,
|
|
+ WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
|
|
SQLITE_FUNC_MINMAX ),
|
|
FUNCTION(max, -1, 1, 1, minmaxFunc ),
|
|
FUNCTION(max, 0, 1, 1, 0 ),
|
|
- AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize,
|
|
+ WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
|
|
SQLITE_FUNC_MINMAX ),
|
|
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
|
|
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
|
|
@@ -108118,14 +113998,17 @@
|
|
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
|
|
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
|
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
|
- AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
|
|
- AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
|
|
- AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
|
|
- AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
|
|
- SQLITE_FUNC_COUNT ),
|
|
- AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
|
|
- AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
|
- AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
|
+ WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
|
|
+ WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
|
|
+ WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
|
|
+ WAGGREGATE(count, 0,0,0, countStep,
|
|
+ countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
|
|
+ WAGGREGATE(count, 1,0,0, countStep,
|
|
+ countFinalize, countFinalize, countInverse, 0 ),
|
|
+ WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
|
|
+ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
|
|
+ WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
|
|
+ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
|
|
|
|
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
|
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
|
@@ -108145,6 +114028,7 @@
|
|
#ifndef SQLITE_OMIT_ALTERTABLE
|
|
sqlite3AlterFunctions();
|
|
#endif
|
|
+ sqlite3WindowFunctions();
|
|
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
|
sqlite3AnalyzeFunctions();
|
|
#endif
|
|
@@ -108503,6 +114387,12 @@
|
|
int iCur = pParse->nTab - 1; /* Cursor number to use */
|
|
int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */
|
|
|
|
+ sqlite3VdbeVerifyAbortable(v,
|
|
+ (!pFKey->isDeferred
|
|
+ && !(pParse->db->flags & SQLITE_DeferFKs)
|
|
+ && !pParse->pToplevel
|
|
+ && !pParse->isMultiWrite) ? OE_Abort : OE_Ignore);
|
|
+
|
|
/* If nIncr is less than zero, then check at runtime if there are any
|
|
** outstanding constraints to resolve. If there are not, there is no need
|
|
** to check if deleting this row resolves any outstanding violations.
|
|
@@ -108668,7 +114558,7 @@
|
|
){
|
|
Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
|
|
if( pExpr ){
|
|
- pExpr->pTab = pTab;
|
|
+ pExpr->y.pTab = pTab;
|
|
pExpr->iTable = iCursor;
|
|
pExpr->iColumn = iCol;
|
|
}
|
|
@@ -108876,11 +114766,12 @@
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
|
sqlite3 *db = pParse->db;
|
|
- if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) && !pTab->pSelect ){
|
|
+ if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){
|
|
int iSkip = 0;
|
|
Vdbe *v = sqlite3GetVdbe(pParse);
|
|
|
|
assert( v ); /* VDBE has already been allocated */
|
|
+ assert( pTab->pSelect==0 ); /* Not a view */
|
|
if( sqlite3FkReferences(pTab)==0 ){
|
|
/* Search for a deferred foreign key constraint for which this table
|
|
** is the child table. If one cannot be found, return without
|
|
@@ -108897,7 +114788,7 @@
|
|
}
|
|
|
|
pParse->disableTriggers = 1;
|
|
- sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0);
|
|
+ sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0, 0, 0);
|
|
pParse->disableTriggers = 0;
|
|
|
|
/* If the DELETE has generated immediate foreign key constraint
|
|
@@ -108910,6 +114801,7 @@
|
|
** constraints are violated.
|
|
*/
|
|
if( (db->flags & SQLITE_DeferFKs)==0 ){
|
|
+ sqlite3VdbeVerifyAbortable(v, OE_Abort);
|
|
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
|
|
VdbeCoverage(v);
|
|
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
|
@@ -109455,7 +115347,7 @@
|
|
sqlite3ExprListAppend(pParse, 0, pRaise),
|
|
sqlite3SrcListAppend(db, 0, &tFrom, 0),
|
|
pWhere,
|
|
- 0, 0, 0, 0, 0, 0
|
|
+ 0, 0, 0, 0, 0
|
|
);
|
|
pWhere = 0;
|
|
}
|
|
@@ -109742,7 +115634,8 @@
|
|
}while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
|
|
pTab->zColAff = zColAff;
|
|
}
|
|
- i = sqlite3Strlen30(zColAff);
|
|
+ assert( zColAff!=0 );
|
|
+ i = sqlite3Strlen30NN(zColAff);
|
|
if( i ){
|
|
if( iReg ){
|
|
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
|
|
@@ -109806,11 +115699,12 @@
|
|
** first use of table pTab. On 2nd and subsequent uses, the original
|
|
** AutoincInfo structure is used.
|
|
**
|
|
-** Three memory locations are allocated:
|
|
+** Four consecutive registers are allocated:
|
|
**
|
|
-** (1) Register to hold the name of the pTab table.
|
|
-** (2) Register to hold the maximum ROWID of pTab.
|
|
-** (3) Register to hold the rowid in sqlite_sequence of pTab
|
|
+** (1) The name of the pTab table.
|
|
+** (2) The maximum ROWID of pTab.
|
|
+** (3) The rowid in sqlite_sequence of pTab
|
|
+** (4) The original value of the max ROWID in pTab, or NULL if none
|
|
**
|
|
** The 2nd register is the one that is returned. That is all the
|
|
** insert routine needs to know about.
|
|
@@ -109821,12 +115715,27 @@
|
|
Table *pTab /* The table we are writing to */
|
|
){
|
|
int memId = 0; /* Register holding maximum rowid */
|
|
+ assert( pParse->db->aDb[iDb].pSchema!=0 );
|
|
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
|
- && (pParse->db->flags & SQLITE_Vacuum)==0
|
|
+ && (pParse->db->mDbFlags & DBFLAG_Vacuum)==0
|
|
){
|
|
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
|
AutoincInfo *pInfo;
|
|
+ Table *pSeqTab = pParse->db->aDb[iDb].pSchema->pSeqTab;
|
|
|
|
+ /* Verify that the sqlite_sequence table exists and is an ordinary
|
|
+ ** rowid table with exactly two columns.
|
|
+ ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */
|
|
+ if( pSeqTab==0
|
|
+ || !HasRowid(pSeqTab)
|
|
+ || IsVirtual(pSeqTab)
|
|
+ || pSeqTab->nCol!=2
|
|
+ ){
|
|
+ pParse->nErr++;
|
|
+ pParse->rc = SQLITE_CORRUPT_SEQUENCE;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
pInfo = pToplevel->pAinc;
|
|
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
|
|
if( pInfo==0 ){
|
|
@@ -109838,7 +115747,7 @@
|
|
pInfo->iDb = iDb;
|
|
pToplevel->nMem++; /* Register to hold name of table */
|
|
pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */
|
|
- pToplevel->nMem++; /* Rowid in sqlite_sequence */
|
|
+ pToplevel->nMem +=2; /* Rowid in sqlite_sequence + orig max val */
|
|
}
|
|
memId = pInfo->regCtr;
|
|
}
|
|
@@ -109866,15 +115775,17 @@
|
|
static const int iLn = VDBE_OFFSET_LINENO(2);
|
|
static const VdbeOpList autoInc[] = {
|
|
/* 0 */ {OP_Null, 0, 0, 0},
|
|
- /* 1 */ {OP_Rewind, 0, 9, 0},
|
|
+ /* 1 */ {OP_Rewind, 0, 10, 0},
|
|
/* 2 */ {OP_Column, 0, 0, 0},
|
|
- /* 3 */ {OP_Ne, 0, 7, 0},
|
|
+ /* 3 */ {OP_Ne, 0, 9, 0},
|
|
/* 4 */ {OP_Rowid, 0, 0, 0},
|
|
/* 5 */ {OP_Column, 0, 1, 0},
|
|
- /* 6 */ {OP_Goto, 0, 9, 0},
|
|
- /* 7 */ {OP_Next, 0, 2, 0},
|
|
- /* 8 */ {OP_Integer, 0, 0, 0},
|
|
- /* 9 */ {OP_Close, 0, 0, 0}
|
|
+ /* 6 */ {OP_AddImm, 0, 0, 0},
|
|
+ /* 7 */ {OP_Copy, 0, 0, 0},
|
|
+ /* 8 */ {OP_Goto, 0, 11, 0},
|
|
+ /* 9 */ {OP_Next, 0, 2, 0},
|
|
+ /* 10 */ {OP_Integer, 0, 0, 0},
|
|
+ /* 11 */ {OP_Close, 0, 0, 0}
|
|
};
|
|
VdbeOp *aOp;
|
|
pDb = &db->aDb[p->iDb];
|
|
@@ -109885,7 +115796,7 @@
|
|
aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn);
|
|
if( aOp==0 ) break;
|
|
aOp[0].p2 = memId;
|
|
- aOp[0].p3 = memId+1;
|
|
+ aOp[0].p3 = memId+2;
|
|
aOp[2].p3 = memId;
|
|
aOp[3].p1 = memId-1;
|
|
aOp[3].p3 = memId;
|
|
@@ -109892,7 +115803,10 @@
|
|
aOp[3].p5 = SQLITE_JUMPIFNULL;
|
|
aOp[4].p2 = memId+1;
|
|
aOp[5].p3 = memId;
|
|
- aOp[8].p2 = memId;
|
|
+ aOp[6].p1 = memId;
|
|
+ aOp[7].p2 = memId+2;
|
|
+ aOp[7].p1 = memId;
|
|
+ aOp[10].p2 = memId;
|
|
}
|
|
}
|
|
|
|
@@ -109939,6 +115853,8 @@
|
|
|
|
iRec = sqlite3GetTempReg(pParse);
|
|
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
|
|
+ sqlite3VdbeAddOp3(v, OP_Le, memId+2, sqlite3VdbeCurrentAddr(v)+7, memId);
|
|
+ VdbeCoverage(v);
|
|
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
|
|
aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn);
|
|
if( aOp==0 ) break;
|
|
@@ -110076,11 +115992,11 @@
|
|
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. */
|
|
- int onError /* How to handle constraint errors */
|
|
+ int onError, /* How to handle constraint errors */
|
|
+ Upsert *pUpsert /* ON CONFLICT clauses for upsert, or NULL */
|
|
){
|
|
sqlite3 *db; /* The main database structure */
|
|
Table *pTab; /* The table to insert into. aka TABLE */
|
|
- char *zTab; /* Name of the table into which we are inserting */
|
|
int i, j; /* Loop counters */
|
|
Vdbe *v; /* Generate code into this virtual machine */
|
|
Index *pIdx; /* For looping over indices of the table */
|
|
@@ -110136,8 +116052,6 @@
|
|
/* Locate the table into which we will be inserting new information.
|
|
*/
|
|
assert( pTabList->nSrc==1 );
|
|
- zTab = pTabList->a[0].zName;
|
|
- if( NEVER(zTab==0) ) goto insert_cleanup;
|
|
pTab = sqlite3SrcListLookup(pParse, pTabList);
|
|
if( pTab==0 ){
|
|
goto insert_cleanup;
|
|
@@ -110374,7 +116288,10 @@
|
|
|
|
/* Initialize the count of rows to be inserted
|
|
*/
|
|
- if( db->flags & SQLITE_CountRows ){
|
|
+ if( (db->flags & SQLITE_CountRows)!=0
|
|
+ && !pParse->nested
|
|
+ && !pParse->pTriggerTab
|
|
+ ){
|
|
regRowCount = ++pParse->nMem;
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
|
}
|
|
@@ -110394,7 +116311,20 @@
|
|
pParse->nMem += pIdx->nColumn;
|
|
}
|
|
}
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+ if( pUpsert ){
|
|
+ pTabList->a[0].iCursor = iDataCur;
|
|
+ pUpsert->pUpsertSrc = pTabList;
|
|
+ pUpsert->regData = regData;
|
|
+ pUpsert->iDataCur = iDataCur;
|
|
+ pUpsert->iIdxCur = iIdxCur;
|
|
+ if( pUpsert->pUpsertTarget ){
|
|
+ sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
|
|
+
|
|
/* This is the top of the main insertion loop */
|
|
if( useTempTable ){
|
|
/* This block codes the top of loop only. The complete loop is the
|
|
@@ -110508,7 +116438,8 @@
|
|
VdbeOp *pOp;
|
|
sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
|
|
pOp = sqlite3VdbeGetOp(v, -1);
|
|
- if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
|
|
+ assert( pOp!=0 );
|
|
+ if( pOp->opcode==OP_Null && !IsVirtual(pTab) ){
|
|
appendFlag = 1;
|
|
pOp->opcode = OP_NewRowid;
|
|
pOp->p1 = iDataCur;
|
|
@@ -110595,7 +116526,7 @@
|
|
int isReplace; /* Set to true if constraints may cause a replace */
|
|
int bUseSeek; /* True to use OPFLAG_SEEKRESULT */
|
|
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
|
- regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0
|
|
+ regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert
|
|
);
|
|
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
|
|
|
|
@@ -110618,7 +116549,7 @@
|
|
|
|
/* Update the count of rows that are inserted
|
|
*/
|
|
- if( (db->flags & SQLITE_CountRows)!=0 ){
|
|
+ if( regRowCount ){
|
|
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
|
|
}
|
|
|
|
@@ -110655,7 +116586,7 @@
|
|
** generating code because of a call to sqlite3NestedParse(), do not
|
|
** invoke the callback function.
|
|
*/
|
|
- if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
|
|
+ if( regRowCount ){
|
|
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
|
sqlite3VdbeSetNumCols(v, 1);
|
|
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
|
|
@@ -110664,6 +116595,7 @@
|
|
insert_cleanup:
|
|
sqlite3SrcListDelete(db, pTabList);
|
|
sqlite3ExprListDelete(db, pList);
|
|
+ sqlite3UpsertDelete(db, pUpsert);
|
|
sqlite3SelectDelete(db, pSelect);
|
|
sqlite3IdListDelete(db, pColumn);
|
|
sqlite3DbFree(db, aRegIdx);
|
|
@@ -110683,14 +116615,15 @@
|
|
#endif
|
|
|
|
/*
|
|
-** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged()
|
|
+** Meanings of bits in of pWalker->eCode for
|
|
+** sqlite3ExprReferencesUpdatedColumn()
|
|
*/
|
|
#define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */
|
|
#define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */
|
|
|
|
-/* This is the Walker callback from checkConstraintUnchanged(). Set
|
|
-** bit 0x01 of pWalker->eCode if
|
|
-** pWalker->eCode to 0 if this expression node references any of the
|
|
+/* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn().
|
|
+* Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this
|
|
+** expression node references any of the
|
|
** columns that are being modifed by an UPDATE statement.
|
|
*/
|
|
static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){
|
|
@@ -110712,12 +116645,21 @@
|
|
** only columns that are modified by the UPDATE are those for which
|
|
** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true.
|
|
**
|
|
-** Return true if CHECK constraint pExpr does not use any of the
|
|
+** Return true if CHECK constraint pExpr uses any of the
|
|
** changing columns (or the rowid if it is changing). In other words,
|
|
-** return true if this CHECK constraint can be skipped when validating
|
|
+** return true if this CHECK constraint must be validated for
|
|
** the new row in the UPDATE statement.
|
|
+**
|
|
+** 2018-09-15: pExpr might also be an expression for an index-on-expressions.
|
|
+** The operation of this routine is the same - return true if an only if
|
|
+** the expression uses one or more of columns identified by the second and
|
|
+** third arguments.
|
|
*/
|
|
-static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
|
|
+SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(
|
|
+ Expr *pExpr, /* The expression to be checked */
|
|
+ int *aiChng, /* aiChng[x]>=0 if column x changed by the UPDATE */
|
|
+ int chngRowid /* True if UPDATE changes the rowid */
|
|
+){
|
|
Walker w;
|
|
memset(&w, 0, sizeof(w));
|
|
w.eCode = 0;
|
|
@@ -110732,7 +116674,7 @@
|
|
testcase( w.eCode==CKCNSTRNT_COLUMN );
|
|
testcase( w.eCode==CKCNSTRNT_ROWID );
|
|
testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) );
|
|
- return !w.eCode;
|
|
+ return w.eCode!=0;
|
|
}
|
|
|
|
/*
|
|
@@ -110830,7 +116772,8 @@
|
|
u8 overrideError, /* Override onError to this if not OE_Default */
|
|
int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
|
|
int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */
|
|
- int *aiChng /* column i is unchanged if aiChng[i]<0 */
|
|
+ int *aiChng, /* column i is unchanged if aiChng[i]<0 */
|
|
+ Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */
|
|
){
|
|
Vdbe *v; /* VDBE under constrution */
|
|
Index *pIdx; /* Pointer to one of the indices */
|
|
@@ -110843,10 +116786,13 @@
|
|
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 */
|
|
- int ipkTop = 0; /* Top of the rowid change constraint check */
|
|
- int ipkBottom = 0; /* Bottom of the rowid change constraint check */
|
|
+ Index *pUpIdx = 0; /* Index to which to apply the upsert */
|
|
u8 isUpdate; /* True if this is an UPDATE operation */
|
|
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
|
|
+ int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */
|
|
+ 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 */
|
|
|
|
isUpdate = regOldData!=0;
|
|
db = pParse->db;
|
|
@@ -110934,8 +116880,15 @@
|
|
for(i=0; i<pCheck->nExpr; i++){
|
|
int allOk;
|
|
Expr *pExpr = pCheck->a[i].pExpr;
|
|
- if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue;
|
|
+ if( aiChng
|
|
+ && !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng)
|
|
+ ){
|
|
+ /* The check constraints do not reference any of the columns being
|
|
+ ** updated so there is no point it verifying the check constraint */
|
|
+ continue;
|
|
+ }
|
|
allOk = sqlite3VdbeMakeLabel(v);
|
|
+ sqlite3VdbeVerifyAbortable(v, onError);
|
|
sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
|
|
if( onError==OE_Ignore ){
|
|
sqlite3VdbeGoto(v, ignoreDest);
|
|
@@ -110953,6 +116906,50 @@
|
|
}
|
|
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
|
|
|
+ /* UNIQUE and PRIMARY KEY constraints should be handled in the following
|
|
+ ** order:
|
|
+ **
|
|
+ ** (1) OE_Update
|
|
+ ** (2) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
|
|
+ ** (3) OE_Replace
|
|
+ **
|
|
+ ** OE_Fail and OE_Ignore must happen before any changes are made.
|
|
+ ** OE_Update guarantees that only a single row will change, so it
|
|
+ ** must happen before OE_Replace. Technically, OE_Abort and OE_Rollback
|
|
+ ** could happen in any order, but they are grouped up front for
|
|
+ ** convenience.
|
|
+ **
|
|
+ ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43
|
|
+ ** The order of constraints used to have OE_Update as (2) and OE_Abort
|
|
+ ** and so forth as (1). But apparently PostgreSQL checks the OE_Update
|
|
+ ** constraint before any others, so it had to be moved.
|
|
+ **
|
|
+ ** Constraint checking code is generated in this order:
|
|
+ ** (A) The rowid constraint
|
|
+ ** (B) Unique index constraints that do not have OE_Replace as their
|
|
+ ** default conflict resolution strategy
|
|
+ ** (C) Unique index that do use OE_Replace by default.
|
|
+ **
|
|
+ ** The ordering of (2) and (3) is accomplished by making sure the linked
|
|
+ ** list of indexes attached to a table puts all OE_Replace indexes last
|
|
+ ** in the list. See sqlite3CreateIndex() for where that happens.
|
|
+ */
|
|
+
|
|
+ if( pUpsert ){
|
|
+ if( pUpsert->pUpsertTarget==0 ){
|
|
+ /* An ON CONFLICT DO NOTHING clause, without a constraint-target.
|
|
+ ** Make all unique constraint resolution be OE_Ignore */
|
|
+ assert( pUpsert->pUpsertSet==0 );
|
|
+ overrideError = OE_Ignore;
|
|
+ pUpsert = 0;
|
|
+ }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){
|
|
+ /* If the constraint-target uniqueness check must be run first.
|
|
+ ** Jump to that uniqueness check now */
|
|
+ upsertJump = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
+ VdbeComment((v, "UPSERT constraint goes first"));
|
|
+ }
|
|
+ }
|
|
+
|
|
/* If rowid is changing, make sure the new rowid does not previously
|
|
** exist in the table.
|
|
*/
|
|
@@ -110967,6 +116964,28 @@
|
|
onError = OE_Abort;
|
|
}
|
|
|
|
+ /* figure out whether or not upsert applies in this case */
|
|
+ if( pUpsert && pUpsert->pUpsertIdx==0 ){
|
|
+ if( pUpsert->pUpsertSet==0 ){
|
|
+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
|
|
+ }else{
|
|
+ onError = OE_Update; /* DO UPDATE */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If the response to a rowid conflict is REPLACE but the response
|
|
+ ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
|
|
+ ** to defer the running of the rowid conflict checking until after
|
|
+ ** the UNIQUE constraints have run.
|
|
+ */
|
|
+ if( onError==OE_Replace /* IPK rule is REPLACE */
|
|
+ && onError!=overrideError /* Rules for other contraints are different */
|
|
+ && pTab->pIndex /* There exist other constraints */
|
|
+ ){
|
|
+ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
|
|
+ VdbeComment((v, "defer IPK REPLACE until last"));
|
|
+ }
|
|
+
|
|
if( isUpdate ){
|
|
/* pkChng!=0 does not mean that the rowid has changed, only that
|
|
** it might have changed. Skip the conflict logic below if the rowid
|
|
@@ -110976,26 +116995,13 @@
|
|
VdbeCoverage(v);
|
|
}
|
|
|
|
- /* If the response to a rowid conflict is REPLACE but the response
|
|
- ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
|
|
- ** to defer the running of the rowid conflict checking until after
|
|
- ** the UNIQUE constraints have run.
|
|
- */
|
|
- if( onError==OE_Replace && overrideError!=OE_Replace ){
|
|
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
- if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){
|
|
- ipkTop = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
/* Check to see if the new rowid already exists in the table. Skip
|
|
** the following conflict logic if it does not. */
|
|
+ VdbeNoopComment((v, "uniqueness check for ROWID"));
|
|
+ sqlite3VdbeVerifyAbortable(v, onError);
|
|
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
|
|
VdbeCoverage(v);
|
|
|
|
- /* Generate code that deals with a rowid collision */
|
|
switch( onError ){
|
|
default: {
|
|
onError = OE_Abort;
|
|
@@ -111004,6 +117010,9 @@
|
|
case OE_Rollback:
|
|
case OE_Abort:
|
|
case OE_Fail: {
|
|
+ testcase( onError==OE_Rollback );
|
|
+ testcase( onError==OE_Abort );
|
|
+ testcase( onError==OE_Fail );
|
|
sqlite3RowidConstraint(pParse, onError, pTab);
|
|
break;
|
|
}
|
|
@@ -111040,14 +117049,13 @@
|
|
regNewData, 1, 0, OE_Replace, 1, -1);
|
|
}else{
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
|
- if( HasRowid(pTab) ){
|
|
- /* This OP_Delete opcode fires the pre-update-hook only. It does
|
|
- ** not modify the b-tree. It is more efficient to let the coming
|
|
- ** OP_Insert replace the existing entry than it is to delete the
|
|
- ** existing entry and then insert a new one. */
|
|
- sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
|
|
- sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
|
- }
|
|
+ assert( HasRowid(pTab) );
|
|
+ /* This OP_Delete opcode fires the pre-update-hook only. It does
|
|
+ ** not modify the b-tree. It is more efficient to let the coming
|
|
+ ** OP_Insert replace the existing entry than it is to delete the
|
|
+ ** existing entry and then insert a new one. */
|
|
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
|
|
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
|
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
|
if( pTab->pIndex ){
|
|
sqlite3MultiWrite(pParse);
|
|
@@ -111057,8 +117065,14 @@
|
|
seenReplace = 1;
|
|
break;
|
|
}
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+ case OE_Update: {
|
|
+ sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur);
|
|
+ /* Fall through */
|
|
+ }
|
|
+#endif
|
|
case OE_Ignore: {
|
|
- /*assert( seenReplace==0 );*/
|
|
+ testcase( onError==OE_Ignore );
|
|
sqlite3VdbeGoto(v, ignoreDest);
|
|
break;
|
|
}
|
|
@@ -111066,7 +117080,7 @@
|
|
sqlite3VdbeResolveLabel(v, addrRowidOk);
|
|
if( ipkTop ){
|
|
ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
- sqlite3VdbeJumpHere(v, ipkTop);
|
|
+ sqlite3VdbeJumpHere(v, ipkTop-1);
|
|
}
|
|
}
|
|
|
|
@@ -111084,13 +117098,22 @@
|
|
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
|
|
|
|
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
|
|
- if( bAffinityDone==0 ){
|
|
+ if( pUpIdx==pIdx ){
|
|
+ addrUniqueOk = upsertJump+1;
|
|
+ upsertBypass = sqlite3VdbeGoto(v, 0);
|
|
+ VdbeComment((v, "Skip upsert subroutine"));
|
|
+ sqlite3VdbeJumpHere(v, upsertJump);
|
|
+ }else{
|
|
+ addrUniqueOk = sqlite3VdbeMakeLabel(v);
|
|
+ }
|
|
+ if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
|
|
sqlite3TableAffinity(v, pTab, regNewData+1);
|
|
bAffinityDone = 1;
|
|
}
|
|
+ VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
|
|
iThisCur = iIdxCur+ix;
|
|
- addrUniqueOk = sqlite3VdbeMakeLabel(v);
|
|
|
|
+
|
|
/* Skip partial indices for which the WHERE clause is not true */
|
|
if( pIdx->pPartIdxWhere ){
|
|
sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
|
|
@@ -111149,6 +117172,15 @@
|
|
onError = OE_Abort;
|
|
}
|
|
|
|
+ /* Figure out if the upsert clause applies to this index */
|
|
+ if( pUpIdx==pIdx ){
|
|
+ if( pUpsert->pUpsertSet==0 ){
|
|
+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
|
|
+ }else{
|
|
+ onError = OE_Update; /* DO UPDATE */
|
|
+ }
|
|
+ }
|
|
+
|
|
/* Collision detection may be omitted if all of the following are true:
|
|
** (1) The conflict resolution algorithm is REPLACE
|
|
** (2) The table is a WITHOUT ROWID table
|
|
@@ -111169,6 +117201,7 @@
|
|
}
|
|
|
|
/* 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);
|
|
|
|
@@ -111230,15 +117263,25 @@
|
|
|
|
/* Generate code that executes if the new index entry is not unique */
|
|
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
|
- || onError==OE_Ignore || onError==OE_Replace );
|
|
+ || onError==OE_Ignore || onError==OE_Replace || onError==OE_Update );
|
|
switch( onError ){
|
|
case OE_Rollback:
|
|
case OE_Abort:
|
|
case OE_Fail: {
|
|
+ testcase( onError==OE_Rollback );
|
|
+ testcase( onError==OE_Abort );
|
|
+ testcase( onError==OE_Fail );
|
|
sqlite3UniqueConstraint(pParse, onError, pIdx);
|
|
break;
|
|
}
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+ case OE_Update: {
|
|
+ sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix);
|
|
+ /* Fall through */
|
|
+ }
|
|
+#endif
|
|
case OE_Ignore: {
|
|
+ testcase( onError==OE_Ignore );
|
|
sqlite3VdbeGoto(v, ignoreDest);
|
|
break;
|
|
}
|
|
@@ -111245,10 +117288,12 @@
|
|
default: {
|
|
Trigger *pTrigger = 0;
|
|
assert( onError==OE_Replace );
|
|
- sqlite3MultiWrite(pParse);
|
|
if( db->flags&SQLITE_RecTriggers ){
|
|
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
|
}
|
|
+ if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
|
+ sqlite3MultiWrite(pParse);
|
|
+ }
|
|
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
|
regR, nPkField, 0, OE_Replace,
|
|
(pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur);
|
|
@@ -111256,14 +117301,22 @@
|
|
break;
|
|
}
|
|
}
|
|
- sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
|
+ if( pUpIdx==pIdx ){
|
|
+ sqlite3VdbeGoto(v, upsertJump+1);
|
|
+ sqlite3VdbeJumpHere(v, upsertBypass);
|
|
+ }else{
|
|
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
|
+ }
|
|
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
|
|
}
|
|
+
|
|
+ /* If the IPK constraint is a REPLACE, run it last */
|
|
if( ipkTop ){
|
|
sqlite3VdbeGoto(v, ipkTop+1);
|
|
+ VdbeComment((v, "Do IPK REPLACE"));
|
|
sqlite3VdbeJumpHere(v, ipkBottom);
|
|
}
|
|
-
|
|
+
|
|
*pbMayReplace = seenReplace;
|
|
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
|
|
}
|
|
@@ -111359,7 +117412,6 @@
|
|
sqlite3SetMakeRecordP5(v, pTab);
|
|
if( !bAffinityDone ){
|
|
sqlite3TableAffinity(v, pTab, 0);
|
|
- sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
|
|
}
|
|
if( pParse->nested ){
|
|
pik_flags = 0;
|
|
@@ -111605,7 +117657,6 @@
|
|
if( pSelect->pLimit ){
|
|
return 0; /* SELECT may not have a LIMIT clause */
|
|
}
|
|
- assert( pSelect->pOffset==0 ); /* Must be so if pLimit==0 */
|
|
if( pSelect->pPrior ){
|
|
return 0; /* SELECT may not be a compound query */
|
|
}
|
|
@@ -111655,7 +117706,7 @@
|
|
Column *pDestCol = &pDest->aCol[i];
|
|
Column *pSrcCol = &pSrc->aCol[i];
|
|
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
|
|
- if( (db->flags & SQLITE_Vacuum)==0
|
|
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0
|
|
&& (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
|
|
){
|
|
return 0; /* Neither table may have __hidden__ columns */
|
|
@@ -111731,7 +117782,7 @@
|
|
regRowid = sqlite3GetTempReg(pParse);
|
|
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
|
|
assert( HasRowid(pDest) || destHasUniqueIdx );
|
|
- if( (db->flags & SQLITE_Vacuum)==0 && (
|
|
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (
|
|
(pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
|
|
|| destHasUniqueIdx /* (2) */
|
|
|| (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
|
|
@@ -111738,8 +117789,8 @@
|
|
)){
|
|
/* In some circumstances, we are able to run the xfer optimization
|
|
** only if the destination table is initially empty. Unless the
|
|
- ** SQLITE_Vacuum flag is set, this block generates code to make
|
|
- ** that determination. If SQLITE_Vacuum is set, then the destination
|
|
+ ** DBFLAG_Vacuum flag is set, this block generates code to make
|
|
+ ** that determination. If DBFLAG_Vacuum is set, then the destination
|
|
** table is always empty.
|
|
**
|
|
** Conditions under which the destination must be empty:
|
|
@@ -111763,6 +117814,7 @@
|
|
emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
|
if( pDest->iPKey>=0 ){
|
|
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
|
|
+ sqlite3VdbeVerifyAbortable(v, onError);
|
|
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
|
|
VdbeCoverage(v);
|
|
sqlite3RowidConstraint(pParse, onError, pDest);
|
|
@@ -111775,8 +117827,8 @@
|
|
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
|
}
|
|
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
|
- if( db->flags & SQLITE_Vacuum ){
|
|
- sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
|
+ if( db->mDbFlags & DBFLAG_Vacuum ){
|
|
+ sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
|
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
|
|
OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
|
}else{
|
|
@@ -111807,13 +117859,13 @@
|
|
VdbeComment((v, "%s", pDestIdx->zName));
|
|
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
|
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
|
- if( db->flags & SQLITE_Vacuum ){
|
|
+ if( db->mDbFlags & DBFLAG_Vacuum ){
|
|
/* This INSERT command is part of a VACUUM operation, which guarantees
|
|
** that the destination table is empty. If all indexed columns use
|
|
** collation sequence BINARY, then it can also be assumed that the
|
|
** index will be populated by inserting keys in strictly sorted
|
|
** order. In this case, instead of seeking within the b-tree as part
|
|
- ** of every OP_IdxInsert opcode, an OP_Last is added before the
|
|
+ ** of every OP_IdxInsert opcode, an OP_SeekEnd is added before the
|
|
** OP_IdxInsert to seek to the point within the b-tree where each key
|
|
** should be inserted. This is faster.
|
|
**
|
|
@@ -111828,7 +117880,7 @@
|
|
}
|
|
if( i==pSrcIdx->nColumn ){
|
|
idxInsFlags = OPFLAG_USESEEKRESULT;
|
|
- sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
|
+ sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
|
}
|
|
}
|
|
if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
|
|
@@ -112159,7 +118211,7 @@
|
|
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
|
const char*,const char*),void*);
|
|
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
|
- char * (*snprintf)(int,char*,const char*,...);
|
|
+ char * (*xsnprintf)(int,char*,const char*,...);
|
|
int (*step)(sqlite3_stmt*);
|
|
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
|
char const**,char const**,int*,int*,int*);
|
|
@@ -112271,7 +118323,7 @@
|
|
int (*uri_boolean)(const char*,const char*,int);
|
|
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
|
const char *(*uri_parameter)(const char*,const char*);
|
|
- char *(*vsnprintf)(int,char*,const char*,va_list);
|
|
+ char *(*xvsnprintf)(int,char*,const char*,va_list);
|
|
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
|
/* Version 3.8.7 and later */
|
|
int (*auto_extension)(void(*)(void));
|
|
@@ -112317,6 +118369,33 @@
|
|
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
|
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
|
void *(*value_pointer)(sqlite3_value*,const char*);
|
|
+ int (*vtab_nochange)(sqlite3_context*);
|
|
+ int (*value_nochange)(sqlite3_value*);
|
|
+ const char *(*vtab_collation)(sqlite3_index_info*,int);
|
|
+ /* Version 3.24.0 and later */
|
|
+ int (*keyword_count)(void);
|
|
+ int (*keyword_name)(int,const char**,int*);
|
|
+ int (*keyword_check)(const char*,int);
|
|
+ sqlite3_str *(*str_new)(sqlite3*);
|
|
+ char *(*str_finish)(sqlite3_str*);
|
|
+ void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
|
+ void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
|
+ void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
|
+ void (*str_appendall)(sqlite3_str*, const char *zIn);
|
|
+ void (*str_appendchar)(sqlite3_str*, int N, char C);
|
|
+ void (*str_reset)(sqlite3_str*);
|
|
+ int (*str_errcode)(sqlite3_str*);
|
|
+ int (*str_length)(sqlite3_str*);
|
|
+ char *(*str_value)(sqlite3_str*);
|
|
+ /* Version 3.25.0 and later */
|
|
+ int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void (*xFinal)(sqlite3_context*),
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void(*xDestroy)(void*));
|
|
+ /* Version 3.26.0 and later */
|
|
+ const char *(*normalized_sql)(sqlite3_stmt*);
|
|
};
|
|
|
|
/*
|
|
@@ -112443,7 +118522,7 @@
|
|
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
|
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
|
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
|
-#define sqlite3_snprintf sqlite3_api->snprintf
|
|
+#define sqlite3_snprintf sqlite3_api->xsnprintf
|
|
#define sqlite3_step sqlite3_api->step
|
|
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
|
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
|
@@ -112467,7 +118546,7 @@
|
|
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
|
#define sqlite3_value_type sqlite3_api->value_type
|
|
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
|
-#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
|
+#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
|
#define sqlite3_overload_function sqlite3_api->overload_function
|
|
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
@@ -112543,7 +118622,7 @@
|
|
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
|
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
|
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
|
-#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
|
+#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
|
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
|
/* Version 3.8.7 and later */
|
|
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
|
@@ -112583,6 +118662,29 @@
|
|
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
|
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
|
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
|
+/* Version 3.22.0 and later */
|
|
+#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
|
+#define sqlite3_value_nochange sqlite3_api->value_nochange
|
|
+#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
|
+/* Version 3.24.0 and later */
|
|
+#define sqlite3_keyword_count sqlite3_api->keyword_count
|
|
+#define sqlite3_keyword_name sqlite3_api->keyword_name
|
|
+#define sqlite3_keyword_check sqlite3_api->keyword_check
|
|
+#define sqlite3_str_new sqlite3_api->str_new
|
|
+#define sqlite3_str_finish sqlite3_api->str_finish
|
|
+#define sqlite3_str_appendf sqlite3_api->str_appendf
|
|
+#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
|
+#define sqlite3_str_append sqlite3_api->str_append
|
|
+#define sqlite3_str_appendall sqlite3_api->str_appendall
|
|
+#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
|
+#define sqlite3_str_reset sqlite3_api->str_reset
|
|
+#define sqlite3_str_errcode sqlite3_api->str_errcode
|
|
+#define sqlite3_str_length sqlite3_api->str_length
|
|
+#define sqlite3_str_value sqlite3_api->str_value
|
|
+/* Version 3.25.0 and later */
|
|
+#define sqlite3_create_window_function sqlite3_api->create_window_function
|
|
+/* Version 3.26.0 and later */
|
|
+#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
|
|
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
@@ -112671,6 +118773,7 @@
|
|
# define sqlite3_declare_vtab 0
|
|
# define sqlite3_vtab_config 0
|
|
# define sqlite3_vtab_on_conflict 0
|
|
+# define sqlite3_vtab_collation 0
|
|
#endif
|
|
|
|
#ifdef SQLITE_OMIT_SHARED_CACHE
|
|
@@ -113017,7 +119120,34 @@
|
|
sqlite3_prepare16_v3,
|
|
sqlite3_bind_pointer,
|
|
sqlite3_result_pointer,
|
|
- sqlite3_value_pointer
|
|
+ sqlite3_value_pointer,
|
|
+ /* Version 3.22.0 and later */
|
|
+ sqlite3_vtab_nochange,
|
|
+ sqlite3_value_nochange,
|
|
+ sqlite3_vtab_collation,
|
|
+ /* Version 3.24.0 and later */
|
|
+ sqlite3_keyword_count,
|
|
+ sqlite3_keyword_name,
|
|
+ sqlite3_keyword_check,
|
|
+ sqlite3_str_new,
|
|
+ sqlite3_str_finish,
|
|
+ sqlite3_str_appendf,
|
|
+ sqlite3_str_vappendf,
|
|
+ sqlite3_str_append,
|
|
+ sqlite3_str_appendall,
|
|
+ sqlite3_str_appendchar,
|
|
+ sqlite3_str_reset,
|
|
+ sqlite3_str_errcode,
|
|
+ sqlite3_str_length,
|
|
+ sqlite3_str_value,
|
|
+ /* Version 3.25.0 and later */
|
|
+ sqlite3_create_window_function,
|
|
+ /* Version 3.26.0 and later */
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
+ sqlite3_normalized_sql
|
|
+#else
|
|
+ 0
|
|
+#endif
|
|
};
|
|
|
|
/*
|
|
@@ -113467,10 +119597,9 @@
|
|
#define PragTyp_ACTIVATE_EXTENSIONS 40
|
|
#define PragTyp_HEXKEY 41
|
|
#define PragTyp_KEY 42
|
|
-#define PragTyp_REKEY 43
|
|
-#define PragTyp_LOCK_STATUS 44
|
|
-#define PragTyp_PARSER_TRACE 45
|
|
-#define PragTyp_STATS 46
|
|
+#define PragTyp_LOCK_STATUS 43
|
|
+#define PragTyp_PARSER_TRACE 44
|
|
+#define PragTyp_STATS 45
|
|
|
|
/* Property flags associated with various pragma. */
|
|
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
|
|
@@ -113487,21 +119616,22 @@
|
|
** result column is different from the name of the pragma
|
|
*/
|
|
static const char *const pragCName[] = {
|
|
- /* 0 */ "cache_size", /* Used by: default_cache_size */
|
|
- /* 1 */ "cid", /* Used by: table_info */
|
|
- /* 2 */ "name",
|
|
- /* 3 */ "type",
|
|
- /* 4 */ "notnull",
|
|
- /* 5 */ "dflt_value",
|
|
- /* 6 */ "pk",
|
|
- /* 7 */ "tbl", /* Used by: stats */
|
|
- /* 8 */ "idx",
|
|
- /* 9 */ "wdth",
|
|
- /* 10 */ "hght",
|
|
- /* 11 */ "flgs",
|
|
- /* 12 */ "seqno", /* Used by: index_info */
|
|
- /* 13 */ "cid",
|
|
- /* 14 */ "name",
|
|
+ /* 0 */ "id", /* Used by: foreign_key_list */
|
|
+ /* 1 */ "seq",
|
|
+ /* 2 */ "table",
|
|
+ /* 3 */ "from",
|
|
+ /* 4 */ "to",
|
|
+ /* 5 */ "on_update",
|
|
+ /* 6 */ "on_delete",
|
|
+ /* 7 */ "match",
|
|
+ /* 8 */ "cid", /* Used by: table_xinfo */
|
|
+ /* 9 */ "name",
|
|
+ /* 10 */ "type",
|
|
+ /* 11 */ "notnull",
|
|
+ /* 12 */ "dflt_value",
|
|
+ /* 13 */ "pk",
|
|
+ /* 14 */ "hidden",
|
|
+ /* table_info reuses 8 */
|
|
/* 15 */ "seqno", /* Used by: index_xinfo */
|
|
/* 16 */ "cid",
|
|
/* 17 */ "name",
|
|
@@ -113508,37 +119638,35 @@
|
|
/* 18 */ "desc",
|
|
/* 19 */ "coll",
|
|
/* 20 */ "key",
|
|
- /* 21 */ "seq", /* Used by: index_list */
|
|
- /* 22 */ "name",
|
|
- /* 23 */ "unique",
|
|
- /* 24 */ "origin",
|
|
- /* 25 */ "partial",
|
|
- /* 26 */ "seq", /* Used by: database_list */
|
|
+ /* 21 */ "tbl", /* Used by: stats */
|
|
+ /* 22 */ "idx",
|
|
+ /* 23 */ "wdth",
|
|
+ /* 24 */ "hght",
|
|
+ /* 25 */ "flgs",
|
|
+ /* 26 */ "seq", /* Used by: index_list */
|
|
/* 27 */ "name",
|
|
- /* 28 */ "file",
|
|
- /* 29 */ "name", /* Used by: function_list */
|
|
- /* 30 */ "builtin",
|
|
- /* 31 */ "name", /* Used by: module_list pragma_list */
|
|
- /* 32 */ "seq", /* Used by: collation_list */
|
|
- /* 33 */ "name",
|
|
- /* 34 */ "id", /* Used by: foreign_key_list */
|
|
- /* 35 */ "seq",
|
|
- /* 36 */ "table",
|
|
- /* 37 */ "from",
|
|
- /* 38 */ "to",
|
|
- /* 39 */ "on_update",
|
|
- /* 40 */ "on_delete",
|
|
- /* 41 */ "match",
|
|
- /* 42 */ "table", /* Used by: foreign_key_check */
|
|
- /* 43 */ "rowid",
|
|
- /* 44 */ "parent",
|
|
- /* 45 */ "fkid",
|
|
- /* 46 */ "busy", /* Used by: wal_checkpoint */
|
|
- /* 47 */ "log",
|
|
- /* 48 */ "checkpointed",
|
|
- /* 49 */ "timeout", /* Used by: busy_timeout */
|
|
- /* 50 */ "database", /* Used by: lock_status */
|
|
- /* 51 */ "status",
|
|
+ /* 28 */ "unique",
|
|
+ /* 29 */ "origin",
|
|
+ /* 30 */ "partial",
|
|
+ /* 31 */ "table", /* Used by: foreign_key_check */
|
|
+ /* 32 */ "rowid",
|
|
+ /* 33 */ "parent",
|
|
+ /* 34 */ "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 */
|
|
+ /* module_list pragma_list reuses 9 */
|
|
+ /* 46 */ "timeout", /* Used by: busy_timeout */
|
|
};
|
|
|
|
/* Definitions of all built-in pragmas */
|
|
@@ -113548,7 +119676,7 @@
|
|
u8 mPragFlg; /* Zero or more PragFlg_XXX values */
|
|
u8 iPragCName; /* Start of column names in pragCName[] */
|
|
u8 nPragCName; /* Num of col names. 0 means use pragma name */
|
|
- u32 iArg; /* Extra argument */
|
|
+ u64 iArg; /* Extra argument */
|
|
} PragmaName;
|
|
static const PragmaName aPragmaName[] = {
|
|
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
|
|
@@ -113584,7 +119712,7 @@
|
|
{/* zName: */ "busy_timeout",
|
|
/* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
- /* ColNames: */ 49, 1,
|
|
+ /* ColNames: */ 46, 1,
|
|
/* iArg: */ 0 },
|
|
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
|
{/* zName: */ "cache_size",
|
|
@@ -113621,7 +119749,7 @@
|
|
{/* zName: */ "collation_list",
|
|
/* ePragTyp: */ PragTyp_COLLATION_LIST,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
- /* ColNames: */ 32, 2,
|
|
+ /* ColNames: */ 26, 2,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
|
|
@@ -113656,7 +119784,7 @@
|
|
{/* zName: */ "database_list",
|
|
/* ePragTyp: */ PragTyp_DATABASE_LIST,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
|
|
- /* ColNames: */ 26, 3,
|
|
+ /* ColNames: */ 35, 3,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
|
|
@@ -113663,7 +119791,7 @@
|
|
{/* zName: */ "default_cache_size",
|
|
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
|
|
- /* ColNames: */ 0, 1,
|
|
+ /* ColNames: */ 45, 1,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
@@ -113693,7 +119821,7 @@
|
|
{/* zName: */ "foreign_key_check",
|
|
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
|
|
- /* ColNames: */ 42, 4,
|
|
+ /* ColNames: */ 31, 4,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FOREIGN_KEY)
|
|
@@ -113700,7 +119828,7 @@
|
|
{/* zName: */ "foreign_key_list",
|
|
/* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
|
- /* ColNames: */ 34, 8,
|
|
+ /* ColNames: */ 0, 8,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
@@ -113736,7 +119864,7 @@
|
|
{/* zName: */ "function_list",
|
|
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
- /* ColNames: */ 29, 2,
|
|
+ /* ColNames: */ 41, 2,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#endif
|
|
@@ -113745,12 +119873,12 @@
|
|
/* ePragTyp: */ PragTyp_HEXKEY,
|
|
/* ePragFlg: */ 0,
|
|
/* ColNames: */ 0, 0,
|
|
- /* iArg: */ 0 },
|
|
+ /* iArg: */ 2 },
|
|
{/* zName: */ "hexrekey",
|
|
/* ePragTyp: */ PragTyp_HEXKEY,
|
|
/* ePragFlg: */ 0,
|
|
/* ColNames: */ 0, 0,
|
|
- /* iArg: */ 0 },
|
|
+ /* iArg: */ 3 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
#if !defined(SQLITE_OMIT_CHECK)
|
|
@@ -113772,12 +119900,12 @@
|
|
{/* zName: */ "index_info",
|
|
/* ePragTyp: */ PragTyp_INDEX_INFO,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
|
- /* ColNames: */ 12, 3,
|
|
+ /* ColNames: */ 15, 3,
|
|
/* iArg: */ 0 },
|
|
{/* zName: */ "index_list",
|
|
/* ePragTyp: */ PragTyp_INDEX_LIST,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
|
- /* ColNames: */ 21, 5,
|
|
+ /* ColNames: */ 26, 5,
|
|
/* iArg: */ 0 },
|
|
{/* zName: */ "index_xinfo",
|
|
/* ePragTyp: */ PragTyp_INDEX_INFO,
|
|
@@ -113812,6 +119940,11 @@
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
+ {/* zName: */ "legacy_alter_table",
|
|
+ /* ePragTyp: */ PragTyp_FLAG,
|
|
+ /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
|
+ /* ColNames: */ 0, 0,
|
|
+ /* iArg: */ SQLITE_LegacyAlter },
|
|
{/* zName: */ "legacy_file_format",
|
|
/* ePragTyp: */ PragTyp_FLAG,
|
|
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
|
@@ -113829,7 +119962,7 @@
|
|
{/* zName: */ "lock_status",
|
|
/* ePragTyp: */ PragTyp_LOCK_STATUS,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
- /* ColNames: */ 50, 2,
|
|
+ /* ColNames: */ 43, 2,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
|
@@ -113855,7 +119988,7 @@
|
|
{/* zName: */ "module_list",
|
|
/* ePragTyp: */ PragTyp_MODULE_LIST,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
- /* ColNames: */ 31, 1,
|
|
+ /* ColNames: */ 9, 1,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#endif
|
|
@@ -113888,7 +120021,7 @@
|
|
{/* zName: */ "pragma_list",
|
|
/* ePragTyp: */ PragTyp_PRAGMA_LIST,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
- /* ColNames: */ 31, 1,
|
|
+ /* ColNames: */ 9, 1,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
@@ -113919,10 +120052,10 @@
|
|
#endif
|
|
#if defined(SQLITE_HAS_CODEC)
|
|
{/* zName: */ "rekey",
|
|
- /* ePragTyp: */ PragTyp_REKEY,
|
|
+ /* ePragTyp: */ PragTyp_KEY,
|
|
/* ePragFlg: */ 0,
|
|
/* ColNames: */ 0, 0,
|
|
- /* iArg: */ 0 },
|
|
+ /* iArg: */ 1 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
{/* zName: */ "reverse_unordered_selects",
|
|
@@ -113975,7 +120108,7 @@
|
|
{/* zName: */ "stats",
|
|
/* ePragTyp: */ PragTyp_STATS,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
|
|
- /* ColNames: */ 7, 5,
|
|
+ /* ColNames: */ 21, 5,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
|
@@ -113989,8 +120122,13 @@
|
|
{/* zName: */ "table_info",
|
|
/* ePragTyp: */ PragTyp_TABLE_INFO,
|
|
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
|
- /* ColNames: */ 1, 6,
|
|
+ /* ColNames: */ 8, 6,
|
|
/* iArg: */ 0 },
|
|
+ {/* zName: */ "table_xinfo",
|
|
+ /* ePragTyp: */ PragTyp_TABLE_INFO,
|
|
+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
|
|
+ /* ColNames: */ 8, 7,
|
|
+ /* iArg: */ 1 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
|
{/* zName: */ "temp_store",
|
|
@@ -114004,6 +120142,18 @@
|
|
/* ColNames: */ 0, 0,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
+#if defined(SQLITE_HAS_CODEC)
|
|
+ {/* zName: */ "textkey",
|
|
+ /* ePragTyp: */ PragTyp_KEY,
|
|
+ /* ePragFlg: */ 0,
|
|
+ /* ColNames: */ 0, 0,
|
|
+ /* iArg: */ 4 },
|
|
+ {/* zName: */ "textrekey",
|
|
+ /* ePragTyp: */ PragTyp_KEY,
|
|
+ /* ePragFlg: */ 0,
|
|
+ /* ColNames: */ 0, 0,
|
|
+ /* iArg: */ 5 },
|
|
+#endif
|
|
{/* zName: */ "threads",
|
|
/* ePragTyp: */ PragTyp_THREADS,
|
|
/* ePragFlg: */ PragFlg_Result0,
|
|
@@ -114054,7 +120204,7 @@
|
|
{/* zName: */ "wal_checkpoint",
|
|
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
|
|
/* ePragFlg: */ PragFlg_NeedSchema,
|
|
- /* ColNames: */ 46, 3,
|
|
+ /* ColNames: */ 38, 3,
|
|
/* iArg: */ 0 },
|
|
#endif
|
|
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
|
@@ -114062,10 +120212,10 @@
|
|
/* ePragTyp: */ PragTyp_FLAG,
|
|
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
|
/* ColNames: */ 0, 0,
|
|
- /* iArg: */ SQLITE_WriteSchema },
|
|
+ /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
|
|
#endif
|
|
};
|
|
-/* Number of pragmas: 60 on by default, 77 total. */
|
|
+/* Number of pragmas: 62 on by default, 81 total. */
|
|
|
|
/************** End of pragma.h **********************************************/
|
|
/************** Continuing where we left off in pragma.c *********************/
|
|
@@ -114338,16 +120488,16 @@
|
|
/*
|
|
** Helper subroutine for PRAGMA integrity_check:
|
|
**
|
|
-** Generate code to output a single-column result row with the result
|
|
-** held in register regResult. Decrement the result count and halt if
|
|
-** the maximum number of result rows have been issued.
|
|
+** Generate code to output a single-column result row with a value of the
|
|
+** string held in register 3. Decrement the result count in register 1
|
|
+** and halt if the maximum number of result rows have been issued.
|
|
*/
|
|
-static int integrityCheckResultRow(Vdbe *v, int regResult){
|
|
+static int integrityCheckResultRow(Vdbe *v){
|
|
int addr;
|
|
- sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 1);
|
|
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
|
|
addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1);
|
|
VdbeCoverage(v);
|
|
- sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
|
|
+ sqlite3VdbeAddOp0(v, OP_Halt);
|
|
return addr;
|
|
}
|
|
|
|
@@ -115077,7 +121227,7 @@
|
|
setPragmaResultColumnNames(v, pPragma);
|
|
returnSingleInt(v, (db->flags & pPragma->iArg)!=0 );
|
|
}else{
|
|
- int mask = pPragma->iArg; /* Mask of bits to set or clear. */
|
|
+ u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */
|
|
if( db->autoCommit==0 ){
|
|
/* Foreign key support may not be enabled or disabled while not
|
|
** in auto-commit mode. */
|
|
@@ -115120,20 +121270,23 @@
|
|
** type: Column declaration type.
|
|
** notnull: True if 'NOT NULL' is part of column declaration
|
|
** dflt_value: The default value for the column, if any.
|
|
+ ** pk: Non-zero for PK fields.
|
|
*/
|
|
case PragTyp_TABLE_INFO: if( zRight ){
|
|
Table *pTab;
|
|
pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
|
|
if( pTab ){
|
|
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
int i, k;
|
|
int nHidden = 0;
|
|
Column *pCol;
|
|
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
- pParse->nMem = 6;
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ pParse->nMem = 7;
|
|
+ sqlite3CodeVerifySchema(pParse, iTabDb);
|
|
sqlite3ViewGetColumnNames(pParse, pTab);
|
|
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
|
- if( IsHiddenColumn(pCol) ){
|
|
+ int isHidden = IsHiddenColumn(pCol);
|
|
+ if( isHidden && pPragma->iArg==0 ){
|
|
nHidden++;
|
|
continue;
|
|
}
|
|
@@ -115145,13 +121298,14 @@
|
|
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
|
|
}
|
|
assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
|
|
- sqlite3VdbeMultiLoad(v, 1, "issisi",
|
|
+ sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi",
|
|
i-nHidden,
|
|
pCol->zName,
|
|
sqlite3ColumnType(pCol,""),
|
|
pCol->notNull ? 1 : 0,
|
|
pCol->pDflt ? pCol->pDflt->u.zToken : 0,
|
|
- k);
|
|
+ k,
|
|
+ isHidden);
|
|
}
|
|
}
|
|
}
|
|
@@ -115189,6 +121343,7 @@
|
|
Table *pTab;
|
|
pIdx = sqlite3FindIndex(db, zRight, zDb);
|
|
if( pIdx ){
|
|
+ int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
|
|
int i;
|
|
int mx;
|
|
if( pPragma->iArg ){
|
|
@@ -115201,7 +121356,7 @@
|
|
pParse->nMem = 3;
|
|
}
|
|
pTab = pIdx->pTable;
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ sqlite3CodeVerifySchema(pParse, iIdxDb);
|
|
assert( pParse->nMem<=pPragma->nPragCName );
|
|
for(i=0; i<mx; i++){
|
|
i16 cnum = pIdx->aiColumn[i];
|
|
@@ -115225,8 +121380,9 @@
|
|
int i;
|
|
pTab = sqlite3FindTable(db, zRight, zDb);
|
|
if( pTab ){
|
|
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
pParse->nMem = 5;
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ sqlite3CodeVerifySchema(pParse, iTabDb);
|
|
for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){
|
|
const char *azOrigin[] = { "c", "u", "pk" };
|
|
sqlite3VdbeMultiLoad(v, 1, "isisi",
|
|
@@ -115273,14 +121429,13 @@
|
|
pParse->nMem = 2;
|
|
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);
|
|
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
|
}
|
|
}
|
|
for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){
|
|
p = (FuncDef*)sqliteHashData(j);
|
|
sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 0);
|
|
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
|
|
}
|
|
}
|
|
break;
|
|
@@ -115292,7 +121447,6 @@
|
|
for(j=sqliteHashFirst(&db->aModule); j; j=sqliteHashNext(j)){
|
|
Module *pMod = (Module*)sqliteHashData(j);
|
|
sqlite3VdbeMultiLoad(v, 1, "s", pMod->zName);
|
|
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
|
}
|
|
}
|
|
break;
|
|
@@ -115302,7 +121456,6 @@
|
|
int i;
|
|
for(i=0; i<ArraySize(aPragmaName); i++){
|
|
sqlite3VdbeMultiLoad(v, 1, "s", aPragmaName[i].zName);
|
|
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
|
}
|
|
}
|
|
break;
|
|
@@ -115318,9 +121471,10 @@
|
|
if( pTab ){
|
|
pFK = pTab->pFKey;
|
|
if( pFK ){
|
|
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
int i = 0;
|
|
pParse->nMem = 8;
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ sqlite3CodeVerifySchema(pParse, iTabDb);
|
|
while(pFK){
|
|
int j;
|
|
for(j=0; j<pFK->nCol; j++){
|
|
@@ -115365,9 +121519,9 @@
|
|
pParse->nMem += 4;
|
|
regKey = ++pParse->nMem;
|
|
regRow = ++pParse->nMem;
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
|
|
while( k ){
|
|
+ int iTabDb;
|
|
if( zRight ){
|
|
pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
|
|
k = 0;
|
|
@@ -115376,21 +121530,23 @@
|
|
k = sqliteHashNext(k);
|
|
}
|
|
if( pTab==0 || pTab->pFKey==0 ) continue;
|
|
- sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
|
+ iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
+ sqlite3CodeVerifySchema(pParse, iTabDb);
|
|
+ sqlite3TableLock(pParse, iTabDb, pTab->tnum, 0, pTab->zName);
|
|
if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
|
|
- sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
|
|
+ sqlite3OpenTable(pParse, 0, iTabDb, pTab, OP_OpenRead);
|
|
sqlite3VdbeLoadString(v, regResult, pTab->zName);
|
|
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
|
|
pParent = sqlite3FindTable(db, pFK->zTo, zDb);
|
|
if( pParent==0 ) continue;
|
|
pIdx = 0;
|
|
- sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
|
|
+ sqlite3TableLock(pParse, iTabDb, pParent->tnum, 0, pParent->zName);
|
|
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
|
|
if( x==0 ){
|
|
if( pIdx==0 ){
|
|
- sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
|
|
+ sqlite3OpenTable(pParse, i, iTabDb, pParent, OP_OpenRead);
|
|
}else{
|
|
- sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
|
|
+ sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iTabDb);
|
|
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
|
}
|
|
}else{
|
|
@@ -115528,12 +121684,11 @@
|
|
|
|
/* Do an integrity check on each database file */
|
|
for(i=0; i<db->nDb; i++){
|
|
- HashElem *x;
|
|
- Hash *pTbls;
|
|
- int *aRoot;
|
|
- int cnt = 0;
|
|
- int mxIdx = 0;
|
|
- int nIdx;
|
|
+ HashElem *x; /* For looping over tables in the schema */
|
|
+ Hash *pTbls; /* Set of all tables in the schema */
|
|
+ int *aRoot; /* Array of root page numbers of all btrees */
|
|
+ int cnt = 0; /* Number of entries in aRoot[] */
|
|
+ int mxIdx = 0; /* Maximum number of indexes for any table */
|
|
|
|
if( OMIT_TEMPDB && i==1 ) continue;
|
|
if( iDb>=0 && i!=iDb ) continue;
|
|
@@ -115548,8 +121703,9 @@
|
|
assert( sqlite3SchemaMutexHeld(db, i, 0) );
|
|
pTbls = &db->aDb[i].pSchema->tblHash;
|
|
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
|
- Table *pTab = sqliteHashData(x);
|
|
- Index *pIdx;
|
|
+ Table *pTab = sqliteHashData(x); /* Current table */
|
|
+ Index *pIdx; /* An index on pTab */
|
|
+ int nIdx; /* Number of indexes on pTab */
|
|
if( HasRowid(pTab) ) cnt++;
|
|
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
|
|
if( nIdx>mxIdx ) mxIdx = nIdx;
|
|
@@ -115559,12 +121715,12 @@
|
|
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
|
Table *pTab = sqliteHashData(x);
|
|
Index *pIdx;
|
|
- if( HasRowid(pTab) ) aRoot[cnt++] = pTab->tnum;
|
|
+ if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
|
|
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
- aRoot[cnt++] = pIdx->tnum;
|
|
+ aRoot[++cnt] = pIdx->tnum;
|
|
}
|
|
}
|
|
- aRoot[cnt] = 0;
|
|
+ aRoot[0] = cnt;
|
|
|
|
/* Make sure sufficient number of registers have been allocated */
|
|
pParse->nMem = MAX( pParse->nMem, 8+mxIdx );
|
|
@@ -115577,9 +121733,8 @@
|
|
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
|
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
|
|
P4_DYNAMIC);
|
|
- sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
|
|
- sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
|
|
- integrityCheckResultRow(v, 2);
|
|
+ sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3);
|
|
+ integrityCheckResultRow(v);
|
|
sqlite3VdbeJumpHere(v, addr);
|
|
|
|
/* Make sure all the indices are constructed correctly.
|
|
@@ -115593,16 +121748,12 @@
|
|
int r1 = -1;
|
|
|
|
if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */
|
|
- if( pTab->pCheck==0
|
|
- && (pTab->tabFlags & TF_HasNotNull)==0
|
|
- && (pTab->pIndex==0 || isQuick)
|
|
- ){
|
|
- continue; /* No additional checks needed for this table */
|
|
- }
|
|
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
|
- sqlite3ExprCacheClear(pParse);
|
|
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
|
|
1, 0, &iDataCur, &iIdxCur);
|
|
+ /* reg[7] counts the number of entries in the table.
|
|
+ ** reg[8+i] counts the number of entries in the i-th index
|
|
+ */
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
|
|
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
|
|
@@ -115611,6 +121762,11 @@
|
|
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
|
|
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
|
|
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
|
|
+ if( !isQuick ){
|
|
+ /* Sanity check on record header decoding */
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nCol-1, 3);
|
|
+ sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
|
|
+ }
|
|
/* Verify that all NOT NULL columns really are NOT NULL */
|
|
for(j=0; j<pTab->nCol; j++){
|
|
char *zErr;
|
|
@@ -115623,7 +121779,7 @@
|
|
zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
|
|
pTab->aCol[j].zName);
|
|
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
|
|
- integrityCheckResultRow(v, 3);
|
|
+ integrityCheckResultRow(v);
|
|
sqlite3VdbeJumpHere(v, jmp2);
|
|
}
|
|
/* Verify CHECK constraints */
|
|
@@ -115635,7 +121791,6 @@
|
|
char *zErr;
|
|
int k;
|
|
pParse->iSelfTab = iDataCur + 1;
|
|
- sqlite3ExprCachePush(pParse);
|
|
for(k=pCheck->nExpr-1; k>0; k--){
|
|
sqlite3ExprIfFalse(pParse, pCheck->a[k].pExpr, addrCkFault, 0);
|
|
}
|
|
@@ -115646,57 +121801,58 @@
|
|
zErr = sqlite3MPrintf(db, "CHECK constraint failed in %s",
|
|
pTab->zName);
|
|
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
|
|
- integrityCheckResultRow(v, 3);
|
|
+ integrityCheckResultRow(v);
|
|
sqlite3VdbeResolveLabel(v, addrCkOk);
|
|
- sqlite3ExprCachePop(pParse);
|
|
}
|
|
sqlite3ExprListDelete(db, pCheck);
|
|
}
|
|
- /* Validate index entries for the current row */
|
|
- for(j=0, pIdx=pTab->pIndex; pIdx && !isQuick; pIdx=pIdx->pNext, j++){
|
|
- int jmp2, jmp3, jmp4, jmp5;
|
|
- int ckUniq = sqlite3VdbeMakeLabel(v);
|
|
- if( pPk==pIdx ) continue;
|
|
- r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
|
|
- pPrior, r1);
|
|
- pPrior = pIdx;
|
|
- sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */
|
|
- /* Verify that an index entry exists for the current table row */
|
|
- jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
|
|
- pIdx->nColumn); VdbeCoverage(v);
|
|
- sqlite3VdbeLoadString(v, 3, "row ");
|
|
- sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
|
|
- sqlite3VdbeLoadString(v, 4, " missing from index ");
|
|
- sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
|
- jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
|
- sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
|
- jmp4 = integrityCheckResultRow(v, 3);
|
|
- sqlite3VdbeJumpHere(v, jmp2);
|
|
- /* For UNIQUE indexes, verify that only one entry exists with the
|
|
- ** current key. The entry is unique if (1) any column is NULL
|
|
- ** or (2) the next entry has a different key */
|
|
- if( IsUniqueIndex(pIdx) ){
|
|
- int uniqOk = sqlite3VdbeMakeLabel(v);
|
|
- int jmp6;
|
|
- int kk;
|
|
- for(kk=0; kk<pIdx->nKeyCol; kk++){
|
|
- int iCol = pIdx->aiColumn[kk];
|
|
- assert( iCol!=XN_ROWID && iCol<pTab->nCol );
|
|
- if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
|
|
- sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
|
|
- VdbeCoverage(v);
|
|
+ if( !isQuick ){ /* Omit the remaining tests for quick_check */
|
|
+ /* Validate index entries for the current row */
|
|
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
|
+ int jmp2, jmp3, jmp4, jmp5;
|
|
+ int ckUniq = sqlite3VdbeMakeLabel(v);
|
|
+ if( pPk==pIdx ) continue;
|
|
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
|
|
+ pPrior, r1);
|
|
+ pPrior = pIdx;
|
|
+ sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1);/* increment entry count */
|
|
+ /* Verify that an index entry exists for the current table row */
|
|
+ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
|
|
+ pIdx->nColumn); VdbeCoverage(v);
|
|
+ sqlite3VdbeLoadString(v, 3, "row ");
|
|
+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
|
|
+ sqlite3VdbeLoadString(v, 4, " missing from index ");
|
|
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
|
+ jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
|
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
|
+ jmp4 = integrityCheckResultRow(v);
|
|
+ sqlite3VdbeJumpHere(v, jmp2);
|
|
+ /* For UNIQUE indexes, verify that only one entry exists with the
|
|
+ ** current key. The entry is unique if (1) any column is NULL
|
|
+ ** or (2) the next entry has a different key */
|
|
+ if( IsUniqueIndex(pIdx) ){
|
|
+ int uniqOk = sqlite3VdbeMakeLabel(v);
|
|
+ int jmp6;
|
|
+ int kk;
|
|
+ for(kk=0; kk<pIdx->nKeyCol; kk++){
|
|
+ int iCol = pIdx->aiColumn[kk];
|
|
+ assert( iCol!=XN_ROWID && iCol<pTab->nCol );
|
|
+ if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
|
|
+ sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
+ jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
|
|
+ sqlite3VdbeGoto(v, uniqOk);
|
|
+ sqlite3VdbeJumpHere(v, jmp6);
|
|
+ sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
|
|
+ pIdx->nKeyCol); VdbeCoverage(v);
|
|
+ sqlite3VdbeLoadString(v, 3, "non-unique entry in index ");
|
|
+ sqlite3VdbeGoto(v, jmp5);
|
|
+ sqlite3VdbeResolveLabel(v, uniqOk);
|
|
}
|
|
- jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
|
|
- sqlite3VdbeGoto(v, uniqOk);
|
|
- sqlite3VdbeJumpHere(v, jmp6);
|
|
- sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
|
|
- pIdx->nKeyCol); VdbeCoverage(v);
|
|
- sqlite3VdbeLoadString(v, 3, "non-unique entry in index ");
|
|
- sqlite3VdbeGoto(v, jmp5);
|
|
- sqlite3VdbeResolveLabel(v, uniqOk);
|
|
+ sqlite3VdbeJumpHere(v, jmp4);
|
|
+ sqlite3ResolvePartIdxLabel(pParse, jmp3);
|
|
}
|
|
- sqlite3VdbeJumpHere(v, jmp4);
|
|
- sqlite3ResolvePartIdxLabel(pParse, jmp3);
|
|
}
|
|
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
|
|
sqlite3VdbeJumpHere(v, loopTop-1);
|
|
@@ -115708,9 +121864,9 @@
|
|
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
|
|
addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v);
|
|
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
|
|
- sqlite3VdbeLoadString(v, 3, pIdx->zName);
|
|
- sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
|
|
- integrityCheckResultRow(v, 7);
|
|
+ sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
|
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
|
|
+ integrityCheckResultRow(v);
|
|
sqlite3VdbeJumpHere(v, addr);
|
|
}
|
|
}
|
|
@@ -115724,6 +121880,9 @@
|
|
{ OP_IfNotZero, 1, 4, 0}, /* 1 */
|
|
{ OP_String8, 0, 3, 0}, /* 2 */
|
|
{ OP_ResultRow, 3, 1, 0}, /* 3 */
|
|
+ { OP_Halt, 0, 0, 0}, /* 4 */
|
|
+ { OP_String8, 0, 3, 0}, /* 5 */
|
|
+ { OP_Goto, 0, 3, 0}, /* 6 */
|
|
};
|
|
VdbeOp *aOp;
|
|
|
|
@@ -115732,7 +121891,10 @@
|
|
aOp[0].p2 = 1-mxErr;
|
|
aOp[2].p4type = P4_STATIC;
|
|
aOp[2].p4.z = "ok";
|
|
+ aOp[5].p4type = P4_STATIC;
|
|
+ aOp[5].p4.z = (char*)sqlite3ErrStr(SQLITE_CORRUPT);
|
|
}
|
|
+ sqlite3VdbeChangeP3(v, 0, sqlite3VdbeCurrentAddr(v)-2);
|
|
}
|
|
}
|
|
break;
|
|
@@ -116153,14 +122315,26 @@
|
|
#endif
|
|
|
|
#ifdef SQLITE_HAS_CODEC
|
|
+ /* Pragma iArg
|
|
+ ** ---------- ------
|
|
+ ** key 0
|
|
+ ** rekey 1
|
|
+ ** hexkey 2
|
|
+ ** hexrekey 3
|
|
+ ** textkey 4
|
|
+ ** textrekey 5
|
|
+ */
|
|
case PragTyp_KEY: {
|
|
- if( zRight ) sqlite3_key_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
|
|
+ if( zRight ){
|
|
+ int n = pPragma->iArg<4 ? sqlite3Strlen30(zRight) : -1;
|
|
+ if( (pPragma->iArg & 1)==0 ){
|
|
+ sqlite3_key_v2(db, zDb, zRight, n);
|
|
+ }else{
|
|
+ sqlite3_rekey_v2(db, zDb, zRight, n);
|
|
+ }
|
|
+ }
|
|
break;
|
|
}
|
|
- case PragTyp_REKEY: {
|
|
- if( zRight ) sqlite3_rekey_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
|
|
- break;
|
|
- }
|
|
case PragTyp_HEXKEY: {
|
|
if( zRight ){
|
|
u8 iByte;
|
|
@@ -116170,7 +122344,7 @@
|
|
iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
|
|
if( (i&1)!=0 ) zKey[i/2] = iByte;
|
|
}
|
|
- if( (zLeft[3] & 0xf)==0xb ){
|
|
+ if( (pPragma->iArg & 1)==0 ){
|
|
sqlite3_key_v2(db, zDb, zKey, i/2);
|
|
}else{
|
|
sqlite3_rekey_v2(db, zDb, zKey, i/2);
|
|
@@ -116252,26 +122426,25 @@
|
|
UNUSED_PARAMETER(argc);
|
|
UNUSED_PARAMETER(argv);
|
|
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
|
- sqlite3StrAccumAppendAll(&acc, "CREATE TABLE x");
|
|
+ sqlite3_str_appendall(&acc, "CREATE TABLE x");
|
|
for(i=0, j=pPragma->iPragCName; i<pPragma->nPragCName; i++, j++){
|
|
- sqlite3XPrintf(&acc, "%c\"%s\"", cSep, pragCName[j]);
|
|
+ sqlite3_str_appendf(&acc, "%c\"%s\"", cSep, pragCName[j]);
|
|
cSep = ',';
|
|
}
|
|
if( i==0 ){
|
|
- sqlite3XPrintf(&acc, "(\"%s\"", pPragma->zName);
|
|
- cSep = ',';
|
|
+ sqlite3_str_appendf(&acc, "(\"%s\"", pPragma->zName);
|
|
i++;
|
|
}
|
|
j = 0;
|
|
if( pPragma->mPragFlg & PragFlg_Result1 ){
|
|
- sqlite3StrAccumAppendAll(&acc, ",arg HIDDEN");
|
|
+ sqlite3_str_appendall(&acc, ",arg HIDDEN");
|
|
j++;
|
|
}
|
|
if( pPragma->mPragFlg & (PragFlg_SchemaOpt|PragFlg_SchemaReq) ){
|
|
- sqlite3StrAccumAppendAll(&acc, ",schema HIDDEN");
|
|
+ sqlite3_str_appendall(&acc, ",schema HIDDEN");
|
|
j++;
|
|
}
|
|
- sqlite3StrAccumAppend(&acc, ")", 1);
|
|
+ sqlite3_str_append(&acc, ")", 1);
|
|
sqlite3StrAccumFinish(&acc);
|
|
assert( strlen(zBuf) < sizeof(zBuf)-1 );
|
|
rc = sqlite3_declare_vtab(db, zBuf);
|
|
@@ -116423,13 +122596,13 @@
|
|
}
|
|
}
|
|
sqlite3StrAccumInit(&acc, 0, 0, 0, pTab->db->aLimit[SQLITE_LIMIT_SQL_LENGTH]);
|
|
- sqlite3StrAccumAppendAll(&acc, "PRAGMA ");
|
|
+ sqlite3_str_appendall(&acc, "PRAGMA ");
|
|
if( pCsr->azArg[1] ){
|
|
- sqlite3XPrintf(&acc, "%Q.", pCsr->azArg[1]);
|
|
+ sqlite3_str_appendf(&acc, "%Q.", pCsr->azArg[1]);
|
|
}
|
|
- sqlite3StrAccumAppendAll(&acc, pTab->pName->zName);
|
|
+ sqlite3_str_appendall(&acc, pTab->pName->zName);
|
|
if( pCsr->azArg[0] ){
|
|
- sqlite3XPrintf(&acc, "=%Q", pCsr->azArg[0]);
|
|
+ sqlite3_str_appendf(&acc, "=%Q", pCsr->azArg[0]);
|
|
}
|
|
zSql = sqlite3StrAccumFinish(&acc);
|
|
if( zSql==0 ) return SQLITE_NOMEM;
|
|
@@ -116501,7 +122674,8 @@
|
|
0, /* xRename - rename the table */
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
- 0 /* xRollbackTo */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
};
|
|
|
|
/*
|
|
@@ -116552,15 +122726,23 @@
|
|
const char *zExtra /* Error information */
|
|
){
|
|
sqlite3 *db = pData->db;
|
|
- if( !db->mallocFailed && (db->flags & SQLITE_WriteSchema)==0 ){
|
|
+ if( db->mallocFailed ){
|
|
+ pData->rc = SQLITE_NOMEM_BKPT;
|
|
+ }else if( pData->pzErrMsg[0]!=0 ){
|
|
+ /* A error message has already been generated. Do not overwrite it */
|
|
+ }else if( pData->mInitFlags & INITFLAG_AlterTable ){
|
|
+ *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
|
|
+ pData->rc = SQLITE_ERROR;
|
|
+ }else if( db->flags & SQLITE_WriteSchema ){
|
|
+ pData->rc = SQLITE_CORRUPT_BKPT;
|
|
+ }else{
|
|
char *z;
|
|
if( zObj==0 ) zObj = "?";
|
|
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
|
|
- if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
|
|
- sqlite3DbFree(db, *pData->pzErrMsg);
|
|
+ if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
|
|
*pData->pzErrMsg = z;
|
|
+ pData->rc = SQLITE_CORRUPT_BKPT;
|
|
}
|
|
- pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
|
|
}
|
|
|
|
/*
|
|
@@ -116612,7 +122794,7 @@
|
|
rc = db->errCode;
|
|
assert( (rc&0xFF)==(rcp&0xFF) );
|
|
db->init.iDb = saved_iDb;
|
|
- assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
|
|
+ /* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */
|
|
if( SQLITE_OK!=rc ){
|
|
if( db->init.orphanTrigger ){
|
|
assert( iDb==1 );
|
|
@@ -116659,7 +122841,7 @@
|
|
** auxiliary databases. Return one of the SQLITE_ error codes to
|
|
** indicate success or failure.
|
|
*/
|
|
-static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
|
+SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
|
int rc;
|
|
int i;
|
|
#ifndef SQLITE_OMIT_DEPRECATED
|
|
@@ -116672,11 +122854,14 @@
|
|
const char *zMasterName;
|
|
int openedTransaction = 0;
|
|
|
|
+ assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
|
|
assert( iDb>=0 && iDb<db->nDb );
|
|
assert( db->aDb[iDb].pSchema );
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
|
|
|
+ db->init.busy = 1;
|
|
+
|
|
/* Construct the in-memory representation schema tables (sqlite_master or
|
|
** sqlite_temp_master) by invoking the parser directly. The appropriate
|
|
** table name will be inserted automatically by the parser so we can just
|
|
@@ -116685,12 +122870,13 @@
|
|
azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
|
|
azArg[1] = "1";
|
|
azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
|
|
- "rootpage integer,sql text)";
|
|
+ "rootpage int,sql text)";
|
|
azArg[3] = 0;
|
|
initData.db = db;
|
|
initData.iDb = iDb;
|
|
initData.rc = SQLITE_OK;
|
|
initData.pzErrMsg = pzErrMsg;
|
|
+ initData.mInitFlags = mFlags;
|
|
sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
|
|
if( initData.rc ){
|
|
rc = initData.rc;
|
|
@@ -116701,10 +122887,10 @@
|
|
*/
|
|
pDb = &db->aDb[iDb];
|
|
if( pDb->pBt==0 ){
|
|
- if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){
|
|
- DbSetProperty(db, 1, DB_SchemaLoaded);
|
|
- }
|
|
- return SQLITE_OK;
|
|
+ assert( iDb==1 );
|
|
+ DbSetProperty(db, 1, DB_SchemaLoaded);
|
|
+ rc = SQLITE_OK;
|
|
+ goto error_out;
|
|
}
|
|
|
|
/* If there is not already a read-only (or read-write) transaction opened
|
|
@@ -116712,7 +122898,7 @@
|
|
** will be closed before this function returns. */
|
|
sqlite3BtreeEnter(pDb->pBt);
|
|
if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
|
|
- rc = sqlite3BtreeBeginTrans(pDb->pBt, 0);
|
|
+ rc = sqlite3BtreeBeginTrans(pDb->pBt, 0, 0);
|
|
if( rc!=SQLITE_OK ){
|
|
sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
|
|
goto initone_error_out;
|
|
@@ -116740,6 +122926,9 @@
|
|
for(i=0; i<ArraySize(meta); i++){
|
|
sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
|
|
}
|
|
+ if( (db->flags & SQLITE_ResetDatabase)!=0 ){
|
|
+ memset(meta, 0, sizeof(meta));
|
|
+ }
|
|
pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1];
|
|
|
|
/* If opening a non-empty database, check the text encoding. For the
|
|
@@ -116839,8 +123028,8 @@
|
|
rc = SQLITE_NOMEM_BKPT;
|
|
sqlite3ResetAllSchemasOfConnection(db);
|
|
}
|
|
- if( rc==SQLITE_OK || (db->flags&SQLITE_WriteSchema)){
|
|
- /* Black magic: If the SQLITE_WriteSchema flag is set, then consider
|
|
+ if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){
|
|
+ /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider
|
|
** the schema loaded, even if errors occurred. In this situation the
|
|
** current sqlite3_prepare() operation will fail, but the following one
|
|
** will attempt to compile the supplied statement against whatever subset
|
|
@@ -116863,9 +123052,13 @@
|
|
sqlite3BtreeLeave(pDb->pBt);
|
|
|
|
error_out:
|
|
- if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
|
- sqlite3OomFault(db);
|
|
+ if( rc ){
|
|
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
|
+ sqlite3OomFault(db);
|
|
+ }
|
|
+ sqlite3ResetOneSchema(db, iDb);
|
|
}
|
|
+ db->init.busy = 0;
|
|
return rc;
|
|
}
|
|
|
|
@@ -116881,42 +123074,30 @@
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
|
int i, rc;
|
|
- int commit_internal = !(db->flags&SQLITE_InternChanges);
|
|
+ int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);
|
|
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
|
|
assert( db->init.busy==0 );
|
|
- rc = SQLITE_OK;
|
|
- db->init.busy = 1;
|
|
ENC(db) = SCHEMA_ENC(db);
|
|
- for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
|
- if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
|
|
- rc = sqlite3InitOne(db, i, pzErrMsg);
|
|
- if( rc ){
|
|
- sqlite3ResetOneSchema(db, i);
|
|
- }
|
|
+ assert( db->nDb>0 );
|
|
+ /* Do the main schema first */
|
|
+ if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){
|
|
+ rc = sqlite3InitOne(db, 0, pzErrMsg, 0);
|
|
+ if( rc ) return rc;
|
|
}
|
|
-
|
|
- /* Once all the other databases have been initialized, load the schema
|
|
- ** for the TEMP database. This is loaded last, as the TEMP database
|
|
- ** schema may contain references to objects in other databases.
|
|
- */
|
|
-#ifndef SQLITE_OMIT_TEMPDB
|
|
- assert( db->nDb>1 );
|
|
- if( rc==SQLITE_OK && !DbHasProperty(db, 1, DB_SchemaLoaded) ){
|
|
- rc = sqlite3InitOne(db, 1, pzErrMsg);
|
|
- if( rc ){
|
|
- sqlite3ResetOneSchema(db, 1);
|
|
+ /* All other schemas after the main schema. The "temp" schema must be last */
|
|
+ for(i=db->nDb-1; i>0; i--){
|
|
+ assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
|
|
+ if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
|
|
+ rc = sqlite3InitOne(db, i, pzErrMsg, 0);
|
|
+ if( rc ) return rc;
|
|
}
|
|
}
|
|
-#endif
|
|
-
|
|
- db->init.busy = 0;
|
|
- if( rc==SQLITE_OK && commit_internal ){
|
|
+ if( commit_internal ){
|
|
sqlite3CommitInternalChanges(db);
|
|
}
|
|
-
|
|
- return rc;
|
|
+ return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
@@ -116929,11 +123110,13 @@
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
if( !db->init.busy ){
|
|
rc = sqlite3Init(db, &pParse->zErrMsg);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ pParse->rc = rc;
|
|
+ pParse->nErr++;
|
|
+ }else if( db->noSharedCache ){
|
|
+ db->mDbFlags |= DBFLAG_SchemaKnownOk;
|
|
+ }
|
|
}
|
|
- if( rc!=SQLITE_OK ){
|
|
- pParse->rc = rc;
|
|
- pParse->nErr++;
|
|
- }
|
|
return rc;
|
|
}
|
|
|
|
@@ -116960,7 +123143,7 @@
|
|
** on the b-tree database, open one now. If a transaction is opened, it
|
|
** will be closed immediately after reading the meta-value. */
|
|
if( !sqlite3BtreeIsInReadTrans(pBt) ){
|
|
- rc = sqlite3BtreeBeginTrans(pBt, 0);
|
|
+ rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
|
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
|
sqlite3OomFault(db);
|
|
}
|
|
@@ -117007,7 +123190,8 @@
|
|
*/
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
if( pSchema ){
|
|
- for(i=0; ALWAYS(i<db->nDb); i++){
|
|
+ for(i=0; 1; i++){
|
|
+ assert( i<db->nDb );
|
|
if( db->aDb[i].pSchema==pSchema ){
|
|
break;
|
|
}
|
|
@@ -117021,16 +123205,14 @@
|
|
** Free all memory allocations in the pParse object
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
|
|
- if( pParse ){
|
|
- sqlite3 *db = pParse->db;
|
|
- sqlite3DbFree(db, pParse->aLabel);
|
|
- sqlite3ExprListDelete(db, pParse->pConstExpr);
|
|
- if( db ){
|
|
- assert( db->lookaside.bDisable >= pParse->disableLookaside );
|
|
- db->lookaside.bDisable -= pParse->disableLookaside;
|
|
- }
|
|
- pParse->disableLookaside = 0;
|
|
+ sqlite3 *db = pParse->db;
|
|
+ sqlite3DbFree(db, pParse->aLabel);
|
|
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
|
|
+ if( db ){
|
|
+ assert( db->lookaside.bDisable >= pParse->disableLookaside );
|
|
+ db->lookaside.bDisable -= pParse->disableLookaside;
|
|
}
|
|
+ pParse->disableLookaside = 0;
|
|
}
|
|
|
|
/*
|
|
@@ -117144,7 +123326,7 @@
|
|
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
|
|
static const char * const azColName[] = {
|
|
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
|
|
- "selectid", "order", "from", "detail"
|
|
+ "id", "parent", "notused", "detail"
|
|
};
|
|
int iFirst, mx;
|
|
if( sParse.explain==2 ){
|
|
@@ -117190,8 +123372,6 @@
|
|
end_prepare:
|
|
|
|
sqlite3ParserReset(&sParse);
|
|
- rc = sqlite3ApiExit(db, rc);
|
|
- assert( (rc&db->errMask)==rc );
|
|
return rc;
|
|
}
|
|
static int sqlite3LockAndPrepare(
|
|
@@ -117204,6 +123384,7 @@
|
|
const char **pzTail /* OUT: End of parsed string */
|
|
){
|
|
int rc;
|
|
+ int cnt = 0;
|
|
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
|
|
@@ -117214,18 +123395,310 @@
|
|
}
|
|
sqlite3_mutex_enter(db->mutex);
|
|
sqlite3BtreeEnterAll(db);
|
|
- rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
|
|
- if( rc==SQLITE_SCHEMA ){
|
|
- sqlite3_finalize(*ppStmt);
|
|
+ do{
|
|
+ /* Make multiple attempts to compile the SQL, until it either succeeds
|
|
+ ** or encounters a permanent error. A schema problem after one schema
|
|
+ ** reset is considered a permanent error. */
|
|
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
|
|
- }
|
|
+ assert( rc==SQLITE_OK || *ppStmt==0 );
|
|
+ }while( rc==SQLITE_ERROR_RETRY
|
|
+ || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
|
|
sqlite3BtreeLeaveAll(db);
|
|
+ rc = sqlite3ApiExit(db, rc);
|
|
+ assert( (rc&db->errMask)==rc );
|
|
sqlite3_mutex_leave(db->mutex);
|
|
- assert( rc==SQLITE_OK || *ppStmt==0 );
|
|
return rc;
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
/*
|
|
+** Checks if the specified token is a table, column, or function name,
|
|
+** based on the databases associated with the statement being prepared.
|
|
+** If the function fails, zero is returned and pRc is filled with the
|
|
+** error code.
|
|
+*/
|
|
+static int shouldTreatAsIdentifier(
|
|
+ sqlite3 *db, /* Database handle. */
|
|
+ const char *zToken, /* Pointer to start of token to be checked */
|
|
+ int nToken, /* Length of token to be checked */
|
|
+ int *pRc /* Pointer to error code upon failure */
|
|
+){
|
|
+ int bFound = 0; /* Non-zero if token is an identifier name. */
|
|
+ int i, j; /* Database and column loop indexes. */
|
|
+ Schema *pSchema; /* Schema for current database. */
|
|
+ Hash *pHash; /* Hash table of tables for current database. */
|
|
+ HashElem *e; /* Hash element for hash table iteration. */
|
|
+ Table *pTab; /* Database table for columns being checked. */
|
|
+
|
|
+ if( sqlite3IsRowidN(zToken, nToken) ){
|
|
+ return 1;
|
|
+ }
|
|
+ if( nToken>0 ){
|
|
+ int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken);
|
|
+ if( sqlite3FunctionSearchN(hash, zToken, nToken) ) return 1;
|
|
+ }
|
|
+ assert( db!=0 );
|
|
+ sqlite3_mutex_enter(db->mutex);
|
|
+ sqlite3BtreeEnterAll(db);
|
|
+ for(i=0; i<db->nDb; i++){
|
|
+ pHash = &db->aFunc;
|
|
+ if( sqlite3HashFindN(pHash, zToken, nToken) ){
|
|
+ bFound = 1;
|
|
+ break;
|
|
+ }
|
|
+ pSchema = db->aDb[i].pSchema;
|
|
+ if( pSchema==0 ) continue;
|
|
+ pHash = &pSchema->tblHash;
|
|
+ if( sqlite3HashFindN(pHash, zToken, nToken) ){
|
|
+ bFound = 1;
|
|
+ break;
|
|
+ }
|
|
+ for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){
|
|
+ pTab = sqliteHashData(e);
|
|
+ if( pTab==0 ) continue;
|
|
+ pHash = pTab->pColHash;
|
|
+ if( pHash==0 ){
|
|
+ pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash));
|
|
+ if( pHash ){
|
|
+ sqlite3HashInit(pHash);
|
|
+ for(j=0; j<pTab->nCol; j++){
|
|
+ Column *pCol = &pTab->aCol[j];
|
|
+ sqlite3HashInsert(pHash, pCol->zName, pCol);
|
|
+ }
|
|
+ }else{
|
|
+ *pRc = SQLITE_NOMEM_BKPT;
|
|
+ bFound = 0;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ if( pHash && sqlite3HashFindN(pHash, zToken, nToken) ){
|
|
+ bFound = 1;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+done:
|
|
+ sqlite3BtreeLeaveAll(db);
|
|
+ sqlite3_mutex_leave(db->mutex);
|
|
+ return bFound;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Attempt to estimate the final output buffer size needed for the fully
|
|
+** normalized version of the specified SQL string. This should take into
|
|
+** account any potential expansion that could occur (e.g. via IN clauses
|
|
+** being expanded, etc). This size returned is the total number of bytes
|
|
+** including the NUL terminator.
|
|
+*/
|
|
+static int estimateNormalizedSize(
|
|
+ const char *zSql, /* The original SQL string */
|
|
+ int nSql, /* Length of original SQL string */
|
|
+ u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */
|
|
+){
|
|
+ int nOut = nSql + 4;
|
|
+ const char *z = zSql;
|
|
+ while( nOut<nSql*5 ){
|
|
+ while( z[0]!=0 && z[0]!='I' && z[0]!='i' ){ z++; }
|
|
+ if( z[0]==0 ) break;
|
|
+ z++;
|
|
+ if( z[0]!='N' && z[0]!='n' ) break;
|
|
+ z++;
|
|
+ while( sqlite3Isspace(z[0]) ){ z++; }
|
|
+ if( z[0]!='(' ) break;
|
|
+ z++;
|
|
+ nOut += 5; /* ?,?,? */
|
|
+ }
|
|
+ return nOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Copy the current token into the output buffer while dealing with quoted
|
|
+** identifiers. By default, all letters will be converted into lowercase.
|
|
+** If the bUpper flag is set, uppercase will be used. The piOut argument
|
|
+** will be used to update the target index into the output string.
|
|
+*/
|
|
+static void copyNormalizedToken(
|
|
+ const char *zSql, /* The original SQL string */
|
|
+ int iIn, /* Current index into the original SQL string */
|
|
+ int nToken, /* Number of bytes in the current token */
|
|
+ int tokenFlags, /* Flags returned by the tokenizer */
|
|
+ char *zOut, /* The output string */
|
|
+ int *piOut /* Pointer to target index into the output string */
|
|
+){
|
|
+ int bQuoted = tokenFlags & SQLITE_TOKEN_QUOTED;
|
|
+ int bKeyword = tokenFlags & SQLITE_TOKEN_KEYWORD;
|
|
+ int j = *piOut, k = 0;
|
|
+ for(; k<nToken; k++){
|
|
+ if( bQuoted ){
|
|
+ if( k==0 && iIn>0 ){
|
|
+ zOut[j++] = '"';
|
|
+ continue;
|
|
+ }else if( k==nToken-1 ){
|
|
+ zOut[j++] = '"';
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if( bKeyword ){
|
|
+ zOut[j++] = sqlite3Toupper(zSql[iIn+k]);
|
|
+ }else{
|
|
+ zOut[j++] = sqlite3Tolower(zSql[iIn+k]);
|
|
+ }
|
|
+ }
|
|
+ *piOut = j;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Perform normalization of the SQL contained in the prepared statement and
|
|
+** store the result in the zNormSql field. The schema for the associated
|
|
+** databases are consulted while performing the normalization in order to
|
|
+** determine if a token appears to be an identifier. All identifiers are
|
|
+** left intact in the normalized SQL and all literals are replaced with a
|
|
+** single '?'.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3Normalize(
|
|
+ Vdbe *pVdbe, /* VM being reprepared */
|
|
+ const char *zSql, /* The original SQL string */
|
|
+ int nSql, /* Size of the input string in bytes */
|
|
+ u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */
|
|
+){
|
|
+ sqlite3 *db; /* Database handle. */
|
|
+ char *z; /* The output string */
|
|
+ int nZ; /* Size of the output string in bytes */
|
|
+ int i; /* Next character to read from zSql[] */
|
|
+ int j; /* Next character to fill in on z[] */
|
|
+ int tokenType = 0; /* Type of the next token */
|
|
+ int prevTokenType = 0; /* Type of the previous token, except spaces */
|
|
+ int n; /* Size of the next token */
|
|
+ int nParen = 0; /* Nesting level of parenthesis */
|
|
+ Hash inHash; /* Table of parenthesis levels to output index. */
|
|
+
|
|
+ db = sqlite3VdbeDb(pVdbe);
|
|
+ assert( db!=0 );
|
|
+ assert( pVdbe->zNormSql==0 );
|
|
+ if( zSql==0 ) return;
|
|
+ nZ = estimateNormalizedSize(zSql, nSql, prepFlags);
|
|
+ z = sqlite3DbMallocRawNN(db, nZ);
|
|
+ if( z==0 ) return;
|
|
+ sqlite3HashInit(&inHash);
|
|
+ for(i=j=0; i<nSql && zSql[i]; i+=n){
|
|
+ int flags = 0;
|
|
+ if( tokenType!=TK_SPACE ) prevTokenType = tokenType;
|
|
+ n = sqlite3GetTokenNormalized((unsigned char*)zSql+i, &tokenType, &flags);
|
|
+ switch( tokenType ){
|
|
+ case TK_SPACE: {
|
|
+ break;
|
|
+ }
|
|
+ case TK_ILLEGAL: {
|
|
+ sqlite3DbFree(db, z);
|
|
+ sqlite3HashClear(&inHash);
|
|
+ return;
|
|
+ }
|
|
+ case TK_STRING:
|
|
+ case TK_INTEGER:
|
|
+ case TK_FLOAT:
|
|
+ case TK_VARIABLE:
|
|
+ case TK_BLOB: {
|
|
+ z[j++] = '?';
|
|
+ break;
|
|
+ }
|
|
+ case TK_LP:
|
|
+ case TK_RP: {
|
|
+ if( tokenType==TK_LP ){
|
|
+ nParen++;
|
|
+ if( prevTokenType==TK_IN ){
|
|
+ assert( nParen<nSql );
|
|
+ sqlite3HashInsert(&inHash, zSql+nParen, SQLITE_INT_TO_PTR(j));
|
|
+ }
|
|
+ }else{
|
|
+ int jj;
|
|
+ assert( nParen<nSql );
|
|
+ jj = SQLITE_PTR_TO_INT(sqlite3HashFind(&inHash, zSql+nParen));
|
|
+ if( jj>0 ){
|
|
+ sqlite3HashInsert(&inHash, zSql+nParen, 0);
|
|
+ assert( jj+6<nZ );
|
|
+ memcpy(z+jj+1, "?,?,?", 5);
|
|
+ j = jj+6;
|
|
+ assert( nZ-1-j>=0 );
|
|
+ assert( nZ-1-j<nZ );
|
|
+ memset(z+j, 0, nZ-1-j);
|
|
+ }
|
|
+ nParen--;
|
|
+ }
|
|
+ assert( nParen>=0 );
|
|
+ /* Fall through */
|
|
+ }
|
|
+ case TK_MINUS:
|
|
+ case TK_SEMI:
|
|
+ case TK_PLUS:
|
|
+ case TK_STAR:
|
|
+ case TK_SLASH:
|
|
+ case TK_REM:
|
|
+ case TK_EQ:
|
|
+ case TK_LE:
|
|
+ case TK_NE:
|
|
+ case TK_LSHIFT:
|
|
+ case TK_LT:
|
|
+ case TK_RSHIFT:
|
|
+ case TK_GT:
|
|
+ case TK_GE:
|
|
+ case TK_BITOR:
|
|
+ case TK_CONCAT:
|
|
+ case TK_COMMA:
|
|
+ case TK_BITAND:
|
|
+ case TK_BITNOT:
|
|
+ case TK_DOT:
|
|
+ case TK_IN:
|
|
+ case TK_IS:
|
|
+ case TK_NOT:
|
|
+ case TK_NULL:
|
|
+ case TK_ID: {
|
|
+ if( tokenType==TK_NULL ){
|
|
+ if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){
|
|
+ /* NULL is a keyword in this case, not a literal value */
|
|
+ }else{
|
|
+ /* Here the NULL is a literal value */
|
|
+ z[j++] = '?';
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){
|
|
+ z[j++] = ' ';
|
|
+ }
|
|
+ if( tokenType==TK_ID ){
|
|
+ int i2 = i, n2 = n, rc = SQLITE_OK;
|
|
+ if( nParen>0 ){
|
|
+ assert( nParen<nSql );
|
|
+ sqlite3HashInsert(&inHash, zSql+nParen, 0);
|
|
+ }
|
|
+ if( flags&SQLITE_TOKEN_QUOTED ){ i2++; n2-=2; }
|
|
+ if( shouldTreatAsIdentifier(db, zSql+i2, n2, &rc)==0 ){
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3DbFree(db, z);
|
|
+ sqlite3HashClear(&inHash);
|
|
+ return;
|
|
+ }
|
|
+ if( sqlite3_keyword_check(zSql+i2, n2)==0 ){
|
|
+ z[j++] = '?';
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ copyNormalizedToken(zSql, i, n, flags, z, &j);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ assert( j<nZ && "one" );
|
|
+ while( j>0 && z[j-1]==' ' ){ j--; }
|
|
+ if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; }
|
|
+ z[j] = 0;
|
|
+ assert( j<nZ && "two" );
|
|
+ pVdbe->zNormSql = z;
|
|
+ sqlite3HashClear(&inHash);
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
+
|
|
+/*
|
|
** Rerun the compilation of a statement after a schema change.
|
|
**
|
|
** If the statement is successfully recompiled, return SQLITE_OK. Otherwise,
|
|
@@ -117455,8 +123928,7 @@
|
|
/***/ int sqlite3SelectTrace = 0;
|
|
# define SELECTTRACE(K,P,S,X) \
|
|
if(sqlite3SelectTrace&(K)) \
|
|
- sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\
|
|
- (S)->zSelName,(S)),\
|
|
+ sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
|
|
sqlite3DebugPrintf X
|
|
#else
|
|
# define SELECTTRACE(K,P,S,X)
|
|
@@ -117479,6 +123951,20 @@
|
|
/*
|
|
** An instance of the following object is used to record information about
|
|
** the ORDER BY (or GROUP BY) clause of query is being coded.
|
|
+**
|
|
+** The aDefer[] array is used by the sorter-references optimization. For
|
|
+** example, assuming there is no index that can be used for the ORDER BY,
|
|
+** for the query:
|
|
+**
|
|
+** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10;
|
|
+**
|
|
+** it may be more efficient to add just the "a" values to the sorter, and
|
|
+** retrieve the associated "bigblob" values directly from table t1 as the
|
|
+** 10 smallest "a" values are extracted from the sorter.
|
|
+**
|
|
+** When the sorter-reference optimization is used, there is one entry in the
|
|
+** aDefer[] array for each database table that may be read as values are
|
|
+** extracted from the sorter.
|
|
*/
|
|
typedef struct SortCtx SortCtx;
|
|
struct SortCtx {
|
|
@@ -117489,8 +123975,17 @@
|
|
int labelBkOut; /* Start label for the block-output subroutine */
|
|
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
|
|
int labelDone; /* Jump here when done, ex: LIMIT reached */
|
|
+ int labelOBLopt; /* Jump here when sorter is full */
|
|
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
|
|
- u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ u8 nDefer; /* Number of valid entries in aDefer[] */
|
|
+ struct DeferredCsr {
|
|
+ Table *pTab; /* Table definition */
|
|
+ int iCsr; /* Cursor number for table */
|
|
+ int nKey; /* Number of PK columns for table pTab (>=1) */
|
|
+ } aDefer[4];
|
|
+#endif
|
|
+ struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */
|
|
};
|
|
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
|
|
|
|
@@ -117508,8 +124003,12 @@
|
|
sqlite3ExprDelete(db, p->pHaving);
|
|
sqlite3ExprListDelete(db, p->pOrderBy);
|
|
sqlite3ExprDelete(db, p->pLimit);
|
|
- sqlite3ExprDelete(db, p->pOffset);
|
|
- if( p->pWith ) sqlite3WithDelete(db, p->pWith);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
|
|
+ sqlite3WindowListDelete(db, p->pWinDefn);
|
|
+ }
|
|
+#endif
|
|
+ if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
|
|
if( bFree ) sqlite3DbFreeNN(db, p);
|
|
p = pPrior;
|
|
bFree = 1;
|
|
@@ -117541,8 +124040,7 @@
|
|
Expr *pHaving, /* the HAVING clause */
|
|
ExprList *pOrderBy, /* the ORDER BY clause */
|
|
u32 selFlags, /* Flag parameters, such as SF_Distinct */
|
|
- Expr *pLimit, /* LIMIT value. NULL means not used */
|
|
- Expr *pOffset /* OFFSET value. NULL means no offset */
|
|
+ Expr *pLimit /* LIMIT value. NULL means not used */
|
|
){
|
|
Select *pNew;
|
|
Select standin;
|
|
@@ -117552,7 +124050,8 @@
|
|
pNew = &standin;
|
|
}
|
|
if( pEList==0 ){
|
|
- pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(pParse->db,TK_ASTERISK,0));
|
|
+ pEList = sqlite3ExprListAppend(pParse, 0,
|
|
+ sqlite3Expr(pParse->db,TK_ASTERISK,0));
|
|
}
|
|
pNew->pEList = pEList;
|
|
pNew->op = TK_SELECT;
|
|
@@ -117559,9 +124058,7 @@
|
|
pNew->selFlags = selFlags;
|
|
pNew->iLimit = 0;
|
|
pNew->iOffset = 0;
|
|
-#if SELECTTRACE_ENABLED
|
|
- pNew->zSelName[0] = 0;
|
|
-#endif
|
|
+ pNew->selId = ++pParse->nSelect;
|
|
pNew->addrOpenEphm[0] = -1;
|
|
pNew->addrOpenEphm[1] = -1;
|
|
pNew->nSelectRow = 0;
|
|
@@ -117574,9 +124071,11 @@
|
|
pNew->pPrior = 0;
|
|
pNew->pNext = 0;
|
|
pNew->pLimit = pLimit;
|
|
- pNew->pOffset = pOffset;
|
|
pNew->pWith = 0;
|
|
- assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || pParse->db->mallocFailed!=0 );
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ pNew->pWin = 0;
|
|
+ pNew->pWinDefn = 0;
|
|
+#endif
|
|
if( pParse->db->mallocFailed ) {
|
|
clearSelect(pParse->db, pNew, pNew!=&standin);
|
|
pNew = 0;
|
|
@@ -117587,23 +124086,12 @@
|
|
return pNew;
|
|
}
|
|
|
|
-#if SELECTTRACE_ENABLED
|
|
-/*
|
|
-** Set the name of a Select object
|
|
-*/
|
|
-SQLITE_PRIVATE void sqlite3SelectSetName(Select *p, const char *zName){
|
|
- if( p && zName ){
|
|
- sqlite3_snprintf(sizeof(p->zSelName), p->zSelName, "%s", zName);
|
|
- }
|
|
-}
|
|
-#endif
|
|
|
|
-
|
|
/*
|
|
** Delete the given Select structure and all of its substructures.
|
|
*/
|
|
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
|
|
- if( p ) clearSelect(db, p, 1);
|
|
+ if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
|
|
}
|
|
|
|
/*
|
|
@@ -117820,6 +124308,29 @@
|
|
}
|
|
}
|
|
|
|
+/* Undo the work of setJoinExpr(). In the expression tree p, convert every
|
|
+** term that is marked with EP_FromJoin and iRightJoinTable==iTable into
|
|
+** an ordinary term that omits the EP_FromJoin mark.
|
|
+**
|
|
+** This happens when a LEFT JOIN is simplified into an ordinary JOIN.
|
|
+*/
|
|
+static void unsetJoinExpr(Expr *p, int iTable){
|
|
+ while( p ){
|
|
+ if( ExprHasProperty(p, EP_FromJoin)
|
|
+ && (iTable<0 || p->iRightJoinTable==iTable) ){
|
|
+ ExprClearProperty(p, EP_FromJoin);
|
|
+ }
|
|
+ if( p->op==TK_FUNCTION && p->x.pList ){
|
|
+ int i;
|
|
+ for(i=0; i<p->x.pList->nExpr; i++){
|
|
+ unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
|
|
+ }
|
|
+ }
|
|
+ unsetJoinExpr(p->pLeft, iTable);
|
|
+ p = p->pRight;
|
|
+ }
|
|
+}
|
|
+
|
|
/*
|
|
** This routine processes the join information for a SELECT statement.
|
|
** ON and USING clauses are converted into extra terms of the WHERE clause.
|
|
@@ -117844,11 +124355,10 @@
|
|
pLeft = &pSrc->a[0];
|
|
pRight = &pLeft[1];
|
|
for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
|
|
- Table *pLeftTab = pLeft->pTab;
|
|
Table *pRightTab = pRight->pTab;
|
|
int isOuter;
|
|
|
|
- if( NEVER(pLeftTab==0 || pRightTab==0) ) continue;
|
|
+ if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue;
|
|
isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
|
|
|
|
/* When the NATURAL keyword is present, add WHERE clause terms for
|
|
@@ -117922,15 +124432,63 @@
|
|
return 0;
|
|
}
|
|
|
|
-/* Forward reference */
|
|
-static KeyInfo *keyInfoFromExprList(
|
|
- Parse *pParse, /* Parsing context */
|
|
- ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
|
- int iStart, /* Begin with this column of pList */
|
|
- int nExtra /* Add this many extra columns to the end */
|
|
-);
|
|
+/*
|
|
+** An instance of this object holds information (beyond pParse and pSelect)
|
|
+** needed to load the next result row that is to be added to the sorter.
|
|
+*/
|
|
+typedef struct RowLoadInfo RowLoadInfo;
|
|
+struct RowLoadInfo {
|
|
+ int regResult; /* Store results in array of registers here */
|
|
+ u8 ecelFlags; /* Flag argument to ExprCodeExprList() */
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ ExprList *pExtra; /* Extra columns needed by sorter refs */
|
|
+ int regExtraResult; /* Where to load the extra columns */
|
|
+#endif
|
|
+};
|
|
|
|
/*
|
|
+** This routine does the work of loading query data into an array of
|
|
+** registers so that it can be added to the sorter.
|
|
+*/
|
|
+static void innerLoopLoadRow(
|
|
+ Parse *pParse, /* Statement under construction */
|
|
+ Select *pSelect, /* The query being coded */
|
|
+ RowLoadInfo *pInfo /* Info needed to complete the row load */
|
|
+){
|
|
+ sqlite3ExprCodeExprList(pParse, pSelect->pEList, pInfo->regResult,
|
|
+ 0, pInfo->ecelFlags);
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( pInfo->pExtra ){
|
|
+ sqlite3ExprCodeExprList(pParse, pInfo->pExtra, pInfo->regExtraResult, 0, 0);
|
|
+ sqlite3ExprListDelete(pParse->db, pInfo->pExtra);
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** Code the OP_MakeRecord instruction that generates the entry to be
|
|
+** added into the sorter.
|
|
+**
|
|
+** Return the register in which the result is stored.
|
|
+*/
|
|
+static int makeSorterRecord(
|
|
+ Parse *pParse,
|
|
+ SortCtx *pSort,
|
|
+ Select *pSelect,
|
|
+ int regBase,
|
|
+ int nBase
|
|
+){
|
|
+ int nOBSat = pSort->nOBSat;
|
|
+ Vdbe *v = pParse->pVdbe;
|
|
+ int regOut = ++pParse->nMem;
|
|
+ if( pSort->pDeferredRowLoad ){
|
|
+ innerLoopLoadRow(pParse, pSelect, pSort->pDeferredRowLoad);
|
|
+ }
|
|
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regOut);
|
|
+ return regOut;
|
|
+}
|
|
+
|
|
+/*
|
|
** Generate code that will push the record in registers regData
|
|
** through regData+nData-1 onto the sorter.
|
|
*/
|
|
@@ -117940,7 +124498,7 @@
|
|
Select *pSelect, /* The whole SELECT statement */
|
|
int regData, /* First register holding data to be sorted */
|
|
int regOrigData, /* First register holding data before packing */
|
|
- int nData, /* Number of elements in the data array */
|
|
+ int nData, /* Number of elements in the regData data array */
|
|
int nPrefixReg /* No. of reg prior to regData available for use */
|
|
){
|
|
Vdbe *v = pParse->pVdbe; /* Stmt under construction */
|
|
@@ -117948,16 +124506,32 @@
|
|
int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
|
|
int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
|
|
int regBase; /* Regs for sorter record */
|
|
- int regRecord = ++pParse->nMem; /* Assembled sorter record */
|
|
+ int regRecord = 0; /* Assembled sorter record */
|
|
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
|
|
int op; /* Opcode to add sorter record to sorter */
|
|
int iLimit; /* LIMIT counter */
|
|
+ int iSkip = 0; /* End of the sorter insert loop */
|
|
|
|
assert( bSeq==0 || bSeq==1 );
|
|
+
|
|
+ /* Three cases:
|
|
+ ** (1) The data to be sorted has already been packed into a Record
|
|
+ ** by a prior OP_MakeRecord. In this case nData==1 and regData
|
|
+ ** will be completely unrelated to regOrigData.
|
|
+ ** (2) All output columns are included in the sort record. In that
|
|
+ ** case regData==regOrigData.
|
|
+ ** (3) Some output columns are omitted from the sort record due to
|
|
+ ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the
|
|
+ ** SQLITE_ECEL_OMITREF optimization, or due to the
|
|
+ ** SortCtx.pDeferredRowLoad optimiation. In any of these cases
|
|
+ ** regOrigData is 0 to prevent this routine from trying to copy
|
|
+ ** values that might not yet exist.
|
|
+ */
|
|
assert( nData==1 || regData==regOrigData || regOrigData==0 );
|
|
+
|
|
if( nPrefixReg ){
|
|
assert( nPrefixReg==nExpr+bSeq );
|
|
- regBase = regData - nExpr - bSeq;
|
|
+ regBase = regData - nPrefixReg;
|
|
}else{
|
|
regBase = pParse->nMem + 1;
|
|
pParse->nMem += nBase;
|
|
@@ -117973,7 +124547,6 @@
|
|
if( nPrefixReg==0 && nData>0 ){
|
|
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData);
|
|
}
|
|
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
|
|
if( nOBSat>0 ){
|
|
int regPrevKey; /* The first nOBSat columns of the previous row */
|
|
int addrFirst; /* Address of the OP_IfNot opcode */
|
|
@@ -117982,6 +124555,7 @@
|
|
int nKey; /* Number of sorting key columns, including OP_Sequence */
|
|
KeyInfo *pKI; /* Original KeyInfo on the sorter table */
|
|
|
|
+ regRecord = makeSorterRecord(pParse, pSort, pSelect, regBase, nBase);
|
|
regPrevKey = pParse->nMem+1;
|
|
pParse->nMem += pSort->nOBSat;
|
|
nKey = nExpr - pSort->nOBSat + bSeq;
|
|
@@ -117996,11 +124570,11 @@
|
|
if( pParse->db->mallocFailed ) return;
|
|
pOp->p2 = nKey + nData;
|
|
pKI = pOp->p4.pKeyInfo;
|
|
- memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
|
|
+ memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
|
|
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
|
- testcase( pKI->nXField>2 );
|
|
- pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
|
|
- pKI->nXField-1);
|
|
+ testcase( pKI->nAllField > pKI->nKeyField+2 );
|
|
+ pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
|
|
+ pKI->nAllField-pKI->nKeyField-1);
|
|
addrJmp = sqlite3VdbeCurrentAddr(v);
|
|
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
|
|
pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
|
|
@@ -118015,6 +124589,34 @@
|
|
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
|
|
sqlite3VdbeJumpHere(v, addrJmp);
|
|
}
|
|
+ if( iLimit ){
|
|
+ /* At this point the values for the new sorter entry are stored
|
|
+ ** in an array of registers. They need to be composed into a record
|
|
+ ** and inserted into the sorter if either (a) there are currently
|
|
+ ** less than LIMIT+OFFSET items or (b) the new record is smaller than
|
|
+ ** the largest record currently in the sorter. If (b) is true and there
|
|
+ ** are already LIMIT+OFFSET items in the sorter, delete the largest
|
|
+ ** entry before inserting the new one. This way there are never more
|
|
+ ** than LIMIT+OFFSET items in the sorter.
|
|
+ **
|
|
+ ** If the new record does not need to be inserted into the sorter,
|
|
+ ** jump to the next iteration of the loop. If the pSort->labelOBLopt
|
|
+ ** value is not zero, then it is a label of where to jump. Otherwise,
|
|
+ ** just bypass the row insert logic. See the header comment on the
|
|
+ ** sqlite3WhereOrderByLimitOptLabel() function for additional info.
|
|
+ */
|
|
+ int iCsr = pSort->iECursor;
|
|
+ sqlite3VdbeAddOp2(v, OP_IfNotZero, iLimit, sqlite3VdbeCurrentAddr(v)+4);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Last, iCsr, 0);
|
|
+ iSkip = sqlite3VdbeAddOp4Int(v, OP_IdxLE,
|
|
+ iCsr, 0, regBase+nOBSat, nExpr-nOBSat);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp1(v, OP_Delete, iCsr);
|
|
+ }
|
|
+ if( regRecord==0 ){
|
|
+ regRecord = makeSorterRecord(pParse, pSort, pSelect, regBase, nBase);
|
|
+ }
|
|
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
|
op = OP_SorterInsert;
|
|
}else{
|
|
@@ -118022,33 +124624,9 @@
|
|
}
|
|
sqlite3VdbeAddOp4Int(v, op, pSort->iECursor, regRecord,
|
|
regBase+nOBSat, nBase-nOBSat);
|
|
- if( iLimit ){
|
|
- int addr;
|
|
- int r1 = 0;
|
|
- /* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit
|
|
- ** register is initialized with value of LIMIT+OFFSET.) After the sorter
|
|
- ** fills up, delete the least entry in the sorter after each insert.
|
|
- ** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */
|
|
- addr = sqlite3VdbeAddOp1(v, OP_IfNotZero, iLimit); VdbeCoverage(v);
|
|
- sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
|
|
- if( pSort->bOrderedInnerLoop ){
|
|
- r1 = ++pParse->nMem;
|
|
- sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1);
|
|
- VdbeComment((v, "seq"));
|
|
- }
|
|
- sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
|
|
- if( pSort->bOrderedInnerLoop ){
|
|
- /* If the inner loop is driven by an index such that values from
|
|
- ** the same iteration of the inner loop are in sorted order, then
|
|
- ** immediately jump to the next iteration of an inner loop if the
|
|
- ** entry from the current iteration does not fit into the top
|
|
- ** LIMIT+OFFSET entries of the sorter. */
|
|
- int iBrk = sqlite3VdbeCurrentAddr(v) + 2;
|
|
- sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1);
|
|
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
|
- VdbeCoverage(v);
|
|
- }
|
|
- sqlite3VdbeJumpHere(v, addr);
|
|
+ if( iSkip ){
|
|
+ sqlite3VdbeChangeP2(v, iSkip,
|
|
+ pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v));
|
|
}
|
|
}
|
|
|
|
@@ -118094,20 +124672,100 @@
|
|
sqlite3ReleaseTempReg(pParse, r1);
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
/*
|
|
+** This function is called as part of inner-loop generation for a SELECT
|
|
+** statement with an ORDER BY that is not optimized by an index. It
|
|
+** determines the expressions, if any, that the sorter-reference
|
|
+** optimization should be used for. The sorter-reference optimization
|
|
+** is used for SELECT queries like:
|
|
+**
|
|
+** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10
|
|
+**
|
|
+** If the optimization is used for expression "bigblob", then instead of
|
|
+** storing values read from that column in the sorter records, the PK of
|
|
+** the row from table t1 is stored instead. Then, as records are extracted from
|
|
+** the sorter to return to the user, the required value of bigblob is
|
|
+** retrieved directly from table t1. If the values are very large, this
|
|
+** can be more efficient than storing them directly in the sorter records.
|
|
+**
|
|
+** The ExprList_item.bSorterRef flag is set for each expression in pEList
|
|
+** for which the sorter-reference optimization should be enabled.
|
|
+** Additionally, the pSort->aDefer[] array is populated with entries
|
|
+** for all cursors required to evaluate all selected expressions. Finally.
|
|
+** output variable (*ppExtra) is set to an expression list containing
|
|
+** expressions for all extra PK values that should be stored in the
|
|
+** sorter records.
|
|
+*/
|
|
+static void selectExprDefer(
|
|
+ Parse *pParse, /* Leave any error here */
|
|
+ SortCtx *pSort, /* Sorter context */
|
|
+ ExprList *pEList, /* Expressions destined for sorter */
|
|
+ ExprList **ppExtra /* Expressions to append to sorter record */
|
|
+){
|
|
+ int i;
|
|
+ int nDefer = 0;
|
|
+ ExprList *pExtra = 0;
|
|
+ for(i=0; i<pEList->nExpr; i++){
|
|
+ struct ExprList_item *pItem = &pEList->a[i];
|
|
+ if( pItem->u.x.iOrderByCol==0 ){
|
|
+ Expr *pExpr = pItem->pExpr;
|
|
+ Table *pTab = pExpr->y.pTab;
|
|
+ if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab)
|
|
+ && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)
|
|
+ ){
|
|
+ int j;
|
|
+ for(j=0; j<nDefer; j++){
|
|
+ if( pSort->aDefer[j].iCsr==pExpr->iTable ) break;
|
|
+ }
|
|
+ if( j==nDefer ){
|
|
+ if( nDefer==ArraySize(pSort->aDefer) ){
|
|
+ continue;
|
|
+ }else{
|
|
+ int nKey = 1;
|
|
+ int k;
|
|
+ Index *pPk = 0;
|
|
+ if( !HasRowid(pTab) ){
|
|
+ pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
+ nKey = pPk->nKeyCol;
|
|
+ }
|
|
+ for(k=0; k<nKey; k++){
|
|
+ Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0);
|
|
+ if( pNew ){
|
|
+ pNew->iTable = pExpr->iTable;
|
|
+ pNew->y.pTab = pExpr->y.pTab;
|
|
+ pNew->iColumn = pPk ? pPk->aiColumn[k] : -1;
|
|
+ pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew);
|
|
+ }
|
|
+ }
|
|
+ pSort->aDefer[nDefer].pTab = pExpr->y.pTab;
|
|
+ pSort->aDefer[nDefer].iCsr = pExpr->iTable;
|
|
+ pSort->aDefer[nDefer].nKey = nKey;
|
|
+ nDefer++;
|
|
+ }
|
|
+ }
|
|
+ pItem->bSorterRef = 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ pSort->nDefer = (u8)nDefer;
|
|
+ *ppExtra = pExtra;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** This routine generates the code for the inside of the inner loop
|
|
** of a SELECT.
|
|
**
|
|
-** If srcTab is negative, then the pEList expressions
|
|
+** If srcTab is negative, then the p->pEList expressions
|
|
** are evaluated in order to get the data for this row. If srcTab is
|
|
-** zero or more, then data is pulled from srcTab and pEList is used only
|
|
+** zero or more, then data is pulled from srcTab and p->pEList is used only
|
|
** to get the number of columns and the collation sequence for each column.
|
|
*/
|
|
static void selectInnerLoop(
|
|
Parse *pParse, /* The parser context */
|
|
Select *p, /* The complete select statement being coded */
|
|
- ExprList *pEList, /* List of values being extracted */
|
|
- int srcTab, /* Pull data from this table */
|
|
+ int srcTab, /* Pull data from this table if non-negative */
|
|
SortCtx *pSort, /* If not NULL, info on how to process ORDER BY */
|
|
DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */
|
|
SelectDest *pDest, /* How to dispose of the results */
|
|
@@ -118121,6 +124779,7 @@
|
|
int iParm = pDest->iSDParm; /* First argument to disposal method */
|
|
int nResultCol; /* Number of result columns */
|
|
int nPrefixReg = 0; /* Number of extra registers before regResult */
|
|
+ RowLoadInfo sRowLoadInfo; /* Info for deferred row loading */
|
|
|
|
/* Usually, regResult is the first cell in an array of memory cells
|
|
** containing the current result row. In this case regOrig is set to the
|
|
@@ -118131,7 +124790,7 @@
|
|
int regOrig; /* Start of memory holding full result (or 0) */
|
|
|
|
assert( v );
|
|
- assert( pEList!=0 );
|
|
+ assert( p->pEList!=0 );
|
|
hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP;
|
|
if( pSort && pSort->pOrderBy==0 ) pSort = 0;
|
|
if( pSort==0 && !hasDistinct ){
|
|
@@ -118141,7 +124800,7 @@
|
|
|
|
/* Pull the requested columns.
|
|
*/
|
|
- nResultCol = pEList->nExpr;
|
|
+ nResultCol = p->pEList->nExpr;
|
|
|
|
if( pDest->iSdst==0 ){
|
|
if( pSort ){
|
|
@@ -118164,13 +124823,17 @@
|
|
if( srcTab>=0 ){
|
|
for(i=0; i<nResultCol; i++){
|
|
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
|
|
- VdbeComment((v, "%s", pEList->a[i].zName));
|
|
+ VdbeComment((v, "%s", p->pEList->a[i].zName));
|
|
}
|
|
}else if( eDest!=SRT_Exists ){
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ ExprList *pExtra = 0;
|
|
+#endif
|
|
/* If the destination is an EXISTS(...) expression, the actual
|
|
** values returned by the SELECT are not required.
|
|
*/
|
|
- u8 ecelFlags;
|
|
+ u8 ecelFlags; /* "ecel" is an abbreviation of "ExprCodeExprList" */
|
|
+ ExprList *pEList;
|
|
if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){
|
|
ecelFlags = SQLITE_ECEL_DUP;
|
|
}else{
|
|
@@ -118177,24 +124840,75 @@
|
|
ecelFlags = 0;
|
|
}
|
|
if( pSort && hasDistinct==0 && eDest!=SRT_EphemTab && eDest!=SRT_Table ){
|
|
- /* For each expression in pEList that is a copy of an expression in
|
|
+ /* For each expression in p->pEList that is a copy of an expression in
|
|
** the ORDER BY clause (pSort->pOrderBy), set the associated
|
|
** iOrderByCol value to one more than the index of the ORDER BY
|
|
** expression within the sort-key that pushOntoSorter() will generate.
|
|
- ** This allows the pEList field to be omitted from the sorted record,
|
|
+ ** This allows the p->pEList field to be omitted from the sorted record,
|
|
** saving space and CPU cycles. */
|
|
ecelFlags |= (SQLITE_ECEL_OMITREF|SQLITE_ECEL_REF);
|
|
+
|
|
for(i=pSort->nOBSat; i<pSort->pOrderBy->nExpr; i++){
|
|
int j;
|
|
if( (j = pSort->pOrderBy->a[i].u.x.iOrderByCol)>0 ){
|
|
- pEList->a[j-1].u.x.iOrderByCol = i+1-pSort->nOBSat;
|
|
+ p->pEList->a[j-1].u.x.iOrderByCol = i+1-pSort->nOBSat;
|
|
}
|
|
}
|
|
- regOrig = 0;
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ selectExprDefer(pParse, pSort, p->pEList, &pExtra);
|
|
+ if( pExtra && pParse->db->mallocFailed==0 ){
|
|
+ /* If there are any extra PK columns to add to the sorter records,
|
|
+ ** allocate extra memory cells and adjust the OpenEphemeral
|
|
+ ** instruction to account for the larger records. This is only
|
|
+ ** required if there are one or more WITHOUT ROWID tables with
|
|
+ ** composite primary keys in the SortCtx.aDefer[] array. */
|
|
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
|
|
+ pOp->p2 += (pExtra->nExpr - pSort->nDefer);
|
|
+ pOp->p4.pKeyInfo->nAllField += (pExtra->nExpr - pSort->nDefer);
|
|
+ pParse->nMem += pExtra->nExpr;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* Adjust nResultCol to account for columns that are omitted
|
|
+ ** from the sorter by the optimizations in this branch */
|
|
+ pEList = p->pEList;
|
|
+ for(i=0; i<pEList->nExpr; i++){
|
|
+ if( pEList->a[i].u.x.iOrderByCol>0
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ || pEList->a[i].bSorterRef
|
|
+#endif
|
|
+ ){
|
|
+ nResultCol--;
|
|
+ regOrig = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ testcase( regOrig );
|
|
+ testcase( eDest==SRT_Set );
|
|
+ testcase( eDest==SRT_Mem );
|
|
+ testcase( eDest==SRT_Coroutine );
|
|
+ testcase( eDest==SRT_Output );
|
|
assert( eDest==SRT_Set || eDest==SRT_Mem
|
|
|| eDest==SRT_Coroutine || eDest==SRT_Output );
|
|
}
|
|
- nResultCol = sqlite3ExprCodeExprList(pParse,pEList,regResult,0,ecelFlags);
|
|
+ sRowLoadInfo.regResult = regResult;
|
|
+ sRowLoadInfo.ecelFlags = ecelFlags;
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ sRowLoadInfo.pExtra = pExtra;
|
|
+ sRowLoadInfo.regExtraResult = regResult + nResultCol;
|
|
+ if( pExtra ) nResultCol += pExtra->nExpr;
|
|
+#endif
|
|
+ if( p->iLimit
|
|
+ && (ecelFlags & SQLITE_ECEL_OMITREF)!=0
|
|
+ && nPrefixReg>0
|
|
+ ){
|
|
+ assert( pSort!=0 );
|
|
+ assert( hasDistinct==0 );
|
|
+ pSort->pDeferredRowLoad = &sRowLoadInfo;
|
|
+ regOrig = 0;
|
|
+ }else{
|
|
+ innerLoopLoadRow(pParse, p, &sRowLoadInfo);
|
|
+ }
|
|
}
|
|
|
|
/* If the DISTINCT keyword was present on the SELECT statement
|
|
@@ -118226,7 +124940,7 @@
|
|
|
|
iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
|
|
for(i=0; i<nResultCol; i++){
|
|
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[i].pExpr);
|
|
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, p->pEList->a[i].pExpr);
|
|
if( i<nResultCol-1 ){
|
|
sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i);
|
|
VdbeCoverage(v);
|
|
@@ -118310,7 +125024,8 @@
|
|
}
|
|
#endif
|
|
if( pSort ){
|
|
- pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg);
|
|
+ assert( regResult==regOrig );
|
|
+ pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, regOrig, 1, nPrefixReg);
|
|
}else{
|
|
int r2 = sqlite3GetTempReg(pParse);
|
|
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
|
|
@@ -118340,7 +125055,6 @@
|
|
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
|
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
|
|
r1, pDest->zAffSdst, nResultCol);
|
|
- sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
|
|
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol);
|
|
sqlite3ReleaseTempReg(pParse, r1);
|
|
}
|
|
@@ -118384,7 +125098,6 @@
|
|
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
|
}else{
|
|
sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol);
|
|
- sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
|
|
}
|
|
break;
|
|
}
|
|
@@ -118469,8 +125182,8 @@
|
|
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
|
|
if( p ){
|
|
p->aSortOrder = (u8*)&p->aColl[N+X];
|
|
- p->nField = (u16)N;
|
|
- p->nXField = (u16)X;
|
|
+ p->nKeyField = (u16)N;
|
|
+ p->nAllField = (u16)(N+X);
|
|
p->enc = ENC(db);
|
|
p->db = db;
|
|
p->nRef = 1;
|
|
@@ -118527,7 +125240,7 @@
|
|
** function is responsible for seeing that this structure is eventually
|
|
** freed.
|
|
*/
|
|
-static KeyInfo *keyInfoFromExprList(
|
|
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(
|
|
Parse *pParse, /* Parsing context */
|
|
ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
|
int iStart, /* Begin with this column of pList */
|
|
@@ -118544,10 +125257,7 @@
|
|
if( pInfo ){
|
|
assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
|
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
|
|
- CollSeq *pColl;
|
|
- pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
|
|
- if( !pColl ) pColl = db->pDfltColl;
|
|
- pInfo->aColl[i-iStart] = pColl;
|
|
+ pInfo->aColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr);
|
|
pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
|
|
}
|
|
}
|
|
@@ -118580,11 +125290,7 @@
|
|
** is determined by the zUsage argument.
|
|
*/
|
|
static void explainTempTable(Parse *pParse, const char *zUsage){
|
|
- if( pParse->explain==2 ){
|
|
- Vdbe *v = pParse->pVdbe;
|
|
- char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage);
|
|
- sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
|
- }
|
|
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s", zUsage));
|
|
}
|
|
|
|
/*
|
|
@@ -118602,42 +125308,6 @@
|
|
# define explainSetInteger(y,z)
|
|
#endif
|
|
|
|
-#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT)
|
|
-/*
|
|
-** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
|
|
-** is a no-op. Otherwise, it adds a single row of output to the EQP result,
|
|
-** where the caption is of one of the two forms:
|
|
-**
|
|
-** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)"
|
|
-** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)"
|
|
-**
|
|
-** where iSub1 and iSub2 are the integers passed as the corresponding
|
|
-** function parameters, and op is the text representation of the parameter
|
|
-** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT,
|
|
-** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is
|
|
-** false, or the second form if it is true.
|
|
-*/
|
|
-static void explainComposite(
|
|
- Parse *pParse, /* Parse context */
|
|
- int op, /* One of TK_UNION, TK_EXCEPT etc. */
|
|
- int iSub1, /* Subquery id 1 */
|
|
- int iSub2, /* Subquery id 2 */
|
|
- int bUseTmp /* True if a temp table was used */
|
|
-){
|
|
- assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL );
|
|
- if( pParse->explain==2 ){
|
|
- Vdbe *v = pParse->pVdbe;
|
|
- char *zMsg = sqlite3MPrintf(
|
|
- pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2,
|
|
- bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)
|
|
- );
|
|
- sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
|
- }
|
|
-}
|
|
-#else
|
|
-/* No-op versions of the explainXXX() functions and macros. */
|
|
-# define explainComposite(v,w,x,y,z)
|
|
-#endif
|
|
|
|
/*
|
|
** If the inner loop was generated using a non-null pOrderBy argument,
|
|
@@ -118655,7 +125325,7 @@
|
|
Vdbe *v = pParse->pVdbe; /* The prepared statement */
|
|
int addrBreak = pSort->labelDone; /* Jump here to exit loop */
|
|
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
|
|
- int addr;
|
|
+ int addr; /* Top of output loop. Jump for Next. */
|
|
int addrOnce = 0;
|
|
int iTab;
|
|
ExprList *pOrderBy = pSort->pOrderBy;
|
|
@@ -118664,11 +125334,11 @@
|
|
int regRow;
|
|
int regRowid;
|
|
int iCol;
|
|
- int nKey;
|
|
+ int nKey; /* Number of key columns in sorter record */
|
|
int iSortTab; /* Sorter cursor to read from */
|
|
- int nSortData; /* Trailing values to read from sorter */
|
|
int i;
|
|
int bSeq; /* True if sorter record includes seq. no. */
|
|
+ int nRefKey = 0;
|
|
struct ExprList_item *aOutEx = p->pEList->a;
|
|
|
|
assert( addrBreak<0 );
|
|
@@ -118677,15 +125347,24 @@
|
|
sqlite3VdbeGoto(v, addrBreak);
|
|
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
|
}
|
|
+
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ /* Open any cursors needed for sorter-reference expressions */
|
|
+ for(i=0; i<pSort->nDefer; i++){
|
|
+ Table *pTab = pSort->aDefer[i].pTab;
|
|
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
|
+ sqlite3OpenTable(pParse, pSort->aDefer[i].iCsr, iDb, pTab, OP_OpenRead);
|
|
+ nRefKey = MAX(nRefKey, pSort->aDefer[i].nKey);
|
|
+ }
|
|
+#endif
|
|
+
|
|
iTab = pSort->iECursor;
|
|
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
|
|
regRowid = 0;
|
|
regRow = pDest->iSdst;
|
|
- nSortData = nColumn;
|
|
}else{
|
|
regRowid = sqlite3GetTempReg(pParse);
|
|
regRow = sqlite3GetTempRange(pParse, nColumn);
|
|
- nSortData = nColumn;
|
|
}
|
|
nKey = pOrderBy->nExpr - pSort->nOBSat;
|
|
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
|
@@ -118694,7 +125373,8 @@
|
|
if( pSort->labelBkOut ){
|
|
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
|
}
|
|
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
|
|
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut,
|
|
+ nKey+1+nColumn+nRefKey);
|
|
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
|
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
|
VdbeCoverage(v);
|
|
@@ -118707,16 +125387,60 @@
|
|
iSortTab = iTab;
|
|
bSeq = 1;
|
|
}
|
|
- for(i=0, iCol=nKey+bSeq; i<nSortData; i++){
|
|
- int iRead;
|
|
- if( aOutEx[i].u.x.iOrderByCol ){
|
|
- iRead = aOutEx[i].u.x.iOrderByCol-1;
|
|
- }else{
|
|
- iRead = iCol++;
|
|
+ for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( aOutEx[i].bSorterRef ) continue;
|
|
+#endif
|
|
+ if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++;
|
|
+ }
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( pSort->nDefer ){
|
|
+ int iKey = iCol+1;
|
|
+ int regKey = sqlite3GetTempRange(pParse, nRefKey);
|
|
+
|
|
+ for(i=0; i<pSort->nDefer; i++){
|
|
+ int iCsr = pSort->aDefer[i].iCsr;
|
|
+ Table *pTab = pSort->aDefer[i].pTab;
|
|
+ int nKey = pSort->aDefer[i].nKey;
|
|
+
|
|
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
|
|
+ if( HasRowid(pTab) ){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey);
|
|
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iCsr,
|
|
+ sqlite3VdbeCurrentAddr(v)+1, regKey);
|
|
+ }else{
|
|
+ int k;
|
|
+ int iJmp;
|
|
+ assert( sqlite3PrimaryKeyIndex(pTab)->nKeyCol==nKey );
|
|
+ for(k=0; k<nKey; k++){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey+k);
|
|
+ }
|
|
+ iJmp = sqlite3VdbeCurrentAddr(v);
|
|
+ sqlite3VdbeAddOp4Int(v, OP_SeekGE, iCsr, iJmp+2, regKey, nKey);
|
|
+ sqlite3VdbeAddOp4Int(v, OP_IdxLE, iCsr, iJmp+3, regKey, nKey);
|
|
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
|
|
+ }
|
|
}
|
|
- sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
|
|
- VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
|
|
+ sqlite3ReleaseTempRange(pParse, regKey, nRefKey);
|
|
}
|
|
+#endif
|
|
+ for(i=nColumn-1; i>=0; i--){
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ if( aOutEx[i].bSorterRef ){
|
|
+ sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i);
|
|
+ }else
|
|
+#endif
|
|
+ {
|
|
+ int iRead;
|
|
+ if( aOutEx[i].u.x.iOrderByCol ){
|
|
+ iRead = aOutEx[i].u.x.iOrderByCol-1;
|
|
+ }else{
|
|
+ iRead = iCol--;
|
|
+ }
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
|
|
+ VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName : aOutEx[i].zSpan));
|
|
+ }
|
|
+ }
|
|
switch( eDest ){
|
|
case SRT_Table:
|
|
case SRT_EphemTab: {
|
|
@@ -118730,7 +125454,6 @@
|
|
assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
|
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
|
|
pDest->zAffSdst, nColumn);
|
|
- sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
|
|
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, regRowid, regRow, nColumn);
|
|
break;
|
|
}
|
|
@@ -118745,7 +125468,6 @@
|
|
testcase( eDest==SRT_Coroutine );
|
|
if( eDest==SRT_Output ){
|
|
sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn);
|
|
- sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn);
|
|
}else{
|
|
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
|
}
|
|
@@ -118797,23 +125519,23 @@
|
|
** the SQLITE_ENABLE_COLUMN_METADATA compile-time option is used.
|
|
*/
|
|
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
|
-# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
|
|
+# define columnType(A,B,C,D,E) columnTypeImpl(A,B,C,D,E)
|
|
#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
|
|
-# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
|
|
+# define columnType(A,B,C,D,E) columnTypeImpl(A,B)
|
|
#endif
|
|
static const char *columnTypeImpl(
|
|
NameContext *pNC,
|
|
+#ifndef SQLITE_ENABLE_COLUMN_METADATA
|
|
+ Expr *pExpr
|
|
+#else
|
|
Expr *pExpr,
|
|
-#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
|
const char **pzOrigDb,
|
|
const char **pzOrigTab,
|
|
- const char **pzOrigCol,
|
|
+ const char **pzOrigCol
|
|
#endif
|
|
- u8 *pEstWidth
|
|
){
|
|
char const *zType = 0;
|
|
int j;
|
|
- u8 estWidth = 1;
|
|
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
|
char const *zOrigDb = 0;
|
|
char const *zOrigTab = 0;
|
|
@@ -118822,8 +125544,9 @@
|
|
|
|
assert( pExpr!=0 );
|
|
assert( pNC->pSrcList!=0 );
|
|
+ assert( pExpr->op!=TK_AGG_COLUMN ); /* This routine runes before aggregates
|
|
+ ** are processed */
|
|
switch( pExpr->op ){
|
|
- case TK_AGG_COLUMN:
|
|
case TK_COLUMN: {
|
|
/* The expression is a column. Locate the table the column is being
|
|
** extracted from in NameContext.pSrcList. This table may be real
|
|
@@ -118832,8 +125555,6 @@
|
|
Table *pTab = 0; /* Table structure column is extracted from */
|
|
Select *pS = 0; /* Select the column is extracted from */
|
|
int iCol = pExpr->iColumn; /* Index of column in pTab */
|
|
- testcase( pExpr->op==TK_AGG_COLUMN );
|
|
- testcase( pExpr->op==TK_COLUMN );
|
|
while( pNC && !pTab ){
|
|
SrcList *pTabList = pNC->pSrcList;
|
|
for(j=0;j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++);
|
|
@@ -118866,7 +125587,7 @@
|
|
break;
|
|
}
|
|
|
|
- assert( pTab && pExpr->pTab==pTab );
|
|
+ assert( pTab && pExpr->y.pTab==pTab );
|
|
if( pS ){
|
|
/* The "table" is actually a sub-select or a view in the FROM clause
|
|
** of the SELECT statement. Return the declaration type and origin
|
|
@@ -118882,14 +125603,14 @@
|
|
sNC.pSrcList = pS->pSrc;
|
|
sNC.pNext = pNC;
|
|
sNC.pParse = pNC->pParse;
|
|
- zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth);
|
|
+ zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol);
|
|
}
|
|
- }else if( pTab->pSchema ){
|
|
- /* A real table */
|
|
+ }else{
|
|
+ /* A real table or a CTE table */
|
|
assert( !pS );
|
|
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
|
if( iCol<0 ) iCol = pTab->iPKey;
|
|
- assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
|
|
-#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
|
+ assert( iCol==XN_ROWID || (iCol>=0 && iCol<pTab->nCol) );
|
|
if( iCol<0 ){
|
|
zType = "INTEGER";
|
|
zOrigCol = "rowid";
|
|
@@ -118896,19 +125617,18 @@
|
|
}else{
|
|
zOrigCol = pTab->aCol[iCol].zName;
|
|
zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
|
|
- estWidth = pTab->aCol[iCol].szEst;
|
|
}
|
|
zOrigTab = pTab->zName;
|
|
- if( pNC->pParse ){
|
|
+ if( pNC->pParse && pTab->pSchema ){
|
|
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
|
|
zOrigDb = pNC->pParse->db->aDb[iDb].zDbSName;
|
|
}
|
|
#else
|
|
+ assert( iCol==XN_ROWID || (iCol>=0 && iCol<pTab->nCol) );
|
|
if( iCol<0 ){
|
|
zType = "INTEGER";
|
|
}else{
|
|
zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
|
|
- estWidth = pTab->aCol[iCol].szEst;
|
|
}
|
|
#endif
|
|
}
|
|
@@ -118927,7 +125647,7 @@
|
|
sNC.pSrcList = pS->pSrc;
|
|
sNC.pNext = pNC;
|
|
sNC.pParse = pNC->pParse;
|
|
- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &estWidth);
|
|
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
|
|
break;
|
|
}
|
|
#endif
|
|
@@ -118941,7 +125661,6 @@
|
|
*pzOrigCol = zOrigCol;
|
|
}
|
|
#endif
|
|
- if( pEstWidth ) *pEstWidth = estWidth;
|
|
return zType;
|
|
}
|
|
|
|
@@ -118968,7 +125687,7 @@
|
|
const char *zOrigDb = 0;
|
|
const char *zOrigTab = 0;
|
|
const char *zOrigCol = 0;
|
|
- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, 0);
|
|
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
|
|
|
|
/* The vdbe must make its own copy of the column-type and other
|
|
** column specific strings, in case the schema is reset before this
|
|
@@ -118978,7 +125697,7 @@
|
|
sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
|
|
sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
|
|
#else
|
|
- zType = columnType(&sNC, p, 0, 0, 0, 0);
|
|
+ zType = columnType(&sNC, p, 0, 0, 0);
|
|
#endif
|
|
sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
|
|
}
|
|
@@ -119008,9 +125727,9 @@
|
|
** other words, the zSpan of the result expression.
|
|
**
|
|
** short=ON, full=OFF: (This is the default setting). If the result
|
|
-** refers directly to a table column, then the result
|
|
-** column name is just the table column name: COLUMN.
|
|
-** Otherwise use zSpan.
|
|
+** refers directly to a table column, then the
|
|
+** result column name is just the table column
|
|
+** name: COLUMN. Otherwise use zSpan.
|
|
**
|
|
** full=ON, short=ANY: If the result refers directly to a table column,
|
|
** then the result column name with the table name
|
|
@@ -119036,9 +125755,10 @@
|
|
}
|
|
#endif
|
|
|
|
- if( pParse->colNamesSet || db->mallocFailed ) return;
|
|
+ if( pParse->colNamesSet ) return;
|
|
/* Column names are determined by the left-most term of a compound select */
|
|
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
|
|
+ SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
|
|
pTabList = pSelect->pSrc;
|
|
pEList = pSelect->pEList;
|
|
assert( v!=0 );
|
|
@@ -119051,6 +125771,8 @@
|
|
Expr *p = pEList->a[i].pExpr;
|
|
|
|
assert( p!=0 );
|
|
+ assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */
|
|
+ assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */
|
|
if( pEList->a[i].zName ){
|
|
/* An AS clause always takes first priority */
|
|
char *zName = pEList->a[i].zName;
|
|
@@ -119058,7 +125780,7 @@
|
|
}else if( srcName && p->op==TK_COLUMN ){
|
|
char *zCol;
|
|
int iCol = p->iColumn;
|
|
- pTab = p->pTab;
|
|
+ pTab = p->y.pTab;
|
|
assert( pTab!=0 );
|
|
if( iCol<0 ) iCol = pTab->iPKey;
|
|
assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
|
|
@@ -119125,6 +125847,7 @@
|
|
nCol = pEList->nExpr;
|
|
aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
|
|
testcase( aCol==0 );
|
|
+ if( nCol>32767 ) nCol = 32767;
|
|
}else{
|
|
nCol = 0;
|
|
aCol = 0;
|
|
@@ -119144,10 +125867,12 @@
|
|
pColExpr = pColExpr->pRight;
|
|
assert( pColExpr!=0 );
|
|
}
|
|
- if( pColExpr->op==TK_COLUMN && pColExpr->pTab!=0 ){
|
|
+ assert( pColExpr->op!=TK_AGG_COLUMN );
|
|
+ if( pColExpr->op==TK_COLUMN ){
|
|
/* For columns use the column name name */
|
|
int iCol = pColExpr->iColumn;
|
|
- Table *pTab = pColExpr->pTab;
|
|
+ Table *pTab = pColExpr->y.pTab;
|
|
+ assert( pTab!=0 );
|
|
if( iCol<0 ) iCol = pTab->iPKey;
|
|
zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
|
|
}else if( pColExpr->op==TK_ID ){
|
|
@@ -119219,7 +125944,6 @@
|
|
int i;
|
|
Expr *p;
|
|
struct ExprList_item *a;
|
|
- u64 szAll = 0;
|
|
|
|
assert( pSelect!=0 );
|
|
assert( (pSelect->selFlags & SF_Resolved)!=0 );
|
|
@@ -119232,10 +125956,11 @@
|
|
const char *zType;
|
|
int n, m;
|
|
p = a[i].pExpr;
|
|
- zType = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
|
|
- szAll += pCol->szEst;
|
|
+ zType = columnType(&sNC, p, 0, 0, 0);
|
|
+ /* pCol->szEst = ... // Column size est for SELECT tables never used */
|
|
pCol->affinity = sqlite3ExprAffinity(p);
|
|
- if( zType && (m = sqlite3Strlen30(zType))>0 ){
|
|
+ if( zType ){
|
|
+ m = sqlite3Strlen30(zType);
|
|
n = sqlite3Strlen30(pCol->zName);
|
|
pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2);
|
|
if( pCol->zName ){
|
|
@@ -119249,7 +125974,7 @@
|
|
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
|
|
}
|
|
}
|
|
- pTab->szTabRow = sqlite3LogEst(szAll*4);
|
|
+ pTab->szTabRow = 1; /* Any non-zero value works */
|
|
}
|
|
|
|
/*
|
|
@@ -119292,25 +126017,22 @@
|
|
** Get a VDBE for the given parser context. Create a new one if necessary.
|
|
** If an error occurs, return NULL and leave a message in pParse.
|
|
*/
|
|
-static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
|
|
- Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
|
|
- if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1);
|
|
+SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
|
|
+ if( pParse->pVdbe ){
|
|
+ return pParse->pVdbe;
|
|
+ }
|
|
if( pParse->pToplevel==0
|
|
&& OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
|
|
){
|
|
pParse->okConstFactor = 1;
|
|
}
|
|
- return v;
|
|
+ return sqlite3VdbeCreate(pParse);
|
|
}
|
|
-SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
|
|
- Vdbe *v = pParse->pVdbe;
|
|
- return v ? v : allocVdbe(pParse);
|
|
-}
|
|
|
|
|
|
/*
|
|
** Compute the iLimit and iOffset fields of the SELECT based on the
|
|
-** pLimit and pOffset expressions. pLimit and pOffset hold the expressions
|
|
+** pLimit expressions. pLimit->pLeft and pLimit->pRight hold the expressions
|
|
** that appear in the original SQL statement after the LIMIT and OFFSET
|
|
** keywords. Or NULL if those keywords are omitted. iLimit and iOffset
|
|
** are the integer memory register numbers for counters used to compute
|
|
@@ -119318,8 +126040,8 @@
|
|
** iLimit and iOffset are negative.
|
|
**
|
|
** This routine changes the values of iLimit and iOffset only if
|
|
-** a limit or offset is defined by pLimit and pOffset. iLimit and
|
|
-** iOffset should have been preset to appropriate default values (zero)
|
|
+** a limit or offset is defined by pLimit->pLeft and pLimit->pRight. iLimit
|
|
+** and iOffset should have been preset to appropriate default values (zero)
|
|
** prior to calling this routine.
|
|
**
|
|
** The iOffset register (if it exists) is initialized to the value
|
|
@@ -119326,7 +126048,7 @@
|
|
** of the OFFSET. The iLimit register is initialized to LIMIT. Register
|
|
** iOffset+1 is initialized to LIMIT+OFFSET.
|
|
**
|
|
-** Only if pLimit!=0 or pOffset!=0 do the limit registers get
|
|
+** Only if pLimit->pLeft!=0 do the limit registers get
|
|
** redefined. The UNION ALL operator uses this property to force
|
|
** the reuse of the same limit and offset registers across multiple
|
|
** SELECT statements.
|
|
@@ -119336,6 +126058,8 @@
|
|
int iLimit = 0;
|
|
int iOffset;
|
|
int n;
|
|
+ Expr *pLimit = p->pLimit;
|
|
+
|
|
if( p->iLimit ) return;
|
|
|
|
/*
|
|
@@ -119344,13 +126068,13 @@
|
|
** The current implementation interprets "LIMIT 0" to mean
|
|
** no rows.
|
|
*/
|
|
- sqlite3ExprCacheClear(pParse);
|
|
- assert( p->pOffset==0 || p->pLimit!=0 );
|
|
- if( p->pLimit ){
|
|
+ if( pLimit ){
|
|
+ assert( pLimit->op==TK_LIMIT );
|
|
+ assert( pLimit->pLeft!=0 );
|
|
p->iLimit = iLimit = ++pParse->nMem;
|
|
v = sqlite3GetVdbe(pParse);
|
|
assert( v!=0 );
|
|
- if( sqlite3ExprIsInteger(p->pLimit, &n) ){
|
|
+ if( sqlite3ExprIsInteger(pLimit->pLeft, &n) ){
|
|
sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit);
|
|
VdbeComment((v, "LIMIT counter"));
|
|
if( n==0 ){
|
|
@@ -119360,15 +126084,15 @@
|
|
p->selFlags |= SF_FixedLimit;
|
|
}
|
|
}else{
|
|
- sqlite3ExprCode(pParse, p->pLimit, iLimit);
|
|
+ sqlite3ExprCode(pParse, pLimit->pLeft, iLimit);
|
|
sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v);
|
|
VdbeComment((v, "LIMIT counter"));
|
|
sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, iBreak); VdbeCoverage(v);
|
|
}
|
|
- if( p->pOffset ){
|
|
+ if( pLimit->pRight ){
|
|
p->iOffset = iOffset = ++pParse->nMem;
|
|
pParse->nMem++; /* Allocate an extra register for limit+offset */
|
|
- sqlite3ExprCode(pParse, p->pOffset, iOffset);
|
|
+ sqlite3ExprCode(pParse, pLimit->pRight, iOffset);
|
|
sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v);
|
|
VdbeComment((v, "OFFSET counter"));
|
|
sqlite3VdbeAddOp3(v, OP_OffsetLimit, iLimit, iOffset+1, iOffset);
|
|
@@ -119498,9 +126222,16 @@
|
|
int i; /* Loop counter */
|
|
int rc; /* Result code */
|
|
ExprList *pOrderBy; /* The ORDER BY clause */
|
|
- Expr *pLimit, *pOffset; /* Saved LIMIT and OFFSET */
|
|
+ Expr *pLimit; /* Saved LIMIT and OFFSET */
|
|
int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */
|
|
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( p->pWin ){
|
|
+ sqlite3ErrorMsg(pParse, "cannot use window functions in recursive queries");
|
|
+ return;
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* Obtain authorization to do a recursive query */
|
|
if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
|
|
|
|
@@ -119509,10 +126240,9 @@
|
|
p->nSelectRow = 320; /* 4 billion rows */
|
|
computeLimitRegisters(pParse, p, addrBreak);
|
|
pLimit = p->pLimit;
|
|
- pOffset = p->pOffset;
|
|
regLimit = p->iLimit;
|
|
regOffset = p->iOffset;
|
|
- p->pLimit = p->pOffset = 0;
|
|
+ p->pLimit = 0;
|
|
p->iLimit = p->iOffset = 0;
|
|
pOrderBy = p->pOrderBy;
|
|
|
|
@@ -119558,6 +126288,7 @@
|
|
|
|
/* Store the results of the setup-query in Queue. */
|
|
pSetup->pNext = 0;
|
|
+ ExplainQueryPlan((pParse, 1, "SETUP"));
|
|
rc = sqlite3Select(pParse, pSetup, &destQueue);
|
|
pSetup->pNext = p;
|
|
if( rc ) goto end_of_recursive_query;
|
|
@@ -119577,7 +126308,7 @@
|
|
/* Output the single row in Current */
|
|
addrCont = sqlite3VdbeMakeLabel(v);
|
|
codeOffset(v, regOffset, addrCont);
|
|
- selectInnerLoop(pParse, p, p->pEList, iCurrent,
|
|
+ selectInnerLoop(pParse, p, iCurrent,
|
|
0, 0, pDest, addrCont, addrBreak);
|
|
if( regLimit ){
|
|
sqlite3VdbeAddOp2(v, OP_DecrJumpZero, regLimit, addrBreak);
|
|
@@ -119592,6 +126323,7 @@
|
|
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
|
}else{
|
|
p->pPrior = 0;
|
|
+ ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
|
|
sqlite3Select(pParse, p, &destQueue);
|
|
assert( p->pPrior==0 );
|
|
p->pPrior = pSetup;
|
|
@@ -119605,7 +126337,6 @@
|
|
sqlite3ExprListDelete(pParse->db, p->pOrderBy);
|
|
p->pOrderBy = pOrderBy;
|
|
p->pLimit = pLimit;
|
|
- p->pOffset = pOffset;
|
|
return;
|
|
}
|
|
#endif /* SQLITE_OMIT_CTE */
|
|
@@ -119624,9 +126355,14 @@
|
|
** on a VALUES clause.
|
|
**
|
|
** Because the Select object originates from a VALUES clause:
|
|
-** (1) It has no LIMIT or OFFSET
|
|
+** (1) There is no LIMIT or OFFSET or else there is a LIMIT of exactly 1
|
|
** (2) All terms are UNION ALL
|
|
** (3) There is no ORDER BY clause
|
|
+**
|
|
+** The "LIMIT of exactly 1" case of condition (1) comes about when a VALUES
|
|
+** clause occurs within scalar expression (ex: "SELECT (VALUES(1),(2),(3))").
|
|
+** The sqlite3CodeSubselect will have added the LIMIT 1 clause in tht case.
|
|
+** Since the limit is exactly 1, we only need to evalutes the left-most VALUES.
|
|
*/
|
|
static int multiSelectValues(
|
|
Parse *pParse, /* Parsing context */
|
|
@@ -119633,27 +126369,24 @@
|
|
Select *p, /* The right-most of SELECTs to be coded */
|
|
SelectDest *pDest /* What to do with query results */
|
|
){
|
|
- Select *pPrior;
|
|
int nRow = 1;
|
|
int rc = 0;
|
|
+ int bShowAll = p->pLimit==0;
|
|
assert( p->selFlags & SF_MultiValue );
|
|
do{
|
|
assert( p->selFlags & SF_Values );
|
|
assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) );
|
|
- assert( p->pLimit==0 );
|
|
- assert( p->pOffset==0 );
|
|
assert( p->pNext==0 || p->pEList->nExpr==p->pNext->pEList->nExpr );
|
|
if( p->pPrior==0 ) break;
|
|
assert( p->pPrior->pNext==p );
|
|
p = p->pPrior;
|
|
- nRow++;
|
|
+ nRow += bShowAll;
|
|
}while(1);
|
|
+ ExplainQueryPlan((pParse, 0, "SCAN %d CONSTANT ROW%s", nRow,
|
|
+ nRow==1 ? "" : "S"));
|
|
while( p ){
|
|
- pPrior = p->pPrior;
|
|
- p->pPrior = 0;
|
|
- rc = sqlite3Select(pParse, p, pDest);
|
|
- p->pPrior = pPrior;
|
|
- if( rc ) break;
|
|
+ selectInnerLoop(pParse, p, -1, 0, 0, pDest, 1, 1);
|
|
+ if( !bShowAll ) break;
|
|
p->nSelectRow = nRow;
|
|
p = p->pNext;
|
|
}
|
|
@@ -119702,10 +126435,6 @@
|
|
SelectDest dest; /* Alternative data destination */
|
|
Select *pDelete = 0; /* Chain of simple selects to delete */
|
|
sqlite3 *db; /* Database connection */
|
|
-#ifndef SQLITE_OMIT_EXPLAIN
|
|
- int iSub1 = 0; /* EQP id of left-hand query */
|
|
- int iSub2 = 0; /* EQP id of right-hand query */
|
|
-#endif
|
|
|
|
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
|
|
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
|
|
@@ -119715,18 +126444,12 @@
|
|
db = pParse->db;
|
|
pPrior = p->pPrior;
|
|
dest = *pDest;
|
|
- if( pPrior->pOrderBy ){
|
|
- sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
|
|
- selectOpName(p->op));
|
|
+ if( pPrior->pOrderBy || pPrior->pLimit ){
|
|
+ sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
|
|
+ pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op));
|
|
rc = 1;
|
|
goto multi_select_end;
|
|
}
|
|
- if( pPrior->pLimit ){
|
|
- sqlite3ErrorMsg(pParse,"LIMIT clause should come after %s not before",
|
|
- selectOpName(p->op));
|
|
- rc = 1;
|
|
- goto multi_select_end;
|
|
- }
|
|
|
|
v = sqlite3GetVdbe(pParse);
|
|
assert( v!=0 ); /* The VDBE already created by calling function */
|
|
@@ -119762,226 +126485,231 @@
|
|
*/
|
|
if( p->pOrderBy ){
|
|
return multiSelectOrderBy(pParse, p, pDest);
|
|
- }else
|
|
+ }else{
|
|
|
|
- /* Generate code for the left and right SELECT statements.
|
|
- */
|
|
- switch( p->op ){
|
|
- case TK_ALL: {
|
|
- int addr = 0;
|
|
- int nLimit;
|
|
- assert( !pPrior->pLimit );
|
|
- pPrior->iLimit = p->iLimit;
|
|
- pPrior->iOffset = p->iOffset;
|
|
- pPrior->pLimit = p->pLimit;
|
|
- pPrior->pOffset = p->pOffset;
|
|
- explainSetInteger(iSub1, pParse->iNextSelectId);
|
|
- rc = sqlite3Select(pParse, pPrior, &dest);
|
|
- p->pLimit = 0;
|
|
- p->pOffset = 0;
|
|
- if( rc ){
|
|
- goto multi_select_end;
|
|
+#ifndef SQLITE_OMIT_EXPLAIN
|
|
+ if( pPrior->pPrior==0 ){
|
|
+ ExplainQueryPlan((pParse, 1, "COMPOUND QUERY"));
|
|
+ ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY"));
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* Generate code for the left and right SELECT statements.
|
|
+ */
|
|
+ switch( p->op ){
|
|
+ case TK_ALL: {
|
|
+ int addr = 0;
|
|
+ int nLimit;
|
|
+ assert( !pPrior->pLimit );
|
|
+ pPrior->iLimit = p->iLimit;
|
|
+ pPrior->iOffset = p->iOffset;
|
|
+ pPrior->pLimit = p->pLimit;
|
|
+ rc = sqlite3Select(pParse, pPrior, &dest);
|
|
+ p->pLimit = 0;
|
|
+ if( rc ){
|
|
+ goto multi_select_end;
|
|
+ }
|
|
+ p->pPrior = 0;
|
|
+ p->iLimit = pPrior->iLimit;
|
|
+ p->iOffset = pPrior->iOffset;
|
|
+ if( p->iLimit ){
|
|
+ addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
|
|
+ VdbeComment((v, "Jump ahead if LIMIT reached"));
|
|
+ if( p->iOffset ){
|
|
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit,
|
|
+ p->iLimit, p->iOffset+1, p->iOffset);
|
|
+ }
|
|
+ }
|
|
+ ExplainQueryPlan((pParse, 1, "UNION ALL"));
|
|
+ rc = sqlite3Select(pParse, p, &dest);
|
|
+ testcase( rc!=SQLITE_OK );
|
|
+ pDelete = p->pPrior;
|
|
+ p->pPrior = pPrior;
|
|
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
|
+ if( pPrior->pLimit
|
|
+ && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
|
|
+ && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
|
|
+ ){
|
|
+ p->nSelectRow = sqlite3LogEst((u64)nLimit);
|
|
+ }
|
|
+ if( addr ){
|
|
+ sqlite3VdbeJumpHere(v, addr);
|
|
+ }
|
|
+ break;
|
|
}
|
|
- p->pPrior = 0;
|
|
- p->iLimit = pPrior->iLimit;
|
|
- p->iOffset = pPrior->iOffset;
|
|
- if( p->iLimit ){
|
|
- addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
|
|
- VdbeComment((v, "Jump ahead if LIMIT reached"));
|
|
- if( p->iOffset ){
|
|
- sqlite3VdbeAddOp3(v, OP_OffsetLimit,
|
|
- p->iLimit, p->iOffset+1, p->iOffset);
|
|
+ case TK_EXCEPT:
|
|
+ case TK_UNION: {
|
|
+ int unionTab; /* Cursor number of the temp table holding result */
|
|
+ u8 op = 0; /* One of the SRT_ operations to apply to self */
|
|
+ int priorOp; /* The SRT_ operation to apply to prior selects */
|
|
+ Expr *pLimit; /* Saved values of p->nLimit */
|
|
+ int addr;
|
|
+ SelectDest uniondest;
|
|
+
|
|
+ testcase( p->op==TK_EXCEPT );
|
|
+ testcase( p->op==TK_UNION );
|
|
+ priorOp = SRT_Union;
|
|
+ if( dest.eDest==priorOp ){
|
|
+ /* We can reuse a temporary table generated by a SELECT to our
|
|
+ ** right.
|
|
+ */
|
|
+ assert( p->pLimit==0 ); /* Not allowed on leftward elements */
|
|
+ unionTab = dest.iSDParm;
|
|
+ }else{
|
|
+ /* We will need to create our own temporary table to hold the
|
|
+ ** intermediate results.
|
|
+ */
|
|
+ unionTab = pParse->nTab++;
|
|
+ assert( p->pOrderBy==0 );
|
|
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
|
|
+ assert( p->addrOpenEphm[0] == -1 );
|
|
+ p->addrOpenEphm[0] = addr;
|
|
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
|
+ assert( p->pEList );
|
|
}
|
|
+
|
|
+ /* Code the SELECT statements to our left
|
|
+ */
|
|
+ assert( !pPrior->pOrderBy );
|
|
+ sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
|
+ rc = sqlite3Select(pParse, pPrior, &uniondest);
|
|
+ if( rc ){
|
|
+ goto multi_select_end;
|
|
+ }
|
|
+
|
|
+ /* Code the current SELECT statement
|
|
+ */
|
|
+ if( p->op==TK_EXCEPT ){
|
|
+ op = SRT_Except;
|
|
+ }else{
|
|
+ assert( p->op==TK_UNION );
|
|
+ op = SRT_Union;
|
|
+ }
|
|
+ p->pPrior = 0;
|
|
+ pLimit = p->pLimit;
|
|
+ p->pLimit = 0;
|
|
+ uniondest.eDest = op;
|
|
+ ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
|
|
+ selectOpName(p->op)));
|
|
+ rc = sqlite3Select(pParse, p, &uniondest);
|
|
+ testcase( rc!=SQLITE_OK );
|
|
+ /* Query flattening in sqlite3Select() might refill p->pOrderBy.
|
|
+ ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
|
|
+ sqlite3ExprListDelete(db, p->pOrderBy);
|
|
+ pDelete = p->pPrior;
|
|
+ p->pPrior = pPrior;
|
|
+ p->pOrderBy = 0;
|
|
+ if( p->op==TK_UNION ){
|
|
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
|
+ }
|
|
+ sqlite3ExprDelete(db, p->pLimit);
|
|
+ p->pLimit = pLimit;
|
|
+ p->iLimit = 0;
|
|
+ p->iOffset = 0;
|
|
+
|
|
+ /* Convert the data in the temporary table into whatever form
|
|
+ ** it is that we currently need.
|
|
+ */
|
|
+ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
|
|
+ if( dest.eDest!=priorOp ){
|
|
+ int iCont, iBreak, iStart;
|
|
+ assert( p->pEList );
|
|
+ iBreak = sqlite3VdbeMakeLabel(v);
|
|
+ iCont = sqlite3VdbeMakeLabel(v);
|
|
+ computeLimitRegisters(pParse, p, iBreak);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
|
|
+ iStart = sqlite3VdbeCurrentAddr(v);
|
|
+ selectInnerLoop(pParse, p, unionTab,
|
|
+ 0, 0, &dest, iCont, iBreak);
|
|
+ sqlite3VdbeResolveLabel(v, iCont);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
|
|
+ sqlite3VdbeResolveLabel(v, iBreak);
|
|
+ sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
|
|
+ }
|
|
+ break;
|
|
}
|
|
- explainSetInteger(iSub2, pParse->iNextSelectId);
|
|
- rc = sqlite3Select(pParse, p, &dest);
|
|
- testcase( rc!=SQLITE_OK );
|
|
- pDelete = p->pPrior;
|
|
- p->pPrior = pPrior;
|
|
- p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
|
- if( pPrior->pLimit
|
|
- && sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
|
|
- && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
|
|
- ){
|
|
- p->nSelectRow = sqlite3LogEst((u64)nLimit);
|
|
- }
|
|
- if( addr ){
|
|
- sqlite3VdbeJumpHere(v, addr);
|
|
- }
|
|
- break;
|
|
- }
|
|
- case TK_EXCEPT:
|
|
- case TK_UNION: {
|
|
- int unionTab; /* Cursor number of the temporary table holding result */
|
|
- u8 op = 0; /* One of the SRT_ operations to apply to self */
|
|
- int priorOp; /* The SRT_ operation to apply to prior selects */
|
|
- Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
|
|
- int addr;
|
|
- SelectDest uniondest;
|
|
-
|
|
- testcase( p->op==TK_EXCEPT );
|
|
- testcase( p->op==TK_UNION );
|
|
- priorOp = SRT_Union;
|
|
- if( dest.eDest==priorOp ){
|
|
- /* We can reuse a temporary table generated by a SELECT to our
|
|
- ** right.
|
|
+ default: assert( p->op==TK_INTERSECT ); {
|
|
+ int tab1, tab2;
|
|
+ int iCont, iBreak, iStart;
|
|
+ Expr *pLimit;
|
|
+ int addr;
|
|
+ SelectDest intersectdest;
|
|
+ int r1;
|
|
+
|
|
+ /* INTERSECT is different from the others since it requires
|
|
+ ** two temporary tables. Hence it has its own case. Begin
|
|
+ ** by allocating the tables we will need.
|
|
*/
|
|
- assert( p->pLimit==0 ); /* Not allowed on leftward elements */
|
|
- assert( p->pOffset==0 ); /* Not allowed on leftward elements */
|
|
- unionTab = dest.iSDParm;
|
|
- }else{
|
|
- /* We will need to create our own temporary table to hold the
|
|
- ** intermediate results.
|
|
- */
|
|
- unionTab = pParse->nTab++;
|
|
+ tab1 = pParse->nTab++;
|
|
+ tab2 = pParse->nTab++;
|
|
assert( p->pOrderBy==0 );
|
|
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
|
|
+
|
|
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
|
|
assert( p->addrOpenEphm[0] == -1 );
|
|
p->addrOpenEphm[0] = addr;
|
|
findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
|
assert( p->pEList );
|
|
- }
|
|
-
|
|
- /* Code the SELECT statements to our left
|
|
- */
|
|
- assert( !pPrior->pOrderBy );
|
|
- sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
|
- explainSetInteger(iSub1, pParse->iNextSelectId);
|
|
- rc = sqlite3Select(pParse, pPrior, &uniondest);
|
|
- if( rc ){
|
|
- goto multi_select_end;
|
|
- }
|
|
-
|
|
- /* Code the current SELECT statement
|
|
- */
|
|
- if( p->op==TK_EXCEPT ){
|
|
- op = SRT_Except;
|
|
- }else{
|
|
- assert( p->op==TK_UNION );
|
|
- op = SRT_Union;
|
|
- }
|
|
- p->pPrior = 0;
|
|
- pLimit = p->pLimit;
|
|
- p->pLimit = 0;
|
|
- pOffset = p->pOffset;
|
|
- p->pOffset = 0;
|
|
- uniondest.eDest = op;
|
|
- explainSetInteger(iSub2, pParse->iNextSelectId);
|
|
- rc = sqlite3Select(pParse, p, &uniondest);
|
|
- testcase( rc!=SQLITE_OK );
|
|
- /* Query flattening in sqlite3Select() might refill p->pOrderBy.
|
|
- ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
|
|
- sqlite3ExprListDelete(db, p->pOrderBy);
|
|
- pDelete = p->pPrior;
|
|
- p->pPrior = pPrior;
|
|
- p->pOrderBy = 0;
|
|
- if( p->op==TK_UNION ){
|
|
- p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
|
- }
|
|
- sqlite3ExprDelete(db, p->pLimit);
|
|
- p->pLimit = pLimit;
|
|
- p->pOffset = pOffset;
|
|
- p->iLimit = 0;
|
|
- p->iOffset = 0;
|
|
-
|
|
- /* Convert the data in the temporary table into whatever form
|
|
- ** it is that we currently need.
|
|
- */
|
|
- assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
|
|
- if( dest.eDest!=priorOp ){
|
|
- int iCont, iBreak, iStart;
|
|
+
|
|
+ /* Code the SELECTs to our left into temporary table "tab1".
|
|
+ */
|
|
+ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
|
+ rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
|
+ if( rc ){
|
|
+ goto multi_select_end;
|
|
+ }
|
|
+
|
|
+ /* Code the current SELECT into temporary table "tab2"
|
|
+ */
|
|
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
|
|
+ assert( p->addrOpenEphm[1] == -1 );
|
|
+ p->addrOpenEphm[1] = addr;
|
|
+ p->pPrior = 0;
|
|
+ pLimit = p->pLimit;
|
|
+ p->pLimit = 0;
|
|
+ intersectdest.iSDParm = tab2;
|
|
+ ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
|
|
+ selectOpName(p->op)));
|
|
+ rc = sqlite3Select(pParse, p, &intersectdest);
|
|
+ testcase( rc!=SQLITE_OK );
|
|
+ pDelete = p->pPrior;
|
|
+ p->pPrior = pPrior;
|
|
+ if( p->nSelectRow>pPrior->nSelectRow ){
|
|
+ p->nSelectRow = pPrior->nSelectRow;
|
|
+ }
|
|
+ sqlite3ExprDelete(db, p->pLimit);
|
|
+ p->pLimit = pLimit;
|
|
+
|
|
+ /* Generate code to take the intersection of the two temporary
|
|
+ ** tables.
|
|
+ */
|
|
assert( p->pEList );
|
|
iBreak = sqlite3VdbeMakeLabel(v);
|
|
iCont = sqlite3VdbeMakeLabel(v);
|
|
computeLimitRegisters(pParse, p, iBreak);
|
|
- sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
|
|
- iStart = sqlite3VdbeCurrentAddr(v);
|
|
- selectInnerLoop(pParse, p, p->pEList, unionTab,
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
|
|
+ r1 = sqlite3GetTempReg(pParse);
|
|
+ iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
|
|
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3ReleaseTempReg(pParse, r1);
|
|
+ selectInnerLoop(pParse, p, tab1,
|
|
0, 0, &dest, iCont, iBreak);
|
|
sqlite3VdbeResolveLabel(v, iCont);
|
|
- sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
|
|
sqlite3VdbeResolveLabel(v, iBreak);
|
|
- sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
|
|
+ sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
|
|
+ sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
|
|
+ break;
|
|
}
|
|
- break;
|
|
}
|
|
- default: assert( p->op==TK_INTERSECT ); {
|
|
- int tab1, tab2;
|
|
- int iCont, iBreak, iStart;
|
|
- Expr *pLimit, *pOffset;
|
|
- int addr;
|
|
- SelectDest intersectdest;
|
|
- int r1;
|
|
-
|
|
- /* INTERSECT is different from the others since it requires
|
|
- ** two temporary tables. Hence it has its own case. Begin
|
|
- ** by allocating the tables we will need.
|
|
- */
|
|
- tab1 = pParse->nTab++;
|
|
- tab2 = pParse->nTab++;
|
|
- assert( p->pOrderBy==0 );
|
|
-
|
|
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
|
|
- assert( p->addrOpenEphm[0] == -1 );
|
|
- p->addrOpenEphm[0] = addr;
|
|
- findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
|
- assert( p->pEList );
|
|
-
|
|
- /* Code the SELECTs to our left into temporary table "tab1".
|
|
- */
|
|
- sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
|
- explainSetInteger(iSub1, pParse->iNextSelectId);
|
|
- rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
|
- if( rc ){
|
|
- goto multi_select_end;
|
|
- }
|
|
-
|
|
- /* Code the current SELECT into temporary table "tab2"
|
|
- */
|
|
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
|
|
- assert( p->addrOpenEphm[1] == -1 );
|
|
- p->addrOpenEphm[1] = addr;
|
|
- p->pPrior = 0;
|
|
- pLimit = p->pLimit;
|
|
- p->pLimit = 0;
|
|
- pOffset = p->pOffset;
|
|
- p->pOffset = 0;
|
|
- intersectdest.iSDParm = tab2;
|
|
- explainSetInteger(iSub2, pParse->iNextSelectId);
|
|
- rc = sqlite3Select(pParse, p, &intersectdest);
|
|
- testcase( rc!=SQLITE_OK );
|
|
- pDelete = p->pPrior;
|
|
- p->pPrior = pPrior;
|
|
- if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
|
|
- sqlite3ExprDelete(db, p->pLimit);
|
|
- p->pLimit = pLimit;
|
|
- p->pOffset = pOffset;
|
|
-
|
|
- /* Generate code to take the intersection of the two temporary
|
|
- ** tables.
|
|
- */
|
|
- assert( p->pEList );
|
|
- iBreak = sqlite3VdbeMakeLabel(v);
|
|
- iCont = sqlite3VdbeMakeLabel(v);
|
|
- computeLimitRegisters(pParse, p, iBreak);
|
|
- sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
|
|
- r1 = sqlite3GetTempReg(pParse);
|
|
- iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
|
|
- sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v);
|
|
- sqlite3ReleaseTempReg(pParse, r1);
|
|
- selectInnerLoop(pParse, p, p->pEList, tab1,
|
|
- 0, 0, &dest, iCont, iBreak);
|
|
- sqlite3VdbeResolveLabel(v, iCont);
|
|
- sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
|
|
- sqlite3VdbeResolveLabel(v, iBreak);
|
|
- sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
|
|
- sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
|
|
- break;
|
|
+
|
|
+ #ifndef SQLITE_OMIT_EXPLAIN
|
|
+ if( p->pNext==0 ){
|
|
+ ExplainQueryPlanPop(pParse);
|
|
}
|
|
+ #endif
|
|
}
|
|
-
|
|
- explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL);
|
|
-
|
|
+
|
|
/* Compute collating sequences used by
|
|
** temporary tables needed to implement the compound select.
|
|
** Attach the KeyInfo structure to all temporary tables.
|
|
@@ -120132,7 +126860,6 @@
|
|
r1 = sqlite3GetTempReg(pParse);
|
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
|
|
r1, pDest->zAffSdst, pIn->nSdst);
|
|
- sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
|
|
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pDest->iSDParm, r1,
|
|
pIn->iSdst, pIn->nSdst);
|
|
sqlite3ReleaseTempReg(pParse, r1);
|
|
@@ -120175,7 +126902,6 @@
|
|
default: {
|
|
assert( pDest->eDest==SRT_Output );
|
|
sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iSdst, pIn->nSdst);
|
|
- sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
|
|
break;
|
|
}
|
|
}
|
|
@@ -120319,10 +127045,6 @@
|
|
ExprList *pOrderBy; /* The ORDER BY clause */
|
|
int nOrderBy; /* Number of terms in the ORDER BY clause */
|
|
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
|
|
-#ifndef SQLITE_OMIT_EXPLAIN
|
|
- int iSub1; /* EQP id of left-hand query */
|
|
- int iSub2; /* EQP id of right-hand query */
|
|
-#endif
|
|
|
|
assert( p->pOrderBy!=0 );
|
|
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
|
|
@@ -120434,8 +127156,6 @@
|
|
}
|
|
sqlite3ExprDelete(db, p->pLimit);
|
|
p->pLimit = 0;
|
|
- sqlite3ExprDelete(db, p->pOffset);
|
|
- p->pOffset = 0;
|
|
|
|
regAddrA = ++pParse->nMem;
|
|
regAddrB = ++pParse->nMem;
|
|
@@ -120444,6 +127164,8 @@
|
|
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
|
|
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
|
|
|
|
+ ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op)));
|
|
+
|
|
/* Generate a coroutine to evaluate the SELECT statement to the
|
|
** left of the compound operator - the "A" select.
|
|
*/
|
|
@@ -120451,7 +127173,7 @@
|
|
addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
|
|
VdbeComment((v, "left SELECT"));
|
|
pPrior->iLimit = regLimitA;
|
|
- explainSetInteger(iSub1, pParse->iNextSelectId);
|
|
+ ExplainQueryPlan((pParse, 1, "LEFT"));
|
|
sqlite3Select(pParse, pPrior, &destA);
|
|
sqlite3VdbeEndCoroutine(v, regAddrA);
|
|
sqlite3VdbeJumpHere(v, addr1);
|
|
@@ -120466,7 +127188,7 @@
|
|
savedOffset = p->iOffset;
|
|
p->iLimit = regLimitB;
|
|
p->iOffset = 0;
|
|
- explainSetInteger(iSub2, pParse->iNextSelectId);
|
|
+ ExplainQueryPlan((pParse, 1, "RIGHT"));
|
|
sqlite3Select(pParse, p, &destB);
|
|
p->iLimit = savedLimit;
|
|
p->iOffset = savedOffset;
|
|
@@ -120578,7 +127300,7 @@
|
|
|
|
/*** TBD: Insert subroutine calls to close cursors on incomplete
|
|
**** subqueries ****/
|
|
- explainComposite(pParse, p->op, iSub1, iSub2, 0);
|
|
+ ExplainQueryPlanPop(pParse);
|
|
return pParse->nErr!=0;
|
|
}
|
|
#endif
|
|
@@ -120621,7 +127343,9 @@
|
|
Expr *pExpr /* Expr in which substitution occurs */
|
|
){
|
|
if( pExpr==0 ) return 0;
|
|
- if( ExprHasProperty(pExpr, EP_FromJoin) && pExpr->iRightJoinTable==pSubst->iTable ){
|
|
+ if( ExprHasProperty(pExpr, EP_FromJoin)
|
|
+ && pExpr->iRightJoinTable==pSubst->iTable
|
|
+ ){
|
|
pExpr->iRightJoinTable = pSubst->iNewTable;
|
|
}
|
|
if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){
|
|
@@ -120632,7 +127356,7 @@
|
|
Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
|
|
Expr ifNullRow;
|
|
assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr );
|
|
- assert( pExpr->pLeft==0 && pExpr->pRight==0 );
|
|
+ assert( pExpr->pRight==0 );
|
|
if( sqlite3ExprIsVector(pCopy) ){
|
|
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
|
|
}else{
|
|
@@ -120734,69 +127458,75 @@
|
|
** exist on the table t1, a complete scan of the data might be
|
|
** avoided.
|
|
**
|
|
-** Flattening is only attempted if all of the following are true:
|
|
+** Flattening is subject to the following constraints:
|
|
**
|
|
-** (1) The subquery and the outer query do not both use aggregates.
|
|
+** (**) We no longer attempt to flatten aggregate subqueries. Was:
|
|
+** The subquery and the outer query cannot both be aggregates.
|
|
**
|
|
-** (2) The subquery is not an aggregate or (2a) the outer query is not a join
|
|
-** and (2b) the outer query does not use subqueries other than the one
|
|
-** FROM-clause subquery that is a candidate for flattening. (2b is
|
|
-** due to ticket [2f7170d73bf9abf80] from 2015-02-09.)
|
|
+** (**) We no longer attempt to flatten aggregate subqueries. Was:
|
|
+** (2) If the subquery is an aggregate then
|
|
+** (2a) the outer query must not be a join and
|
|
+** (2b) the outer query must not use subqueries
|
|
+** other than the one FROM-clause subquery that is a candidate
|
|
+** for flattening. (This is due to ticket [2f7170d73bf9abf80]
|
|
+** from 2015-02-09.)
|
|
**
|
|
-** (3) The subquery is not the right operand of a LEFT JOIN
|
|
-** or (a) the subquery is not itself a join and (b) the FROM clause
|
|
-** of the subquery does not contain a virtual table and (c) the
|
|
-** outer query is not an aggregate.
|
|
+** (3) If the subquery is the right operand of a LEFT JOIN then
|
|
+** (3a) the subquery may not be a join and
|
|
+** (3b) the FROM clause of the subquery may not contain a virtual
|
|
+** table and
|
|
+** (3c) the outer query may not be an aggregate.
|
|
**
|
|
-** (4) The subquery is not DISTINCT.
|
|
+** (4) The subquery can not be DISTINCT.
|
|
**
|
|
** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT
|
|
** sub-queries that were excluded from this optimization. Restriction
|
|
** (4) has since been expanded to exclude all DISTINCT subqueries.
|
|
**
|
|
-** (6) The subquery does not use aggregates or the outer query is not
|
|
-** DISTINCT.
|
|
+** (**) We no longer attempt to flatten aggregate subqueries. Was:
|
|
+** If the subquery is aggregate, the outer query may not be DISTINCT.
|
|
**
|
|
-** (7) The subquery has a FROM clause. TODO: For subqueries without
|
|
+** (7) The subquery must have a FROM clause. TODO: For subqueries without
|
|
** A FROM clause, consider adding a FROM clause with the special
|
|
** table sqlite_once that consists of a single row containing a
|
|
** single NULL.
|
|
**
|
|
-** (8) The subquery does not use LIMIT or the outer query is not a join.
|
|
+** (8) If the subquery uses LIMIT then the outer query may not be a join.
|
|
**
|
|
-** (9) The subquery does not use LIMIT or the outer query does not use
|
|
-** aggregates.
|
|
+** (9) If the subquery uses LIMIT then the outer query may not be aggregate.
|
|
**
|
|
** (**) Restriction (10) was removed from the code on 2005-02-05 but we
|
|
** accidently carried the comment forward until 2014-09-15. Original
|
|
-** text: "The subquery does not use aggregates or the outer query
|
|
-** does not use LIMIT."
|
|
+** constraint: "If the subquery is aggregate then the outer query
|
|
+** may not use LIMIT."
|
|
**
|
|
-** (11) The subquery and the outer query do not both have ORDER BY clauses.
|
|
+** (11) The subquery and the outer query may not both have ORDER BY clauses.
|
|
**
|
|
** (**) Not implemented. Subsumed into restriction (3). Was previously
|
|
** a separate restriction deriving from ticket #350.
|
|
**
|
|
-** (13) The subquery and outer query do not both use LIMIT.
|
|
+** (13) The subquery and outer query may not both use LIMIT.
|
|
**
|
|
-** (14) The subquery does not use OFFSET.
|
|
+** (14) The subquery may not use OFFSET.
|
|
**
|
|
-** (15) The outer query is not part of a compound select or the
|
|
-** subquery does not have a LIMIT clause.
|
|
+** (15) If the outer query is part of a compound select, then the
|
|
+** subquery may not use LIMIT.
|
|
** (See ticket #2339 and ticket [02a8e81d44]).
|
|
**
|
|
-** (16) The outer query is not an aggregate or the subquery does
|
|
-** not contain ORDER BY. (Ticket #2942) This used to not matter
|
|
+** (16) If the outer query is aggregate, then the subquery may not
|
|
+** use ORDER BY. (Ticket #2942) This used to not matter
|
|
** until we introduced the group_concat() function.
|
|
**
|
|
-** (17) The sub-query is not a compound select, or it is a UNION ALL
|
|
-** compound clause made up entirely of non-aggregate queries, and
|
|
-** the parent query:
|
|
+** (17) If the subquery is a compound select, then
|
|
+** (17a) all compound operators must be a UNION ALL, and
|
|
+** (17b) no terms within the subquery compound may be aggregate
|
|
+** or DISTINCT, and
|
|
+** (17c) every term within the subquery compound must have a FROM clause
|
|
+** (17d) the outer query may not be
|
|
+** (17d1) aggregate, or
|
|
+** (17d2) DISTINCT, or
|
|
+** (17d3) a join.
|
|
**
|
|
-** * is not itself part of a compound select,
|
|
-** * is not an aggregate or DISTINCT query, and
|
|
-** * is not a join
|
|
-**
|
|
** The parent and sub-query may contain WHERE clauses. Subject to
|
|
** rules (11), (13) and (14), they may also contain ORDER BY,
|
|
** LIMIT and OFFSET clauses. The subquery cannot use any compound
|
|
@@ -120811,10 +127541,10 @@
|
|
** syntax error and return a detailed message.
|
|
**
|
|
** (18) If the sub-query is a compound select, then all terms of the
|
|
-** ORDER by clause of the parent must be simple references to
|
|
+** ORDER BY clause of the parent must be simple references to
|
|
** columns of the sub-query.
|
|
**
|
|
-** (19) The subquery does not use LIMIT or the outer query does not
|
|
+** (19) If the subquery uses LIMIT then the outer query may not
|
|
** have a WHERE clause.
|
|
**
|
|
** (20) If the sub-query is a compound select, then it must not use
|
|
@@ -120823,25 +127553,31 @@
|
|
** appear as unmodified result columns in the outer query. But we
|
|
** have other optimizations in mind to deal with that case.
|
|
**
|
|
-** (21) The subquery does not use LIMIT or the outer query is not
|
|
+** (21) If the subquery uses LIMIT then the outer query may not be
|
|
** DISTINCT. (See ticket [752e1646fc]).
|
|
**
|
|
-** (22) The subquery is not a recursive CTE.
|
|
+** (22) The subquery may not be a recursive CTE.
|
|
**
|
|
-** (23) The parent is not a recursive CTE, or the sub-query is not a
|
|
-** compound query. This restriction is because transforming the
|
|
+** (**) Subsumed into restriction (17d3). Was: If the outer query is
|
|
+** a recursive CTE, then the sub-query may not be a compound query.
|
|
+** This restriction is because transforming the
|
|
** parent to a compound query confuses the code that handles
|
|
** recursive queries in multiSelect().
|
|
**
|
|
-** (24) The subquery is not an aggregate that uses the built-in min() or
|
|
+** (**) We no longer attempt to flatten aggregate subqueries. Was:
|
|
+** The subquery may not be an aggregate that uses the built-in min() or
|
|
** or max() functions. (Without this restriction, a query like:
|
|
** "SELECT x FROM (SELECT max(y), x FROM t1)" would not necessarily
|
|
** return the value X for which Y was maximal.)
|
|
**
|
|
+** (25) If either the subquery or the parent query contains a window
|
|
+** function in the select list or ORDER BY clause, flattening
|
|
+** is not attempted.
|
|
**
|
|
+**
|
|
** In this routine, the "p" parameter is a pointer to the outer query.
|
|
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
|
|
-** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
|
|
+** uses aggregates.
|
|
**
|
|
** If flattening is not attempted, this routine is a no-op and returns 0.
|
|
** If flattening is attempted this routine returns 1.
|
|
@@ -120853,8 +127589,7 @@
|
|
Parse *pParse, /* Parsing context */
|
|
Select *p, /* The parent or outer SELECT statement */
|
|
int iFrom, /* Index in p->pSrc->a[] of the inner subquery */
|
|
- int isAgg, /* True if outer SELECT uses aggregate functions */
|
|
- int subqueryIsAgg /* True if the subquery uses aggregate functions */
|
|
+ int isAgg /* True if outer SELECT uses aggregate functions */
|
|
){
|
|
const char *zSavedAuthContext = pParse->zAuthContext;
|
|
Select *pParent; /* Current UNION ALL term of the other query */
|
|
@@ -120873,7 +127608,7 @@
|
|
/* Check to see if flattening is permitted. Return 0 if not.
|
|
*/
|
|
assert( p!=0 );
|
|
- assert( p->pPrior==0 ); /* Unable to flatten compound queries */
|
|
+ assert( p->pPrior==0 );
|
|
if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0;
|
|
pSrc = p->pSrc;
|
|
assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
|
|
@@ -120881,17 +127616,11 @@
|
|
iParent = pSubitem->iCursor;
|
|
pSub = pSubitem->pSelect;
|
|
assert( pSub!=0 );
|
|
- if( subqueryIsAgg ){
|
|
- if( isAgg ) return 0; /* Restriction (1) */
|
|
- if( pSrc->nSrc>1 ) return 0; /* Restriction (2a) */
|
|
- if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery))
|
|
- || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0
|
|
- || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0
|
|
- ){
|
|
- return 0; /* Restriction (2b) */
|
|
- }
|
|
- }
|
|
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( p->pWin || pSub->pWin ) return 0; /* Restriction (25) */
|
|
+#endif
|
|
+
|
|
pSubSrc = pSub->pSrc;
|
|
assert( pSubSrc );
|
|
/* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
|
|
@@ -120900,18 +127629,15 @@
|
|
** became arbitrary expressions, we were forced to add restrictions (13)
|
|
** and (14). */
|
|
if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */
|
|
- if( pSub->pOffset ) return 0; /* Restriction (14) */
|
|
+ if( pSub->pLimit && pSub->pLimit->pRight ) return 0; /* Restriction (14) */
|
|
if( (p->selFlags & SF_Compound)!=0 && pSub->pLimit ){
|
|
return 0; /* Restriction (15) */
|
|
}
|
|
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
|
|
- if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (5) */
|
|
+ if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (4) */
|
|
if( pSub->pLimit && (pSrc->nSrc>1 || isAgg) ){
|
|
return 0; /* Restrictions (8)(9) */
|
|
}
|
|
- if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){
|
|
- return 0; /* Restriction (6) */
|
|
- }
|
|
if( p->pOrderBy && pSub->pOrderBy ){
|
|
return 0; /* Restriction (11) */
|
|
}
|
|
@@ -120920,18 +127646,14 @@
|
|
if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){
|
|
return 0; /* Restriction (21) */
|
|
}
|
|
- testcase( pSub->selFlags & SF_Recursive );
|
|
- testcase( pSub->selFlags & SF_MinMaxAgg );
|
|
- if( pSub->selFlags & (SF_Recursive|SF_MinMaxAgg) ){
|
|
- return 0; /* Restrictions (22) and (24) */
|
|
+ if( pSub->selFlags & (SF_Recursive) ){
|
|
+ return 0; /* Restrictions (22) */
|
|
}
|
|
- if( (p->selFlags & SF_Recursive) && pSub->pPrior ){
|
|
- return 0; /* Restriction (23) */
|
|
- }
|
|
|
|
/*
|
|
** If the subquery is the right operand of a LEFT JOIN, then the
|
|
- ** subquery may not be a join itself. Example of why this is not allowed:
|
|
+ ** subquery may not be a join itself (3a). Example of why this is not
|
|
+ ** allowed:
|
|
**
|
|
** t1 LEFT OUTER JOIN (t2 JOIN t3)
|
|
**
|
|
@@ -120942,9 +127664,9 @@
|
|
** which is not at all the same thing.
|
|
**
|
|
** If the subquery is the right operand of a LEFT JOIN, then the outer
|
|
- ** query cannot be an aggregate. This is an artifact of the way aggregates
|
|
- ** are processed - there is no mechanism to determine if the LEFT JOIN
|
|
- ** table should be all-NULL.
|
|
+ ** query cannot be an aggregate. (3c) This is an artifact of the way
|
|
+ ** aggregates are processed - there is no mechanism to determine if
|
|
+ ** the LEFT JOIN table should be all-NULL.
|
|
**
|
|
** See also tickets #306, #350, and #3300.
|
|
*/
|
|
@@ -120951,19 +127673,21 @@
|
|
if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
|
|
isLeftJoin = 1;
|
|
if( pSubSrc->nSrc>1 || isAgg || IsVirtual(pSubSrc->a[0].pTab) ){
|
|
- return 0; /* Restriction (3) */
|
|
+ /* (3a) (3c) (3b) */
|
|
+ return 0;
|
|
}
|
|
}
|
|
#ifdef SQLITE_EXTRA_IFNULLROW
|
|
else if( iFrom>0 && !isAgg ){
|
|
/* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for
|
|
- ** every reference to any result column from subquery in a join, even though
|
|
- ** they are not necessary. This will stress-test the OP_IfNullRow opcode. */
|
|
+ ** every reference to any result column from subquery in a join, even
|
|
+ ** though they are not necessary. This will stress-test the OP_IfNullRow
|
|
+ ** opcode. */
|
|
isLeftJoin = -1;
|
|
}
|
|
#endif
|
|
|
|
- /* Restriction 17: If the sub-query is a compound SELECT, then it must
|
|
+ /* Restriction (17): If the sub-query is a compound SELECT, then it must
|
|
** use only the UNION ALL operator. And none of the simple select queries
|
|
** that make up the compound SELECT are allowed to be aggregate or distinct
|
|
** queries.
|
|
@@ -120970,10 +127694,10 @@
|
|
*/
|
|
if( pSub->pPrior ){
|
|
if( pSub->pOrderBy ){
|
|
- return 0; /* Restriction 20 */
|
|
+ return 0; /* Restriction (20) */
|
|
}
|
|
if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
|
|
- return 0;
|
|
+ return 0; /* (17d1), (17d2), or (17d3) */
|
|
}
|
|
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
|
|
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
|
|
@@ -120980,9 +127704,9 @@
|
|
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
|
|
assert( pSub->pSrc!=0 );
|
|
assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
|
|
- if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|
|
- || (pSub1->pPrior && pSub1->op!=TK_ALL)
|
|
- || pSub1->pSrc->nSrc<1
|
|
+ if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */
|
|
+ || (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */
|
|
+ || pSub1->pSrc->nSrc<1 /* (17c) */
|
|
){
|
|
return 0;
|
|
}
|
|
@@ -120989,7 +127713,7 @@
|
|
testcase( pSub1->pSrc->nSrc>1 );
|
|
}
|
|
|
|
- /* Restriction 18. */
|
|
+ /* Restriction (18). */
|
|
if( p->pOrderBy ){
|
|
int ii;
|
|
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
|
|
@@ -120998,9 +127722,17 @@
|
|
}
|
|
}
|
|
|
|
+ /* Ex-restriction (23):
|
|
+ ** The only way that the recursive part of a CTE can contain a compound
|
|
+ ** subquery is for the subquery to be one term of a join. But if the
|
|
+ ** subquery is a join, then the flattening has already been stopped by
|
|
+ ** restriction (17d3)
|
|
+ */
|
|
+ assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 );
|
|
+
|
|
/***** If we reach this point, flattening is permitted. *****/
|
|
- SELECTTRACE(1,pParse,p,("flatten %s.%p from term %d\n",
|
|
- pSub->zSelName, pSub, iFrom));
|
|
+ SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
|
|
+ pSub->selId, pSub, iFrom));
|
|
|
|
/* Authorize the subquery */
|
|
pParse->zAuthContext = pSubitem->zName;
|
|
@@ -121045,16 +127777,12 @@
|
|
Select *pNew;
|
|
ExprList *pOrderBy = p->pOrderBy;
|
|
Expr *pLimit = p->pLimit;
|
|
- Expr *pOffset = p->pOffset;
|
|
Select *pPrior = p->pPrior;
|
|
p->pOrderBy = 0;
|
|
p->pSrc = 0;
|
|
p->pPrior = 0;
|
|
p->pLimit = 0;
|
|
- p->pOffset = 0;
|
|
pNew = sqlite3SelectDup(db, p, 0);
|
|
- sqlite3SelectSetName(pNew, pSub->zSelName);
|
|
- p->pOffset = pOffset;
|
|
p->pLimit = pLimit;
|
|
p->pOrderBy = pOrderBy;
|
|
p->pSrc = pSrc;
|
|
@@ -121066,9 +127794,8 @@
|
|
if( pPrior ) pPrior->pNext = pNew;
|
|
pNew->pNext = p;
|
|
p->pPrior = pNew;
|
|
- SELECTTRACE(2,pParse,p,
|
|
- ("compound-subquery flattener creates %s.%p as peer\n",
|
|
- pNew->zSelName, pNew));
|
|
+ SELECTTRACE(2,pParse,p,("compound-subquery flattener"
|
|
+ " creates %u as peer\n",pNew->selId));
|
|
}
|
|
if( db->mallocFailed ) return 1;
|
|
}
|
|
@@ -121202,7 +127929,6 @@
|
|
pOrderBy->a[i].u.x.iOrderByCol = 0;
|
|
}
|
|
assert( pParent->pOrderBy==0 );
|
|
- assert( pSub->pPrior==0 );
|
|
pParent->pOrderBy = pOrderBy;
|
|
pSub->pOrderBy = 0;
|
|
}
|
|
@@ -121210,18 +127936,7 @@
|
|
if( isLeftJoin>0 ){
|
|
setJoinExpr(pWhere, iNewParent);
|
|
}
|
|
- if( subqueryIsAgg ){
|
|
- assert( pParent->pHaving==0 );
|
|
- pParent->pHaving = pParent->pWhere;
|
|
- pParent->pWhere = pWhere;
|
|
- pParent->pHaving = sqlite3ExprAnd(db,
|
|
- sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving
|
|
- );
|
|
- assert( pParent->pGroupBy==0 );
|
|
- pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
|
|
- }else{
|
|
- pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
|
|
- }
|
|
+ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
|
|
if( db->mallocFailed==0 ){
|
|
SubstContext x;
|
|
x.pParse = pParse;
|
|
@@ -121265,8 +127980,184 @@
|
|
}
|
|
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
|
|
|
+/*
|
|
+** A structure to keep track of all of the column values that are fixed to
|
|
+** a known value due to WHERE clause constraints of the form COLUMN=VALUE.
|
|
+*/
|
|
+typedef struct WhereConst WhereConst;
|
|
+struct WhereConst {
|
|
+ Parse *pParse; /* Parsing context */
|
|
+ int nConst; /* Number for COLUMN=CONSTANT terms */
|
|
+ int nChng; /* Number of times a constant is propagated */
|
|
+ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */
|
|
+};
|
|
|
|
+/*
|
|
+** Add a new entry to the pConst object. Except, do not add duplicate
|
|
+** pColumn entires.
|
|
+*/
|
|
+static void constInsert(
|
|
+ WhereConst *pConst, /* The WhereConst into which we are inserting */
|
|
+ Expr *pColumn, /* The COLUMN part of the constraint */
|
|
+ Expr *pValue /* The VALUE part of the constraint */
|
|
+){
|
|
+ int i;
|
|
+ assert( pColumn->op==TK_COLUMN );
|
|
|
|
+ /* 2018-10-25 ticket [cf5ed20f]
|
|
+ ** Make sure the same pColumn is not inserted more than once */
|
|
+ for(i=0; i<pConst->nConst; i++){
|
|
+ const Expr *pExpr = pConst->apExpr[i*2];
|
|
+ assert( pExpr->op==TK_COLUMN );
|
|
+ if( pExpr->iTable==pColumn->iTable
|
|
+ && pExpr->iColumn==pColumn->iColumn
|
|
+ ){
|
|
+ return; /* Already present. Return without doing anything. */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pConst->nConst++;
|
|
+ pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr,
|
|
+ pConst->nConst*2*sizeof(Expr*));
|
|
+ if( pConst->apExpr==0 ){
|
|
+ pConst->nConst = 0;
|
|
+ }else{
|
|
+ if( ExprHasProperty(pValue, EP_FixedCol) ) pValue = pValue->pLeft;
|
|
+ pConst->apExpr[pConst->nConst*2-2] = pColumn;
|
|
+ pConst->apExpr[pConst->nConst*2-1] = pValue;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Find all terms of COLUMN=VALUE or VALUE=COLUMN in pExpr where VALUE
|
|
+** is a constant expression and where the term must be true because it
|
|
+** is part of the AND-connected terms of the expression. For each term
|
|
+** found, add it to the pConst structure.
|
|
+*/
|
|
+static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
|
|
+ Expr *pRight, *pLeft;
|
|
+ if( pExpr==0 ) return;
|
|
+ if( ExprHasProperty(pExpr, EP_FromJoin) ) return;
|
|
+ if( pExpr->op==TK_AND ){
|
|
+ findConstInWhere(pConst, pExpr->pRight);
|
|
+ findConstInWhere(pConst, pExpr->pLeft);
|
|
+ return;
|
|
+ }
|
|
+ if( pExpr->op!=TK_EQ ) return;
|
|
+ pRight = pExpr->pRight;
|
|
+ pLeft = pExpr->pLeft;
|
|
+ assert( pRight!=0 );
|
|
+ assert( pLeft!=0 );
|
|
+ if( pRight->op==TK_COLUMN
|
|
+ && !ExprHasProperty(pRight, EP_FixedCol)
|
|
+ && sqlite3ExprIsConstant(pLeft)
|
|
+ && sqlite3IsBinary(sqlite3BinaryCompareCollSeq(pConst->pParse,pLeft,pRight))
|
|
+ ){
|
|
+ constInsert(pConst, pRight, pLeft);
|
|
+ }else
|
|
+ if( pLeft->op==TK_COLUMN
|
|
+ && !ExprHasProperty(pLeft, EP_FixedCol)
|
|
+ && sqlite3ExprIsConstant(pRight)
|
|
+ && sqlite3IsBinary(sqlite3BinaryCompareCollSeq(pConst->pParse,pLeft,pRight))
|
|
+ ){
|
|
+ constInsert(pConst, pLeft, pRight);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This is a Walker expression callback. pExpr is a candidate expression
|
|
+** to be replaced by a value. If pExpr is equivalent to one of the
|
|
+** columns named in pWalker->u.pConst, then overwrite it with its
|
|
+** corresponding value.
|
|
+*/
|
|
+static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
|
|
+ int i;
|
|
+ WhereConst *pConst;
|
|
+ if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
|
|
+ if( ExprHasProperty(pExpr, EP_FixedCol) ) return WRC_Continue;
|
|
+ pConst = pWalker->u.pConst;
|
|
+ for(i=0; i<pConst->nConst; i++){
|
|
+ Expr *pColumn = pConst->apExpr[i*2];
|
|
+ if( pColumn==pExpr ) continue;
|
|
+ if( pColumn->iTable!=pExpr->iTable ) continue;
|
|
+ if( pColumn->iColumn!=pExpr->iColumn ) continue;
|
|
+ /* A match is found. Add the EP_FixedCol property */
|
|
+ pConst->nChng++;
|
|
+ ExprClearProperty(pExpr, EP_Leaf);
|
|
+ ExprSetProperty(pExpr, EP_FixedCol);
|
|
+ assert( pExpr->pLeft==0 );
|
|
+ pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0);
|
|
+ break;
|
|
+ }
|
|
+ return WRC_Prune;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The WHERE-clause constant propagation optimization.
|
|
+**
|
|
+** If the WHERE clause contains terms of the form COLUMN=CONSTANT or
|
|
+** CONSTANT=COLUMN that must be tree (in other words, if the terms top-level
|
|
+** AND-connected terms that are not part of a ON clause from a LEFT JOIN)
|
|
+** then throughout the query replace all other occurrences of COLUMN
|
|
+** with CONSTANT within the WHERE clause.
|
|
+**
|
|
+** For example, the query:
|
|
+**
|
|
+** SELECT * FROM t1, t2, t3 WHERE t1.a=39 AND t2.b=t1.a AND t3.c=t2.b
|
|
+**
|
|
+** Is transformed into
|
|
+**
|
|
+** SELECT * FROM t1, t2, t3 WHERE t1.a=39 AND t2.b=39 AND t3.c=39
|
|
+**
|
|
+** Return true if any transformations where made and false if not.
|
|
+**
|
|
+** Implementation note: Constant propagation is tricky due to affinity
|
|
+** and collating sequence interactions. Consider this example:
|
|
+**
|
|
+** CREATE TABLE t1(a INT,b TEXT);
|
|
+** INSERT INTO t1 VALUES(123,'0123');
|
|
+** SELECT * FROM t1 WHERE a=123 AND b=a;
|
|
+** SELECT * FROM t1 WHERE a=123 AND b=123;
|
|
+**
|
|
+** The two SELECT statements above should return different answers. b=a
|
|
+** is alway true because the comparison uses numeric affinity, but b=123
|
|
+** is false because it uses text affinity and '0123' is not the same as '123'.
|
|
+** To work around this, the expression tree is not actually changed from
|
|
+** "b=a" to "b=123" but rather the "a" in "b=a" is tagged with EP_FixedCol
|
|
+** and the "123" value is hung off of the pLeft pointer. Code generator
|
|
+** routines know to generate the constant "123" instead of looking up the
|
|
+** column value. Also, to avoid collation problems, this optimization is
|
|
+** only attempted if the "a=123" term uses the default BINARY collation.
|
|
+*/
|
|
+static int propagateConstants(
|
|
+ Parse *pParse, /* The parsing context */
|
|
+ Select *p /* The query in which to propagate constants */
|
|
+){
|
|
+ WhereConst x;
|
|
+ Walker w;
|
|
+ int nChng = 0;
|
|
+ x.pParse = pParse;
|
|
+ do{
|
|
+ x.nConst = 0;
|
|
+ x.nChng = 0;
|
|
+ x.apExpr = 0;
|
|
+ findConstInWhere(&x, p->pWhere);
|
|
+ if( x.nConst ){
|
|
+ memset(&w, 0, sizeof(w));
|
|
+ w.pParse = pParse;
|
|
+ w.xExprCallback = propagateConstantExprRewrite;
|
|
+ w.xSelectCallback = sqlite3SelectWalkNoop;
|
|
+ w.xSelectCallback2 = 0;
|
|
+ w.walkerDepth = 0;
|
|
+ w.u.pConst = &x;
|
|
+ sqlite3WalkExpr(&w, p->pWhere);
|
|
+ sqlite3DbFree(x.pParse->db, x.apExpr);
|
|
+ nChng += x.nChng;
|
|
+ }
|
|
+ }while( x.nChng );
|
|
+ return nChng;
|
|
+}
|
|
+
|
|
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
|
/*
|
|
** Make copies of relevant WHERE clause terms of the outer query into
|
|
@@ -121284,22 +128175,40 @@
|
|
**
|
|
** Do not attempt this optimization if:
|
|
**
|
|
-** (1) The inner query is an aggregate. (In that case, we'd really want
|
|
-** to copy the outer WHERE-clause terms onto the HAVING clause of the
|
|
-** inner query. But they probably won't help there so do not bother.)
|
|
+** (1) (** This restriction was removed on 2017-09-29. We used to
|
|
+** disallow this optimization for aggregate subqueries, but now
|
|
+** it is allowed by putting the extra terms on the HAVING clause.
|
|
+** The added HAVING clause is pointless if the subquery lacks
|
|
+** a GROUP BY clause. But such a HAVING clause is also harmless
|
|
+** so there does not appear to be any reason to add extra logic
|
|
+** to suppress it. **)
|
|
**
|
|
** (2) The inner query is the recursive part of a common table expression.
|
|
**
|
|
** (3) The inner query has a LIMIT clause (since the changes to the WHERE
|
|
-** close would change the meaning of the LIMIT).
|
|
+** clause would change the meaning of the LIMIT).
|
|
**
|
|
-** (4) The inner query is the right operand of a LEFT JOIN. (The caller
|
|
-** enforces this restriction since this routine does not have enough
|
|
-** information to know.)
|
|
+** (4) The inner query is the right operand of a LEFT JOIN and the
|
|
+** expression to be pushed down does not come from the ON clause
|
|
+** on that LEFT JOIN.
|
|
**
|
|
** (5) The WHERE clause expression originates in the ON or USING clause
|
|
-** of a LEFT JOIN.
|
|
+** of a LEFT JOIN where iCursor is not the right-hand table of that
|
|
+** left join. An example:
|
|
**
|
|
+** SELECT *
|
|
+** FROM (SELECT 1 AS a1 UNION ALL SELECT 2) AS aa
|
|
+** JOIN (SELECT 1 AS b2 UNION ALL SELECT 2) AS bb ON (a1=b2)
|
|
+** LEFT JOIN (SELECT 8 AS c3 UNION ALL SELECT 9) AS cc ON (b2=2);
|
|
+**
|
|
+** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9).
|
|
+** But if the (b2=2) term were to be pushed down into the bb subquery,
|
|
+** then the (1,1,NULL) row would be suppressed.
|
|
+**
|
|
+** (6) The inner query features one or more window-functions (since
|
|
+** changes to the WHERE clause of the inner query could change the
|
|
+** window over which window functions are calculated).
|
|
+**
|
|
** Return 0 if no changes are made and non-zero if one or more WHERE clause
|
|
** terms are duplicated into the subquery.
|
|
*/
|
|
@@ -121307,33 +128216,54 @@
|
|
Parse *pParse, /* Parse context (for malloc() and error reporting) */
|
|
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
|
|
Expr *pWhere, /* The WHERE clause of the outer query */
|
|
- int iCursor /* Cursor number of the subquery */
|
|
+ int iCursor, /* Cursor number of the subquery */
|
|
+ int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
|
|
){
|
|
Expr *pNew;
|
|
int nChng = 0;
|
|
- Select *pX; /* For looping over compound SELECTs in pSubq */
|
|
if( pWhere==0 ) return 0;
|
|
- for(pX=pSubq; pX; pX=pX->pPrior){
|
|
- if( (pX->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
|
|
- testcase( pX->selFlags & SF_Aggregate );
|
|
- testcase( pX->selFlags & SF_Recursive );
|
|
- testcase( pX!=pSubq );
|
|
- return 0; /* restrictions (1) and (2) */
|
|
+ if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pSubq->pWin ) return 0; /* restriction (6) */
|
|
+#endif
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+ /* Only the first term of a compound can have a WITH clause. But make
|
|
+ ** sure no other terms are marked SF_Recursive in case something changes
|
|
+ ** in the future.
|
|
+ */
|
|
+ {
|
|
+ Select *pX;
|
|
+ for(pX=pSubq; pX; pX=pX->pPrior){
|
|
+ assert( (pX->selFlags & (SF_Recursive))==0 );
|
|
}
|
|
}
|
|
+#endif
|
|
+
|
|
if( pSubq->pLimit!=0 ){
|
|
return 0; /* restriction (3) */
|
|
}
|
|
while( pWhere->op==TK_AND ){
|
|
- nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, iCursor);
|
|
+ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight,
|
|
+ iCursor, isLeftJoin);
|
|
pWhere = pWhere->pLeft;
|
|
}
|
|
- if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction 5 */
|
|
+ if( isLeftJoin
|
|
+ && (ExprHasProperty(pWhere,EP_FromJoin)==0
|
|
+ || pWhere->iRightJoinTable!=iCursor)
|
|
+ ){
|
|
+ return 0; /* restriction (4) */
|
|
+ }
|
|
+ if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){
|
|
+ return 0; /* restriction (5) */
|
|
+ }
|
|
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
|
|
nChng++;
|
|
while( pSubq ){
|
|
SubstContext x;
|
|
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
|
|
+ unsetJoinExpr(pNew, -1);
|
|
x.pParse = pParse;
|
|
x.iTable = iCursor;
|
|
x.iNewTable = iCursor;
|
|
@@ -121340,7 +128270,11 @@
|
|
x.isLeftJoin = 0;
|
|
x.pEList = pSubq->pEList;
|
|
pNew = substExpr(&x, pNew);
|
|
- pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew);
|
|
+ if( pSubq->selFlags & SF_Aggregate ){
|
|
+ pSubq->pHaving = sqlite3ExprAnd(pParse->db, pSubq->pHaving, pNew);
|
|
+ }else{
|
|
+ pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew);
|
|
+ }
|
|
pSubq = pSubq->pPrior;
|
|
}
|
|
}
|
|
@@ -121349,42 +128283,44 @@
|
|
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
|
|
|
/*
|
|
-** Based on the contents of the AggInfo structure indicated by the first
|
|
-** argument, this function checks if the following are true:
|
|
+** The pFunc is the only aggregate function in the query. Check to see
|
|
+** if the query is a candidate for the min/max optimization.
|
|
**
|
|
-** * the query contains just a single aggregate function,
|
|
-** * the aggregate function is either min() or max(), and
|
|
-** * the argument to the aggregate function is a column value.
|
|
+** If the query is a candidate for the min/max optimization, then set
|
|
+** *ppMinMax to be an ORDER BY clause to be used for the optimization
|
|
+** and return either WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX depending on
|
|
+** whether pFunc is a min() or max() function.
|
|
**
|
|
-** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
|
|
-** is returned as appropriate. Also, *ppMinMax is set to point to the
|
|
-** list of arguments passed to the aggregate before returning.
|
|
+** If the query is not a candidate for the min/max optimization, return
|
|
+** WHERE_ORDERBY_NORMAL (which must be zero).
|
|
**
|
|
-** Or, if the conditions above are not met, *ppMinMax is set to 0 and
|
|
-** WHERE_ORDERBY_NORMAL is returned.
|
|
+** This routine must be called after aggregate functions have been
|
|
+** located but before their arguments have been subjected to aggregate
|
|
+** analysis.
|
|
*/
|
|
-static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
|
|
- int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
|
|
+static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
|
|
+ int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
|
|
+ ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
|
|
+ const char *zFunc; /* Name of aggregate function pFunc */
|
|
+ ExprList *pOrderBy;
|
|
+ u8 sortOrder;
|
|
|
|
- *ppMinMax = 0;
|
|
- if( pAggInfo->nFunc==1 ){
|
|
- Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
|
|
- ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
|
|
-
|
|
- assert( pExpr->op==TK_AGG_FUNCTION );
|
|
- if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
|
|
- const char *zFunc = pExpr->u.zToken;
|
|
- if( sqlite3StrICmp(zFunc, "min")==0 ){
|
|
- eRet = WHERE_ORDERBY_MIN;
|
|
- *ppMinMax = pEList;
|
|
- }else if( sqlite3StrICmp(zFunc, "max")==0 ){
|
|
- eRet = WHERE_ORDERBY_MAX;
|
|
- *ppMinMax = pEList;
|
|
- }
|
|
- }
|
|
+ assert( *ppMinMax==0 );
|
|
+ assert( pFunc->op==TK_AGG_FUNCTION );
|
|
+ if( pEList==0 || pEList->nExpr!=1 ) return eRet;
|
|
+ zFunc = pFunc->u.zToken;
|
|
+ if( sqlite3StrICmp(zFunc, "min")==0 ){
|
|
+ eRet = WHERE_ORDERBY_MIN;
|
|
+ sortOrder = SQLITE_SO_ASC;
|
|
+ }else if( sqlite3StrICmp(zFunc, "max")==0 ){
|
|
+ eRet = WHERE_ORDERBY_MAX;
|
|
+ sortOrder = SQLITE_SO_DESC;
|
|
+ }else{
|
|
+ return eRet;
|
|
}
|
|
-
|
|
- assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
|
|
+ *ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
|
|
+ assert( pOrderBy!=0 || db->mallocFailed );
|
|
+ if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
|
|
return eRet;
|
|
}
|
|
|
|
@@ -121515,7 +128451,6 @@
|
|
assert( pNew->pPrior!=0 );
|
|
pNew->pPrior->pNext = pNew;
|
|
pNew->pLimit = 0;
|
|
- pNew->pOffset = 0;
|
|
return WRC_Continue;
|
|
}
|
|
|
|
@@ -121668,7 +128603,8 @@
|
|
);
|
|
return SQLITE_ERROR;
|
|
}
|
|
- assert( pTab->nTabRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 ));
|
|
+ assert( pTab->nTabRef==1 ||
|
|
+ ((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 ));
|
|
|
|
pCte->zCteErr = "circular reference: %s";
|
|
pSavedWith = pParse->pWith;
|
|
@@ -121725,7 +128661,7 @@
|
|
*/
|
|
static void selectPopWith(Walker *pWalker, Select *p){
|
|
Parse *pParse = pWalker->pParse;
|
|
- if( pParse->pWith && p->pPrior==0 ){
|
|
+ if( OK_IF_ALWAYS_TRUE(pParse->pWith) && p->pPrior==0 ){
|
|
With *pWith = findRightmost(p)->pWith;
|
|
if( pWith!=0 ){
|
|
assert( pParse->pWith==pWith );
|
|
@@ -121738,6 +128674,35 @@
|
|
#endif
|
|
|
|
/*
|
|
+** The SrcList_item structure passed as the second argument represents a
|
|
+** sub-query in the FROM clause of a SELECT statement. This function
|
|
+** allocates and populates the SrcList_item.pTab object. If successful,
|
|
+** SQLITE_OK is returned. Otherwise, if an OOM error is encountered,
|
|
+** SQLITE_NOMEM.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
|
|
+ Select *pSel = pFrom->pSelect;
|
|
+ Table *pTab;
|
|
+
|
|
+ assert( pSel );
|
|
+ pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table));
|
|
+ if( pTab==0 ) return SQLITE_NOMEM;
|
|
+ pTab->nTabRef = 1;
|
|
+ if( pFrom->zAlias ){
|
|
+ pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias);
|
|
+ }else{
|
|
+ pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId);
|
|
+ }
|
|
+ while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
|
+ sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
|
|
+ pTab->iPKey = -1;
|
|
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
|
+ pTab->tabFlags |= TF_Ephemeral;
|
|
+
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
** This routine is a Walker callback for "expanding" a SELECT statement.
|
|
** "Expanding" means to do the following:
|
|
**
|
|
@@ -121770,19 +128735,19 @@
|
|
sqlite3 *db = pParse->db;
|
|
Expr *pE, *pRight, *pExpr;
|
|
u16 selFlags = p->selFlags;
|
|
+ u32 elistFlags = 0;
|
|
|
|
p->selFlags |= SF_Expanded;
|
|
if( db->mallocFailed ){
|
|
return WRC_Abort;
|
|
}
|
|
- if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){
|
|
+ assert( p->pSrc!=0 );
|
|
+ if( (selFlags & SF_Expanded)!=0 ){
|
|
return WRC_Prune;
|
|
}
|
|
pTabList = p->pSrc;
|
|
pEList = p->pEList;
|
|
- if( p->pWith ){
|
|
- sqlite3WithPush(pParse, p->pWith, 0);
|
|
- }
|
|
+ sqlite3WithPush(pParse, p->pWith, 0);
|
|
|
|
/* Make sure cursor numbers have been assigned to all entries in
|
|
** the FROM clause of the SELECT statement.
|
|
@@ -121809,15 +128774,7 @@
|
|
assert( pSel!=0 );
|
|
assert( pFrom->pTab==0 );
|
|
if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
|
|
- pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
|
- if( pTab==0 ) return WRC_Abort;
|
|
- pTab->nTabRef = 1;
|
|
- pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab);
|
|
- while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
|
- sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
|
|
- pTab->iPKey = -1;
|
|
- pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
|
- pTab->tabFlags |= TF_Ephemeral;
|
|
+ if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort;
|
|
#endif
|
|
}else{
|
|
/* An ordinary table or view name in the FROM clause */
|
|
@@ -121840,7 +128797,6 @@
|
|
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
|
|
assert( pFrom->pSelect==0 );
|
|
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
|
|
- sqlite3SelectSetName(pFrom->pSelect, pTab->zName);
|
|
nCol = pTab->nCol;
|
|
pTab->nCol = -1;
|
|
sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
|
@@ -121878,6 +128834,7 @@
|
|
assert( pE->op!=TK_DOT || pE->pRight!=0 );
|
|
assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
|
|
if( pE->op==TK_DOT && pE->pRight->op==TK_ASTERISK ) break;
|
|
+ elistFlags |= pE->flags;
|
|
}
|
|
if( k<pEList->nExpr ){
|
|
/*
|
|
@@ -121893,6 +128850,7 @@
|
|
|
|
for(k=0; k<pEList->nExpr; k++){
|
|
pE = a[k].pExpr;
|
|
+ elistFlags |= pE->flags;
|
|
pRight = pE->pRight;
|
|
assert( pE->op!=TK_DOT || pRight!=0 );
|
|
if( pE->op!=TK_ASTERISK
|
|
@@ -122022,12 +128980,15 @@
|
|
sqlite3ExprListDelete(db, pEList);
|
|
p->pEList = pNew;
|
|
}
|
|
-#if SQLITE_MAX_COLUMN
|
|
- if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
|
- sqlite3ErrorMsg(pParse, "too many columns in result set");
|
|
- return WRC_Abort;
|
|
+ if( p->pEList ){
|
|
+ if( p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
|
+ sqlite3ErrorMsg(pParse, "too many columns in result set");
|
|
+ return WRC_Abort;
|
|
+ }
|
|
+ if( (elistFlags & (EP_HasFunc|EP_Subquery))!=0 ){
|
|
+ p->selFlags |= SF_ComplexResult;
|
|
+ }
|
|
}
|
|
-#endif
|
|
return WRC_Continue;
|
|
}
|
|
|
|
@@ -122081,7 +129042,7 @@
|
|
Walker w;
|
|
w.xExprCallback = sqlite3ExprWalkNoop;
|
|
w.pParse = pParse;
|
|
- if( pParse->hasCompound ){
|
|
+ if( OK_IF_ALWAYS_TRUE(pParse->hasCompound) ){
|
|
w.xSelectCallback = convertCompoundSelectToSubquery;
|
|
w.xSelectCallback2 = 0;
|
|
sqlite3WalkSelect(&w, pSelect);
|
|
@@ -122113,7 +129074,7 @@
|
|
struct SrcList_item *pFrom;
|
|
|
|
assert( p->selFlags & SF_Resolved );
|
|
- assert( (p->selFlags & SF_HasTypeInfo)==0 );
|
|
+ if( p->selFlags & SF_HasTypeInfo ) return;
|
|
p->selFlags |= SF_HasTypeInfo;
|
|
pParse = pWalker->pParse;
|
|
pTabList = p->pSrc;
|
|
@@ -122169,15 +129130,13 @@
|
|
Select *p, /* The SELECT statement being coded. */
|
|
NameContext *pOuterNC /* Name context for container */
|
|
){
|
|
- sqlite3 *db;
|
|
- if( NEVER(p==0) ) return;
|
|
- db = pParse->db;
|
|
- if( db->mallocFailed ) return;
|
|
+ assert( p!=0 || pParse->db->mallocFailed );
|
|
+ if( pParse->db->mallocFailed ) return;
|
|
if( p->selFlags & SF_HasTypeInfo ) return;
|
|
sqlite3SelectExpand(pParse, p);
|
|
- if( pParse->nErr || db->mallocFailed ) return;
|
|
+ if( pParse->nErr || pParse->db->mallocFailed ) return;
|
|
sqlite3ResolveSelectNames(pParse, p, pOuterNC);
|
|
- if( pParse->nErr || db->mallocFailed ) return;
|
|
+ if( pParse->nErr || pParse->db->mallocFailed ) return;
|
|
sqlite3SelectAddTypeInfo(pParse, p);
|
|
}
|
|
|
|
@@ -122218,7 +129177,7 @@
|
|
"argument");
|
|
pFunc->iDistinct = -1;
|
|
}else{
|
|
- KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0);
|
|
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0);
|
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
|
(char*)pKeyInfo, P4_KEYINFO);
|
|
}
|
|
@@ -122242,11 +129201,17 @@
|
|
}
|
|
}
|
|
|
|
+
|
|
/*
|
|
** Update the accumulator memory cells for an aggregate based on
|
|
** the current cursor position.
|
|
+**
|
|
+** If regAcc is non-zero and there are no min() or max() aggregates
|
|
+** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
|
|
+** registers i register regAcc contains 0. The caller will take care
|
|
+** of setting and clearing regAcc.
|
|
*/
|
|
-static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|
+static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
|
|
Vdbe *v = pParse->pVdbe;
|
|
int i;
|
|
int regHit = 0;
|
|
@@ -122289,36 +129254,24 @@
|
|
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
|
|
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
|
|
}
|
|
- sqlite3VdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem);
|
|
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem);
|
|
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
|
sqlite3VdbeChangeP5(v, (u8)nArg);
|
|
- sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
|
|
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
|
|
if( addrNext ){
|
|
sqlite3VdbeResolveLabel(v, addrNext);
|
|
- sqlite3ExprCacheClear(pParse);
|
|
}
|
|
}
|
|
-
|
|
- /* Before populating the accumulator registers, clear the column cache.
|
|
- ** Otherwise, if any of the required column values are already present
|
|
- ** in registers, sqlite3ExprCode() may use OP_SCopy to copy the value
|
|
- ** to pC->iMem. But by the time the value is used, the original register
|
|
- ** may have been used, invalidating the underlying buffer holding the
|
|
- ** text or blob value. See ticket [883034dcb5].
|
|
- **
|
|
- ** Another solution would be to change the OP_SCopy used to copy cached
|
|
- ** values to an OP_Copy.
|
|
- */
|
|
+ if( regHit==0 && pAggInfo->nAccumulator ){
|
|
+ regHit = regAcc;
|
|
+ }
|
|
if( regHit ){
|
|
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
|
|
}
|
|
- sqlite3ExprCacheClear(pParse);
|
|
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
|
|
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
|
|
}
|
|
pAggInfo->directMode = 0;
|
|
- sqlite3ExprCacheClear(pParse);
|
|
if( addrHitTest ){
|
|
sqlite3VdbeJumpHere(v, addrHitTest);
|
|
}
|
|
@@ -122336,14 +129289,11 @@
|
|
){
|
|
if( pParse->explain==2 ){
|
|
int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
|
|
- char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
|
|
+ sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s",
|
|
pTab->zName,
|
|
bCover ? " USING COVERING INDEX " : "",
|
|
bCover ? pIdx->zName : ""
|
|
);
|
|
- sqlite3VdbeAddOp4(
|
|
- pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
|
|
- );
|
|
}
|
|
}
|
|
#else
|
|
@@ -122351,14 +129301,6 @@
|
|
#endif
|
|
|
|
/*
|
|
-** Context object for havingToWhereExprCb().
|
|
-*/
|
|
-struct HavingToWhereCtx {
|
|
- Expr **ppWhere;
|
|
- ExprList *pGroupBy;
|
|
-};
|
|
-
|
|
-/*
|
|
** sqlite3WalkExpr() callback used by havingToWhere().
|
|
**
|
|
** If the node passed to the callback is a TK_AND node, return
|
|
@@ -122371,15 +129313,16 @@
|
|
*/
|
|
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
|
|
if( pExpr->op!=TK_AND ){
|
|
- struct HavingToWhereCtx *p = pWalker->u.pHavingCtx;
|
|
- if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, p->pGroupBy) ){
|
|
+ Select *pS = pWalker->u.pSelect;
|
|
+ if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
|
|
sqlite3 *db = pWalker->pParse->db;
|
|
Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0);
|
|
if( pNew ){
|
|
- Expr *pWhere = *(p->ppWhere);
|
|
+ Expr *pWhere = pS->pWhere;
|
|
SWAP(Expr, *pNew, *pExpr);
|
|
pNew = sqlite3ExprAnd(db, pWhere, pNew);
|
|
- *(p->ppWhere) = pNew;
|
|
+ pS->pWhere = pNew;
|
|
+ pWalker->eCode = 1;
|
|
}
|
|
}
|
|
return WRC_Prune;
|
|
@@ -122402,23 +129345,19 @@
|
|
** entirely of constants and expressions that are also GROUP BY terms that
|
|
** use the "BINARY" collation sequence.
|
|
*/
|
|
-static void havingToWhere(
|
|
- Parse *pParse,
|
|
- ExprList *pGroupBy,
|
|
- Expr *pHaving,
|
|
- Expr **ppWhere
|
|
-){
|
|
- struct HavingToWhereCtx sCtx;
|
|
+static void havingToWhere(Parse *pParse, Select *p){
|
|
Walker sWalker;
|
|
-
|
|
- sCtx.ppWhere = ppWhere;
|
|
- sCtx.pGroupBy = pGroupBy;
|
|
-
|
|
memset(&sWalker, 0, sizeof(sWalker));
|
|
sWalker.pParse = pParse;
|
|
sWalker.xExprCallback = havingToWhereExprCb;
|
|
- sWalker.u.pHavingCtx = &sCtx;
|
|
- sqlite3WalkExpr(&sWalker, pHaving);
|
|
+ sWalker.u.pSelect = p;
|
|
+ sqlite3WalkExpr(&sWalker, p->pHaving);
|
|
+#if SELECTTRACE_ENABLED
|
|
+ if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){
|
|
+ SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
|
|
+ sqlite3TreeViewSelect(0, p, 0);
|
|
+ }
|
|
+#endif
|
|
}
|
|
|
|
/*
|
|
@@ -122462,6 +129401,7 @@
|
|
** The transformation only works if all of the following are true:
|
|
**
|
|
** * The subquery is a UNION ALL of two or more terms
|
|
+** * The subquery does not have a LIMIT clause
|
|
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
|
|
** * The outer query is a simple count(*)
|
|
**
|
|
@@ -122472,24 +129412,25 @@
|
|
Expr *pExpr;
|
|
Expr *pCount;
|
|
sqlite3 *db;
|
|
- if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate query */
|
|
+ if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */
|
|
if( p->pEList->nExpr!=1 ) return 0; /* Single result column */
|
|
pExpr = p->pEList->a[0].pExpr;
|
|
if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */
|
|
- if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Must be count() */
|
|
+ if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */
|
|
if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */
|
|
- if( p->pSrc->nSrc!=1 ) return 0; /* One table in the FROM clause */
|
|
+ if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */
|
|
pSub = p->pSrc->a[0].pSelect;
|
|
if( pSub==0 ) return 0; /* The FROM is a subquery */
|
|
- if( pSub->pPrior==0 ) return 0; /* Must be a compound subquery */
|
|
+ if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */
|
|
do{
|
|
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
|
|
if( pSub->pWhere ) return 0; /* No WHERE clause */
|
|
+ if( pSub->pLimit ) return 0; /* No LIMIT clause */
|
|
if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
|
|
- pSub = pSub->pPrior; /* Repeat over compound terms */
|
|
+ pSub = pSub->pPrior; /* Repeat over compound */
|
|
}while( pSub );
|
|
|
|
- /* If we reach this point, that means it is OK to perform the transformation */
|
|
+ /* If we reach this point then it is OK to perform the transformation */
|
|
|
|
db = pParse->db;
|
|
pCount = pExpr;
|
|
@@ -122564,13 +129505,11 @@
|
|
AggInfo sAggInfo; /* Information used by aggregate queries */
|
|
int iEnd; /* Address of the end of the query */
|
|
sqlite3 *db; /* The database connection */
|
|
+ ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */
|
|
+ u8 minMaxFlag; /* Flag for min/max queries */
|
|
|
|
-#ifndef SQLITE_OMIT_EXPLAIN
|
|
- int iRestoreSelectId = pParse->iSelectId;
|
|
- pParse->iSelectId = pParse->iNextSelectId++;
|
|
-#endif
|
|
-
|
|
db = pParse->db;
|
|
+ v = sqlite3GetVdbe(pParse);
|
|
if( p==0 || db->mallocFailed || pParse->nErr ){
|
|
return 1;
|
|
}
|
|
@@ -122577,8 +129516,7 @@
|
|
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
|
|
memset(&sAggInfo, 0, sizeof(sAggInfo));
|
|
#if SELECTTRACE_ENABLED
|
|
- pParse->nSelectIndent++;
|
|
- SELECTTRACE(1,pParse,p, ("begin processing:\n"));
|
|
+ SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
|
|
if( sqlite3SelectTrace & 0x100 ){
|
|
sqlite3TreeViewSelect(0, p, 0);
|
|
}
|
|
@@ -122600,37 +129538,60 @@
|
|
p->selFlags &= ~SF_Distinct;
|
|
}
|
|
sqlite3SelectPrep(pParse, p, 0);
|
|
- memset(&sSort, 0, sizeof(sSort));
|
|
- sSort.pOrderBy = p->pOrderBy;
|
|
- pTabList = p->pSrc;
|
|
if( pParse->nErr || db->mallocFailed ){
|
|
goto select_end;
|
|
}
|
|
assert( p->pEList!=0 );
|
|
- isAgg = (p->selFlags & SF_Aggregate)!=0;
|
|
#if SELECTTRACE_ENABLED
|
|
- if( sqlite3SelectTrace & 0x100 ){
|
|
- SELECTTRACE(0x100,pParse,p, ("after name resolution:\n"));
|
|
+ if( sqlite3SelectTrace & 0x104 ){
|
|
+ SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
|
|
sqlite3TreeViewSelect(0, p, 0);
|
|
}
|
|
#endif
|
|
|
|
- /* Get a pointer the VDBE under construction, allocating a new VDBE if one
|
|
- ** does not already exist */
|
|
- v = sqlite3GetVdbe(pParse);
|
|
- if( v==0 ) goto select_end;
|
|
if( pDest->eDest==SRT_Output ){
|
|
generateColumnNames(pParse, p);
|
|
}
|
|
|
|
- /* Try to flatten subqueries in the FROM clause up into the main query
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( sqlite3WindowRewrite(pParse, p) ){
|
|
+ goto select_end;
|
|
+ }
|
|
+#if SELECTTRACE_ENABLED
|
|
+ if( sqlite3SelectTrace & 0x108 ){
|
|
+ SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
|
|
+ sqlite3TreeViewSelect(0, p, 0);
|
|
+ }
|
|
+#endif
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+ pTabList = p->pSrc;
|
|
+ isAgg = (p->selFlags & SF_Aggregate)!=0;
|
|
+ memset(&sSort, 0, sizeof(sSort));
|
|
+ sSort.pOrderBy = p->pOrderBy;
|
|
+
|
|
+ /* Try to various optimizations (flattening subqueries, and strength
|
|
+ ** reduction of join operators) in the FROM clause up into the main query
|
|
*/
|
|
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
|
for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
|
|
struct SrcList_item *pItem = &pTabList->a[i];
|
|
Select *pSub = pItem->pSelect;
|
|
- int isAggSub;
|
|
Table *pTab = pItem->pTab;
|
|
+
|
|
+ /* Convert LEFT JOIN into JOIN if there are terms of the right table
|
|
+ ** of the LEFT JOIN used in the WHERE clause.
|
|
+ */
|
|
+ if( (pItem->fg.jointype & JT_LEFT)!=0
|
|
+ && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
|
|
+ && OptimizationEnabled(db, SQLITE_SimplifyJoin)
|
|
+ ){
|
|
+ SELECTTRACE(0x100,pParse,p,
|
|
+ ("LEFT-JOIN simplifies to JOIN on term %d\n",i));
|
|
+ pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
|
|
+ unsetJoinExpr(p->pWhere, pItem->iCursor);
|
|
+ }
|
|
+
|
|
+ /* No futher action if this term of the FROM clause is no a subquery */
|
|
if( pSub==0 ) continue;
|
|
|
|
/* Catch mismatch in the declared columns of a view and the number of
|
|
@@ -122641,13 +129602,45 @@
|
|
goto select_end;
|
|
}
|
|
|
|
- isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
|
|
- if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
|
|
+ /* Do not try to flatten an aggregate subquery.
|
|
+ **
|
|
+ ** Flattening an aggregate subquery is only possible if the outer query
|
|
+ ** is not a join. But if the outer query is not a join, then the subquery
|
|
+ ** will be implemented as a co-routine and there is no advantage to
|
|
+ ** flattening in that case.
|
|
+ */
|
|
+ if( (pSub->selFlags & SF_Aggregate)!=0 ) continue;
|
|
+ assert( pSub->pGroupBy==0 );
|
|
+
|
|
+ /* If the outer query contains a "complex" result set (that is,
|
|
+ ** if the result set of the outer query uses functions or subqueries)
|
|
+ ** and if the subquery contains an ORDER BY clause and if
|
|
+ ** it will be implemented as a co-routine, then do not flatten. This
|
|
+ ** restriction allows SQL constructs like this:
|
|
+ **
|
|
+ ** SELECT expensive_function(x)
|
|
+ ** FROM (SELECT x FROM tab ORDER BY y LIMIT 10);
|
|
+ **
|
|
+ ** The expensive_function() is only computed on the 10 rows that
|
|
+ ** are output, rather than every row of the table.
|
|
+ **
|
|
+ ** The requirement that the outer query have a complex result set
|
|
+ ** means that flattening does occur on simpler SQL constraints without
|
|
+ ** the expensive_function() like:
|
|
+ **
|
|
+ ** SELECT x FROM (SELECT x FROM tab ORDER BY y LIMIT 10);
|
|
+ */
|
|
+ if( pSub->pOrderBy!=0
|
|
+ && i==0
|
|
+ && (p->selFlags & SF_ComplexResult)!=0
|
|
+ && (pTabList->nSrc==1
|
|
+ || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0)
|
|
+ ){
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if( flattenSubquery(pParse, p, i, isAgg) ){
|
|
/* This subquery can be absorbed into its parent. */
|
|
- if( isAggSub ){
|
|
- isAgg = 1;
|
|
- p->selFlags |= SF_Aggregate;
|
|
- }
|
|
i = -1;
|
|
}
|
|
pTabList = p->pSrc;
|
|
@@ -122664,15 +129657,46 @@
|
|
*/
|
|
if( p->pPrior ){
|
|
rc = multiSelect(pParse, p, pDest);
|
|
- explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
|
#if SELECTTRACE_ENABLED
|
|
- SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
|
|
- pParse->nSelectIndent--;
|
|
+ SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
|
|
+ if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
|
+ sqlite3TreeViewSelect(0, p, 0);
|
|
+ }
|
|
#endif
|
|
+ if( p->pNext==0 ) ExplainQueryPlanPop(pParse);
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
+ /* Do the WHERE-clause constant propagation optimization if this is
|
|
+ ** a join. No need to speed time on this operation for non-join queries
|
|
+ ** as the equivalent optimization will be handled by query planner in
|
|
+ ** sqlite3WhereBegin().
|
|
+ */
|
|
+ if( pTabList->nSrc>1
|
|
+ && OptimizationEnabled(db, SQLITE_PropagateConst)
|
|
+ && propagateConstants(pParse, p)
|
|
+ ){
|
|
+#if SELECTTRACE_ENABLED
|
|
+ if( sqlite3SelectTrace & 0x100 ){
|
|
+ SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
|
|
+ sqlite3TreeViewSelect(0, p, 0);
|
|
+ }
|
|
+#endif
|
|
+ }else{
|
|
+ SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
|
|
+ }
|
|
+
|
|
+#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
|
|
+ if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView)
|
|
+ && countOfViewOptimization(pParse, p)
|
|
+ ){
|
|
+ if( db->mallocFailed ) goto select_end;
|
|
+ pEList = p->pEList;
|
|
+ pTabList = p->pSrc;
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* For each term in the FROM clause, do two things:
|
|
** (1) Authorized unreferenced tables
|
|
** (2) Generate code for all sub-queries
|
|
@@ -122681,10 +129705,14 @@
|
|
struct SrcList_item *pItem = &pTabList->a[i];
|
|
SelectDest dest;
|
|
Select *pSub;
|
|
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
|
+ const char *zSavedAuthContext;
|
|
+#endif
|
|
|
|
- /* Issue SQLITE_READ authorizations with a fake column name for any tables that
|
|
- ** are referenced but from which no values are extracted. Examples of where these
|
|
- ** kinds of null SQLITE_READ authorizations would occur:
|
|
+ /* Issue SQLITE_READ authorizations with a fake column name for any
|
|
+ ** tables that are referenced but from which no values are extracted.
|
|
+ ** Examples of where these kinds of null SQLITE_READ authorizations
|
|
+ ** would occur:
|
|
**
|
|
** SELECT count(*) FROM t1; -- SQLITE_READ t1.""
|
|
** SELECT t1.* FROM t1, t2; -- SQLITE_READ t2.""
|
|
@@ -122692,10 +129720,10 @@
|
|
** The fake column name is an empty string. It is possible for a table to
|
|
** have a column named by the empty string, in which case there is no way to
|
|
** distinguish between an unreferenced table and an actual reference to the
|
|
- ** "" column. The original design was for the fake column name to be a NULL,
|
|
+ ** "" column. The original design was for the fake column name to be a NULL,
|
|
** which would be unambiguous. But legacy authorization callbacks might
|
|
- ** assume the column name is non-NULL and segfault. The use of an empty string
|
|
- ** for the fake column name seems safer.
|
|
+ ** assume the column name is non-NULL and segfault. The use of an empty
|
|
+ ** string for the fake column name seems safer.
|
|
*/
|
|
if( pItem->colUsed==0 ){
|
|
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase);
|
|
@@ -122736,27 +129764,29 @@
|
|
/* Make copies of constant WHERE-clause terms in the outer query down
|
|
** inside the subquery. This can help the subquery to run more efficiently.
|
|
*/
|
|
- if( (pItem->fg.jointype & JT_OUTER)==0
|
|
- && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor)
|
|
+ if( OptimizationEnabled(db, SQLITE_PushDown)
|
|
+ && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
|
|
+ (pItem->fg.jointype & JT_OUTER)!=0)
|
|
){
|
|
#if SELECTTRACE_ENABLED
|
|
if( sqlite3SelectTrace & 0x100 ){
|
|
- SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n"));
|
|
+ SELECTTRACE(0x100,pParse,p,
|
|
+ ("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
|
|
sqlite3TreeViewSelect(0, p, 0);
|
|
}
|
|
#endif
|
|
+ }else{
|
|
+ SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
|
|
}
|
|
|
|
+ zSavedAuthContext = pParse->zAuthContext;
|
|
+ pParse->zAuthContext = pItem->zName;
|
|
+
|
|
/* Generate code to implement the subquery
|
|
**
|
|
- ** The subquery is implemented as a co-routine if all of these are true:
|
|
- ** (1) The subquery is guaranteed to be the outer loop (so that it
|
|
- ** does not need to be computed more than once)
|
|
- ** (2) The ALL keyword after SELECT is omitted. (Applications are
|
|
- ** allowed to say "SELECT ALL" instead of just "SELECT" to disable
|
|
- ** the use of co-routines.)
|
|
- ** (3) Co-routines are not disabled using sqlite3_test_control()
|
|
- ** with SQLITE_TESTCTRL_OPTIMIZATIONS.
|
|
+ ** The subquery is implemented as a co-routine if the subquery is
|
|
+ ** guaranteed to be the outer loop (so that it does not need to be
|
|
+ ** computed more than once)
|
|
**
|
|
** TODO: Are there other reasons beside (1) to use a co-routine
|
|
** implementation?
|
|
@@ -122764,19 +129794,18 @@
|
|
if( i==0
|
|
&& (pTabList->nSrc==1
|
|
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
|
|
- && (p->selFlags & SF_All)==0 /* (2) */
|
|
- && OptimizationEnabled(db, SQLITE_SubqCoroutine) /* (3) */
|
|
){
|
|
/* Implement a co-routine that will return a single row of the result
|
|
** set on each invocation.
|
|
*/
|
|
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
|
|
+
|
|
pItem->regReturn = ++pParse->nMem;
|
|
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
|
|
VdbeComment((v, "%s", pItem->pTab->zName));
|
|
pItem->addrFillSub = addrTop;
|
|
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
|
- explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
|
+ ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId));
|
|
sqlite3Select(pParse, pSub, &dest);
|
|
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
|
pItem->fg.viaCoroutine = 1;
|
|
@@ -122811,12 +129840,11 @@
|
|
pPrior = isSelfJoinView(pTabList, pItem);
|
|
if( pPrior ){
|
|
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
|
|
- explainSetInteger(pItem->iSelectId, pPrior->iSelectId);
|
|
assert( pPrior->pSelect!=0 );
|
|
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
|
|
}else{
|
|
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
|
- explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
|
+ ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
|
|
sqlite3Select(pParse, pSub, &dest);
|
|
}
|
|
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
|
@@ -122828,6 +129856,7 @@
|
|
}
|
|
if( db->mallocFailed ) goto select_end;
|
|
pParse->nHeight -= sqlite3SelectExprHeight(p);
|
|
+ pParse->zAuthContext = zSavedAuthContext;
|
|
#endif
|
|
}
|
|
|
|
@@ -122846,16 +129875,6 @@
|
|
}
|
|
#endif
|
|
|
|
-#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
|
|
- if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView)
|
|
- && countOfViewOptimization(pParse, p)
|
|
- ){
|
|
- if( db->mallocFailed ) goto select_end;
|
|
- pEList = p->pEList;
|
|
- pTabList = p->pSrc;
|
|
- }
|
|
-#endif
|
|
-
|
|
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
|
|
** if the select-list is the same as the ORDER BY list, then this query
|
|
** can be rewritten as a GROUP BY. In other words, this:
|
|
@@ -122899,7 +129918,8 @@
|
|
*/
|
|
if( sSort.pOrderBy ){
|
|
KeyInfo *pKeyInfo;
|
|
- pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr);
|
|
+ pKeyInfo = sqlite3KeyInfoFromExprList(
|
|
+ pParse, sSort.pOrderBy, 0, pEList->nExpr);
|
|
sSort.iECursor = pParse->nTab++;
|
|
sSort.addrSortIndex =
|
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
|
@@ -122933,9 +129953,9 @@
|
|
if( p->selFlags & SF_Distinct ){
|
|
sDistinct.tabTnct = pParse->nTab++;
|
|
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
|
- sDistinct.tabTnct, 0, 0,
|
|
- (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
|
|
- P4_KEYINFO);
|
|
+ sDistinct.tabTnct, 0, 0,
|
|
+ (char*)sqlite3KeyInfoFromExprList(pParse, p->pEList,0,0),
|
|
+ P4_KEYINFO);
|
|
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
|
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
|
}else{
|
|
@@ -122944,11 +129964,19 @@
|
|
|
|
if( !isAgg && pGroupBy==0 ){
|
|
/* No aggregate functions and no GROUP BY clause */
|
|
- u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
|
|
+ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0)
|
|
+ | (p->selFlags & SF_FixedLimit);
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ Window *pWin = p->pWin; /* Master window object (or NULL) */
|
|
+ if( pWin ){
|
|
+ sqlite3WindowCodeInit(pParse, pWin);
|
|
+ }
|
|
+#endif
|
|
assert( WHERE_USE_LIMIT==SF_FixedLimit );
|
|
- wctrlFlags |= p->selFlags & SF_FixedLimit;
|
|
|
|
+
|
|
/* Begin the database scan. */
|
|
+ SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
|
|
p->pEList, wctrlFlags, p->nSelectRow);
|
|
if( pWInfo==0 ) goto select_end;
|
|
@@ -122960,7 +129988,7 @@
|
|
}
|
|
if( sSort.pOrderBy ){
|
|
sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
|
|
- sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo);
|
|
+ sSort.labelOBLopt = sqlite3WhereOrderByLimitOptLabel(pWInfo);
|
|
if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
|
|
sSort.pOrderBy = 0;
|
|
}
|
|
@@ -122974,14 +130002,37 @@
|
|
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
|
|
}
|
|
|
|
- /* Use the standard inner loop. */
|
|
- selectInnerLoop(pParse, p, pEList, -1, &sSort, &sDistinct, pDest,
|
|
- sqlite3WhereContinueLabel(pWInfo),
|
|
- sqlite3WhereBreakLabel(pWInfo));
|
|
+ assert( p->pEList==pEList );
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( pWin ){
|
|
+ int addrGosub = sqlite3VdbeMakeLabel(v);
|
|
+ int iCont = sqlite3VdbeMakeLabel(v);
|
|
+ int iBreak = sqlite3VdbeMakeLabel(v);
|
|
+ int regGosub = ++pParse->nMem;
|
|
|
|
- /* End the database scan loop.
|
|
- */
|
|
- sqlite3WhereEnd(pWInfo);
|
|
+ sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
|
|
+
|
|
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
|
|
+ sqlite3VdbeResolveLabel(v, addrGosub);
|
|
+ VdbeNoopComment((v, "inner-loop subroutine"));
|
|
+ sSort.labelOBLopt = 0;
|
|
+ selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, iCont, iBreak);
|
|
+ sqlite3VdbeResolveLabel(v, iCont);
|
|
+ sqlite3VdbeAddOp1(v, OP_Return, regGosub);
|
|
+ VdbeComment((v, "end inner-loop subroutine"));
|
|
+ sqlite3VdbeResolveLabel(v, iBreak);
|
|
+ }else
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+ {
|
|
+ /* Use the standard inner loop. */
|
|
+ selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest,
|
|
+ sqlite3WhereContinueLabel(pWInfo),
|
|
+ sqlite3WhereBreakLabel(pWInfo));
|
|
+
|
|
+ /* End the database scan loop.
|
|
+ */
|
|
+ sqlite3WhereEnd(pWInfo);
|
|
+ }
|
|
}else{
|
|
/* This case when there exist aggregate functions or a GROUP BY clause
|
|
** or both */
|
|
@@ -123040,7 +130091,8 @@
|
|
memset(&sNC, 0, sizeof(sNC));
|
|
sNC.pParse = pParse;
|
|
sNC.pSrcList = pTabList;
|
|
- sNC.pAggInfo = &sAggInfo;
|
|
+ sNC.uNC.pAggInfo = &sAggInfo;
|
|
+ VVA_ONLY( sNC.ncFlags = NC_UAggInfo; )
|
|
sAggInfo.mnReg = pParse->nMem+1;
|
|
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
|
|
sAggInfo.pGroupBy = pGroupBy;
|
|
@@ -123049,12 +130101,19 @@
|
|
if( pHaving ){
|
|
if( pGroupBy ){
|
|
assert( pWhere==p->pWhere );
|
|
- havingToWhere(pParse, pGroupBy, pHaving, &p->pWhere);
|
|
+ assert( pHaving==p->pHaving );
|
|
+ assert( pGroupBy==p->pGroupBy );
|
|
+ havingToWhere(pParse, p);
|
|
pWhere = p->pWhere;
|
|
}
|
|
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
|
|
}
|
|
sAggInfo.nAccumulator = sAggInfo.nColumn;
|
|
+ if( p->pGroupBy==0 && p->pHaving==0 && sAggInfo.nFunc==1 ){
|
|
+ minMaxFlag = minMaxQuery(db, sAggInfo.aFunc[0].pExpr, &pMinMaxOrderBy);
|
|
+ }else{
|
|
+ minMaxFlag = WHERE_ORDERBY_NORMAL;
|
|
+ }
|
|
for(i=0; i<sAggInfo.nFunc; i++){
|
|
assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
|
|
sNC.ncFlags |= NC_InAggFunc;
|
|
@@ -123063,7 +130122,25 @@
|
|
}
|
|
sAggInfo.mxReg = pParse->nMem;
|
|
if( db->mallocFailed ) goto select_end;
|
|
+#if SELECTTRACE_ENABLED
|
|
+ if( sqlite3SelectTrace & 0x400 ){
|
|
+ int ii;
|
|
+ SELECTTRACE(0x400,pParse,p,("After aggregate analysis:\n"));
|
|
+ sqlite3TreeViewSelect(0, p, 0);
|
|
+ for(ii=0; ii<sAggInfo.nColumn; ii++){
|
|
+ sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
|
|
+ ii, sAggInfo.aCol[ii].iMem);
|
|
+ sqlite3TreeViewExpr(0, sAggInfo.aCol[ii].pExpr, 0);
|
|
+ }
|
|
+ for(ii=0; ii<sAggInfo.nFunc; ii++){
|
|
+ sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
|
|
+ ii, sAggInfo.aFunc[ii].iMem);
|
|
+ sqlite3TreeViewExpr(0, sAggInfo.aFunc[ii].pExpr, 0);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
|
|
+
|
|
/* Processing for aggregates with GROUP BY is very different and
|
|
** much more complex than aggregates without a GROUP BY.
|
|
*/
|
|
@@ -123084,7 +130161,7 @@
|
|
** will be converted into a Noop.
|
|
*/
|
|
sAggInfo.sortingIdx = pParse->nTab++;
|
|
- pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn);
|
|
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn);
|
|
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
|
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
|
0, (char*)pKeyInfo, P4_KEYINFO);
|
|
@@ -123103,8 +130180,6 @@
|
|
pParse->nMem += pGroupBy->nExpr;
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
|
|
VdbeComment((v, "clear abort flag"));
|
|
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
|
|
- VdbeComment((v, "indicate accumulator empty"));
|
|
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
|
|
|
|
/* Begin a loop that will extract all source rows in GROUP BY order.
|
|
@@ -123113,6 +130188,7 @@
|
|
** in the right order to begin with.
|
|
*/
|
|
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
|
+ SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
|
|
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
|
|
);
|
|
@@ -123149,7 +130225,6 @@
|
|
}
|
|
}
|
|
regBase = sqlite3GetTempRange(pParse, nCol);
|
|
- sqlite3ExprCacheClear(pParse);
|
|
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
|
|
j = nGroupBy;
|
|
for(i=0; i<sAggInfo.nColumn; i++){
|
|
@@ -123156,8 +130231,8 @@
|
|
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
|
|
if( pCol->iSorterColumn>=j ){
|
|
int r1 = j + regBase;
|
|
- sqlite3ExprCodeGetColumnToReg(pParse,
|
|
- pCol->pTab, pCol->iColumn, pCol->iTable, r1);
|
|
+ sqlite3ExprCodeGetColumnOfTable(v,
|
|
+ pCol->pTab, pCol->iTable, pCol->iColumn, r1);
|
|
j++;
|
|
}
|
|
}
|
|
@@ -123173,8 +130248,6 @@
|
|
sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
|
|
VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
|
|
sAggInfo.useSortingIdx = 1;
|
|
- sqlite3ExprCacheClear(pParse);
|
|
-
|
|
}
|
|
|
|
/* If the index or temporary table used by the GROUP BY sort
|
|
@@ -123197,7 +130270,6 @@
|
|
** from the previous row currently stored in a0, a1, a2...
|
|
*/
|
|
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
|
|
- sqlite3ExprCacheClear(pParse);
|
|
if( groupBySort ){
|
|
sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx,
|
|
sortOut, sortPTab);
|
|
@@ -123236,7 +130308,7 @@
|
|
** the current row
|
|
*/
|
|
sqlite3VdbeJumpHere(v, addr1);
|
|
- updateAccumulator(pParse, &sAggInfo);
|
|
+ updateAccumulator(pParse, iUseFlag, &sAggInfo);
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
|
|
VdbeComment((v, "indicate data in accumulator"));
|
|
|
|
@@ -123278,7 +130350,7 @@
|
|
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
|
finalizeAggFunctions(pParse, &sAggInfo);
|
|
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
|
|
- selectInnerLoop(pParse, p, p->pEList, -1, &sSort,
|
|
+ selectInnerLoop(pParse, p, -1, &sSort,
|
|
&sDistinct, pDest,
|
|
addrOutputRow+1, addrSetAbort);
|
|
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
|
@@ -123288,11 +130360,12 @@
|
|
*/
|
|
sqlite3VdbeResolveLabel(v, addrReset);
|
|
resetAccumulator(pParse, &sAggInfo);
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
|
|
+ VdbeComment((v, "indicate accumulator empty"));
|
|
sqlite3VdbeAddOp1(v, OP_Return, regReset);
|
|
|
|
} /* endif pGroupBy. Begin aggregate queries without GROUP BY: */
|
|
else {
|
|
- ExprList *pDel = 0;
|
|
#ifndef SQLITE_OMIT_BTREECOUNT
|
|
Table *pTab;
|
|
if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
|
|
@@ -123354,67 +130427,50 @@
|
|
}else
|
|
#endif /* SQLITE_OMIT_BTREECOUNT */
|
|
{
|
|
- /* Check if the query is of one of the following forms:
|
|
- **
|
|
- ** SELECT min(x) FROM ...
|
|
- ** SELECT max(x) FROM ...
|
|
- **
|
|
- ** If it is, then ask the code in where.c to attempt to sort results
|
|
- ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause.
|
|
- ** If where.c is able to produce results sorted in this order, then
|
|
- ** add vdbe code to break out of the processing loop after the
|
|
- ** first iteration (since the first iteration of the loop is
|
|
- ** guaranteed to operate on the row with the minimum or maximum
|
|
- ** value of x, the only row required).
|
|
- **
|
|
- ** A special flag must be passed to sqlite3WhereBegin() to slightly
|
|
- ** modify behavior as follows:
|
|
- **
|
|
- ** + If the query is a "SELECT min(x)", then the loop coded by
|
|
- ** where.c should not iterate over any values with a NULL value
|
|
- ** for x.
|
|
- **
|
|
- ** + The optimizer code in where.c (the thing that decides which
|
|
- ** index or indices to use) should place a different priority on
|
|
- ** satisfying the 'ORDER BY' clause than it does in other cases.
|
|
- ** Refer to code and comments in where.c for details.
|
|
- */
|
|
- ExprList *pMinMax = 0;
|
|
- u8 flag = WHERE_ORDERBY_NORMAL;
|
|
-
|
|
- assert( p->pGroupBy==0 );
|
|
- assert( flag==0 );
|
|
- if( p->pHaving==0 ){
|
|
- flag = minMaxQuery(&sAggInfo, &pMinMax);
|
|
- }
|
|
- assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) );
|
|
+ int regAcc = 0; /* "populate accumulators" flag */
|
|
|
|
- if( flag ){
|
|
- pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
|
|
- pDel = pMinMax;
|
|
- assert( db->mallocFailed || pMinMax!=0 );
|
|
- if( !db->mallocFailed ){
|
|
- pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
|
|
- pMinMax->a[0].pExpr->op = TK_COLUMN;
|
|
+ /* If there are accumulator registers but no min() or max() functions,
|
|
+ ** allocate register regAcc. Register regAcc will contain 0 the first
|
|
+ ** time the inner loop runs, and 1 thereafter. The code generated
|
|
+ ** by updateAccumulator() only updates the accumulator registers if
|
|
+ ** regAcc contains 0. */
|
|
+ if( sAggInfo.nAccumulator ){
|
|
+ for(i=0; i<sAggInfo.nFunc; i++){
|
|
+ if( sAggInfo.aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ) break;
|
|
}
|
|
+ if( i==sAggInfo.nFunc ){
|
|
+ regAcc = ++pParse->nMem;
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc);
|
|
+ }
|
|
}
|
|
-
|
|
+
|
|
/* This case runs if the aggregate has no GROUP BY clause. The
|
|
** processing is much simpler since there is only a single row
|
|
** of output.
|
|
*/
|
|
+ assert( p->pGroupBy==0 );
|
|
resetAccumulator(pParse, &sAggInfo);
|
|
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax, 0,flag,0);
|
|
+
|
|
+ /* If this query is a candidate for the min/max optimization, then
|
|
+ ** minMaxFlag will have been previously set to either
|
|
+ ** WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX and pMinMaxOrderBy will
|
|
+ ** be an appropriate ORDER BY expression for the optimization.
|
|
+ */
|
|
+ assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
|
|
+ assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
|
|
+
|
|
+ SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
|
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
|
|
+ 0, minMaxFlag, 0);
|
|
if( pWInfo==0 ){
|
|
- sqlite3ExprListDelete(db, pDel);
|
|
goto select_end;
|
|
}
|
|
- updateAccumulator(pParse, &sAggInfo);
|
|
- assert( pMinMax==0 || pMinMax->nExpr==1 );
|
|
+ updateAccumulator(pParse, regAcc, &sAggInfo);
|
|
+ if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
|
|
if( sqlite3WhereIsOrdered(pWInfo)>0 ){
|
|
sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo));
|
|
VdbeComment((v, "%s() by index",
|
|
- (flag==WHERE_ORDERBY_MIN?"min":"max")));
|
|
+ (minMaxFlag==WHERE_ORDERBY_MIN?"min":"max")));
|
|
}
|
|
sqlite3WhereEnd(pWInfo);
|
|
finalizeAggFunctions(pParse, &sAggInfo);
|
|
@@ -123422,9 +130478,8 @@
|
|
|
|
sSort.pOrderBy = 0;
|
|
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
|
|
- selectInnerLoop(pParse, p, p->pEList, -1, 0, 0,
|
|
+ selectInnerLoop(pParse, p, -1, 0, 0,
|
|
pDest, addrEnd, addrEnd);
|
|
- sqlite3ExprListDelete(db, pDel);
|
|
}
|
|
sqlite3VdbeResolveLabel(v, addrEnd);
|
|
|
|
@@ -123440,6 +130495,7 @@
|
|
if( sSort.pOrderBy ){
|
|
explainTempTable(pParse,
|
|
sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
|
|
+ assert( p->pEList==pEList );
|
|
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
|
|
}
|
|
|
|
@@ -123455,14 +130511,16 @@
|
|
** successful coding of the SELECT.
|
|
*/
|
|
select_end:
|
|
- explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
|
-
|
|
+ sqlite3ExprListDelete(db, pMinMaxOrderBy);
|
|
sqlite3DbFree(db, sAggInfo.aCol);
|
|
sqlite3DbFree(db, sAggInfo.aFunc);
|
|
#if SELECTTRACE_ENABLED
|
|
- SELECTTRACE(1,pParse,p,("end processing\n"));
|
|
- pParse->nSelectIndent--;
|
|
+ SELECTTRACE(0x1,pParse,p,("end processing\n"));
|
|
+ if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
|
+ sqlite3TreeViewSelect(0, p, 0);
|
|
+ }
|
|
#endif
|
|
+ ExplainQueryPlanPop(pParse);
|
|
return rc;
|
|
}
|
|
|
|
@@ -123696,6 +130754,8 @@
|
|
sqlite3ExprListDelete(db, pTmp->pExprList);
|
|
sqlite3SelectDelete(db, pTmp->pSelect);
|
|
sqlite3IdListDelete(db, pTmp->pIdList);
|
|
+ sqlite3UpsertDelete(db, pTmp->pUpsert);
|
|
+ sqlite3DbFree(db, pTmp->zSpan);
|
|
|
|
sqlite3DbFree(db, pTmp);
|
|
}
|
|
@@ -123850,14 +130910,16 @@
|
|
goto trigger_cleanup;
|
|
}
|
|
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
|
- if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
|
|
- if( !noErr ){
|
|
- sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
|
|
- }else{
|
|
- assert( !db->init.busy );
|
|
- sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ if( !IN_RENAME_OBJECT ){
|
|
+ if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
|
|
+ if( !noErr ){
|
|
+ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
|
|
+ }else{
|
|
+ assert( !db->init.busy );
|
|
+ sqlite3CodeVerifySchema(pParse, iDb);
|
|
+ }
|
|
+ goto trigger_cleanup;
|
|
}
|
|
- goto trigger_cleanup;
|
|
}
|
|
|
|
/* Do not create a trigger on a system table */
|
|
@@ -123881,7 +130943,7 @@
|
|
}
|
|
|
|
#ifndef SQLITE_OMIT_AUTHORIZATION
|
|
- {
|
|
+ if( !IN_RENAME_OBJECT ){
|
|
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
|
int code = SQLITE_CREATE_TRIGGER;
|
|
const char *zDb = db->aDb[iTabDb].zDbSName;
|
|
@@ -123915,8 +130977,15 @@
|
|
pTrigger->pTabSchema = pTab->pSchema;
|
|
pTrigger->op = (u8)op;
|
|
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
|
|
- pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
|
|
- pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
|
|
+ pTrigger->pWhen = pWhen;
|
|
+ pWhen = 0;
|
|
+ }else{
|
|
+ pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
|
|
+ }
|
|
+ pTrigger->pColumns = pColumns;
|
|
+ pColumns = 0;
|
|
assert( pParse->pNewTrigger==0 );
|
|
pParse->pNewTrigger = pTrigger;
|
|
|
|
@@ -123965,6 +131034,14 @@
|
|
goto triggerfinish_cleanup;
|
|
}
|
|
|
|
+#ifndef SQLITE_OMIT_ALTERTABLE
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ assert( !db->init.busy );
|
|
+ pParse->pNewTrigger = pTrig;
|
|
+ pTrig = 0;
|
|
+ }else
|
|
+#endif
|
|
+
|
|
/* if we are not initializing,
|
|
** build the sqlite_master entry
|
|
*/
|
|
@@ -124006,11 +131083,22 @@
|
|
|
|
triggerfinish_cleanup:
|
|
sqlite3DeleteTrigger(db, pTrig);
|
|
- assert( !pParse->pNewTrigger );
|
|
+ assert( IN_RENAME_OBJECT || !pParse->pNewTrigger );
|
|
sqlite3DeleteTriggerStep(db, pStepList);
|
|
}
|
|
|
|
/*
|
|
+** Duplicate a range of text from an SQL statement, then convert all
|
|
+** whitespace characters into ordinary space characters.
|
|
+*/
|
|
+static char *triggerSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
|
+ char *z = sqlite3DbSpanDup(db, zStart, zEnd);
|
|
+ int i;
|
|
+ if( z ) for(i=0; z[i]; i++) if( sqlite3Isspace(z[i]) ) z[i] = ' ';
|
|
+ return z;
|
|
+}
|
|
+
|
|
+/*
|
|
** Turn a SELECT statement (that the pSelect parameter points to) into
|
|
** a trigger step. Return a pointer to a TriggerStep structure.
|
|
**
|
|
@@ -124017,7 +131105,12 @@
|
|
** The parser calls this routine when it finds a SELECT statement in
|
|
** body of a TRIGGER.
|
|
*/
|
|
-SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
|
+SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(
|
|
+ sqlite3 *db, /* Database connection */
|
|
+ Select *pSelect, /* The SELECT statement */
|
|
+ const char *zStart, /* Start of SQL text */
|
|
+ const char *zEnd /* End of SQL text */
|
|
+){
|
|
TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
|
|
if( pTriggerStep==0 ) {
|
|
sqlite3SelectDelete(db, pSelect);
|
|
@@ -124026,6 +131119,7 @@
|
|
pTriggerStep->op = TK_SELECT;
|
|
pTriggerStep->pSelect = pSelect;
|
|
pTriggerStep->orconf = OE_Default;
|
|
+ pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
|
return pTriggerStep;
|
|
}
|
|
|
|
@@ -124036,10 +131130,13 @@
|
|
** If an OOM error occurs, NULL is returned and db->mallocFailed is set.
|
|
*/
|
|
static TriggerStep *triggerStepAllocate(
|
|
- sqlite3 *db, /* Database connection */
|
|
+ Parse *pParse, /* Parser context */
|
|
u8 op, /* Trigger opcode */
|
|
- Token *pName /* The target name */
|
|
+ Token *pName, /* The target name */
|
|
+ const char *zStart, /* Start of SQL text */
|
|
+ const char *zEnd /* End of SQL text */
|
|
){
|
|
+ sqlite3 *db = pParse->db;
|
|
TriggerStep *pTriggerStep;
|
|
|
|
pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
|
|
@@ -124049,6 +131146,10 @@
|
|
sqlite3Dequote(z);
|
|
pTriggerStep->zTarget = z;
|
|
pTriggerStep->op = op;
|
|
+ pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
|
|
+ }
|
|
}
|
|
return pTriggerStep;
|
|
}
|
|
@@ -124061,23 +131162,36 @@
|
|
** body of a trigger.
|
|
*/
|
|
SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(
|
|
- sqlite3 *db, /* The database connection */
|
|
+ Parse *pParse, /* Parser */
|
|
Token *pTableName, /* Name of the table into which we insert */
|
|
IdList *pColumn, /* List of columns in pTableName to insert into */
|
|
Select *pSelect, /* A SELECT statement that supplies values */
|
|
- u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
|
+ u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
|
+ Upsert *pUpsert, /* ON CONFLICT clauses for upsert */
|
|
+ const char *zStart, /* Start of SQL text */
|
|
+ const char *zEnd /* End of SQL text */
|
|
){
|
|
+ sqlite3 *db = pParse->db;
|
|
TriggerStep *pTriggerStep;
|
|
|
|
assert(pSelect != 0 || db->mallocFailed);
|
|
|
|
- pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName);
|
|
+ pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
|
|
if( pTriggerStep ){
|
|
- pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ pTriggerStep->pSelect = pSelect;
|
|
+ pSelect = 0;
|
|
+ }else{
|
|
+ pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
|
+ }
|
|
pTriggerStep->pIdList = pColumn;
|
|
+ pTriggerStep->pUpsert = pUpsert;
|
|
pTriggerStep->orconf = orconf;
|
|
}else{
|
|
+ testcase( pColumn );
|
|
sqlite3IdListDelete(db, pColumn);
|
|
+ testcase( pUpsert );
|
|
+ sqlite3UpsertDelete(db, pUpsert);
|
|
}
|
|
sqlite3SelectDelete(db, pSelect);
|
|
|
|
@@ -124090,18 +131204,28 @@
|
|
** sees an UPDATE statement inside the body of a CREATE TRIGGER.
|
|
*/
|
|
SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(
|
|
- sqlite3 *db, /* The database connection */
|
|
+ Parse *pParse, /* Parser */
|
|
Token *pTableName, /* Name of the table to be updated */
|
|
ExprList *pEList, /* The SET clause: list of column and new values */
|
|
Expr *pWhere, /* The WHERE clause */
|
|
- u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
|
+ u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
|
+ const char *zStart, /* Start of SQL text */
|
|
+ const char *zEnd /* End of SQL text */
|
|
){
|
|
+ sqlite3 *db = pParse->db;
|
|
TriggerStep *pTriggerStep;
|
|
|
|
- pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName);
|
|
+ pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
|
|
if( pTriggerStep ){
|
|
- pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
|
- pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ pTriggerStep->pExprList = pEList;
|
|
+ pTriggerStep->pWhere = pWhere;
|
|
+ pEList = 0;
|
|
+ pWhere = 0;
|
|
+ }else{
|
|
+ pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
|
+ pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
|
+ }
|
|
pTriggerStep->orconf = orconf;
|
|
}
|
|
sqlite3ExprListDelete(db, pEList);
|
|
@@ -124115,15 +131239,23 @@
|
|
** sees a DELETE statement inside the body of a CREATE TRIGGER.
|
|
*/
|
|
SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(
|
|
- sqlite3 *db, /* Database connection */
|
|
+ Parse *pParse, /* Parser */
|
|
Token *pTableName, /* The table from which rows are deleted */
|
|
- Expr *pWhere /* The WHERE clause */
|
|
+ Expr *pWhere, /* The WHERE clause */
|
|
+ const char *zStart, /* Start of SQL text */
|
|
+ const char *zEnd /* End of SQL text */
|
|
){
|
|
+ sqlite3 *db = pParse->db;
|
|
TriggerStep *pTriggerStep;
|
|
|
|
- pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName);
|
|
+ pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
|
|
if( pTriggerStep ){
|
|
- pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ pTriggerStep->pWhere = pWhere;
|
|
+ pWhere = 0;
|
|
+ }else{
|
|
+ pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
|
+ }
|
|
pTriggerStep->orconf = OE_Default;
|
|
}
|
|
sqlite3ExprDelete(db, pWhere);
|
|
@@ -124256,7 +131388,7 @@
|
|
*pp = (*pp)->pNext;
|
|
}
|
|
sqlite3DeleteTrigger(db, pTrigger);
|
|
- db->flags |= SQLITE_InternChanges;
|
|
+ db->mDbFlags |= DBFLAG_SchemaChange;
|
|
}
|
|
}
|
|
|
|
@@ -124376,6 +131508,14 @@
|
|
pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf;
|
|
assert( pParse->okConstFactor==0 );
|
|
|
|
+#ifndef SQLITE_OMIT_TRACE
|
|
+ if( pStep->zSpan ){
|
|
+ sqlite3VdbeAddOp4(v, OP_Trace, 0x7fffffff, 1, 0,
|
|
+ sqlite3MPrintf(db, "-- %s", pStep->zSpan),
|
|
+ P4_DYNAMIC);
|
|
+ }
|
|
+#endif
|
|
+
|
|
switch( pStep->op ){
|
|
case TK_UPDATE: {
|
|
sqlite3Update(pParse,
|
|
@@ -124382,7 +131522,7 @@
|
|
targetSrcList(pParse, pStep),
|
|
sqlite3ExprListDup(db, pStep->pExprList, 0),
|
|
sqlite3ExprDup(db, pStep->pWhere, 0),
|
|
- pParse->eOrconf
|
|
+ pParse->eOrconf, 0, 0, 0
|
|
);
|
|
break;
|
|
}
|
|
@@ -124391,7 +131531,8 @@
|
|
targetSrcList(pParse, pStep),
|
|
sqlite3SelectDup(db, pStep->pSelect, 0),
|
|
sqlite3IdListDup(db, pStep->pIdList),
|
|
- pParse->eOrconf
|
|
+ pParse->eOrconf,
|
|
+ sqlite3UpsertDup(db, pStep->pUpsert)
|
|
);
|
|
break;
|
|
}
|
|
@@ -124398,7 +131539,7 @@
|
|
case TK_DELETE: {
|
|
sqlite3DeleteFrom(pParse,
|
|
targetSrcList(pParse, pStep),
|
|
- sqlite3ExprDup(db, pStep->pWhere, 0)
|
|
+ sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
|
);
|
|
break;
|
|
}
|
|
@@ -124516,9 +131657,11 @@
|
|
pTab->zName
|
|
));
|
|
#ifndef SQLITE_OMIT_TRACE
|
|
- sqlite3VdbeChangeP4(v, -1,
|
|
- sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
|
|
- );
|
|
+ if( pTrigger->zName ){
|
|
+ sqlite3VdbeChangeP4(v, -1,
|
|
+ sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
|
|
+ );
|
|
+ }
|
|
#endif
|
|
|
|
/* If one was specified, code the WHEN clause. If it evaluates to false
|
|
@@ -124546,7 +131689,7 @@
|
|
VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf)));
|
|
|
|
transferParseError(pParse, pSubParse);
|
|
- if( db->mallocFailed==0 ){
|
|
+ if( db->mallocFailed==0 && pParse->nErr==0 ){
|
|
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
|
|
}
|
|
pProgram->nMem = pSubParse->nMem;
|
|
@@ -124854,6 +131997,57 @@
|
|
}
|
|
|
|
/*
|
|
+** Check to see if column iCol of index pIdx references any of the
|
|
+** columns defined by aXRef and chngRowid. Return true if it does
|
|
+** and false if not. This is an optimization. False-positives are a
|
|
+** performance degradation, but false-negatives can result in a corrupt
|
|
+** index and incorrect answers.
|
|
+**
|
|
+** aXRef[j] will be non-negative if column j of the original table is
|
|
+** being updated. chngRowid will be true if the rowid of the table is
|
|
+** being updated.
|
|
+*/
|
|
+static int indexColumnIsBeingUpdated(
|
|
+ Index *pIdx, /* The index to check */
|
|
+ int iCol, /* Which column of the index to check */
|
|
+ int *aXRef, /* aXRef[j]>=0 if column j is being updated */
|
|
+ int chngRowid /* true if the rowid is being updated */
|
|
+){
|
|
+ i16 iIdxCol = pIdx->aiColumn[iCol];
|
|
+ assert( iIdxCol!=XN_ROWID ); /* Cannot index rowid */
|
|
+ if( iIdxCol>=0 ){
|
|
+ return aXRef[iIdxCol]>=0;
|
|
+ }
|
|
+ assert( iIdxCol==XN_EXPR );
|
|
+ assert( pIdx->aColExpr!=0 );
|
|
+ assert( pIdx->aColExpr->a[iCol].pExpr!=0 );
|
|
+ return sqlite3ExprReferencesUpdatedColumn(pIdx->aColExpr->a[iCol].pExpr,
|
|
+ aXRef,chngRowid);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Check to see if index pIdx is a partial index whose conditional
|
|
+** expression might change values due to an UPDATE. Return true if
|
|
+** the index is subject to change and false if the index is guaranteed
|
|
+** to be unchanged. This is an optimization. False-positives are a
|
|
+** performance degradation, but false-negatives can result in a corrupt
|
|
+** index and incorrect answers.
|
|
+**
|
|
+** aXRef[j] will be non-negative if column j of the original table is
|
|
+** being updated. chngRowid will be true if the rowid of the table is
|
|
+** being updated.
|
|
+*/
|
|
+static int indexWhereClauseMightChange(
|
|
+ Index *pIdx, /* The index to check */
|
|
+ int *aXRef, /* aXRef[j]>=0 if column j is being updated */
|
|
+ int chngRowid /* true if the rowid is being updated */
|
|
+){
|
|
+ if( pIdx->pPartIdxWhere==0 ) return 0;
|
|
+ return sqlite3ExprReferencesUpdatedColumn(pIdx->pPartIdxWhere,
|
|
+ aXRef, chngRowid);
|
|
+}
|
|
+
|
|
+/*
|
|
** Process an UPDATE statement.
|
|
**
|
|
** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
|
|
@@ -124865,7 +132059,10 @@
|
|
SrcList *pTabList, /* The table in which we should change things */
|
|
ExprList *pChanges, /* Things to be changed */
|
|
Expr *pWhere, /* The WHERE clause. May be null */
|
|
- int onError /* How to handle constraint errors */
|
|
+ int onError, /* How to handle constraint errors */
|
|
+ ExprList *pOrderBy, /* ORDER BY clause. May be null */
|
|
+ Expr *pLimit, /* LIMIT clause. May be null */
|
|
+ Upsert *pUpsert /* ON CONFLICT clause, or null */
|
|
){
|
|
int i, j; /* Loop counters */
|
|
Table *pTab; /* The table to be updated */
|
|
@@ -124950,6 +132147,16 @@
|
|
# define isView 0
|
|
#endif
|
|
|
|
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
|
+ if( !isView ){
|
|
+ pWhere = sqlite3LimitWhere(
|
|
+ pParse, pTabList, pWhere, pOrderBy, pLimit, "UPDATE"
|
|
+ );
|
|
+ pOrderBy = 0;
|
|
+ pLimit = 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
|
goto update_cleanup;
|
|
}
|
|
@@ -124962,16 +132169,23 @@
|
|
** need to occur right after the database cursor. So go ahead and
|
|
** allocate enough space, just in case.
|
|
*/
|
|
- pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++;
|
|
+ iBaseCur = iDataCur = pParse->nTab++;
|
|
iIdxCur = iDataCur+1;
|
|
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
|
+ testcase( pPk!=0 && pPk!=pTab->pIndex );
|
|
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
|
|
- if( IsPrimaryKeyIndex(pIdx) && pPk!=0 ){
|
|
+ if( pPk==pIdx ){
|
|
iDataCur = pParse->nTab;
|
|
- pTabList->a[0].iCursor = iDataCur;
|
|
}
|
|
pParse->nTab++;
|
|
}
|
|
+ if( pUpsert ){
|
|
+ /* On an UPSERT, reuse the same cursors already opened by INSERT */
|
|
+ iDataCur = pUpsert->iDataCur;
|
|
+ iIdxCur = pUpsert->iIdxCur;
|
|
+ pParse->nTab = iBaseCur;
|
|
+ }
|
|
+ pTabList->a[0].iCursor = iDataCur;
|
|
|
|
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
|
|
** Initialize aXRef[] and aToOpen[] to their default values.
|
|
@@ -124988,6 +132202,8 @@
|
|
memset(&sNC, 0, sizeof(sNC));
|
|
sNC.pParse = pParse;
|
|
sNC.pSrcList = pTabList;
|
|
+ sNC.uNC.pUpsert = pUpsert;
|
|
+ sNC.ncFlags = NC_UUpsert;
|
|
|
|
/* Resolve the column names in all the expressions of the
|
|
** of the UPDATE statement. Also find the column index
|
|
@@ -125054,19 +132270,18 @@
|
|
/* There is one entry in the aRegIdx[] array for each index on the table
|
|
** being updated. Fill in aRegIdx[] with a register number that will hold
|
|
** the key for accessing each index.
|
|
- **
|
|
- ** FIXME: Be smarter about omitting indexes that use expressions.
|
|
*/
|
|
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
|
int reg;
|
|
- if( chngKey || hasFK>1 || pIdx->pPartIdxWhere || pIdx==pPk ){
|
|
+ if( chngKey || hasFK>1 || pIdx==pPk
|
|
+ || indexWhereClauseMightChange(pIdx,aXRef,chngRowid)
|
|
+ ){
|
|
reg = ++pParse->nMem;
|
|
pParse->nMem += pIdx->nColumn;
|
|
}else{
|
|
reg = 0;
|
|
for(i=0; i<pIdx->nKeyCol; i++){
|
|
- i16 iIdxCol = pIdx->aiColumn[i];
|
|
- if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
|
|
+ if( indexColumnIsBeingUpdated(pIdx, i, aXRef, chngRowid) ){
|
|
reg = ++pParse->nMem;
|
|
pParse->nMem += pIdx->nColumn;
|
|
if( (onError==OE_Replace)
|
|
@@ -125091,7 +132306,7 @@
|
|
v = sqlite3GetVdbe(pParse);
|
|
if( v==0 ) goto update_cleanup;
|
|
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
|
- sqlite3BeginWriteOperation(pParse, 1, iDb);
|
|
+ sqlite3BeginWriteOperation(pParse, pTrigger || hasFK, iDb);
|
|
|
|
/* Allocate required registers. */
|
|
if( !IsVirtual(pTab) ){
|
|
@@ -125118,7 +132333,11 @@
|
|
*/
|
|
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
|
if( isView ){
|
|
- sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
|
|
+ sqlite3MaterializeView(pParse, pTab,
|
|
+ pWhere, pOrderBy, pLimit, iDataCur
|
|
+ );
|
|
+ pOrderBy = 0;
|
|
+ pLimit = 0;
|
|
}
|
|
#endif
|
|
|
|
@@ -125138,8 +132357,16 @@
|
|
}
|
|
#endif
|
|
|
|
- /* Initialize the count of updated rows */
|
|
- if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
|
+ /* Jump to labelBreak to abandon further processing of this UPDATE */
|
|
+ labelContinue = labelBreak = sqlite3VdbeMakeLabel(v);
|
|
+
|
|
+ /* Not an UPSERT. Normal processing. Begin by
|
|
+ ** initialize the count of updated rows */
|
|
+ if( (db->flags&SQLITE_CountRows)!=0
|
|
+ && !pParse->pTriggerTab
|
|
+ && !pParse->nested
|
|
+ && pUpsert==0
|
|
+ ){
|
|
regRowCount = ++pParse->nMem;
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
|
}
|
|
@@ -125152,46 +132379,61 @@
|
|
iPk = pParse->nMem+1;
|
|
pParse->nMem += nPk;
|
|
regKey = ++pParse->nMem;
|
|
- iEph = pParse->nTab++;
|
|
-
|
|
- sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
|
|
- addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
|
- sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
|
+ if( pUpsert==0 ){
|
|
+ iEph = pParse->nTab++;
|
|
+ sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
|
|
+ addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
|
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
|
+ }
|
|
}
|
|
-
|
|
- /* Begin the database scan.
|
|
- **
|
|
- ** Do not consider a single-pass strategy for a multi-row update if
|
|
- ** there are any triggers or foreign keys to process, or rows may
|
|
- ** be deleted as a result of REPLACE conflict handling. Any of these
|
|
- ** things might disturb a cursor being used to scan through the table
|
|
- ** or index, causing a single-pass approach to malfunction. */
|
|
- flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
|
- if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
|
- flags |= WHERE_ONEPASS_MULTIROW;
|
|
+
|
|
+ if( pUpsert ){
|
|
+ /* If this is an UPSERT, then all cursors have already been opened by
|
|
+ ** the outer INSERT and the data cursor should be pointing at the row
|
|
+ ** that is to be updated. So bypass the code that searches for the
|
|
+ ** row(s) to be updated.
|
|
+ */
|
|
+ pWInfo = 0;
|
|
+ eOnePass = ONEPASS_SINGLE;
|
|
+ sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
|
|
+ }else{
|
|
+ /* Begin the database scan.
|
|
+ **
|
|
+ ** Do not consider a single-pass strategy for a multi-row update if
|
|
+ ** there are any triggers or foreign keys to process, or rows may
|
|
+ ** be deleted as a result of REPLACE conflict handling. Any of these
|
|
+ ** things might disturb a cursor being used to scan through the table
|
|
+ ** or index, causing a single-pass approach to malfunction. */
|
|
+ flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
|
+ if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
|
+ flags |= WHERE_ONEPASS_MULTIROW;
|
|
+ }
|
|
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
|
+ if( pWInfo==0 ) goto update_cleanup;
|
|
+
|
|
+ /* A one-pass strategy that might update more than one row may not
|
|
+ ** be used if any column of the index used for the scan is being
|
|
+ ** updated. Otherwise, if there is an index on "b", statements like
|
|
+ ** the following could create an infinite loop:
|
|
+ **
|
|
+ ** UPDATE t1 SET b=b+1 WHERE b>?
|
|
+ **
|
|
+ ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
|
+ ** strategy that uses an index for which one or more columns are being
|
|
+ ** updated. */
|
|
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
|
+ if( eOnePass!=ONEPASS_SINGLE ){
|
|
+ sqlite3MultiWrite(pParse);
|
|
+ if( eOnePass==ONEPASS_MULTI ){
|
|
+ int iCur = aiCurOnePass[1];
|
|
+ if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
|
+ eOnePass = ONEPASS_OFF;
|
|
+ }
|
|
+ assert( iCur!=iDataCur || !HasRowid(pTab) );
|
|
+ }
|
|
+ }
|
|
}
|
|
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
|
- if( pWInfo==0 ) goto update_cleanup;
|
|
|
|
- /* A one-pass strategy that might update more than one row may not
|
|
- ** be used if any column of the index used for the scan is being
|
|
- ** updated. Otherwise, if there is an index on "b", statements like
|
|
- ** the following could create an infinite loop:
|
|
- **
|
|
- ** UPDATE t1 SET b=b+1 WHERE b>?
|
|
- **
|
|
- ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
|
- ** strategy that uses an index for which one or more columns are being
|
|
- ** updated. */
|
|
- eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
|
- if( eOnePass==ONEPASS_MULTI ){
|
|
- int iCur = aiCurOnePass[1];
|
|
- if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
|
- eOnePass = ONEPASS_OFF;
|
|
- }
|
|
- assert( iCur!=iDataCur || !HasRowid(pTab) );
|
|
- }
|
|
-
|
|
if( HasRowid(pTab) ){
|
|
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
|
|
** mode, write the rowid into the FIFO. In either of the one-pass modes,
|
|
@@ -125211,7 +132453,7 @@
|
|
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i);
|
|
}
|
|
if( eOnePass ){
|
|
- sqlite3VdbeChangeToNoop(v, addrOpen);
|
|
+ if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
|
|
nKey = nPk;
|
|
regKey = iPk;
|
|
}else{
|
|
@@ -125221,59 +132463,58 @@
|
|
}
|
|
}
|
|
|
|
- if( eOnePass!=ONEPASS_MULTI ){
|
|
- sqlite3WhereEnd(pWInfo);
|
|
- }
|
|
-
|
|
- labelBreak = sqlite3VdbeMakeLabel(v);
|
|
- if( !isView ){
|
|
- int addrOnce = 0;
|
|
-
|
|
- /* Open every index that needs updating. */
|
|
- if( eOnePass!=ONEPASS_OFF ){
|
|
- if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
|
- if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
|
+ if( pUpsert==0 ){
|
|
+ if( eOnePass!=ONEPASS_MULTI ){
|
|
+ sqlite3WhereEnd(pWInfo);
|
|
}
|
|
-
|
|
- if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
|
|
- addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
|
+
|
|
+ if( !isView ){
|
|
+ int addrOnce = 0;
|
|
+
|
|
+ /* Open every index that needs updating. */
|
|
+ if( eOnePass!=ONEPASS_OFF ){
|
|
+ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
|
+ if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
|
+ }
|
|
+
|
|
+ if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
|
|
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
|
+ }
|
|
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur,
|
|
+ aToOpen, 0, 0);
|
|
+ if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
|
}
|
|
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
|
|
- 0, 0);
|
|
- if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
|
- }
|
|
-
|
|
- /* Top of the update loop */
|
|
- if( eOnePass!=ONEPASS_OFF ){
|
|
- if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
|
|
- assert( pPk );
|
|
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
|
|
- VdbeCoverageNeverTaken(v);
|
|
- }
|
|
- if( eOnePass==ONEPASS_SINGLE ){
|
|
- labelContinue = labelBreak;
|
|
+
|
|
+ /* Top of the update loop */
|
|
+ if( eOnePass!=ONEPASS_OFF ){
|
|
+ if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
|
|
+ assert( pPk );
|
|
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
+ if( eOnePass!=ONEPASS_SINGLE ){
|
|
+ labelContinue = sqlite3VdbeMakeLabel(v);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
|
+ VdbeCoverageIf(v, pPk==0);
|
|
+ VdbeCoverageIf(v, pPk!=0);
|
|
+ }else if( pPk ){
|
|
+ labelContinue = sqlite3VdbeMakeLabel(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
|
+ addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
|
|
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
|
+ VdbeCoverage(v);
|
|
}else{
|
|
- labelContinue = sqlite3VdbeMakeLabel(v);
|
|
+ labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
|
|
+ regOldRowid);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
|
+ VdbeCoverage(v);
|
|
}
|
|
- sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
|
- VdbeCoverageIf(v, pPk==0);
|
|
- VdbeCoverageIf(v, pPk!=0);
|
|
- }else if( pPk ){
|
|
- labelContinue = sqlite3VdbeMakeLabel(v);
|
|
- sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
|
- addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
|
|
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
|
- VdbeCoverage(v);
|
|
- }else{
|
|
- labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
|
|
- regOldRowid);
|
|
- VdbeCoverage(v);
|
|
- sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
|
- VdbeCoverage(v);
|
|
}
|
|
|
|
- /* If the record number will change, set register regNewRowid to
|
|
- ** contain the new value. If the record number is not being modified,
|
|
+ /* If the rowid value will change, set register regNewRowid to
|
|
+ ** contain the new value. If the rowid is not being modified,
|
|
** then regNewRowid is the same register as regOldRowid, which is
|
|
** already populated. */
|
|
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
|
|
@@ -125336,7 +132577,7 @@
|
|
*/
|
|
testcase( i==31 );
|
|
testcase( i==32 );
|
|
- sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
|
|
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
|
}else{
|
|
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
|
}
|
|
@@ -125365,10 +132606,14 @@
|
|
VdbeCoverage(v);
|
|
}
|
|
|
|
- /* If it did not delete it, the row-trigger may still have modified
|
|
+ /* After-BEFORE-trigger-reload-loop:
|
|
+ ** If it did not delete it, the BEFORE trigger may still have modified
|
|
** some of the columns of the row being updated. Load the values for
|
|
- ** all columns not modified by the update statement into their
|
|
- ** registers in case this has happened.
|
|
+ ** all columns not modified by the update statement into their registers
|
|
+ ** in case this has happened. Only unmodified columns are reloaded.
|
|
+ ** The values computed for modified columns use the values before the
|
|
+ ** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
|
|
+ ** for an example.
|
|
*/
|
|
for(i=0; i<pTab->nCol; i++){
|
|
if( aXRef[i]<0 && i!=pTab->iPKey ){
|
|
@@ -125384,7 +132629,7 @@
|
|
assert( regOldRowid>0 );
|
|
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
|
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
|
|
- aXRef);
|
|
+ aXRef, 0);
|
|
|
|
/* Do FK constraint checks. */
|
|
if( hasFK ){
|
|
@@ -125454,7 +132699,7 @@
|
|
|
|
/* Increment the row counter
|
|
*/
|
|
- if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
|
|
+ if( regRowCount ){
|
|
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
|
|
}
|
|
|
|
@@ -125481,16 +132726,15 @@
|
|
** maximum rowid counter values recorded while inserting into
|
|
** autoincrement tables.
|
|
*/
|
|
- if( pParse->nested==0 && pParse->pTriggerTab==0 ){
|
|
+ if( pParse->nested==0 && pParse->pTriggerTab==0 && pUpsert==0 ){
|
|
sqlite3AutoincrementEnd(pParse);
|
|
}
|
|
|
|
/*
|
|
- ** Return the number of rows that were changed. If this routine is
|
|
- ** generating code because of a call to sqlite3NestedParse(), do not
|
|
- ** invoke the callback function.
|
|
+ ** Return the number of rows that were changed, if we are tracking
|
|
+ ** that information.
|
|
*/
|
|
- if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){
|
|
+ if( regRowCount ){
|
|
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
|
sqlite3VdbeSetNumCols(v, 1);
|
|
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
|
|
@@ -125502,6 +132746,10 @@
|
|
sqlite3SrcListDelete(db, pTabList);
|
|
sqlite3ExprListDelete(db, pChanges);
|
|
sqlite3ExprDelete(db, pWhere);
|
|
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
|
+ sqlite3ExprListDelete(db, pOrderBy);
|
|
+ sqlite3ExprDelete(db, pLimit);
|
|
+#endif
|
|
return;
|
|
}
|
|
/* Make sure "isView" and other macros defined above are undefined. Otherwise
|
|
@@ -125558,10 +132806,10 @@
|
|
int regRowid; /* Register for ephem table rowid */
|
|
int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
|
|
int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
|
|
- int bOnePass; /* True to use onepass strategy */
|
|
+ int eOnePass; /* True to use onepass strategy */
|
|
int addr; /* Address of OP_OpenEphemeral */
|
|
|
|
- /* Allocate nArg registers to martial the arguments to VUpdate. Then
|
|
+ /* Allocate nArg registers in which to gather the arguments for VUpdate. Then
|
|
** create and open the ephemeral table in which the records created from
|
|
** these arguments will be temporarily stored. */
|
|
assert( v );
|
|
@@ -125577,40 +132825,58 @@
|
|
if( pWInfo==0 ) return;
|
|
|
|
/* Populate the argument registers. */
|
|
- sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
|
- if( pRowid ){
|
|
- sqlite3ExprCode(pParse, pRowid, regArg+1);
|
|
- }else{
|
|
- sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
|
- }
|
|
for(i=0; i<pTab->nCol; i++){
|
|
if( aXRef[i]>=0 ){
|
|
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
|
}else{
|
|
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
|
|
+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */
|
|
}
|
|
}
|
|
+ if( HasRowid(pTab) ){
|
|
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
|
+ if( pRowid ){
|
|
+ sqlite3ExprCode(pParse, pRowid, regArg+1);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
|
+ }
|
|
+ }else{
|
|
+ Index *pPk; /* PRIMARY KEY index */
|
|
+ i16 iPk; /* PRIMARY KEY column */
|
|
+ pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
+ assert( pPk!=0 );
|
|
+ assert( pPk->nKeyCol==1 );
|
|
+ iPk = pPk->aiColumn[0];
|
|
+ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
|
|
+ sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
|
|
+ }
|
|
|
|
- bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
|
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
|
|
|
- if( bOnePass ){
|
|
+ /* There is no ONEPASS_MULTI on virtual tables */
|
|
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
|
+
|
|
+ if( eOnePass ){
|
|
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
|
- ** above. Also, if this is a top-level parse (not a trigger), clear the
|
|
- ** multi-write flag so that the VM does not open a statement journal */
|
|
+ ** above. */
|
|
sqlite3VdbeChangeToNoop(v, addr);
|
|
- if( sqlite3IsToplevel(pParse) ){
|
|
- pParse->isMultiWrite = 0;
|
|
- }
|
|
+ sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
|
}else{
|
|
/* Create a record from the argument register contents and insert it into
|
|
** the ephemeral table. */
|
|
+ sqlite3MultiWrite(pParse);
|
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
|
|
+#ifdef SQLITE_DEBUG
|
|
+ /* Signal an assert() within OP_MakeRecord that it is allowed to
|
|
+ ** accept no-change records with serial_type 10 */
|
|
+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
|
|
+#endif
|
|
sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
|
|
sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
|
|
}
|
|
|
|
|
|
- if( bOnePass==0 ){
|
|
+ if( eOnePass==ONEPASS_OFF ){
|
|
/* End the virtual table scan */
|
|
sqlite3WhereEnd(pWInfo);
|
|
|
|
@@ -125630,7 +132896,7 @@
|
|
|
|
/* End of the ephemeral table scan. Or, if using the onepass strategy,
|
|
** jump to here if the scan visited zero rows. */
|
|
- if( bOnePass==0 ){
|
|
+ if( eOnePass==ONEPASS_OFF ){
|
|
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
|
|
sqlite3VdbeJumpHere(v, addr);
|
|
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
|
|
@@ -125641,6 +132907,261 @@
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
|
|
/************** End of update.c **********************************************/
|
|
+/************** Begin file upsert.c ******************************************/
|
|
+/*
|
|
+** 2018-04-12
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+*************************************************************************
|
|
+** This file contains code to implement various aspects of UPSERT
|
|
+** processing and handling of the Upsert object.
|
|
+*/
|
|
+/* #include "sqliteInt.h" */
|
|
+
|
|
+#ifndef SQLITE_OMIT_UPSERT
|
|
+/*
|
|
+** Free a list of Upsert objects
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
|
|
+ if( p ){
|
|
+ sqlite3ExprListDelete(db, p->pUpsertTarget);
|
|
+ sqlite3ExprDelete(db, p->pUpsertTargetWhere);
|
|
+ sqlite3ExprListDelete(db, p->pUpsertSet);
|
|
+ sqlite3ExprDelete(db, p->pUpsertWhere);
|
|
+ sqlite3DbFree(db, p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Duplicate an Upsert object.
|
|
+*/
|
|
+SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
|
|
+ if( p==0 ) return 0;
|
|
+ return sqlite3UpsertNew(db,
|
|
+ sqlite3ExprListDup(db, p->pUpsertTarget, 0),
|
|
+ sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
|
|
+ sqlite3ExprListDup(db, p->pUpsertSet, 0),
|
|
+ sqlite3ExprDup(db, p->pUpsertWhere, 0)
|
|
+ );
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create a new Upsert object.
|
|
+*/
|
|
+SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
|
|
+ sqlite3 *db, /* Determines which memory allocator to use */
|
|
+ ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */
|
|
+ Expr *pTargetWhere, /* Optional WHERE clause on the target */
|
|
+ ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */
|
|
+ Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */
|
|
+){
|
|
+ Upsert *pNew;
|
|
+ pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
|
|
+ if( pNew==0 ){
|
|
+ sqlite3ExprListDelete(db, pTarget);
|
|
+ sqlite3ExprDelete(db, pTargetWhere);
|
|
+ sqlite3ExprListDelete(db, pSet);
|
|
+ sqlite3ExprDelete(db, pWhere);
|
|
+ return 0;
|
|
+ }else{
|
|
+ pNew->pUpsertTarget = pTarget;
|
|
+ pNew->pUpsertTargetWhere = pTargetWhere;
|
|
+ pNew->pUpsertSet = pSet;
|
|
+ pNew->pUpsertWhere = pWhere;
|
|
+ pNew->pUpsertIdx = 0;
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Analyze the ON CONFLICT clause described by pUpsert. Resolve all
|
|
+** symbols in the conflict-target.
|
|
+**
|
|
+** Return SQLITE_OK if everything works, or an error code is something
|
|
+** is wrong.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
|
|
+ Parse *pParse, /* The parsing context */
|
|
+ SrcList *pTabList, /* Table into which we are inserting */
|
|
+ Upsert *pUpsert /* The ON CONFLICT clauses */
|
|
+){
|
|
+ Table *pTab; /* That table into which we are inserting */
|
|
+ int rc; /* Result code */
|
|
+ int iCursor; /* Cursor used by pTab */
|
|
+ Index *pIdx; /* One of the indexes of pTab */
|
|
+ ExprList *pTarget; /* The conflict-target clause */
|
|
+ Expr *pTerm; /* One term of the conflict-target clause */
|
|
+ NameContext sNC; /* Context for resolving symbolic names */
|
|
+ Expr sCol[2]; /* Index column converted into an Expr */
|
|
+
|
|
+ assert( pTabList->nSrc==1 );
|
|
+ assert( pTabList->a[0].pTab!=0 );
|
|
+ assert( pUpsert!=0 );
|
|
+ assert( pUpsert->pUpsertTarget!=0 );
|
|
+
|
|
+ /* Resolve all symbolic names in the conflict-target clause, which
|
|
+ ** includes both the list of columns and the optional partial-index
|
|
+ ** WHERE clause.
|
|
+ */
|
|
+ memset(&sNC, 0, sizeof(sNC));
|
|
+ sNC.pParse = pParse;
|
|
+ sNC.pSrcList = pTabList;
|
|
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
|
+ if( rc ) return rc;
|
|
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
|
+ if( rc ) return rc;
|
|
+
|
|
+ /* Check to see if the conflict target matches the rowid. */
|
|
+ pTab = pTabList->a[0].pTab;
|
|
+ pTarget = pUpsert->pUpsertTarget;
|
|
+ iCursor = pTabList->a[0].iCursor;
|
|
+ if( HasRowid(pTab)
|
|
+ && pTarget->nExpr==1
|
|
+ && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
|
|
+ && pTerm->iColumn==XN_ROWID
|
|
+ ){
|
|
+ /* The conflict-target is the rowid of the primary table */
|
|
+ assert( pUpsert->pUpsertIdx==0 );
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+
|
|
+ /* Initialize sCol[0..1] to be an expression parse tree for a
|
|
+ ** single column of an index. The sCol[0] node will be the TK_COLLATE
|
|
+ ** operator and sCol[1] will be the TK_COLUMN operator. Code below
|
|
+ ** will populate the specific collation and column number values
|
|
+ ** prior to comparing against the conflict-target expression.
|
|
+ */
|
|
+ memset(sCol, 0, sizeof(sCol));
|
|
+ sCol[0].op = TK_COLLATE;
|
|
+ sCol[0].pLeft = &sCol[1];
|
|
+ sCol[1].op = TK_COLUMN;
|
|
+ sCol[1].iTable = pTabList->a[0].iCursor;
|
|
+
|
|
+ /* Check for matches against other indexes */
|
|
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
|
+ int ii, jj, nn;
|
|
+ if( !IsUniqueIndex(pIdx) ) continue;
|
|
+ if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
|
|
+ if( pIdx->pPartIdxWhere ){
|
|
+ if( pUpsert->pUpsertTargetWhere==0 ) continue;
|
|
+ if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
|
|
+ pIdx->pPartIdxWhere, iCursor)!=0 ){
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ nn = pIdx->nKeyCol;
|
|
+ for(ii=0; ii<nn; ii++){
|
|
+ Expr *pExpr;
|
|
+ sCol[0].u.zToken = (char*)pIdx->azColl[ii];
|
|
+ if( pIdx->aiColumn[ii]==XN_EXPR ){
|
|
+ assert( pIdx->aColExpr!=0 );
|
|
+ assert( pIdx->aColExpr->nExpr>ii );
|
|
+ pExpr = pIdx->aColExpr->a[ii].pExpr;
|
|
+ if( pExpr->op!=TK_COLLATE ){
|
|
+ sCol[0].pLeft = pExpr;
|
|
+ pExpr = &sCol[0];
|
|
+ }
|
|
+ }else{
|
|
+ sCol[0].pLeft = &sCol[1];
|
|
+ sCol[1].iColumn = pIdx->aiColumn[ii];
|
|
+ pExpr = &sCol[0];
|
|
+ }
|
|
+ for(jj=0; jj<nn; jj++){
|
|
+ if( sqlite3ExprCompare(pParse, pTarget->a[jj].pExpr, pExpr,iCursor)<2 ){
|
|
+ break; /* Column ii of the index matches column jj of target */
|
|
+ }
|
|
+ }
|
|
+ if( jj>=nn ){
|
|
+ /* The target contains no match for column jj of the index */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( ii<nn ){
|
|
+ /* Column ii of the index did not match any term of the conflict target.
|
|
+ ** Continue the search with the next index. */
|
|
+ continue;
|
|
+ }
|
|
+ pUpsert->pUpsertIdx = pIdx;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
|
|
+ "PRIMARY KEY or UNIQUE constraint");
|
|
+ return SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Generate bytecode that does an UPDATE as part of an upsert.
|
|
+**
|
|
+** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.
|
|
+** In this case parameter iCur is a cursor open on the table b-tree that
|
|
+** currently points to the conflicting table row. Otherwise, if pIdx
|
|
+** is not NULL, then pIdx is the constraint that failed and iCur is a
|
|
+** cursor points to the conflicting row.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
|
|
+ Parse *pParse, /* The parsing and code-generating context */
|
|
+ Upsert *pUpsert, /* The ON CONFLICT clause for the upsert */
|
|
+ Table *pTab, /* The table being updated */
|
|
+ Index *pIdx, /* The UNIQUE constraint that failed */
|
|
+ int iCur /* Cursor for pIdx (or pTab if pIdx==NULL) */
|
|
+){
|
|
+ Vdbe *v = pParse->pVdbe;
|
|
+ sqlite3 *db = pParse->db;
|
|
+ SrcList *pSrc; /* FROM clause for the UPDATE */
|
|
+ int iDataCur;
|
|
+
|
|
+ assert( v!=0 );
|
|
+ assert( pUpsert!=0 );
|
|
+ VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
|
|
+ iDataCur = pUpsert->iDataCur;
|
|
+ if( pIdx && iCur!=iDataCur ){
|
|
+ if( HasRowid(pTab) ){
|
|
+ int regRowid = sqlite3GetTempReg(pParse);
|
|
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
|
|
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3ReleaseTempReg(pParse, regRowid);
|
|
+ }else{
|
|
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
+ int nPk = pPk->nKeyCol;
|
|
+ int iPk = pParse->nMem+1;
|
|
+ int i;
|
|
+ pParse->nMem += nPk;
|
|
+ for(i=0; i<nPk; i++){
|
|
+ int k;
|
|
+ assert( pPk->aiColumn[i]>=0 );
|
|
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);
|
|
+ VdbeComment((v, "%s.%s", pIdx->zName,
|
|
+ pTab->aCol[pPk->aiColumn[i]].zName));
|
|
+ }
|
|
+ sqlite3VdbeVerifyAbortable(v, OE_Abort);
|
|
+ i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
|
|
+ "corrupt database", P4_STATIC);
|
|
+ sqlite3VdbeJumpHere(v, i);
|
|
+ }
|
|
+ }
|
|
+ /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
|
|
+ ** we have to make a copy before passing it down into sqlite3Update() */
|
|
+ pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
|
|
+ sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
|
|
+ pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
|
|
+ pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
|
|
+ pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */
|
|
+ VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
|
|
+}
|
|
+
|
|
+#endif /* SQLITE_OMIT_UPSERT */
|
|
+
|
|
+/************** End of upsert.c **********************************************/
|
|
/************** Begin file vacuum.c ******************************************/
|
|
/*
|
|
** 2003 April 6
|
|
@@ -125683,8 +133204,14 @@
|
|
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
|
|
const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
|
|
assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
|
|
- if( zSubSql ){
|
|
- assert( zSubSql[0]!='S' );
|
|
+ /* The secondary SQL must be one of CREATE TABLE, CREATE INDEX,
|
|
+ ** or INSERT. Historically there have been attacks that first
|
|
+ ** corrupt the sqlite_master.sql field with other kinds of statements
|
|
+ ** then run VACUUM to get those statements to execute at inappropriate
|
|
+ ** times. */
|
|
+ if( zSubSql
|
|
+ && (strncmp(zSubSql,"CRE",3)==0 || strncmp(zSubSql,"INS",3)==0)
|
|
+ ){
|
|
rc = execSql(db, pzErrMsg, zSubSql);
|
|
if( rc!=SQLITE_OK ) break;
|
|
}
|
|
@@ -125774,7 +133301,8 @@
|
|
int rc = SQLITE_OK; /* Return code from service routines */
|
|
Btree *pMain; /* The database being vacuumed */
|
|
Btree *pTemp; /* The temporary database we vacuum into */
|
|
- int saved_flags; /* Saved value of the db->flags */
|
|
+ u16 saved_mDbFlags; /* Saved value of db->mDbFlags */
|
|
+ u32 saved_flags; /* Saved value of db->flags */
|
|
int saved_nChange; /* Saved value of db->nChange */
|
|
int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
|
u8 saved_mTrace; /* Saved trace settings */
|
|
@@ -125797,12 +133325,14 @@
|
|
** restored before returning. Then set the writable-schema flag, and
|
|
** disable CHECK and foreign key constraints. */
|
|
saved_flags = db->flags;
|
|
+ saved_mDbFlags = db->mDbFlags;
|
|
saved_nChange = db->nChange;
|
|
saved_nTotalChange = db->nTotalChange;
|
|
saved_mTrace = db->mTrace;
|
|
- db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
|
|
- | SQLITE_PreferBuiltin | SQLITE_Vacuum);
|
|
- db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
|
|
+ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
|
|
+ db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
|
|
+ db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder
|
|
+ | SQLITE_Defensive | SQLITE_CountRows);
|
|
db->mTrace = 0;
|
|
|
|
zDbMain = db->aDb[iDb].zDbSName;
|
|
@@ -125860,7 +133390,7 @@
|
|
*/
|
|
rc = execSql(db, pzErrMsg, "BEGIN");
|
|
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
|
- rc = sqlite3BtreeBeginTrans(pMain, 2);
|
|
+ rc = sqlite3BtreeBeginTrans(pMain, 2, 0);
|
|
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
|
|
|
/* Do not attempt to change the page size for a WAL database */
|
|
@@ -125895,7 +133425,7 @@
|
|
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
|
rc = execSqlF(db, pzErrMsg,
|
|
"SELECT sql FROM \"%w\".sqlite_master"
|
|
- " WHERE type='index' AND length(sql)>10",
|
|
+ " WHERE type='index'",
|
|
zDbMain
|
|
);
|
|
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
|
@@ -125912,8 +133442,8 @@
|
|
"WHERE type='table'AND coalesce(rootpage,1)>0",
|
|
zDbMain
|
|
);
|
|
- assert( (db->flags & SQLITE_Vacuum)!=0 );
|
|
- db->flags &= ~SQLITE_Vacuum;
|
|
+ assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 );
|
|
+ db->mDbFlags &= ~DBFLAG_Vacuum;
|
|
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
|
|
|
/* Copy the triggers, views, and virtual tables from the main database
|
|
@@ -125981,6 +133511,7 @@
|
|
end_of_vacuum:
|
|
/* Restore the original value of db->flags */
|
|
db->init.iDb = 0;
|
|
+ db->mDbFlags = saved_mDbFlags;
|
|
db->flags = saved_flags;
|
|
db->nChange = saved_nChange;
|
|
db->nTotalChange = saved_nTotalChange;
|
|
@@ -126057,8 +133588,10 @@
|
|
){
|
|
Module *pMod;
|
|
int nName = sqlite3Strlen30(zName);
|
|
- pMod = (Module *)sqlite3DbMallocRawNN(db, sizeof(Module) + nName + 1);
|
|
- if( pMod ){
|
|
+ pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
|
|
+ if( pMod==0 ){
|
|
+ sqlite3OomFault(db);
|
|
+ }else{
|
|
Module *pDel;
|
|
char *zCopy = (char *)(&pMod[1]);
|
|
memcpy(zCopy, zName, nName+1);
|
|
@@ -126275,7 +133808,7 @@
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
|
|
if( p ){
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
do {
|
|
VTable *pNext = p->pNext;
|
|
sqlite3VtabUnlock(p);
|
|
@@ -126341,7 +133874,6 @@
|
|
Token *pModuleName, /* Name of the module for the virtual table */
|
|
int ifNotExists /* No error if the table already exists */
|
|
){
|
|
- int iDb; /* The database the table is being created in */
|
|
Table *pTable; /* The new virtual table */
|
|
sqlite3 *db; /* Database connection */
|
|
|
|
@@ -126351,8 +133883,6 @@
|
|
assert( 0==pTable->pIndex );
|
|
|
|
db = pParse->db;
|
|
- iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
|
|
- assert( iDb>=0 );
|
|
|
|
assert( pTable->nModuleArg==0 );
|
|
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
|
|
@@ -126372,6 +133902,8 @@
|
|
** The second call, to obtain permission to create the table, is made now.
|
|
*/
|
|
if( pTable->azModuleArg ){
|
|
+ int iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
|
|
+ assert( iDb>=0 ); /* The database the table is being created in */
|
|
sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
|
|
pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName);
|
|
}
|
|
@@ -126533,13 +134065,14 @@
|
|
}
|
|
}
|
|
|
|
- zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
|
|
+ zModuleName = sqlite3DbStrDup(db, pTab->zName);
|
|
if( !zModuleName ){
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
|
|
- pVTable = sqlite3DbMallocZero(db, sizeof(VTable));
|
|
+ pVTable = sqlite3MallocZero(sizeof(VTable));
|
|
if( !pVTable ){
|
|
+ sqlite3OomFault(db);
|
|
sqlite3DbFree(db, zModuleName);
|
|
return SQLITE_NOMEM_BKPT;
|
|
}
|
|
@@ -126659,6 +134192,7 @@
|
|
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr);
|
|
if( rc!=SQLITE_OK ){
|
|
sqlite3ErrorMsg(pParse, "%s", zErr);
|
|
+ pParse->rc = rc;
|
|
}
|
|
sqlite3DbFree(db, zErr);
|
|
}
|
|
@@ -126748,10 +134282,10 @@
|
|
*/
|
|
SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
|
VtabCtx *pCtx;
|
|
- Parse *pParse;
|
|
int rc = SQLITE_OK;
|
|
Table *pTab;
|
|
char *zErr = 0;
|
|
+ Parse sParse;
|
|
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
|
|
@@ -126768,56 +134302,56 @@
|
|
pTab = pCtx->pTab;
|
|
assert( IsVirtual(pTab) );
|
|
|
|
- pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
|
|
- if( pParse==0 ){
|
|
- rc = SQLITE_NOMEM_BKPT;
|
|
- }else{
|
|
- pParse->declareVtab = 1;
|
|
- pParse->db = db;
|
|
- pParse->nQueryLoop = 1;
|
|
-
|
|
- if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr)
|
|
- && pParse->pNewTable
|
|
- && !db->mallocFailed
|
|
- && !pParse->pNewTable->pSelect
|
|
- && !IsVirtual(pParse->pNewTable)
|
|
- ){
|
|
- if( !pTab->aCol ){
|
|
- Table *pNew = pParse->pNewTable;
|
|
- Index *pIdx;
|
|
- pTab->aCol = pNew->aCol;
|
|
- pTab->nCol = pNew->nCol;
|
|
- pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
|
- pNew->nCol = 0;
|
|
- pNew->aCol = 0;
|
|
- assert( pTab->pIndex==0 );
|
|
- if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){
|
|
- rc = SQLITE_ERROR;
|
|
- }
|
|
- pIdx = pNew->pIndex;
|
|
- if( pIdx ){
|
|
- assert( pIdx->pNext==0 );
|
|
- pTab->pIndex = pIdx;
|
|
- pNew->pIndex = 0;
|
|
- pIdx->pTable = pTab;
|
|
- }
|
|
+ memset(&sParse, 0, sizeof(sParse));
|
|
+ sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
|
|
+ sParse.db = db;
|
|
+ sParse.nQueryLoop = 1;
|
|
+ if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
|
|
+ && sParse.pNewTable
|
|
+ && !db->mallocFailed
|
|
+ && !sParse.pNewTable->pSelect
|
|
+ && !IsVirtual(sParse.pNewTable)
|
|
+ ){
|
|
+ if( !pTab->aCol ){
|
|
+ Table *pNew = sParse.pNewTable;
|
|
+ Index *pIdx;
|
|
+ pTab->aCol = pNew->aCol;
|
|
+ pTab->nCol = pNew->nCol;
|
|
+ pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
|
+ pNew->nCol = 0;
|
|
+ pNew->aCol = 0;
|
|
+ assert( pTab->pIndex==0 );
|
|
+ assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
|
|
+ if( !HasRowid(pNew)
|
|
+ && pCtx->pVTable->pMod->pModule->xUpdate!=0
|
|
+ && sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1
|
|
+ ){
|
|
+ /* WITHOUT ROWID virtual tables must either be read-only (xUpdate==0)
|
|
+ ** or else must have a single-column PRIMARY KEY */
|
|
+ rc = SQLITE_ERROR;
|
|
}
|
|
- pCtx->bDeclared = 1;
|
|
- }else{
|
|
- sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
|
- sqlite3DbFree(db, zErr);
|
|
- rc = SQLITE_ERROR;
|
|
+ pIdx = pNew->pIndex;
|
|
+ if( pIdx ){
|
|
+ assert( pIdx->pNext==0 );
|
|
+ pTab->pIndex = pIdx;
|
|
+ pNew->pIndex = 0;
|
|
+ pIdx->pTable = pTab;
|
|
+ }
|
|
}
|
|
- pParse->declareVtab = 0;
|
|
-
|
|
- if( pParse->pVdbe ){
|
|
- sqlite3VdbeFinalize(pParse->pVdbe);
|
|
- }
|
|
- sqlite3DeleteTable(db, pParse->pNewTable);
|
|
- sqlite3ParserReset(pParse);
|
|
- sqlite3StackFree(db, pParse);
|
|
+ pCtx->bDeclared = 1;
|
|
+ }else{
|
|
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
|
+ sqlite3DbFree(db, zErr);
|
|
+ rc = SQLITE_ERROR;
|
|
}
|
|
+ sParse.eParseMode = PARSE_MODE_NORMAL;
|
|
|
|
+ if( sParse.pVdbe ){
|
|
+ sqlite3VdbeFinalize(sParse.pVdbe);
|
|
+ }
|
|
+ sqlite3DeleteTable(db, sParse.pNewTable);
|
|
+ sqlite3ParserReset(&sParse);
|
|
+
|
|
assert( (rc&0xff)==rc );
|
|
rc = sqlite3ApiExit(db, rc);
|
|
sqlite3_mutex_leave(db->mutex);
|
|
@@ -127060,14 +134594,11 @@
|
|
void *pArg = 0;
|
|
FuncDef *pNew;
|
|
int rc = 0;
|
|
- char *zLowerName;
|
|
- unsigned char *z;
|
|
|
|
-
|
|
/* Check to see the left operand is a column in a virtual table */
|
|
if( NEVER(pExpr==0) ) return pDef;
|
|
if( pExpr->op!=TK_COLUMN ) return pDef;
|
|
- pTab = pExpr->pTab;
|
|
+ pTab = pExpr->y.pTab;
|
|
if( pTab==0 ) return pDef;
|
|
if( !IsVirtual(pTab) ) return pDef;
|
|
pVtab = sqlite3GetVTable(db, pTab)->pVtab;
|
|
@@ -127077,16 +134608,22 @@
|
|
if( pMod->xFindFunction==0 ) return pDef;
|
|
|
|
/* Call the xFindFunction method on the virtual table implementation
|
|
- ** to see if the implementation wants to overload this function
|
|
+ ** to see if the implementation wants to overload this function.
|
|
+ **
|
|
+ ** Though undocumented, we have historically always invoked xFindFunction
|
|
+ ** with an all lower-case function name. Continue in this tradition to
|
|
+ ** avoid any chance of an incompatibility.
|
|
*/
|
|
- zLowerName = sqlite3DbStrDup(db, pDef->zName);
|
|
- if( zLowerName ){
|
|
- for(z=(unsigned char*)zLowerName; *z; z++){
|
|
- *z = sqlite3UpperToLower[*z];
|
|
+#ifdef SQLITE_DEBUG
|
|
+ {
|
|
+ int i;
|
|
+ for(i=0; pDef->zName[i]; i++){
|
|
+ unsigned char x = (unsigned char)pDef->zName[i];
|
|
+ assert( x==sqlite3UpperToLower[x] );
|
|
}
|
|
- rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xSFunc, &pArg);
|
|
- sqlite3DbFree(db, zLowerName);
|
|
}
|
|
+#endif
|
|
+ rc = pMod->xFindFunction(pVtab, nArg, pDef->zName, &xSFunc, &pArg);
|
|
if( rc==0 ){
|
|
return pDef;
|
|
}
|
|
@@ -127298,7 +134835,7 @@
|
|
** Trace output macros
|
|
*/
|
|
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
|
-/***/ int sqlite3WhereTrace;
|
|
+/***/ extern int sqlite3WhereTrace;
|
|
#endif
|
|
#if defined(SQLITE_DEBUG) \
|
|
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
|
|
@@ -127361,6 +134898,8 @@
|
|
struct InLoop {
|
|
int iCur; /* The VDBE cursor used by this IN operator */
|
|
int addrInTop; /* Top of the IN loop */
|
|
+ int iBase; /* Base register of multi-key index record */
|
|
+ int nPrefix; /* Number of prior entires in the key */
|
|
u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
|
|
} *aInLoop; /* Information about each nested IN operator */
|
|
} in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
|
|
@@ -127599,6 +135138,7 @@
|
|
WhereInfo *pWInfo; /* WHERE clause processing context */
|
|
WhereClause *pOuter; /* Outer conjunction */
|
|
u8 op; /* Split operator. TK_AND or TK_OR */
|
|
+ u8 hasOr; /* True if any a[].eOperator is WO_OR */
|
|
int nTerm; /* Number of terms */
|
|
int nSlot; /* Number of entries in a[] */
|
|
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
|
|
@@ -127678,6 +135218,7 @@
|
|
int nRecValid; /* Number of valid fields currently in pRec */
|
|
#endif
|
|
unsigned int bldFlags; /* SQLITE_BLDF_* flags */
|
|
+ unsigned int iPlanLimit; /* Search limiter */
|
|
};
|
|
|
|
/* Allowed values for WhereLoopBuider.bldFlags */
|
|
@@ -127684,6 +135225,26 @@
|
|
#define SQLITE_BLDF_INDEXED 0x0001 /* An index is used */
|
|
#define SQLITE_BLDF_UNIQUE 0x0002 /* All keys of a UNIQUE index used */
|
|
|
|
+/* The WhereLoopBuilder.iPlanLimit is used to limit the number of
|
|
+** index+constraint combinations the query planner will consider for a
|
|
+** particular query. If this parameter is unlimited, then certain
|
|
+** pathological queries can spend excess time in the sqlite3WhereBegin()
|
|
+** routine. The limit is high enough that is should not impact real-world
|
|
+** queries.
|
|
+**
|
|
+** SQLITE_QUERY_PLANNER_LIMIT is the baseline limit. The limit is
|
|
+** increased by SQLITE_QUERY_PLANNER_LIMIT_INCR before each term of the FROM
|
|
+** clause is processed, so that every table in a join is guaranteed to be
|
|
+** able to propose a some index+constraint combinations even if the initial
|
|
+** baseline limit was exhausted by prior tables of the join.
|
|
+*/
|
|
+#ifndef SQLITE_QUERY_PLANNER_LIMIT
|
|
+# define SQLITE_QUERY_PLANNER_LIMIT 20000
|
|
+#endif
|
|
+#ifndef SQLITE_QUERY_PLANNER_LIMIT_INCR
|
|
+# define SQLITE_QUERY_PLANNER_LIMIT_INCR 1000
|
|
+#endif
|
|
+
|
|
/*
|
|
** The WHERE clause processing routine has two halves. The
|
|
** first part does the start of the WHERE loop and the second
|
|
@@ -127746,12 +135307,10 @@
|
|
Parse *pParse, /* Parse context */
|
|
SrcList *pTabList, /* Table list this loop refers to */
|
|
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
|
- int iLevel, /* Value for "level" column of output */
|
|
- int iFrom, /* Value for "from" column of output */
|
|
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
|
);
|
|
#else
|
|
-# define sqlite3WhereExplainOneScan(u,v,w,x,y,z) 0
|
|
+# define sqlite3WhereExplainOneScan(u,v,w,x) 0
|
|
#endif /* SQLITE_OMIT_EXPLAIN */
|
|
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
|
SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
|
|
@@ -127774,6 +135333,7 @@
|
|
SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause*);
|
|
SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause*,Expr*,u8);
|
|
SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
|
|
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*);
|
|
SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
|
|
SQLITE_PRIVATE void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
|
|
SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
|
|
@@ -127794,7 +135354,6 @@
|
|
** WO_LE == SQLITE_INDEX_CONSTRAINT_LE
|
|
** WO_GT == SQLITE_INDEX_CONSTRAINT_GT
|
|
** WO_GE == SQLITE_INDEX_CONSTRAINT_GE
|
|
-** WO_MATCH == SQLITE_INDEX_CONSTRAINT_MATCH
|
|
*/
|
|
#define WO_IN 0x0001
|
|
#define WO_EQ 0x0002
|
|
@@ -127802,7 +135361,7 @@
|
|
#define WO_LE (WO_EQ<<(TK_LE-TK_EQ))
|
|
#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
|
|
#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
|
|
-#define WO_MATCH 0x0040
|
|
+#define WO_AUX 0x0040 /* Op useful to virtual tables only */
|
|
#define WO_IS 0x0080
|
|
#define WO_ISNULL 0x0100
|
|
#define WO_OR 0x0200 /* Two or more OR-connected terms */
|
|
@@ -127837,6 +135396,7 @@
|
|
#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
|
|
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
|
|
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
|
|
+#define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */
|
|
|
|
/************** End of whereInt.h ********************************************/
|
|
/************** Continuing where we left off in wherecode.c ******************/
|
|
@@ -127872,23 +135432,23 @@
|
|
int i;
|
|
|
|
assert( nTerm>=1 );
|
|
- if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
|
+ if( bAnd ) sqlite3_str_append(pStr, " AND ", 5);
|
|
|
|
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
|
+ if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
|
|
for(i=0; i<nTerm; i++){
|
|
- if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
|
- sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
|
+ if( i ) sqlite3_str_append(pStr, ",", 1);
|
|
+ sqlite3_str_appendall(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
|
}
|
|
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
|
+ if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
|
|
|
|
- sqlite3StrAccumAppend(pStr, zOp, 1);
|
|
+ sqlite3_str_append(pStr, zOp, 1);
|
|
|
|
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
|
+ if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
|
|
for(i=0; i<nTerm; i++){
|
|
- if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
|
- sqlite3StrAccumAppend(pStr, "?", 1);
|
|
+ if( i ) sqlite3_str_append(pStr, ",", 1);
|
|
+ sqlite3_str_append(pStr, "?", 1);
|
|
}
|
|
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
|
+ if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
|
|
}
|
|
|
|
/*
|
|
@@ -127912,11 +135472,11 @@
|
|
int i, j;
|
|
|
|
if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
|
|
- sqlite3StrAccumAppend(pStr, " (", 2);
|
|
+ sqlite3_str_append(pStr, " (", 2);
|
|
for(i=0; i<nEq; i++){
|
|
const char *z = explainIndexColumnName(pIndex, i);
|
|
- if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
|
- sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
|
|
+ if( i ) sqlite3_str_append(pStr, " AND ", 5);
|
|
+ sqlite3_str_appendf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
|
|
}
|
|
|
|
j = i;
|
|
@@ -127927,7 +135487,7 @@
|
|
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
|
|
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
|
|
}
|
|
- sqlite3StrAccumAppend(pStr, ")", 1);
|
|
+ sqlite3_str_append(pStr, ")", 1);
|
|
}
|
|
|
|
/*
|
|
@@ -127943,19 +135503,16 @@
|
|
Parse *pParse, /* Parse context */
|
|
SrcList *pTabList, /* Table list this loop refers to */
|
|
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
|
- int iLevel, /* Value for "level" column of output */
|
|
- int iFrom, /* Value for "from" column of output */
|
|
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
|
){
|
|
int ret = 0;
|
|
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
|
- if( pParse->explain==2 )
|
|
+ if( sqlite3ParseToplevel(pParse)->explain==2 )
|
|
#endif
|
|
{
|
|
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
|
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
|
sqlite3 *db = pParse->db; /* Database handle */
|
|
- int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
|
int isSearch; /* True for a SEARCH. False for SCAN. */
|
|
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
|
u32 flags; /* Flags that describe this loop */
|
|
@@ -127972,15 +135529,15 @@
|
|
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
|
|
|
|
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
|
- sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
|
+ sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN");
|
|
if( pItem->pSelect ){
|
|
- sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
|
|
+ sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId);
|
|
}else{
|
|
- sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
|
|
+ sqlite3_str_appendf(&str, " TABLE %s", pItem->zName);
|
|
}
|
|
|
|
if( pItem->zAlias ){
|
|
- sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
|
|
+ sqlite3_str_appendf(&str, " AS %s", pItem->zAlias);
|
|
}
|
|
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
|
const char *zFmt = 0;
|
|
@@ -128003,8 +135560,8 @@
|
|
zFmt = "INDEX %s";
|
|
}
|
|
if( zFmt ){
|
|
- sqlite3StrAccumAppend(&str, " USING ", 7);
|
|
- sqlite3XPrintf(&str, zFmt, pIdx->zName);
|
|
+ sqlite3_str_append(&str, " USING ", 7);
|
|
+ sqlite3_str_appendf(&str, zFmt, pIdx->zName);
|
|
explainIndexRange(&str, pLoop);
|
|
}
|
|
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
|
|
@@ -128019,23 +135576,26 @@
|
|
assert( flags&WHERE_TOP_LIMIT);
|
|
zRangeOp = "<";
|
|
}
|
|
- sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
|
|
+ sqlite3_str_appendf(&str,
|
|
+ " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
|
|
}
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
|
|
- sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
|
|
+ sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s",
|
|
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
|
|
}
|
|
#endif
|
|
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
|
|
if( pLoop->nOut>=10 ){
|
|
- sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
|
|
+ sqlite3_str_appendf(&str, " (~%llu rows)",
|
|
+ sqlite3LogEstToInt(pLoop->nOut));
|
|
}else{
|
|
- sqlite3StrAccumAppend(&str, " (~1 row)", 9);
|
|
+ sqlite3_str_append(&str, " (~1 row)", 9);
|
|
}
|
|
#endif
|
|
zMsg = sqlite3StrAccumFinish(&str);
|
|
- ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
|
|
+ ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
|
|
+ pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
|
|
}
|
|
return ret;
|
|
}
|
|
@@ -128115,8 +135675,8 @@
|
|
*/
|
|
static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
|
|
int nLoop = 0;
|
|
- while( ALWAYS(pTerm!=0)
|
|
- && (pTerm->wtFlags & TERM_CODED)==0
|
|
+ assert( pTerm!=0 );
|
|
+ while( (pTerm->wtFlags & TERM_CODED)==0
|
|
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
|
|
&& (pLevel->notReady & pTerm->prereqAll)==0
|
|
){
|
|
@@ -128127,6 +135687,7 @@
|
|
}
|
|
if( pTerm->iParent<0 ) break;
|
|
pTerm = &pTerm->pWC->a[pTerm->iParent];
|
|
+ assert( pTerm!=0 );
|
|
pTerm->nChild--;
|
|
if( pTerm->nChild!=0 ) break;
|
|
nLoop++;
|
|
@@ -128167,7 +135728,6 @@
|
|
/* Code the OP_Affinity opcode if there is anything left to do. */
|
|
if( n>0 ){
|
|
sqlite3VdbeAddOp4(v, OP_Affinity, base, n, 0, zAff, n);
|
|
- sqlite3ExprCacheAffinityChange(pParse, base, n);
|
|
}
|
|
}
|
|
|
|
@@ -128197,7 +135757,103 @@
|
|
}
|
|
}
|
|
|
|
+
|
|
/*
|
|
+** pX is an expression of the form: (vector) IN (SELECT ...)
|
|
+** In other words, it is a vector IN operator with a SELECT clause on the
|
|
+** LHS. But not all terms in the vector are indexable and the terms might
|
|
+** not be in the correct order for indexing.
|
|
+**
|
|
+** This routine makes a copy of the input pX expression and then adjusts
|
|
+** the vector on the LHS with corresponding changes to the SELECT so that
|
|
+** the vector contains only index terms and those terms are in the correct
|
|
+** order. The modified IN expression is returned. The caller is responsible
|
|
+** for deleting the returned expression.
|
|
+**
|
|
+** Example:
|
|
+**
|
|
+** CREATE TABLE t1(a,b,c,d,e,f);
|
|
+** CREATE INDEX t1x1 ON t1(e,c);
|
|
+** SELECT * FROM t1 WHERE (a,b,c,d,e) IN (SELECT v,w,x,y,z FROM t2)
|
|
+** \_______________________________________/
|
|
+** The pX expression
|
|
+**
|
|
+** Since only columns e and c can be used with the index, in that order,
|
|
+** the modified IN expression that is returned will be:
|
|
+**
|
|
+** (e,c) IN (SELECT z,x FROM t2)
|
|
+**
|
|
+** The reduced pX is different from the original (obviously) and thus is
|
|
+** only used for indexing, to improve performance. The original unaltered
|
|
+** IN expression must also be run on each output row for correctness.
|
|
+*/
|
|
+static Expr *removeUnindexableInClauseTerms(
|
|
+ Parse *pParse, /* The parsing context */
|
|
+ int iEq, /* Look at loop terms starting here */
|
|
+ WhereLoop *pLoop, /* The current loop */
|
|
+ Expr *pX /* The IN expression to be reduced */
|
|
+){
|
|
+ sqlite3 *db = pParse->db;
|
|
+ Expr *pNew = sqlite3ExprDup(db, pX, 0);
|
|
+ if( db->mallocFailed==0 ){
|
|
+ ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */
|
|
+ ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */
|
|
+ ExprList *pRhs = 0; /* New RHS after modifications */
|
|
+ ExprList *pLhs = 0; /* New LHS after mods */
|
|
+ int i; /* Loop counter */
|
|
+ Select *pSelect; /* Pointer to the SELECT on the RHS */
|
|
+
|
|
+ for(i=iEq; i<pLoop->nLTerm; i++){
|
|
+ if( pLoop->aLTerm[i]->pExpr==pX ){
|
|
+ int iField = pLoop->aLTerm[i]->iField - 1;
|
|
+ if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
|
|
+ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
|
|
+ pOrigRhs->a[iField].pExpr = 0;
|
|
+ assert( pOrigLhs->a[iField].pExpr!=0 );
|
|
+ pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr);
|
|
+ pOrigLhs->a[iField].pExpr = 0;
|
|
+ }
|
|
+ }
|
|
+ sqlite3ExprListDelete(db, pOrigRhs);
|
|
+ sqlite3ExprListDelete(db, pOrigLhs);
|
|
+ pNew->pLeft->x.pList = pLhs;
|
|
+ pNew->x.pSelect->pEList = pRhs;
|
|
+ if( pLhs && pLhs->nExpr==1 ){
|
|
+ /* Take care here not to generate a TK_VECTOR containing only a
|
|
+ ** single value. Since the parser never creates such a vector, some
|
|
+ ** of the subroutines do not handle this case. */
|
|
+ Expr *p = pLhs->a[0].pExpr;
|
|
+ pLhs->a[0].pExpr = 0;
|
|
+ sqlite3ExprDelete(db, pNew->pLeft);
|
|
+ pNew->pLeft = p;
|
|
+ }
|
|
+ pSelect = pNew->x.pSelect;
|
|
+ if( pSelect->pOrderBy ){
|
|
+ /* If the SELECT statement has an ORDER BY clause, zero the
|
|
+ ** iOrderByCol variables. These are set to non-zero when an
|
|
+ ** ORDER BY term exactly matches one of the terms of the
|
|
+ ** result-set. Since the result-set of the SELECT statement may
|
|
+ ** have been modified or reordered, these variables are no longer
|
|
+ ** set correctly. Since setting them is just an optimization,
|
|
+ ** it's easiest just to zero them here. */
|
|
+ ExprList *pOrderBy = pSelect->pOrderBy;
|
|
+ for(i=0; i<pOrderBy->nExpr; i++){
|
|
+ pOrderBy->a[i].u.x.iOrderByCol = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ printf("For indexing, change the IN expr:\n");
|
|
+ sqlite3TreeViewExpr(0, pX, 0);
|
|
+ printf("Into:\n");
|
|
+ sqlite3TreeViewExpr(0, pNew, 0);
|
|
+#endif
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
** Generate code for a single equality term of the WHERE clause. An equality
|
|
** term can be either X=expr or X IN (...). pTerm is the term to be
|
|
** coded.
|
|
@@ -128259,68 +135915,23 @@
|
|
}
|
|
}
|
|
for(i=iEq;i<pLoop->nLTerm; i++){
|
|
- if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
|
|
+ assert( pLoop->aLTerm[i]!=0 );
|
|
+ if( pLoop->aLTerm[i]->pExpr==pX ) nEq++;
|
|
}
|
|
|
|
if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
|
|
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
|
|
}else{
|
|
- Select *pSelect = pX->x.pSelect;
|
|
sqlite3 *db = pParse->db;
|
|
- u16 savedDbOptFlags = db->dbOptFlags;
|
|
- ExprList *pOrigRhs = pSelect->pEList;
|
|
- ExprList *pOrigLhs = pX->pLeft->x.pList;
|
|
- ExprList *pRhs = 0; /* New Select.pEList for RHS */
|
|
- ExprList *pLhs = 0; /* New pX->pLeft vector */
|
|
+ pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX);
|
|
|
|
- for(i=iEq;i<pLoop->nLTerm; i++){
|
|
- if( pLoop->aLTerm[i]->pExpr==pX ){
|
|
- int iField = pLoop->aLTerm[i]->iField - 1;
|
|
- Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
|
|
- Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
|
|
-
|
|
- pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
|
|
- pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
|
|
- }
|
|
- }
|
|
if( !db->mallocFailed ){
|
|
- Expr *pLeft = pX->pLeft;
|
|
-
|
|
- if( pSelect->pOrderBy ){
|
|
- /* If the SELECT statement has an ORDER BY clause, zero the
|
|
- ** iOrderByCol variables. These are set to non-zero when an
|
|
- ** ORDER BY term exactly matches one of the terms of the
|
|
- ** result-set. Since the result-set of the SELECT statement may
|
|
- ** have been modified or reordered, these variables are no longer
|
|
- ** set correctly. Since setting them is just an optimization,
|
|
- ** it's easiest just to zero them here. */
|
|
- ExprList *pOrderBy = pSelect->pOrderBy;
|
|
- for(i=0; i<pOrderBy->nExpr; i++){
|
|
- pOrderBy->a[i].u.x.iOrderByCol = 0;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Take care here not to generate a TK_VECTOR containing only a
|
|
- ** single value. Since the parser never creates such a vector, some
|
|
- ** of the subroutines do not handle this case. */
|
|
- if( pLhs->nExpr==1 ){
|
|
- pX->pLeft = pLhs->a[0].pExpr;
|
|
- }else{
|
|
- pLeft->x.pList = pLhs;
|
|
- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
|
|
- testcase( aiMap==0 );
|
|
- }
|
|
- pSelect->pEList = pRhs;
|
|
- db->dbOptFlags |= SQLITE_QueryFlattener;
|
|
+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
|
|
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
|
|
- db->dbOptFlags = savedDbOptFlags;
|
|
- testcase( aiMap!=0 && aiMap[0]!=0 );
|
|
- pSelect->pEList = pOrigRhs;
|
|
- pLeft->x.pList = pOrigLhs;
|
|
- pX->pLeft = pLeft;
|
|
+ pTerm->pExpr->iTable = pX->iTable;
|
|
}
|
|
- sqlite3ExprListDelete(pParse->db, pLhs);
|
|
- sqlite3ExprListDelete(pParse->db, pRhs);
|
|
+ sqlite3ExprDelete(db, pX);
|
|
+ pX = pTerm->pExpr;
|
|
}
|
|
|
|
if( eType==IN_INDEX_INDEX_DESC ){
|
|
@@ -128360,7 +135971,14 @@
|
|
sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
|
|
if( i==iEq ){
|
|
pIn->iCur = iTab;
|
|
- pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
|
|
+ pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next;
|
|
+ if( iEq>0 && (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){
|
|
+ pIn->iBase = iReg - i;
|
|
+ pIn->nPrefix = i;
|
|
+ pLoop->wsFlags |= WHERE_IN_EARLYOUT;
|
|
+ }else{
|
|
+ pIn->nPrefix = 0;
|
|
+ }
|
|
}else{
|
|
pIn->eEndLoopOp = OP_Noop;
|
|
}
|
|
@@ -128615,7 +136233,7 @@
|
|
pWalker->eCode = 1;
|
|
}else if( pExpr->op==TK_FUNCTION ){
|
|
int d1;
|
|
- char d2[3];
|
|
+ char d2[4];
|
|
if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
|
|
pWalker->eCode = 1;
|
|
}
|
|
@@ -128647,11 +136265,8 @@
|
|
struct CCurHint *pHint = pWalker->u.pCCurHint;
|
|
if( pExpr->op==TK_COLUMN ){
|
|
if( pExpr->iTable!=pHint->iTabCur ){
|
|
- Vdbe *v = pWalker->pParse->pVdbe;
|
|
int reg = ++pWalker->pParse->nMem; /* Register for column value */
|
|
- sqlite3ExprCodeGetColumnOfTable(
|
|
- v, pExpr->pTab, pExpr->iTable, pExpr->iColumn, reg
|
|
- );
|
|
+ sqlite3ExprCode(pWalker->pParse, pExpr, reg);
|
|
pExpr->op = TK_REGISTER;
|
|
pExpr->iTable = reg;
|
|
}else if( pHint->pIdx!=0 ){
|
|
@@ -128838,7 +136453,7 @@
|
|
*/
|
|
static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
|
|
assert( nReg>0 );
|
|
- if( sqlite3ExprIsVector(p) ){
|
|
+ if( p && sqlite3ExprIsVector(p) ){
|
|
#ifndef SQLITE_OMIT_SUBQUERY
|
|
if( (p->flags & EP_xIsSelect) ){
|
|
Vdbe *v = pParse->pVdbe;
|
|
@@ -128883,7 +136498,7 @@
|
|
pExpr->op = TK_COLUMN;
|
|
pExpr->iTable = pX->iIdxCur;
|
|
pExpr->iColumn = pX->iIdxCol;
|
|
- pExpr->pTab = 0;
|
|
+ pExpr->y.pTab = 0;
|
|
return WRC_Prune;
|
|
}else{
|
|
return WRC_Continue;
|
|
@@ -128891,9 +136506,9 @@
|
|
}
|
|
|
|
/*
|
|
-** For an indexes on expression X, locate every instance of expression X in pExpr
|
|
-** and change that subexpression into a reference to the appropriate column of
|
|
-** the index.
|
|
+** For an indexes on expression X, locate every instance of expression X
|
|
+** in pExpr and change that subexpression into a reference to the appropriate
|
|
+** column of the index.
|
|
*/
|
|
static void whereIndexExprTrans(
|
|
Index *pIdx, /* The Index */
|
|
@@ -128984,6 +136599,9 @@
|
|
** initialize a memory cell that records if this table matches any
|
|
** row of the left table of the join.
|
|
*/
|
|
+ assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
|
|
+ || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0
|
|
+ );
|
|
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
|
|
pLevel->iLeftJoin = ++pParse->nMem;
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
|
|
@@ -129001,7 +136619,7 @@
|
|
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
|
|
pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
|
|
VdbeCoverage(v);
|
|
- VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
|
|
+ VdbeComment((v, "next row of %s", pTabItem->pTab->zName));
|
|
pLevel->op = OP_Goto;
|
|
}else
|
|
|
|
@@ -129015,7 +136633,6 @@
|
|
int nConstraint = pLoop->nLTerm;
|
|
int iIn; /* Counter for IN constraints */
|
|
|
|
- sqlite3ExprCachePush(pParse);
|
|
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
|
|
addrNotFound = pLevel->addrBrk;
|
|
for(j=0; j<nConstraint; j++){
|
|
@@ -129088,7 +136705,6 @@
|
|
**
|
|
** sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
|
|
*/
|
|
- sqlite3ExprCachePop(pParse);
|
|
}else
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
|
|
@@ -129112,9 +136728,6 @@
|
|
addrNxt = pLevel->addrNxt;
|
|
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
|
|
VdbeCoverage(v);
|
|
- sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
|
|
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
|
- VdbeComment((v, "pk"));
|
|
pLevel->op = OP_Noop;
|
|
}else if( (pLoop->wsFlags & WHERE_IPK)!=0
|
|
&& (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
|
|
@@ -129164,7 +136777,15 @@
|
|
if( sqlite3ExprIsVector(pX->pRight) ){
|
|
r1 = rTemp = sqlite3GetTempReg(pParse);
|
|
codeExprOrVector(pParse, pX->pRight, r1, 1);
|
|
- op = aMoveOp[(pX->op - TK_GT) | 0x0001];
|
|
+ testcase( pX->op==TK_GT );
|
|
+ testcase( pX->op==TK_GE );
|
|
+ testcase( pX->op==TK_LT );
|
|
+ testcase( pX->op==TK_LE );
|
|
+ op = aMoveOp[((pX->op - TK_GT - 1) & 0x3) | 0x1];
|
|
+ assert( pX->op!=TK_GT || op==OP_SeekGE );
|
|
+ assert( pX->op!=TK_GE || op==OP_SeekGE );
|
|
+ assert( pX->op!=TK_LT || op==OP_SeekLE );
|
|
+ assert( pX->op!=TK_LE || op==OP_SeekLE );
|
|
}else{
|
|
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
|
|
disableTerm(pLevel, pStart);
|
|
@@ -129176,7 +136797,6 @@
|
|
VdbeCoverageIf(v, pX->op==TK_LE);
|
|
VdbeCoverageIf(v, pX->op==TK_LT);
|
|
VdbeCoverageIf(v, pX->op==TK_GE);
|
|
- sqlite3ExprCacheAffinityChange(pParse, r1, 1);
|
|
sqlite3ReleaseTempReg(pParse, rTemp);
|
|
}else{
|
|
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt);
|
|
@@ -129211,7 +136831,6 @@
|
|
if( testOp!=OP_Noop ){
|
|
iRowidReg = ++pParse->nMem;
|
|
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
|
|
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
|
sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
|
|
VdbeCoverageIf(v, testOp==OP_Le);
|
|
VdbeCoverageIf(v, testOp==OP_Lt);
|
|
@@ -129416,6 +137035,9 @@
|
|
** above has already left the cursor sitting on the correct row,
|
|
** so no further seeking is needed */
|
|
}else{
|
|
+ if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
|
|
+ sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur);
|
|
+ }
|
|
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
|
|
assert( op!=0 );
|
|
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
|
|
@@ -129434,7 +137056,6 @@
|
|
nConstraint = nEq;
|
|
if( pRangeEnd ){
|
|
Expr *pRight = pRangeEnd->pExpr->pRight;
|
|
- sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
|
|
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
|
|
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
|
|
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
|
|
@@ -129478,6 +137099,10 @@
|
|
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
|
|
}
|
|
|
|
+ if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
|
|
+ sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1);
|
|
+ }
|
|
+
|
|
/* Seek the table cursor, if required */
|
|
if( omitTable ){
|
|
/* pIdx is a covering index. No need to access the main table. */
|
|
@@ -129488,7 +137113,6 @@
|
|
)){
|
|
iRowidReg = ++pParse->nMem;
|
|
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
|
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
|
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
|
|
VdbeCoverage(v);
|
|
}else{
|
|
@@ -129508,10 +137132,17 @@
|
|
/* If pIdx is an index on one or more expressions, then look through
|
|
** all the expressions in pWInfo and try to transform matching expressions
|
|
** into reference to index columns.
|
|
+ **
|
|
+ ** Do not do this for the RHS of a LEFT JOIN. This is because the
|
|
+ ** expression may be evaluated after OP_NullRow has been executed on
|
|
+ ** the cursor. In this case it is important to do the full evaluation,
|
|
+ ** as the result of the expression may not be NULL, even if all table
|
|
+ ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a
|
|
*/
|
|
- whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
|
|
+ if( pLevel->iLeftJoin==0 ){
|
|
+ whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
|
|
+ }
|
|
|
|
-
|
|
/* Record the instruction used to terminate the loop. */
|
|
if( pLoop->wsFlags & WHERE_ONEROW ){
|
|
pLevel->op = OP_Noop;
|
|
@@ -129666,7 +137297,6 @@
|
|
for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
|
|
Expr *pExpr = pWC->a[iTerm].pExpr;
|
|
if( &pWC->a[iTerm] == pTerm ) continue;
|
|
- if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
|
|
testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
|
|
testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
|
|
if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
|
|
@@ -129685,6 +137315,7 @@
|
|
** sub-WHERE clause is to to invoke the main loop body as a subroutine.
|
|
*/
|
|
wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
|
|
+ ExplainQueryPlan((pParse, 1, "MULTI-INDEX OR"));
|
|
for(ii=0; ii<pOrWc->nTerm; ii++){
|
|
WhereTerm *pOrTerm = &pOrWc->a[ii];
|
|
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
|
|
@@ -129691,7 +137322,10 @@
|
|
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
|
|
Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
|
|
int jmp1 = 0; /* Address of jump operation */
|
|
- if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
|
|
+ assert( (pTabItem[0].fg.jointype & JT_LEFT)==0
|
|
+ || ExprHasProperty(pOrExpr, EP_FromJoin)
|
|
+ );
|
|
+ if( pAndExpr ){
|
|
pAndExpr->pLeft = pOrExpr;
|
|
pOrExpr = pAndExpr;
|
|
}
|
|
@@ -129703,7 +137337,7 @@
|
|
if( pSubWInfo ){
|
|
WhereLoop *pSubLoop;
|
|
int addrExplain = sqlite3WhereExplainOneScan(
|
|
- pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
|
+ pParse, pOrTab, &pSubWInfo->a[0], 0
|
|
);
|
|
sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
|
|
|
|
@@ -129713,23 +137347,23 @@
|
|
** row will be skipped in subsequent sub-WHERE clauses.
|
|
*/
|
|
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
|
- int r;
|
|
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
|
if( HasRowid(pTab) ){
|
|
- r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
|
|
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, regRowid);
|
|
jmp1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0,
|
|
- r,iSet);
|
|
+ regRowid, iSet);
|
|
VdbeCoverage(v);
|
|
}else{
|
|
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
int nPk = pPk->nKeyCol;
|
|
int iPk;
|
|
+ int r;
|
|
|
|
/* Read the PK into an array of temp registers. */
|
|
r = sqlite3GetTempRange(pParse, nPk);
|
|
for(iPk=0; iPk<nPk; iPk++){
|
|
int iCol = pPk->aiColumn[iPk];
|
|
- sqlite3ExprCodeGetColumnToReg(pParse, pTab, iCol, iCur, r+iPk);
|
|
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, r+iPk);
|
|
}
|
|
|
|
/* Check if the temp table already contains this key. If so,
|
|
@@ -129802,6 +137436,7 @@
|
|
}
|
|
}
|
|
}
|
|
+ ExplainQueryPlanPop(pParse);
|
|
pLevel->u.pCovidx = pCov;
|
|
if( pCov ) pLevel->iIdxCur = iCovCur;
|
|
if( pAndExpr ){
|
|
@@ -129874,7 +137509,7 @@
|
|
}
|
|
pE = pTerm->pExpr;
|
|
assert( pE!=0 );
|
|
- if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
|
|
+ if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){
|
|
continue;
|
|
}
|
|
|
|
@@ -129887,7 +137522,7 @@
|
|
continue;
|
|
}
|
|
|
|
- if( pTerm->wtFlags & TERM_LIKECOND ){
|
|
+ if( (pTerm->wtFlags & TERM_LIKECOND)!=0 ){
|
|
/* If the TERM_LIKECOND flag is set, that means that the range search
|
|
** is sufficient to guarantee that the LIKE operator is true, so we
|
|
** can skip the call to the like(A,B) function. But this only works
|
|
@@ -129897,8 +137532,9 @@
|
|
continue;
|
|
#else
|
|
u32 x = pLevel->iLikeRepCntr;
|
|
- assert( x>0 );
|
|
- skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If, (int)(x>>1));
|
|
+ if( x>0 ){
|
|
+ skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1));
|
|
+ }
|
|
VdbeCoverage(v);
|
|
#endif
|
|
}
|
|
@@ -129938,6 +137574,12 @@
|
|
WO_EQ|WO_IN|WO_IS, 0);
|
|
if( pAlt==0 ) continue;
|
|
if( pAlt->wtFlags & (TERM_CODED) ) continue;
|
|
+ if( (pAlt->eOperator & WO_IN)
|
|
+ && (pAlt->pExpr->flags & EP_xIsSelect)
|
|
+ && (pAlt->pExpr->x.pSelect->pEList->nExpr>1)
|
|
+ ){
|
|
+ continue;
|
|
+ }
|
|
testcase( pAlt->eOperator & WO_EQ );
|
|
testcase( pAlt->eOperator & WO_IS );
|
|
testcase( pAlt->eOperator & WO_IN );
|
|
@@ -129954,7 +137596,6 @@
|
|
pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
|
|
sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
|
|
VdbeComment((v, "record LEFT JOIN hit"));
|
|
- sqlite3ExprCacheClear(pParse);
|
|
for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
|
|
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
|
testcase( pTerm->wtFlags & TERM_CODED );
|
|
@@ -130170,18 +137811,18 @@
|
|
int *pisComplete, /* True if the only wildcard is % in the last character */
|
|
int *pnoCase /* True if uppercase is equivalent to lowercase */
|
|
){
|
|
- const char *z = 0; /* String on RHS of LIKE operator */
|
|
+ const u8 *z = 0; /* String on RHS of LIKE operator */
|
|
Expr *pRight, *pLeft; /* Right and left size of LIKE operator */
|
|
ExprList *pList; /* List of operands to the LIKE operator */
|
|
- int c; /* One character in z[] */
|
|
+ u8 c; /* One character in z[] */
|
|
int cnt; /* Number of non-wildcard prefix characters */
|
|
- char wc[3]; /* Wildcard characters */
|
|
+ u8 wc[4]; /* Wildcard characters */
|
|
sqlite3 *db = pParse->db; /* Database connection */
|
|
sqlite3_value *pVal = 0;
|
|
int op; /* Opcode of pRight */
|
|
int rc; /* Result code to return */
|
|
|
|
- if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
|
|
+ if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, (char*)wc) ){
|
|
return 0;
|
|
}
|
|
#ifdef SQLITE_EBCDIC
|
|
@@ -130197,41 +137838,78 @@
|
|
int iCol = pRight->iColumn;
|
|
pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
|
|
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
|
|
- z = (char *)sqlite3_value_text(pVal);
|
|
+ z = sqlite3_value_text(pVal);
|
|
}
|
|
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
|
|
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
|
|
}else if( op==TK_STRING ){
|
|
- z = pRight->u.zToken;
|
|
+ z = (u8*)pRight->u.zToken;
|
|
}
|
|
if( z ){
|
|
|
|
- /* If the RHS begins with a digit or a minus sign, then the LHS must
|
|
- ** be an ordinary column (not a virtual table column) with TEXT affinity.
|
|
- ** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
|
|
- ** even though "lhs LIKE rhs" is true. But if the RHS does not start
|
|
- ** with a digit or '-', then "lhs LIKE rhs" will always be false if
|
|
- ** the LHS is numeric and so the optimization still works.
|
|
- */
|
|
- if( sqlite3Isdigit(z[0]) || z[0]=='-' ){
|
|
- if( pLeft->op!=TK_COLUMN
|
|
- || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|
|
- || IsVirtual(pLeft->pTab) /* Value might be numeric */
|
|
- ){
|
|
- sqlite3ValueFree(pVal);
|
|
- return 0;
|
|
- }
|
|
- }
|
|
+ /* Count the number of prefix characters prior to the first wildcard */
|
|
cnt = 0;
|
|
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
|
|
cnt++;
|
|
+ if( c==wc[3] && z[cnt]!=0 ) cnt++;
|
|
}
|
|
- if( cnt!=0 && 255!=(u8)z[cnt-1] ){
|
|
+
|
|
+ /* The optimization is possible only if (1) the pattern does not begin
|
|
+ ** with a wildcard and if (2) the non-wildcard prefix does not end with
|
|
+ ** an (illegal 0xff) character, or (3) the pattern does not consist of
|
|
+ ** a single escape character. The second condition is necessary so
|
|
+ ** that we can increment the prefix key to find an upper bound for the
|
|
+ ** range search. The third is because the caller assumes that the pattern
|
|
+ ** consists of at least one character after all escapes have been
|
|
+ ** removed. */
|
|
+ if( cnt!=0 && 255!=(u8)z[cnt-1] && (cnt>1 || z[0]!=wc[3]) ){
|
|
Expr *pPrefix;
|
|
+
|
|
+ /* A "complete" match if the pattern ends with "*" or "%" */
|
|
*pisComplete = c==wc[0] && z[cnt+1]==0;
|
|
- pPrefix = sqlite3Expr(db, TK_STRING, z);
|
|
- if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
|
|
+
|
|
+ /* Get the pattern prefix. Remove all escapes from the prefix. */
|
|
+ pPrefix = sqlite3Expr(db, TK_STRING, (char*)z);
|
|
+ if( pPrefix ){
|
|
+ int iFrom, iTo;
|
|
+ char *zNew = pPrefix->u.zToken;
|
|
+ zNew[cnt] = 0;
|
|
+ for(iFrom=iTo=0; iFrom<cnt; iFrom++){
|
|
+ if( zNew[iFrom]==wc[3] ) iFrom++;
|
|
+ zNew[iTo++] = zNew[iFrom];
|
|
+ }
|
|
+ zNew[iTo] = 0;
|
|
+
|
|
+ /* If the RHS begins with a digit or a minus sign, then the LHS must be
|
|
+ ** an ordinary column (not a virtual table column) with TEXT affinity.
|
|
+ ** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
|
|
+ ** even though "lhs LIKE rhs" is true. But if the RHS does not start
|
|
+ ** with a digit or '-', then "lhs LIKE rhs" will always be false if
|
|
+ ** the LHS is numeric and so the optimization still works.
|
|
+ **
|
|
+ ** 2018-09-10 ticket c94369cae9b561b1f996d0054bfab11389f9d033
|
|
+ ** The RHS pattern must not be '/%' because the termination condition
|
|
+ ** will then become "x<'0'" and if the affinity is numeric, will then
|
|
+ ** be converted into "x<0", which is incorrect.
|
|
+ */
|
|
+ if( sqlite3Isdigit(zNew[0])
|
|
+ || zNew[0]=='-'
|
|
+ || (zNew[0]+1=='0' && iTo==1)
|
|
+ ){
|
|
+ if( pLeft->op!=TK_COLUMN
|
|
+ || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|
|
+ || IsVirtual(pLeft->y.pTab) /* Value might be numeric */
|
|
+ ){
|
|
+ sqlite3ExprDelete(db, pPrefix);
|
|
+ sqlite3ValueFree(pVal);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
*ppPrefix = pPrefix;
|
|
+
|
|
+ /* If the RHS pattern is a bound parameter, make arrangements to
|
|
+ ** reprepare the statement when that parameter is rebound */
|
|
if( op==TK_VARIABLE ){
|
|
Vdbe *v = pParse->pVdbe;
|
|
sqlite3VdbeSetVarmask(v, pRight->iColumn);
|
|
@@ -130262,48 +137940,123 @@
|
|
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
/*
|
|
-** Check to see if the given expression is of the form
|
|
+** Check to see if the pExpr expression is a form that needs to be passed
|
|
+** to the xBestIndex method of virtual tables. Forms of interest include:
|
|
**
|
|
-** column OP expr
|
|
+** Expression Virtual Table Operator
|
|
+** ----------------------- ---------------------------------
|
|
+** 1. column MATCH expr SQLITE_INDEX_CONSTRAINT_MATCH
|
|
+** 2. column GLOB expr SQLITE_INDEX_CONSTRAINT_GLOB
|
|
+** 3. column LIKE expr SQLITE_INDEX_CONSTRAINT_LIKE
|
|
+** 4. column REGEXP expr SQLITE_INDEX_CONSTRAINT_REGEXP
|
|
+** 5. column != expr SQLITE_INDEX_CONSTRAINT_NE
|
|
+** 6. expr != column SQLITE_INDEX_CONSTRAINT_NE
|
|
+** 7. column IS NOT expr SQLITE_INDEX_CONSTRAINT_ISNOT
|
|
+** 8. expr IS NOT column SQLITE_INDEX_CONSTRAINT_ISNOT
|
|
+** 9. column IS NOT NULL SQLITE_INDEX_CONSTRAINT_ISNOTNULL
|
|
**
|
|
-** where OP is one of MATCH, GLOB, LIKE or REGEXP and "column" is a
|
|
-** column of a virtual table.
|
|
+** In every case, "column" must be a column of a virtual table. If there
|
|
+** is a match, set *ppLeft to the "column" expression, set *ppRight to the
|
|
+** "expr" expression (even though in forms (6) and (8) the column is on the
|
|
+** right and the expression is on the left). Also set *peOp2 to the
|
|
+** appropriate virtual table operator. The return value is 1 or 2 if there
|
|
+** is a match. The usual return is 1, but if the RHS is also a column
|
|
+** of virtual table in forms (5) or (7) then return 2.
|
|
**
|
|
-** If it is then return TRUE. If not, return FALSE.
|
|
+** If the expression matches none of the patterns above, return 0.
|
|
*/
|
|
-static int isMatchOfColumn(
|
|
+static int isAuxiliaryVtabOperator(
|
|
+ sqlite3 *db, /* Parsing context */
|
|
Expr *pExpr, /* Test this expression */
|
|
- unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */
|
|
+ unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */
|
|
+ Expr **ppLeft, /* Column expression to left of MATCH/op2 */
|
|
+ Expr **ppRight /* Expression to left of MATCH/op2 */
|
|
){
|
|
- static const struct Op2 {
|
|
- const char *zOp;
|
|
- unsigned char eOp2;
|
|
- } aOp[] = {
|
|
- { "match", SQLITE_INDEX_CONSTRAINT_MATCH },
|
|
- { "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
|
|
- { "like", SQLITE_INDEX_CONSTRAINT_LIKE },
|
|
- { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
|
|
- };
|
|
- ExprList *pList;
|
|
- Expr *pCol; /* Column reference */
|
|
- int i;
|
|
+ if( pExpr->op==TK_FUNCTION ){
|
|
+ static const struct Op2 {
|
|
+ const char *zOp;
|
|
+ unsigned char eOp2;
|
|
+ } aOp[] = {
|
|
+ { "match", SQLITE_INDEX_CONSTRAINT_MATCH },
|
|
+ { "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
|
|
+ { "like", SQLITE_INDEX_CONSTRAINT_LIKE },
|
|
+ { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
|
|
+ };
|
|
+ ExprList *pList;
|
|
+ Expr *pCol; /* Column reference */
|
|
+ int i;
|
|
|
|
- if( pExpr->op!=TK_FUNCTION ){
|
|
- return 0;
|
|
- }
|
|
- pList = pExpr->x.pList;
|
|
- if( pList==0 || pList->nExpr!=2 ){
|
|
- return 0;
|
|
- }
|
|
- pCol = pList->a[1].pExpr;
|
|
- if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){
|
|
- return 0;
|
|
- }
|
|
- for(i=0; i<ArraySize(aOp); i++){
|
|
- if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
|
|
- *peOp2 = aOp[i].eOp2;
|
|
- return 1;
|
|
+ pList = pExpr->x.pList;
|
|
+ if( pList==0 || pList->nExpr!=2 ){
|
|
+ return 0;
|
|
}
|
|
+
|
|
+ /* Built-in operators MATCH, GLOB, LIKE, and REGEXP attach to a
|
|
+ ** virtual table on their second argument, which is the same as
|
|
+ ** the left-hand side operand in their in-fix form.
|
|
+ **
|
|
+ ** vtab_column MATCH expression
|
|
+ ** MATCH(expression,vtab_column)
|
|
+ */
|
|
+ pCol = pList->a[1].pExpr;
|
|
+ if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){
|
|
+ for(i=0; i<ArraySize(aOp); i++){
|
|
+ if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
|
|
+ *peOp2 = aOp[i].eOp2;
|
|
+ *ppRight = pList->a[0].pExpr;
|
|
+ *ppLeft = pCol;
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* We can also match against the first column of overloaded
|
|
+ ** functions where xFindFunction returns a value of at least
|
|
+ ** SQLITE_INDEX_CONSTRAINT_FUNCTION.
|
|
+ **
|
|
+ ** OVERLOADED(vtab_column,expression)
|
|
+ **
|
|
+ ** Historically, xFindFunction expected to see lower-case function
|
|
+ ** names. But for this use case, xFindFunction is expected to deal
|
|
+ ** with function names in an arbitrary case.
|
|
+ */
|
|
+ pCol = pList->a[0].pExpr;
|
|
+ if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){
|
|
+ sqlite3_vtab *pVtab;
|
|
+ sqlite3_module *pMod;
|
|
+ void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**);
|
|
+ void *pNotUsed;
|
|
+ pVtab = sqlite3GetVTable(db, pCol->y.pTab)->pVtab;
|
|
+ assert( pVtab!=0 );
|
|
+ assert( pVtab->pModule!=0 );
|
|
+ pMod = (sqlite3_module *)pVtab->pModule;
|
|
+ if( pMod->xFindFunction!=0 ){
|
|
+ i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed);
|
|
+ if( i>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){
|
|
+ *peOp2 = i;
|
|
+ *ppRight = pList->a[1].pExpr;
|
|
+ *ppLeft = pCol;
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){
|
|
+ int res = 0;
|
|
+ Expr *pLeft = pExpr->pLeft;
|
|
+ Expr *pRight = pExpr->pRight;
|
|
+ if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){
|
|
+ res++;
|
|
+ }
|
|
+ if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){
|
|
+ res++;
|
|
+ SWAP(Expr*, pLeft, pRight);
|
|
+ }
|
|
+ *ppLeft = pLeft;
|
|
+ *ppRight = pRight;
|
|
+ if( pExpr->op==TK_NE ) *peOp2 = SQLITE_INDEX_CONSTRAINT_NE;
|
|
+ if( pExpr->op==TK_ISNOT ) *peOp2 = SQLITE_INDEX_CONSTRAINT_ISNOT;
|
|
+ if( pExpr->op==TK_NOTNULL ) *peOp2 = SQLITE_INDEX_CONSTRAINT_ISNOTNULL;
|
|
+ return res;
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -130554,7 +138307,7 @@
|
|
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
|
|
assert( pAndTerm->pExpr );
|
|
if( allowedOp(pAndTerm->pExpr->op)
|
|
- || pAndTerm->eOperator==WO_MATCH
|
|
+ || pAndTerm->eOperator==WO_AUX
|
|
){
|
|
b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
|
|
}
|
|
@@ -130586,7 +138339,12 @@
|
|
** empty.
|
|
*/
|
|
pOrInfo->indexable = indexable;
|
|
- pTerm->eOperator = indexable==0 ? 0 : WO_OR;
|
|
+ if( indexable ){
|
|
+ pTerm->eOperator = WO_OR;
|
|
+ pWC->hasOr = 1;
|
|
+ }else{
|
|
+ pTerm->eOperator = WO_OR;
|
|
+ }
|
|
|
|
/* For a two-way OR, attempt to implementation case 2.
|
|
*/
|
|
@@ -130727,12 +138485,11 @@
|
|
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
|
testcase( idxNew==0 );
|
|
exprAnalyze(pSrc, pWC, idxNew);
|
|
- pTerm = &pWC->a[idxTerm];
|
|
+ /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */
|
|
markTermAsChild(pWC, idxNew, idxTerm);
|
|
}else{
|
|
sqlite3ExprListDelete(db, pList);
|
|
}
|
|
- pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */
|
|
}
|
|
}
|
|
}
|
|
@@ -130756,7 +138513,6 @@
|
|
static int termIsEquivalence(Parse *pParse, Expr *pExpr){
|
|
char aff1, aff2;
|
|
CollSeq *pColl;
|
|
- const char *zColl1, *zColl2;
|
|
if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
|
|
if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
|
|
if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
|
|
@@ -130768,12 +138524,8 @@
|
|
return 0;
|
|
}
|
|
pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
|
|
- if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
|
|
- pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
|
- zColl1 = pColl ? pColl->zName : 0;
|
|
- pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
|
|
- zColl2 = pColl ? pColl->zName : 0;
|
|
- return sqlite3_stricmp(zColl1, zColl2)==0;
|
|
+ if( sqlite3IsBinary(pColl) ) return 1;
|
|
+ return sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight);
|
|
}
|
|
|
|
/*
|
|
@@ -130795,6 +138547,9 @@
|
|
for(i=0; i<pSrc->nSrc; i++){
|
|
mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect);
|
|
mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn);
|
|
+ if( pSrc->a[i].fg.isTabFunc ){
|
|
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg);
|
|
+ }
|
|
}
|
|
}
|
|
pS = pS->pPrior;
|
|
@@ -130902,7 +138657,7 @@
|
|
int op; /* Top-level operator. pExpr->op */
|
|
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
|
sqlite3 *db = pParse->db; /* Database connection */
|
|
- unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */
|
|
+ unsigned char eOp2 = 0; /* op2 value for LIKE/REGEXP/GLOB */
|
|
int nLeft; /* Number of elements on left side vector */
|
|
|
|
if( db->mallocFailed ){
|
|
@@ -130928,7 +138683,7 @@
|
|
pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight);
|
|
}
|
|
pMaskSet->bVarSelect = 0;
|
|
- prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr);
|
|
+ prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr);
|
|
if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT;
|
|
if( ExprHasProperty(pExpr, EP_FromJoin) ){
|
|
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable);
|
|
@@ -131110,7 +138865,7 @@
|
|
}
|
|
*pC = c + 1;
|
|
}
|
|
- zCollSeqName = noCase ? "NOCASE" : "BINARY";
|
|
+ zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY;
|
|
pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
|
|
pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
|
|
sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
|
|
@@ -131136,41 +138891,46 @@
|
|
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
|
|
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- /* Add a WO_MATCH auxiliary term to the constraint set if the
|
|
- ** current expression is of the form: column MATCH expr.
|
|
+ /* Add a WO_AUX auxiliary term to the constraint set if the
|
|
+ ** current expression is of the form "column OP expr" where OP
|
|
+ ** is an operator that gets passed into virtual tables but which is
|
|
+ ** not normally optimized for ordinary tables. In other words, OP
|
|
+ ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL.
|
|
** This information is used by the xBestIndex methods of
|
|
** virtual tables. The native query optimizer does not attempt
|
|
** to do anything with MATCH functions.
|
|
*/
|
|
- if( pWC->op==TK_AND && isMatchOfColumn(pExpr, &eOp2) ){
|
|
- int idxNew;
|
|
- Expr *pRight, *pLeft;
|
|
- WhereTerm *pNewTerm;
|
|
- Bitmask prereqColumn, prereqExpr;
|
|
+ if( pWC->op==TK_AND ){
|
|
+ Expr *pRight = 0, *pLeft = 0;
|
|
+ int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
|
|
+ while( res-- > 0 ){
|
|
+ int idxNew;
|
|
+ WhereTerm *pNewTerm;
|
|
+ Bitmask prereqColumn, prereqExpr;
|
|
|
|
- pRight = pExpr->x.pList->a[0].pExpr;
|
|
- pLeft = pExpr->x.pList->a[1].pExpr;
|
|
- prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight);
|
|
- prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
|
|
- if( (prereqExpr & prereqColumn)==0 ){
|
|
- Expr *pNewExpr;
|
|
- pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
|
|
- 0, sqlite3ExprDup(db, pRight, 0));
|
|
- if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){
|
|
- ExprSetProperty(pNewExpr, EP_FromJoin);
|
|
+ prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight);
|
|
+ prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
|
|
+ if( (prereqExpr & prereqColumn)==0 ){
|
|
+ Expr *pNewExpr;
|
|
+ pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
|
|
+ 0, sqlite3ExprDup(db, pRight, 0));
|
|
+ if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){
|
|
+ ExprSetProperty(pNewExpr, EP_FromJoin);
|
|
+ }
|
|
+ idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
|
+ testcase( idxNew==0 );
|
|
+ pNewTerm = &pWC->a[idxNew];
|
|
+ pNewTerm->prereqRight = prereqExpr;
|
|
+ pNewTerm->leftCursor = pLeft->iTable;
|
|
+ pNewTerm->u.leftColumn = pLeft->iColumn;
|
|
+ pNewTerm->eOperator = WO_AUX;
|
|
+ pNewTerm->eMatchOp = eOp2;
|
|
+ markTermAsChild(pWC, idxNew, idxTerm);
|
|
+ pTerm = &pWC->a[idxTerm];
|
|
+ pTerm->wtFlags |= TERM_COPIED;
|
|
+ pNewTerm->prereqAll = pTerm->prereqAll;
|
|
}
|
|
- idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
|
- testcase( idxNew==0 );
|
|
- pNewTerm = &pWC->a[idxNew];
|
|
- pNewTerm->prereqRight = prereqExpr;
|
|
- pNewTerm->leftCursor = pLeft->iTable;
|
|
- pNewTerm->u.leftColumn = pLeft->iColumn;
|
|
- pNewTerm->eOperator = WO_MATCH;
|
|
- pNewTerm->eMatchOp = eOp2;
|
|
- markTermAsChild(pWC, idxNew, idxTerm);
|
|
- pTerm = &pWC->a[idxTerm];
|
|
- pTerm->wtFlags |= TERM_COPIED;
|
|
- pNewTerm->prereqAll = pTerm->prereqAll;
|
|
+ SWAP(Expr*, pLeft, pRight);
|
|
}
|
|
}
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
@@ -131202,7 +138962,7 @@
|
|
exprAnalyze(pSrc, pWC, idxNew);
|
|
}
|
|
pTerm = &pWC->a[idxTerm];
|
|
- pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
|
+ pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
|
pTerm->eOperator = 0;
|
|
}
|
|
|
|
@@ -131239,6 +138999,7 @@
|
|
if( pExpr->op==TK_NOTNULL
|
|
&& pExpr->pLeft->op==TK_COLUMN
|
|
&& pExpr->pLeft->iColumn>=0
|
|
+ && !ExprHasProperty(pExpr, EP_FromJoin)
|
|
&& OptimizationEnabled(db, SQLITE_Stat34)
|
|
){
|
|
Expr *pNewExpr;
|
|
@@ -131316,6 +139077,7 @@
|
|
WhereInfo *pWInfo /* The WHERE processing context */
|
|
){
|
|
pWC->pWInfo = pWInfo;
|
|
+ pWC->hasOr = 0;
|
|
pWC->pOuter = 0;
|
|
pWC->nTerm = 0;
|
|
pWC->nSlot = ArraySize(pWC->aStatic);
|
|
@@ -131352,17 +139114,18 @@
|
|
** a bitmask indicating which tables are used in that expression
|
|
** tree.
|
|
*/
|
|
-SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
|
|
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){
|
|
Bitmask mask;
|
|
- if( p==0 ) return 0;
|
|
- if( p->op==TK_COLUMN ){
|
|
+ if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){
|
|
return sqlite3WhereGetMask(pMaskSet, p->iTable);
|
|
+ }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
|
|
+ assert( p->op!=TK_IF_NULL_ROW );
|
|
+ return 0;
|
|
}
|
|
mask = (p->op==TK_IF_NULL_ROW) ? sqlite3WhereGetMask(pMaskSet, p->iTable) : 0;
|
|
- assert( !ExprHasProperty(p, EP_TokenOnly) );
|
|
- if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft);
|
|
+ if( p->pLeft ) mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pLeft);
|
|
if( p->pRight ){
|
|
- mask |= sqlite3WhereExprUsage(pMaskSet, p->pRight);
|
|
+ mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pRight);
|
|
assert( p->x.pList==0 );
|
|
}else if( ExprHasProperty(p, EP_xIsSelect) ){
|
|
if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1;
|
|
@@ -131372,6 +139135,9 @@
|
|
}
|
|
return mask;
|
|
}
|
|
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
|
|
+ return p ? sqlite3WhereExprUsageNN(pMaskSet,p) : 0;
|
|
+}
|
|
SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet *pMaskSet, ExprList *pList){
|
|
int i;
|
|
Bitmask mask = 0;
|
|
@@ -131425,6 +139191,7 @@
|
|
pArgs = pItem->u1.pFuncArg;
|
|
if( pArgs==0 ) return;
|
|
for(j=k=0; j<pArgs->nExpr; j++){
|
|
+ Expr *pRhs;
|
|
while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
|
|
if( k>=pTab->nCol ){
|
|
sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
|
|
@@ -131435,9 +139202,10 @@
|
|
if( pColRef==0 ) return;
|
|
pColRef->iTable = pItem->iCursor;
|
|
pColRef->iColumn = k++;
|
|
- pColRef->pTab = pTab;
|
|
- pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
|
|
- sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0));
|
|
+ pColRef->y.pTab = pTab;
|
|
+ pRhs = sqlite3PExpr(pParse, TK_UPLUS,
|
|
+ sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
|
|
+ pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
|
|
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
|
|
}
|
|
}
|
|
@@ -131465,6 +139233,21 @@
|
|
/* #include "sqliteInt.h" */
|
|
/* #include "whereInt.h" */
|
|
|
|
+/*
|
|
+** Extra information appended to the end of sqlite3_index_info but not
|
|
+** visible to the xBestIndex function, at least not directly. The
|
|
+** sqlite3_vtab_collation() interface knows how to reach it, however.
|
|
+**
|
|
+** This object is not an API and can be changed from one release to the
|
|
+** next. As long as allocateIndexInfo() and sqlite3_vtab_collation()
|
|
+** agree on the structure, all will be well.
|
|
+*/
|
|
+typedef struct HiddenIndexInfo HiddenIndexInfo;
|
|
+struct HiddenIndexInfo {
|
|
+ WhereClause *pWC; /* The Where clause being analyzed */
|
|
+ Parse *pParse; /* The parsing context */
|
|
+};
|
|
+
|
|
/* Forward declaration of methods */
|
|
static int whereLoopResize(sqlite3*, WhereLoop*, int);
|
|
|
|
@@ -131498,15 +139281,38 @@
|
|
}
|
|
|
|
/*
|
|
-** Return TRUE if the innermost loop of the WHERE clause implementation
|
|
-** returns rows in ORDER BY order for complete run of the inner loop.
|
|
+** In the ORDER BY LIMIT optimization, if the inner-most loop is known
|
|
+** to emit rows in increasing order, and if the last row emitted by the
|
|
+** inner-most loop did not fit within the sorter, then we can skip all
|
|
+** subsequent rows for the current iteration of the inner loop (because they
|
|
+** will not fit in the sorter either) and continue with the second inner
|
|
+** loop - the loop immediately outside the inner-most.
|
|
**
|
|
-** Across multiple iterations of outer loops, the output rows need not be
|
|
-** sorted. As long as rows are sorted for just the innermost loop, this
|
|
-** routine can return TRUE.
|
|
+** When a row does not fit in the sorter (because the sorter already
|
|
+** holds LIMIT+OFFSET rows that are smaller), then a jump is made to the
|
|
+** label returned by this function.
|
|
+**
|
|
+** If the ORDER BY LIMIT optimization applies, the jump destination should
|
|
+** be the continuation for the second-inner-most loop. If the ORDER BY
|
|
+** LIMIT optimization does not apply, then the jump destination should
|
|
+** be the continuation for the inner-most loop.
|
|
+**
|
|
+** It is always safe for this routine to return the continuation of the
|
|
+** inner-most loop, in the sense that a correct answer will result.
|
|
+** Returning the continuation the second inner loop is an optimization
|
|
+** that might make the code run a little faster, but should not change
|
|
+** the final answer.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3WhereOrderedInnerLoop(WhereInfo *pWInfo){
|
|
- return pWInfo->bOrderedInnerLoop;
|
|
+SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){
|
|
+ WhereLevel *pInner;
|
|
+ if( !pWInfo->bOrderedInnerLoop ){
|
|
+ /* The ORDER BY LIMIT optimization does not apply. Jump to the
|
|
+ ** continuation of the inner-most loop. */
|
|
+ return pWInfo->iContinue;
|
|
+ }
|
|
+ pInner = &pWInfo->a[pWInfo->nLevel-1];
|
|
+ assert( pInner->addrNxt!=0 );
|
|
+ return pInner->addrNxt;
|
|
}
|
|
|
|
/*
|
|
@@ -131849,8 +139655,8 @@
|
|
&& p->iColumn==pIdx->aiColumn[iCol]
|
|
&& p->iTable==iBase
|
|
){
|
|
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
|
|
- if( pColl && 0==sqlite3StrICmp(pColl->zName, zColl) ){
|
|
+ CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr);
|
|
+ if( 0==sqlite3StrICmp(pColl->zName, zColl) ){
|
|
return i;
|
|
}
|
|
}
|
|
@@ -132233,7 +140039,6 @@
|
|
VdbeComment((v, "for %s", pTable->zName));
|
|
|
|
/* Fill the automatic index with content */
|
|
- sqlite3ExprCachePush(pParse);
|
|
pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
|
|
if( pTabItem->fg.viaCoroutine ){
|
|
int regYield = pTabItem->regReturn;
|
|
@@ -132241,7 +140046,7 @@
|
|
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
|
|
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
|
|
VdbeCoverage(v);
|
|
- VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
|
|
+ VdbeComment((v, "next row of %s", pTabItem->pTab->zName));
|
|
}else{
|
|
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
|
|
}
|
|
@@ -132263,7 +140068,6 @@
|
|
translateColumnToCopy(pParse, addrTop, pLevel->iTabCur,
|
|
pTabItem->regResult, 1);
|
|
sqlite3VdbeGoto(v, addrTop);
|
|
- pTabItem->fg.viaCoroutine = 0;
|
|
}else{
|
|
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
|
|
}
|
|
@@ -132270,7 +140074,6 @@
|
|
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
|
|
sqlite3VdbeJumpHere(v, addrTop);
|
|
sqlite3ReleaseTempReg(pParse, regRecord);
|
|
- sqlite3ExprCachePop(pParse);
|
|
|
|
/* Jump here when skipping the initialization */
|
|
sqlite3VdbeJumpHere(v, addrInit);
|
|
@@ -132287,11 +140090,11 @@
|
|
** by passing the pointer returned by this function to sqlite3_free().
|
|
*/
|
|
static sqlite3_index_info *allocateIndexInfo(
|
|
- Parse *pParse,
|
|
- WhereClause *pWC,
|
|
+ Parse *pParse, /* The parsing context */
|
|
+ WhereClause *pWC, /* The WHERE clause being analyzed */
|
|
Bitmask mUnusable, /* Ignore terms with these prereqs */
|
|
- struct SrcList_item *pSrc,
|
|
- ExprList *pOrderBy,
|
|
+ struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */
|
|
+ ExprList *pOrderBy, /* The ORDER BY clause */
|
|
u16 *pmNoOmit /* Mask of terms not to omit */
|
|
){
|
|
int i, j;
|
|
@@ -132299,6 +140102,7 @@
|
|
struct sqlite3_index_constraint *pIdxCons;
|
|
struct sqlite3_index_orderby *pIdxOrderBy;
|
|
struct sqlite3_index_constraint_usage *pUsage;
|
|
+ struct HiddenIndexInfo *pHidden;
|
|
WhereTerm *pTerm;
|
|
int nOrderBy;
|
|
sqlite3_index_info *pIdxInfo;
|
|
@@ -132314,7 +140118,7 @@
|
|
testcase( pTerm->eOperator & WO_ISNULL );
|
|
testcase( pTerm->eOperator & WO_IS );
|
|
testcase( pTerm->eOperator & WO_ALL );
|
|
- if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
|
|
+ if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
|
|
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
|
assert( pTerm->u.leftColumn>=(-1) );
|
|
nTerm++;
|
|
@@ -132340,7 +140144,7 @@
|
|
*/
|
|
pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
|
|
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
|
|
- + sizeof(*pIdxOrderBy)*nOrderBy );
|
|
+ + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) );
|
|
if( pIdxInfo==0 ){
|
|
sqlite3ErrorMsg(pParse, "out of memory");
|
|
return 0;
|
|
@@ -132351,7 +140155,8 @@
|
|
** changing them. We have to do some funky casting in order to
|
|
** initialize those fields.
|
|
*/
|
|
- pIdxCons = (struct sqlite3_index_constraint*)&pIdxInfo[1];
|
|
+ pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
|
|
+ pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1];
|
|
pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm];
|
|
pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy];
|
|
*(int*)&pIdxInfo->nConstraint = nTerm;
|
|
@@ -132361,8 +140166,10 @@
|
|
*(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
|
|
pUsage;
|
|
|
|
+ pHidden->pWC = pWC;
|
|
+ pHidden->pParse = pParse;
|
|
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
|
- u8 op;
|
|
+ u16 op;
|
|
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
|
if( pTerm->prereqRight & mUnusable ) continue;
|
|
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
|
@@ -132370,34 +140177,54 @@
|
|
testcase( pTerm->eOperator & WO_IS );
|
|
testcase( pTerm->eOperator & WO_ISNULL );
|
|
testcase( pTerm->eOperator & WO_ALL );
|
|
- if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
|
|
+ if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
|
|
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
|
+ if( (pSrc->fg.jointype & JT_LEFT)!=0
|
|
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
|
+ && (pTerm->eOperator & (WO_IS|WO_ISNULL))
|
|
+ ){
|
|
+ /* An "IS" term in the WHERE clause where the virtual table is the rhs
|
|
+ ** of a LEFT JOIN. Do not pass this term to the virtual table
|
|
+ ** implementation, as this can lead to incorrect results from SQL such
|
|
+ ** as:
|
|
+ **
|
|
+ ** "LEFT JOIN vtab WHERE vtab.col IS NULL" */
|
|
+ testcase( pTerm->eOperator & WO_ISNULL );
|
|
+ testcase( pTerm->eOperator & WO_IS );
|
|
+ continue;
|
|
+ }
|
|
assert( pTerm->u.leftColumn>=(-1) );
|
|
pIdxCons[j].iColumn = pTerm->u.leftColumn;
|
|
pIdxCons[j].iTermOffset = i;
|
|
- op = (u8)pTerm->eOperator & WO_ALL;
|
|
+ op = pTerm->eOperator & WO_ALL;
|
|
if( op==WO_IN ) op = WO_EQ;
|
|
- if( op==WO_MATCH ){
|
|
- op = pTerm->eMatchOp;
|
|
- }
|
|
- pIdxCons[j].op = op;
|
|
- /* The direct assignment in the previous line is possible only because
|
|
- ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
|
|
- ** following asserts verify this fact. */
|
|
- assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
|
|
- assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
|
|
- assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
|
|
- assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
|
|
- assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
|
|
- assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
- assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
|
|
+ if( op==WO_AUX ){
|
|
+ pIdxCons[j].op = pTerm->eMatchOp;
|
|
+ }else if( op & (WO_ISNULL|WO_IS) ){
|
|
+ if( op==WO_ISNULL ){
|
|
+ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL;
|
|
+ }else{
|
|
+ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS;
|
|
+ }
|
|
+ }else{
|
|
+ pIdxCons[j].op = (u8)op;
|
|
+ /* The direct assignment in the previous line is possible only because
|
|
+ ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
|
|
+ ** following asserts verify this fact. */
|
|
+ assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
|
|
+ assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
|
|
+ assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
|
|
+ assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
|
|
+ assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
|
|
+ assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) );
|
|
|
|
- if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
|
|
- && sqlite3ExprIsVector(pTerm->pExpr->pRight)
|
|
- ){
|
|
- if( i<16 ) mNoOmit |= (1 << i);
|
|
- if( op==WO_LT ) pIdxCons[j].op = WO_LE;
|
|
- if( op==WO_GT ) pIdxCons[j].op = WO_GE;
|
|
+ if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
|
|
+ && sqlite3ExprIsVector(pTerm->pExpr->pRight)
|
|
+ ){
|
|
+ if( i<16 ) mNoOmit |= (1 << i);
|
|
+ if( op==WO_LT ) pIdxCons[j].op = WO_LE;
|
|
+ if( op==WO_GT ) pIdxCons[j].op = WO_GE;
|
|
+ }
|
|
}
|
|
|
|
j++;
|
|
@@ -132418,9 +140245,11 @@
|
|
** method of the virtual table with the sqlite3_index_info object that
|
|
** comes in as the 3rd argument to this function.
|
|
**
|
|
-** If an error occurs, pParse is populated with an error message and a
|
|
-** non-zero value is returned. Otherwise, 0 is returned and the output
|
|
-** part of the sqlite3_index_info structure is left populated.
|
|
+** If an error occurs, pParse is populated with an error message and an
|
|
+** appropriate error code is returned. A return of SQLITE_CONSTRAINT from
|
|
+** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that
|
|
+** the current configuration of "unusable" flags in sqlite3_index_info can
|
|
+** not result in a valid plan.
|
|
**
|
|
** Whether or not an error is returned, it is the responsibility of the
|
|
** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates
|
|
@@ -132434,7 +140263,7 @@
|
|
rc = pVtab->pModule->xBestIndex(pVtab, p);
|
|
TRACE_IDX_OUTPUTS(p);
|
|
|
|
- if( rc!=SQLITE_OK ){
|
|
+ if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
|
|
if( rc==SQLITE_NOMEM ){
|
|
sqlite3OomFault(pParse->db);
|
|
}else if( !pVtab->zErrMsg ){
|
|
@@ -132445,19 +140274,7 @@
|
|
}
|
|
sqlite3_free(pVtab->zErrMsg);
|
|
pVtab->zErrMsg = 0;
|
|
-
|
|
-#if 0
|
|
- /* This error is now caught by the caller.
|
|
- ** Search for "xBestIndex malfunction" below */
|
|
- for(i=0; i<p->nConstraint; i++){
|
|
- if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){
|
|
- sqlite3ErrorMsg(pParse,
|
|
- "table %s: xBestIndex returned an invalid plan", pTab->zName);
|
|
- }
|
|
- }
|
|
-#endif
|
|
-
|
|
- return pParse->nErr;
|
|
+ return rc;
|
|
}
|
|
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
|
|
|
@@ -132857,7 +140674,9 @@
|
|
Index *p = pLoop->u.btree.pIndex;
|
|
int nEq = pLoop->u.btree.nEq;
|
|
|
|
- if( p->nSample>0 && nEq<p->nSampleCol ){
|
|
+ if( p->nSample>0 && nEq<p->nSampleCol
|
|
+ && OptimizationEnabled(pParse->db, SQLITE_Stat34)
|
|
+ ){
|
|
if( nEq==pBuilder->nRecValid ){
|
|
UnpackedRecord *pRec = pBuilder->pRec;
|
|
tRowcnt a[2];
|
|
@@ -133303,22 +141122,21 @@
|
|
** Free a WhereInfo structure
|
|
*/
|
|
static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
|
|
- if( ALWAYS(pWInfo) ){
|
|
- int i;
|
|
- for(i=0; i<pWInfo->nLevel; i++){
|
|
- WhereLevel *pLevel = &pWInfo->a[i];
|
|
- if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){
|
|
- sqlite3DbFree(db, pLevel->u.in.aInLoop);
|
|
- }
|
|
+ int i;
|
|
+ assert( pWInfo!=0 );
|
|
+ for(i=0; i<pWInfo->nLevel; i++){
|
|
+ WhereLevel *pLevel = &pWInfo->a[i];
|
|
+ if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){
|
|
+ sqlite3DbFree(db, pLevel->u.in.aInLoop);
|
|
}
|
|
- sqlite3WhereClauseClear(&pWInfo->sWC);
|
|
- while( pWInfo->pLoops ){
|
|
- WhereLoop *p = pWInfo->pLoops;
|
|
- pWInfo->pLoops = p->pNextLoop;
|
|
- whereLoopDelete(db, p);
|
|
- }
|
|
- sqlite3DbFreeNN(db, pWInfo);
|
|
}
|
|
+ sqlite3WhereClauseClear(&pWInfo->sWC);
|
|
+ while( pWInfo->pLoops ){
|
|
+ WhereLoop *p = pWInfo->pLoops;
|
|
+ pWInfo->pLoops = p->pNextLoop;
|
|
+ whereLoopDelete(db, p);
|
|
+ }
|
|
+ sqlite3DbFreeNN(db, pWInfo);
|
|
}
|
|
|
|
/*
|
|
@@ -133325,18 +141143,19 @@
|
|
** Return TRUE if all of the following are true:
|
|
**
|
|
** (1) X has the same or lower cost that Y
|
|
-** (2) X is a proper subset of Y
|
|
-** (3) X skips at least as many columns as Y
|
|
+** (2) X uses fewer WHERE clause terms than Y
|
|
+** (3) Every WHERE clause term used by X is also used by Y
|
|
+** (4) X skips at least as many columns as Y
|
|
+** (5) If X is a covering index, than Y is too
|
|
**
|
|
-** By "proper subset" we mean that X uses fewer WHERE clause terms
|
|
-** than Y and that every WHERE clause term used by X is also used
|
|
-** by Y.
|
|
-**
|
|
+** Conditions (2) and (3) mean that X is a "proper subset" of Y.
|
|
** If X is a proper subset of Y then Y is a better choice and ought
|
|
** to have a lower cost. This routine returns TRUE when that cost
|
|
-** relationship is inverted and needs to be adjusted. The third rule
|
|
+** relationship is inverted and needs to be adjusted. Constraint (4)
|
|
** was added because if X uses skip-scan less than Y it still might
|
|
-** deserve a lower cost even if it is a proper subset of Y.
|
|
+** deserve a lower cost even if it is a proper subset of Y. Constraint (5)
|
|
+** was added because a covering index probably deserves to have a lower cost
|
|
+** than a non-covering index even if it is a proper subset.
|
|
*/
|
|
static int whereLoopCheaperProperSubset(
|
|
const WhereLoop *pX, /* First WhereLoop to compare */
|
|
@@ -133358,6 +141177,10 @@
|
|
}
|
|
if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
|
|
}
|
|
+ if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
|
|
+ && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
|
|
+ return 0; /* Constraint (5) */
|
|
+ }
|
|
return 1; /* All conditions meet */
|
|
}
|
|
|
|
@@ -133506,6 +141329,14 @@
|
|
sqlite3 *db = pWInfo->pParse->db;
|
|
int rc;
|
|
|
|
+ /* Stop the search once we hit the query planner search limit */
|
|
+ if( pBuilder->iPlanLimit==0 ){
|
|
+ WHERETRACE(0xffffffff,("=== query planner search limit reached ===\n"));
|
|
+ if( pBuilder->pOrSet ) pBuilder->pOrSet->n = 0;
|
|
+ return SQLITE_DONE;
|
|
+ }
|
|
+ pBuilder->iPlanLimit--;
|
|
+
|
|
/* If pBuilder->pOrSet is defined, then only keep track of the costs
|
|
** and prereqs.
|
|
*/
|
|
@@ -133790,8 +141621,8 @@
|
|
|
|
pNew = pBuilder->pNew;
|
|
if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
|
|
- WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n",
|
|
- pProbe->zName, pNew->u.btree.nEq));
|
|
+ WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n",
|
|
+ pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq));
|
|
|
|
assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
|
|
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
|
|
@@ -133837,15 +141668,12 @@
|
|
** to mix with a lower range bound from some other source */
|
|
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
|
|
|
|
- /* Do not allow IS constraints from the WHERE clause to be used by the
|
|
+ /* Do not allow constraints from the WHERE clause to be used by the
|
|
** right table of a LEFT JOIN. Only constraints in the ON clause are
|
|
** allowed */
|
|
if( (pSrc->fg.jointype & JT_LEFT)!=0
|
|
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
|
- && (eOp & (WO_IS|WO_ISNULL))!=0
|
|
){
|
|
- testcase( eOp & WO_IS );
|
|
- testcase( eOp & WO_ISNULL );
|
|
continue;
|
|
}
|
|
|
|
@@ -133871,7 +141699,6 @@
|
|
|
|
if( eOp & WO_IN ){
|
|
Expr *pExpr = pTerm->pExpr;
|
|
- pNew->wsFlags |= WHERE_COLUMN_IN;
|
|
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
|
/* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
|
|
int i;
|
|
@@ -133891,17 +141718,55 @@
|
|
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
|
|
** changes "x IN (?)" into "x=?". */
|
|
}
|
|
+ if( pProbe->hasStat1 ){
|
|
+ LogEst M, logK, safetyMargin;
|
|
+ /* Let:
|
|
+ ** N = the total number of rows in the table
|
|
+ ** K = the number of entries on the RHS of the IN operator
|
|
+ ** M = the number of rows in the table that match terms to the
|
|
+ ** to the left in the same index. If the IN operator is on
|
|
+ ** the left-most index column, M==N.
|
|
+ **
|
|
+ ** Given the definitions above, it is better to omit the IN operator
|
|
+ ** from the index lookup and instead do a scan of the M elements,
|
|
+ ** testing each scanned row against the IN operator separately, if:
|
|
+ **
|
|
+ ** M*log(K) < K*log(N)
|
|
+ **
|
|
+ ** Our estimates for M, K, and N might be inaccurate, so we build in
|
|
+ ** a safety margin of 2 (LogEst: 10) that favors using the IN operator
|
|
+ ** with the index, as using an index has better worst-case behavior.
|
|
+ ** If we do not have real sqlite_stat1 data, always prefer to use
|
|
+ ** the index.
|
|
+ */
|
|
+ M = pProbe->aiRowLogEst[saved_nEq];
|
|
+ logK = estLog(nIn);
|
|
+ safetyMargin = 10; /* TUNING: extra weight for indexed IN */
|
|
+ if( M + logK + safetyMargin < nIn + rLogSize ){
|
|
+ WHERETRACE(0x40,
|
|
+ ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n",
|
|
+ saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize));
|
|
+ continue;
|
|
+ }else{
|
|
+ WHERETRACE(0x40,
|
|
+ ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n",
|
|
+ saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize));
|
|
+ }
|
|
+ }
|
|
+ pNew->wsFlags |= WHERE_COLUMN_IN;
|
|
}else if( eOp & (WO_EQ|WO_IS) ){
|
|
int iCol = pProbe->aiColumn[saved_nEq];
|
|
pNew->wsFlags |= WHERE_COLUMN_EQ;
|
|
assert( saved_nEq==pNew->u.btree.nEq );
|
|
if( iCol==XN_ROWID
|
|
- || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
|
|
+ || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
|
|
){
|
|
- if( iCol>=0 && pProbe->uniqNotNull==0 ){
|
|
+ if( iCol==XN_ROWID || pProbe->uniqNotNull
|
|
+ || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ)
|
|
+ ){
|
|
+ pNew->wsFlags |= WHERE_ONEROW;
|
|
+ }else{
|
|
pNew->wsFlags |= WHERE_UNQ_WANTED;
|
|
- }else{
|
|
- pNew->wsFlags |= WHERE_ONEROW;
|
|
}
|
|
}
|
|
}else if( eOp & WO_ISNULL ){
|
|
@@ -133967,6 +141832,7 @@
|
|
&& pProbe->nSample
|
|
&& pNew->u.btree.nEq<=pProbe->nSampleCol
|
|
&& ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
|
|
+ && OptimizationEnabled(db, SQLITE_Stat34)
|
|
){
|
|
Expr *pExpr = pTerm->pExpr;
|
|
if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
|
|
@@ -134055,6 +141921,7 @@
|
|
if( saved_nEq==saved_nSkip
|
|
&& saved_nEq+1<pProbe->nKeyCol
|
|
&& pProbe->noSkipScan==0
|
|
+ && OptimizationEnabled(db, SQLITE_SkipScan)
|
|
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
|
|
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
|
|
){
|
|
@@ -134075,8 +141942,8 @@
|
|
pNew->wsFlags = saved_wsFlags;
|
|
}
|
|
|
|
- WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n",
|
|
- pProbe->zName, saved_nEq, rc));
|
|
+ WHERETRACE(0x800, ("END %s.addBtreeIdx(%s), nEq=%d, rc=%d\n",
|
|
+ pProbe->pTable->zName, pProbe->zName, saved_nEq, rc));
|
|
return rc;
|
|
}
|
|
|
|
@@ -134109,7 +141976,7 @@
|
|
}else if( (aColExpr = pIndex->aColExpr)!=0 ){
|
|
for(jj=0; jj<pIndex->nKeyCol; jj++){
|
|
if( pIndex->aiColumn[jj]!=XN_EXPR ) continue;
|
|
- if( sqlite3ExprCompare(0, pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){
|
|
+ if( sqlite3ExprCompareSkip(pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){
|
|
return 1;
|
|
}
|
|
}
|
|
@@ -134118,24 +141985,6 @@
|
|
return 0;
|
|
}
|
|
|
|
-/*
|
|
-** Return a bitmask where 1s indicate that the corresponding column of
|
|
-** the table is used by an index. Only the first 63 columns are considered.
|
|
-*/
|
|
-static Bitmask columnsInIndex(Index *pIdx){
|
|
- Bitmask m = 0;
|
|
- int j;
|
|
- for(j=pIdx->nColumn-1; j>=0; j--){
|
|
- int x = pIdx->aiColumn[j];
|
|
- if( x>=0 ){
|
|
- testcase( x==BMS-1 );
|
|
- testcase( x==BMS-2 );
|
|
- if( x<BMS-1 ) m |= MASKBIT(x);
|
|
- }
|
|
- }
|
|
- return m;
|
|
-}
|
|
-
|
|
/* Check to see if a partial index with pPartIndexWhere can be used
|
|
** in the current query. Return true if it can be and false if not.
|
|
*/
|
|
@@ -134280,14 +142129,16 @@
|
|
/* TUNING: One-time cost for computing the automatic index is
|
|
** estimated to be X*N*log2(N) where N is the number of rows in
|
|
** the table being indexed and where X is 7 (LogEst=28) for normal
|
|
- ** tables or 1.375 (LogEst=4) for views and subqueries. The value
|
|
+ ** tables or 0.5 (LogEst=-10) for views and subqueries. The value
|
|
** of X is smaller for views and subqueries so that the query planner
|
|
** will be more aggressive about generating automatic indexes for
|
|
** those objects, since there is no opportunity to add schema
|
|
** indexes on subqueries and views. */
|
|
- pNew->rSetup = rLogSize + rSize + 4;
|
|
+ pNew->rSetup = rLogSize + rSize;
|
|
if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
|
|
- pNew->rSetup += 24;
|
|
+ pNew->rSetup += 28;
|
|
+ }else{
|
|
+ pNew->rSetup -= 10;
|
|
}
|
|
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
|
|
if( pNew->rSetup<0 ) pNew->rSetup = 0;
|
|
@@ -134305,14 +142156,17 @@
|
|
}
|
|
#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
|
|
|
|
- /* Loop over all indices
|
|
- */
|
|
- for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){
|
|
+ /* Loop over all indices. If there was an INDEXED BY clause, then only
|
|
+ ** consider index pProbe. */
|
|
+ for(; rc==SQLITE_OK && pProbe;
|
|
+ pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++
|
|
+ ){
|
|
if( pProbe->pPartIdxWhere!=0
|
|
&& !whereUsablePartialIndex(pSrc->iCursor, pWC, pProbe->pPartIdxWhere) ){
|
|
testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */
|
|
continue; /* Partial index inappropriate for this query */
|
|
}
|
|
+ if( pProbe->bNoQuery ) continue;
|
|
rSize = pProbe->aiRowLogEst[0];
|
|
pNew->u.btree.nEq = 0;
|
|
pNew->u.btree.nBtm = 0;
|
|
@@ -134346,7 +142200,7 @@
|
|
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
|
|
m = 0;
|
|
}else{
|
|
- m = pSrc->colUsed & ~columnsInIndex(pProbe);
|
|
+ m = pSrc->colUsed & pProbe->colNotIdxed;
|
|
pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
|
|
}
|
|
|
|
@@ -134417,10 +142271,6 @@
|
|
pBuilder->nRecValid = 0;
|
|
pBuilder->pRec = 0;
|
|
#endif
|
|
-
|
|
- /* If there was an INDEXED BY clause, then only that one index is
|
|
- ** considered. */
|
|
- if( pSrc->pIBIndex ) break;
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -134497,7 +142347,17 @@
|
|
|
|
/* Invoke the virtual table xBestIndex() method */
|
|
rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
|
|
- if( rc ) return rc;
|
|
+ if( rc ){
|
|
+ if( rc==SQLITE_CONSTRAINT ){
|
|
+ /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means
|
|
+ ** that the particular combination of parameters provided is unusable.
|
|
+ ** Make no entries in the loop table.
|
|
+ */
|
|
+ WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n"));
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ return rc;
|
|
+ }
|
|
|
|
mxTerm = -1;
|
|
assert( pNew->nLSlot>=nConstraint );
|
|
@@ -134515,9 +142375,9 @@
|
|
|| pNew->aLTerm[iTerm]!=0
|
|
|| pIdxCons->usable==0
|
|
){
|
|
- rc = SQLITE_ERROR;
|
|
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
|
|
- return rc;
|
|
+ testcase( pIdxInfo->needToFreeIdxStr );
|
|
+ return SQLITE_ERROR;
|
|
}
|
|
testcase( iTerm==nConstraint-1 );
|
|
testcase( j==0 );
|
|
@@ -134545,6 +142405,15 @@
|
|
pNew->u.vtab.omitMask &= ~mNoOmit;
|
|
|
|
pNew->nLTerm = mxTerm+1;
|
|
+ for(i=0; i<=mxTerm; i++){
|
|
+ if( pNew->aLTerm[i]==0 ){
|
|
+ /* The non-zero argvIdx values must be contiguous. Raise an
|
|
+ ** error if they are not */
|
|
+ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
|
|
+ testcase( pIdxInfo->needToFreeIdxStr );
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
assert( pNew->nLTerm<=pNew->nLSlot );
|
|
pNew->u.vtab.idxNum = pIdxInfo->idxNum;
|
|
pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
|
|
@@ -134575,6 +142444,27 @@
|
|
return rc;
|
|
}
|
|
|
|
+/*
|
|
+** If this function is invoked from within an xBestIndex() callback, it
|
|
+** returns a pointer to a buffer containing the name of the collation
|
|
+** sequence associated with element iCons of the sqlite3_index_info.aConstraint
|
|
+** array. Or, if iCons is out of range or there is no active xBestIndex
|
|
+** call, return NULL.
|
|
+*/
|
|
+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){
|
|
+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
|
|
+ const char *zRet = 0;
|
|
+ if( iCons>=0 && iCons<pIdxInfo->nConstraint ){
|
|
+ CollSeq *pC = 0;
|
|
+ int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset;
|
|
+ Expr *pX = pHidden->pWC->a[iTerm].pExpr;
|
|
+ if( pX->pLeft ){
|
|
+ pC = sqlite3BinaryCompareCollSeq(pHidden->pParse, pX->pLeft, pX->pRight);
|
|
+ }
|
|
+ zRet = (pC ? pC->zName : sqlite3StrBINARY);
|
|
+ }
|
|
+ return zRet;
|
|
+}
|
|
|
|
/*
|
|
** Add all WhereLoop objects for a table of the join identified by
|
|
@@ -134639,6 +142529,7 @@
|
|
}
|
|
|
|
/* First call xBestIndex() with all constraints usable. */
|
|
+ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName));
|
|
WHERETRACE(0x40, (" VirtualOne: all usable\n"));
|
|
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
|
|
|
|
@@ -134714,6 +142605,7 @@
|
|
|
|
if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
|
|
sqlite3DbFreeNN(pParse->db, p);
|
|
+ WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc));
|
|
return rc;
|
|
}
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
@@ -134861,9 +142753,11 @@
|
|
/* Loop over the tables in the join, from left to right */
|
|
pNew = pBuilder->pNew;
|
|
whereLoopInit(pNew);
|
|
+ pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT;
|
|
for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
|
|
Bitmask mUnusable = 0;
|
|
pNew->iTab = iTab;
|
|
+ pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR;
|
|
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
|
|
if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
|
|
/* This condition is true when pItem is the FROM clause term on the
|
|
@@ -134885,11 +142779,19 @@
|
|
{
|
|
rc = whereLoopAddBtree(pBuilder, mPrereq);
|
|
}
|
|
- if( rc==SQLITE_OK ){
|
|
+ if( rc==SQLITE_OK && pBuilder->pWC->hasOr ){
|
|
rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable);
|
|
}
|
|
mPrior |= pNew->maskSelf;
|
|
- if( rc || db->mallocFailed ) break;
|
|
+ if( rc || db->mallocFailed ){
|
|
+ if( rc==SQLITE_DONE ){
|
|
+ /* We hit the query planner search limit set by iPlanLimit */
|
|
+ sqlite3_log(SQLITE_WARNING, "abbreviated query algorithm search");
|
|
+ rc = SQLITE_OK;
|
|
+ }else{
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
whereLoopClear(db, pNew);
|
|
@@ -135019,14 +142921,10 @@
|
|
if( j>=pLoop->nLTerm ) continue;
|
|
}
|
|
if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
|
|
- const char *z1, *z2;
|
|
- pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
|
|
- if( !pColl ) pColl = db->pDfltColl;
|
|
- z1 = pColl->zName;
|
|
- pColl = sqlite3ExprCollSeq(pWInfo->pParse, pTerm->pExpr);
|
|
- if( !pColl ) pColl = db->pDfltColl;
|
|
- z2 = pColl->zName;
|
|
- if( sqlite3StrICmp(z1, z2)!=0 ) continue;
|
|
+ if( sqlite3ExprCollSeqMatch(pWInfo->pParse,
|
|
+ pOrderBy->a[i].pExpr, pTerm->pExpr)==0 ){
|
|
+ continue;
|
|
+ }
|
|
testcase( pTerm->pExpr->op==TK_IS );
|
|
}
|
|
obSat |= MASKBIT(i);
|
|
@@ -135098,7 +142996,7 @@
|
|
if( pIndex ){
|
|
iColumn = pIndex->aiColumn[j];
|
|
revIdx = pIndex->aSortOrder[j];
|
|
- if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
|
|
+ if( iColumn==pIndex->pTable->iPKey ) iColumn = XN_ROWID;
|
|
}else{
|
|
iColumn = XN_ROWID;
|
|
revIdx = 0;
|
|
@@ -135125,19 +143023,18 @@
|
|
testcase( wctrlFlags & WHERE_GROUPBY );
|
|
testcase( wctrlFlags & WHERE_DISTINCTBY );
|
|
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
|
|
- if( iColumn>=(-1) ){
|
|
+ if( iColumn>=XN_ROWID ){
|
|
if( pOBExpr->op!=TK_COLUMN ) continue;
|
|
if( pOBExpr->iTable!=iCur ) continue;
|
|
if( pOBExpr->iColumn!=iColumn ) continue;
|
|
}else{
|
|
- if( sqlite3ExprCompare(0,
|
|
- pOBExpr,pIndex->aColExpr->a[j].pExpr,iCur) ){
|
|
+ Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr;
|
|
+ if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){
|
|
continue;
|
|
}
|
|
}
|
|
- if( iColumn>=0 ){
|
|
- pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
|
|
- if( !pColl ) pColl = db->pDfltColl;
|
|
+ if( iColumn!=XN_ROWID ){
|
|
+ pColl = sqlite3ExprNNCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
|
|
if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
|
|
}
|
|
pLoop->u.btree.nIdxCol = j+1;
|
|
@@ -135397,12 +143294,15 @@
|
|
|
|
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
|
|
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
|
|
- if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<10 ){
|
|
+ if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<3 ){
|
|
/* Do not use an automatic index if the this loop is expected
|
|
- ** to run less than 2 times. */
|
|
+ ** to run less than 1.25 times. It is tempting to also exclude
|
|
+ ** automatic index usage on an outer loop, but sometimes an automatic
|
|
+ ** index is useful in the outer loop of a correlated subquery. */
|
|
assert( 10==sqlite3LogEst(2) );
|
|
continue;
|
|
}
|
|
+
|
|
/* At this point, pWLoop is a candidate to be the next loop.
|
|
** Compute its cost */
|
|
rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
|
|
@@ -135422,7 +143322,11 @@
|
|
pWInfo, nRowEst, nOrderBy, isOrdered
|
|
);
|
|
}
|
|
- rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]);
|
|
+ /* TUNING: Add a small extra penalty (5) to sorting as an
|
|
+ ** extra encouragment to the query planner to select a plan
|
|
+ ** where the rows emerge in the correct order without any sorting
|
|
+ ** required. */
|
|
+ rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 5;
|
|
|
|
WHERETRACE(0x002,
|
|
("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
|
|
@@ -135612,6 +143516,7 @@
|
|
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
|
|
}
|
|
}
|
|
+ pWInfo->bOrderedInnerLoop = 0;
|
|
if( pWInfo->pOrderBy ){
|
|
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
|
|
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
|
|
@@ -135723,7 +143628,7 @@
|
|
}
|
|
if( j!=pIdx->nKeyCol ) continue;
|
|
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
|
|
- if( pIdx->isCovering || (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){
|
|
+ if( pIdx->isCovering || (pItem->colUsed & pIdx->colNotIdxed)==0 ){
|
|
pLoop->wsFlags |= WHERE_IDX_ONLY;
|
|
}
|
|
pLoop->nLTerm = j;
|
|
@@ -135774,6 +143679,7 @@
|
|
memset(&w, 0, sizeof(w));
|
|
w.eCode = 1;
|
|
w.xExprCallback = exprNodeIsDeterministic;
|
|
+ w.xSelectCallback = sqlite3SelectWalkFail;
|
|
sqlite3WalkExpr(&w, p);
|
|
return w.eCode;
|
|
}
|
|
@@ -135983,37 +143889,39 @@
|
|
if( wctrlFlags & WHERE_WANT_DISTINCT ){
|
|
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
|
|
}
|
|
- }
|
|
-
|
|
- /* Assign a bit from the bitmask to every term in the FROM clause.
|
|
- **
|
|
- ** The N-th term of the FROM clause is assigned a bitmask of 1<<N.
|
|
- **
|
|
- ** The rule of the previous sentence ensures thta if X is the bitmask for
|
|
- ** a table T, then X-1 is the bitmask for all other tables to the left of T.
|
|
- ** Knowing the bitmask for all tables to the left of a left join is
|
|
- ** important. Ticket #3015.
|
|
- **
|
|
- ** Note that bitmasks are created for all pTabList->nSrc tables in
|
|
- ** pTabList, not just the first nTabList tables. nTabList is normally
|
|
- ** equal to pTabList->nSrc but might be shortened to 1 if the
|
|
- ** WHERE_OR_SUBCLAUSE flag is set.
|
|
- */
|
|
- for(ii=0; ii<pTabList->nSrc; ii++){
|
|
- createMask(pMaskSet, pTabList->a[ii].iCursor);
|
|
- sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
|
|
- }
|
|
-#ifdef SQLITE_DEBUG
|
|
- {
|
|
- Bitmask mx = 0;
|
|
- for(ii=0; ii<pTabList->nSrc; ii++){
|
|
- Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor);
|
|
- assert( m>=mx );
|
|
- mx = m;
|
|
+ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
|
|
+ }else{
|
|
+ /* Assign a bit from the bitmask to every term in the FROM clause.
|
|
+ **
|
|
+ ** The N-th term of the FROM clause is assigned a bitmask of 1<<N.
|
|
+ **
|
|
+ ** The rule of the previous sentence ensures thta if X is the bitmask for
|
|
+ ** a table T, then X-1 is the bitmask for all other tables to the left of T.
|
|
+ ** Knowing the bitmask for all tables to the left of a left join is
|
|
+ ** important. Ticket #3015.
|
|
+ **
|
|
+ ** Note that bitmasks are created for all pTabList->nSrc tables in
|
|
+ ** pTabList, not just the first nTabList tables. nTabList is normally
|
|
+ ** equal to pTabList->nSrc but might be shortened to 1 if the
|
|
+ ** WHERE_OR_SUBCLAUSE flag is set.
|
|
+ */
|
|
+ ii = 0;
|
|
+ do{
|
|
+ createMask(pMaskSet, pTabList->a[ii].iCursor);
|
|
+ sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
|
|
+ }while( (++ii)<pTabList->nSrc );
|
|
+ #ifdef SQLITE_DEBUG
|
|
+ {
|
|
+ Bitmask mx = 0;
|
|
+ for(ii=0; ii<pTabList->nSrc; ii++){
|
|
+ Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor);
|
|
+ assert( m>=mx );
|
|
+ mx = m;
|
|
+ }
|
|
}
|
|
+ #endif
|
|
}
|
|
-#endif
|
|
-
|
|
+
|
|
/* Analyze all of the subexpressions. */
|
|
sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
|
|
if( db->mallocFailed ) goto whereBeginError;
|
|
@@ -136031,6 +143939,7 @@
|
|
*/
|
|
for(ii=0; ii<sWLB.pWC->nTerm; ii++){
|
|
WhereTerm *pT = &sWLB.pWC->a[ii];
|
|
+ if( pT->wtFlags & TERM_VIRTUAL ) continue;
|
|
if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){
|
|
sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL);
|
|
pT->wtFlags |= TERM_CODED;
|
|
@@ -136118,35 +144027,80 @@
|
|
}
|
|
}
|
|
#endif
|
|
- /* Attempt to omit tables from the join that do not effect the result */
|
|
+
|
|
+ /* Attempt to omit tables from the join that do not affect the result.
|
|
+ ** For a table to not affect the result, the following must be true:
|
|
+ **
|
|
+ ** 1) The query must not be an aggregate.
|
|
+ ** 2) The table must be the RHS of a LEFT JOIN.
|
|
+ ** 3) Either the query must be DISTINCT, or else the ON or USING clause
|
|
+ ** must contain a constraint that limits the scan of the table to
|
|
+ ** at most a single row.
|
|
+ ** 4) The table must not be referenced by any part of the query apart
|
|
+ ** from its own USING or ON clause.
|
|
+ **
|
|
+ ** For example, given:
|
|
+ **
|
|
+ ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1);
|
|
+ ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2);
|
|
+ ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3);
|
|
+ **
|
|
+ ** then table t2 can be omitted from the following:
|
|
+ **
|
|
+ ** SELECT v1, v3 FROM t1
|
|
+ ** LEFT JOIN t2 USING (t1.ipk=t2.ipk)
|
|
+ ** LEFT JOIN t3 USING (t1.ipk=t3.ipk)
|
|
+ **
|
|
+ ** or from:
|
|
+ **
|
|
+ ** SELECT DISTINCT v1, v3 FROM t1
|
|
+ ** LEFT JOIN t2
|
|
+ ** LEFT JOIN t3 USING (t1.ipk=t3.ipk)
|
|
+ */
|
|
+ notReady = ~(Bitmask)0;
|
|
if( pWInfo->nLevel>=2
|
|
- && pResultSet!=0
|
|
+ && pResultSet!=0 /* guarantees condition (1) above */
|
|
&& OptimizationEnabled(db, SQLITE_OmitNoopJoin)
|
|
){
|
|
+ int i;
|
|
Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet);
|
|
if( sWLB.pOrderBy ){
|
|
tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy);
|
|
}
|
|
- while( pWInfo->nLevel>=2 ){
|
|
+ for(i=pWInfo->nLevel-1; i>=1; i--){
|
|
WhereTerm *pTerm, *pEnd;
|
|
- pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
|
|
- if( (pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0 ) break;
|
|
+ struct SrcList_item *pItem;
|
|
+ pLoop = pWInfo->a[i].pWLoop;
|
|
+ pItem = &pWInfo->pTabList->a[pLoop->iTab];
|
|
+ if( (pItem->fg.jointype & JT_LEFT)==0 ) continue;
|
|
if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
|
|
&& (pLoop->wsFlags & WHERE_ONEROW)==0
|
|
){
|
|
- break;
|
|
+ continue;
|
|
}
|
|
- if( (tabUsed & pLoop->maskSelf)!=0 ) break;
|
|
+ if( (tabUsed & pLoop->maskSelf)!=0 ) continue;
|
|
pEnd = sWLB.pWC->a + sWLB.pWC->nTerm;
|
|
for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
|
|
- if( (pTerm->prereqAll & pLoop->maskSelf)!=0
|
|
- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
|
- ){
|
|
- break;
|
|
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
|
|
+ if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
|
+ || pTerm->pExpr->iRightJoinTable!=pItem->iCursor
|
|
+ ){
|
|
+ break;
|
|
+ }
|
|
}
|
|
}
|
|
- if( pTerm<pEnd ) break;
|
|
+ if( pTerm<pEnd ) continue;
|
|
WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
|
|
+ notReady &= ~pLoop->maskSelf;
|
|
+ for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
|
|
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
|
|
+ pTerm->wtFlags |= TERM_CODED;
|
|
+ }
|
|
+ }
|
|
+ if( i!=pWInfo->nLevel-1 ){
|
|
+ int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel);
|
|
+ memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte);
|
|
+ }
|
|
pWInfo->nLevel--;
|
|
nTabList--;
|
|
}
|
|
@@ -136156,15 +144110,32 @@
|
|
|
|
/* If the caller is an UPDATE or DELETE statement that is requesting
|
|
** to use a one-pass algorithm, determine if this is appropriate.
|
|
+ **
|
|
+ ** A one-pass approach can be used if the caller has requested one
|
|
+ ** and either (a) the scan visits at most one row or (b) each
|
|
+ ** of the following are true:
|
|
+ **
|
|
+ ** * the caller has indicated that a one-pass approach can be used
|
|
+ ** with multiple rows (by setting WHERE_ONEPASS_MULTIROW), and
|
|
+ ** * the table is not a virtual table, and
|
|
+ ** * either the scan does not use the OR optimization or the caller
|
|
+ ** is a DELETE operation (WHERE_DUPLICATES_OK is only specified
|
|
+ ** for DELETE).
|
|
+ **
|
|
+ ** The last qualification is because an UPDATE statement uses
|
|
+ ** WhereInfo.aiCurOnePass[1] to determine whether or not it really can
|
|
+ ** use a one-pass approach, and this is not set accurately for scans
|
|
+ ** that use the OR optimization.
|
|
*/
|
|
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
|
|
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
|
|
int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
|
|
int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
|
|
- if( bOnerow
|
|
- || ((wctrlFlags & WHERE_ONEPASS_MULTIROW)!=0
|
|
- && 0==(wsFlags & WHERE_VIRTUALTABLE))
|
|
- ){
|
|
+ if( bOnerow || (
|
|
+ 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW)
|
|
+ && 0==(wsFlags & WHERE_VIRTUALTABLE)
|
|
+ && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK))
|
|
+ )){
|
|
pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
|
|
if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){
|
|
if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){
|
|
@@ -136236,7 +144207,7 @@
|
|
Index *pIx = pLoop->u.btree.pIndex;
|
|
int iIndexCur;
|
|
int op = OP_OpenRead;
|
|
- /* iAuxArg is always set if to a positive value if ONEPASS is possible */
|
|
+ /* iAuxArg is always set to a positive value if ONEPASS is possible */
|
|
assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
|
|
if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx)
|
|
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0
|
|
@@ -136301,7 +144272,6 @@
|
|
** loop below generates code for a single nested loop of the VM
|
|
** program.
|
|
*/
|
|
- notReady = ~(Bitmask)0;
|
|
for(ii=0; ii<nTabList; ii++){
|
|
int addrExplain;
|
|
int wsFlags;
|
|
@@ -136315,7 +144285,7 @@
|
|
}
|
|
#endif
|
|
addrExplain = sqlite3WhereExplainOneScan(
|
|
- pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
|
|
+ pParse, pTabList, pLevel, wctrlFlags
|
|
);
|
|
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
|
|
notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
|
|
@@ -136339,6 +144309,26 @@
|
|
}
|
|
|
|
/*
|
|
+** Part of sqlite3WhereEnd() will rewrite opcodes to reference the
|
|
+** index rather than the main table. In SQLITE_DEBUG mode, we want
|
|
+** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine
|
|
+** does that.
|
|
+*/
|
|
+#ifndef SQLITE_DEBUG
|
|
+# define OpcodeRewriteTrace(D,K,P) /* no-op */
|
|
+#else
|
|
+# define OpcodeRewriteTrace(D,K,P) sqlite3WhereOpcodeRewriteTrace(D,K,P)
|
|
+ static void sqlite3WhereOpcodeRewriteTrace(
|
|
+ sqlite3 *db,
|
|
+ int pc,
|
|
+ VdbeOp *pOp
|
|
+ ){
|
|
+ if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
|
|
+ sqlite3VdbePrintOp(0, pc, pOp);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Generate the end of the WHERE loop. See comments on
|
|
** sqlite3WhereBegin() for additional information.
|
|
*/
|
|
@@ -136354,7 +144344,6 @@
|
|
/* Generate loop termination code.
|
|
*/
|
|
VdbeModuleComment((v, "End WHERE-core"));
|
|
- sqlite3ExprCacheClear(pParse);
|
|
for(i=pWInfo->nLevel-1; i>=0; i--){
|
|
int addr;
|
|
pLevel = &pWInfo->a[i];
|
|
@@ -136365,6 +144354,7 @@
|
|
Index *pIdx;
|
|
int n;
|
|
if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED
|
|
+ && i==pWInfo->nLevel-1 /* Ticket [ef9318757b152e3] 2017-10-21 */
|
|
&& (pLoop->wsFlags & WHERE_INDEXED)!=0
|
|
&& (pIdx = pLoop->u.btree.pIndex)->hasStat1
|
|
&& (n = pLoop->u.btree.nIdxCol)>0
|
|
@@ -136404,10 +144394,17 @@
|
|
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
|
|
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
|
|
if( pIn->eEndLoopOp!=OP_Noop ){
|
|
+ if( pIn->nPrefix ){
|
|
+ assert( pLoop->wsFlags & WHERE_IN_EARLYOUT );
|
|
+ sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur,
|
|
+ sqlite3VdbeCurrentAddr(v)+2,
|
|
+ pIn->iBase, pIn->nPrefix);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
|
|
VdbeCoverage(v);
|
|
- VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
|
|
- VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
|
|
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_Prev);
|
|
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_Next);
|
|
}
|
|
sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
|
|
}
|
|
@@ -136431,7 +144428,8 @@
|
|
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
|
|
assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 );
|
|
if( (ws & WHERE_IDX_ONLY)==0 ){
|
|
- sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor);
|
|
+ assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor );
|
|
+ sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur);
|
|
}
|
|
if( (ws & WHERE_INDEXED)
|
|
|| ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx)
|
|
@@ -136497,10 +144495,19 @@
|
|
){
|
|
last = sqlite3VdbeCurrentAddr(v);
|
|
k = pLevel->addrBody;
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( db->flags & SQLITE_VdbeAddopTrace ){
|
|
+ printf("TRANSLATE opcodes in range %d..%d\n", k, last-1);
|
|
+ }
|
|
+#endif
|
|
pOp = sqlite3VdbeGetOp(v, k);
|
|
for(; k<last; k++, pOp++){
|
|
if( pOp->p1!=pLevel->iTabCur ) continue;
|
|
- if( pOp->opcode==OP_Column ){
|
|
+ if( pOp->opcode==OP_Column
|
|
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
|
+ || pOp->opcode==OP_Offset
|
|
+#endif
|
|
+ ){
|
|
int x = pOp->p2;
|
|
assert( pIdx->pTable==pTab );
|
|
if( !HasRowid(pTab) ){
|
|
@@ -136512,6 +144519,7 @@
|
|
if( x>=0 ){
|
|
pOp->p2 = x;
|
|
pOp->p1 = pLevel->iIdxCur;
|
|
+ OpcodeRewriteTrace(db, k, pOp);
|
|
}
|
|
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0
|
|
|| pWInfo->eOnePass );
|
|
@@ -136518,10 +144526,15 @@
|
|
}else if( pOp->opcode==OP_Rowid ){
|
|
pOp->p1 = pLevel->iIdxCur;
|
|
pOp->opcode = OP_IdxRowid;
|
|
+ OpcodeRewriteTrace(db, k, pOp);
|
|
}else if( pOp->opcode==OP_IfNullRow ){
|
|
pOp->p1 = pLevel->iIdxCur;
|
|
+ OpcodeRewriteTrace(db, k, pOp);
|
|
}
|
|
}
|
|
+#ifdef SQLITE_DEBUG
|
|
+ if( db->flags & SQLITE_VdbeAddopTrace ) printf("TRANSLATE complete\n");
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
@@ -136533,6 +144546,2263 @@
|
|
}
|
|
|
|
/************** End of where.c ***********************************************/
|
|
+/************** Begin file window.c ******************************************/
|
|
+/*
|
|
+** 2018 May 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.
|
|
+**
|
|
+*************************************************************************
|
|
+*/
|
|
+/* #include "sqliteInt.h" */
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+
|
|
+/*
|
|
+** SELECT REWRITING
|
|
+**
|
|
+** Any SELECT statement that contains one or more window functions in
|
|
+** either the select list or ORDER BY clause (the only two places window
|
|
+** functions may be used) is transformed by function sqlite3WindowRewrite()
|
|
+** in order to support window function processing. For example, with the
|
|
+** schema:
|
|
+**
|
|
+** CREATE TABLE t1(a, b, c, d, e, f, g);
|
|
+**
|
|
+** the statement:
|
|
+**
|
|
+** SELECT a+1, max(b) OVER (PARTITION BY c ORDER BY d) FROM t1 ORDER BY e;
|
|
+**
|
|
+** is transformed to:
|
|
+**
|
|
+** SELECT a+1, max(b) OVER (PARTITION BY c ORDER BY d) FROM (
|
|
+** SELECT a, e, c, d, b FROM t1 ORDER BY c, d
|
|
+** ) ORDER BY e;
|
|
+**
|
|
+** The flattening optimization is disabled when processing this transformed
|
|
+** SELECT statement. This allows the implementation of the window function
|
|
+** (in this case max()) to process rows sorted in order of (c, d), which
|
|
+** makes things easier for obvious reasons. More generally:
|
|
+**
|
|
+** * FROM, WHERE, GROUP BY and HAVING clauses are all moved to
|
|
+** the sub-query.
|
|
+**
|
|
+** * ORDER BY, LIMIT and OFFSET remain part of the parent query.
|
|
+**
|
|
+** * Terminals from each of the expression trees that make up the
|
|
+** select-list and ORDER BY expressions in the parent query are
|
|
+** selected by the sub-query. For the purposes of the transformation,
|
|
+** terminals are column references and aggregate functions.
|
|
+**
|
|
+** If there is more than one window function in the SELECT that uses
|
|
+** the same window declaration (the OVER bit), then a single scan may
|
|
+** be used to process more than one window function. For example:
|
|
+**
|
|
+** SELECT max(b) OVER (PARTITION BY c ORDER BY d),
|
|
+** min(e) OVER (PARTITION BY c ORDER BY d)
|
|
+** FROM t1;
|
|
+**
|
|
+** is transformed in the same way as the example above. However:
|
|
+**
|
|
+** SELECT max(b) OVER (PARTITION BY c ORDER BY d),
|
|
+** min(e) OVER (PARTITION BY a ORDER BY b)
|
|
+** FROM t1;
|
|
+**
|
|
+** Must be transformed to:
|
|
+**
|
|
+** SELECT max(b) OVER (PARTITION BY c ORDER BY d) FROM (
|
|
+** SELECT e, min(e) OVER (PARTITION BY a ORDER BY b), c, d, b FROM
|
|
+** SELECT a, e, c, d, b FROM t1 ORDER BY a, b
|
|
+** ) ORDER BY c, d
|
|
+** ) ORDER BY e;
|
|
+**
|
|
+** so that both min() and max() may process rows in the order defined by
|
|
+** their respective window declarations.
|
|
+**
|
|
+** INTERFACE WITH SELECT.C
|
|
+**
|
|
+** When processing the rewritten SELECT statement, code in select.c calls
|
|
+** sqlite3WhereBegin() to begin iterating through the results of the
|
|
+** sub-query, which is always implemented as a co-routine. It then calls
|
|
+** sqlite3WindowCodeStep() to process rows and finish the scan by calling
|
|
+** sqlite3WhereEnd().
|
|
+**
|
|
+** sqlite3WindowCodeStep() generates VM code so that, for each row returned
|
|
+** by the sub-query a sub-routine (OP_Gosub) coded by select.c is invoked.
|
|
+** When the sub-routine is invoked:
|
|
+**
|
|
+** * The results of all window-functions for the row are stored
|
|
+** in the associated Window.regResult registers.
|
|
+**
|
|
+** * The required terminal values are stored in the current row of
|
|
+** temp table Window.iEphCsr.
|
|
+**
|
|
+** In some cases, depending on the window frame and the specific window
|
|
+** functions invoked, sqlite3WindowCodeStep() caches each entire partition
|
|
+** in a temp table before returning any rows. In other cases it does not.
|
|
+** This detail is encapsulated within this file, the code generated by
|
|
+** select.c is the same in either case.
|
|
+**
|
|
+** BUILT-IN WINDOW FUNCTIONS
|
|
+**
|
|
+** This implementation features the following built-in window functions:
|
|
+**
|
|
+** row_number()
|
|
+** rank()
|
|
+** dense_rank()
|
|
+** percent_rank()
|
|
+** cume_dist()
|
|
+** ntile(N)
|
|
+** lead(expr [, offset [, default]])
|
|
+** lag(expr [, offset [, default]])
|
|
+** first_value(expr)
|
|
+** last_value(expr)
|
|
+** nth_value(expr, N)
|
|
+**
|
|
+** These are the same built-in window functions supported by Postgres.
|
|
+** Although the behaviour of aggregate window functions (functions that
|
|
+** can be used as either aggregates or window funtions) allows them to
|
|
+** be implemented using an API, built-in window functions are much more
|
|
+** esoteric. Additionally, some window functions (e.g. nth_value())
|
|
+** may only be implemented by caching the entire partition in memory.
|
|
+** As such, some built-in window functions use the same API as aggregate
|
|
+** window functions and some are implemented directly using VDBE
|
|
+** instructions. Additionally, for those functions that use the API, the
|
|
+** window frame is sometimes modified before the SELECT statement is
|
|
+** rewritten. For example, regardless of the specified window frame, the
|
|
+** row_number() function always uses:
|
|
+**
|
|
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+**
|
|
+** See sqlite3WindowUpdate() for details.
|
|
+**
|
|
+** As well as some of the built-in window functions, aggregate window
|
|
+** functions min() and max() are implemented using VDBE instructions if
|
|
+** the start of the window frame is declared as anything other than
|
|
+** UNBOUNDED PRECEDING.
|
|
+*/
|
|
+
|
|
+/*
|
|
+** Implementation of built-in window function row_number(). Assumes that the
|
|
+** window frame has been coerced to:
|
|
+**
|
|
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+*/
|
|
+static void row_numberStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ i64 *p = (i64*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ) (*p)++;
|
|
+ UNUSED_PARAMETER(nArg);
|
|
+ UNUSED_PARAMETER(apArg);
|
|
+}
|
|
+static void row_numberValueFunc(sqlite3_context *pCtx){
|
|
+ i64 *p = (i64*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ sqlite3_result_int64(pCtx, (p ? *p : 0));
|
|
+}
|
|
+
|
|
+/*
|
|
+** Context object type used by rank(), dense_rank(), percent_rank() and
|
|
+** cume_dist().
|
|
+*/
|
|
+struct CallCount {
|
|
+ i64 nValue;
|
|
+ i64 nStep;
|
|
+ i64 nTotal;
|
|
+};
|
|
+
|
|
+/*
|
|
+** Implementation of built-in window function dense_rank(). Assumes that
|
|
+** the window frame has been set to:
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+*/
|
|
+static void dense_rankStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct CallCount *p;
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ) p->nStep = 1;
|
|
+ UNUSED_PARAMETER(nArg);
|
|
+ UNUSED_PARAMETER(apArg);
|
|
+}
|
|
+static void dense_rankValueFunc(sqlite3_context *pCtx){
|
|
+ struct CallCount *p;
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ if( p->nStep ){
|
|
+ p->nValue++;
|
|
+ p->nStep = 0;
|
|
+ }
|
|
+ sqlite3_result_int64(pCtx, p->nValue);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of built-in window function rank(). Assumes that
|
|
+** the window frame has been set to:
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+*/
|
|
+static void rankStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct CallCount *p;
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ p->nStep++;
|
|
+ if( p->nValue==0 ){
|
|
+ p->nValue = p->nStep;
|
|
+ }
|
|
+ }
|
|
+ UNUSED_PARAMETER(nArg);
|
|
+ UNUSED_PARAMETER(apArg);
|
|
+}
|
|
+static void rankValueFunc(sqlite3_context *pCtx){
|
|
+ struct CallCount *p;
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ sqlite3_result_int64(pCtx, p->nValue);
|
|
+ p->nValue = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of built-in window function percent_rank(). Assumes that
|
|
+** the window frame has been set to:
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+*/
|
|
+static void percent_rankStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct CallCount *p;
|
|
+ UNUSED_PARAMETER(nArg); assert( nArg==1 );
|
|
+
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ if( p->nTotal==0 ){
|
|
+ p->nTotal = sqlite3_value_int64(apArg[0]);
|
|
+ }
|
|
+ p->nStep++;
|
|
+ if( p->nValue==0 ){
|
|
+ p->nValue = p->nStep;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+static void percent_rankValueFunc(sqlite3_context *pCtx){
|
|
+ struct CallCount *p;
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ if( p->nTotal>1 ){
|
|
+ double r = (double)(p->nValue-1) / (double)(p->nTotal-1);
|
|
+ sqlite3_result_double(pCtx, r);
|
|
+ }else{
|
|
+ sqlite3_result_double(pCtx, 0.0);
|
|
+ }
|
|
+ p->nValue = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of built-in window function cume_dist(). Assumes that
|
|
+** the window frame has been set to:
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+*/
|
|
+static void cume_distStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct CallCount *p;
|
|
+ assert( nArg==1 ); UNUSED_PARAMETER(nArg);
|
|
+
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ if( p->nTotal==0 ){
|
|
+ p->nTotal = sqlite3_value_int64(apArg[0]);
|
|
+ }
|
|
+ p->nStep++;
|
|
+ }
|
|
+}
|
|
+static void cume_distValueFunc(sqlite3_context *pCtx){
|
|
+ struct CallCount *p;
|
|
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p && p->nTotal ){
|
|
+ double r = (double)(p->nStep) / (double)(p->nTotal);
|
|
+ sqlite3_result_double(pCtx, r);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Context object for ntile() window function.
|
|
+*/
|
|
+struct NtileCtx {
|
|
+ i64 nTotal; /* Total rows in partition */
|
|
+ i64 nParam; /* Parameter passed to ntile(N) */
|
|
+ i64 iRow; /* Current row */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Implementation of ntile(). This assumes that the window frame has
|
|
+** been coerced to:
|
|
+**
|
|
+** ROWS UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+*/
|
|
+static void ntileStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct NtileCtx *p;
|
|
+ assert( nArg==2 ); UNUSED_PARAMETER(nArg);
|
|
+ p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ if( p->nTotal==0 ){
|
|
+ p->nParam = sqlite3_value_int64(apArg[0]);
|
|
+ p->nTotal = sqlite3_value_int64(apArg[1]);
|
|
+ if( p->nParam<=0 ){
|
|
+ sqlite3_result_error(
|
|
+ pCtx, "argument of ntile must be a positive integer", -1
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ p->iRow++;
|
|
+ }
|
|
+}
|
|
+static void ntileValueFunc(sqlite3_context *pCtx){
|
|
+ struct NtileCtx *p;
|
|
+ p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p && p->nParam>0 ){
|
|
+ int nSize = (p->nTotal / p->nParam);
|
|
+ if( nSize==0 ){
|
|
+ sqlite3_result_int64(pCtx, p->iRow);
|
|
+ }else{
|
|
+ i64 nLarge = p->nTotal - p->nParam*nSize;
|
|
+ i64 iSmall = nLarge*(nSize+1);
|
|
+ i64 iRow = p->iRow-1;
|
|
+
|
|
+ assert( (nLarge*(nSize+1) + (p->nParam-nLarge)*nSize)==p->nTotal );
|
|
+
|
|
+ if( iRow<iSmall ){
|
|
+ sqlite3_result_int64(pCtx, 1 + iRow/(nSize+1));
|
|
+ }else{
|
|
+ sqlite3_result_int64(pCtx, 1 + nLarge + (iRow-iSmall)/nSize);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Context object for last_value() window function.
|
|
+*/
|
|
+struct LastValueCtx {
|
|
+ sqlite3_value *pVal;
|
|
+ int nVal;
|
|
+};
|
|
+
|
|
+/*
|
|
+** Implementation of last_value().
|
|
+*/
|
|
+static void last_valueStepFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct LastValueCtx *p;
|
|
+ UNUSED_PARAMETER(nArg);
|
|
+ p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p ){
|
|
+ sqlite3_value_free(p->pVal);
|
|
+ p->pVal = sqlite3_value_dup(apArg[0]);
|
|
+ if( p->pVal==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ p->nVal++;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+static void last_valueInvFunc(
|
|
+ sqlite3_context *pCtx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ struct LastValueCtx *p;
|
|
+ UNUSED_PARAMETER(nArg);
|
|
+ UNUSED_PARAMETER(apArg);
|
|
+ p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( ALWAYS(p) ){
|
|
+ p->nVal--;
|
|
+ if( p->nVal==0 ){
|
|
+ sqlite3_value_free(p->pVal);
|
|
+ p->pVal = 0;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+static void last_valueValueFunc(sqlite3_context *pCtx){
|
|
+ struct LastValueCtx *p;
|
|
+ p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p && p->pVal ){
|
|
+ sqlite3_result_value(pCtx, p->pVal);
|
|
+ }
|
|
+}
|
|
+static void last_valueFinalizeFunc(sqlite3_context *pCtx){
|
|
+ struct LastValueCtx *p;
|
|
+ p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
|
+ if( p && p->pVal ){
|
|
+ sqlite3_result_value(pCtx, p->pVal);
|
|
+ sqlite3_value_free(p->pVal);
|
|
+ p->pVal = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Static names for the built-in window function names. These static
|
|
+** names are used, rather than string literals, so that FuncDef objects
|
|
+** can be associated with a particular window function by direct
|
|
+** comparison of the zName pointer. Example:
|
|
+**
|
|
+** if( pFuncDef->zName==row_valueName ){ ... }
|
|
+*/
|
|
+static const char row_numberName[] = "row_number";
|
|
+static const char dense_rankName[] = "dense_rank";
|
|
+static const char rankName[] = "rank";
|
|
+static const char percent_rankName[] = "percent_rank";
|
|
+static const char cume_distName[] = "cume_dist";
|
|
+static const char ntileName[] = "ntile";
|
|
+static const char last_valueName[] = "last_value";
|
|
+static const char nth_valueName[] = "nth_value";
|
|
+static const char first_valueName[] = "first_value";
|
|
+static const char leadName[] = "lead";
|
|
+static const char lagName[] = "lag";
|
|
+
|
|
+/*
|
|
+** No-op implementations of xStep() and xFinalize(). Used as place-holders
|
|
+** for built-in window functions that never call those interfaces.
|
|
+**
|
|
+** The noopValueFunc() is called but is expected to do nothing. The
|
|
+** noopStepFunc() is never called, and so it is marked with NO_TEST to
|
|
+** let the test coverage routine know not to expect this function to be
|
|
+** invoked.
|
|
+*/
|
|
+static void noopStepFunc( /*NO_TEST*/
|
|
+ sqlite3_context *p, /*NO_TEST*/
|
|
+ int n, /*NO_TEST*/
|
|
+ sqlite3_value **a /*NO_TEST*/
|
|
+){ /*NO_TEST*/
|
|
+ UNUSED_PARAMETER(p); /*NO_TEST*/
|
|
+ UNUSED_PARAMETER(n); /*NO_TEST*/
|
|
+ UNUSED_PARAMETER(a); /*NO_TEST*/
|
|
+ assert(0); /*NO_TEST*/
|
|
+} /*NO_TEST*/
|
|
+static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ }
|
|
+
|
|
+/* Window functions that use all window interfaces: xStep, xFinal,
|
|
+** xValue, and xInverse */
|
|
+#define WINDOWFUNCALL(name,nArg,extra) { \
|
|
+ nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
|
|
+ name ## StepFunc, name ## FinalizeFunc, name ## ValueFunc, \
|
|
+ name ## InvFunc, name ## Name, {0} \
|
|
+}
|
|
+
|
|
+/* Window functions that are implemented using bytecode and thus have
|
|
+** no-op routines for their methods */
|
|
+#define WINDOWFUNCNOOP(name,nArg,extra) { \
|
|
+ nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
|
|
+ noopStepFunc, noopValueFunc, noopValueFunc, \
|
|
+ noopStepFunc, name ## Name, {0} \
|
|
+}
|
|
+
|
|
+/* Window functions that use all window interfaces: xStep, the
|
|
+** same routine for xFinalize and xValue and which never call
|
|
+** xInverse. */
|
|
+#define WINDOWFUNCX(name,nArg,extra) { \
|
|
+ nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
|
|
+ name ## StepFunc, name ## ValueFunc, name ## ValueFunc, \
|
|
+ noopStepFunc, name ## Name, {0} \
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Register those built-in window functions that are not also aggregates.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowFunctions(void){
|
|
+ static FuncDef aWindowFuncs[] = {
|
|
+ WINDOWFUNCX(row_number, 0, 0),
|
|
+ WINDOWFUNCX(dense_rank, 0, 0),
|
|
+ WINDOWFUNCX(rank, 0, 0),
|
|
+ WINDOWFUNCX(percent_rank, 0, SQLITE_FUNC_WINDOW_SIZE),
|
|
+ WINDOWFUNCX(cume_dist, 0, SQLITE_FUNC_WINDOW_SIZE),
|
|
+ WINDOWFUNCX(ntile, 1, SQLITE_FUNC_WINDOW_SIZE),
|
|
+ WINDOWFUNCALL(last_value, 1, 0),
|
|
+ WINDOWFUNCNOOP(nth_value, 2, 0),
|
|
+ WINDOWFUNCNOOP(first_value, 1, 0),
|
|
+ WINDOWFUNCNOOP(lead, 1, 0),
|
|
+ WINDOWFUNCNOOP(lead, 2, 0),
|
|
+ WINDOWFUNCNOOP(lead, 3, 0),
|
|
+ WINDOWFUNCNOOP(lag, 1, 0),
|
|
+ WINDOWFUNCNOOP(lag, 2, 0),
|
|
+ WINDOWFUNCNOOP(lag, 3, 0),
|
|
+ };
|
|
+ sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs));
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is called immediately after resolving the function name
|
|
+** for a window function within a SELECT statement. Argument pList is a
|
|
+** linked list of WINDOW definitions for the current SELECT statement.
|
|
+** Argument pFunc is the function definition just resolved and pWin
|
|
+** is the Window object representing the associated OVER clause. This
|
|
+** function updates the contents of pWin as follows:
|
|
+**
|
|
+** * If the OVER clause refered to a named window (as in "max(x) OVER win"),
|
|
+** search list pList for a matching WINDOW definition, and update pWin
|
|
+** accordingly. If no such WINDOW clause can be found, leave an error
|
|
+** in pParse.
|
|
+**
|
|
+** * If the function is a built-in window function that requires the
|
|
+** window to be coerced (see "BUILT-IN WINDOW FUNCTIONS" at the top
|
|
+** of this file), pWin is updated here.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowUpdate(
|
|
+ Parse *pParse,
|
|
+ Window *pList, /* List of named windows for this SELECT */
|
|
+ Window *pWin, /* Window frame to update */
|
|
+ FuncDef *pFunc /* Window function definition */
|
|
+){
|
|
+ if( pWin->zName && pWin->eType==0 ){
|
|
+ Window *p;
|
|
+ for(p=pList; p; p=p->pNextWin){
|
|
+ if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break;
|
|
+ }
|
|
+ if( p==0 ){
|
|
+ sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName);
|
|
+ return;
|
|
+ }
|
|
+ pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0);
|
|
+ pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0);
|
|
+ pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0);
|
|
+ pWin->pEnd = sqlite3ExprDup(pParse->db, p->pEnd, 0);
|
|
+ pWin->eStart = p->eStart;
|
|
+ pWin->eEnd = p->eEnd;
|
|
+ pWin->eType = p->eType;
|
|
+ }
|
|
+ if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){
|
|
+ sqlite3 *db = pParse->db;
|
|
+ if( pWin->pFilter ){
|
|
+ sqlite3ErrorMsg(pParse,
|
|
+ "FILTER clause may only be used with aggregate window functions"
|
|
+ );
|
|
+ }else
|
|
+ if( pFunc->zName==row_numberName || pFunc->zName==ntileName ){
|
|
+ sqlite3ExprDelete(db, pWin->pStart);
|
|
+ sqlite3ExprDelete(db, pWin->pEnd);
|
|
+ pWin->pStart = pWin->pEnd = 0;
|
|
+ pWin->eType = TK_ROWS;
|
|
+ pWin->eStart = TK_UNBOUNDED;
|
|
+ pWin->eEnd = TK_CURRENT;
|
|
+ }else
|
|
+
|
|
+ if( pFunc->zName==dense_rankName || pFunc->zName==rankName
|
|
+ || pFunc->zName==percent_rankName || pFunc->zName==cume_distName
|
|
+ ){
|
|
+ sqlite3ExprDelete(db, pWin->pStart);
|
|
+ sqlite3ExprDelete(db, pWin->pEnd);
|
|
+ pWin->pStart = pWin->pEnd = 0;
|
|
+ pWin->eType = TK_RANGE;
|
|
+ pWin->eStart = TK_UNBOUNDED;
|
|
+ pWin->eEnd = TK_CURRENT;
|
|
+ }
|
|
+ }
|
|
+ pWin->pFunc = pFunc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Context object passed through sqlite3WalkExprList() to
|
|
+** selectWindowRewriteExprCb() by selectWindowRewriteEList().
|
|
+*/
|
|
+typedef struct WindowRewrite WindowRewrite;
|
|
+struct WindowRewrite {
|
|
+ Window *pWin;
|
|
+ SrcList *pSrc;
|
|
+ ExprList *pSub;
|
|
+ Select *pSubSelect; /* Current sub-select, if any */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Callback function used by selectWindowRewriteEList(). If necessary,
|
|
+** this function appends to the output expression-list and updates
|
|
+** expression (*ppExpr) in place.
|
|
+*/
|
|
+static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
|
|
+ struct WindowRewrite *p = pWalker->u.pRewrite;
|
|
+ Parse *pParse = pWalker->pParse;
|
|
+
|
|
+ /* If this function is being called from within a scalar sub-select
|
|
+ ** that used by the SELECT statement being processed, only process
|
|
+ ** TK_COLUMN expressions that refer to it (the outer SELECT). Do
|
|
+ ** not process aggregates or window functions at all, as they belong
|
|
+ ** to the scalar sub-select. */
|
|
+ if( p->pSubSelect ){
|
|
+ if( pExpr->op!=TK_COLUMN ){
|
|
+ return WRC_Continue;
|
|
+ }else{
|
|
+ int nSrc = p->pSrc->nSrc;
|
|
+ int i;
|
|
+ for(i=0; i<nSrc; i++){
|
|
+ if( pExpr->iTable==p->pSrc->a[i].iCursor ) break;
|
|
+ }
|
|
+ if( i==nSrc ) return WRC_Continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ switch( pExpr->op ){
|
|
+
|
|
+ case TK_FUNCTION:
|
|
+ if( !ExprHasProperty(pExpr, EP_WinFunc) ){
|
|
+ break;
|
|
+ }else{
|
|
+ Window *pWin;
|
|
+ for(pWin=p->pWin; pWin; pWin=pWin->pNextWin){
|
|
+ if( pExpr->y.pWin==pWin ){
|
|
+ assert( pWin->pOwner==pExpr );
|
|
+ return WRC_Prune;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ /* Fall through. */
|
|
+
|
|
+ case TK_AGG_FUNCTION:
|
|
+ case TK_COLUMN: {
|
|
+ Expr *pDup = sqlite3ExprDup(pParse->db, pExpr, 0);
|
|
+ p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup);
|
|
+ if( p->pSub ){
|
|
+ assert( ExprHasProperty(pExpr, EP_Static)==0 );
|
|
+ ExprSetProperty(pExpr, EP_Static);
|
|
+ sqlite3ExprDelete(pParse->db, pExpr);
|
|
+ ExprClearProperty(pExpr, EP_Static);
|
|
+ memset(pExpr, 0, sizeof(Expr));
|
|
+
|
|
+ pExpr->op = TK_COLUMN;
|
|
+ pExpr->iColumn = p->pSub->nExpr-1;
|
|
+ pExpr->iTable = p->pWin->iEphCsr;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ default: /* no-op */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return WRC_Continue;
|
|
+}
|
|
+static int selectWindowRewriteSelectCb(Walker *pWalker, Select *pSelect){
|
|
+ struct WindowRewrite *p = pWalker->u.pRewrite;
|
|
+ Select *pSave = p->pSubSelect;
|
|
+ if( pSave==pSelect ){
|
|
+ return WRC_Continue;
|
|
+ }else{
|
|
+ p->pSubSelect = pSelect;
|
|
+ sqlite3WalkSelect(pWalker, pSelect);
|
|
+ p->pSubSelect = pSave;
|
|
+ }
|
|
+ return WRC_Prune;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Iterate through each expression in expression-list pEList. For each:
|
|
+**
|
|
+** * TK_COLUMN,
|
|
+** * aggregate function, or
|
|
+** * window function with a Window object that is not a member of the
|
|
+** Window list passed as the second argument (pWin).
|
|
+**
|
|
+** Append the node to output expression-list (*ppSub). And replace it
|
|
+** with a TK_COLUMN that reads the (N-1)th element of table
|
|
+** pWin->iEphCsr, where N is the number of elements in (*ppSub) after
|
|
+** appending the new one.
|
|
+*/
|
|
+static void selectWindowRewriteEList(
|
|
+ Parse *pParse,
|
|
+ Window *pWin,
|
|
+ SrcList *pSrc,
|
|
+ ExprList *pEList, /* Rewrite expressions in this list */
|
|
+ ExprList **ppSub /* IN/OUT: Sub-select expression-list */
|
|
+){
|
|
+ Walker sWalker;
|
|
+ WindowRewrite sRewrite;
|
|
+
|
|
+ memset(&sWalker, 0, sizeof(Walker));
|
|
+ memset(&sRewrite, 0, sizeof(WindowRewrite));
|
|
+
|
|
+ sRewrite.pSub = *ppSub;
|
|
+ sRewrite.pWin = pWin;
|
|
+ sRewrite.pSrc = pSrc;
|
|
+
|
|
+ sWalker.pParse = pParse;
|
|
+ sWalker.xExprCallback = selectWindowRewriteExprCb;
|
|
+ sWalker.xSelectCallback = selectWindowRewriteSelectCb;
|
|
+ sWalker.u.pRewrite = &sRewrite;
|
|
+
|
|
+ (void)sqlite3WalkExprList(&sWalker, pEList);
|
|
+
|
|
+ *ppSub = sRewrite.pSub;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Append a copy of each expression in expression-list pAppend to
|
|
+** expression list pList. Return a pointer to the result list.
|
|
+*/
|
|
+static ExprList *exprListAppendList(
|
|
+ Parse *pParse, /* Parsing context */
|
|
+ ExprList *pList, /* List to which to append. Might be NULL */
|
|
+ ExprList *pAppend /* List of values to append. Might be NULL */
|
|
+){
|
|
+ if( pAppend ){
|
|
+ int i;
|
|
+ int nInit = pList ? pList->nExpr : 0;
|
|
+ for(i=0; i<pAppend->nExpr; i++){
|
|
+ Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0);
|
|
+ pList = sqlite3ExprListAppend(pParse, pList, pDup);
|
|
+ if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder;
|
|
+ }
|
|
+ }
|
|
+ return pList;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If the SELECT statement passed as the second argument does not invoke
|
|
+** any SQL window functions, this function is a no-op. Otherwise, it
|
|
+** rewrites the SELECT statement so that window function xStep functions
|
|
+** are invoked in the correct order as described under "SELECT REWRITING"
|
|
+** at the top of this file.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( p->pWin && p->pPrior==0 ){
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ sqlite3 *db = pParse->db;
|
|
+ Select *pSub = 0; /* The subquery */
|
|
+ SrcList *pSrc = p->pSrc;
|
|
+ Expr *pWhere = p->pWhere;
|
|
+ ExprList *pGroupBy = p->pGroupBy;
|
|
+ Expr *pHaving = p->pHaving;
|
|
+ ExprList *pSort = 0;
|
|
+
|
|
+ ExprList *pSublist = 0; /* Expression list for sub-query */
|
|
+ Window *pMWin = p->pWin; /* Master window object */
|
|
+ Window *pWin; /* Window object iterator */
|
|
+
|
|
+ p->pSrc = 0;
|
|
+ p->pWhere = 0;
|
|
+ p->pGroupBy = 0;
|
|
+ p->pHaving = 0;
|
|
+
|
|
+ /* Create the ORDER BY clause for the sub-select. This is the concatenation
|
|
+ ** of the window PARTITION and ORDER BY clauses. Then, if this makes it
|
|
+ ** redundant, remove the ORDER BY from the parent SELECT. */
|
|
+ pSort = sqlite3ExprListDup(db, pMWin->pPartition, 0);
|
|
+ pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy);
|
|
+ if( pSort && p->pOrderBy ){
|
|
+ if( sqlite3ExprListCompare(pSort, p->pOrderBy, -1)==0 ){
|
|
+ sqlite3ExprListDelete(db, p->pOrderBy);
|
|
+ p->pOrderBy = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Assign a cursor number for the ephemeral table used to buffer rows.
|
|
+ ** The OpenEphemeral instruction is coded later, after it is known how
|
|
+ ** many columns the table will have. */
|
|
+ pMWin->iEphCsr = pParse->nTab++;
|
|
+
|
|
+ selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist);
|
|
+ selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist);
|
|
+ pMWin->nBufferCol = (pSublist ? pSublist->nExpr : 0);
|
|
+
|
|
+ /* Append the PARTITION BY and ORDER BY expressions to the to the
|
|
+ ** sub-select expression list. They are required to figure out where
|
|
+ ** boundaries for partitions and sets of peer rows lie. */
|
|
+ pSublist = exprListAppendList(pParse, pSublist, pMWin->pPartition);
|
|
+ pSublist = exprListAppendList(pParse, pSublist, pMWin->pOrderBy);
|
|
+
|
|
+ /* Append the arguments passed to each window function to the
|
|
+ ** sub-select expression list. Also allocate two registers for each
|
|
+ ** window function - one for the accumulator, another for interim
|
|
+ ** results. */
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
|
|
+ pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList);
|
|
+ if( pWin->pFilter ){
|
|
+ Expr *pFilter = sqlite3ExprDup(db, pWin->pFilter, 0);
|
|
+ pSublist = sqlite3ExprListAppend(pParse, pSublist, pFilter);
|
|
+ }
|
|
+ pWin->regAccum = ++pParse->nMem;
|
|
+ pWin->regResult = ++pParse->nMem;
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
|
|
+ }
|
|
+
|
|
+ /* If there is no ORDER BY or PARTITION BY clause, and the window
|
|
+ ** function accepts zero arguments, and there are no other columns
|
|
+ ** selected (e.g. "SELECT row_number() OVER () FROM t1"), it is possible
|
|
+ ** that pSublist is still NULL here. Add a constant expression here to
|
|
+ ** keep everything legal in this case.
|
|
+ */
|
|
+ if( pSublist==0 ){
|
|
+ pSublist = sqlite3ExprListAppend(pParse, 0,
|
|
+ sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0)
|
|
+ );
|
|
+ }
|
|
+
|
|
+ pSub = sqlite3SelectNew(
|
|
+ pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
|
|
+ );
|
|
+ p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
|
|
+ assert( p->pSrc || db->mallocFailed );
|
|
+ if( p->pSrc ){
|
|
+ p->pSrc->a[0].pSelect = pSub;
|
|
+ sqlite3SrcListAssignCursors(pParse, p->pSrc);
|
|
+ if( sqlite3ExpandSubquery(pParse, &p->pSrc->a[0]) ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ pSub->selFlags |= SF_Expanded;
|
|
+ p->selFlags &= ~SF_Aggregate;
|
|
+ sqlite3SelectPrep(pParse, pSub, 0);
|
|
+ }
|
|
+
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr);
|
|
+ }else{
|
|
+ sqlite3SelectDelete(db, pSub);
|
|
+ }
|
|
+ if( db->mallocFailed ) rc = SQLITE_NOMEM;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the Window object passed as the second argument.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){
|
|
+ if( p ){
|
|
+ sqlite3ExprDelete(db, p->pFilter);
|
|
+ sqlite3ExprListDelete(db, p->pPartition);
|
|
+ sqlite3ExprListDelete(db, p->pOrderBy);
|
|
+ sqlite3ExprDelete(db, p->pEnd);
|
|
+ sqlite3ExprDelete(db, p->pStart);
|
|
+ sqlite3DbFree(db, p->zName);
|
|
+ sqlite3DbFree(db, p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free the linked list of Window objects starting at the second argument.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){
|
|
+ while( p ){
|
|
+ Window *pNext = p->pNextWin;
|
|
+ sqlite3WindowDelete(db, p);
|
|
+ p = pNext;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The argument expression is an PRECEDING or FOLLOWING offset. The
|
|
+** value should be a non-negative integer. If the value is not a
|
|
+** constant, change it to NULL. The fact that it is then a non-negative
|
|
+** integer will be caught later. But it is important not to leave
|
|
+** variable values in the expression tree.
|
|
+*/
|
|
+static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
|
|
+ if( 0==sqlite3ExprIsConstant(pExpr) ){
|
|
+ sqlite3ExprDelete(pParse->db, pExpr);
|
|
+ pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0);
|
|
+ }
|
|
+ return pExpr;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate and return a new Window object describing a Window Definition.
|
|
+*/
|
|
+SQLITE_PRIVATE Window *sqlite3WindowAlloc(
|
|
+ Parse *pParse, /* Parsing context */
|
|
+ int eType, /* Frame type. TK_RANGE or TK_ROWS */
|
|
+ int eStart, /* Start type: CURRENT, PRECEDING, FOLLOWING, UNBOUNDED */
|
|
+ Expr *pStart, /* Start window size if TK_PRECEDING or FOLLOWING */
|
|
+ int eEnd, /* End type: CURRENT, FOLLOWING, TK_UNBOUNDED, PRECEDING */
|
|
+ Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */
|
|
+){
|
|
+ Window *pWin = 0;
|
|
+
|
|
+ /* Parser assures the following: */
|
|
+ assert( eType==TK_RANGE || eType==TK_ROWS );
|
|
+ assert( eStart==TK_CURRENT || eStart==TK_PRECEDING
|
|
+ || eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING );
|
|
+ assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING
|
|
+ || eEnd==TK_UNBOUNDED || eEnd==TK_PRECEDING );
|
|
+ assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) );
|
|
+ assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) );
|
|
+
|
|
+
|
|
+ /* If a frame is declared "RANGE" (not "ROWS"), then it may not use
|
|
+ ** either "<expr> PRECEDING" or "<expr> FOLLOWING".
|
|
+ */
|
|
+ if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){
|
|
+ sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW");
|
|
+ goto windowAllocErr;
|
|
+ }
|
|
+
|
|
+ /* Additionally, the
|
|
+ ** starting boundary type may not occur earlier in the following list than
|
|
+ ** the ending boundary type:
|
|
+ **
|
|
+ ** UNBOUNDED PRECEDING
|
|
+ ** <expr> PRECEDING
|
|
+ ** CURRENT ROW
|
|
+ ** <expr> FOLLOWING
|
|
+ ** UNBOUNDED FOLLOWING
|
|
+ **
|
|
+ ** The parser ensures that "UNBOUNDED PRECEDING" cannot be used as an ending
|
|
+ ** boundary, and than "UNBOUNDED FOLLOWING" cannot be used as a starting
|
|
+ ** frame boundary.
|
|
+ */
|
|
+ if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING)
|
|
+ || (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT))
|
|
+ ){
|
|
+ sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS");
|
|
+ goto windowAllocErr;
|
|
+ }
|
|
+
|
|
+ pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
|
|
+ if( pWin==0 ) goto windowAllocErr;
|
|
+ pWin->eType = eType;
|
|
+ pWin->eStart = eStart;
|
|
+ pWin->eEnd = eEnd;
|
|
+ pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd);
|
|
+ pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart);
|
|
+ return pWin;
|
|
+
|
|
+windowAllocErr:
|
|
+ sqlite3ExprDelete(pParse->db, pEnd);
|
|
+ sqlite3ExprDelete(pParse->db, pStart);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Attach window object pWin to expression p.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
|
|
+ if( p ){
|
|
+ assert( p->op==TK_FUNCTION );
|
|
+ /* This routine is only called for the parser. If pWin was not
|
|
+ ** allocated due to an OOM, then the parser would fail before ever
|
|
+ ** invoking this routine */
|
|
+ if( ALWAYS(pWin) ){
|
|
+ p->y.pWin = pWin;
|
|
+ ExprSetProperty(p, EP_WinFunc);
|
|
+ pWin->pOwner = p;
|
|
+ if( p->flags & EP_Distinct ){
|
|
+ sqlite3ErrorMsg(pParse,
|
|
+ "DISTINCT is not supported for window functions");
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3WindowDelete(pParse->db, pWin);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return 0 if the two window objects are identical, or non-zero otherwise.
|
|
+** Identical window objects can be processed in a single scan.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
|
|
+ if( p1->eType!=p2->eType ) return 1;
|
|
+ if( p1->eStart!=p2->eStart ) return 1;
|
|
+ if( p1->eEnd!=p2->eEnd ) return 1;
|
|
+ if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1;
|
|
+ if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1;
|
|
+ if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1;
|
|
+ if( sqlite3ExprListCompare(p1->pOrderBy, p2->pOrderBy, -1) ) return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** This is called by code in select.c before it calls sqlite3WhereBegin()
|
|
+** to begin iterating through the sub-query results. It is used to allocate
|
|
+** and initialize registers and cursors used by sqlite3WindowCodeStep().
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
|
|
+ Window *pWin;
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0);
|
|
+ nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
|
|
+ if( nPart ){
|
|
+ pMWin->regPart = pParse->nMem+1;
|
|
+ pParse->nMem += nPart;
|
|
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1);
|
|
+ }
|
|
+
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ FuncDef *p = pWin->pFunc;
|
|
+ if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
|
|
+ /* The inline versions of min() and max() require a single ephemeral
|
|
+ ** table and 3 registers. The registers are used as follows:
|
|
+ **
|
|
+ ** regApp+0: slot to copy min()/max() argument to for MakeRecord
|
|
+ ** regApp+1: integer value used to ensure keys are unique
|
|
+ ** regApp+2: output of MakeRecord
|
|
+ */
|
|
+ ExprList *pList = pWin->pOwner->x.pList;
|
|
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0);
|
|
+ pWin->csrApp = pParse->nTab++;
|
|
+ pWin->regApp = pParse->nMem+1;
|
|
+ pParse->nMem += 3;
|
|
+ if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){
|
|
+ assert( pKeyInfo->aSortOrder[0]==0 );
|
|
+ pKeyInfo->aSortOrder[0] = 1;
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->csrApp, 2);
|
|
+ sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
|
|
+ }
|
|
+ else if( p->zName==nth_valueName || p->zName==first_valueName ){
|
|
+ /* Allocate two registers at pWin->regApp. These will be used to
|
|
+ ** store the start and end index of the current frame. */
|
|
+ assert( pMWin->iEphCsr );
|
|
+ pWin->regApp = pParse->nMem+1;
|
|
+ pWin->csrApp = pParse->nTab++;
|
|
+ pParse->nMem += 2;
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
|
|
+ }
|
|
+ else if( p->zName==leadName || p->zName==lagName ){
|
|
+ assert( pMWin->iEphCsr );
|
|
+ pWin->csrApp = pParse->nTab++;
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** A "PRECEDING <expr>" (eCond==0) or "FOLLOWING <expr>" (eCond==1) or the
|
|
+** value of the second argument to nth_value() (eCond==2) has just been
|
|
+** evaluated and the result left in register reg. This function generates VM
|
|
+** code to check that the value is a non-negative integer and throws an
|
|
+** exception if it is not.
|
|
+*/
|
|
+static void windowCheckIntValue(Parse *pParse, int reg, int eCond){
|
|
+ static const char *azErr[] = {
|
|
+ "frame starting offset must be a non-negative integer",
|
|
+ "frame ending offset must be a non-negative integer",
|
|
+ "second argument to nth_value must be a positive integer"
|
|
+ };
|
|
+ static int aOp[] = { OP_Ge, OP_Ge, OP_Gt };
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int regZero = sqlite3GetTempReg(pParse);
|
|
+ assert( eCond==0 || eCond==1 || eCond==2 );
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regZero);
|
|
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2);
|
|
+ VdbeCoverageIf(v, eCond==0);
|
|
+ VdbeCoverageIf(v, eCond==1);
|
|
+ VdbeCoverageIf(v, eCond==2);
|
|
+ sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg);
|
|
+ VdbeCoverageNeverNullIf(v, eCond==0);
|
|
+ VdbeCoverageNeverNullIf(v, eCond==1);
|
|
+ VdbeCoverageNeverNullIf(v, eCond==2);
|
|
+ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort);
|
|
+ sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC);
|
|
+ sqlite3ReleaseTempReg(pParse, regZero);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the number of arguments passed to the window-function associated
|
|
+** with the object passed as the only argument to this function.
|
|
+*/
|
|
+static int windowArgCount(Window *pWin){
|
|
+ ExprList *pList = pWin->pOwner->x.pList;
|
|
+ return (pList ? pList->nExpr : 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Generate VM code to invoke either xStep() (if bInverse is 0) or
|
|
+** xInverse (if bInverse is non-zero) for each window function in the
|
|
+** linked list starting at pMWin. Or, for built-in window functions
|
|
+** that do not use the standard function API, generate the required
|
|
+** inline VM code.
|
|
+**
|
|
+** If argument csr is greater than or equal to 0, then argument reg is
|
|
+** the first register in an array of registers guaranteed to be large
|
|
+** enough to hold the array of arguments for each function. In this case
|
|
+** the arguments are extracted from the current row of csr into the
|
|
+** array of registers before invoking OP_AggStep or OP_AggInverse
|
|
+**
|
|
+** Or, if csr is less than zero, then the array of registers at reg is
|
|
+** already populated with all columns from the current row of the sub-query.
|
|
+**
|
|
+** If argument regPartSize is non-zero, then it is a register containing the
|
|
+** number of rows in the current partition.
|
|
+*/
|
|
+static void windowAggStep(
|
|
+ Parse *pParse,
|
|
+ Window *pMWin, /* Linked list of window functions */
|
|
+ int csr, /* Read arguments from this cursor */
|
|
+ int bInverse, /* True to invoke xInverse instead of xStep */
|
|
+ int reg, /* Array of registers */
|
|
+ int regPartSize /* Register containing size of partition */
|
|
+){
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ Window *pWin;
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ int flags = pWin->pFunc->funcFlags;
|
|
+ int regArg;
|
|
+ int nArg = windowArgCount(pWin);
|
|
+
|
|
+ if( csr>=0 ){
|
|
+ int i;
|
|
+ for(i=0; i<nArg; i++){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
|
|
+ }
|
|
+ regArg = reg;
|
|
+ if( flags & SQLITE_FUNC_WINDOW_SIZE ){
|
|
+ if( nArg==0 ){
|
|
+ regArg = regPartSize;
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_SCopy, regPartSize, reg+nArg);
|
|
+ }
|
|
+ nArg++;
|
|
+ }
|
|
+ }else{
|
|
+ assert( !(flags & SQLITE_FUNC_WINDOW_SIZE) );
|
|
+ regArg = reg + pWin->iArgCol;
|
|
+ }
|
|
+
|
|
+ if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
|
|
+ && pWin->eStart!=TK_UNBOUNDED
|
|
+ ){
|
|
+ int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
|
|
+ VdbeCoverage(v);
|
|
+ if( bInverse==0 ){
|
|
+ sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
|
|
+ sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
|
|
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
|
|
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
|
|
+ VdbeCoverageNeverTaken(v);
|
|
+ sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
|
|
+ sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
|
+ }
|
|
+ sqlite3VdbeJumpHere(v, addrIsNull);
|
|
+ }else if( pWin->regApp ){
|
|
+ assert( pWin->pFunc->zName==nth_valueName
|
|
+ || pWin->pFunc->zName==first_valueName
|
|
+ );
|
|
+ assert( bInverse==0 || bInverse==1 );
|
|
+ sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
|
|
+ }else if( pWin->pFunc->zName==leadName
|
|
+ || pWin->pFunc->zName==lagName
|
|
+ ){
|
|
+ /* no-op */
|
|
+ }else{
|
|
+ int addrIf = 0;
|
|
+ if( pWin->pFilter ){
|
|
+ int regTmp;
|
|
+ assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr );
|
|
+ assert( nArg || pWin->pOwner->x.pList==0 );
|
|
+ if( csr>0 ){
|
|
+ regTmp = sqlite3GetTempReg(pParse);
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
|
|
+ }else{
|
|
+ regTmp = regArg + nArg;
|
|
+ }
|
|
+ addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
|
|
+ VdbeCoverage(v);
|
|
+ if( csr>0 ){
|
|
+ sqlite3ReleaseTempReg(pParse, regTmp);
|
|
+ }
|
|
+ }
|
|
+ if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
|
+ CollSeq *pColl;
|
|
+ assert( nArg>0 );
|
|
+ pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
|
|
+ sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
|
|
+ }
|
|
+ sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
|
|
+ bInverse, regArg, pWin->regAccum);
|
|
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
+ sqlite3VdbeChangeP5(v, (u8)nArg);
|
|
+ if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize()
|
|
+** (bFinal==1) for each window function in the linked list starting at
|
|
+** pMWin. Or, for built-in window-functions that do not use the standard
|
|
+** API, generate the equivalent VM code.
|
|
+*/
|
|
+static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ Window *pWin;
|
|
+
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
|
|
+ && pWin->eStart!=TK_UNBOUNDED
|
|
+ ){
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
|
|
+ sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult);
|
|
+ sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
|
+ if( bFinal ){
|
|
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
|
|
+ }
|
|
+ }else if( pWin->regApp ){
|
|
+ }else{
|
|
+ if( bFinal ){
|
|
+ sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, windowArgCount(pWin));
|
|
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
+ sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp3(v, OP_AggValue, pWin->regAccum, windowArgCount(pWin),
|
|
+ pWin->regResult);
|
|
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function generates VM code to invoke the sub-routine at address
|
|
+** lblFlushPart once for each partition with the entire partition cached in
|
|
+** the Window.iEphCsr temp table.
|
|
+*/
|
|
+static void windowPartitionCache(
|
|
+ Parse *pParse,
|
|
+ Select *p, /* The rewritten SELECT statement */
|
|
+ WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */
|
|
+ int regFlushPart, /* Register to use with Gosub lblFlushPart */
|
|
+ int lblFlushPart, /* Subroutine to Gosub to */
|
|
+ int *pRegSize /* OUT: Register containing partition size */
|
|
+){
|
|
+ Window *pMWin = p->pWin;
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int iSubCsr = p->pSrc->a[0].iCursor;
|
|
+ int nSub = p->pSrc->a[0].pTab->nCol;
|
|
+ int k;
|
|
+
|
|
+ int reg = pParse->nMem+1;
|
|
+ int regRecord = reg+nSub;
|
|
+ int regRowid = regRecord+1;
|
|
+
|
|
+ *pRegSize = regRowid;
|
|
+ pParse->nMem += nSub + 2;
|
|
+
|
|
+ /* Load the column values for the row returned by the sub-select
|
|
+ ** into an array of registers starting at reg. */
|
|
+ for(k=0; k<nSub; k++){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
|
|
+ }
|
|
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
|
|
+
|
|
+ /* Check if this is the start of a new partition. If so, call the
|
|
+ ** flush_partition sub-routine. */
|
|
+ if( pMWin->pPartition ){
|
|
+ int addr;
|
|
+ ExprList *pPart = pMWin->pPartition;
|
|
+ int nPart = pPart->nExpr;
|
|
+ int regNewPart = reg + pMWin->nBufferCol;
|
|
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
|
|
+
|
|
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
|
|
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
|
+ sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
|
|
+ VdbeCoverageEqNe(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
|
|
+ sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
|
|
+ VdbeComment((v, "call flush_partition"));
|
|
+ }
|
|
+
|
|
+ /* Buffer the current row in the ephemeral table. */
|
|
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
|
|
+ sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
|
|
+
|
|
+ /* End of the input loop */
|
|
+ sqlite3WhereEnd(pWInfo);
|
|
+
|
|
+ /* Invoke "flush_partition" to deal with the final (or only) partition */
|
|
+ sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
|
|
+ VdbeComment((v, "call flush_partition"));
|
|
+}
|
|
+
|
|
+/*
|
|
+** Invoke the sub-routine at regGosub (generated by code in select.c) to
|
|
+** return the current row of Window.iEphCsr. If all window functions are
|
|
+** aggregate window functions that use the standard API, a single
|
|
+** OP_Gosub instruction is all that this routine generates. Extra VM code
|
|
+** for per-row processing is only generated for the following built-in window
|
|
+** functions:
|
|
+**
|
|
+** nth_value()
|
|
+** first_value()
|
|
+** lag()
|
|
+** lead()
|
|
+*/
|
|
+static void windowReturnOneRow(
|
|
+ Parse *pParse,
|
|
+ Window *pMWin,
|
|
+ int regGosub,
|
|
+ int addrGosub
|
|
+){
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ Window *pWin;
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ FuncDef *pFunc = pWin->pFunc;
|
|
+ if( pFunc->zName==nth_valueName
|
|
+ || pFunc->zName==first_valueName
|
|
+ ){
|
|
+ int csr = pWin->csrApp;
|
|
+ int lbl = sqlite3VdbeMakeLabel(v);
|
|
+ int tmpReg = sqlite3GetTempReg(pParse);
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
|
|
+
|
|
+ if( pFunc->zName==nth_valueName ){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+1,tmpReg);
|
|
+ windowCheckIntValue(pParse, tmpReg, 2);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg);
|
|
+ }
|
|
+ sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg);
|
|
+ sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg);
|
|
+ VdbeCoverageNeverNull(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg);
|
|
+ VdbeCoverageNeverTaken(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
|
|
+ sqlite3VdbeResolveLabel(v, lbl);
|
|
+ sqlite3ReleaseTempReg(pParse, tmpReg);
|
|
+ }
|
|
+ else if( pFunc->zName==leadName || pFunc->zName==lagName ){
|
|
+ int nArg = pWin->pOwner->x.pList->nExpr;
|
|
+ int iEph = pMWin->iEphCsr;
|
|
+ int csr = pWin->csrApp;
|
|
+ int lbl = sqlite3VdbeMakeLabel(v);
|
|
+ int tmpReg = sqlite3GetTempReg(pParse);
|
|
+
|
|
+ if( nArg<3 ){
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+2, pWin->regResult);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg);
|
|
+ if( nArg<2 ){
|
|
+ int val = (pFunc->zName==leadName ? 1 : -1);
|
|
+ sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val);
|
|
+ }else{
|
|
+ int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract);
|
|
+ int tmpReg2 = sqlite3GetTempReg(pParse);
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2);
|
|
+ sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg);
|
|
+ sqlite3ReleaseTempReg(pParse, tmpReg2);
|
|
+ }
|
|
+
|
|
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
|
|
+ sqlite3VdbeResolveLabel(v, lbl);
|
|
+ sqlite3ReleaseTempReg(pParse, tmpReg);
|
|
+ }
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Invoke the code generated by windowReturnOneRow() and, optionally, the
|
|
+** xInverse() function for each window function, for one or more rows
|
|
+** from the Window.iEphCsr temp table. This routine generates VM code
|
|
+** similar to:
|
|
+**
|
|
+** while( regCtr>0 ){
|
|
+** regCtr--;
|
|
+** windowReturnOneRow()
|
|
+** if( bInverse ){
|
|
+** AggInverse
|
|
+** }
|
|
+** Next (Window.iEphCsr)
|
|
+** }
|
|
+*/
|
|
+static void windowReturnRows(
|
|
+ Parse *pParse,
|
|
+ Window *pMWin, /* List of window functions */
|
|
+ int regCtr, /* Register containing number of rows */
|
|
+ int regGosub, /* Register for Gosub addrGosub */
|
|
+ int addrGosub, /* Address of sub-routine for ReturnOneRow */
|
|
+ int regInvArg, /* Array of registers for xInverse args */
|
|
+ int regInvSize /* Register containing size of partition */
|
|
+){
|
|
+ int addr;
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ windowAggFinal(pParse, pMWin, 0);
|
|
+ addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
|
+ windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
|
+ if( regInvArg ){
|
|
+ windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */
|
|
+}
|
|
+
|
|
+/*
|
|
+** Generate code to set the accumulator register for each window function
|
|
+** in the linked list passed as the second argument to NULL. And perform
|
|
+** any equivalent initialization required by any built-in window functions
|
|
+** in the list.
|
|
+*/
|
|
+static int windowInitAccum(Parse *pParse, Window *pMWin){
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int regArg;
|
|
+ int nArg = 0;
|
|
+ Window *pWin;
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ FuncDef *pFunc = pWin->pFunc;
|
|
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
|
|
+ nArg = MAX(nArg, windowArgCount(pWin));
|
|
+ if( pFunc->zName==nth_valueName
|
|
+ || pFunc->zName==first_valueName
|
|
+ ){
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
|
|
+ }
|
|
+
|
|
+ if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){
|
|
+ assert( pWin->eStart!=TK_UNBOUNDED );
|
|
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
|
|
+ }
|
|
+ }
|
|
+ regArg = pParse->nMem+1;
|
|
+ pParse->nMem += nArg;
|
|
+ return regArg;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** This function does the work of sqlite3WindowCodeStep() for all "ROWS"
|
|
+** window frame types except for "BETWEEN UNBOUNDED PRECEDING AND CURRENT
|
|
+** ROW". Pseudo-code for each follows.
|
|
+**
|
|
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
|
|
+**
|
|
+** ...
|
|
+** if( new partition ){
|
|
+** Gosub flush_partition
|
|
+** }
|
|
+** Insert (record in eph-table)
|
|
+** sqlite3WhereEnd()
|
|
+** Gosub flush_partition
|
|
+**
|
|
+** flush_partition:
|
|
+** Once {
|
|
+** OpenDup (iEphCsr -> csrStart)
|
|
+** OpenDup (iEphCsr -> csrEnd)
|
|
+** }
|
|
+** regStart = <expr1> // PRECEDING expression
|
|
+** regEnd = <expr2> // FOLLOWING expression
|
|
+** if( regStart<0 || regEnd<0 ){ error! }
|
|
+** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
|
|
+** Next(csrEnd) // if EOF skip Aggstep
|
|
+** Aggstep (csrEnd)
|
|
+** if( (regEnd--)<=0 ){
|
|
+** AggFinal (xValue)
|
|
+** Gosub addrGosub
|
|
+** Next(csr) // if EOF goto flush_partition_done
|
|
+** if( (regStart--)<=0 ){
|
|
+** AggInverse (csrStart)
|
|
+** Next(csrStart)
|
|
+** }
|
|
+** }
|
|
+** flush_partition_done:
|
|
+** ResetSorter (csr)
|
|
+** Return
|
|
+**
|
|
+** ROWS BETWEEN <expr> PRECEDING AND CURRENT ROW
|
|
+** ROWS BETWEEN CURRENT ROW AND <expr> FOLLOWING
|
|
+** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> FOLLOWING
|
|
+**
|
|
+** These are similar to the above. For "CURRENT ROW", intialize the
|
|
+** register to 0. For "UNBOUNDED PRECEDING" to infinity.
|
|
+**
|
|
+** ROWS BETWEEN <expr> PRECEDING AND UNBOUNDED FOLLOWING
|
|
+** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
|
+**
|
|
+** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
|
|
+** while( 1 ){
|
|
+** Next(csrEnd) // Exit while(1) at EOF
|
|
+** Aggstep (csrEnd)
|
|
+** }
|
|
+** while( 1 ){
|
|
+** AggFinal (xValue)
|
|
+** Gosub addrGosub
|
|
+** Next(csr) // if EOF goto flush_partition_done
|
|
+** if( (regStart--)<=0 ){
|
|
+** AggInverse (csrStart)
|
|
+** Next(csrStart)
|
|
+** }
|
|
+** }
|
|
+**
|
|
+** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if()
|
|
+** condition is always true (as if regStart were initialized to 0).
|
|
+**
|
|
+** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
|
+**
|
|
+** This is the only RANGE case handled by this routine. It modifies the
|
|
+** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to
|
|
+** be:
|
|
+**
|
|
+** while( 1 ){
|
|
+** AggFinal (xValue)
|
|
+** while( 1 ){
|
|
+** regPeer++
|
|
+** Gosub addrGosub
|
|
+** Next(csr) // if EOF goto flush_partition_done
|
|
+** if( new peer ) break;
|
|
+** }
|
|
+** while( (regPeer--)>0 ){
|
|
+** AggInverse (csrStart)
|
|
+** Next(csrStart)
|
|
+** }
|
|
+** }
|
|
+**
|
|
+** ROWS BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING
|
|
+**
|
|
+** regEnd = regEnd - regStart
|
|
+** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
|
|
+** Aggstep (csrEnd)
|
|
+** Next(csrEnd) // if EOF fall-through
|
|
+** if( (regEnd--)<=0 ){
|
|
+** if( (regStart--)<=0 ){
|
|
+** AggFinal (xValue)
|
|
+** Gosub addrGosub
|
|
+** Next(csr) // if EOF goto flush_partition_done
|
|
+** }
|
|
+** AggInverse (csrStart)
|
|
+** Next (csrStart)
|
|
+** }
|
|
+**
|
|
+** ROWS BETWEEN <expr> PRECEDING AND <expr> PRECEDING
|
|
+**
|
|
+** Replace the bit after "Rewind" in the above with:
|
|
+**
|
|
+** if( (regEnd--)<=0 ){
|
|
+** AggStep (csrEnd)
|
|
+** Next (csrEnd)
|
|
+** }
|
|
+** AggFinal (xValue)
|
|
+** Gosub addrGosub
|
|
+** Next(csr) // if EOF goto flush_partition_done
|
|
+** if( (regStart--)<=0 ){
|
|
+** AggInverse (csr2)
|
|
+** Next (csr2)
|
|
+** }
|
|
+**
|
|
+*/
|
|
+static void windowCodeRowExprStep(
|
|
+ Parse *pParse,
|
|
+ Select *p,
|
|
+ WhereInfo *pWInfo,
|
|
+ int regGosub,
|
|
+ int addrGosub
|
|
+){
|
|
+ Window *pMWin = p->pWin;
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int regFlushPart; /* Register for "Gosub flush_partition" */
|
|
+ int lblFlushPart; /* Label for "Gosub flush_partition" */
|
|
+ int lblFlushDone; /* Label for "Gosub flush_partition_done" */
|
|
+
|
|
+ int regArg;
|
|
+ int addr;
|
|
+ int csrStart = pParse->nTab++;
|
|
+ int csrEnd = pParse->nTab++;
|
|
+ int regStart; /* Value of <expr> PRECEDING */
|
|
+ int regEnd; /* Value of <expr> FOLLOWING */
|
|
+ int addrGoto;
|
|
+ int addrTop;
|
|
+ int addrIfPos1 = 0;
|
|
+ int addrIfPos2 = 0;
|
|
+ int regSize = 0;
|
|
+
|
|
+ assert( pMWin->eStart==TK_PRECEDING
|
|
+ || pMWin->eStart==TK_CURRENT
|
|
+ || pMWin->eStart==TK_FOLLOWING
|
|
+ || pMWin->eStart==TK_UNBOUNDED
|
|
+ );
|
|
+ assert( pMWin->eEnd==TK_FOLLOWING
|
|
+ || pMWin->eEnd==TK_CURRENT
|
|
+ || pMWin->eEnd==TK_UNBOUNDED
|
|
+ || pMWin->eEnd==TK_PRECEDING
|
|
+ );
|
|
+
|
|
+ /* Allocate register and label for the "flush_partition" sub-routine. */
|
|
+ regFlushPart = ++pParse->nMem;
|
|
+ lblFlushPart = sqlite3VdbeMakeLabel(v);
|
|
+ lblFlushDone = sqlite3VdbeMakeLabel(v);
|
|
+
|
|
+ regStart = ++pParse->nMem;
|
|
+ regEnd = ++pParse->nMem;
|
|
+
|
|
+ windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size);
|
|
+
|
|
+ addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
+
|
|
+ /* Start of "flush_partition" */
|
|
+ sqlite3VdbeResolveLabel(v, lblFlushPart);
|
|
+ sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3);
|
|
+ VdbeCoverage(v);
|
|
+ VdbeComment((v, "Flush_partition subroutine"));
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr);
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr);
|
|
+
|
|
+ /* If either regStart or regEnd are not non-negative integers, throw
|
|
+ ** an exception. */
|
|
+ if( pMWin->pStart ){
|
|
+ sqlite3ExprCode(pParse, pMWin->pStart, regStart);
|
|
+ windowCheckIntValue(pParse, regStart, 0);
|
|
+ }
|
|
+ if( pMWin->pEnd ){
|
|
+ sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
|
|
+ windowCheckIntValue(pParse, regEnd, 1);
|
|
+ }
|
|
+
|
|
+ /* If this is "ROWS <expr1> FOLLOWING AND ROWS <expr2> FOLLOWING", do:
|
|
+ **
|
|
+ ** if( regEnd<regStart ){
|
|
+ ** // The frame always consists of 0 rows
|
|
+ ** regStart = regSize;
|
|
+ ** }
|
|
+ ** regEnd = regEnd - regStart;
|
|
+ */
|
|
+ if( pMWin->pEnd && pMWin->eStart==TK_FOLLOWING ){
|
|
+ assert( pMWin->pStart!=0 );
|
|
+ assert( pMWin->eEnd==TK_FOLLOWING );
|
|
+ sqlite3VdbeAddOp3(v, OP_Ge, regStart, sqlite3VdbeCurrentAddr(v)+2, regEnd);
|
|
+ VdbeCoverageNeverNull(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart);
|
|
+ sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd);
|
|
+ }
|
|
+
|
|
+ if( pMWin->pStart && pMWin->eEnd==TK_PRECEDING ){
|
|
+ assert( pMWin->pEnd!=0 );
|
|
+ assert( pMWin->eStart==TK_PRECEDING );
|
|
+ sqlite3VdbeAddOp3(v, OP_Le, regStart, sqlite3VdbeCurrentAddr(v)+3, regEnd);
|
|
+ VdbeCoverageNeverNull(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart);
|
|
+ sqlite3VdbeAddOp2(v, OP_Copy, regSize, regEnd);
|
|
+ }
|
|
+
|
|
+ /* Initialize the accumulator register for each window function to NULL */
|
|
+ regArg = windowInitAccum(pParse, pMWin);
|
|
+
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone);
|
|
+ VdbeCoverageNeverTaken(v);
|
|
+ sqlite3VdbeChangeP5(v, 1);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone);
|
|
+ VdbeCoverageNeverTaken(v);
|
|
+ sqlite3VdbeChangeP5(v, 1);
|
|
+
|
|
+ /* Invoke AggStep function for each window function using the row that
|
|
+ ** csrEnd currently points to. Or, if csrEnd is already at EOF,
|
|
+ ** do nothing. */
|
|
+ addrTop = sqlite3VdbeCurrentAddr(v);
|
|
+ if( pMWin->eEnd==TK_PRECEDING ){
|
|
+ addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2);
|
|
+ VdbeCoverage(v);
|
|
+ addr = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
+ windowAggStep(pParse, pMWin, csrEnd, 0, regArg, regSize);
|
|
+ if( pMWin->eEnd==TK_UNBOUNDED ){
|
|
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
|
+ sqlite3VdbeJumpHere(v, addr);
|
|
+ addrTop = sqlite3VdbeCurrentAddr(v);
|
|
+ }else{
|
|
+ sqlite3VdbeJumpHere(v, addr);
|
|
+ if( pMWin->eEnd==TK_PRECEDING ){
|
|
+ sqlite3VdbeJumpHere(v, addrIfPos1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( pMWin->eEnd==TK_FOLLOWING ){
|
|
+ addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
+ if( pMWin->eStart==TK_FOLLOWING ){
|
|
+ addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
+ windowAggFinal(pParse, pMWin, 0);
|
|
+ windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone);
|
|
+ if( pMWin->eStart==TK_FOLLOWING ){
|
|
+ sqlite3VdbeJumpHere(v, addrIfPos2);
|
|
+ }
|
|
+
|
|
+ if( pMWin->eStart==TK_CURRENT
|
|
+ || pMWin->eStart==TK_PRECEDING
|
|
+ || pMWin->eStart==TK_FOLLOWING
|
|
+ ){
|
|
+ int lblSkipInverse = sqlite3VdbeMakeLabel(v);;
|
|
+ if( pMWin->eStart==TK_PRECEDING ){
|
|
+ sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1);
|
|
+ VdbeCoverage(v);
|
|
+ }
|
|
+ if( pMWin->eStart==TK_FOLLOWING ){
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblSkipInverse);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
|
|
+ VdbeCoverageAlwaysTaken(v);
|
|
+ }
|
|
+ windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize);
|
|
+ sqlite3VdbeResolveLabel(v, lblSkipInverse);
|
|
+ }
|
|
+ if( pMWin->eEnd==TK_FOLLOWING ){
|
|
+ sqlite3VdbeJumpHere(v, addrIfPos1);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
|
+
|
|
+ /* flush_partition_done: */
|
|
+ sqlite3VdbeResolveLabel(v, lblFlushDone);
|
|
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
|
+ sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
|
|
+ VdbeComment((v, "end flush_partition subroutine"));
|
|
+
|
|
+ /* Jump to here to skip over flush_partition */
|
|
+ sqlite3VdbeJumpHere(v, addrGoto);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function does the work of sqlite3WindowCodeStep() for cases that
|
|
+** would normally be handled by windowCodeDefaultStep() when there are
|
|
+** one or more built-in window-functions that require the entire partition
|
|
+** to be cached in a temp table before any rows can be returned. Additionally.
|
|
+** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by
|
|
+** this function.
|
|
+**
|
|
+** Pseudo-code corresponding to the VM code generated by this function
|
|
+** for each type of window follows.
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+**
|
|
+** flush_partition:
|
|
+** Once {
|
|
+** OpenDup (iEphCsr -> csrLead)
|
|
+** }
|
|
+** Integer ctr 0
|
|
+** foreach row (csrLead){
|
|
+** if( new peer ){
|
|
+** AggFinal (xValue)
|
|
+** for(i=0; i<ctr; i++){
|
|
+** Gosub addrGosub
|
|
+** Next iEphCsr
|
|
+** }
|
|
+** Integer ctr 0
|
|
+** }
|
|
+** AggStep (csrLead)
|
|
+** Incr ctr
|
|
+** }
|
|
+**
|
|
+** AggFinal (xFinalize)
|
|
+** for(i=0; i<ctr; i++){
|
|
+** Gosub addrGosub
|
|
+** Next iEphCsr
|
|
+** }
|
|
+**
|
|
+** ResetSorter (csr)
|
|
+** Return
|
|
+**
|
|
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+**
|
|
+** As above, except that the "if( new peer )" branch is always taken.
|
|
+**
|
|
+** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
|
+**
|
|
+** As above, except that each of the for() loops becomes:
|
|
+**
|
|
+** for(i=0; i<ctr; i++){
|
|
+** Gosub addrGosub
|
|
+** AggInverse (iEphCsr)
|
|
+** Next iEphCsr
|
|
+** }
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
|
+**
|
|
+** flush_partition:
|
|
+** Once {
|
|
+** OpenDup (iEphCsr -> csrLead)
|
|
+** }
|
|
+** foreach row (csrLead) {
|
|
+** AggStep (csrLead)
|
|
+** }
|
|
+** foreach row (iEphCsr) {
|
|
+** Gosub addrGosub
|
|
+** }
|
|
+**
|
|
+** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
|
+**
|
|
+** flush_partition:
|
|
+** Once {
|
|
+** OpenDup (iEphCsr -> csrLead)
|
|
+** }
|
|
+** foreach row (csrLead){
|
|
+** AggStep (csrLead)
|
|
+** }
|
|
+** Rewind (csrLead)
|
|
+** Integer ctr 0
|
|
+** foreach row (csrLead){
|
|
+** if( new peer ){
|
|
+** AggFinal (xValue)
|
|
+** for(i=0; i<ctr; i++){
|
|
+** Gosub addrGosub
|
|
+** AggInverse (iEphCsr)
|
|
+** Next iEphCsr
|
|
+** }
|
|
+** Integer ctr 0
|
|
+** }
|
|
+** Incr ctr
|
|
+** }
|
|
+**
|
|
+** AggFinal (xFinalize)
|
|
+** for(i=0; i<ctr; i++){
|
|
+** Gosub addrGosub
|
|
+** Next iEphCsr
|
|
+** }
|
|
+**
|
|
+** ResetSorter (csr)
|
|
+** Return
|
|
+*/
|
|
+static void windowCodeCacheStep(
|
|
+ Parse *pParse,
|
|
+ Select *p,
|
|
+ WhereInfo *pWInfo,
|
|
+ int regGosub,
|
|
+ int addrGosub
|
|
+){
|
|
+ Window *pMWin = p->pWin;
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int k;
|
|
+ int addr;
|
|
+ ExprList *pPart = pMWin->pPartition;
|
|
+ ExprList *pOrderBy = pMWin->pOrderBy;
|
|
+ int nPeer = pOrderBy ? pOrderBy->nExpr : 0;
|
|
+ int regNewPeer;
|
|
+
|
|
+ int addrGoto; /* Address of Goto used to jump flush_par.. */
|
|
+ int addrNext; /* Jump here for next iteration of loop */
|
|
+ int regFlushPart;
|
|
+ int lblFlushPart;
|
|
+ int csrLead;
|
|
+ int regCtr;
|
|
+ int regArg; /* Register array to martial function args */
|
|
+ int regSize;
|
|
+ int lblEmpty;
|
|
+ int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT
|
|
+ && pMWin->eEnd==TK_UNBOUNDED;
|
|
+
|
|
+ assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
|
+ || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
|
|
+ || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
|
|
+ || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED)
|
|
+ );
|
|
+
|
|
+ lblEmpty = sqlite3VdbeMakeLabel(v);
|
|
+ regNewPeer = pParse->nMem+1;
|
|
+ pParse->nMem += nPeer;
|
|
+
|
|
+ /* Allocate register and label for the "flush_partition" sub-routine. */
|
|
+ regFlushPart = ++pParse->nMem;
|
|
+ lblFlushPart = sqlite3VdbeMakeLabel(v);
|
|
+
|
|
+ csrLead = pParse->nTab++;
|
|
+ regCtr = ++pParse->nMem;
|
|
+
|
|
+ windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size);
|
|
+ addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
+
|
|
+ /* Start of "flush_partition" */
|
|
+ sqlite3VdbeResolveLabel(v, lblFlushPart);
|
|
+ sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr);
|
|
+
|
|
+ /* Initialize the accumulator register for each window function to NULL */
|
|
+ regArg = windowInitAccum(pParse, pMWin);
|
|
+
|
|
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty);
|
|
+ VdbeCoverageNeverTaken(v);
|
|
+
|
|
+ if( bReverse ){
|
|
+ int addr2 = sqlite3VdbeCurrentAddr(v);
|
|
+ windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
|
|
+ VdbeCoverageNeverTaken(v);
|
|
+ }
|
|
+ addrNext = sqlite3VdbeCurrentAddr(v);
|
|
+
|
|
+ if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){
|
|
+ int bCurrent = (pMWin->eStart==TK_CURRENT);
|
|
+ int addrJump = 0; /* Address of OP_Jump below */
|
|
+ if( pMWin->eType==TK_RANGE ){
|
|
+ int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
|
|
+ int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
|
|
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
|
+ for(k=0; k<nPeer; k++){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
|
|
+ }
|
|
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
|
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
|
+ addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
|
|
+ }
|
|
+
|
|
+ windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub,
|
|
+ (bCurrent ? regArg : 0), (bCurrent ? regSize : 0)
|
|
+ );
|
|
+ if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
|
+ }
|
|
+
|
|
+ if( bReverse==0 ){
|
|
+ windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext);
|
|
+ VdbeCoverage(v);
|
|
+
|
|
+ windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0);
|
|
+
|
|
+ sqlite3VdbeResolveLabel(v, lblEmpty);
|
|
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
|
+ sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
|
|
+
|
|
+ /* Jump to here to skip over flush_partition */
|
|
+ sqlite3VdbeJumpHere(v, addrGoto);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+**
|
|
+** ...
|
|
+** if( new partition ){
|
|
+** AggFinal (xFinalize)
|
|
+** Gosub addrGosub
|
|
+** ResetSorter eph-table
|
|
+** }
|
|
+** else if( new peer ){
|
|
+** AggFinal (xValue)
|
|
+** Gosub addrGosub
|
|
+** ResetSorter eph-table
|
|
+** }
|
|
+** AggStep
|
|
+** Insert (record into eph-table)
|
|
+** sqlite3WhereEnd()
|
|
+** AggFinal (xFinalize)
|
|
+** Gosub addrGosub
|
|
+**
|
|
+** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
|
+**
|
|
+** As above, except take no action for a "new peer". Invoke
|
|
+** the sub-routine once only for each partition.
|
|
+**
|
|
+** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
|
+**
|
|
+** As above, except that the "new peer" condition is handled in the
|
|
+** same way as "new partition" (so there is no "else if" block).
|
|
+**
|
|
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+**
|
|
+** As above, except assume every row is a "new peer".
|
|
+*/
|
|
+static void windowCodeDefaultStep(
|
|
+ Parse *pParse,
|
|
+ Select *p,
|
|
+ WhereInfo *pWInfo,
|
|
+ int regGosub,
|
|
+ int addrGosub
|
|
+){
|
|
+ Window *pMWin = p->pWin;
|
|
+ Vdbe *v = sqlite3GetVdbe(pParse);
|
|
+ int k;
|
|
+ int iSubCsr = p->pSrc->a[0].iCursor;
|
|
+ int nSub = p->pSrc->a[0].pTab->nCol;
|
|
+ int reg = pParse->nMem+1;
|
|
+ int regRecord = reg+nSub;
|
|
+ int regRowid = regRecord+1;
|
|
+ int addr;
|
|
+ ExprList *pPart = pMWin->pPartition;
|
|
+ ExprList *pOrderBy = pMWin->pOrderBy;
|
|
+
|
|
+ assert( pMWin->eType==TK_RANGE
|
|
+ || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
|
+ );
|
|
+
|
|
+ assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
|
+ || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
|
|
+ || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
|
|
+ || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy)
|
|
+ );
|
|
+
|
|
+ if( pMWin->eEnd==TK_UNBOUNDED ){
|
|
+ pOrderBy = 0;
|
|
+ }
|
|
+
|
|
+ pParse->nMem += nSub + 2;
|
|
+
|
|
+ /* Load the individual column values of the row returned by
|
|
+ ** the sub-select into an array of registers. */
|
|
+ for(k=0; k<nSub; k++){
|
|
+ sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
|
|
+ }
|
|
+
|
|
+ /* Check if this is the start of a new partition or peer group. */
|
|
+ if( pPart || pOrderBy ){
|
|
+ int nPart = (pPart ? pPart->nExpr : 0);
|
|
+ int addrGoto = 0;
|
|
+ int addrJump = 0;
|
|
+ int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
|
|
+
|
|
+ if( pPart ){
|
|
+ int regNewPart = reg + pMWin->nBufferCol;
|
|
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
|
|
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
|
|
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
|
+ addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
|
+ VdbeCoverageEqNe(v);
|
|
+ windowAggFinal(pParse, pMWin, 1);
|
|
+ if( pOrderBy ){
|
|
+ addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( pOrderBy ){
|
|
+ int regNewPeer = reg + pMWin->nBufferCol + nPart;
|
|
+ int regPeer = pMWin->regPart + nPart;
|
|
+
|
|
+ if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
|
+ if( pMWin->eType==TK_RANGE ){
|
|
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
|
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
|
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
|
+ addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
|
+ VdbeCoverage(v);
|
|
+ }else{
|
|
+ addrJump = 0;
|
|
+ }
|
|
+ windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT);
|
|
+ if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
|
+ }
|
|
+
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
|
|
+ VdbeCoverage(v);
|
|
+
|
|
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
|
+ sqlite3VdbeAddOp3(
|
|
+ v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
|
|
+ );
|
|
+
|
|
+ if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
|
+ }
|
|
+
|
|
+ /* Invoke step function for window functions */
|
|
+ windowAggStep(pParse, pMWin, -1, 0, reg, 0);
|
|
+
|
|
+ /* Buffer the current row in the ephemeral table. */
|
|
+ if( pMWin->nBufferCol>0 ){
|
|
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
|
|
+ }else{
|
|
+ sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
|
|
+ sqlite3VdbeAppendP4(v, (void*)"", 0);
|
|
+ }
|
|
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
|
|
+ sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
|
|
+
|
|
+ /* End the database scan loop. */
|
|
+ sqlite3WhereEnd(pWInfo);
|
|
+
|
|
+ windowAggFinal(pParse, pMWin, 1);
|
|
+ sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
|
|
+ VdbeCoverage(v);
|
|
+ sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
|
+ sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
|
|
+ VdbeCoverage(v);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Allocate and return a duplicate of the Window object indicated by the
|
|
+** third argument. Set the Window.pOwner field of the new object to
|
|
+** pOwner.
|
|
+*/
|
|
+SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
|
|
+ Window *pNew = 0;
|
|
+ if( ALWAYS(p) ){
|
|
+ pNew = sqlite3DbMallocZero(db, sizeof(Window));
|
|
+ if( pNew ){
|
|
+ pNew->zName = sqlite3DbStrDup(db, p->zName);
|
|
+ pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
|
|
+ pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
|
|
+ pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0);
|
|
+ pNew->eType = p->eType;
|
|
+ pNew->eEnd = p->eEnd;
|
|
+ pNew->eStart = p->eStart;
|
|
+ pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
|
|
+ pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
|
|
+ pNew->pOwner = pOwner;
|
|
+ }
|
|
+ }
|
|
+ return pNew;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a copy of the linked list of Window objects passed as the
|
|
+** second argument.
|
|
+*/
|
|
+SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
|
|
+ Window *pWin;
|
|
+ Window *pRet = 0;
|
|
+ Window **pp = &pRet;
|
|
+
|
|
+ for(pWin=p; pWin; pWin=pWin->pNextWin){
|
|
+ *pp = sqlite3WindowDup(db, 0, pWin);
|
|
+ if( *pp==0 ) break;
|
|
+ pp = &((*pp)->pNextWin);
|
|
+ }
|
|
+
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** sqlite3WhereBegin() has already been called for the SELECT statement
|
|
+** passed as the second argument when this function is invoked. It generates
|
|
+** code to populate the Window.regResult register for each window function and
|
|
+** invoke the sub-routine at instruction addrGosub once for each row.
|
|
+** This function calls sqlite3WhereEnd() before returning.
|
|
+*/
|
|
+SQLITE_PRIVATE void sqlite3WindowCodeStep(
|
|
+ Parse *pParse, /* Parse context */
|
|
+ Select *p, /* Rewritten SELECT statement */
|
|
+ WhereInfo *pWInfo, /* Context returned by sqlite3WhereBegin() */
|
|
+ int regGosub, /* Register for OP_Gosub */
|
|
+ int addrGosub /* OP_Gosub here to return each row */
|
|
+){
|
|
+ Window *pMWin = p->pWin;
|
|
+
|
|
+ /* There are three different functions that may be used to do the work
|
|
+ ** of this one, depending on the window frame and the specific built-in
|
|
+ ** window functions used (if any).
|
|
+ **
|
|
+ ** windowCodeRowExprStep() handles all "ROWS" window frames, except for:
|
|
+ **
|
|
+ ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+ **
|
|
+ ** The exception is because windowCodeRowExprStep() implements all window
|
|
+ ** frame types by caching the entire partition in a temp table, and
|
|
+ ** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to
|
|
+ ** implement without such a cache.
|
|
+ **
|
|
+ ** windowCodeCacheStep() is used for:
|
|
+ **
|
|
+ ** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
|
+ **
|
|
+ ** It is also used for anything not handled by windowCodeRowExprStep()
|
|
+ ** that invokes a built-in window function that requires the entire
|
|
+ ** partition to be cached in a temp table before any rows are returned
|
|
+ ** (e.g. nth_value() or percent_rank()).
|
|
+ **
|
|
+ ** Finally, assuming there is no built-in window function that requires
|
|
+ ** the partition to be cached, windowCodeDefaultStep() is used for:
|
|
+ **
|
|
+ ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+ ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
|
+ ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
|
+ ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
+ **
|
|
+ ** windowCodeDefaultStep() is the only one of the three functions that
|
|
+ ** does not cache each partition in a temp table before beginning to
|
|
+ ** return rows.
|
|
+ */
|
|
+ if( pMWin->eType==TK_ROWS
|
|
+ && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy)
|
|
+ ){
|
|
+ VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()"));
|
|
+ windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
|
|
+ }else{
|
|
+ Window *pWin;
|
|
+ int bCache = 0; /* True to use CacheStep() */
|
|
+
|
|
+ if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){
|
|
+ bCache = 1;
|
|
+ }else{
|
|
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
+ FuncDef *pFunc = pWin->pFunc;
|
|
+ if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
|
|
+ || (pFunc->zName==nth_valueName)
|
|
+ || (pFunc->zName==first_valueName)
|
|
+ || (pFunc->zName==leadName)
|
|
+ || (pFunc->zName==lagName)
|
|
+ ){
|
|
+ bCache = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Otherwise, call windowCodeDefaultStep(). */
|
|
+ if( bCache ){
|
|
+ VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()"));
|
|
+ windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
|
|
+ }else{
|
|
+ VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()"));
|
|
+ windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
+/************** End of window.c **********************************************/
|
|
/************** Begin file parse.c *******************************************/
|
|
/*
|
|
** 2000-05-29
|
|
@@ -136559,6 +146829,7 @@
|
|
** input grammar file:
|
|
*/
|
|
/* #include <stdio.h> */
|
|
+/* #include <assert.h> */
|
|
/************ Begin %include sections from the grammar ************************/
|
|
|
|
/* #include "sqliteInt.h" */
|
|
@@ -136600,15 +146871,6 @@
|
|
#define YYMALLOCARGTYPE u64
|
|
|
|
/*
|
|
-** An instance of this structure holds information about the
|
|
-** LIMIT clause of a SELECT statement.
|
|
-*/
|
|
-struct LimitVal {
|
|
- Expr *pLimit; /* The LIMIT expression. NULL if there is no limit */
|
|
- Expr *pOffset; /* The OFFSET expression. NULL if there is none */
|
|
-};
|
|
-
|
|
-/*
|
|
** An instance of the following structure describes the event of a
|
|
** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
|
|
** TK_DELETE, or TK_INSTEAD. If the event is of the form
|
|
@@ -136619,6 +146881,8 @@
|
|
*/
|
|
struct TrigEvent { int a; IdList * b; };
|
|
|
|
+struct FrameBound { int eType; Expr *pExpr; };
|
|
+
|
|
/*
|
|
** Disable lookaside memory allocation for objects that might be
|
|
** shared across database connections.
|
|
@@ -136651,26 +146915,26 @@
|
|
}
|
|
}
|
|
|
|
- /* This is a utility routine used to set the ExprSpan.zStart and
|
|
- ** ExprSpan.zEnd values of pOut so that the span covers the complete
|
|
- ** range of text beginning with pStart and going to the end of pEnd.
|
|
- */
|
|
- static void spanSet(ExprSpan *pOut, Token *pStart, Token *pEnd){
|
|
- pOut->zStart = pStart->z;
|
|
- pOut->zEnd = &pEnd->z[pEnd->n];
|
|
- }
|
|
|
|
/* Construct a new Expr object from a single identifier. Use the
|
|
** new Expr to populate pOut. Set the span of pOut to be the identifier
|
|
** that created the expression.
|
|
*/
|
|
- static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){
|
|
+ static Expr *tokenExpr(Parse *pParse, int op, Token t){
|
|
Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1);
|
|
if( p ){
|
|
- memset(p, 0, sizeof(Expr));
|
|
+ /* memset(p, 0, sizeof(Expr)); */
|
|
p->op = (u8)op;
|
|
+ p->affinity = 0;
|
|
p->flags = EP_Leaf;
|
|
p->iAgg = -1;
|
|
+ p->pLeft = p->pRight = 0;
|
|
+ p->x.pList = 0;
|
|
+ p->pAggInfo = 0;
|
|
+ p->y.pTab = 0;
|
|
+ p->op2 = 0;
|
|
+ p->iTable = 0;
|
|
+ p->iColumn = 0;
|
|
p->u.zToken = (char*)&p[1];
|
|
memcpy(p->u.zToken, t.z, t.n);
|
|
p->u.zToken[t.n] = 0;
|
|
@@ -136681,51 +146945,19 @@
|
|
#if SQLITE_MAX_EXPR_DEPTH>0
|
|
p->nHeight = 1;
|
|
#endif
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ return (Expr*)sqlite3RenameTokenMap(pParse, (void*)p, &t);
|
|
+ }
|
|
}
|
|
- pOut->pExpr = p;
|
|
- pOut->zStart = t.z;
|
|
- pOut->zEnd = &t.z[t.n];
|
|
+ return p;
|
|
}
|
|
|
|
- /* This routine constructs a binary expression node out of two ExprSpan
|
|
- ** objects and uses the result to populate a new ExprSpan object.
|
|
- */
|
|
- static void spanBinaryExpr(
|
|
- Parse *pParse, /* The parsing context. Errors accumulate here */
|
|
- int op, /* The binary operation */
|
|
- ExprSpan *pLeft, /* The left operand, and output */
|
|
- ExprSpan *pRight /* The right operand */
|
|
- ){
|
|
- pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr);
|
|
- pLeft->zEnd = pRight->zEnd;
|
|
- }
|
|
|
|
- /* If doNot is true, then add a TK_NOT Expr-node wrapper around the
|
|
- ** outside of *ppExpr.
|
|
- */
|
|
- static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){
|
|
- if( doNot ){
|
|
- pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0);
|
|
- }
|
|
- }
|
|
-
|
|
- /* Construct an expression node for a unary postfix operator
|
|
- */
|
|
- static void spanUnaryPostfix(
|
|
- Parse *pParse, /* Parsing context to record errors */
|
|
- int op, /* The operator */
|
|
- ExprSpan *pOperand, /* The operand, and output */
|
|
- Token *pPostOp /* The operand token for setting the span */
|
|
- ){
|
|
- pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0);
|
|
- pOperand->zEnd = &pPostOp->z[pPostOp->n];
|
|
- }
|
|
-
|
|
/* A routine to convert a binary TK_IS or TK_ISNOT expression into a
|
|
** unary TK_ISNULL or TK_NOTNULL expression. */
|
|
static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){
|
|
sqlite3 *db = pParse->db;
|
|
- if( pA && pY && pY->op==TK_NULL ){
|
|
+ if( pA && pY && pY->op==TK_NULL && !IN_RENAME_OBJECT ){
|
|
pA->op = (u8)op;
|
|
sqlite3ExprDelete(db, pA->pRight);
|
|
pA->pRight = 0;
|
|
@@ -136732,20 +146964,6 @@
|
|
}
|
|
}
|
|
|
|
- /* Construct an expression node for a unary prefix operator
|
|
- */
|
|
- static void spanUnaryPrefix(
|
|
- ExprSpan *pOut, /* Write the new expression node here */
|
|
- Parse *pParse, /* Parsing context to record errors */
|
|
- int op, /* The operator */
|
|
- ExprSpan *pOperand, /* The operand */
|
|
- Token *pPreOp /* The operand token for setting the span */
|
|
- ){
|
|
- pOut->zStart = pPreOp->z;
|
|
- pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0);
|
|
- pOut->zEnd = pOperand->zEnd;
|
|
- }
|
|
-
|
|
/* Add a single new term to an ExprList that is used to store a
|
|
** list of identifiers. Report an error if the ID list contains
|
|
** a COLLATE clause or an ASC or DESC keyword, except ignore the
|
|
@@ -136808,64 +147026,78 @@
|
|
** zero the stack is dynamically sized using realloc()
|
|
** sqlite3ParserARG_SDECL A static variable declaration for the %extra_argument
|
|
** sqlite3ParserARG_PDECL A parameter declaration for the %extra_argument
|
|
+** sqlite3ParserARG_PARAM Code to pass %extra_argument as a subroutine parameter
|
|
** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser
|
|
** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser
|
|
+** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context
|
|
** YYERRORSYMBOL is the code number of the error symbol. If not
|
|
** defined, then do no error processing.
|
|
** YYNSTATE the combined number of states.
|
|
** YYNRULE the number of rules in the grammar
|
|
+** YYNTOKEN Number of terminal symbols
|
|
** YY_MAX_SHIFT Maximum value for shift actions
|
|
** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
|
|
** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
|
|
-** YY_MIN_REDUCE Maximum value for reduce actions
|
|
** YY_ERROR_ACTION The yy_action[] code for syntax error
|
|
** YY_ACCEPT_ACTION The yy_action[] code for accept
|
|
** YY_NO_ACTION The yy_action[] code for no-op
|
|
+** YY_MIN_REDUCE Minimum value for reduce actions
|
|
+** YY_MAX_REDUCE Maximum value for reduce actions
|
|
*/
|
|
#ifndef INTERFACE
|
|
# define INTERFACE 1
|
|
#endif
|
|
/************* Begin control #defines *****************************************/
|
|
-#define YYCODETYPE unsigned char
|
|
-#define YYNOCODE 252
|
|
+#define YYCODETYPE unsigned short int
|
|
+#define YYNOCODE 277
|
|
#define YYACTIONTYPE unsigned short int
|
|
-#define YYWILDCARD 69
|
|
+#define YYWILDCARD 91
|
|
#define sqlite3ParserTOKENTYPE Token
|
|
typedef union {
|
|
int yyinit;
|
|
sqlite3ParserTOKENTYPE yy0;
|
|
- Expr* yy72;
|
|
- TriggerStep* yy145;
|
|
- ExprList* yy148;
|
|
- SrcList* yy185;
|
|
- ExprSpan yy190;
|
|
- int yy194;
|
|
- Select* yy243;
|
|
- IdList* yy254;
|
|
- With* yy285;
|
|
- struct TrigEvent yy332;
|
|
- struct LimitVal yy354;
|
|
- struct {int value; int mask;} yy497;
|
|
+ Expr* yy18;
|
|
+ struct TrigEvent yy34;
|
|
+ IdList* yy48;
|
|
+ int yy70;
|
|
+ struct {int value; int mask;} yy111;
|
|
+ struct FrameBound yy119;
|
|
+ SrcList* yy135;
|
|
+ TriggerStep* yy207;
|
|
+ Window* yy327;
|
|
+ Upsert* yy340;
|
|
+ const char* yy392;
|
|
+ ExprList* yy420;
|
|
+ With* yy449;
|
|
+ Select* yy489;
|
|
} YYMINORTYPE;
|
|
#ifndef YYSTACKDEPTH
|
|
#define YYSTACKDEPTH 100
|
|
#endif
|
|
-#define sqlite3ParserARG_SDECL Parse *pParse;
|
|
-#define sqlite3ParserARG_PDECL ,Parse *pParse
|
|
-#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
|
|
-#define sqlite3ParserARG_STORE yypParser->pParse = pParse
|
|
+#define sqlite3ParserARG_SDECL
|
|
+#define sqlite3ParserARG_PDECL
|
|
+#define sqlite3ParserARG_PARAM
|
|
+#define sqlite3ParserARG_FETCH
|
|
+#define sqlite3ParserARG_STORE
|
|
+#define sqlite3ParserCTX_SDECL Parse *pParse;
|
|
+#define sqlite3ParserCTX_PDECL ,Parse *pParse
|
|
+#define sqlite3ParserCTX_PARAM ,pParse
|
|
+#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
|
|
+#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
|
|
#define YYFALLBACK 1
|
|
-#define YYNSTATE 455
|
|
-#define YYNRULE 329
|
|
-#define YY_MAX_SHIFT 454
|
|
-#define YY_MIN_SHIFTREDUCE 664
|
|
-#define YY_MAX_SHIFTREDUCE 992
|
|
-#define YY_MIN_REDUCE 993
|
|
-#define YY_MAX_REDUCE 1321
|
|
-#define YY_ERROR_ACTION 1322
|
|
-#define YY_ACCEPT_ACTION 1323
|
|
-#define YY_NO_ACTION 1324
|
|
+#define YYNSTATE 521
|
|
+#define YYNRULE 367
|
|
+#define YYNTOKEN 155
|
|
+#define YY_MAX_SHIFT 520
|
|
+#define YY_MIN_SHIFTREDUCE 756
|
|
+#define YY_MAX_SHIFTREDUCE 1122
|
|
+#define YY_ERROR_ACTION 1123
|
|
+#define YY_ACCEPT_ACTION 1124
|
|
+#define YY_NO_ACTION 1125
|
|
+#define YY_MIN_REDUCE 1126
|
|
+#define YY_MAX_REDUCE 1492
|
|
/************* End control #defines *******************************************/
|
|
+#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
|
|
|
|
/* Define the yytestcase() macro to be a no-op if is not already defined
|
|
** otherwise.
|
|
@@ -136894,9 +147126,6 @@
|
|
** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
|
|
** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
|
|
**
|
|
-** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
|
-** and YY_MAX_REDUCE
|
|
-**
|
|
** N == YY_ERROR_ACTION A syntax error has occurred.
|
|
**
|
|
** N == YY_ACCEPT_ACTION The parser accepts its input.
|
|
@@ -136904,6 +147133,9 @@
|
|
** N == YY_NO_ACTION No such action. Denotes unused
|
|
** slots in the yy_action[] table.
|
|
**
|
|
+** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
|
+** and YY_MAX_REDUCE
|
|
+**
|
|
** The action table is constructed as a single large table named yy_action[].
|
|
** Given state S and lookahead X, the action is computed as either:
|
|
**
|
|
@@ -136910,19 +147142,13 @@
|
|
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
|
|
** (B) N = yy_default[S]
|
|
**
|
|
-** The (A) formula is preferred. The B formula is used instead if:
|
|
-** (1) The yy_shift_ofst[S]+X value is out of range, or
|
|
-** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
|
|
-** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
|
|
-** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
|
|
-** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
|
-** Hence only tests (1) and (2) need to be evaluated.)
|
|
+** The (A) formula is preferred. The B formula is used instead if
|
|
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
|
|
**
|
|
** The formulas above are for computing the action when the lookahead is
|
|
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
|
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
|
-** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
|
-** YY_SHIFT_USE_DFLT.
|
|
+** the yy_shift_ofst[] array.
|
|
**
|
|
** The following are the tables generated in this section:
|
|
**
|
|
@@ -136936,463 +147162,568 @@
|
|
** yy_default[] Default action for each state.
|
|
**
|
|
*********** Begin parsing tables **********************************************/
|
|
-#define YY_ACTTAB_COUNT (1565)
|
|
+#define YY_ACTTAB_COUNT (2009)
|
|
static const YYACTIONTYPE yy_action[] = {
|
|
- /* 0 */ 324, 410, 342, 747, 747, 203, 939, 353, 969, 98,
|
|
- /* 10 */ 98, 98, 98, 91, 96, 96, 96, 96, 95, 95,
|
|
- /* 20 */ 94, 94, 94, 93, 350, 1323, 155, 155, 2, 808,
|
|
- /* 30 */ 971, 971, 98, 98, 98, 98, 20, 96, 96, 96,
|
|
- /* 40 */ 96, 95, 95, 94, 94, 94, 93, 350, 92, 89,
|
|
- /* 50 */ 178, 99, 100, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 60 */ 98, 98, 98, 98, 350, 96, 96, 96, 96, 95,
|
|
- /* 70 */ 95, 94, 94, 94, 93, 350, 324, 339, 969, 262,
|
|
- /* 80 */ 364, 251, 212, 169, 287, 404, 282, 403, 199, 786,
|
|
- /* 90 */ 242, 411, 21, 950, 378, 280, 93, 350, 787, 95,
|
|
- /* 100 */ 95, 94, 94, 94, 93, 350, 971, 971, 96, 96,
|
|
- /* 110 */ 96, 96, 95, 95, 94, 94, 94, 93, 350, 808,
|
|
- /* 120 */ 328, 242, 411, 1235, 826, 1235, 132, 99, 100, 90,
|
|
- /* 130 */ 847, 850, 839, 839, 97, 97, 98, 98, 98, 98,
|
|
- /* 140 */ 449, 96, 96, 96, 96, 95, 95, 94, 94, 94,
|
|
- /* 150 */ 93, 350, 324, 819, 348, 347, 120, 818, 120, 75,
|
|
- /* 160 */ 52, 52, 950, 951, 952, 1084, 977, 146, 360, 262,
|
|
- /* 170 */ 369, 261, 950, 975, 954, 976, 92, 89, 178, 370,
|
|
- /* 180 */ 230, 370, 971, 971, 1141, 360, 359, 101, 818, 818,
|
|
- /* 190 */ 820, 383, 24, 1286, 380, 427, 412, 368, 978, 379,
|
|
- /* 200 */ 978, 1032, 324, 99, 100, 90, 847, 850, 839, 839,
|
|
- /* 210 */ 97, 97, 98, 98, 98, 98, 372, 96, 96, 96,
|
|
- /* 220 */ 96, 95, 95, 94, 94, 94, 93, 350, 950, 132,
|
|
- /* 230 */ 890, 449, 971, 971, 890, 60, 94, 94, 94, 93,
|
|
- /* 240 */ 350, 950, 951, 952, 954, 103, 360, 950, 384, 333,
|
|
- /* 250 */ 697, 52, 52, 99, 100, 90, 847, 850, 839, 839,
|
|
- /* 260 */ 97, 97, 98, 98, 98, 98, 1022, 96, 96, 96,
|
|
- /* 270 */ 96, 95, 95, 94, 94, 94, 93, 350, 324, 454,
|
|
- /* 280 */ 995, 449, 227, 61, 157, 243, 343, 114, 1025, 1211,
|
|
- /* 290 */ 147, 826, 950, 372, 1071, 950, 319, 950, 951, 952,
|
|
- /* 300 */ 194, 10, 10, 401, 398, 397, 1211, 1213, 971, 971,
|
|
- /* 310 */ 757, 171, 170, 157, 396, 336, 950, 951, 952, 697,
|
|
- /* 320 */ 819, 310, 153, 950, 818, 320, 82, 23, 80, 99,
|
|
- /* 330 */ 100, 90, 847, 850, 839, 839, 97, 97, 98, 98,
|
|
- /* 340 */ 98, 98, 888, 96, 96, 96, 96, 95, 95, 94,
|
|
- /* 350 */ 94, 94, 93, 350, 324, 818, 818, 820, 277, 231,
|
|
- /* 360 */ 300, 950, 951, 952, 950, 951, 952, 1211, 194, 25,
|
|
- /* 370 */ 449, 401, 398, 397, 950, 354, 300, 449, 950, 74,
|
|
- /* 380 */ 449, 1, 396, 132, 971, 971, 950, 224, 224, 808,
|
|
- /* 390 */ 10, 10, 950, 951, 952, 1290, 132, 52, 52, 414,
|
|
- /* 400 */ 52, 52, 1063, 1063, 338, 99, 100, 90, 847, 850,
|
|
- /* 410 */ 839, 839, 97, 97, 98, 98, 98, 98, 1114, 96,
|
|
- /* 420 */ 96, 96, 96, 95, 95, 94, 94, 94, 93, 350,
|
|
- /* 430 */ 324, 1113, 427, 417, 701, 427, 426, 1260, 1260, 262,
|
|
- /* 440 */ 369, 261, 950, 950, 951, 952, 752, 950, 951, 952,
|
|
- /* 450 */ 449, 751, 449, 1058, 1037, 950, 951, 952, 442, 706,
|
|
- /* 460 */ 971, 971, 1058, 393, 92, 89, 178, 446, 446, 446,
|
|
- /* 470 */ 51, 51, 52, 52, 438, 773, 1024, 92, 89, 178,
|
|
- /* 480 */ 172, 99, 100, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 490 */ 98, 98, 98, 98, 198, 96, 96, 96, 96, 95,
|
|
- /* 500 */ 95, 94, 94, 94, 93, 350, 324, 427, 407, 909,
|
|
- /* 510 */ 694, 950, 951, 952, 92, 89, 178, 224, 224, 157,
|
|
- /* 520 */ 241, 221, 418, 299, 771, 910, 415, 374, 449, 414,
|
|
- /* 530 */ 58, 323, 1061, 1061, 1242, 378, 971, 971, 378, 772,
|
|
- /* 540 */ 448, 911, 362, 735, 296, 681, 9, 9, 52, 52,
|
|
- /* 550 */ 234, 329, 234, 256, 416, 736, 280, 99, 100, 90,
|
|
- /* 560 */ 847, 850, 839, 839, 97, 97, 98, 98, 98, 98,
|
|
- /* 570 */ 449, 96, 96, 96, 96, 95, 95, 94, 94, 94,
|
|
- /* 580 */ 93, 350, 324, 422, 72, 449, 827, 120, 367, 449,
|
|
- /* 590 */ 10, 10, 5, 301, 203, 449, 177, 969, 253, 419,
|
|
- /* 600 */ 255, 771, 200, 175, 233, 10, 10, 836, 836, 36,
|
|
- /* 610 */ 36, 1289, 971, 971, 724, 37, 37, 348, 347, 424,
|
|
- /* 620 */ 203, 260, 771, 969, 232, 930, 1316, 870, 337, 1316,
|
|
- /* 630 */ 421, 848, 851, 99, 100, 90, 847, 850, 839, 839,
|
|
- /* 640 */ 97, 97, 98, 98, 98, 98, 268, 96, 96, 96,
|
|
- /* 650 */ 96, 95, 95, 94, 94, 94, 93, 350, 324, 840,
|
|
- /* 660 */ 449, 978, 813, 978, 1200, 449, 909, 969, 715, 349,
|
|
- /* 670 */ 349, 349, 928, 177, 449, 930, 1317, 254, 198, 1317,
|
|
- /* 680 */ 12, 12, 910, 402, 449, 27, 27, 250, 971, 971,
|
|
- /* 690 */ 118, 716, 162, 969, 38, 38, 268, 176, 911, 771,
|
|
- /* 700 */ 432, 1265, 939, 353, 39, 39, 316, 991, 324, 99,
|
|
- /* 710 */ 100, 90, 847, 850, 839, 839, 97, 97, 98, 98,
|
|
- /* 720 */ 98, 98, 928, 96, 96, 96, 96, 95, 95, 94,
|
|
- /* 730 */ 94, 94, 93, 350, 449, 329, 449, 357, 971, 971,
|
|
- /* 740 */ 1041, 316, 929, 340, 893, 893, 386, 669, 670, 671,
|
|
- /* 750 */ 275, 1318, 317, 992, 40, 40, 41, 41, 268, 99,
|
|
- /* 760 */ 100, 90, 847, 850, 839, 839, 97, 97, 98, 98,
|
|
- /* 770 */ 98, 98, 449, 96, 96, 96, 96, 95, 95, 94,
|
|
- /* 780 */ 94, 94, 93, 350, 324, 449, 355, 449, 992, 449,
|
|
- /* 790 */ 1016, 330, 42, 42, 786, 270, 449, 273, 449, 228,
|
|
- /* 800 */ 449, 298, 449, 787, 449, 28, 28, 29, 29, 31,
|
|
- /* 810 */ 31, 449, 1141, 449, 971, 971, 43, 43, 44, 44,
|
|
- /* 820 */ 45, 45, 11, 11, 46, 46, 887, 78, 887, 268,
|
|
- /* 830 */ 268, 105, 105, 47, 47, 99, 100, 90, 847, 850,
|
|
- /* 840 */ 839, 839, 97, 97, 98, 98, 98, 98, 449, 96,
|
|
- /* 850 */ 96, 96, 96, 95, 95, 94, 94, 94, 93, 350,
|
|
- /* 860 */ 324, 449, 117, 449, 1073, 158, 449, 691, 48, 48,
|
|
- /* 870 */ 229, 1241, 449, 1250, 449, 414, 449, 334, 449, 245,
|
|
- /* 880 */ 449, 33, 33, 49, 49, 449, 50, 50, 246, 1141,
|
|
- /* 890 */ 971, 971, 34, 34, 122, 122, 123, 123, 124, 124,
|
|
- /* 900 */ 56, 56, 268, 81, 249, 35, 35, 197, 196, 195,
|
|
- /* 910 */ 324, 99, 100, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 920 */ 98, 98, 98, 98, 449, 96, 96, 96, 96, 95,
|
|
- /* 930 */ 95, 94, 94, 94, 93, 350, 449, 691, 449, 1141,
|
|
- /* 940 */ 971, 971, 968, 1207, 106, 106, 268, 1209, 268, 1266,
|
|
- /* 950 */ 2, 886, 268, 886, 335, 1040, 53, 53, 107, 107,
|
|
- /* 960 */ 324, 99, 100, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 970 */ 98, 98, 98, 98, 449, 96, 96, 96, 96, 95,
|
|
- /* 980 */ 95, 94, 94, 94, 93, 350, 449, 1070, 449, 1066,
|
|
- /* 990 */ 971, 971, 1039, 267, 108, 108, 445, 330, 331, 133,
|
|
- /* 1000 */ 223, 175, 301, 225, 385, 1255, 104, 104, 121, 121,
|
|
- /* 1010 */ 324, 99, 88, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 1020 */ 98, 98, 98, 98, 1141, 96, 96, 96, 96, 95,
|
|
- /* 1030 */ 95, 94, 94, 94, 93, 350, 449, 346, 449, 167,
|
|
- /* 1040 */ 971, 971, 925, 810, 371, 318, 202, 202, 373, 263,
|
|
- /* 1050 */ 394, 202, 74, 208, 721, 722, 119, 119, 112, 112,
|
|
- /* 1060 */ 324, 406, 100, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 1070 */ 98, 98, 98, 98, 449, 96, 96, 96, 96, 95,
|
|
- /* 1080 */ 95, 94, 94, 94, 93, 350, 449, 752, 449, 344,
|
|
- /* 1090 */ 971, 971, 751, 278, 111, 111, 74, 714, 713, 704,
|
|
- /* 1100 */ 286, 877, 749, 1279, 257, 77, 109, 109, 110, 110,
|
|
- /* 1110 */ 1230, 285, 1134, 90, 847, 850, 839, 839, 97, 97,
|
|
- /* 1120 */ 98, 98, 98, 98, 1233, 96, 96, 96, 96, 95,
|
|
- /* 1130 */ 95, 94, 94, 94, 93, 350, 86, 444, 449, 3,
|
|
- /* 1140 */ 1193, 449, 1069, 132, 351, 120, 1013, 86, 444, 780,
|
|
- /* 1150 */ 3, 1091, 202, 376, 447, 351, 1229, 120, 55, 55,
|
|
- /* 1160 */ 449, 57, 57, 822, 873, 447, 449, 208, 449, 704,
|
|
- /* 1170 */ 449, 877, 237, 433, 435, 120, 439, 428, 361, 120,
|
|
- /* 1180 */ 54, 54, 132, 449, 433, 826, 52, 52, 26, 26,
|
|
- /* 1190 */ 30, 30, 381, 132, 408, 443, 826, 689, 264, 389,
|
|
- /* 1200 */ 116, 269, 272, 32, 32, 83, 84, 120, 274, 120,
|
|
- /* 1210 */ 120, 276, 85, 351, 451, 450, 83, 84, 818, 1054,
|
|
- /* 1220 */ 1038, 427, 429, 85, 351, 451, 450, 120, 120, 818,
|
|
- /* 1230 */ 377, 218, 281, 822, 1107, 1140, 86, 444, 409, 3,
|
|
- /* 1240 */ 1087, 1098, 430, 431, 351, 302, 303, 1146, 1021, 818,
|
|
- /* 1250 */ 818, 820, 821, 19, 447, 1015, 1004, 1003, 1005, 1273,
|
|
- /* 1260 */ 818, 818, 820, 821, 19, 289, 159, 291, 293, 7,
|
|
- /* 1270 */ 315, 173, 259, 433, 1129, 363, 252, 1232, 375, 1037,
|
|
- /* 1280 */ 295, 434, 168, 986, 399, 826, 284, 1204, 1203, 205,
|
|
- /* 1290 */ 1276, 308, 1249, 86, 444, 983, 3, 1247, 332, 144,
|
|
- /* 1300 */ 130, 351, 72, 135, 59, 83, 84, 756, 137, 365,
|
|
- /* 1310 */ 1126, 447, 85, 351, 451, 450, 139, 226, 818, 140,
|
|
- /* 1320 */ 156, 62, 314, 314, 313, 215, 311, 366, 392, 678,
|
|
- /* 1330 */ 433, 185, 141, 1234, 142, 160, 148, 1136, 1198, 382,
|
|
- /* 1340 */ 189, 67, 826, 180, 388, 248, 1218, 1099, 219, 818,
|
|
- /* 1350 */ 818, 820, 821, 19, 247, 190, 266, 154, 390, 271,
|
|
- /* 1360 */ 191, 192, 83, 84, 1006, 405, 1057, 182, 321, 85,
|
|
- /* 1370 */ 351, 451, 450, 1056, 183, 818, 341, 132, 181, 706,
|
|
- /* 1380 */ 1055, 420, 76, 444, 1029, 3, 322, 1028, 283, 1048,
|
|
- /* 1390 */ 351, 1095, 1027, 1288, 1047, 71, 204, 6, 288, 290,
|
|
- /* 1400 */ 447, 1096, 1094, 1093, 79, 292, 818, 818, 820, 821,
|
|
- /* 1410 */ 19, 294, 297, 437, 345, 441, 102, 1184, 1077, 433,
|
|
- /* 1420 */ 238, 425, 73, 305, 239, 304, 325, 240, 423, 306,
|
|
- /* 1430 */ 307, 826, 213, 1012, 22, 945, 452, 214, 216, 217,
|
|
- /* 1440 */ 453, 1001, 115, 996, 125, 126, 235, 127, 665, 352,
|
|
- /* 1450 */ 326, 83, 84, 358, 166, 244, 179, 327, 85, 351,
|
|
- /* 1460 */ 451, 450, 134, 356, 818, 113, 885, 806, 883, 136,
|
|
- /* 1470 */ 128, 138, 738, 258, 184, 899, 143, 145, 63, 64,
|
|
- /* 1480 */ 65, 66, 129, 902, 187, 186, 898, 8, 13, 188,
|
|
- /* 1490 */ 265, 891, 149, 202, 980, 818, 818, 820, 821, 19,
|
|
- /* 1500 */ 150, 387, 161, 680, 285, 391, 151, 395, 400, 193,
|
|
- /* 1510 */ 68, 14, 236, 279, 15, 69, 717, 825, 131, 824,
|
|
- /* 1520 */ 853, 70, 746, 16, 413, 750, 4, 174, 220, 222,
|
|
- /* 1530 */ 152, 779, 857, 774, 201, 77, 74, 868, 17, 854,
|
|
- /* 1540 */ 852, 908, 18, 907, 207, 206, 934, 163, 436, 210,
|
|
- /* 1550 */ 935, 164, 209, 165, 440, 856, 823, 690, 87, 211,
|
|
- /* 1560 */ 309, 312, 1281, 940, 1280,
|
|
+ /* 0 */ 368, 105, 102, 197, 105, 102, 197, 515, 1124, 1,
|
|
+ /* 10 */ 1, 520, 2, 1128, 515, 1192, 1171, 1456, 275, 370,
|
|
+ /* 20 */ 127, 1389, 1197, 1197, 1192, 1166, 178, 1205, 64, 64,
|
|
+ /* 30 */ 477, 887, 322, 428, 348, 37, 37, 808, 362, 888,
|
|
+ /* 40 */ 509, 509, 509, 112, 113, 103, 1100, 1100, 953, 956,
|
|
+ /* 50 */ 946, 946, 110, 110, 111, 111, 111, 111, 365, 252,
|
|
+ /* 60 */ 252, 515, 252, 252, 497, 515, 309, 515, 459, 515,
|
|
+ /* 70 */ 1079, 491, 512, 478, 6, 512, 809, 134, 498, 228,
|
|
+ /* 80 */ 194, 428, 37, 37, 515, 208, 64, 64, 64, 64,
|
|
+ /* 90 */ 13, 13, 109, 109, 109, 109, 108, 108, 107, 107,
|
|
+ /* 100 */ 107, 106, 401, 258, 381, 13, 13, 398, 397, 428,
|
|
+ /* 110 */ 252, 252, 370, 476, 405, 1104, 1079, 1080, 1081, 386,
|
|
+ /* 120 */ 1106, 390, 497, 512, 497, 1423, 1419, 304, 1105, 307,
|
|
+ /* 130 */ 1256, 496, 370, 499, 16, 16, 112, 113, 103, 1100,
|
|
+ /* 140 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111,
|
|
+ /* 150 */ 111, 262, 1107, 495, 1107, 401, 112, 113, 103, 1100,
|
|
+ /* 160 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111,
|
|
+ /* 170 */ 111, 129, 1425, 343, 1420, 339, 1059, 492, 1057, 263,
|
|
+ /* 180 */ 73, 105, 102, 197, 994, 109, 109, 109, 109, 108,
|
|
+ /* 190 */ 108, 107, 107, 107, 106, 401, 370, 111, 111, 111,
|
|
+ /* 200 */ 111, 104, 492, 89, 1432, 109, 109, 109, 109, 108,
|
|
+ /* 210 */ 108, 107, 107, 107, 106, 401, 111, 111, 111, 111,
|
|
+ /* 220 */ 112, 113, 103, 1100, 1100, 953, 956, 946, 946, 110,
|
|
+ /* 230 */ 110, 111, 111, 111, 111, 109, 109, 109, 109, 108,
|
|
+ /* 240 */ 108, 107, 107, 107, 106, 401, 114, 108, 108, 107,
|
|
+ /* 250 */ 107, 107, 106, 401, 109, 109, 109, 109, 108, 108,
|
|
+ /* 260 */ 107, 107, 107, 106, 401, 152, 399, 399, 399, 109,
|
|
+ /* 270 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401,
|
|
+ /* 280 */ 178, 493, 1412, 434, 1037, 1486, 1079, 515, 1486, 370,
|
|
+ /* 290 */ 421, 297, 357, 412, 74, 1079, 109, 109, 109, 109,
|
|
+ /* 300 */ 108, 108, 107, 107, 107, 106, 401, 1413, 37, 37,
|
|
+ /* 310 */ 1431, 274, 506, 112, 113, 103, 1100, 1100, 953, 956,
|
|
+ /* 320 */ 946, 946, 110, 110, 111, 111, 111, 111, 1436, 520,
|
|
+ /* 330 */ 2, 1128, 1079, 1080, 1081, 430, 275, 1079, 127, 366,
|
|
+ /* 340 */ 933, 1079, 1080, 1081, 220, 1205, 913, 458, 455, 454,
|
|
+ /* 350 */ 392, 167, 515, 1035, 152, 445, 924, 453, 152, 874,
|
|
+ /* 360 */ 923, 289, 109, 109, 109, 109, 108, 108, 107, 107,
|
|
+ /* 370 */ 107, 106, 401, 13, 13, 261, 853, 252, 252, 227,
|
|
+ /* 380 */ 106, 401, 370, 1079, 1080, 1081, 311, 388, 1079, 296,
|
|
+ /* 390 */ 512, 923, 923, 925, 231, 323, 1255, 1388, 1423, 490,
|
|
+ /* 400 */ 274, 506, 12, 208, 274, 506, 112, 113, 103, 1100,
|
|
+ /* 410 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111,
|
|
+ /* 420 */ 111, 1440, 286, 1128, 288, 1079, 1097, 247, 275, 1098,
|
|
+ /* 430 */ 127, 387, 405, 389, 1079, 1080, 1081, 1205, 159, 238,
|
|
+ /* 440 */ 255, 321, 461, 316, 460, 225, 790, 105, 102, 197,
|
|
+ /* 450 */ 513, 314, 842, 842, 445, 109, 109, 109, 109, 108,
|
|
+ /* 460 */ 108, 107, 107, 107, 106, 401, 515, 514, 515, 252,
|
|
+ /* 470 */ 252, 1079, 1080, 1081, 435, 370, 1098, 933, 1460, 794,
|
|
+ /* 480 */ 274, 506, 512, 105, 102, 197, 336, 63, 63, 64,
|
|
+ /* 490 */ 64, 27, 790, 924, 287, 208, 1354, 923, 515, 112,
|
|
+ /* 500 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110,
|
|
+ /* 510 */ 111, 111, 111, 111, 107, 107, 107, 106, 401, 49,
|
|
+ /* 520 */ 49, 515, 28, 1079, 405, 497, 421, 297, 923, 923,
|
|
+ /* 530 */ 925, 186, 468, 1079, 467, 999, 999, 442, 515, 1079,
|
|
+ /* 540 */ 334, 515, 45, 45, 1083, 342, 173, 168, 109, 109,
|
|
+ /* 550 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 13,
|
|
+ /* 560 */ 13, 205, 13, 13, 252, 252, 1195, 1195, 370, 1079,
|
|
+ /* 570 */ 1080, 1081, 787, 265, 5, 359, 494, 512, 469, 1079,
|
|
+ /* 580 */ 1080, 1081, 398, 397, 1079, 1079, 1080, 1081, 3, 282,
|
|
+ /* 590 */ 1079, 1083, 112, 113, 103, 1100, 1100, 953, 956, 946,
|
|
+ /* 600 */ 946, 110, 110, 111, 111, 111, 111, 252, 252, 1015,
|
|
+ /* 610 */ 220, 1079, 873, 458, 455, 454, 943, 943, 954, 957,
|
|
+ /* 620 */ 512, 252, 252, 453, 1016, 1079, 445, 1107, 1209, 1107,
|
|
+ /* 630 */ 1079, 1080, 1081, 515, 512, 426, 1079, 1080, 1081, 1017,
|
|
+ /* 640 */ 512, 109, 109, 109, 109, 108, 108, 107, 107, 107,
|
|
+ /* 650 */ 106, 401, 1052, 515, 50, 50, 515, 1079, 1080, 1081,
|
|
+ /* 660 */ 828, 370, 1051, 379, 411, 1064, 1358, 207, 408, 773,
|
|
+ /* 670 */ 829, 1079, 1080, 1081, 64, 64, 322, 64, 64, 1302,
|
|
+ /* 680 */ 947, 411, 410, 1358, 1360, 112, 113, 103, 1100, 1100,
|
|
+ /* 690 */ 953, 956, 946, 946, 110, 110, 111, 111, 111, 111,
|
|
+ /* 700 */ 294, 482, 515, 1037, 1487, 515, 434, 1487, 354, 1120,
|
|
+ /* 710 */ 483, 996, 913, 485, 466, 996, 132, 178, 33, 450,
|
|
+ /* 720 */ 1203, 136, 406, 64, 64, 479, 64, 64, 419, 369,
|
|
+ /* 730 */ 283, 1146, 252, 252, 109, 109, 109, 109, 108, 108,
|
|
+ /* 740 */ 107, 107, 107, 106, 401, 512, 224, 440, 411, 266,
|
|
+ /* 750 */ 1358, 266, 252, 252, 370, 296, 416, 284, 934, 396,
|
|
+ /* 760 */ 976, 470, 400, 252, 252, 512, 9, 473, 231, 500,
|
|
+ /* 770 */ 354, 1036, 1035, 1488, 355, 374, 512, 1121, 112, 113,
|
|
+ /* 780 */ 103, 1100, 1100, 953, 956, 946, 946, 110, 110, 111,
|
|
+ /* 790 */ 111, 111, 111, 252, 252, 1015, 515, 1347, 295, 252,
|
|
+ /* 800 */ 252, 252, 252, 1098, 375, 249, 512, 445, 872, 322,
|
|
+ /* 810 */ 1016, 480, 512, 195, 512, 434, 273, 15, 15, 515,
|
|
+ /* 820 */ 314, 515, 95, 515, 93, 1017, 367, 109, 109, 109,
|
|
+ /* 830 */ 109, 108, 108, 107, 107, 107, 106, 401, 515, 1121,
|
|
+ /* 840 */ 39, 39, 51, 51, 52, 52, 503, 370, 515, 1204,
|
|
+ /* 850 */ 1098, 918, 439, 341, 133, 436, 223, 222, 221, 53,
|
|
+ /* 860 */ 53, 322, 1400, 761, 762, 763, 515, 370, 88, 54,
|
|
+ /* 870 */ 54, 112, 113, 103, 1100, 1100, 953, 956, 946, 946,
|
|
+ /* 880 */ 110, 110, 111, 111, 111, 111, 407, 55, 55, 196,
|
|
+ /* 890 */ 515, 112, 113, 103, 1100, 1100, 953, 956, 946, 946,
|
|
+ /* 900 */ 110, 110, 111, 111, 111, 111, 135, 264, 1149, 376,
|
|
+ /* 910 */ 515, 40, 40, 515, 872, 515, 993, 515, 993, 116,
|
|
+ /* 920 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106,
|
|
+ /* 930 */ 401, 41, 41, 515, 43, 43, 44, 44, 56, 56,
|
|
+ /* 940 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106,
|
|
+ /* 950 */ 401, 515, 379, 515, 57, 57, 515, 799, 515, 379,
|
|
+ /* 960 */ 515, 445, 200, 515, 323, 515, 1397, 515, 1459, 515,
|
|
+ /* 970 */ 1287, 817, 58, 58, 14, 14, 515, 59, 59, 118,
|
|
+ /* 980 */ 118, 60, 60, 515, 46, 46, 61, 61, 62, 62,
|
|
+ /* 990 */ 47, 47, 515, 190, 189, 91, 515, 140, 140, 515,
|
|
+ /* 1000 */ 394, 515, 277, 1200, 141, 141, 515, 1115, 515, 992,
|
|
+ /* 1010 */ 515, 992, 515, 69, 69, 370, 278, 48, 48, 259,
|
|
+ /* 1020 */ 65, 65, 119, 119, 246, 246, 260, 66, 66, 120,
|
|
+ /* 1030 */ 120, 121, 121, 117, 117, 370, 515, 512, 383, 112,
|
|
+ /* 1040 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110,
|
|
+ /* 1050 */ 111, 111, 111, 111, 515, 872, 515, 139, 139, 112,
|
|
+ /* 1060 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110,
|
|
+ /* 1070 */ 111, 111, 111, 111, 1287, 138, 138, 125, 125, 515,
|
|
+ /* 1080 */ 12, 515, 281, 1287, 515, 445, 131, 1287, 109, 109,
|
|
+ /* 1090 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515,
|
|
+ /* 1100 */ 124, 124, 122, 122, 515, 123, 123, 515, 109, 109,
|
|
+ /* 1110 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515,
|
|
+ /* 1120 */ 68, 68, 463, 783, 515, 70, 70, 302, 67, 67,
|
|
+ /* 1130 */ 1032, 253, 253, 356, 1287, 191, 196, 1433, 465, 1301,
|
|
+ /* 1140 */ 38, 38, 384, 94, 512, 42, 42, 177, 848, 274,
|
|
+ /* 1150 */ 506, 385, 420, 847, 1356, 441, 508, 376, 377, 153,
|
|
+ /* 1160 */ 423, 872, 432, 370, 224, 251, 194, 887, 182, 293,
|
|
+ /* 1170 */ 783, 848, 88, 254, 466, 888, 847, 915, 807, 806,
|
|
+ /* 1180 */ 230, 1241, 910, 370, 17, 413, 797, 112, 113, 103,
|
|
+ /* 1190 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111,
|
|
+ /* 1200 */ 111, 111, 395, 814, 815, 1175, 983, 112, 101, 103,
|
|
+ /* 1210 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111,
|
|
+ /* 1220 */ 111, 111, 375, 422, 427, 429, 298, 230, 230, 88,
|
|
+ /* 1230 */ 1240, 451, 312, 797, 226, 88, 109, 109, 109, 109,
|
|
+ /* 1240 */ 108, 108, 107, 107, 107, 106, 401, 86, 433, 979,
|
|
+ /* 1250 */ 927, 881, 226, 983, 230, 415, 109, 109, 109, 109,
|
|
+ /* 1260 */ 108, 108, 107, 107, 107, 106, 401, 320, 845, 781,
|
|
+ /* 1270 */ 846, 100, 130, 100, 1403, 290, 370, 319, 1377, 1376,
|
|
+ /* 1280 */ 437, 1449, 299, 1237, 303, 306, 308, 310, 1188, 1174,
|
|
+ /* 1290 */ 1173, 1172, 315, 324, 325, 1228, 370, 927, 1249, 271,
|
|
+ /* 1300 */ 1286, 113, 103, 1100, 1100, 953, 956, 946, 946, 110,
|
|
+ /* 1310 */ 110, 111, 111, 111, 111, 1224, 1235, 502, 501, 1292,
|
|
+ /* 1320 */ 1221, 1155, 103, 1100, 1100, 953, 956, 946, 946, 110,
|
|
+ /* 1330 */ 110, 111, 111, 111, 111, 1148, 1137, 1136, 1138, 1443,
|
|
+ /* 1340 */ 446, 244, 184, 98, 507, 188, 4, 353, 327, 109,
|
|
+ /* 1350 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401,
|
|
+ /* 1360 */ 510, 329, 331, 199, 414, 456, 292, 285, 318, 109,
|
|
+ /* 1370 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401,
|
|
+ /* 1380 */ 11, 1271, 1279, 402, 361, 192, 1171, 1351, 431, 505,
|
|
+ /* 1390 */ 346, 1350, 333, 98, 507, 504, 4, 187, 1446, 1115,
|
|
+ /* 1400 */ 233, 1396, 155, 1394, 1112, 152, 72, 75, 378, 425,
|
|
+ /* 1410 */ 510, 165, 149, 157, 933, 1276, 86, 30, 1268, 417,
|
|
+ /* 1420 */ 96, 96, 8, 160, 161, 162, 163, 97, 418, 402,
|
|
+ /* 1430 */ 517, 516, 449, 402, 923, 210, 358, 424, 1282, 438,
|
|
+ /* 1440 */ 169, 214, 360, 1345, 80, 504, 31, 444, 1365, 301,
|
|
+ /* 1450 */ 245, 274, 506, 216, 174, 305, 488, 447, 217, 462,
|
|
+ /* 1460 */ 1139, 487, 218, 363, 933, 923, 923, 925, 926, 24,
|
|
+ /* 1470 */ 96, 96, 1191, 1190, 1189, 391, 1182, 97, 1163, 402,
|
|
+ /* 1480 */ 517, 516, 799, 364, 923, 1162, 317, 1161, 98, 507,
|
|
+ /* 1490 */ 1181, 4, 1458, 472, 393, 269, 270, 475, 481, 1232,
|
|
+ /* 1500 */ 85, 1233, 326, 328, 232, 510, 495, 1231, 330, 98,
|
|
+ /* 1510 */ 507, 1230, 4, 486, 335, 923, 923, 925, 926, 24,
|
|
+ /* 1520 */ 1435, 1068, 404, 181, 336, 256, 510, 115, 402, 332,
|
|
+ /* 1530 */ 352, 352, 351, 241, 349, 1214, 1414, 770, 338, 10,
|
|
+ /* 1540 */ 504, 340, 272, 92, 1331, 1213, 87, 183, 484, 402,
|
|
+ /* 1550 */ 201, 488, 280, 239, 344, 345, 489, 1145, 29, 933,
|
|
+ /* 1560 */ 279, 504, 1074, 518, 240, 96, 96, 242, 243, 519,
|
|
+ /* 1570 */ 1134, 1129, 97, 154, 402, 517, 516, 372, 373, 923,
|
|
+ /* 1580 */ 933, 142, 143, 128, 1381, 267, 96, 96, 852, 757,
|
|
+ /* 1590 */ 203, 144, 403, 97, 1382, 402, 517, 516, 204, 1380,
|
|
+ /* 1600 */ 923, 146, 1379, 1159, 1158, 71, 1156, 276, 202, 185,
|
|
+ /* 1610 */ 923, 923, 925, 926, 24, 198, 257, 126, 991, 989,
|
|
+ /* 1620 */ 907, 98, 507, 156, 4, 145, 158, 206, 831, 209,
|
|
+ /* 1630 */ 291, 923, 923, 925, 926, 24, 1005, 911, 510, 164,
|
|
+ /* 1640 */ 147, 380, 371, 382, 166, 76, 77, 274, 506, 148,
|
|
+ /* 1650 */ 78, 79, 1008, 211, 212, 1004, 137, 213, 18, 300,
|
|
+ /* 1660 */ 230, 402, 997, 1109, 443, 215, 32, 170, 171, 772,
|
|
+ /* 1670 */ 409, 448, 319, 504, 219, 172, 452, 81, 19, 457,
|
|
+ /* 1680 */ 313, 20, 82, 268, 488, 150, 810, 179, 83, 487,
|
|
+ /* 1690 */ 464, 151, 933, 180, 959, 84, 1040, 34, 96, 96,
|
|
+ /* 1700 */ 471, 1041, 35, 474, 193, 97, 248, 402, 517, 516,
|
|
+ /* 1710 */ 1068, 404, 923, 250, 256, 880, 229, 175, 875, 352,
|
|
+ /* 1720 */ 352, 351, 241, 349, 100, 21, 770, 22, 1054, 1056,
|
|
+ /* 1730 */ 7, 98, 507, 1045, 4, 337, 1058, 23, 974, 201,
|
|
+ /* 1740 */ 176, 280, 88, 923, 923, 925, 926, 24, 510, 279,
|
|
+ /* 1750 */ 960, 958, 962, 1014, 963, 1013, 235, 234, 25, 36,
|
|
+ /* 1760 */ 99, 90, 507, 928, 4, 511, 350, 782, 26, 841,
|
|
+ /* 1770 */ 236, 402, 347, 1069, 237, 1125, 1125, 1451, 510, 203,
|
|
+ /* 1780 */ 1450, 1125, 1125, 504, 1125, 1125, 1125, 204, 1125, 1125,
|
|
+ /* 1790 */ 146, 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125,
|
|
+ /* 1800 */ 1125, 402, 933, 1125, 1125, 1125, 1125, 1125, 96, 96,
|
|
+ /* 1810 */ 1125, 1125, 1125, 504, 1125, 97, 1125, 402, 517, 516,
|
|
+ /* 1820 */ 1125, 1125, 923, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 1830 */ 1125, 371, 933, 1125, 1125, 1125, 274, 506, 96, 96,
|
|
+ /* 1840 */ 1125, 1125, 1125, 1125, 1125, 97, 1125, 402, 517, 516,
|
|
+ /* 1850 */ 1125, 1125, 923, 923, 923, 925, 926, 24, 1125, 409,
|
|
+ /* 1860 */ 1125, 1125, 1125, 256, 1125, 1125, 1125, 1125, 352, 352,
|
|
+ /* 1870 */ 351, 241, 349, 1125, 1125, 770, 1125, 1125, 1125, 1125,
|
|
+ /* 1880 */ 1125, 1125, 1125, 923, 923, 925, 926, 24, 201, 1125,
|
|
+ /* 1890 */ 280, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 279, 1125,
|
|
+ /* 1900 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 1910 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 1920 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 203, 1125,
|
|
+ /* 1930 */ 1125, 1125, 1125, 1125, 1125, 1125, 204, 1125, 1125, 146,
|
|
+ /* 1940 */ 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125, 1125,
|
|
+ /* 1950 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 1960 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 1970 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 1980 */ 371, 1125, 1125, 1125, 1125, 274, 506, 1125, 1125, 1125,
|
|
+ /* 1990 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
|
|
+ /* 2000 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 409,
|
|
};
|
|
static const YYCODETYPE yy_lookahead[] = {
|
|
- /* 0 */ 19, 115, 19, 117, 118, 24, 1, 2, 27, 79,
|
|
- /* 10 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
|
|
- /* 20 */ 90, 91, 92, 93, 94, 144, 145, 146, 147, 58,
|
|
- /* 30 */ 49, 50, 79, 80, 81, 82, 22, 84, 85, 86,
|
|
- /* 40 */ 87, 88, 89, 90, 91, 92, 93, 94, 221, 222,
|
|
- /* 50 */ 223, 70, 71, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 60 */ 79, 80, 81, 82, 94, 84, 85, 86, 87, 88,
|
|
- /* 70 */ 89, 90, 91, 92, 93, 94, 19, 94, 97, 108,
|
|
- /* 80 */ 109, 110, 99, 100, 101, 102, 103, 104, 105, 32,
|
|
- /* 90 */ 119, 120, 78, 27, 152, 112, 93, 94, 41, 88,
|
|
- /* 100 */ 89, 90, 91, 92, 93, 94, 49, 50, 84, 85,
|
|
- /* 110 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 58,
|
|
- /* 120 */ 157, 119, 120, 163, 68, 163, 65, 70, 71, 72,
|
|
- /* 130 */ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
|
|
- /* 140 */ 152, 84, 85, 86, 87, 88, 89, 90, 91, 92,
|
|
- /* 150 */ 93, 94, 19, 97, 88, 89, 196, 101, 196, 26,
|
|
- /* 160 */ 172, 173, 96, 97, 98, 210, 100, 22, 152, 108,
|
|
- /* 170 */ 109, 110, 27, 107, 27, 109, 221, 222, 223, 219,
|
|
- /* 180 */ 238, 219, 49, 50, 152, 169, 170, 54, 132, 133,
|
|
- /* 190 */ 134, 228, 232, 171, 231, 207, 208, 237, 132, 237,
|
|
- /* 200 */ 134, 179, 19, 70, 71, 72, 73, 74, 75, 76,
|
|
- /* 210 */ 77, 78, 79, 80, 81, 82, 152, 84, 85, 86,
|
|
- /* 220 */ 87, 88, 89, 90, 91, 92, 93, 94, 27, 65,
|
|
- /* 230 */ 30, 152, 49, 50, 34, 52, 90, 91, 92, 93,
|
|
- /* 240 */ 94, 96, 97, 98, 97, 22, 230, 27, 48, 217,
|
|
- /* 250 */ 27, 172, 173, 70, 71, 72, 73, 74, 75, 76,
|
|
- /* 260 */ 77, 78, 79, 80, 81, 82, 172, 84, 85, 86,
|
|
- /* 270 */ 87, 88, 89, 90, 91, 92, 93, 94, 19, 148,
|
|
- /* 280 */ 149, 152, 218, 24, 152, 154, 207, 156, 172, 152,
|
|
- /* 290 */ 22, 68, 27, 152, 163, 27, 164, 96, 97, 98,
|
|
- /* 300 */ 99, 172, 173, 102, 103, 104, 169, 170, 49, 50,
|
|
- /* 310 */ 90, 88, 89, 152, 113, 186, 96, 97, 98, 96,
|
|
- /* 320 */ 97, 160, 57, 27, 101, 164, 137, 196, 139, 70,
|
|
- /* 330 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
|
|
- /* 340 */ 81, 82, 11, 84, 85, 86, 87, 88, 89, 90,
|
|
- /* 350 */ 91, 92, 93, 94, 19, 132, 133, 134, 23, 218,
|
|
- /* 360 */ 152, 96, 97, 98, 96, 97, 98, 230, 99, 22,
|
|
- /* 370 */ 152, 102, 103, 104, 27, 244, 152, 152, 27, 26,
|
|
- /* 380 */ 152, 22, 113, 65, 49, 50, 27, 194, 195, 58,
|
|
- /* 390 */ 172, 173, 96, 97, 98, 185, 65, 172, 173, 206,
|
|
- /* 400 */ 172, 173, 190, 191, 186, 70, 71, 72, 73, 74,
|
|
- /* 410 */ 75, 76, 77, 78, 79, 80, 81, 82, 175, 84,
|
|
- /* 420 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
|
|
- /* 430 */ 19, 175, 207, 208, 23, 207, 208, 119, 120, 108,
|
|
- /* 440 */ 109, 110, 27, 96, 97, 98, 116, 96, 97, 98,
|
|
- /* 450 */ 152, 121, 152, 179, 180, 96, 97, 98, 250, 106,
|
|
- /* 460 */ 49, 50, 188, 19, 221, 222, 223, 168, 169, 170,
|
|
- /* 470 */ 172, 173, 172, 173, 250, 124, 172, 221, 222, 223,
|
|
- /* 480 */ 26, 70, 71, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 490 */ 79, 80, 81, 82, 50, 84, 85, 86, 87, 88,
|
|
- /* 500 */ 89, 90, 91, 92, 93, 94, 19, 207, 208, 12,
|
|
- /* 510 */ 23, 96, 97, 98, 221, 222, 223, 194, 195, 152,
|
|
- /* 520 */ 199, 23, 19, 225, 26, 28, 152, 152, 152, 206,
|
|
- /* 530 */ 209, 164, 190, 191, 241, 152, 49, 50, 152, 124,
|
|
- /* 540 */ 152, 44, 219, 46, 152, 21, 172, 173, 172, 173,
|
|
- /* 550 */ 183, 107, 185, 16, 163, 58, 112, 70, 71, 72,
|
|
- /* 560 */ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
|
|
- /* 570 */ 152, 84, 85, 86, 87, 88, 89, 90, 91, 92,
|
|
- /* 580 */ 93, 94, 19, 207, 130, 152, 23, 196, 64, 152,
|
|
- /* 590 */ 172, 173, 22, 152, 24, 152, 98, 27, 61, 96,
|
|
- /* 600 */ 63, 26, 211, 212, 186, 172, 173, 49, 50, 172,
|
|
- /* 610 */ 173, 23, 49, 50, 26, 172, 173, 88, 89, 186,
|
|
- /* 620 */ 24, 238, 124, 27, 238, 22, 23, 103, 187, 26,
|
|
- /* 630 */ 152, 73, 74, 70, 71, 72, 73, 74, 75, 76,
|
|
- /* 640 */ 77, 78, 79, 80, 81, 82, 152, 84, 85, 86,
|
|
- /* 650 */ 87, 88, 89, 90, 91, 92, 93, 94, 19, 101,
|
|
- /* 660 */ 152, 132, 23, 134, 140, 152, 12, 97, 36, 168,
|
|
- /* 670 */ 169, 170, 69, 98, 152, 22, 23, 140, 50, 26,
|
|
- /* 680 */ 172, 173, 28, 51, 152, 172, 173, 193, 49, 50,
|
|
- /* 690 */ 22, 59, 24, 97, 172, 173, 152, 152, 44, 124,
|
|
- /* 700 */ 46, 0, 1, 2, 172, 173, 22, 23, 19, 70,
|
|
- /* 710 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
|
|
- /* 720 */ 81, 82, 69, 84, 85, 86, 87, 88, 89, 90,
|
|
- /* 730 */ 91, 92, 93, 94, 152, 107, 152, 193, 49, 50,
|
|
- /* 740 */ 181, 22, 23, 111, 108, 109, 110, 7, 8, 9,
|
|
- /* 750 */ 16, 247, 248, 69, 172, 173, 172, 173, 152, 70,
|
|
- /* 760 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
|
|
- /* 770 */ 81, 82, 152, 84, 85, 86, 87, 88, 89, 90,
|
|
- /* 780 */ 91, 92, 93, 94, 19, 152, 242, 152, 69, 152,
|
|
- /* 790 */ 166, 167, 172, 173, 32, 61, 152, 63, 152, 193,
|
|
- /* 800 */ 152, 152, 152, 41, 152, 172, 173, 172, 173, 172,
|
|
- /* 810 */ 173, 152, 152, 152, 49, 50, 172, 173, 172, 173,
|
|
- /* 820 */ 172, 173, 172, 173, 172, 173, 132, 138, 134, 152,
|
|
- /* 830 */ 152, 172, 173, 172, 173, 70, 71, 72, 73, 74,
|
|
- /* 840 */ 75, 76, 77, 78, 79, 80, 81, 82, 152, 84,
|
|
- /* 850 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
|
|
- /* 860 */ 19, 152, 22, 152, 195, 24, 152, 27, 172, 173,
|
|
- /* 870 */ 193, 193, 152, 152, 152, 206, 152, 217, 152, 152,
|
|
- /* 880 */ 152, 172, 173, 172, 173, 152, 172, 173, 152, 152,
|
|
- /* 890 */ 49, 50, 172, 173, 172, 173, 172, 173, 172, 173,
|
|
- /* 900 */ 172, 173, 152, 138, 152, 172, 173, 108, 109, 110,
|
|
- /* 910 */ 19, 70, 71, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 920 */ 79, 80, 81, 82, 152, 84, 85, 86, 87, 88,
|
|
- /* 930 */ 89, 90, 91, 92, 93, 94, 152, 97, 152, 152,
|
|
- /* 940 */ 49, 50, 26, 193, 172, 173, 152, 152, 152, 146,
|
|
- /* 950 */ 147, 132, 152, 134, 217, 181, 172, 173, 172, 173,
|
|
- /* 960 */ 19, 70, 71, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 970 */ 79, 80, 81, 82, 152, 84, 85, 86, 87, 88,
|
|
- /* 980 */ 89, 90, 91, 92, 93, 94, 152, 193, 152, 193,
|
|
- /* 990 */ 49, 50, 181, 193, 172, 173, 166, 167, 245, 246,
|
|
- /* 1000 */ 211, 212, 152, 22, 217, 152, 172, 173, 172, 173,
|
|
- /* 1010 */ 19, 70, 71, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 1020 */ 79, 80, 81, 82, 152, 84, 85, 86, 87, 88,
|
|
- /* 1030 */ 89, 90, 91, 92, 93, 94, 152, 187, 152, 123,
|
|
- /* 1040 */ 49, 50, 23, 23, 23, 26, 26, 26, 23, 23,
|
|
- /* 1050 */ 23, 26, 26, 26, 7, 8, 172, 173, 172, 173,
|
|
- /* 1060 */ 19, 90, 71, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 1070 */ 79, 80, 81, 82, 152, 84, 85, 86, 87, 88,
|
|
- /* 1080 */ 89, 90, 91, 92, 93, 94, 152, 116, 152, 217,
|
|
- /* 1090 */ 49, 50, 121, 23, 172, 173, 26, 100, 101, 27,
|
|
- /* 1100 */ 101, 27, 23, 122, 152, 26, 172, 173, 172, 173,
|
|
- /* 1110 */ 152, 112, 163, 72, 73, 74, 75, 76, 77, 78,
|
|
- /* 1120 */ 79, 80, 81, 82, 163, 84, 85, 86, 87, 88,
|
|
- /* 1130 */ 89, 90, 91, 92, 93, 94, 19, 20, 152, 22,
|
|
- /* 1140 */ 23, 152, 163, 65, 27, 196, 163, 19, 20, 23,
|
|
- /* 1150 */ 22, 213, 26, 19, 37, 27, 152, 196, 172, 173,
|
|
- /* 1160 */ 152, 172, 173, 27, 23, 37, 152, 26, 152, 97,
|
|
- /* 1170 */ 152, 97, 210, 56, 163, 196, 163, 163, 100, 196,
|
|
- /* 1180 */ 172, 173, 65, 152, 56, 68, 172, 173, 172, 173,
|
|
- /* 1190 */ 172, 173, 152, 65, 163, 163, 68, 23, 152, 234,
|
|
- /* 1200 */ 26, 152, 152, 172, 173, 88, 89, 196, 152, 196,
|
|
- /* 1210 */ 196, 152, 95, 96, 97, 98, 88, 89, 101, 152,
|
|
- /* 1220 */ 152, 207, 208, 95, 96, 97, 98, 196, 196, 101,
|
|
- /* 1230 */ 96, 233, 152, 97, 152, 152, 19, 20, 207, 22,
|
|
- /* 1240 */ 152, 152, 152, 191, 27, 152, 152, 152, 152, 132,
|
|
- /* 1250 */ 133, 134, 135, 136, 37, 152, 152, 152, 152, 152,
|
|
- /* 1260 */ 132, 133, 134, 135, 136, 210, 197, 210, 210, 198,
|
|
- /* 1270 */ 150, 184, 239, 56, 201, 214, 214, 201, 239, 180,
|
|
- /* 1280 */ 214, 227, 198, 38, 176, 68, 175, 175, 175, 122,
|
|
- /* 1290 */ 155, 200, 159, 19, 20, 40, 22, 159, 159, 22,
|
|
- /* 1300 */ 70, 27, 130, 243, 240, 88, 89, 90, 189, 18,
|
|
- /* 1310 */ 201, 37, 95, 96, 97, 98, 192, 5, 101, 192,
|
|
- /* 1320 */ 220, 240, 10, 11, 12, 13, 14, 159, 18, 17,
|
|
- /* 1330 */ 56, 158, 192, 201, 192, 220, 189, 189, 201, 159,
|
|
- /* 1340 */ 158, 137, 68, 31, 45, 33, 236, 159, 159, 132,
|
|
- /* 1350 */ 133, 134, 135, 136, 42, 158, 235, 22, 177, 159,
|
|
- /* 1360 */ 158, 158, 88, 89, 159, 107, 174, 55, 177, 95,
|
|
- /* 1370 */ 96, 97, 98, 174, 62, 101, 47, 65, 66, 106,
|
|
- /* 1380 */ 174, 125, 19, 20, 174, 22, 177, 176, 174, 182,
|
|
- /* 1390 */ 27, 216, 174, 174, 182, 107, 159, 22, 215, 215,
|
|
- /* 1400 */ 37, 216, 216, 216, 137, 215, 132, 133, 134, 135,
|
|
- /* 1410 */ 136, 215, 159, 177, 94, 177, 129, 224, 205, 56,
|
|
- /* 1420 */ 226, 126, 128, 203, 229, 204, 114, 229, 127, 202,
|
|
- /* 1430 */ 201, 68, 25, 162, 26, 13, 161, 153, 153, 6,
|
|
- /* 1440 */ 151, 151, 178, 151, 165, 165, 178, 165, 4, 3,
|
|
- /* 1450 */ 249, 88, 89, 141, 22, 142, 15, 249, 95, 96,
|
|
- /* 1460 */ 97, 98, 246, 67, 101, 16, 23, 120, 23, 131,
|
|
- /* 1470 */ 111, 123, 20, 16, 125, 1, 123, 131, 78, 78,
|
|
- /* 1480 */ 78, 78, 111, 96, 122, 35, 1, 5, 22, 107,
|
|
- /* 1490 */ 140, 53, 53, 26, 60, 132, 133, 134, 135, 136,
|
|
- /* 1500 */ 107, 43, 24, 20, 112, 19, 22, 52, 52, 105,
|
|
- /* 1510 */ 22, 22, 52, 23, 22, 22, 29, 23, 39, 23,
|
|
- /* 1520 */ 23, 26, 116, 22, 26, 23, 22, 122, 23, 23,
|
|
- /* 1530 */ 22, 96, 11, 124, 35, 26, 26, 23, 35, 23,
|
|
- /* 1540 */ 23, 23, 35, 23, 22, 26, 23, 22, 24, 122,
|
|
- /* 1550 */ 23, 22, 26, 22, 24, 23, 23, 23, 22, 122,
|
|
- /* 1560 */ 23, 15, 122, 1, 122,
|
|
+ /* 0 */ 184, 238, 239, 240, 238, 239, 240, 163, 155, 156,
|
|
+ /* 10 */ 157, 158, 159, 160, 163, 191, 192, 183, 165, 19,
|
|
+ /* 20 */ 167, 258, 202, 203, 200, 191, 163, 174, 184, 185,
|
|
+ /* 30 */ 174, 31, 163, 163, 171, 184, 185, 35, 175, 39,
|
|
+ /* 40 */ 179, 180, 181, 43, 44, 45, 46, 47, 48, 49,
|
|
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 206,
|
|
+ /* 60 */ 207, 163, 206, 207, 220, 163, 16, 163, 66, 163,
|
|
+ /* 70 */ 59, 270, 219, 229, 273, 219, 74, 208, 174, 223,
|
|
+ /* 80 */ 224, 163, 184, 185, 163, 232, 184, 185, 184, 185,
|
|
+ /* 90 */ 184, 185, 92, 93, 94, 95, 96, 97, 98, 99,
|
|
+ /* 100 */ 100, 101, 102, 233, 198, 184, 185, 96, 97, 163,
|
|
+ /* 110 */ 206, 207, 19, 163, 261, 104, 105, 106, 107, 198,
|
|
+ /* 120 */ 109, 119, 220, 219, 220, 274, 275, 77, 117, 79,
|
|
+ /* 130 */ 187, 229, 19, 229, 184, 185, 43, 44, 45, 46,
|
|
+ /* 140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
+ /* 150 */ 57, 233, 141, 134, 143, 102, 43, 44, 45, 46,
|
|
+ /* 160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
+ /* 170 */ 57, 152, 274, 216, 276, 218, 83, 163, 85, 233,
|
|
+ /* 180 */ 67, 238, 239, 240, 11, 92, 93, 94, 95, 96,
|
|
+ /* 190 */ 97, 98, 99, 100, 101, 102, 19, 54, 55, 56,
|
|
+ /* 200 */ 57, 58, 163, 26, 163, 92, 93, 94, 95, 96,
|
|
+ /* 210 */ 97, 98, 99, 100, 101, 102, 54, 55, 56, 57,
|
|
+ /* 220 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
|
|
+ /* 230 */ 53, 54, 55, 56, 57, 92, 93, 94, 95, 96,
|
|
+ /* 240 */ 97, 98, 99, 100, 101, 102, 69, 96, 97, 98,
|
|
+ /* 250 */ 99, 100, 101, 102, 92, 93, 94, 95, 96, 97,
|
|
+ /* 260 */ 98, 99, 100, 101, 102, 81, 179, 180, 181, 92,
|
|
+ /* 270 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
|
|
+ /* 280 */ 163, 267, 268, 163, 22, 23, 59, 163, 26, 19,
|
|
+ /* 290 */ 117, 118, 175, 109, 24, 59, 92, 93, 94, 95,
|
|
+ /* 300 */ 96, 97, 98, 99, 100, 101, 102, 268, 184, 185,
|
|
+ /* 310 */ 269, 127, 128, 43, 44, 45, 46, 47, 48, 49,
|
|
+ /* 320 */ 50, 51, 52, 53, 54, 55, 56, 57, 157, 158,
|
|
+ /* 330 */ 159, 160, 105, 106, 107, 163, 165, 59, 167, 184,
|
|
+ /* 340 */ 90, 105, 106, 107, 108, 174, 73, 111, 112, 113,
|
|
+ /* 350 */ 19, 22, 163, 91, 81, 163, 106, 121, 81, 132,
|
|
+ /* 360 */ 110, 16, 92, 93, 94, 95, 96, 97, 98, 99,
|
|
+ /* 370 */ 100, 101, 102, 184, 185, 255, 98, 206, 207, 26,
|
|
+ /* 380 */ 101, 102, 19, 105, 106, 107, 23, 198, 59, 116,
|
|
+ /* 390 */ 219, 141, 142, 143, 24, 163, 187, 205, 274, 275,
|
|
+ /* 400 */ 127, 128, 182, 232, 127, 128, 43, 44, 45, 46,
|
|
+ /* 410 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
|
+ /* 420 */ 57, 158, 77, 160, 79, 59, 26, 182, 165, 59,
|
|
+ /* 430 */ 167, 199, 261, 102, 105, 106, 107, 174, 72, 108,
|
|
+ /* 440 */ 109, 110, 111, 112, 113, 114, 59, 238, 239, 240,
|
|
+ /* 450 */ 123, 120, 125, 126, 163, 92, 93, 94, 95, 96,
|
|
+ /* 460 */ 97, 98, 99, 100, 101, 102, 163, 163, 163, 206,
|
|
+ /* 470 */ 207, 105, 106, 107, 254, 19, 106, 90, 197, 23,
|
|
+ /* 480 */ 127, 128, 219, 238, 239, 240, 22, 184, 185, 184,
|
|
+ /* 490 */ 185, 22, 105, 106, 149, 232, 205, 110, 163, 43,
|
|
+ /* 500 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
|
+ /* 510 */ 54, 55, 56, 57, 98, 99, 100, 101, 102, 184,
|
|
+ /* 520 */ 185, 163, 53, 59, 261, 220, 117, 118, 141, 142,
|
|
+ /* 530 */ 143, 131, 174, 59, 229, 116, 117, 118, 163, 59,
|
|
+ /* 540 */ 163, 163, 184, 185, 59, 242, 72, 22, 92, 93,
|
|
+ /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 184,
|
|
+ /* 560 */ 185, 24, 184, 185, 206, 207, 202, 203, 19, 105,
|
|
+ /* 570 */ 106, 107, 23, 198, 22, 174, 198, 219, 220, 105,
|
|
+ /* 580 */ 106, 107, 96, 97, 59, 105, 106, 107, 22, 174,
|
|
+ /* 590 */ 59, 106, 43, 44, 45, 46, 47, 48, 49, 50,
|
|
+ /* 600 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 12,
|
|
+ /* 610 */ 108, 59, 132, 111, 112, 113, 46, 47, 48, 49,
|
|
+ /* 620 */ 219, 206, 207, 121, 27, 59, 163, 141, 207, 143,
|
|
+ /* 630 */ 105, 106, 107, 163, 219, 234, 105, 106, 107, 42,
|
|
+ /* 640 */ 219, 92, 93, 94, 95, 96, 97, 98, 99, 100,
|
|
+ /* 650 */ 101, 102, 76, 163, 184, 185, 163, 105, 106, 107,
|
|
+ /* 660 */ 63, 19, 86, 163, 163, 23, 163, 130, 205, 21,
|
|
+ /* 670 */ 73, 105, 106, 107, 184, 185, 163, 184, 185, 237,
|
|
+ /* 680 */ 110, 180, 181, 180, 181, 43, 44, 45, 46, 47,
|
|
+ /* 690 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
|
+ /* 700 */ 174, 163, 163, 22, 23, 163, 163, 26, 22, 23,
|
|
+ /* 710 */ 220, 29, 73, 220, 272, 33, 22, 163, 24, 19,
|
|
+ /* 720 */ 174, 208, 259, 184, 185, 19, 184, 185, 80, 175,
|
|
+ /* 730 */ 230, 174, 206, 207, 92, 93, 94, 95, 96, 97,
|
|
+ /* 740 */ 98, 99, 100, 101, 102, 219, 46, 65, 247, 195,
|
|
+ /* 750 */ 247, 197, 206, 207, 19, 116, 117, 118, 23, 220,
|
|
+ /* 760 */ 112, 174, 220, 206, 207, 219, 22, 174, 24, 174,
|
|
+ /* 770 */ 22, 23, 91, 264, 265, 168, 219, 91, 43, 44,
|
|
+ /* 780 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
|
+ /* 790 */ 55, 56, 57, 206, 207, 12, 163, 149, 255, 206,
|
|
+ /* 800 */ 207, 206, 207, 59, 104, 23, 219, 163, 26, 163,
|
|
+ /* 810 */ 27, 105, 219, 163, 219, 163, 211, 184, 185, 163,
|
|
+ /* 820 */ 120, 163, 146, 163, 148, 42, 221, 92, 93, 94,
|
|
+ /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 91,
|
|
+ /* 840 */ 184, 185, 184, 185, 184, 185, 63, 19, 163, 205,
|
|
+ /* 850 */ 106, 23, 245, 163, 208, 248, 116, 117, 118, 184,
|
|
+ /* 860 */ 185, 163, 163, 7, 8, 9, 163, 19, 26, 184,
|
|
+ /* 870 */ 185, 43, 44, 45, 46, 47, 48, 49, 50, 51,
|
|
+ /* 880 */ 52, 53, 54, 55, 56, 57, 163, 184, 185, 107,
|
|
+ /* 890 */ 163, 43, 44, 45, 46, 47, 48, 49, 50, 51,
|
|
+ /* 900 */ 52, 53, 54, 55, 56, 57, 208, 255, 177, 178,
|
|
+ /* 910 */ 163, 184, 185, 163, 132, 163, 141, 163, 143, 22,
|
|
+ /* 920 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
|
|
+ /* 930 */ 102, 184, 185, 163, 184, 185, 184, 185, 184, 185,
|
|
+ /* 940 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
|
|
+ /* 950 */ 102, 163, 163, 163, 184, 185, 163, 115, 163, 163,
|
|
+ /* 960 */ 163, 163, 15, 163, 163, 163, 163, 163, 23, 163,
|
|
+ /* 970 */ 163, 26, 184, 185, 184, 185, 163, 184, 185, 184,
|
|
+ /* 980 */ 185, 184, 185, 163, 184, 185, 184, 185, 184, 185,
|
|
+ /* 990 */ 184, 185, 163, 96, 97, 147, 163, 184, 185, 163,
|
|
+ /* 1000 */ 199, 163, 163, 205, 184, 185, 163, 60, 163, 141,
|
|
+ /* 1010 */ 163, 143, 163, 184, 185, 19, 163, 184, 185, 230,
|
|
+ /* 1020 */ 184, 185, 184, 185, 206, 207, 230, 184, 185, 184,
|
|
+ /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 219, 231, 43,
|
|
+ /* 1040 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
|
+ /* 1050 */ 54, 55, 56, 57, 163, 26, 163, 184, 185, 43,
|
|
+ /* 1060 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
|
+ /* 1070 */ 54, 55, 56, 57, 163, 184, 185, 184, 185, 163,
|
|
+ /* 1080 */ 182, 163, 163, 163, 163, 163, 22, 163, 92, 93,
|
|
+ /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163,
|
|
+ /* 1100 */ 184, 185, 184, 185, 163, 184, 185, 163, 92, 93,
|
|
+ /* 1110 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163,
|
|
+ /* 1120 */ 184, 185, 98, 59, 163, 184, 185, 205, 184, 185,
|
|
+ /* 1130 */ 23, 206, 207, 26, 163, 26, 107, 153, 154, 237,
|
|
+ /* 1140 */ 184, 185, 231, 147, 219, 184, 185, 249, 124, 127,
|
|
+ /* 1150 */ 128, 231, 254, 129, 163, 231, 177, 178, 262, 263,
|
|
+ /* 1160 */ 118, 132, 19, 19, 46, 223, 224, 31, 24, 23,
|
|
+ /* 1170 */ 106, 124, 26, 22, 272, 39, 129, 23, 109, 110,
|
|
+ /* 1180 */ 26, 163, 140, 19, 22, 234, 59, 43, 44, 45,
|
|
+ /* 1190 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
|
|
+ /* 1200 */ 56, 57, 231, 7, 8, 193, 59, 43, 44, 45,
|
|
+ /* 1210 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
|
|
+ /* 1220 */ 56, 57, 104, 61, 23, 23, 23, 26, 26, 26,
|
|
+ /* 1230 */ 163, 23, 23, 106, 26, 26, 92, 93, 94, 95,
|
|
+ /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 138, 105, 23,
|
|
+ /* 1250 */ 59, 23, 26, 106, 26, 163, 92, 93, 94, 95,
|
|
+ /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 110, 23, 23,
|
|
+ /* 1270 */ 23, 26, 26, 26, 163, 163, 19, 120, 163, 163,
|
|
+ /* 1280 */ 163, 130, 163, 163, 163, 163, 163, 163, 163, 193,
|
|
+ /* 1290 */ 193, 163, 163, 163, 163, 225, 19, 106, 163, 222,
|
|
+ /* 1300 */ 163, 44, 45, 46, 47, 48, 49, 50, 51, 52,
|
|
+ /* 1310 */ 53, 54, 55, 56, 57, 163, 163, 203, 163, 163,
|
|
+ /* 1320 */ 222, 163, 45, 46, 47, 48, 49, 50, 51, 52,
|
|
+ /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 163, 163, 163,
|
|
+ /* 1340 */ 251, 250, 209, 19, 20, 182, 22, 161, 222, 92,
|
|
+ /* 1350 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
|
|
+ /* 1360 */ 36, 222, 222, 260, 226, 188, 256, 226, 187, 92,
|
|
+ /* 1370 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
|
|
+ /* 1380 */ 210, 213, 213, 59, 213, 196, 192, 187, 256, 244,
|
|
+ /* 1390 */ 212, 187, 226, 19, 20, 71, 22, 210, 166, 60,
|
|
+ /* 1400 */ 130, 170, 260, 170, 38, 81, 257, 257, 170, 104,
|
|
+ /* 1410 */ 36, 22, 43, 201, 90, 236, 138, 235, 213, 18,
|
|
+ /* 1420 */ 96, 97, 48, 204, 204, 204, 204, 103, 170, 105,
|
|
+ /* 1430 */ 106, 107, 18, 59, 110, 169, 213, 213, 201, 170,
|
|
+ /* 1440 */ 201, 169, 236, 213, 146, 71, 235, 62, 253, 252,
|
|
+ /* 1450 */ 170, 127, 128, 169, 22, 170, 82, 189, 169, 104,
|
|
+ /* 1460 */ 170, 87, 169, 189, 90, 141, 142, 143, 144, 145,
|
|
+ /* 1470 */ 96, 97, 186, 186, 186, 64, 194, 103, 186, 105,
|
|
+ /* 1480 */ 106, 107, 115, 189, 110, 188, 186, 186, 19, 20,
|
|
+ /* 1490 */ 194, 22, 186, 189, 102, 246, 246, 189, 133, 228,
|
|
+ /* 1500 */ 104, 228, 227, 227, 170, 36, 134, 228, 227, 19,
|
|
+ /* 1510 */ 20, 228, 22, 84, 271, 141, 142, 143, 144, 145,
|
|
+ /* 1520 */ 0, 1, 2, 216, 22, 5, 36, 137, 59, 227,
|
|
+ /* 1530 */ 10, 11, 12, 13, 14, 217, 269, 17, 216, 22,
|
|
+ /* 1540 */ 71, 170, 243, 146, 241, 217, 136, 215, 135, 59,
|
|
+ /* 1550 */ 30, 82, 32, 25, 214, 213, 87, 173, 26, 90,
|
|
+ /* 1560 */ 40, 71, 13, 172, 164, 96, 97, 164, 6, 162,
|
|
+ /* 1570 */ 162, 162, 103, 263, 105, 106, 107, 266, 266, 110,
|
|
+ /* 1580 */ 90, 176, 176, 190, 182, 190, 96, 97, 98, 4,
|
|
+ /* 1590 */ 70, 176, 3, 103, 182, 105, 106, 107, 78, 182,
|
|
+ /* 1600 */ 110, 81, 182, 182, 182, 182, 182, 151, 88, 22,
|
|
+ /* 1610 */ 141, 142, 143, 144, 145, 15, 89, 16, 23, 23,
|
|
+ /* 1620 */ 128, 19, 20, 139, 22, 119, 131, 24, 20, 133,
|
|
+ /* 1630 */ 16, 141, 142, 143, 144, 145, 1, 140, 36, 131,
|
|
+ /* 1640 */ 119, 61, 122, 37, 139, 53, 53, 127, 128, 119,
|
|
+ /* 1650 */ 53, 53, 105, 34, 130, 1, 5, 104, 22, 149,
|
|
+ /* 1660 */ 26, 59, 68, 75, 41, 130, 24, 68, 104, 20,
|
|
+ /* 1670 */ 150, 19, 120, 71, 114, 22, 67, 22, 22, 67,
|
|
+ /* 1680 */ 23, 22, 22, 67, 82, 37, 28, 23, 138, 87,
|
|
+ /* 1690 */ 22, 153, 90, 23, 23, 26, 23, 22, 96, 97,
|
|
+ /* 1700 */ 24, 23, 22, 24, 130, 103, 23, 105, 106, 107,
|
|
+ /* 1710 */ 1, 2, 110, 23, 5, 105, 34, 22, 132, 10,
|
|
+ /* 1720 */ 11, 12, 13, 14, 26, 34, 17, 34, 85, 83,
|
|
+ /* 1730 */ 44, 19, 20, 23, 22, 24, 75, 34, 23, 30,
|
|
+ /* 1740 */ 26, 32, 26, 141, 142, 143, 144, 145, 36, 40,
|
|
+ /* 1750 */ 23, 23, 23, 23, 11, 23, 22, 26, 22, 22,
|
|
+ /* 1760 */ 22, 19, 20, 23, 22, 26, 15, 23, 22, 124,
|
|
+ /* 1770 */ 130, 59, 23, 1, 130, 277, 277, 130, 36, 70,
|
|
+ /* 1780 */ 130, 277, 277, 71, 277, 277, 277, 78, 277, 277,
|
|
+ /* 1790 */ 81, 277, 277, 277, 277, 277, 277, 88, 277, 277,
|
|
+ /* 1800 */ 277, 59, 90, 277, 277, 277, 277, 277, 96, 97,
|
|
+ /* 1810 */ 277, 277, 277, 71, 277, 103, 277, 105, 106, 107,
|
|
+ /* 1820 */ 277, 277, 110, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 1830 */ 277, 122, 90, 277, 277, 277, 127, 128, 96, 97,
|
|
+ /* 1840 */ 277, 277, 277, 277, 277, 103, 277, 105, 106, 107,
|
|
+ /* 1850 */ 277, 277, 110, 141, 142, 143, 144, 145, 277, 150,
|
|
+ /* 1860 */ 277, 277, 277, 5, 277, 277, 277, 277, 10, 11,
|
|
+ /* 1870 */ 12, 13, 14, 277, 277, 17, 277, 277, 277, 277,
|
|
+ /* 1880 */ 277, 277, 277, 141, 142, 143, 144, 145, 30, 277,
|
|
+ /* 1890 */ 32, 277, 277, 277, 277, 277, 277, 277, 40, 277,
|
|
+ /* 1900 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 1910 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 1920 */ 277, 277, 277, 277, 277, 277, 277, 277, 70, 277,
|
|
+ /* 1930 */ 277, 277, 277, 277, 277, 277, 78, 277, 277, 81,
|
|
+ /* 1940 */ 277, 277, 277, 277, 277, 277, 88, 277, 277, 277,
|
|
+ /* 1950 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 1960 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 1970 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 1980 */ 122, 277, 277, 277, 277, 127, 128, 277, 277, 277,
|
|
+ /* 1990 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
+ /* 2000 */ 277, 277, 277, 277, 277, 277, 277, 277, 150, 277,
|
|
+ /* 2010 */ 277, 277, 277, 277, 277, 277, 277, 277, 277,
|
|
};
|
|
-#define YY_SHIFT_USE_DFLT (1565)
|
|
-#define YY_SHIFT_COUNT (454)
|
|
-#define YY_SHIFT_MIN (-114)
|
|
-#define YY_SHIFT_MAX (1562)
|
|
-static const short yy_shift_ofst[] = {
|
|
- /* 0 */ 5, 1117, 1312, 1128, 1274, 1274, 1274, 1274, 61, -19,
|
|
- /* 10 */ 57, 57, 183, 1274, 1274, 1274, 1274, 1274, 1274, 1274,
|
|
- /* 20 */ 66, 66, 201, -29, 331, 318, 133, 259, 335, 411,
|
|
- /* 30 */ 487, 563, 639, 689, 765, 841, 891, 891, 891, 891,
|
|
- /* 40 */ 891, 891, 891, 891, 891, 891, 891, 891, 891, 891,
|
|
- /* 50 */ 891, 891, 891, 941, 891, 991, 1041, 1041, 1217, 1274,
|
|
- /* 60 */ 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274,
|
|
- /* 70 */ 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274,
|
|
- /* 80 */ 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274,
|
|
- /* 90 */ 1363, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274, 1274,
|
|
- /* 100 */ 1274, 1274, 1274, 1274, -70, -47, -47, -47, -47, -47,
|
|
- /* 110 */ 24, 11, 146, 296, 524, 444, 529, 529, 296, 3,
|
|
- /* 120 */ 2, -30, 1565, 1565, 1565, -17, -17, -17, 145, 145,
|
|
- /* 130 */ 497, 497, 265, 603, 653, 296, 296, 296, 296, 296,
|
|
- /* 140 */ 296, 296, 296, 296, 296, 296, 296, 296, 296, 296,
|
|
- /* 150 */ 296, 296, 296, 296, 296, 701, 1078, 147, 147, 2,
|
|
- /* 160 */ 164, 164, 164, 164, 164, 164, 1565, 1565, 1565, 223,
|
|
- /* 170 */ 56, 56, 268, 269, 220, 347, 351, 415, 359, 296,
|
|
- /* 180 */ 296, 296, 296, 296, 296, 296, 296, 296, 296, 296,
|
|
- /* 190 */ 296, 296, 296, 296, 296, 632, 632, 632, 296, 296,
|
|
- /* 200 */ 498, 296, 296, 296, 570, 296, 296, 654, 296, 296,
|
|
- /* 210 */ 296, 296, 296, 296, 296, 296, 296, 296, 636, 200,
|
|
- /* 220 */ 596, 596, 596, 575, -114, 971, 740, 454, 503, 503,
|
|
- /* 230 */ 1134, 454, 1134, 353, 588, 628, 762, 503, 189, 762,
|
|
- /* 240 */ 762, 916, 330, 668, 1245, 1167, 1167, 1255, 1255, 1167,
|
|
- /* 250 */ 1277, 1230, 1172, 1291, 1291, 1291, 1291, 1167, 1310, 1172,
|
|
- /* 260 */ 1277, 1230, 1230, 1172, 1167, 1310, 1204, 1299, 1167, 1167,
|
|
- /* 270 */ 1310, 1335, 1167, 1310, 1167, 1310, 1335, 1258, 1258, 1258,
|
|
- /* 280 */ 1329, 1335, 1258, 1273, 1258, 1329, 1258, 1258, 1256, 1288,
|
|
- /* 290 */ 1256, 1288, 1256, 1288, 1256, 1288, 1167, 1375, 1167, 1267,
|
|
- /* 300 */ 1335, 1320, 1320, 1335, 1287, 1295, 1294, 1301, 1172, 1407,
|
|
- /* 310 */ 1408, 1422, 1422, 1433, 1433, 1433, 1565, 1565, 1565, 1565,
|
|
- /* 320 */ 1565, 1565, 1565, 1565, 558, 537, 684, 719, 734, 799,
|
|
- /* 330 */ 840, 1019, 14, 1020, 1021, 1025, 1026, 1027, 1070, 1072,
|
|
- /* 340 */ 997, 1047, 999, 1079, 1126, 1074, 1141, 694, 819, 1174,
|
|
- /* 350 */ 1136, 981, 1444, 1446, 1432, 1313, 1441, 1396, 1449, 1443,
|
|
- /* 360 */ 1445, 1347, 1338, 1359, 1348, 1452, 1349, 1457, 1474, 1353,
|
|
- /* 370 */ 1346, 1400, 1401, 1402, 1403, 1371, 1387, 1450, 1362, 1485,
|
|
- /* 380 */ 1482, 1466, 1382, 1350, 1438, 1467, 1439, 1434, 1458, 1393,
|
|
- /* 390 */ 1478, 1483, 1486, 1392, 1404, 1484, 1455, 1488, 1489, 1490,
|
|
- /* 400 */ 1492, 1456, 1487, 1493, 1460, 1479, 1494, 1496, 1497, 1495,
|
|
- /* 410 */ 1406, 1501, 1502, 1504, 1498, 1405, 1505, 1506, 1435, 1499,
|
|
- /* 420 */ 1508, 1409, 1509, 1503, 1510, 1507, 1514, 1509, 1516, 1517,
|
|
- /* 430 */ 1518, 1519, 1520, 1522, 1521, 1523, 1525, 1524, 1526, 1527,
|
|
- /* 440 */ 1529, 1530, 1526, 1532, 1531, 1533, 1534, 1536, 1427, 1437,
|
|
- /* 450 */ 1440, 1442, 1537, 1546, 1562,
|
|
+#define YY_SHIFT_COUNT (520)
|
|
+#define YY_SHIFT_MIN (0)
|
|
+#define YY_SHIFT_MAX (1858)
|
|
+static const unsigned short int yy_shift_ofst[] = {
|
|
+ /* 0 */ 1709, 1520, 1858, 1324, 1324, 277, 1374, 1469, 1602, 1712,
|
|
+ /* 10 */ 1712, 1712, 273, 0, 0, 113, 1016, 1712, 1712, 1712,
|
|
+ /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 11, 11, 236,
|
|
+ /* 30 */ 184, 277, 277, 277, 277, 277, 277, 93, 177, 270,
|
|
+ /* 40 */ 363, 456, 549, 642, 735, 828, 848, 996, 1144, 1016,
|
|
+ /* 50 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016,
|
|
+ /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257, 1277,
|
|
+ /* 70 */ 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
|
|
+ /* 80 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
|
|
+ /* 90 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
|
|
+ /* 100 */ 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712, 1712, 1712,
|
|
+ /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143, 162, 162,
|
|
+ /* 120 */ 162, 162, 162, 204, 151, 416, 531, 648, 700, 531,
|
|
+ /* 130 */ 486, 486, 531, 353, 353, 353, 353, 409, 279, 53,
|
|
+ /* 140 */ 2009, 2009, 331, 331, 331, 329, 366, 329, 329, 597,
|
|
+ /* 150 */ 597, 464, 474, 262, 681, 531, 531, 531, 531, 531,
|
|
+ /* 160 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531,
|
|
+ /* 170 */ 531, 531, 531, 531, 531, 531, 531, 173, 485, 984,
|
|
+ /* 180 */ 984, 576, 485, 19, 1022, 2009, 2009, 2009, 387, 250,
|
|
+ /* 190 */ 250, 525, 502, 278, 552, 227, 480, 566, 531, 531,
|
|
+ /* 200 */ 531, 531, 531, 531, 531, 531, 531, 531, 639, 531,
|
|
+ /* 210 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531,
|
|
+ /* 220 */ 531, 2, 2, 2, 531, 531, 531, 531, 782, 531,
|
|
+ /* 230 */ 531, 531, 744, 531, 531, 783, 531, 531, 531, 531,
|
|
+ /* 240 */ 531, 531, 531, 531, 419, 682, 327, 370, 370, 370,
|
|
+ /* 250 */ 370, 1029, 327, 327, 1024, 897, 856, 947, 1109, 706,
|
|
+ /* 260 */ 706, 1143, 1109, 1109, 1143, 842, 945, 1118, 1136, 1136,
|
|
+ /* 270 */ 1136, 706, 676, 400, 1047, 694, 1339, 1270, 1270, 1366,
|
|
+ /* 280 */ 1366, 1270, 1305, 1389, 1369, 1278, 1401, 1401, 1401, 1401,
|
|
+ /* 290 */ 1270, 1414, 1278, 1278, 1305, 1389, 1369, 1369, 1278, 1270,
|
|
+ /* 300 */ 1414, 1298, 1385, 1270, 1414, 1432, 1270, 1414, 1270, 1414,
|
|
+ /* 310 */ 1432, 1355, 1355, 1355, 1411, 1432, 1355, 1367, 1355, 1411,
|
|
+ /* 320 */ 1355, 1355, 1432, 1392, 1392, 1432, 1365, 1396, 1365, 1396,
|
|
+ /* 330 */ 1365, 1396, 1365, 1396, 1270, 1372, 1429, 1502, 1390, 1372,
|
|
+ /* 340 */ 1517, 1270, 1397, 1390, 1410, 1413, 1278, 1528, 1532, 1549,
|
|
+ /* 350 */ 1549, 1562, 1562, 1562, 2009, 2009, 2009, 2009, 2009, 2009,
|
|
+ /* 360 */ 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009,
|
|
+ /* 370 */ 570, 345, 686, 748, 50, 740, 1064, 1107, 469, 537,
|
|
+ /* 380 */ 1042, 1146, 1162, 1154, 1201, 1202, 1203, 1208, 1209, 1127,
|
|
+ /* 390 */ 1069, 1196, 1157, 1147, 1226, 1228, 1245, 775, 868, 1246,
|
|
+ /* 400 */ 1247, 1191, 1151, 1585, 1589, 1587, 1456, 1600, 1527, 1601,
|
|
+ /* 410 */ 1595, 1596, 1492, 1484, 1506, 1603, 1495, 1608, 1496, 1614,
|
|
+ /* 420 */ 1635, 1508, 1497, 1521, 1580, 1606, 1505, 1592, 1593, 1597,
|
|
+ /* 430 */ 1598, 1530, 1547, 1619, 1524, 1654, 1651, 1636, 1553, 1510,
|
|
+ /* 440 */ 1594, 1634, 1599, 1588, 1623, 1535, 1564, 1642, 1649, 1652,
|
|
+ /* 450 */ 1552, 1560, 1653, 1609, 1655, 1656, 1657, 1659, 1612, 1658,
|
|
+ /* 460 */ 1660, 1616, 1648, 1664, 1550, 1668, 1538, 1670, 1671, 1669,
|
|
+ /* 470 */ 1673, 1675, 1676, 1678, 1680, 1679, 1574, 1683, 1690, 1610,
|
|
+ /* 480 */ 1682, 1695, 1586, 1698, 1691, 1698, 1693, 1643, 1661, 1646,
|
|
+ /* 490 */ 1686, 1710, 1711, 1714, 1716, 1703, 1715, 1698, 1727, 1728,
|
|
+ /* 500 */ 1729, 1730, 1731, 1732, 1734, 1743, 1736, 1737, 1740, 1744,
|
|
+ /* 510 */ 1738, 1746, 1739, 1645, 1640, 1644, 1647, 1650, 1749, 1751,
|
|
+ /* 520 */ 1772,
|
|
};
|
|
-#define YY_REDUCE_USE_DFLT (-174)
|
|
-#define YY_REDUCE_COUNT (323)
|
|
-#define YY_REDUCE_MIN (-173)
|
|
-#define YY_REDUCE_MAX (1292)
|
|
+#define YY_REDUCE_COUNT (369)
|
|
+#define YY_REDUCE_MIN (-237)
|
|
+#define YY_REDUCE_MAX (1424)
|
|
static const short yy_reduce_ofst[] = {
|
|
- /* 0 */ -119, 1014, 131, 1031, -12, 225, 228, 300, -40, -45,
|
|
- /* 10 */ 243, 256, 293, 129, 218, 418, 79, 376, 433, 298,
|
|
- /* 20 */ 16, 137, 367, 323, -38, 391, -173, -173, -173, -173,
|
|
- /* 30 */ -173, -173, -173, -173, -173, -173, -173, -173, -173, -173,
|
|
- /* 40 */ -173, -173, -173, -173, -173, -173, -173, -173, -173, -173,
|
|
- /* 50 */ -173, -173, -173, -173, -173, -173, -173, -173, 374, 437,
|
|
- /* 60 */ 443, 508, 513, 522, 532, 582, 584, 620, 633, 635,
|
|
- /* 70 */ 637, 644, 646, 648, 650, 652, 659, 661, 696, 709,
|
|
- /* 80 */ 711, 714, 720, 722, 724, 726, 728, 733, 772, 784,
|
|
- /* 90 */ 786, 822, 834, 836, 884, 886, 922, 934, 936, 986,
|
|
- /* 100 */ 989, 1008, 1016, 1018, -173, -173, -173, -173, -173, -173,
|
|
- /* 110 */ -173, -173, -173, 544, -37, 274, 299, 501, 161, -173,
|
|
- /* 120 */ 193, -173, -173, -173, -173, 22, 22, 22, 64, 141,
|
|
- /* 130 */ 212, 342, 208, 504, 504, 132, 494, 606, 677, 678,
|
|
- /* 140 */ 750, 794, 796, -58, 32, 383, 660, 737, 386, 787,
|
|
- /* 150 */ 800, 441, 872, 224, 850, 803, 949, 624, 830, 669,
|
|
- /* 160 */ 961, 979, 983, 1011, 1013, 1032, 753, 789, 321, 94,
|
|
- /* 170 */ 116, 304, 375, 210, 388, 392, 478, 545, 649, 721,
|
|
- /* 180 */ 727, 736, 752, 795, 853, 952, 958, 1004, 1040, 1046,
|
|
- /* 190 */ 1049, 1050, 1056, 1059, 1067, 559, 774, 811, 1068, 1080,
|
|
- /* 200 */ 938, 1082, 1083, 1088, 962, 1089, 1090, 1052, 1093, 1094,
|
|
- /* 210 */ 1095, 388, 1096, 1103, 1104, 1105, 1106, 1107, 965, 998,
|
|
- /* 220 */ 1055, 1057, 1058, 938, 1069, 1071, 1120, 1073, 1061, 1062,
|
|
- /* 230 */ 1033, 1076, 1039, 1108, 1087, 1099, 1111, 1066, 1054, 1112,
|
|
- /* 240 */ 1113, 1091, 1084, 1135, 1060, 1133, 1138, 1064, 1081, 1139,
|
|
- /* 250 */ 1100, 1119, 1109, 1124, 1127, 1140, 1142, 1168, 1173, 1132,
|
|
- /* 260 */ 1115, 1147, 1148, 1137, 1180, 1182, 1110, 1121, 1188, 1189,
|
|
- /* 270 */ 1197, 1181, 1200, 1202, 1205, 1203, 1191, 1192, 1199, 1206,
|
|
- /* 280 */ 1207, 1209, 1210, 1211, 1214, 1212, 1218, 1219, 1175, 1183,
|
|
- /* 290 */ 1185, 1184, 1186, 1190, 1187, 1196, 1237, 1193, 1253, 1194,
|
|
- /* 300 */ 1236, 1195, 1198, 1238, 1213, 1221, 1220, 1227, 1229, 1271,
|
|
- /* 310 */ 1275, 1284, 1285, 1289, 1290, 1292, 1201, 1208, 1216, 1279,
|
|
- /* 320 */ 1280, 1264, 1268, 1282,
|
|
+ /* 0 */ -147, 171, 263, -96, 358, -144, -149, -102, 124, -156,
|
|
+ /* 10 */ -98, 305, 401, -57, 209, -237, 245, -94, -79, 189,
|
|
+ /* 20 */ 375, 490, 493, 378, 303, 539, 542, 501, 503, 554,
|
|
+ /* 30 */ 415, 526, 546, 557, 587, 593, 595, -234, -234, -234,
|
|
+ /* 40 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234,
|
|
+ /* 50 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234,
|
|
+ /* 60 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234,
|
|
+ /* 70 */ -234, -50, 335, 470, 633, 656, 658, 660, 675, 685,
|
|
+ /* 80 */ 703, 727, 747, 750, 752, 754, 770, 788, 790, 793,
|
|
+ /* 90 */ 795, 797, 800, 802, 804, 806, 813, 820, 829, 833,
|
|
+ /* 100 */ 836, 838, 843, 845, 847, 849, 873, 891, 893, 916,
|
|
+ /* 110 */ 918, 921, 936, 941, 944, 956, 961, -234, -234, -234,
|
|
+ /* 120 */ -234, -234, -234, -234, -234, -234, 463, 607, -176, 14,
|
|
+ /* 130 */ -139, 87, -137, 818, 925, 818, 925, 898, -234, -234,
|
|
+ /* 140 */ -234, -234, -166, -166, -166, -130, -131, -82, -54, -180,
|
|
+ /* 150 */ 364, 41, 513, 509, 509, 117, 500, 789, 796, 646,
|
|
+ /* 160 */ 192, 291, 644, 798, 120, 807, 543, 911, 920, 652,
|
|
+ /* 170 */ 924, 922, 232, 698, 801, 971, 39, 220, 731, 442,
|
|
+ /* 180 */ 902, -199, 979, -43, 421, 896, 942, 605, -184, -126,
|
|
+ /* 190 */ 155, 172, 281, 304, 377, 538, 650, 690, 699, 723,
|
|
+ /* 200 */ 803, 839, 853, 919, 991, 1018, 1067, 1092, 951, 1111,
|
|
+ /* 210 */ 1112, 1115, 1116, 1117, 1119, 1120, 1121, 1122, 1123, 1124,
|
|
+ /* 220 */ 1125, 1012, 1096, 1097, 1128, 1129, 1130, 1131, 1070, 1135,
|
|
+ /* 230 */ 1137, 1152, 1077, 1153, 1155, 1114, 1156, 304, 1158, 1172,
|
|
+ /* 240 */ 1173, 1174, 1175, 1176, 1089, 1091, 1133, 1098, 1126, 1139,
|
|
+ /* 250 */ 1140, 1070, 1133, 1133, 1170, 1163, 1186, 1103, 1168, 1138,
|
|
+ /* 260 */ 1141, 1110, 1169, 1171, 1132, 1177, 1189, 1194, 1181, 1200,
|
|
+ /* 270 */ 1204, 1166, 1145, 1178, 1187, 1232, 1142, 1231, 1233, 1149,
|
|
+ /* 280 */ 1150, 1238, 1179, 1182, 1212, 1205, 1219, 1220, 1221, 1222,
|
|
+ /* 290 */ 1258, 1266, 1223, 1224, 1206, 1211, 1237, 1239, 1230, 1269,
|
|
+ /* 300 */ 1272, 1195, 1197, 1280, 1284, 1268, 1285, 1289, 1290, 1293,
|
|
+ /* 310 */ 1274, 1286, 1287, 1288, 1282, 1294, 1292, 1297, 1300, 1296,
|
|
+ /* 320 */ 1301, 1306, 1304, 1249, 1250, 1308, 1271, 1275, 1273, 1276,
|
|
+ /* 330 */ 1279, 1281, 1283, 1302, 1334, 1307, 1243, 1267, 1318, 1322,
|
|
+ /* 340 */ 1303, 1371, 1299, 1328, 1332, 1340, 1342, 1384, 1391, 1400,
|
|
+ /* 350 */ 1403, 1407, 1408, 1409, 1311, 1312, 1310, 1405, 1402, 1412,
|
|
+ /* 360 */ 1417, 1420, 1406, 1393, 1395, 1421, 1422, 1423, 1424, 1415,
|
|
};
|
|
static const YYACTIONTYPE yy_default[] = {
|
|
- /* 0 */ 1270, 1260, 1260, 1260, 1193, 1193, 1193, 1193, 1260, 1088,
|
|
- /* 10 */ 1117, 1117, 1244, 1322, 1322, 1322, 1322, 1322, 1322, 1192,
|
|
- /* 20 */ 1322, 1322, 1322, 1322, 1260, 1092, 1123, 1322, 1322, 1322,
|
|
- /* 30 */ 1322, 1194, 1195, 1322, 1322, 1322, 1243, 1245, 1133, 1132,
|
|
- /* 40 */ 1131, 1130, 1226, 1104, 1128, 1121, 1125, 1194, 1188, 1189,
|
|
- /* 50 */ 1187, 1191, 1195, 1322, 1124, 1158, 1172, 1157, 1322, 1322,
|
|
- /* 60 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 70 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 80 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 90 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 100 */ 1322, 1322, 1322, 1322, 1166, 1171, 1178, 1170, 1167, 1160,
|
|
- /* 110 */ 1159, 1161, 1162, 1322, 1011, 1059, 1322, 1322, 1322, 1163,
|
|
- /* 120 */ 1322, 1164, 1175, 1174, 1173, 1251, 1278, 1277, 1322, 1322,
|
|
- /* 130 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 140 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 150 */ 1322, 1322, 1322, 1322, 1322, 1270, 1260, 1017, 1017, 1322,
|
|
- /* 160 */ 1260, 1260, 1260, 1260, 1260, 1260, 1256, 1092, 1083, 1322,
|
|
- /* 170 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 180 */ 1248, 1246, 1322, 1208, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 190 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 200 */ 1322, 1322, 1322, 1322, 1088, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 210 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1272, 1322, 1221,
|
|
- /* 220 */ 1088, 1088, 1088, 1090, 1072, 1082, 997, 1127, 1106, 1106,
|
|
- /* 230 */ 1311, 1127, 1311, 1034, 1292, 1031, 1117, 1106, 1190, 1117,
|
|
- /* 240 */ 1117, 1089, 1082, 1322, 1314, 1097, 1097, 1313, 1313, 1097,
|
|
- /* 250 */ 1138, 1062, 1127, 1068, 1068, 1068, 1068, 1097, 1008, 1127,
|
|
- /* 260 */ 1138, 1062, 1062, 1127, 1097, 1008, 1225, 1308, 1097, 1097,
|
|
- /* 270 */ 1008, 1201, 1097, 1008, 1097, 1008, 1201, 1060, 1060, 1060,
|
|
- /* 280 */ 1049, 1201, 1060, 1034, 1060, 1049, 1060, 1060, 1110, 1105,
|
|
- /* 290 */ 1110, 1105, 1110, 1105, 1110, 1105, 1097, 1196, 1097, 1322,
|
|
- /* 300 */ 1201, 1205, 1205, 1201, 1122, 1111, 1120, 1118, 1127, 1014,
|
|
- /* 310 */ 1052, 1275, 1275, 1271, 1271, 1271, 1319, 1319, 1256, 1287,
|
|
- /* 320 */ 1287, 1036, 1036, 1287, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 330 */ 1282, 1322, 1210, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 340 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 350 */ 1322, 1143, 1322, 993, 1253, 1322, 1322, 1252, 1322, 1322,
|
|
- /* 360 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 370 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1310, 1322,
|
|
- /* 380 */ 1322, 1322, 1322, 1322, 1322, 1224, 1223, 1322, 1322, 1322,
|
|
- /* 390 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 400 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 410 */ 1074, 1322, 1322, 1322, 1296, 1322, 1322, 1322, 1322, 1322,
|
|
- /* 420 */ 1322, 1322, 1119, 1322, 1112, 1322, 1322, 1301, 1322, 1322,
|
|
- /* 430 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1262, 1322,
|
|
- /* 440 */ 1322, 1322, 1261, 1322, 1322, 1322, 1322, 1322, 1145, 1322,
|
|
- /* 450 */ 1144, 1148, 1322, 1002, 1322,
|
|
+ /* 0 */ 1492, 1492, 1492, 1340, 1123, 1229, 1123, 1123, 1123, 1340,
|
|
+ /* 10 */ 1340, 1340, 1123, 1259, 1259, 1391, 1154, 1123, 1123, 1123,
|
|
+ /* 20 */ 1123, 1123, 1123, 1123, 1339, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 30 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1265, 1123,
|
|
+ /* 40 */ 1123, 1123, 1123, 1123, 1341, 1342, 1123, 1123, 1123, 1390,
|
|
+ /* 50 */ 1392, 1275, 1274, 1273, 1272, 1373, 1246, 1270, 1263, 1267,
|
|
+ /* 60 */ 1335, 1336, 1334, 1338, 1342, 1341, 1123, 1266, 1306, 1320,
|
|
+ /* 70 */ 1305, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 80 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 90 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 100 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 110 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1314, 1319, 1325,
|
|
+ /* 120 */ 1318, 1315, 1308, 1307, 1309, 1310, 1123, 1144, 1193, 1123,
|
|
+ /* 130 */ 1123, 1123, 1123, 1409, 1408, 1123, 1123, 1154, 1311, 1312,
|
|
+ /* 140 */ 1322, 1321, 1398, 1448, 1447, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 150 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 160 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 170 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1154, 1150, 1300,
|
|
+ /* 180 */ 1299, 1418, 1150, 1253, 1123, 1404, 1229, 1220, 1123, 1123,
|
|
+ /* 190 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 200 */ 1123, 1395, 1393, 1123, 1355, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 210 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 220 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 230 */ 1123, 1123, 1225, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 240 */ 1123, 1123, 1123, 1442, 1123, 1368, 1207, 1225, 1225, 1225,
|
|
+ /* 250 */ 1225, 1227, 1208, 1206, 1219, 1154, 1130, 1484, 1269, 1248,
|
|
+ /* 260 */ 1248, 1481, 1269, 1269, 1481, 1168, 1462, 1165, 1259, 1259,
|
|
+ /* 270 */ 1259, 1248, 1337, 1226, 1219, 1123, 1484, 1234, 1234, 1483,
|
|
+ /* 280 */ 1483, 1234, 1278, 1284, 1196, 1269, 1202, 1202, 1202, 1202,
|
|
+ /* 290 */ 1234, 1141, 1269, 1269, 1278, 1284, 1196, 1196, 1269, 1234,
|
|
+ /* 300 */ 1141, 1372, 1478, 1234, 1141, 1348, 1234, 1141, 1234, 1141,
|
|
+ /* 310 */ 1348, 1194, 1194, 1194, 1183, 1348, 1194, 1168, 1194, 1183,
|
|
+ /* 320 */ 1194, 1194, 1348, 1352, 1352, 1348, 1252, 1247, 1252, 1247,
|
|
+ /* 330 */ 1252, 1247, 1252, 1247, 1234, 1253, 1417, 1123, 1264, 1253,
|
|
+ /* 340 */ 1343, 1234, 1123, 1264, 1262, 1260, 1269, 1147, 1186, 1445,
|
|
+ /* 350 */ 1445, 1441, 1441, 1441, 1489, 1489, 1404, 1457, 1154, 1154,
|
|
+ /* 360 */ 1154, 1154, 1457, 1170, 1170, 1154, 1154, 1154, 1154, 1457,
|
|
+ /* 370 */ 1123, 1123, 1123, 1123, 1123, 1123, 1452, 1123, 1357, 1238,
|
|
+ /* 380 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 390 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 400 */ 1123, 1123, 1289, 1123, 1126, 1401, 1123, 1123, 1399, 1123,
|
|
+ /* 410 */ 1123, 1123, 1123, 1123, 1123, 1239, 1123, 1123, 1123, 1123,
|
|
+ /* 420 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 430 */ 1123, 1123, 1123, 1123, 1480, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 440 */ 1123, 1371, 1370, 1123, 1123, 1236, 1123, 1123, 1123, 1123,
|
|
+ /* 450 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 460 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 470 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 480 */ 1123, 1123, 1123, 1261, 1123, 1416, 1123, 1123, 1123, 1123,
|
|
+ /* 490 */ 1123, 1123, 1123, 1430, 1254, 1123, 1123, 1471, 1123, 1123,
|
|
+ /* 500 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
|
|
+ /* 510 */ 1123, 1123, 1466, 1210, 1291, 1123, 1290, 1294, 1123, 1135,
|
|
+ /* 520 */ 1123,
|
|
};
|
|
/********** End of lemon-generated parsing tables *****************************/
|
|
|
|
@@ -137414,73 +147745,95 @@
|
|
static const YYCODETYPE yyFallback[] = {
|
|
0, /* $ => nothing */
|
|
0, /* SEMI => nothing */
|
|
- 27, /* EXPLAIN => ID */
|
|
- 27, /* QUERY => ID */
|
|
- 27, /* PLAN => ID */
|
|
- 27, /* BEGIN => ID */
|
|
+ 59, /* EXPLAIN => ID */
|
|
+ 59, /* QUERY => ID */
|
|
+ 59, /* PLAN => ID */
|
|
+ 59, /* BEGIN => ID */
|
|
0, /* TRANSACTION => nothing */
|
|
- 27, /* DEFERRED => ID */
|
|
- 27, /* IMMEDIATE => ID */
|
|
- 27, /* EXCLUSIVE => ID */
|
|
+ 59, /* DEFERRED => ID */
|
|
+ 59, /* IMMEDIATE => ID */
|
|
+ 59, /* EXCLUSIVE => ID */
|
|
0, /* COMMIT => nothing */
|
|
- 27, /* END => ID */
|
|
- 27, /* ROLLBACK => ID */
|
|
- 27, /* SAVEPOINT => ID */
|
|
- 27, /* RELEASE => ID */
|
|
+ 59, /* END => ID */
|
|
+ 59, /* ROLLBACK => ID */
|
|
+ 59, /* SAVEPOINT => ID */
|
|
+ 59, /* RELEASE => ID */
|
|
0, /* TO => nothing */
|
|
0, /* TABLE => nothing */
|
|
0, /* CREATE => nothing */
|
|
- 27, /* IF => ID */
|
|
+ 59, /* IF => ID */
|
|
0, /* NOT => nothing */
|
|
0, /* EXISTS => nothing */
|
|
- 27, /* TEMP => ID */
|
|
+ 59, /* TEMP => ID */
|
|
0, /* LP => nothing */
|
|
0, /* RP => nothing */
|
|
0, /* AS => nothing */
|
|
- 27, /* WITHOUT => ID */
|
|
+ 59, /* WITHOUT => ID */
|
|
0, /* COMMA => nothing */
|
|
+ 59, /* ABORT => ID */
|
|
+ 59, /* ACTION => ID */
|
|
+ 59, /* AFTER => ID */
|
|
+ 59, /* ANALYZE => ID */
|
|
+ 59, /* ASC => ID */
|
|
+ 59, /* ATTACH => ID */
|
|
+ 59, /* BEFORE => ID */
|
|
+ 59, /* BY => ID */
|
|
+ 59, /* CASCADE => ID */
|
|
+ 59, /* CAST => ID */
|
|
+ 59, /* CONFLICT => ID */
|
|
+ 59, /* DATABASE => ID */
|
|
+ 59, /* DESC => ID */
|
|
+ 59, /* DETACH => ID */
|
|
+ 59, /* EACH => ID */
|
|
+ 59, /* FAIL => ID */
|
|
+ 0, /* OR => nothing */
|
|
+ 0, /* AND => nothing */
|
|
+ 0, /* IS => nothing */
|
|
+ 59, /* MATCH => ID */
|
|
+ 59, /* LIKE_KW => ID */
|
|
+ 0, /* BETWEEN => nothing */
|
|
+ 0, /* IN => nothing */
|
|
+ 0, /* ISNULL => nothing */
|
|
+ 0, /* NOTNULL => nothing */
|
|
+ 0, /* NE => nothing */
|
|
+ 0, /* EQ => nothing */
|
|
+ 0, /* GT => nothing */
|
|
+ 0, /* LE => nothing */
|
|
+ 0, /* LT => nothing */
|
|
+ 0, /* GE => nothing */
|
|
+ 0, /* ESCAPE => nothing */
|
|
0, /* ID => nothing */
|
|
- 27, /* ABORT => ID */
|
|
- 27, /* ACTION => ID */
|
|
- 27, /* AFTER => ID */
|
|
- 27, /* ANALYZE => ID */
|
|
- 27, /* ASC => ID */
|
|
- 27, /* ATTACH => ID */
|
|
- 27, /* BEFORE => ID */
|
|
- 27, /* BY => ID */
|
|
- 27, /* CASCADE => ID */
|
|
- 27, /* CAST => ID */
|
|
- 27, /* COLUMNKW => ID */
|
|
- 27, /* CONFLICT => ID */
|
|
- 27, /* DATABASE => ID */
|
|
- 27, /* DESC => ID */
|
|
- 27, /* DETACH => ID */
|
|
- 27, /* EACH => ID */
|
|
- 27, /* FAIL => ID */
|
|
- 27, /* FOR => ID */
|
|
- 27, /* IGNORE => ID */
|
|
- 27, /* INITIALLY => ID */
|
|
- 27, /* INSTEAD => ID */
|
|
- 27, /* LIKE_KW => ID */
|
|
- 27, /* MATCH => ID */
|
|
- 27, /* NO => ID */
|
|
- 27, /* KEY => ID */
|
|
- 27, /* OF => ID */
|
|
- 27, /* OFFSET => ID */
|
|
- 27, /* PRAGMA => ID */
|
|
- 27, /* RAISE => ID */
|
|
- 27, /* RECURSIVE => ID */
|
|
- 27, /* REPLACE => ID */
|
|
- 27, /* RESTRICT => ID */
|
|
- 27, /* ROW => ID */
|
|
- 27, /* TRIGGER => ID */
|
|
- 27, /* VACUUM => ID */
|
|
- 27, /* VIEW => ID */
|
|
- 27, /* VIRTUAL => ID */
|
|
- 27, /* WITH => ID */
|
|
- 27, /* REINDEX => ID */
|
|
- 27, /* RENAME => ID */
|
|
- 27, /* CTIME_KW => ID */
|
|
+ 59, /* COLUMNKW => ID */
|
|
+ 59, /* DO => ID */
|
|
+ 59, /* FOR => ID */
|
|
+ 59, /* IGNORE => ID */
|
|
+ 59, /* INITIALLY => ID */
|
|
+ 59, /* INSTEAD => ID */
|
|
+ 59, /* NO => ID */
|
|
+ 59, /* KEY => ID */
|
|
+ 59, /* OF => ID */
|
|
+ 59, /* OFFSET => ID */
|
|
+ 59, /* PRAGMA => ID */
|
|
+ 59, /* RAISE => ID */
|
|
+ 59, /* RECURSIVE => ID */
|
|
+ 59, /* REPLACE => ID */
|
|
+ 59, /* RESTRICT => ID */
|
|
+ 59, /* ROW => ID */
|
|
+ 59, /* ROWS => ID */
|
|
+ 59, /* TRIGGER => ID */
|
|
+ 59, /* VACUUM => ID */
|
|
+ 59, /* VIEW => ID */
|
|
+ 59, /* VIRTUAL => ID */
|
|
+ 59, /* WITH => ID */
|
|
+ 59, /* CURRENT => ID */
|
|
+ 59, /* FOLLOWING => ID */
|
|
+ 59, /* PARTITION => ID */
|
|
+ 59, /* PRECEDING => ID */
|
|
+ 59, /* RANGE => ID */
|
|
+ 59, /* UNBOUNDED => ID */
|
|
+ 59, /* REINDEX => ID */
|
|
+ 59, /* RENAME => ID */
|
|
+ 59, /* CTIME_KW => ID */
|
|
};
|
|
#endif /* YYFALLBACK */
|
|
|
|
@@ -137520,6 +147873,7 @@
|
|
int yyerrcnt; /* Shifts left before out of the error */
|
|
#endif
|
|
sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
|
|
+ sqlite3ParserCTX_SDECL /* A place to hold %extra_context */
|
|
#if YYSTACKDEPTH<=0
|
|
int yystksz; /* Current side of the stack */
|
|
yyStackEntry *yystack; /* The parser's stack */
|
|
@@ -137563,75 +147917,289 @@
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
-#ifndef NDEBUG
|
|
+#if defined(YYCOVERAGE) || !defined(NDEBUG)
|
|
/* For tracing shifts, the names of all terminals and nonterminals
|
|
** are required. The following table supplies these names */
|
|
static const char *const yyTokenName[] = {
|
|
- "$", "SEMI", "EXPLAIN", "QUERY",
|
|
- "PLAN", "BEGIN", "TRANSACTION", "DEFERRED",
|
|
- "IMMEDIATE", "EXCLUSIVE", "COMMIT", "END",
|
|
- "ROLLBACK", "SAVEPOINT", "RELEASE", "TO",
|
|
- "TABLE", "CREATE", "IF", "NOT",
|
|
- "EXISTS", "TEMP", "LP", "RP",
|
|
- "AS", "WITHOUT", "COMMA", "ID",
|
|
- "ABORT", "ACTION", "AFTER", "ANALYZE",
|
|
- "ASC", "ATTACH", "BEFORE", "BY",
|
|
- "CASCADE", "CAST", "COLUMNKW", "CONFLICT",
|
|
- "DATABASE", "DESC", "DETACH", "EACH",
|
|
- "FAIL", "FOR", "IGNORE", "INITIALLY",
|
|
- "INSTEAD", "LIKE_KW", "MATCH", "NO",
|
|
- "KEY", "OF", "OFFSET", "PRAGMA",
|
|
- "RAISE", "RECURSIVE", "REPLACE", "RESTRICT",
|
|
- "ROW", "TRIGGER", "VACUUM", "VIEW",
|
|
- "VIRTUAL", "WITH", "REINDEX", "RENAME",
|
|
- "CTIME_KW", "ANY", "OR", "AND",
|
|
- "IS", "BETWEEN", "IN", "ISNULL",
|
|
- "NOTNULL", "NE", "EQ", "GT",
|
|
- "LE", "LT", "GE", "ESCAPE",
|
|
- "BITAND", "BITOR", "LSHIFT", "RSHIFT",
|
|
- "PLUS", "MINUS", "STAR", "SLASH",
|
|
- "REM", "CONCAT", "COLLATE", "BITNOT",
|
|
- "INDEXED", "STRING", "JOIN_KW", "CONSTRAINT",
|
|
- "DEFAULT", "NULL", "PRIMARY", "UNIQUE",
|
|
- "CHECK", "REFERENCES", "AUTOINCR", "ON",
|
|
- "INSERT", "DELETE", "UPDATE", "SET",
|
|
- "DEFERRABLE", "FOREIGN", "DROP", "UNION",
|
|
- "ALL", "EXCEPT", "INTERSECT", "SELECT",
|
|
- "VALUES", "DISTINCT", "DOT", "FROM",
|
|
- "JOIN", "USING", "ORDER", "GROUP",
|
|
- "HAVING", "LIMIT", "WHERE", "INTO",
|
|
- "FLOAT", "BLOB", "INTEGER", "VARIABLE",
|
|
- "CASE", "WHEN", "THEN", "ELSE",
|
|
- "INDEX", "ALTER", "ADD", "error",
|
|
- "input", "cmdlist", "ecmd", "explain",
|
|
- "cmdx", "cmd", "transtype", "trans_opt",
|
|
- "nm", "savepoint_opt", "create_table", "create_table_args",
|
|
- "createkw", "temp", "ifnotexists", "dbnm",
|
|
- "columnlist", "conslist_opt", "table_options", "select",
|
|
- "columnname", "carglist", "typetoken", "typename",
|
|
- "signed", "plus_num", "minus_num", "ccons",
|
|
- "term", "expr", "onconf", "sortorder",
|
|
- "autoinc", "eidlist_opt", "refargs", "defer_subclause",
|
|
- "refarg", "refact", "init_deferred_pred_opt", "conslist",
|
|
- "tconscomma", "tcons", "sortlist", "eidlist",
|
|
- "defer_subclause_opt", "orconf", "resolvetype", "raisetype",
|
|
- "ifexists", "fullname", "selectnowith", "oneselect",
|
|
- "with", "multiselect_op", "distinct", "selcollist",
|
|
- "from", "where_opt", "groupby_opt", "having_opt",
|
|
- "orderby_opt", "limit_opt", "values", "nexprlist",
|
|
- "exprlist", "sclp", "as", "seltablist",
|
|
- "stl_prefix", "joinop", "indexed_opt", "on_opt",
|
|
- "using_opt", "idlist", "setlist", "insert_cmd",
|
|
- "idlist_opt", "likeop", "between_op", "in_op",
|
|
- "paren_exprlist", "case_operand", "case_exprlist", "case_else",
|
|
- "uniqueflag", "collate", "nmnum", "trigger_decl",
|
|
- "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause",
|
|
- "when_clause", "trigger_cmd", "trnm", "tridxby",
|
|
- "database_kw_opt", "key_opt", "add_column_fullname", "kwcolumn_opt",
|
|
- "create_vtab", "vtabarglist", "vtabarg", "vtabargtoken",
|
|
- "lp", "anylist", "wqlist",
|
|
+ /* 0 */ "$",
|
|
+ /* 1 */ "SEMI",
|
|
+ /* 2 */ "EXPLAIN",
|
|
+ /* 3 */ "QUERY",
|
|
+ /* 4 */ "PLAN",
|
|
+ /* 5 */ "BEGIN",
|
|
+ /* 6 */ "TRANSACTION",
|
|
+ /* 7 */ "DEFERRED",
|
|
+ /* 8 */ "IMMEDIATE",
|
|
+ /* 9 */ "EXCLUSIVE",
|
|
+ /* 10 */ "COMMIT",
|
|
+ /* 11 */ "END",
|
|
+ /* 12 */ "ROLLBACK",
|
|
+ /* 13 */ "SAVEPOINT",
|
|
+ /* 14 */ "RELEASE",
|
|
+ /* 15 */ "TO",
|
|
+ /* 16 */ "TABLE",
|
|
+ /* 17 */ "CREATE",
|
|
+ /* 18 */ "IF",
|
|
+ /* 19 */ "NOT",
|
|
+ /* 20 */ "EXISTS",
|
|
+ /* 21 */ "TEMP",
|
|
+ /* 22 */ "LP",
|
|
+ /* 23 */ "RP",
|
|
+ /* 24 */ "AS",
|
|
+ /* 25 */ "WITHOUT",
|
|
+ /* 26 */ "COMMA",
|
|
+ /* 27 */ "ABORT",
|
|
+ /* 28 */ "ACTION",
|
|
+ /* 29 */ "AFTER",
|
|
+ /* 30 */ "ANALYZE",
|
|
+ /* 31 */ "ASC",
|
|
+ /* 32 */ "ATTACH",
|
|
+ /* 33 */ "BEFORE",
|
|
+ /* 34 */ "BY",
|
|
+ /* 35 */ "CASCADE",
|
|
+ /* 36 */ "CAST",
|
|
+ /* 37 */ "CONFLICT",
|
|
+ /* 38 */ "DATABASE",
|
|
+ /* 39 */ "DESC",
|
|
+ /* 40 */ "DETACH",
|
|
+ /* 41 */ "EACH",
|
|
+ /* 42 */ "FAIL",
|
|
+ /* 43 */ "OR",
|
|
+ /* 44 */ "AND",
|
|
+ /* 45 */ "IS",
|
|
+ /* 46 */ "MATCH",
|
|
+ /* 47 */ "LIKE_KW",
|
|
+ /* 48 */ "BETWEEN",
|
|
+ /* 49 */ "IN",
|
|
+ /* 50 */ "ISNULL",
|
|
+ /* 51 */ "NOTNULL",
|
|
+ /* 52 */ "NE",
|
|
+ /* 53 */ "EQ",
|
|
+ /* 54 */ "GT",
|
|
+ /* 55 */ "LE",
|
|
+ /* 56 */ "LT",
|
|
+ /* 57 */ "GE",
|
|
+ /* 58 */ "ESCAPE",
|
|
+ /* 59 */ "ID",
|
|
+ /* 60 */ "COLUMNKW",
|
|
+ /* 61 */ "DO",
|
|
+ /* 62 */ "FOR",
|
|
+ /* 63 */ "IGNORE",
|
|
+ /* 64 */ "INITIALLY",
|
|
+ /* 65 */ "INSTEAD",
|
|
+ /* 66 */ "NO",
|
|
+ /* 67 */ "KEY",
|
|
+ /* 68 */ "OF",
|
|
+ /* 69 */ "OFFSET",
|
|
+ /* 70 */ "PRAGMA",
|
|
+ /* 71 */ "RAISE",
|
|
+ /* 72 */ "RECURSIVE",
|
|
+ /* 73 */ "REPLACE",
|
|
+ /* 74 */ "RESTRICT",
|
|
+ /* 75 */ "ROW",
|
|
+ /* 76 */ "ROWS",
|
|
+ /* 77 */ "TRIGGER",
|
|
+ /* 78 */ "VACUUM",
|
|
+ /* 79 */ "VIEW",
|
|
+ /* 80 */ "VIRTUAL",
|
|
+ /* 81 */ "WITH",
|
|
+ /* 82 */ "CURRENT",
|
|
+ /* 83 */ "FOLLOWING",
|
|
+ /* 84 */ "PARTITION",
|
|
+ /* 85 */ "PRECEDING",
|
|
+ /* 86 */ "RANGE",
|
|
+ /* 87 */ "UNBOUNDED",
|
|
+ /* 88 */ "REINDEX",
|
|
+ /* 89 */ "RENAME",
|
|
+ /* 90 */ "CTIME_KW",
|
|
+ /* 91 */ "ANY",
|
|
+ /* 92 */ "BITAND",
|
|
+ /* 93 */ "BITOR",
|
|
+ /* 94 */ "LSHIFT",
|
|
+ /* 95 */ "RSHIFT",
|
|
+ /* 96 */ "PLUS",
|
|
+ /* 97 */ "MINUS",
|
|
+ /* 98 */ "STAR",
|
|
+ /* 99 */ "SLASH",
|
|
+ /* 100 */ "REM",
|
|
+ /* 101 */ "CONCAT",
|
|
+ /* 102 */ "COLLATE",
|
|
+ /* 103 */ "BITNOT",
|
|
+ /* 104 */ "ON",
|
|
+ /* 105 */ "INDEXED",
|
|
+ /* 106 */ "STRING",
|
|
+ /* 107 */ "JOIN_KW",
|
|
+ /* 108 */ "CONSTRAINT",
|
|
+ /* 109 */ "DEFAULT",
|
|
+ /* 110 */ "NULL",
|
|
+ /* 111 */ "PRIMARY",
|
|
+ /* 112 */ "UNIQUE",
|
|
+ /* 113 */ "CHECK",
|
|
+ /* 114 */ "REFERENCES",
|
|
+ /* 115 */ "AUTOINCR",
|
|
+ /* 116 */ "INSERT",
|
|
+ /* 117 */ "DELETE",
|
|
+ /* 118 */ "UPDATE",
|
|
+ /* 119 */ "SET",
|
|
+ /* 120 */ "DEFERRABLE",
|
|
+ /* 121 */ "FOREIGN",
|
|
+ /* 122 */ "DROP",
|
|
+ /* 123 */ "UNION",
|
|
+ /* 124 */ "ALL",
|
|
+ /* 125 */ "EXCEPT",
|
|
+ /* 126 */ "INTERSECT",
|
|
+ /* 127 */ "SELECT",
|
|
+ /* 128 */ "VALUES",
|
|
+ /* 129 */ "DISTINCT",
|
|
+ /* 130 */ "DOT",
|
|
+ /* 131 */ "FROM",
|
|
+ /* 132 */ "JOIN",
|
|
+ /* 133 */ "USING",
|
|
+ /* 134 */ "ORDER",
|
|
+ /* 135 */ "GROUP",
|
|
+ /* 136 */ "HAVING",
|
|
+ /* 137 */ "LIMIT",
|
|
+ /* 138 */ "WHERE",
|
|
+ /* 139 */ "INTO",
|
|
+ /* 140 */ "NOTHING",
|
|
+ /* 141 */ "FLOAT",
|
|
+ /* 142 */ "BLOB",
|
|
+ /* 143 */ "INTEGER",
|
|
+ /* 144 */ "VARIABLE",
|
|
+ /* 145 */ "CASE",
|
|
+ /* 146 */ "WHEN",
|
|
+ /* 147 */ "THEN",
|
|
+ /* 148 */ "ELSE",
|
|
+ /* 149 */ "INDEX",
|
|
+ /* 150 */ "ALTER",
|
|
+ /* 151 */ "ADD",
|
|
+ /* 152 */ "WINDOW",
|
|
+ /* 153 */ "OVER",
|
|
+ /* 154 */ "FILTER",
|
|
+ /* 155 */ "input",
|
|
+ /* 156 */ "cmdlist",
|
|
+ /* 157 */ "ecmd",
|
|
+ /* 158 */ "cmdx",
|
|
+ /* 159 */ "explain",
|
|
+ /* 160 */ "cmd",
|
|
+ /* 161 */ "transtype",
|
|
+ /* 162 */ "trans_opt",
|
|
+ /* 163 */ "nm",
|
|
+ /* 164 */ "savepoint_opt",
|
|
+ /* 165 */ "create_table",
|
|
+ /* 166 */ "create_table_args",
|
|
+ /* 167 */ "createkw",
|
|
+ /* 168 */ "temp",
|
|
+ /* 169 */ "ifnotexists",
|
|
+ /* 170 */ "dbnm",
|
|
+ /* 171 */ "columnlist",
|
|
+ /* 172 */ "conslist_opt",
|
|
+ /* 173 */ "table_options",
|
|
+ /* 174 */ "select",
|
|
+ /* 175 */ "columnname",
|
|
+ /* 176 */ "carglist",
|
|
+ /* 177 */ "typetoken",
|
|
+ /* 178 */ "typename",
|
|
+ /* 179 */ "signed",
|
|
+ /* 180 */ "plus_num",
|
|
+ /* 181 */ "minus_num",
|
|
+ /* 182 */ "scanpt",
|
|
+ /* 183 */ "ccons",
|
|
+ /* 184 */ "term",
|
|
+ /* 185 */ "expr",
|
|
+ /* 186 */ "onconf",
|
|
+ /* 187 */ "sortorder",
|
|
+ /* 188 */ "autoinc",
|
|
+ /* 189 */ "eidlist_opt",
|
|
+ /* 190 */ "refargs",
|
|
+ /* 191 */ "defer_subclause",
|
|
+ /* 192 */ "refarg",
|
|
+ /* 193 */ "refact",
|
|
+ /* 194 */ "init_deferred_pred_opt",
|
|
+ /* 195 */ "conslist",
|
|
+ /* 196 */ "tconscomma",
|
|
+ /* 197 */ "tcons",
|
|
+ /* 198 */ "sortlist",
|
|
+ /* 199 */ "eidlist",
|
|
+ /* 200 */ "defer_subclause_opt",
|
|
+ /* 201 */ "orconf",
|
|
+ /* 202 */ "resolvetype",
|
|
+ /* 203 */ "raisetype",
|
|
+ /* 204 */ "ifexists",
|
|
+ /* 205 */ "fullname",
|
|
+ /* 206 */ "selectnowith",
|
|
+ /* 207 */ "oneselect",
|
|
+ /* 208 */ "wqlist",
|
|
+ /* 209 */ "multiselect_op",
|
|
+ /* 210 */ "distinct",
|
|
+ /* 211 */ "selcollist",
|
|
+ /* 212 */ "from",
|
|
+ /* 213 */ "where_opt",
|
|
+ /* 214 */ "groupby_opt",
|
|
+ /* 215 */ "having_opt",
|
|
+ /* 216 */ "orderby_opt",
|
|
+ /* 217 */ "limit_opt",
|
|
+ /* 218 */ "window_clause",
|
|
+ /* 219 */ "values",
|
|
+ /* 220 */ "nexprlist",
|
|
+ /* 221 */ "sclp",
|
|
+ /* 222 */ "as",
|
|
+ /* 223 */ "seltablist",
|
|
+ /* 224 */ "stl_prefix",
|
|
+ /* 225 */ "joinop",
|
|
+ /* 226 */ "indexed_opt",
|
|
+ /* 227 */ "on_opt",
|
|
+ /* 228 */ "using_opt",
|
|
+ /* 229 */ "exprlist",
|
|
+ /* 230 */ "xfullname",
|
|
+ /* 231 */ "idlist",
|
|
+ /* 232 */ "with",
|
|
+ /* 233 */ "setlist",
|
|
+ /* 234 */ "insert_cmd",
|
|
+ /* 235 */ "idlist_opt",
|
|
+ /* 236 */ "upsert",
|
|
+ /* 237 */ "over_clause",
|
|
+ /* 238 */ "likeop",
|
|
+ /* 239 */ "between_op",
|
|
+ /* 240 */ "in_op",
|
|
+ /* 241 */ "paren_exprlist",
|
|
+ /* 242 */ "case_operand",
|
|
+ /* 243 */ "case_exprlist",
|
|
+ /* 244 */ "case_else",
|
|
+ /* 245 */ "uniqueflag",
|
|
+ /* 246 */ "collate",
|
|
+ /* 247 */ "nmnum",
|
|
+ /* 248 */ "trigger_decl",
|
|
+ /* 249 */ "trigger_cmd_list",
|
|
+ /* 250 */ "trigger_time",
|
|
+ /* 251 */ "trigger_event",
|
|
+ /* 252 */ "foreach_clause",
|
|
+ /* 253 */ "when_clause",
|
|
+ /* 254 */ "trigger_cmd",
|
|
+ /* 255 */ "trnm",
|
|
+ /* 256 */ "tridxby",
|
|
+ /* 257 */ "database_kw_opt",
|
|
+ /* 258 */ "key_opt",
|
|
+ /* 259 */ "add_column_fullname",
|
|
+ /* 260 */ "kwcolumn_opt",
|
|
+ /* 261 */ "create_vtab",
|
|
+ /* 262 */ "vtabarglist",
|
|
+ /* 263 */ "vtabarg",
|
|
+ /* 264 */ "vtabargtoken",
|
|
+ /* 265 */ "lp",
|
|
+ /* 266 */ "anylist",
|
|
+ /* 267 */ "windowdefn_list",
|
|
+ /* 268 */ "windowdefn",
|
|
+ /* 269 */ "window",
|
|
+ /* 270 */ "frame_opt",
|
|
+ /* 271 */ "part_opt",
|
|
+ /* 272 */ "filter_opt",
|
|
+ /* 273 */ "range_or_rows",
|
|
+ /* 274 */ "frame_bound",
|
|
+ /* 275 */ "frame_bound_s",
|
|
+ /* 276 */ "frame_bound_e",
|
|
};
|
|
-#endif /* NDEBUG */
|
|
+#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
|
|
|
|
#ifndef NDEBUG
|
|
/* For tracing reduce actions, the names of all rules are required.
|
|
@@ -137665,307 +148233,345 @@
|
|
/* 25 */ "typetoken ::= typename LP signed RP",
|
|
/* 26 */ "typetoken ::= typename LP signed COMMA signed RP",
|
|
/* 27 */ "typename ::= typename ID|STRING",
|
|
- /* 28 */ "ccons ::= CONSTRAINT nm",
|
|
- /* 29 */ "ccons ::= DEFAULT term",
|
|
- /* 30 */ "ccons ::= DEFAULT LP expr RP",
|
|
- /* 31 */ "ccons ::= DEFAULT PLUS term",
|
|
- /* 32 */ "ccons ::= DEFAULT MINUS term",
|
|
- /* 33 */ "ccons ::= DEFAULT ID|INDEXED",
|
|
- /* 34 */ "ccons ::= NOT NULL onconf",
|
|
- /* 35 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
|
|
- /* 36 */ "ccons ::= UNIQUE onconf",
|
|
- /* 37 */ "ccons ::= CHECK LP expr RP",
|
|
- /* 38 */ "ccons ::= REFERENCES nm eidlist_opt refargs",
|
|
- /* 39 */ "ccons ::= defer_subclause",
|
|
- /* 40 */ "ccons ::= COLLATE ID|STRING",
|
|
- /* 41 */ "autoinc ::=",
|
|
- /* 42 */ "autoinc ::= AUTOINCR",
|
|
- /* 43 */ "refargs ::=",
|
|
- /* 44 */ "refargs ::= refargs refarg",
|
|
- /* 45 */ "refarg ::= MATCH nm",
|
|
- /* 46 */ "refarg ::= ON INSERT refact",
|
|
- /* 47 */ "refarg ::= ON DELETE refact",
|
|
- /* 48 */ "refarg ::= ON UPDATE refact",
|
|
- /* 49 */ "refact ::= SET NULL",
|
|
- /* 50 */ "refact ::= SET DEFAULT",
|
|
- /* 51 */ "refact ::= CASCADE",
|
|
- /* 52 */ "refact ::= RESTRICT",
|
|
- /* 53 */ "refact ::= NO ACTION",
|
|
- /* 54 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
|
|
- /* 55 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
|
|
- /* 56 */ "init_deferred_pred_opt ::=",
|
|
- /* 57 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
|
|
- /* 58 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
|
|
- /* 59 */ "conslist_opt ::=",
|
|
- /* 60 */ "tconscomma ::= COMMA",
|
|
- /* 61 */ "tcons ::= CONSTRAINT nm",
|
|
- /* 62 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf",
|
|
- /* 63 */ "tcons ::= UNIQUE LP sortlist RP onconf",
|
|
- /* 64 */ "tcons ::= CHECK LP expr RP onconf",
|
|
- /* 65 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt",
|
|
- /* 66 */ "defer_subclause_opt ::=",
|
|
- /* 67 */ "onconf ::=",
|
|
- /* 68 */ "onconf ::= ON CONFLICT resolvetype",
|
|
- /* 69 */ "orconf ::=",
|
|
- /* 70 */ "orconf ::= OR resolvetype",
|
|
- /* 71 */ "resolvetype ::= IGNORE",
|
|
- /* 72 */ "resolvetype ::= REPLACE",
|
|
- /* 73 */ "cmd ::= DROP TABLE ifexists fullname",
|
|
- /* 74 */ "ifexists ::= IF EXISTS",
|
|
- /* 75 */ "ifexists ::=",
|
|
- /* 76 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select",
|
|
- /* 77 */ "cmd ::= DROP VIEW ifexists fullname",
|
|
- /* 78 */ "cmd ::= select",
|
|
- /* 79 */ "select ::= with selectnowith",
|
|
- /* 80 */ "selectnowith ::= selectnowith multiselect_op oneselect",
|
|
- /* 81 */ "multiselect_op ::= UNION",
|
|
- /* 82 */ "multiselect_op ::= UNION ALL",
|
|
- /* 83 */ "multiselect_op ::= EXCEPT|INTERSECT",
|
|
- /* 84 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
|
|
- /* 85 */ "values ::= VALUES LP nexprlist RP",
|
|
- /* 86 */ "values ::= values COMMA LP exprlist RP",
|
|
- /* 87 */ "distinct ::= DISTINCT",
|
|
- /* 88 */ "distinct ::= ALL",
|
|
- /* 89 */ "distinct ::=",
|
|
- /* 90 */ "sclp ::=",
|
|
- /* 91 */ "selcollist ::= sclp expr as",
|
|
- /* 92 */ "selcollist ::= sclp STAR",
|
|
- /* 93 */ "selcollist ::= sclp nm DOT STAR",
|
|
- /* 94 */ "as ::= AS nm",
|
|
- /* 95 */ "as ::=",
|
|
- /* 96 */ "from ::=",
|
|
- /* 97 */ "from ::= FROM seltablist",
|
|
- /* 98 */ "stl_prefix ::= seltablist joinop",
|
|
- /* 99 */ "stl_prefix ::=",
|
|
- /* 100 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
|
|
- /* 101 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt",
|
|
- /* 102 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
|
|
- /* 103 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
|
|
- /* 104 */ "dbnm ::=",
|
|
- /* 105 */ "dbnm ::= DOT nm",
|
|
- /* 106 */ "fullname ::= nm dbnm",
|
|
- /* 107 */ "joinop ::= COMMA|JOIN",
|
|
- /* 108 */ "joinop ::= JOIN_KW JOIN",
|
|
- /* 109 */ "joinop ::= JOIN_KW nm JOIN",
|
|
- /* 110 */ "joinop ::= JOIN_KW nm nm JOIN",
|
|
- /* 111 */ "on_opt ::= ON expr",
|
|
- /* 112 */ "on_opt ::=",
|
|
- /* 113 */ "indexed_opt ::=",
|
|
- /* 114 */ "indexed_opt ::= INDEXED BY nm",
|
|
- /* 115 */ "indexed_opt ::= NOT INDEXED",
|
|
- /* 116 */ "using_opt ::= USING LP idlist RP",
|
|
- /* 117 */ "using_opt ::=",
|
|
- /* 118 */ "orderby_opt ::=",
|
|
- /* 119 */ "orderby_opt ::= ORDER BY sortlist",
|
|
- /* 120 */ "sortlist ::= sortlist COMMA expr sortorder",
|
|
- /* 121 */ "sortlist ::= expr sortorder",
|
|
- /* 122 */ "sortorder ::= ASC",
|
|
- /* 123 */ "sortorder ::= DESC",
|
|
- /* 124 */ "sortorder ::=",
|
|
- /* 125 */ "groupby_opt ::=",
|
|
- /* 126 */ "groupby_opt ::= GROUP BY nexprlist",
|
|
- /* 127 */ "having_opt ::=",
|
|
- /* 128 */ "having_opt ::= HAVING expr",
|
|
- /* 129 */ "limit_opt ::=",
|
|
- /* 130 */ "limit_opt ::= LIMIT expr",
|
|
- /* 131 */ "limit_opt ::= LIMIT expr OFFSET expr",
|
|
- /* 132 */ "limit_opt ::= LIMIT expr COMMA expr",
|
|
- /* 133 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt",
|
|
- /* 134 */ "where_opt ::=",
|
|
- /* 135 */ "where_opt ::= WHERE expr",
|
|
- /* 136 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt",
|
|
- /* 137 */ "setlist ::= setlist COMMA nm EQ expr",
|
|
- /* 138 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
|
|
- /* 139 */ "setlist ::= nm EQ expr",
|
|
- /* 140 */ "setlist ::= LP idlist RP EQ expr",
|
|
- /* 141 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select",
|
|
- /* 142 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES",
|
|
- /* 143 */ "insert_cmd ::= INSERT orconf",
|
|
- /* 144 */ "insert_cmd ::= REPLACE",
|
|
- /* 145 */ "idlist_opt ::=",
|
|
- /* 146 */ "idlist_opt ::= LP idlist RP",
|
|
- /* 147 */ "idlist ::= idlist COMMA nm",
|
|
- /* 148 */ "idlist ::= nm",
|
|
- /* 149 */ "expr ::= LP expr RP",
|
|
- /* 150 */ "expr ::= ID|INDEXED",
|
|
- /* 151 */ "expr ::= JOIN_KW",
|
|
- /* 152 */ "expr ::= nm DOT nm",
|
|
- /* 153 */ "expr ::= nm DOT nm DOT nm",
|
|
- /* 154 */ "term ::= NULL|FLOAT|BLOB",
|
|
- /* 155 */ "term ::= STRING",
|
|
- /* 156 */ "term ::= INTEGER",
|
|
- /* 157 */ "expr ::= VARIABLE",
|
|
- /* 158 */ "expr ::= expr COLLATE ID|STRING",
|
|
- /* 159 */ "expr ::= CAST LP expr AS typetoken RP",
|
|
- /* 160 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
|
|
- /* 161 */ "expr ::= ID|INDEXED LP STAR RP",
|
|
- /* 162 */ "term ::= CTIME_KW",
|
|
- /* 163 */ "expr ::= LP nexprlist COMMA expr RP",
|
|
- /* 164 */ "expr ::= expr AND expr",
|
|
- /* 165 */ "expr ::= expr OR expr",
|
|
- /* 166 */ "expr ::= expr LT|GT|GE|LE expr",
|
|
- /* 167 */ "expr ::= expr EQ|NE expr",
|
|
- /* 168 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
|
|
- /* 169 */ "expr ::= expr PLUS|MINUS expr",
|
|
- /* 170 */ "expr ::= expr STAR|SLASH|REM expr",
|
|
- /* 171 */ "expr ::= expr CONCAT expr",
|
|
- /* 172 */ "likeop ::= NOT LIKE_KW|MATCH",
|
|
- /* 173 */ "expr ::= expr likeop expr",
|
|
- /* 174 */ "expr ::= expr likeop expr ESCAPE expr",
|
|
- /* 175 */ "expr ::= expr ISNULL|NOTNULL",
|
|
- /* 176 */ "expr ::= expr NOT NULL",
|
|
- /* 177 */ "expr ::= expr IS expr",
|
|
- /* 178 */ "expr ::= expr IS NOT expr",
|
|
- /* 179 */ "expr ::= NOT expr",
|
|
- /* 180 */ "expr ::= BITNOT expr",
|
|
- /* 181 */ "expr ::= MINUS expr",
|
|
- /* 182 */ "expr ::= PLUS expr",
|
|
- /* 183 */ "between_op ::= BETWEEN",
|
|
- /* 184 */ "between_op ::= NOT BETWEEN",
|
|
- /* 185 */ "expr ::= expr between_op expr AND expr",
|
|
- /* 186 */ "in_op ::= IN",
|
|
- /* 187 */ "in_op ::= NOT IN",
|
|
- /* 188 */ "expr ::= expr in_op LP exprlist RP",
|
|
- /* 189 */ "expr ::= LP select RP",
|
|
- /* 190 */ "expr ::= expr in_op LP select RP",
|
|
- /* 191 */ "expr ::= expr in_op nm dbnm paren_exprlist",
|
|
- /* 192 */ "expr ::= EXISTS LP select RP",
|
|
- /* 193 */ "expr ::= CASE case_operand case_exprlist case_else END",
|
|
- /* 194 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
|
|
- /* 195 */ "case_exprlist ::= WHEN expr THEN expr",
|
|
- /* 196 */ "case_else ::= ELSE expr",
|
|
- /* 197 */ "case_else ::=",
|
|
- /* 198 */ "case_operand ::= expr",
|
|
- /* 199 */ "case_operand ::=",
|
|
- /* 200 */ "exprlist ::=",
|
|
- /* 201 */ "nexprlist ::= nexprlist COMMA expr",
|
|
- /* 202 */ "nexprlist ::= expr",
|
|
- /* 203 */ "paren_exprlist ::=",
|
|
- /* 204 */ "paren_exprlist ::= LP exprlist RP",
|
|
- /* 205 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
|
|
- /* 206 */ "uniqueflag ::= UNIQUE",
|
|
- /* 207 */ "uniqueflag ::=",
|
|
- /* 208 */ "eidlist_opt ::=",
|
|
- /* 209 */ "eidlist_opt ::= LP eidlist RP",
|
|
- /* 210 */ "eidlist ::= eidlist COMMA nm collate sortorder",
|
|
- /* 211 */ "eidlist ::= nm collate sortorder",
|
|
- /* 212 */ "collate ::=",
|
|
- /* 213 */ "collate ::= COLLATE ID|STRING",
|
|
- /* 214 */ "cmd ::= DROP INDEX ifexists fullname",
|
|
- /* 215 */ "cmd ::= VACUUM",
|
|
- /* 216 */ "cmd ::= VACUUM nm",
|
|
- /* 217 */ "cmd ::= PRAGMA nm dbnm",
|
|
- /* 218 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
|
|
- /* 219 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
|
|
- /* 220 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
|
|
- /* 221 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
|
|
- /* 222 */ "plus_num ::= PLUS INTEGER|FLOAT",
|
|
- /* 223 */ "minus_num ::= MINUS INTEGER|FLOAT",
|
|
- /* 224 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
|
|
- /* 225 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
|
|
- /* 226 */ "trigger_time ::= BEFORE|AFTER",
|
|
- /* 227 */ "trigger_time ::= INSTEAD OF",
|
|
- /* 228 */ "trigger_time ::=",
|
|
- /* 229 */ "trigger_event ::= DELETE|INSERT",
|
|
- /* 230 */ "trigger_event ::= UPDATE",
|
|
- /* 231 */ "trigger_event ::= UPDATE OF idlist",
|
|
- /* 232 */ "when_clause ::=",
|
|
- /* 233 */ "when_clause ::= WHEN expr",
|
|
- /* 234 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
|
|
- /* 235 */ "trigger_cmd_list ::= trigger_cmd SEMI",
|
|
- /* 236 */ "trnm ::= nm DOT nm",
|
|
- /* 237 */ "tridxby ::= INDEXED BY nm",
|
|
- /* 238 */ "tridxby ::= NOT INDEXED",
|
|
- /* 239 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
|
|
- /* 240 */ "trigger_cmd ::= insert_cmd INTO trnm idlist_opt select",
|
|
- /* 241 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
|
|
- /* 242 */ "trigger_cmd ::= select",
|
|
- /* 243 */ "expr ::= RAISE LP IGNORE RP",
|
|
- /* 244 */ "expr ::= RAISE LP raisetype COMMA nm RP",
|
|
- /* 245 */ "raisetype ::= ROLLBACK",
|
|
- /* 246 */ "raisetype ::= ABORT",
|
|
- /* 247 */ "raisetype ::= FAIL",
|
|
- /* 248 */ "cmd ::= DROP TRIGGER ifexists fullname",
|
|
- /* 249 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
|
|
- /* 250 */ "cmd ::= DETACH database_kw_opt expr",
|
|
- /* 251 */ "key_opt ::=",
|
|
- /* 252 */ "key_opt ::= KEY expr",
|
|
- /* 253 */ "cmd ::= REINDEX",
|
|
- /* 254 */ "cmd ::= REINDEX nm dbnm",
|
|
- /* 255 */ "cmd ::= ANALYZE",
|
|
- /* 256 */ "cmd ::= ANALYZE nm dbnm",
|
|
- /* 257 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
|
|
- /* 258 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
|
|
- /* 259 */ "add_column_fullname ::= fullname",
|
|
- /* 260 */ "cmd ::= create_vtab",
|
|
- /* 261 */ "cmd ::= create_vtab LP vtabarglist RP",
|
|
- /* 262 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
|
|
- /* 263 */ "vtabarg ::=",
|
|
- /* 264 */ "vtabargtoken ::= ANY",
|
|
- /* 265 */ "vtabargtoken ::= lp anylist RP",
|
|
- /* 266 */ "lp ::= LP",
|
|
- /* 267 */ "with ::=",
|
|
- /* 268 */ "with ::= WITH wqlist",
|
|
- /* 269 */ "with ::= WITH RECURSIVE wqlist",
|
|
- /* 270 */ "wqlist ::= nm eidlist_opt AS LP select RP",
|
|
- /* 271 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
|
|
- /* 272 */ "input ::= cmdlist",
|
|
- /* 273 */ "cmdlist ::= cmdlist ecmd",
|
|
- /* 274 */ "cmdlist ::= ecmd",
|
|
- /* 275 */ "ecmd ::= SEMI",
|
|
- /* 276 */ "ecmd ::= explain cmdx SEMI",
|
|
- /* 277 */ "explain ::=",
|
|
- /* 278 */ "trans_opt ::=",
|
|
- /* 279 */ "trans_opt ::= TRANSACTION",
|
|
- /* 280 */ "trans_opt ::= TRANSACTION nm",
|
|
- /* 281 */ "savepoint_opt ::= SAVEPOINT",
|
|
- /* 282 */ "savepoint_opt ::=",
|
|
- /* 283 */ "cmd ::= create_table create_table_args",
|
|
- /* 284 */ "columnlist ::= columnlist COMMA columnname carglist",
|
|
- /* 285 */ "columnlist ::= columnname carglist",
|
|
- /* 286 */ "nm ::= ID|INDEXED",
|
|
- /* 287 */ "nm ::= STRING",
|
|
- /* 288 */ "nm ::= JOIN_KW",
|
|
- /* 289 */ "typetoken ::= typename",
|
|
- /* 290 */ "typename ::= ID|STRING",
|
|
- /* 291 */ "signed ::= plus_num",
|
|
- /* 292 */ "signed ::= minus_num",
|
|
- /* 293 */ "carglist ::= carglist ccons",
|
|
- /* 294 */ "carglist ::=",
|
|
- /* 295 */ "ccons ::= NULL onconf",
|
|
- /* 296 */ "conslist_opt ::= COMMA conslist",
|
|
- /* 297 */ "conslist ::= conslist tconscomma tcons",
|
|
- /* 298 */ "conslist ::= tcons",
|
|
- /* 299 */ "tconscomma ::=",
|
|
- /* 300 */ "defer_subclause_opt ::= defer_subclause",
|
|
- /* 301 */ "resolvetype ::= raisetype",
|
|
- /* 302 */ "selectnowith ::= oneselect",
|
|
- /* 303 */ "oneselect ::= values",
|
|
- /* 304 */ "sclp ::= selcollist COMMA",
|
|
- /* 305 */ "as ::= ID|STRING",
|
|
- /* 306 */ "expr ::= term",
|
|
- /* 307 */ "likeop ::= LIKE_KW|MATCH",
|
|
- /* 308 */ "exprlist ::= nexprlist",
|
|
- /* 309 */ "nmnum ::= plus_num",
|
|
- /* 310 */ "nmnum ::= nm",
|
|
- /* 311 */ "nmnum ::= ON",
|
|
- /* 312 */ "nmnum ::= DELETE",
|
|
- /* 313 */ "nmnum ::= DEFAULT",
|
|
- /* 314 */ "plus_num ::= INTEGER|FLOAT",
|
|
- /* 315 */ "foreach_clause ::=",
|
|
- /* 316 */ "foreach_clause ::= FOR EACH ROW",
|
|
- /* 317 */ "trnm ::= nm",
|
|
- /* 318 */ "tridxby ::=",
|
|
- /* 319 */ "database_kw_opt ::= DATABASE",
|
|
- /* 320 */ "database_kw_opt ::=",
|
|
- /* 321 */ "kwcolumn_opt ::=",
|
|
- /* 322 */ "kwcolumn_opt ::= COLUMNKW",
|
|
- /* 323 */ "vtabarglist ::= vtabarg",
|
|
- /* 324 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
|
|
- /* 325 */ "vtabarg ::= vtabarg vtabargtoken",
|
|
- /* 326 */ "anylist ::=",
|
|
- /* 327 */ "anylist ::= anylist LP anylist RP",
|
|
- /* 328 */ "anylist ::= anylist ANY",
|
|
+ /* 28 */ "scanpt ::=",
|
|
+ /* 29 */ "ccons ::= CONSTRAINT nm",
|
|
+ /* 30 */ "ccons ::= DEFAULT scanpt term scanpt",
|
|
+ /* 31 */ "ccons ::= DEFAULT LP expr RP",
|
|
+ /* 32 */ "ccons ::= DEFAULT PLUS term scanpt",
|
|
+ /* 33 */ "ccons ::= DEFAULT MINUS term scanpt",
|
|
+ /* 34 */ "ccons ::= DEFAULT scanpt ID|INDEXED",
|
|
+ /* 35 */ "ccons ::= NOT NULL onconf",
|
|
+ /* 36 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
|
|
+ /* 37 */ "ccons ::= UNIQUE onconf",
|
|
+ /* 38 */ "ccons ::= CHECK LP expr RP",
|
|
+ /* 39 */ "ccons ::= REFERENCES nm eidlist_opt refargs",
|
|
+ /* 40 */ "ccons ::= defer_subclause",
|
|
+ /* 41 */ "ccons ::= COLLATE ID|STRING",
|
|
+ /* 42 */ "autoinc ::=",
|
|
+ /* 43 */ "autoinc ::= AUTOINCR",
|
|
+ /* 44 */ "refargs ::=",
|
|
+ /* 45 */ "refargs ::= refargs refarg",
|
|
+ /* 46 */ "refarg ::= MATCH nm",
|
|
+ /* 47 */ "refarg ::= ON INSERT refact",
|
|
+ /* 48 */ "refarg ::= ON DELETE refact",
|
|
+ /* 49 */ "refarg ::= ON UPDATE refact",
|
|
+ /* 50 */ "refact ::= SET NULL",
|
|
+ /* 51 */ "refact ::= SET DEFAULT",
|
|
+ /* 52 */ "refact ::= CASCADE",
|
|
+ /* 53 */ "refact ::= RESTRICT",
|
|
+ /* 54 */ "refact ::= NO ACTION",
|
|
+ /* 55 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
|
|
+ /* 56 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
|
|
+ /* 57 */ "init_deferred_pred_opt ::=",
|
|
+ /* 58 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
|
|
+ /* 59 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
|
|
+ /* 60 */ "conslist_opt ::=",
|
|
+ /* 61 */ "tconscomma ::= COMMA",
|
|
+ /* 62 */ "tcons ::= CONSTRAINT nm",
|
|
+ /* 63 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf",
|
|
+ /* 64 */ "tcons ::= UNIQUE LP sortlist RP onconf",
|
|
+ /* 65 */ "tcons ::= CHECK LP expr RP onconf",
|
|
+ /* 66 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt",
|
|
+ /* 67 */ "defer_subclause_opt ::=",
|
|
+ /* 68 */ "onconf ::=",
|
|
+ /* 69 */ "onconf ::= ON CONFLICT resolvetype",
|
|
+ /* 70 */ "orconf ::=",
|
|
+ /* 71 */ "orconf ::= OR resolvetype",
|
|
+ /* 72 */ "resolvetype ::= IGNORE",
|
|
+ /* 73 */ "resolvetype ::= REPLACE",
|
|
+ /* 74 */ "cmd ::= DROP TABLE ifexists fullname",
|
|
+ /* 75 */ "ifexists ::= IF EXISTS",
|
|
+ /* 76 */ "ifexists ::=",
|
|
+ /* 77 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select",
|
|
+ /* 78 */ "cmd ::= DROP VIEW ifexists fullname",
|
|
+ /* 79 */ "cmd ::= select",
|
|
+ /* 80 */ "select ::= WITH wqlist selectnowith",
|
|
+ /* 81 */ "select ::= WITH RECURSIVE wqlist selectnowith",
|
|
+ /* 82 */ "select ::= selectnowith",
|
|
+ /* 83 */ "selectnowith ::= selectnowith multiselect_op oneselect",
|
|
+ /* 84 */ "multiselect_op ::= UNION",
|
|
+ /* 85 */ "multiselect_op ::= UNION ALL",
|
|
+ /* 86 */ "multiselect_op ::= EXCEPT|INTERSECT",
|
|
+ /* 87 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
|
|
+ /* 88 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt",
|
|
+ /* 89 */ "values ::= VALUES LP nexprlist RP",
|
|
+ /* 90 */ "values ::= values COMMA LP nexprlist RP",
|
|
+ /* 91 */ "distinct ::= DISTINCT",
|
|
+ /* 92 */ "distinct ::= ALL",
|
|
+ /* 93 */ "distinct ::=",
|
|
+ /* 94 */ "sclp ::=",
|
|
+ /* 95 */ "selcollist ::= sclp scanpt expr scanpt as",
|
|
+ /* 96 */ "selcollist ::= sclp scanpt STAR",
|
|
+ /* 97 */ "selcollist ::= sclp scanpt nm DOT STAR",
|
|
+ /* 98 */ "as ::= AS nm",
|
|
+ /* 99 */ "as ::=",
|
|
+ /* 100 */ "from ::=",
|
|
+ /* 101 */ "from ::= FROM seltablist",
|
|
+ /* 102 */ "stl_prefix ::= seltablist joinop",
|
|
+ /* 103 */ "stl_prefix ::=",
|
|
+ /* 104 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
|
|
+ /* 105 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt",
|
|
+ /* 106 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
|
|
+ /* 107 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
|
|
+ /* 108 */ "dbnm ::=",
|
|
+ /* 109 */ "dbnm ::= DOT nm",
|
|
+ /* 110 */ "fullname ::= nm",
|
|
+ /* 111 */ "fullname ::= nm DOT nm",
|
|
+ /* 112 */ "xfullname ::= nm",
|
|
+ /* 113 */ "xfullname ::= nm DOT nm",
|
|
+ /* 114 */ "xfullname ::= nm DOT nm AS nm",
|
|
+ /* 115 */ "xfullname ::= nm AS nm",
|
|
+ /* 116 */ "joinop ::= COMMA|JOIN",
|
|
+ /* 117 */ "joinop ::= JOIN_KW JOIN",
|
|
+ /* 118 */ "joinop ::= JOIN_KW nm JOIN",
|
|
+ /* 119 */ "joinop ::= JOIN_KW nm nm JOIN",
|
|
+ /* 120 */ "on_opt ::= ON expr",
|
|
+ /* 121 */ "on_opt ::=",
|
|
+ /* 122 */ "indexed_opt ::=",
|
|
+ /* 123 */ "indexed_opt ::= INDEXED BY nm",
|
|
+ /* 124 */ "indexed_opt ::= NOT INDEXED",
|
|
+ /* 125 */ "using_opt ::= USING LP idlist RP",
|
|
+ /* 126 */ "using_opt ::=",
|
|
+ /* 127 */ "orderby_opt ::=",
|
|
+ /* 128 */ "orderby_opt ::= ORDER BY sortlist",
|
|
+ /* 129 */ "sortlist ::= sortlist COMMA expr sortorder",
|
|
+ /* 130 */ "sortlist ::= expr sortorder",
|
|
+ /* 131 */ "sortorder ::= ASC",
|
|
+ /* 132 */ "sortorder ::= DESC",
|
|
+ /* 133 */ "sortorder ::=",
|
|
+ /* 134 */ "groupby_opt ::=",
|
|
+ /* 135 */ "groupby_opt ::= GROUP BY nexprlist",
|
|
+ /* 136 */ "having_opt ::=",
|
|
+ /* 137 */ "having_opt ::= HAVING expr",
|
|
+ /* 138 */ "limit_opt ::=",
|
|
+ /* 139 */ "limit_opt ::= LIMIT expr",
|
|
+ /* 140 */ "limit_opt ::= LIMIT expr OFFSET expr",
|
|
+ /* 141 */ "limit_opt ::= LIMIT expr COMMA expr",
|
|
+ /* 142 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
|
|
+ /* 143 */ "where_opt ::=",
|
|
+ /* 144 */ "where_opt ::= WHERE expr",
|
|
+ /* 145 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
|
|
+ /* 146 */ "setlist ::= setlist COMMA nm EQ expr",
|
|
+ /* 147 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
|
|
+ /* 148 */ "setlist ::= nm EQ expr",
|
|
+ /* 149 */ "setlist ::= LP idlist RP EQ expr",
|
|
+ /* 150 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
|
|
+ /* 151 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
|
|
+ /* 152 */ "upsert ::=",
|
|
+ /* 153 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
|
|
+ /* 154 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
|
|
+ /* 155 */ "upsert ::= ON CONFLICT DO NOTHING",
|
|
+ /* 156 */ "insert_cmd ::= INSERT orconf",
|
|
+ /* 157 */ "insert_cmd ::= REPLACE",
|
|
+ /* 158 */ "idlist_opt ::=",
|
|
+ /* 159 */ "idlist_opt ::= LP idlist RP",
|
|
+ /* 160 */ "idlist ::= idlist COMMA nm",
|
|
+ /* 161 */ "idlist ::= nm",
|
|
+ /* 162 */ "expr ::= LP expr RP",
|
|
+ /* 163 */ "expr ::= ID|INDEXED",
|
|
+ /* 164 */ "expr ::= JOIN_KW",
|
|
+ /* 165 */ "expr ::= nm DOT nm",
|
|
+ /* 166 */ "expr ::= nm DOT nm DOT nm",
|
|
+ /* 167 */ "term ::= NULL|FLOAT|BLOB",
|
|
+ /* 168 */ "term ::= STRING",
|
|
+ /* 169 */ "term ::= INTEGER",
|
|
+ /* 170 */ "expr ::= VARIABLE",
|
|
+ /* 171 */ "expr ::= expr COLLATE ID|STRING",
|
|
+ /* 172 */ "expr ::= CAST LP expr AS typetoken RP",
|
|
+ /* 173 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
|
|
+ /* 174 */ "expr ::= ID|INDEXED LP STAR RP",
|
|
+ /* 175 */ "expr ::= ID|INDEXED LP distinct exprlist RP over_clause",
|
|
+ /* 176 */ "expr ::= ID|INDEXED LP STAR RP over_clause",
|
|
+ /* 177 */ "term ::= CTIME_KW",
|
|
+ /* 178 */ "expr ::= LP nexprlist COMMA expr RP",
|
|
+ /* 179 */ "expr ::= expr AND expr",
|
|
+ /* 180 */ "expr ::= expr OR expr",
|
|
+ /* 181 */ "expr ::= expr LT|GT|GE|LE expr",
|
|
+ /* 182 */ "expr ::= expr EQ|NE expr",
|
|
+ /* 183 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
|
|
+ /* 184 */ "expr ::= expr PLUS|MINUS expr",
|
|
+ /* 185 */ "expr ::= expr STAR|SLASH|REM expr",
|
|
+ /* 186 */ "expr ::= expr CONCAT expr",
|
|
+ /* 187 */ "likeop ::= NOT LIKE_KW|MATCH",
|
|
+ /* 188 */ "expr ::= expr likeop expr",
|
|
+ /* 189 */ "expr ::= expr likeop expr ESCAPE expr",
|
|
+ /* 190 */ "expr ::= expr ISNULL|NOTNULL",
|
|
+ /* 191 */ "expr ::= expr NOT NULL",
|
|
+ /* 192 */ "expr ::= expr IS expr",
|
|
+ /* 193 */ "expr ::= expr IS NOT expr",
|
|
+ /* 194 */ "expr ::= NOT expr",
|
|
+ /* 195 */ "expr ::= BITNOT expr",
|
|
+ /* 196 */ "expr ::= PLUS|MINUS expr",
|
|
+ /* 197 */ "between_op ::= BETWEEN",
|
|
+ /* 198 */ "between_op ::= NOT BETWEEN",
|
|
+ /* 199 */ "expr ::= expr between_op expr AND expr",
|
|
+ /* 200 */ "in_op ::= IN",
|
|
+ /* 201 */ "in_op ::= NOT IN",
|
|
+ /* 202 */ "expr ::= expr in_op LP exprlist RP",
|
|
+ /* 203 */ "expr ::= LP select RP",
|
|
+ /* 204 */ "expr ::= expr in_op LP select RP",
|
|
+ /* 205 */ "expr ::= expr in_op nm dbnm paren_exprlist",
|
|
+ /* 206 */ "expr ::= EXISTS LP select RP",
|
|
+ /* 207 */ "expr ::= CASE case_operand case_exprlist case_else END",
|
|
+ /* 208 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
|
|
+ /* 209 */ "case_exprlist ::= WHEN expr THEN expr",
|
|
+ /* 210 */ "case_else ::= ELSE expr",
|
|
+ /* 211 */ "case_else ::=",
|
|
+ /* 212 */ "case_operand ::= expr",
|
|
+ /* 213 */ "case_operand ::=",
|
|
+ /* 214 */ "exprlist ::=",
|
|
+ /* 215 */ "nexprlist ::= nexprlist COMMA expr",
|
|
+ /* 216 */ "nexprlist ::= expr",
|
|
+ /* 217 */ "paren_exprlist ::=",
|
|
+ /* 218 */ "paren_exprlist ::= LP exprlist RP",
|
|
+ /* 219 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
|
|
+ /* 220 */ "uniqueflag ::= UNIQUE",
|
|
+ /* 221 */ "uniqueflag ::=",
|
|
+ /* 222 */ "eidlist_opt ::=",
|
|
+ /* 223 */ "eidlist_opt ::= LP eidlist RP",
|
|
+ /* 224 */ "eidlist ::= eidlist COMMA nm collate sortorder",
|
|
+ /* 225 */ "eidlist ::= nm collate sortorder",
|
|
+ /* 226 */ "collate ::=",
|
|
+ /* 227 */ "collate ::= COLLATE ID|STRING",
|
|
+ /* 228 */ "cmd ::= DROP INDEX ifexists fullname",
|
|
+ /* 229 */ "cmd ::= VACUUM",
|
|
+ /* 230 */ "cmd ::= VACUUM nm",
|
|
+ /* 231 */ "cmd ::= PRAGMA nm dbnm",
|
|
+ /* 232 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
|
|
+ /* 233 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
|
|
+ /* 234 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
|
|
+ /* 235 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
|
|
+ /* 236 */ "plus_num ::= PLUS INTEGER|FLOAT",
|
|
+ /* 237 */ "minus_num ::= MINUS INTEGER|FLOAT",
|
|
+ /* 238 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
|
|
+ /* 239 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
|
|
+ /* 240 */ "trigger_time ::= BEFORE|AFTER",
|
|
+ /* 241 */ "trigger_time ::= INSTEAD OF",
|
|
+ /* 242 */ "trigger_time ::=",
|
|
+ /* 243 */ "trigger_event ::= DELETE|INSERT",
|
|
+ /* 244 */ "trigger_event ::= UPDATE",
|
|
+ /* 245 */ "trigger_event ::= UPDATE OF idlist",
|
|
+ /* 246 */ "when_clause ::=",
|
|
+ /* 247 */ "when_clause ::= WHEN expr",
|
|
+ /* 248 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
|
|
+ /* 249 */ "trigger_cmd_list ::= trigger_cmd SEMI",
|
|
+ /* 250 */ "trnm ::= nm DOT nm",
|
|
+ /* 251 */ "tridxby ::= INDEXED BY nm",
|
|
+ /* 252 */ "tridxby ::= NOT INDEXED",
|
|
+ /* 253 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt",
|
|
+ /* 254 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
|
|
+ /* 255 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
|
|
+ /* 256 */ "trigger_cmd ::= scanpt select scanpt",
|
|
+ /* 257 */ "expr ::= RAISE LP IGNORE RP",
|
|
+ /* 258 */ "expr ::= RAISE LP raisetype COMMA nm RP",
|
|
+ /* 259 */ "raisetype ::= ROLLBACK",
|
|
+ /* 260 */ "raisetype ::= ABORT",
|
|
+ /* 261 */ "raisetype ::= FAIL",
|
|
+ /* 262 */ "cmd ::= DROP TRIGGER ifexists fullname",
|
|
+ /* 263 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
|
|
+ /* 264 */ "cmd ::= DETACH database_kw_opt expr",
|
|
+ /* 265 */ "key_opt ::=",
|
|
+ /* 266 */ "key_opt ::= KEY expr",
|
|
+ /* 267 */ "cmd ::= REINDEX",
|
|
+ /* 268 */ "cmd ::= REINDEX nm dbnm",
|
|
+ /* 269 */ "cmd ::= ANALYZE",
|
|
+ /* 270 */ "cmd ::= ANALYZE nm dbnm",
|
|
+ /* 271 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
|
|
+ /* 272 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
|
|
+ /* 273 */ "add_column_fullname ::= fullname",
|
|
+ /* 274 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
|
|
+ /* 275 */ "cmd ::= create_vtab",
|
|
+ /* 276 */ "cmd ::= create_vtab LP vtabarglist RP",
|
|
+ /* 277 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
|
|
+ /* 278 */ "vtabarg ::=",
|
|
+ /* 279 */ "vtabargtoken ::= ANY",
|
|
+ /* 280 */ "vtabargtoken ::= lp anylist RP",
|
|
+ /* 281 */ "lp ::= LP",
|
|
+ /* 282 */ "with ::= WITH wqlist",
|
|
+ /* 283 */ "with ::= WITH RECURSIVE wqlist",
|
|
+ /* 284 */ "wqlist ::= nm eidlist_opt AS LP select RP",
|
|
+ /* 285 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
|
|
+ /* 286 */ "windowdefn_list ::= windowdefn",
|
|
+ /* 287 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
|
|
+ /* 288 */ "windowdefn ::= nm AS window",
|
|
+ /* 289 */ "window ::= LP part_opt orderby_opt frame_opt RP",
|
|
+ /* 290 */ "part_opt ::= PARTITION BY nexprlist",
|
|
+ /* 291 */ "part_opt ::=",
|
|
+ /* 292 */ "frame_opt ::=",
|
|
+ /* 293 */ "frame_opt ::= range_or_rows frame_bound_s",
|
|
+ /* 294 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e",
|
|
+ /* 295 */ "range_or_rows ::= RANGE",
|
|
+ /* 296 */ "range_or_rows ::= ROWS",
|
|
+ /* 297 */ "frame_bound_s ::= frame_bound",
|
|
+ /* 298 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
|
|
+ /* 299 */ "frame_bound_e ::= frame_bound",
|
|
+ /* 300 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
|
|
+ /* 301 */ "frame_bound ::= expr PRECEDING",
|
|
+ /* 302 */ "frame_bound ::= CURRENT ROW",
|
|
+ /* 303 */ "frame_bound ::= expr FOLLOWING",
|
|
+ /* 304 */ "window_clause ::= WINDOW windowdefn_list",
|
|
+ /* 305 */ "over_clause ::= filter_opt OVER window",
|
|
+ /* 306 */ "over_clause ::= filter_opt OVER nm",
|
|
+ /* 307 */ "filter_opt ::=",
|
|
+ /* 308 */ "filter_opt ::= FILTER LP WHERE expr RP",
|
|
+ /* 309 */ "input ::= cmdlist",
|
|
+ /* 310 */ "cmdlist ::= cmdlist ecmd",
|
|
+ /* 311 */ "cmdlist ::= ecmd",
|
|
+ /* 312 */ "ecmd ::= SEMI",
|
|
+ /* 313 */ "ecmd ::= cmdx SEMI",
|
|
+ /* 314 */ "ecmd ::= explain cmdx",
|
|
+ /* 315 */ "trans_opt ::=",
|
|
+ /* 316 */ "trans_opt ::= TRANSACTION",
|
|
+ /* 317 */ "trans_opt ::= TRANSACTION nm",
|
|
+ /* 318 */ "savepoint_opt ::= SAVEPOINT",
|
|
+ /* 319 */ "savepoint_opt ::=",
|
|
+ /* 320 */ "cmd ::= create_table create_table_args",
|
|
+ /* 321 */ "columnlist ::= columnlist COMMA columnname carglist",
|
|
+ /* 322 */ "columnlist ::= columnname carglist",
|
|
+ /* 323 */ "nm ::= ID|INDEXED",
|
|
+ /* 324 */ "nm ::= STRING",
|
|
+ /* 325 */ "nm ::= JOIN_KW",
|
|
+ /* 326 */ "typetoken ::= typename",
|
|
+ /* 327 */ "typename ::= ID|STRING",
|
|
+ /* 328 */ "signed ::= plus_num",
|
|
+ /* 329 */ "signed ::= minus_num",
|
|
+ /* 330 */ "carglist ::= carglist ccons",
|
|
+ /* 331 */ "carglist ::=",
|
|
+ /* 332 */ "ccons ::= NULL onconf",
|
|
+ /* 333 */ "conslist_opt ::= COMMA conslist",
|
|
+ /* 334 */ "conslist ::= conslist tconscomma tcons",
|
|
+ /* 335 */ "conslist ::= tcons",
|
|
+ /* 336 */ "tconscomma ::=",
|
|
+ /* 337 */ "defer_subclause_opt ::= defer_subclause",
|
|
+ /* 338 */ "resolvetype ::= raisetype",
|
|
+ /* 339 */ "selectnowith ::= oneselect",
|
|
+ /* 340 */ "oneselect ::= values",
|
|
+ /* 341 */ "sclp ::= selcollist COMMA",
|
|
+ /* 342 */ "as ::= ID|STRING",
|
|
+ /* 343 */ "expr ::= term",
|
|
+ /* 344 */ "likeop ::= LIKE_KW|MATCH",
|
|
+ /* 345 */ "exprlist ::= nexprlist",
|
|
+ /* 346 */ "nmnum ::= plus_num",
|
|
+ /* 347 */ "nmnum ::= nm",
|
|
+ /* 348 */ "nmnum ::= ON",
|
|
+ /* 349 */ "nmnum ::= DELETE",
|
|
+ /* 350 */ "nmnum ::= DEFAULT",
|
|
+ /* 351 */ "plus_num ::= INTEGER|FLOAT",
|
|
+ /* 352 */ "foreach_clause ::=",
|
|
+ /* 353 */ "foreach_clause ::= FOR EACH ROW",
|
|
+ /* 354 */ "trnm ::= nm",
|
|
+ /* 355 */ "tridxby ::=",
|
|
+ /* 356 */ "database_kw_opt ::= DATABASE",
|
|
+ /* 357 */ "database_kw_opt ::=",
|
|
+ /* 358 */ "kwcolumn_opt ::=",
|
|
+ /* 359 */ "kwcolumn_opt ::= COLUMNKW",
|
|
+ /* 360 */ "vtabarglist ::= vtabarg",
|
|
+ /* 361 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
|
|
+ /* 362 */ "vtabarg ::= vtabarg vtabargtoken",
|
|
+ /* 363 */ "anylist ::=",
|
|
+ /* 364 */ "anylist ::= anylist LP anylist RP",
|
|
+ /* 365 */ "anylist ::= anylist ANY",
|
|
+ /* 366 */ "with ::=",
|
|
};
|
|
#endif /* NDEBUG */
|
|
|
|
@@ -138014,28 +148620,29 @@
|
|
|
|
/* Initialize a new parser that has already been allocated.
|
|
*/
|
|
-SQLITE_PRIVATE void sqlite3ParserInit(void *yypParser){
|
|
- yyParser *pParser = (yyParser*)yypParser;
|
|
+SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL){
|
|
+ yyParser *yypParser = (yyParser*)yypRawParser;
|
|
+ sqlite3ParserCTX_STORE
|
|
#ifdef YYTRACKMAXSTACKDEPTH
|
|
- pParser->yyhwm = 0;
|
|
+ yypParser->yyhwm = 0;
|
|
#endif
|
|
#if YYSTACKDEPTH<=0
|
|
- pParser->yytos = NULL;
|
|
- pParser->yystack = NULL;
|
|
- pParser->yystksz = 0;
|
|
- if( yyGrowStack(pParser) ){
|
|
- pParser->yystack = &pParser->yystk0;
|
|
- pParser->yystksz = 1;
|
|
+ yypParser->yytos = NULL;
|
|
+ yypParser->yystack = NULL;
|
|
+ yypParser->yystksz = 0;
|
|
+ if( yyGrowStack(yypParser) ){
|
|
+ yypParser->yystack = &yypParser->yystk0;
|
|
+ yypParser->yystksz = 1;
|
|
}
|
|
#endif
|
|
#ifndef YYNOERRORRECOVERY
|
|
- pParser->yyerrcnt = -1;
|
|
+ yypParser->yyerrcnt = -1;
|
|
#endif
|
|
- pParser->yytos = pParser->yystack;
|
|
- pParser->yystack[0].stateno = 0;
|
|
- pParser->yystack[0].major = 0;
|
|
+ yypParser->yytos = yypParser->yystack;
|
|
+ yypParser->yystack[0].stateno = 0;
|
|
+ yypParser->yystack[0].major = 0;
|
|
#if YYSTACKDEPTH>0
|
|
- pParser->yystackEnd = &pParser->yystack[YYSTACKDEPTH-1];
|
|
+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
|
|
#endif
|
|
}
|
|
|
|
@@ -138052,11 +148659,14 @@
|
|
** A pointer to a parser. This pointer is used in subsequent calls
|
|
** to sqlite3Parser and sqlite3ParserFree.
|
|
*/
|
|
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
|
|
- yyParser *pParser;
|
|
- pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
|
|
- if( pParser ) sqlite3ParserInit(pParser);
|
|
- return pParser;
|
|
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) sqlite3ParserCTX_PDECL){
|
|
+ yyParser *yypParser;
|
|
+ yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
|
|
+ if( yypParser ){
|
|
+ sqlite3ParserCTX_STORE
|
|
+ sqlite3ParserInit(yypParser sqlite3ParserCTX_PARAM);
|
|
+ }
|
|
+ return (void*)yypParser;
|
|
}
|
|
#endif /* sqlite3Parser_ENGINEALWAYSONSTACK */
|
|
|
|
@@ -138073,7 +148683,8 @@
|
|
YYCODETYPE yymajor, /* Type code for object to destroy */
|
|
YYMINORTYPE *yypminor /* The object to be destroyed */
|
|
){
|
|
- sqlite3ParserARG_FETCH;
|
|
+ sqlite3ParserARG_FETCH
|
|
+ sqlite3ParserCTX_FETCH
|
|
switch( yymajor ){
|
|
/* Here is inserted the actions which take place when a
|
|
** terminal or non-terminal is destroyed. This can happen
|
|
@@ -138086,79 +148697,98 @@
|
|
** inside the C code.
|
|
*/
|
|
/********* Begin destructor definitions ***************************************/
|
|
- case 163: /* select */
|
|
- case 194: /* selectnowith */
|
|
- case 195: /* oneselect */
|
|
- case 206: /* values */
|
|
+ case 174: /* select */
|
|
+ case 206: /* selectnowith */
|
|
+ case 207: /* oneselect */
|
|
+ case 219: /* values */
|
|
{
|
|
-sqlite3SelectDelete(pParse->db, (yypminor->yy243));
|
|
+sqlite3SelectDelete(pParse->db, (yypminor->yy489));
|
|
}
|
|
break;
|
|
- case 172: /* term */
|
|
- case 173: /* expr */
|
|
+ case 184: /* term */
|
|
+ case 185: /* expr */
|
|
+ case 213: /* where_opt */
|
|
+ case 215: /* having_opt */
|
|
+ case 227: /* on_opt */
|
|
+ case 242: /* case_operand */
|
|
+ case 244: /* case_else */
|
|
+ case 253: /* when_clause */
|
|
+ case 258: /* key_opt */
|
|
+ case 272: /* filter_opt */
|
|
{
|
|
-sqlite3ExprDelete(pParse->db, (yypminor->yy190).pExpr);
|
|
+sqlite3ExprDelete(pParse->db, (yypminor->yy18));
|
|
}
|
|
break;
|
|
- case 177: /* eidlist_opt */
|
|
- case 186: /* sortlist */
|
|
- case 187: /* eidlist */
|
|
- case 199: /* selcollist */
|
|
- case 202: /* groupby_opt */
|
|
- case 204: /* orderby_opt */
|
|
- case 207: /* nexprlist */
|
|
- case 208: /* exprlist */
|
|
- case 209: /* sclp */
|
|
- case 218: /* setlist */
|
|
- case 224: /* paren_exprlist */
|
|
- case 226: /* case_exprlist */
|
|
+ case 189: /* eidlist_opt */
|
|
+ case 198: /* sortlist */
|
|
+ case 199: /* eidlist */
|
|
+ case 211: /* selcollist */
|
|
+ case 214: /* groupby_opt */
|
|
+ case 216: /* orderby_opt */
|
|
+ case 220: /* nexprlist */
|
|
+ case 221: /* sclp */
|
|
+ case 229: /* exprlist */
|
|
+ case 233: /* setlist */
|
|
+ case 241: /* paren_exprlist */
|
|
+ case 243: /* case_exprlist */
|
|
+ case 271: /* part_opt */
|
|
{
|
|
-sqlite3ExprListDelete(pParse->db, (yypminor->yy148));
|
|
+sqlite3ExprListDelete(pParse->db, (yypminor->yy420));
|
|
}
|
|
break;
|
|
- case 193: /* fullname */
|
|
- case 200: /* from */
|
|
- case 211: /* seltablist */
|
|
- case 212: /* stl_prefix */
|
|
+ case 205: /* fullname */
|
|
+ case 212: /* from */
|
|
+ case 223: /* seltablist */
|
|
+ case 224: /* stl_prefix */
|
|
+ case 230: /* xfullname */
|
|
{
|
|
-sqlite3SrcListDelete(pParse->db, (yypminor->yy185));
|
|
+sqlite3SrcListDelete(pParse->db, (yypminor->yy135));
|
|
}
|
|
break;
|
|
- case 196: /* with */
|
|
- case 250: /* wqlist */
|
|
+ case 208: /* wqlist */
|
|
{
|
|
-sqlite3WithDelete(pParse->db, (yypminor->yy285));
|
|
+sqlite3WithDelete(pParse->db, (yypminor->yy449));
|
|
}
|
|
break;
|
|
- case 201: /* where_opt */
|
|
- case 203: /* having_opt */
|
|
- case 215: /* on_opt */
|
|
- case 225: /* case_operand */
|
|
- case 227: /* case_else */
|
|
- case 236: /* when_clause */
|
|
- case 241: /* key_opt */
|
|
+ case 218: /* window_clause */
|
|
+ case 267: /* windowdefn_list */
|
|
{
|
|
-sqlite3ExprDelete(pParse->db, (yypminor->yy72));
|
|
+sqlite3WindowListDelete(pParse->db, (yypminor->yy327));
|
|
}
|
|
break;
|
|
- case 216: /* using_opt */
|
|
- case 217: /* idlist */
|
|
- case 220: /* idlist_opt */
|
|
+ case 228: /* using_opt */
|
|
+ case 231: /* idlist */
|
|
+ case 235: /* idlist_opt */
|
|
{
|
|
-sqlite3IdListDelete(pParse->db, (yypminor->yy254));
|
|
+sqlite3IdListDelete(pParse->db, (yypminor->yy48));
|
|
}
|
|
break;
|
|
- case 232: /* trigger_cmd_list */
|
|
- case 237: /* trigger_cmd */
|
|
+ case 237: /* over_clause */
|
|
+ case 268: /* windowdefn */
|
|
+ case 269: /* window */
|
|
+ case 270: /* frame_opt */
|
|
{
|
|
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy145));
|
|
+sqlite3WindowDelete(pParse->db, (yypminor->yy327));
|
|
}
|
|
break;
|
|
- case 234: /* trigger_event */
|
|
+ case 249: /* trigger_cmd_list */
|
|
+ case 254: /* trigger_cmd */
|
|
{
|
|
-sqlite3IdListDelete(pParse->db, (yypminor->yy332).b);
|
|
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy207));
|
|
}
|
|
break;
|
|
+ case 251: /* trigger_event */
|
|
+{
|
|
+sqlite3IdListDelete(pParse->db, (yypminor->yy34).b);
|
|
+}
|
|
+ break;
|
|
+ case 274: /* frame_bound */
|
|
+ case 275: /* frame_bound_s */
|
|
+ case 276: /* frame_bound_e */
|
|
+{
|
|
+sqlite3ExprDelete(pParse->db, (yypminor->yy119).pExpr);
|
|
+}
|
|
+ break;
|
|
/********* End destructor definitions *****************************************/
|
|
default: break; /* If no destructor action specified: do nothing */
|
|
}
|
|
@@ -138227,24 +148857,66 @@
|
|
}
|
|
#endif
|
|
|
|
+/* This array of booleans keeps track of the parser statement
|
|
+** coverage. The element yycoverage[X][Y] is set when the parser
|
|
+** is in state X and has a lookahead token Y. In a well-tested
|
|
+** systems, every element of this matrix should end up being set.
|
|
+*/
|
|
+#if defined(YYCOVERAGE)
|
|
+static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
|
|
+#endif
|
|
+
|
|
/*
|
|
+** Write into out a description of every state/lookahead combination that
|
|
+**
|
|
+** (1) has not been used by the parser, and
|
|
+** (2) is not a syntax error.
|
|
+**
|
|
+** Return the number of missed state/lookahead combinations.
|
|
+*/
|
|
+#if defined(YYCOVERAGE)
|
|
+SQLITE_PRIVATE int sqlite3ParserCoverage(FILE *out){
|
|
+ int stateno, iLookAhead, i;
|
|
+ int nMissed = 0;
|
|
+ for(stateno=0; stateno<YYNSTATE; stateno++){
|
|
+ i = yy_shift_ofst[stateno];
|
|
+ for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
|
|
+ if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
|
|
+ if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
|
|
+ if( out ){
|
|
+ fprintf(out,"State %d lookahead %s %s\n", stateno,
|
|
+ yyTokenName[iLookAhead],
|
|
+ yycoverage[stateno][iLookAhead] ? "ok" : "missed");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return nMissed;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Find the appropriate action for a parser given the terminal
|
|
** look-ahead token iLookAhead.
|
|
*/
|
|
-static unsigned int yy_find_shift_action(
|
|
- yyParser *pParser, /* The parser */
|
|
- YYCODETYPE iLookAhead /* The look-ahead token */
|
|
+static YYACTIONTYPE yy_find_shift_action(
|
|
+ YYCODETYPE iLookAhead, /* The look-ahead token */
|
|
+ YYACTIONTYPE stateno /* Current state number */
|
|
){
|
|
int i;
|
|
- int stateno = pParser->yytos->stateno;
|
|
-
|
|
- if( stateno>=YY_MIN_REDUCE ) return stateno;
|
|
+
|
|
+ if( stateno>YY_MAX_SHIFT ) return stateno;
|
|
assert( stateno <= YY_SHIFT_COUNT );
|
|
+#if defined(YYCOVERAGE)
|
|
+ yycoverage[stateno][iLookAhead] = 1;
|
|
+#endif
|
|
do{
|
|
i = yy_shift_ofst[stateno];
|
|
+ assert( i>=0 );
|
|
+ /* assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); */
|
|
assert( iLookAhead!=YYNOCODE );
|
|
+ assert( iLookAhead < YYNTOKEN );
|
|
i += iLookAhead;
|
|
- if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
|
|
+ if( i>=YY_NLOOKAHEAD || yy_lookahead[i]!=iLookAhead ){
|
|
#ifdef YYFALLBACK
|
|
YYCODETYPE iFallback; /* Fallback token */
|
|
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
|
|
@@ -138270,6 +148942,7 @@
|
|
#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
|
|
j<YY_ACTTAB_COUNT &&
|
|
#endif
|
|
+ j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) &&
|
|
yy_lookahead[j]==YYWILDCARD && iLookAhead>0
|
|
){
|
|
#ifndef NDEBUG
|
|
@@ -138294,8 +148967,8 @@
|
|
** Find the appropriate action for a parser given the non-terminal
|
|
** look-ahead token iLookAhead.
|
|
*/
|
|
-static int yy_find_reduce_action(
|
|
- int stateno, /* Current state number */
|
|
+static YYACTIONTYPE yy_find_reduce_action(
|
|
+ YYACTIONTYPE stateno, /* Current state number */
|
|
YYCODETYPE iLookAhead /* The look-ahead token */
|
|
){
|
|
int i;
|
|
@@ -138307,7 +148980,6 @@
|
|
assert( stateno<=YY_REDUCE_COUNT );
|
|
#endif
|
|
i = yy_reduce_ofst[stateno];
|
|
- assert( i!=YY_REDUCE_USE_DFLT );
|
|
assert( iLookAhead!=YYNOCODE );
|
|
i += iLookAhead;
|
|
#ifdef YYERRORSYMBOL
|
|
@@ -138325,7 +148997,8 @@
|
|
** The following routine is called if the stack overflows.
|
|
*/
|
|
static void yyStackOverflow(yyParser *yypParser){
|
|
- sqlite3ParserARG_FETCH;
|
|
+ sqlite3ParserARG_FETCH
|
|
+ sqlite3ParserCTX_FETCH
|
|
#ifndef NDEBUG
|
|
if( yyTraceFILE ){
|
|
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
|
|
@@ -138338,7 +149011,8 @@
|
|
|
|
sqlite3ErrorMsg(pParse, "parser stack overflow");
|
|
/******** End %stack_overflow code ********************************************/
|
|
- sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
|
|
+ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */
|
|
+ sqlite3ParserCTX_STORE
|
|
}
|
|
|
|
/*
|
|
@@ -138345,20 +149019,21 @@
|
|
** Print tracing information for a SHIFT action
|
|
*/
|
|
#ifndef NDEBUG
|
|
-static void yyTraceShift(yyParser *yypParser, int yyNewState){
|
|
+static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
|
|
if( yyTraceFILE ){
|
|
if( yyNewState<YYNSTATE ){
|
|
- fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
|
|
- yyTracePrompt,yyTokenName[yypParser->yytos->major],
|
|
+ fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
|
|
+ yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
|
|
yyNewState);
|
|
}else{
|
|
- fprintf(yyTraceFILE,"%sShift '%s'\n",
|
|
- yyTracePrompt,yyTokenName[yypParser->yytos->major]);
|
|
+ fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
|
|
+ yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
|
|
+ yyNewState - YY_MIN_REDUCE);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
-# define yyTraceShift(X,Y)
|
|
+# define yyTraceShift(X,Y,Z)
|
|
#endif
|
|
|
|
/*
|
|
@@ -138366,8 +149041,8 @@
|
|
*/
|
|
static void yy_shift(
|
|
yyParser *yypParser, /* The parser to be shifted */
|
|
- int yyNewState, /* The new state to shift in */
|
|
- int yyMajor, /* The major token to shift in */
|
|
+ YYACTIONTYPE yyNewState, /* The new state to shift in */
|
|
+ YYCODETYPE yyMajor, /* The major token to shift in */
|
|
sqlite3ParserTOKENTYPE yyMinor /* The minor token to shift in */
|
|
){
|
|
yyStackEntry *yytos;
|
|
@@ -138397,10 +149072,10 @@
|
|
yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
|
}
|
|
yytos = yypParser->yytos;
|
|
- yytos->stateno = (YYACTIONTYPE)yyNewState;
|
|
- yytos->major = (YYCODETYPE)yyMajor;
|
|
+ yytos->stateno = yyNewState;
|
|
+ yytos->major = yyMajor;
|
|
yytos->minor.yy0 = yyMinor;
|
|
- yyTraceShift(yypParser, yyNewState);
|
|
+ yyTraceShift(yypParser, yyNewState, "Shift");
|
|
}
|
|
|
|
/* The following table contains information about every rule that
|
|
@@ -138410,335 +149085,373 @@
|
|
YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
|
|
signed char nrhs; /* Negative of the number of RHS symbols in the rule */
|
|
} yyRuleInfo[] = {
|
|
- { 147, -1 },
|
|
- { 147, -3 },
|
|
- { 148, -1 },
|
|
- { 149, -3 },
|
|
- { 150, 0 },
|
|
- { 150, -1 },
|
|
- { 150, -1 },
|
|
- { 150, -1 },
|
|
- { 149, -2 },
|
|
- { 149, -2 },
|
|
- { 149, -2 },
|
|
- { 149, -3 },
|
|
- { 149, -5 },
|
|
- { 154, -6 },
|
|
- { 156, -1 },
|
|
- { 158, 0 },
|
|
- { 158, -3 },
|
|
- { 157, -1 },
|
|
- { 157, 0 },
|
|
- { 155, -5 },
|
|
- { 155, -2 },
|
|
- { 162, 0 },
|
|
- { 162, -2 },
|
|
- { 164, -2 },
|
|
- { 166, 0 },
|
|
- { 166, -4 },
|
|
- { 166, -6 },
|
|
- { 167, -2 },
|
|
- { 171, -2 },
|
|
- { 171, -2 },
|
|
- { 171, -4 },
|
|
- { 171, -3 },
|
|
- { 171, -3 },
|
|
- { 171, -2 },
|
|
- { 171, -3 },
|
|
- { 171, -5 },
|
|
- { 171, -2 },
|
|
- { 171, -4 },
|
|
- { 171, -4 },
|
|
- { 171, -1 },
|
|
- { 171, -2 },
|
|
- { 176, 0 },
|
|
- { 176, -1 },
|
|
- { 178, 0 },
|
|
- { 178, -2 },
|
|
- { 180, -2 },
|
|
- { 180, -3 },
|
|
- { 180, -3 },
|
|
- { 180, -3 },
|
|
- { 181, -2 },
|
|
- { 181, -2 },
|
|
- { 181, -1 },
|
|
- { 181, -1 },
|
|
- { 181, -2 },
|
|
- { 179, -3 },
|
|
- { 179, -2 },
|
|
- { 182, 0 },
|
|
- { 182, -2 },
|
|
- { 182, -2 },
|
|
- { 161, 0 },
|
|
- { 184, -1 },
|
|
- { 185, -2 },
|
|
- { 185, -7 },
|
|
- { 185, -5 },
|
|
- { 185, -5 },
|
|
- { 185, -10 },
|
|
- { 188, 0 },
|
|
- { 174, 0 },
|
|
- { 174, -3 },
|
|
- { 189, 0 },
|
|
- { 189, -2 },
|
|
- { 190, -1 },
|
|
- { 190, -1 },
|
|
- { 149, -4 },
|
|
- { 192, -2 },
|
|
- { 192, 0 },
|
|
- { 149, -9 },
|
|
- { 149, -4 },
|
|
- { 149, -1 },
|
|
- { 163, -2 },
|
|
- { 194, -3 },
|
|
- { 197, -1 },
|
|
- { 197, -2 },
|
|
- { 197, -1 },
|
|
- { 195, -9 },
|
|
- { 206, -4 },
|
|
- { 206, -5 },
|
|
- { 198, -1 },
|
|
- { 198, -1 },
|
|
- { 198, 0 },
|
|
- { 209, 0 },
|
|
- { 199, -3 },
|
|
- { 199, -2 },
|
|
- { 199, -4 },
|
|
- { 210, -2 },
|
|
- { 210, 0 },
|
|
- { 200, 0 },
|
|
- { 200, -2 },
|
|
- { 212, -2 },
|
|
- { 212, 0 },
|
|
- { 211, -7 },
|
|
- { 211, -9 },
|
|
- { 211, -7 },
|
|
- { 211, -7 },
|
|
- { 159, 0 },
|
|
- { 159, -2 },
|
|
- { 193, -2 },
|
|
- { 213, -1 },
|
|
- { 213, -2 },
|
|
- { 213, -3 },
|
|
- { 213, -4 },
|
|
- { 215, -2 },
|
|
- { 215, 0 },
|
|
- { 214, 0 },
|
|
- { 214, -3 },
|
|
- { 214, -2 },
|
|
- { 216, -4 },
|
|
- { 216, 0 },
|
|
- { 204, 0 },
|
|
- { 204, -3 },
|
|
- { 186, -4 },
|
|
- { 186, -2 },
|
|
- { 175, -1 },
|
|
- { 175, -1 },
|
|
- { 175, 0 },
|
|
- { 202, 0 },
|
|
- { 202, -3 },
|
|
- { 203, 0 },
|
|
- { 203, -2 },
|
|
- { 205, 0 },
|
|
- { 205, -2 },
|
|
- { 205, -4 },
|
|
- { 205, -4 },
|
|
- { 149, -6 },
|
|
- { 201, 0 },
|
|
- { 201, -2 },
|
|
- { 149, -8 },
|
|
- { 218, -5 },
|
|
- { 218, -7 },
|
|
- { 218, -3 },
|
|
- { 218, -5 },
|
|
- { 149, -6 },
|
|
- { 149, -7 },
|
|
- { 219, -2 },
|
|
- { 219, -1 },
|
|
- { 220, 0 },
|
|
- { 220, -3 },
|
|
- { 217, -3 },
|
|
- { 217, -1 },
|
|
- { 173, -3 },
|
|
- { 173, -1 },
|
|
- { 173, -1 },
|
|
- { 173, -3 },
|
|
- { 173, -5 },
|
|
- { 172, -1 },
|
|
- { 172, -1 },
|
|
- { 172, -1 },
|
|
- { 173, -1 },
|
|
- { 173, -3 },
|
|
- { 173, -6 },
|
|
- { 173, -5 },
|
|
- { 173, -4 },
|
|
- { 172, -1 },
|
|
- { 173, -5 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 221, -2 },
|
|
- { 173, -3 },
|
|
- { 173, -5 },
|
|
- { 173, -2 },
|
|
- { 173, -3 },
|
|
- { 173, -3 },
|
|
- { 173, -4 },
|
|
- { 173, -2 },
|
|
- { 173, -2 },
|
|
- { 173, -2 },
|
|
- { 173, -2 },
|
|
- { 222, -1 },
|
|
- { 222, -2 },
|
|
- { 173, -5 },
|
|
- { 223, -1 },
|
|
- { 223, -2 },
|
|
- { 173, -5 },
|
|
- { 173, -3 },
|
|
- { 173, -5 },
|
|
- { 173, -5 },
|
|
- { 173, -4 },
|
|
- { 173, -5 },
|
|
- { 226, -5 },
|
|
- { 226, -4 },
|
|
- { 227, -2 },
|
|
- { 227, 0 },
|
|
- { 225, -1 },
|
|
- { 225, 0 },
|
|
- { 208, 0 },
|
|
- { 207, -3 },
|
|
- { 207, -1 },
|
|
- { 224, 0 },
|
|
- { 224, -3 },
|
|
- { 149, -12 },
|
|
- { 228, -1 },
|
|
- { 228, 0 },
|
|
- { 177, 0 },
|
|
- { 177, -3 },
|
|
- { 187, -5 },
|
|
- { 187, -3 },
|
|
- { 229, 0 },
|
|
- { 229, -2 },
|
|
- { 149, -4 },
|
|
- { 149, -1 },
|
|
- { 149, -2 },
|
|
- { 149, -3 },
|
|
- { 149, -5 },
|
|
- { 149, -6 },
|
|
- { 149, -5 },
|
|
- { 149, -6 },
|
|
- { 169, -2 },
|
|
- { 170, -2 },
|
|
- { 149, -5 },
|
|
- { 231, -11 },
|
|
- { 233, -1 },
|
|
- { 233, -2 },
|
|
- { 233, 0 },
|
|
- { 234, -1 },
|
|
- { 234, -1 },
|
|
- { 234, -3 },
|
|
- { 236, 0 },
|
|
- { 236, -2 },
|
|
- { 232, -3 },
|
|
- { 232, -2 },
|
|
- { 238, -3 },
|
|
- { 239, -3 },
|
|
- { 239, -2 },
|
|
- { 237, -7 },
|
|
- { 237, -5 },
|
|
- { 237, -5 },
|
|
- { 237, -1 },
|
|
- { 173, -4 },
|
|
- { 173, -6 },
|
|
- { 191, -1 },
|
|
- { 191, -1 },
|
|
- { 191, -1 },
|
|
- { 149, -4 },
|
|
- { 149, -6 },
|
|
- { 149, -3 },
|
|
- { 241, 0 },
|
|
- { 241, -2 },
|
|
- { 149, -1 },
|
|
- { 149, -3 },
|
|
- { 149, -1 },
|
|
- { 149, -3 },
|
|
- { 149, -6 },
|
|
- { 149, -7 },
|
|
- { 242, -1 },
|
|
- { 149, -1 },
|
|
- { 149, -4 },
|
|
- { 244, -8 },
|
|
- { 246, 0 },
|
|
- { 247, -1 },
|
|
- { 247, -3 },
|
|
- { 248, -1 },
|
|
- { 196, 0 },
|
|
- { 196, -2 },
|
|
- { 196, -3 },
|
|
- { 250, -6 },
|
|
- { 250, -8 },
|
|
- { 144, -1 },
|
|
- { 145, -2 },
|
|
- { 145, -1 },
|
|
- { 146, -1 },
|
|
- { 146, -3 },
|
|
- { 147, 0 },
|
|
- { 151, 0 },
|
|
- { 151, -1 },
|
|
- { 151, -2 },
|
|
- { 153, -1 },
|
|
- { 153, 0 },
|
|
- { 149, -2 },
|
|
- { 160, -4 },
|
|
- { 160, -2 },
|
|
- { 152, -1 },
|
|
- { 152, -1 },
|
|
- { 152, -1 },
|
|
- { 166, -1 },
|
|
- { 167, -1 },
|
|
- { 168, -1 },
|
|
- { 168, -1 },
|
|
- { 165, -2 },
|
|
- { 165, 0 },
|
|
- { 171, -2 },
|
|
- { 161, -2 },
|
|
- { 183, -3 },
|
|
- { 183, -1 },
|
|
- { 184, 0 },
|
|
- { 188, -1 },
|
|
- { 190, -1 },
|
|
- { 194, -1 },
|
|
- { 195, -1 },
|
|
- { 209, -2 },
|
|
- { 210, -1 },
|
|
- { 173, -1 },
|
|
- { 221, -1 },
|
|
- { 208, -1 },
|
|
- { 230, -1 },
|
|
- { 230, -1 },
|
|
- { 230, -1 },
|
|
- { 230, -1 },
|
|
- { 230, -1 },
|
|
- { 169, -1 },
|
|
- { 235, 0 },
|
|
- { 235, -3 },
|
|
- { 238, -1 },
|
|
- { 239, 0 },
|
|
- { 240, -1 },
|
|
- { 240, 0 },
|
|
- { 243, 0 },
|
|
- { 243, -1 },
|
|
- { 245, -1 },
|
|
- { 245, -3 },
|
|
- { 246, -2 },
|
|
- { 249, 0 },
|
|
- { 249, -4 },
|
|
- { 249, -2 },
|
|
+ { 159, -1 }, /* (0) explain ::= EXPLAIN */
|
|
+ { 159, -3 }, /* (1) explain ::= EXPLAIN QUERY PLAN */
|
|
+ { 158, -1 }, /* (2) cmdx ::= cmd */
|
|
+ { 160, -3 }, /* (3) cmd ::= BEGIN transtype trans_opt */
|
|
+ { 161, 0 }, /* (4) transtype ::= */
|
|
+ { 161, -1 }, /* (5) transtype ::= DEFERRED */
|
|
+ { 161, -1 }, /* (6) transtype ::= IMMEDIATE */
|
|
+ { 161, -1 }, /* (7) transtype ::= EXCLUSIVE */
|
|
+ { 160, -2 }, /* (8) cmd ::= COMMIT|END trans_opt */
|
|
+ { 160, -2 }, /* (9) cmd ::= ROLLBACK trans_opt */
|
|
+ { 160, -2 }, /* (10) cmd ::= SAVEPOINT nm */
|
|
+ { 160, -3 }, /* (11) cmd ::= RELEASE savepoint_opt nm */
|
|
+ { 160, -5 }, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
|
|
+ { 165, -6 }, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
|
|
+ { 167, -1 }, /* (14) createkw ::= CREATE */
|
|
+ { 169, 0 }, /* (15) ifnotexists ::= */
|
|
+ { 169, -3 }, /* (16) ifnotexists ::= IF NOT EXISTS */
|
|
+ { 168, -1 }, /* (17) temp ::= TEMP */
|
|
+ { 168, 0 }, /* (18) temp ::= */
|
|
+ { 166, -5 }, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
|
|
+ { 166, -2 }, /* (20) create_table_args ::= AS select */
|
|
+ { 173, 0 }, /* (21) table_options ::= */
|
|
+ { 173, -2 }, /* (22) table_options ::= WITHOUT nm */
|
|
+ { 175, -2 }, /* (23) columnname ::= nm typetoken */
|
|
+ { 177, 0 }, /* (24) typetoken ::= */
|
|
+ { 177, -4 }, /* (25) typetoken ::= typename LP signed RP */
|
|
+ { 177, -6 }, /* (26) typetoken ::= typename LP signed COMMA signed RP */
|
|
+ { 178, -2 }, /* (27) typename ::= typename ID|STRING */
|
|
+ { 182, 0 }, /* (28) scanpt ::= */
|
|
+ { 183, -2 }, /* (29) ccons ::= CONSTRAINT nm */
|
|
+ { 183, -4 }, /* (30) ccons ::= DEFAULT scanpt term scanpt */
|
|
+ { 183, -4 }, /* (31) ccons ::= DEFAULT LP expr RP */
|
|
+ { 183, -4 }, /* (32) ccons ::= DEFAULT PLUS term scanpt */
|
|
+ { 183, -4 }, /* (33) ccons ::= DEFAULT MINUS term scanpt */
|
|
+ { 183, -3 }, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */
|
|
+ { 183, -3 }, /* (35) ccons ::= NOT NULL onconf */
|
|
+ { 183, -5 }, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */
|
|
+ { 183, -2 }, /* (37) ccons ::= UNIQUE onconf */
|
|
+ { 183, -4 }, /* (38) ccons ::= CHECK LP expr RP */
|
|
+ { 183, -4 }, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */
|
|
+ { 183, -1 }, /* (40) ccons ::= defer_subclause */
|
|
+ { 183, -2 }, /* (41) ccons ::= COLLATE ID|STRING */
|
|
+ { 188, 0 }, /* (42) autoinc ::= */
|
|
+ { 188, -1 }, /* (43) autoinc ::= AUTOINCR */
|
|
+ { 190, 0 }, /* (44) refargs ::= */
|
|
+ { 190, -2 }, /* (45) refargs ::= refargs refarg */
|
|
+ { 192, -2 }, /* (46) refarg ::= MATCH nm */
|
|
+ { 192, -3 }, /* (47) refarg ::= ON INSERT refact */
|
|
+ { 192, -3 }, /* (48) refarg ::= ON DELETE refact */
|
|
+ { 192, -3 }, /* (49) refarg ::= ON UPDATE refact */
|
|
+ { 193, -2 }, /* (50) refact ::= SET NULL */
|
|
+ { 193, -2 }, /* (51) refact ::= SET DEFAULT */
|
|
+ { 193, -1 }, /* (52) refact ::= CASCADE */
|
|
+ { 193, -1 }, /* (53) refact ::= RESTRICT */
|
|
+ { 193, -2 }, /* (54) refact ::= NO ACTION */
|
|
+ { 191, -3 }, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
|
|
+ { 191, -2 }, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
|
|
+ { 194, 0 }, /* (57) init_deferred_pred_opt ::= */
|
|
+ { 194, -2 }, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */
|
|
+ { 194, -2 }, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
|
|
+ { 172, 0 }, /* (60) conslist_opt ::= */
|
|
+ { 196, -1 }, /* (61) tconscomma ::= COMMA */
|
|
+ { 197, -2 }, /* (62) tcons ::= CONSTRAINT nm */
|
|
+ { 197, -7 }, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
|
|
+ { 197, -5 }, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */
|
|
+ { 197, -5 }, /* (65) tcons ::= CHECK LP expr RP onconf */
|
|
+ { 197, -10 }, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
|
|
+ { 200, 0 }, /* (67) defer_subclause_opt ::= */
|
|
+ { 186, 0 }, /* (68) onconf ::= */
|
|
+ { 186, -3 }, /* (69) onconf ::= ON CONFLICT resolvetype */
|
|
+ { 201, 0 }, /* (70) orconf ::= */
|
|
+ { 201, -2 }, /* (71) orconf ::= OR resolvetype */
|
|
+ { 202, -1 }, /* (72) resolvetype ::= IGNORE */
|
|
+ { 202, -1 }, /* (73) resolvetype ::= REPLACE */
|
|
+ { 160, -4 }, /* (74) cmd ::= DROP TABLE ifexists fullname */
|
|
+ { 204, -2 }, /* (75) ifexists ::= IF EXISTS */
|
|
+ { 204, 0 }, /* (76) ifexists ::= */
|
|
+ { 160, -9 }, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
|
|
+ { 160, -4 }, /* (78) cmd ::= DROP VIEW ifexists fullname */
|
|
+ { 160, -1 }, /* (79) cmd ::= select */
|
|
+ { 174, -3 }, /* (80) select ::= WITH wqlist selectnowith */
|
|
+ { 174, -4 }, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */
|
|
+ { 174, -1 }, /* (82) select ::= selectnowith */
|
|
+ { 206, -3 }, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */
|
|
+ { 209, -1 }, /* (84) multiselect_op ::= UNION */
|
|
+ { 209, -2 }, /* (85) multiselect_op ::= UNION ALL */
|
|
+ { 209, -1 }, /* (86) multiselect_op ::= EXCEPT|INTERSECT */
|
|
+ { 207, -9 }, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
|
|
+ { 207, -10 }, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
|
|
+ { 219, -4 }, /* (89) values ::= VALUES LP nexprlist RP */
|
|
+ { 219, -5 }, /* (90) values ::= values COMMA LP nexprlist RP */
|
|
+ { 210, -1 }, /* (91) distinct ::= DISTINCT */
|
|
+ { 210, -1 }, /* (92) distinct ::= ALL */
|
|
+ { 210, 0 }, /* (93) distinct ::= */
|
|
+ { 221, 0 }, /* (94) sclp ::= */
|
|
+ { 211, -5 }, /* (95) selcollist ::= sclp scanpt expr scanpt as */
|
|
+ { 211, -3 }, /* (96) selcollist ::= sclp scanpt STAR */
|
|
+ { 211, -5 }, /* (97) selcollist ::= sclp scanpt nm DOT STAR */
|
|
+ { 222, -2 }, /* (98) as ::= AS nm */
|
|
+ { 222, 0 }, /* (99) as ::= */
|
|
+ { 212, 0 }, /* (100) from ::= */
|
|
+ { 212, -2 }, /* (101) from ::= FROM seltablist */
|
|
+ { 224, -2 }, /* (102) stl_prefix ::= seltablist joinop */
|
|
+ { 224, 0 }, /* (103) stl_prefix ::= */
|
|
+ { 223, -7 }, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
|
|
+ { 223, -9 }, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
|
|
+ { 223, -7 }, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
|
|
+ { 223, -7 }, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
|
|
+ { 170, 0 }, /* (108) dbnm ::= */
|
|
+ { 170, -2 }, /* (109) dbnm ::= DOT nm */
|
|
+ { 205, -1 }, /* (110) fullname ::= nm */
|
|
+ { 205, -3 }, /* (111) fullname ::= nm DOT nm */
|
|
+ { 230, -1 }, /* (112) xfullname ::= nm */
|
|
+ { 230, -3 }, /* (113) xfullname ::= nm DOT nm */
|
|
+ { 230, -5 }, /* (114) xfullname ::= nm DOT nm AS nm */
|
|
+ { 230, -3 }, /* (115) xfullname ::= nm AS nm */
|
|
+ { 225, -1 }, /* (116) joinop ::= COMMA|JOIN */
|
|
+ { 225, -2 }, /* (117) joinop ::= JOIN_KW JOIN */
|
|
+ { 225, -3 }, /* (118) joinop ::= JOIN_KW nm JOIN */
|
|
+ { 225, -4 }, /* (119) joinop ::= JOIN_KW nm nm JOIN */
|
|
+ { 227, -2 }, /* (120) on_opt ::= ON expr */
|
|
+ { 227, 0 }, /* (121) on_opt ::= */
|
|
+ { 226, 0 }, /* (122) indexed_opt ::= */
|
|
+ { 226, -3 }, /* (123) indexed_opt ::= INDEXED BY nm */
|
|
+ { 226, -2 }, /* (124) indexed_opt ::= NOT INDEXED */
|
|
+ { 228, -4 }, /* (125) using_opt ::= USING LP idlist RP */
|
|
+ { 228, 0 }, /* (126) using_opt ::= */
|
|
+ { 216, 0 }, /* (127) orderby_opt ::= */
|
|
+ { 216, -3 }, /* (128) orderby_opt ::= ORDER BY sortlist */
|
|
+ { 198, -4 }, /* (129) sortlist ::= sortlist COMMA expr sortorder */
|
|
+ { 198, -2 }, /* (130) sortlist ::= expr sortorder */
|
|
+ { 187, -1 }, /* (131) sortorder ::= ASC */
|
|
+ { 187, -1 }, /* (132) sortorder ::= DESC */
|
|
+ { 187, 0 }, /* (133) sortorder ::= */
|
|
+ { 214, 0 }, /* (134) groupby_opt ::= */
|
|
+ { 214, -3 }, /* (135) groupby_opt ::= GROUP BY nexprlist */
|
|
+ { 215, 0 }, /* (136) having_opt ::= */
|
|
+ { 215, -2 }, /* (137) having_opt ::= HAVING expr */
|
|
+ { 217, 0 }, /* (138) limit_opt ::= */
|
|
+ { 217, -2 }, /* (139) limit_opt ::= LIMIT expr */
|
|
+ { 217, -4 }, /* (140) limit_opt ::= LIMIT expr OFFSET expr */
|
|
+ { 217, -4 }, /* (141) limit_opt ::= LIMIT expr COMMA expr */
|
|
+ { 160, -6 }, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
|
|
+ { 213, 0 }, /* (143) where_opt ::= */
|
|
+ { 213, -2 }, /* (144) where_opt ::= WHERE expr */
|
|
+ { 160, -8 }, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
|
|
+ { 233, -5 }, /* (146) setlist ::= setlist COMMA nm EQ expr */
|
|
+ { 233, -7 }, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */
|
|
+ { 233, -3 }, /* (148) setlist ::= nm EQ expr */
|
|
+ { 233, -5 }, /* (149) setlist ::= LP idlist RP EQ expr */
|
|
+ { 160, -7 }, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
|
|
+ { 160, -7 }, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
|
|
+ { 236, 0 }, /* (152) upsert ::= */
|
|
+ { 236, -11 }, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
|
|
+ { 236, -8 }, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
|
|
+ { 236, -4 }, /* (155) upsert ::= ON CONFLICT DO NOTHING */
|
|
+ { 234, -2 }, /* (156) insert_cmd ::= INSERT orconf */
|
|
+ { 234, -1 }, /* (157) insert_cmd ::= REPLACE */
|
|
+ { 235, 0 }, /* (158) idlist_opt ::= */
|
|
+ { 235, -3 }, /* (159) idlist_opt ::= LP idlist RP */
|
|
+ { 231, -3 }, /* (160) idlist ::= idlist COMMA nm */
|
|
+ { 231, -1 }, /* (161) idlist ::= nm */
|
|
+ { 185, -3 }, /* (162) expr ::= LP expr RP */
|
|
+ { 185, -1 }, /* (163) expr ::= ID|INDEXED */
|
|
+ { 185, -1 }, /* (164) expr ::= JOIN_KW */
|
|
+ { 185, -3 }, /* (165) expr ::= nm DOT nm */
|
|
+ { 185, -5 }, /* (166) expr ::= nm DOT nm DOT nm */
|
|
+ { 184, -1 }, /* (167) term ::= NULL|FLOAT|BLOB */
|
|
+ { 184, -1 }, /* (168) term ::= STRING */
|
|
+ { 184, -1 }, /* (169) term ::= INTEGER */
|
|
+ { 185, -1 }, /* (170) expr ::= VARIABLE */
|
|
+ { 185, -3 }, /* (171) expr ::= expr COLLATE ID|STRING */
|
|
+ { 185, -6 }, /* (172) expr ::= CAST LP expr AS typetoken RP */
|
|
+ { 185, -5 }, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */
|
|
+ { 185, -4 }, /* (174) expr ::= ID|INDEXED LP STAR RP */
|
|
+ { 185, -6 }, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
|
|
+ { 185, -5 }, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */
|
|
+ { 184, -1 }, /* (177) term ::= CTIME_KW */
|
|
+ { 185, -5 }, /* (178) expr ::= LP nexprlist COMMA expr RP */
|
|
+ { 185, -3 }, /* (179) expr ::= expr AND expr */
|
|
+ { 185, -3 }, /* (180) expr ::= expr OR expr */
|
|
+ { 185, -3 }, /* (181) expr ::= expr LT|GT|GE|LE expr */
|
|
+ { 185, -3 }, /* (182) expr ::= expr EQ|NE expr */
|
|
+ { 185, -3 }, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
|
|
+ { 185, -3 }, /* (184) expr ::= expr PLUS|MINUS expr */
|
|
+ { 185, -3 }, /* (185) expr ::= expr STAR|SLASH|REM expr */
|
|
+ { 185, -3 }, /* (186) expr ::= expr CONCAT expr */
|
|
+ { 238, -2 }, /* (187) likeop ::= NOT LIKE_KW|MATCH */
|
|
+ { 185, -3 }, /* (188) expr ::= expr likeop expr */
|
|
+ { 185, -5 }, /* (189) expr ::= expr likeop expr ESCAPE expr */
|
|
+ { 185, -2 }, /* (190) expr ::= expr ISNULL|NOTNULL */
|
|
+ { 185, -3 }, /* (191) expr ::= expr NOT NULL */
|
|
+ { 185, -3 }, /* (192) expr ::= expr IS expr */
|
|
+ { 185, -4 }, /* (193) expr ::= expr IS NOT expr */
|
|
+ { 185, -2 }, /* (194) expr ::= NOT expr */
|
|
+ { 185, -2 }, /* (195) expr ::= BITNOT expr */
|
|
+ { 185, -2 }, /* (196) expr ::= PLUS|MINUS expr */
|
|
+ { 239, -1 }, /* (197) between_op ::= BETWEEN */
|
|
+ { 239, -2 }, /* (198) between_op ::= NOT BETWEEN */
|
|
+ { 185, -5 }, /* (199) expr ::= expr between_op expr AND expr */
|
|
+ { 240, -1 }, /* (200) in_op ::= IN */
|
|
+ { 240, -2 }, /* (201) in_op ::= NOT IN */
|
|
+ { 185, -5 }, /* (202) expr ::= expr in_op LP exprlist RP */
|
|
+ { 185, -3 }, /* (203) expr ::= LP select RP */
|
|
+ { 185, -5 }, /* (204) expr ::= expr in_op LP select RP */
|
|
+ { 185, -5 }, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */
|
|
+ { 185, -4 }, /* (206) expr ::= EXISTS LP select RP */
|
|
+ { 185, -5 }, /* (207) expr ::= CASE case_operand case_exprlist case_else END */
|
|
+ { 243, -5 }, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */
|
|
+ { 243, -4 }, /* (209) case_exprlist ::= WHEN expr THEN expr */
|
|
+ { 244, -2 }, /* (210) case_else ::= ELSE expr */
|
|
+ { 244, 0 }, /* (211) case_else ::= */
|
|
+ { 242, -1 }, /* (212) case_operand ::= expr */
|
|
+ { 242, 0 }, /* (213) case_operand ::= */
|
|
+ { 229, 0 }, /* (214) exprlist ::= */
|
|
+ { 220, -3 }, /* (215) nexprlist ::= nexprlist COMMA expr */
|
|
+ { 220, -1 }, /* (216) nexprlist ::= expr */
|
|
+ { 241, 0 }, /* (217) paren_exprlist ::= */
|
|
+ { 241, -3 }, /* (218) paren_exprlist ::= LP exprlist RP */
|
|
+ { 160, -12 }, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
|
|
+ { 245, -1 }, /* (220) uniqueflag ::= UNIQUE */
|
|
+ { 245, 0 }, /* (221) uniqueflag ::= */
|
|
+ { 189, 0 }, /* (222) eidlist_opt ::= */
|
|
+ { 189, -3 }, /* (223) eidlist_opt ::= LP eidlist RP */
|
|
+ { 199, -5 }, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */
|
|
+ { 199, -3 }, /* (225) eidlist ::= nm collate sortorder */
|
|
+ { 246, 0 }, /* (226) collate ::= */
|
|
+ { 246, -2 }, /* (227) collate ::= COLLATE ID|STRING */
|
|
+ { 160, -4 }, /* (228) cmd ::= DROP INDEX ifexists fullname */
|
|
+ { 160, -1 }, /* (229) cmd ::= VACUUM */
|
|
+ { 160, -2 }, /* (230) cmd ::= VACUUM nm */
|
|
+ { 160, -3 }, /* (231) cmd ::= PRAGMA nm dbnm */
|
|
+ { 160, -5 }, /* (232) cmd ::= PRAGMA nm dbnm EQ nmnum */
|
|
+ { 160, -6 }, /* (233) cmd ::= PRAGMA nm dbnm LP nmnum RP */
|
|
+ { 160, -5 }, /* (234) cmd ::= PRAGMA nm dbnm EQ minus_num */
|
|
+ { 160, -6 }, /* (235) cmd ::= PRAGMA nm dbnm LP minus_num RP */
|
|
+ { 180, -2 }, /* (236) plus_num ::= PLUS INTEGER|FLOAT */
|
|
+ { 181, -2 }, /* (237) minus_num ::= MINUS INTEGER|FLOAT */
|
|
+ { 160, -5 }, /* (238) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
|
|
+ { 248, -11 }, /* (239) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
|
|
+ { 250, -1 }, /* (240) trigger_time ::= BEFORE|AFTER */
|
|
+ { 250, -2 }, /* (241) trigger_time ::= INSTEAD OF */
|
|
+ { 250, 0 }, /* (242) trigger_time ::= */
|
|
+ { 251, -1 }, /* (243) trigger_event ::= DELETE|INSERT */
|
|
+ { 251, -1 }, /* (244) trigger_event ::= UPDATE */
|
|
+ { 251, -3 }, /* (245) trigger_event ::= UPDATE OF idlist */
|
|
+ { 253, 0 }, /* (246) when_clause ::= */
|
|
+ { 253, -2 }, /* (247) when_clause ::= WHEN expr */
|
|
+ { 249, -3 }, /* (248) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
|
|
+ { 249, -2 }, /* (249) trigger_cmd_list ::= trigger_cmd SEMI */
|
|
+ { 255, -3 }, /* (250) trnm ::= nm DOT nm */
|
|
+ { 256, -3 }, /* (251) tridxby ::= INDEXED BY nm */
|
|
+ { 256, -2 }, /* (252) tridxby ::= NOT INDEXED */
|
|
+ { 254, -8 }, /* (253) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
|
|
+ { 254, -8 }, /* (254) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
|
|
+ { 254, -6 }, /* (255) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
|
|
+ { 254, -3 }, /* (256) trigger_cmd ::= scanpt select scanpt */
|
|
+ { 185, -4 }, /* (257) expr ::= RAISE LP IGNORE RP */
|
|
+ { 185, -6 }, /* (258) expr ::= RAISE LP raisetype COMMA nm RP */
|
|
+ { 203, -1 }, /* (259) raisetype ::= ROLLBACK */
|
|
+ { 203, -1 }, /* (260) raisetype ::= ABORT */
|
|
+ { 203, -1 }, /* (261) raisetype ::= FAIL */
|
|
+ { 160, -4 }, /* (262) cmd ::= DROP TRIGGER ifexists fullname */
|
|
+ { 160, -6 }, /* (263) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
|
|
+ { 160, -3 }, /* (264) cmd ::= DETACH database_kw_opt expr */
|
|
+ { 258, 0 }, /* (265) key_opt ::= */
|
|
+ { 258, -2 }, /* (266) key_opt ::= KEY expr */
|
|
+ { 160, -1 }, /* (267) cmd ::= REINDEX */
|
|
+ { 160, -3 }, /* (268) cmd ::= REINDEX nm dbnm */
|
|
+ { 160, -1 }, /* (269) cmd ::= ANALYZE */
|
|
+ { 160, -3 }, /* (270) cmd ::= ANALYZE nm dbnm */
|
|
+ { 160, -6 }, /* (271) cmd ::= ALTER TABLE fullname RENAME TO nm */
|
|
+ { 160, -7 }, /* (272) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
|
|
+ { 259, -1 }, /* (273) add_column_fullname ::= fullname */
|
|
+ { 160, -8 }, /* (274) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
|
|
+ { 160, -1 }, /* (275) cmd ::= create_vtab */
|
|
+ { 160, -4 }, /* (276) cmd ::= create_vtab LP vtabarglist RP */
|
|
+ { 261, -8 }, /* (277) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
|
|
+ { 263, 0 }, /* (278) vtabarg ::= */
|
|
+ { 264, -1 }, /* (279) vtabargtoken ::= ANY */
|
|
+ { 264, -3 }, /* (280) vtabargtoken ::= lp anylist RP */
|
|
+ { 265, -1 }, /* (281) lp ::= LP */
|
|
+ { 232, -2 }, /* (282) with ::= WITH wqlist */
|
|
+ { 232, -3 }, /* (283) with ::= WITH RECURSIVE wqlist */
|
|
+ { 208, -6 }, /* (284) wqlist ::= nm eidlist_opt AS LP select RP */
|
|
+ { 208, -8 }, /* (285) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
|
|
+ { 267, -1 }, /* (286) windowdefn_list ::= windowdefn */
|
|
+ { 267, -3 }, /* (287) windowdefn_list ::= windowdefn_list COMMA windowdefn */
|
|
+ { 268, -3 }, /* (288) windowdefn ::= nm AS window */
|
|
+ { 269, -5 }, /* (289) window ::= LP part_opt orderby_opt frame_opt RP */
|
|
+ { 271, -3 }, /* (290) part_opt ::= PARTITION BY nexprlist */
|
|
+ { 271, 0 }, /* (291) part_opt ::= */
|
|
+ { 270, 0 }, /* (292) frame_opt ::= */
|
|
+ { 270, -2 }, /* (293) frame_opt ::= range_or_rows frame_bound_s */
|
|
+ { 270, -5 }, /* (294) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
|
|
+ { 273, -1 }, /* (295) range_or_rows ::= RANGE */
|
|
+ { 273, -1 }, /* (296) range_or_rows ::= ROWS */
|
|
+ { 275, -1 }, /* (297) frame_bound_s ::= frame_bound */
|
|
+ { 275, -2 }, /* (298) frame_bound_s ::= UNBOUNDED PRECEDING */
|
|
+ { 276, -1 }, /* (299) frame_bound_e ::= frame_bound */
|
|
+ { 276, -2 }, /* (300) frame_bound_e ::= UNBOUNDED FOLLOWING */
|
|
+ { 274, -2 }, /* (301) frame_bound ::= expr PRECEDING */
|
|
+ { 274, -2 }, /* (302) frame_bound ::= CURRENT ROW */
|
|
+ { 274, -2 }, /* (303) frame_bound ::= expr FOLLOWING */
|
|
+ { 218, -2 }, /* (304) window_clause ::= WINDOW windowdefn_list */
|
|
+ { 237, -3 }, /* (305) over_clause ::= filter_opt OVER window */
|
|
+ { 237, -3 }, /* (306) over_clause ::= filter_opt OVER nm */
|
|
+ { 272, 0 }, /* (307) filter_opt ::= */
|
|
+ { 272, -5 }, /* (308) filter_opt ::= FILTER LP WHERE expr RP */
|
|
+ { 155, -1 }, /* (309) input ::= cmdlist */
|
|
+ { 156, -2 }, /* (310) cmdlist ::= cmdlist ecmd */
|
|
+ { 156, -1 }, /* (311) cmdlist ::= ecmd */
|
|
+ { 157, -1 }, /* (312) ecmd ::= SEMI */
|
|
+ { 157, -2 }, /* (313) ecmd ::= cmdx SEMI */
|
|
+ { 157, -2 }, /* (314) ecmd ::= explain cmdx */
|
|
+ { 162, 0 }, /* (315) trans_opt ::= */
|
|
+ { 162, -1 }, /* (316) trans_opt ::= TRANSACTION */
|
|
+ { 162, -2 }, /* (317) trans_opt ::= TRANSACTION nm */
|
|
+ { 164, -1 }, /* (318) savepoint_opt ::= SAVEPOINT */
|
|
+ { 164, 0 }, /* (319) savepoint_opt ::= */
|
|
+ { 160, -2 }, /* (320) cmd ::= create_table create_table_args */
|
|
+ { 171, -4 }, /* (321) columnlist ::= columnlist COMMA columnname carglist */
|
|
+ { 171, -2 }, /* (322) columnlist ::= columnname carglist */
|
|
+ { 163, -1 }, /* (323) nm ::= ID|INDEXED */
|
|
+ { 163, -1 }, /* (324) nm ::= STRING */
|
|
+ { 163, -1 }, /* (325) nm ::= JOIN_KW */
|
|
+ { 177, -1 }, /* (326) typetoken ::= typename */
|
|
+ { 178, -1 }, /* (327) typename ::= ID|STRING */
|
|
+ { 179, -1 }, /* (328) signed ::= plus_num */
|
|
+ { 179, -1 }, /* (329) signed ::= minus_num */
|
|
+ { 176, -2 }, /* (330) carglist ::= carglist ccons */
|
|
+ { 176, 0 }, /* (331) carglist ::= */
|
|
+ { 183, -2 }, /* (332) ccons ::= NULL onconf */
|
|
+ { 172, -2 }, /* (333) conslist_opt ::= COMMA conslist */
|
|
+ { 195, -3 }, /* (334) conslist ::= conslist tconscomma tcons */
|
|
+ { 195, -1 }, /* (335) conslist ::= tcons */
|
|
+ { 196, 0 }, /* (336) tconscomma ::= */
|
|
+ { 200, -1 }, /* (337) defer_subclause_opt ::= defer_subclause */
|
|
+ { 202, -1 }, /* (338) resolvetype ::= raisetype */
|
|
+ { 206, -1 }, /* (339) selectnowith ::= oneselect */
|
|
+ { 207, -1 }, /* (340) oneselect ::= values */
|
|
+ { 221, -2 }, /* (341) sclp ::= selcollist COMMA */
|
|
+ { 222, -1 }, /* (342) as ::= ID|STRING */
|
|
+ { 185, -1 }, /* (343) expr ::= term */
|
|
+ { 238, -1 }, /* (344) likeop ::= LIKE_KW|MATCH */
|
|
+ { 229, -1 }, /* (345) exprlist ::= nexprlist */
|
|
+ { 247, -1 }, /* (346) nmnum ::= plus_num */
|
|
+ { 247, -1 }, /* (347) nmnum ::= nm */
|
|
+ { 247, -1 }, /* (348) nmnum ::= ON */
|
|
+ { 247, -1 }, /* (349) nmnum ::= DELETE */
|
|
+ { 247, -1 }, /* (350) nmnum ::= DEFAULT */
|
|
+ { 180, -1 }, /* (351) plus_num ::= INTEGER|FLOAT */
|
|
+ { 252, 0 }, /* (352) foreach_clause ::= */
|
|
+ { 252, -3 }, /* (353) foreach_clause ::= FOR EACH ROW */
|
|
+ { 255, -1 }, /* (354) trnm ::= nm */
|
|
+ { 256, 0 }, /* (355) tridxby ::= */
|
|
+ { 257, -1 }, /* (356) database_kw_opt ::= DATABASE */
|
|
+ { 257, 0 }, /* (357) database_kw_opt ::= */
|
|
+ { 260, 0 }, /* (358) kwcolumn_opt ::= */
|
|
+ { 260, -1 }, /* (359) kwcolumn_opt ::= COLUMNKW */
|
|
+ { 262, -1 }, /* (360) vtabarglist ::= vtabarg */
|
|
+ { 262, -3 }, /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */
|
|
+ { 263, -2 }, /* (362) vtabarg ::= vtabarg vtabargtoken */
|
|
+ { 266, 0 }, /* (363) anylist ::= */
|
|
+ { 266, -4 }, /* (364) anylist ::= anylist LP anylist RP */
|
|
+ { 266, -2 }, /* (365) anylist ::= anylist ANY */
|
|
+ { 232, 0 }, /* (366) with ::= */
|
|
};
|
|
|
|
static void yy_accept(yyParser*); /* Forward Declaration */
|
|
@@ -138746,22 +149459,39 @@
|
|
/*
|
|
** Perform a reduce action and the shift that must immediately
|
|
** follow the reduce.
|
|
+**
|
|
+** The yyLookahead and yyLookaheadToken parameters provide reduce actions
|
|
+** access to the lookahead token (if any). The yyLookahead will be YYNOCODE
|
|
+** if the lookahead token has already been consumed. As this procedure is
|
|
+** only called from one place, optimizing compilers will in-line it, which
|
|
+** means that the extra parameters have no performance impact.
|
|
*/
|
|
-static void yy_reduce(
|
|
+static YYACTIONTYPE yy_reduce(
|
|
yyParser *yypParser, /* The parser */
|
|
- unsigned int yyruleno /* Number of the rule by which to reduce */
|
|
+ unsigned int yyruleno, /* Number of the rule by which to reduce */
|
|
+ int yyLookahead, /* Lookahead token, or YYNOCODE if none */
|
|
+ sqlite3ParserTOKENTYPE yyLookaheadToken /* Value of the lookahead token */
|
|
+ sqlite3ParserCTX_PDECL /* %extra_context */
|
|
){
|
|
int yygoto; /* The next state */
|
|
- int yyact; /* The next action */
|
|
+ YYACTIONTYPE yyact; /* The next action */
|
|
yyStackEntry *yymsp; /* The top of the parser's stack */
|
|
int yysize; /* Amount to pop the stack */
|
|
- sqlite3ParserARG_FETCH;
|
|
+ sqlite3ParserARG_FETCH
|
|
+ (void)yyLookahead;
|
|
+ (void)yyLookaheadToken;
|
|
yymsp = yypParser->yytos;
|
|
#ifndef NDEBUG
|
|
if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
|
|
yysize = yyRuleInfo[yyruleno].nrhs;
|
|
- fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
|
|
- yyRuleName[yyruleno], yymsp[yysize].stateno);
|
|
+ if( yysize ){
|
|
+ fprintf(yyTraceFILE, "%sReduce %d [%s], go to state %d.\n",
|
|
+ yyTracePrompt,
|
|
+ yyruleno, yyRuleName[yyruleno], yymsp[yysize].stateno);
|
|
+ }else{
|
|
+ fprintf(yyTraceFILE, "%sReduce %d [%s].\n",
|
|
+ yyTracePrompt, yyruleno, yyRuleName[yyruleno]);
|
|
+ }
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
@@ -138778,13 +149508,19 @@
|
|
#if YYSTACKDEPTH>0
|
|
if( yypParser->yytos>=yypParser->yystackEnd ){
|
|
yyStackOverflow(yypParser);
|
|
- return;
|
|
+ /* The call to yyStackOverflow() above pops the stack until it is
|
|
+ ** empty, causing the main parser loop to exit. So the return value
|
|
+ ** is never used and does not matter. */
|
|
+ return 0;
|
|
}
|
|
#else
|
|
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
|
|
if( yyGrowStack(yypParser) ){
|
|
yyStackOverflow(yypParser);
|
|
- return;
|
|
+ /* The call to yyStackOverflow() above pops the stack until it is
|
|
+ ** empty, causing the main parser loop to exit. So the return value
|
|
+ ** is never used and does not matter. */
|
|
+ return 0;
|
|
}
|
|
yymsp = yypParser->yytos;
|
|
}
|
|
@@ -138812,15 +149548,15 @@
|
|
{ sqlite3FinishCoding(pParse); }
|
|
break;
|
|
case 3: /* cmd ::= BEGIN transtype trans_opt */
|
|
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy194);}
|
|
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy70);}
|
|
break;
|
|
case 4: /* transtype ::= */
|
|
-{yymsp[1].minor.yy194 = TK_DEFERRED;}
|
|
+{yymsp[1].minor.yy70 = TK_DEFERRED;}
|
|
break;
|
|
case 5: /* transtype ::= DEFERRED */
|
|
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
|
|
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
|
|
-{yymsp[0].minor.yy194 = yymsp[0].major; /*A-overwrites-X*/}
|
|
+{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/}
|
|
break;
|
|
case 8: /* cmd ::= COMMIT|END trans_opt */
|
|
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
|
|
@@ -138843,7 +149579,7 @@
|
|
break;
|
|
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
|
|
{
|
|
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy194,0,0,yymsp[-2].minor.yy194);
|
|
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy70,0,0,yymsp[-2].minor.yy70);
|
|
}
|
|
break;
|
|
case 14: /* createkw ::= CREATE */
|
|
@@ -138852,38 +149588,38 @@
|
|
case 15: /* ifnotexists ::= */
|
|
case 18: /* temp ::= */ yytestcase(yyruleno==18);
|
|
case 21: /* table_options ::= */ yytestcase(yyruleno==21);
|
|
- case 41: /* autoinc ::= */ yytestcase(yyruleno==41);
|
|
- case 56: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==56);
|
|
- case 66: /* defer_subclause_opt ::= */ yytestcase(yyruleno==66);
|
|
- case 75: /* ifexists ::= */ yytestcase(yyruleno==75);
|
|
- case 89: /* distinct ::= */ yytestcase(yyruleno==89);
|
|
- case 212: /* collate ::= */ yytestcase(yyruleno==212);
|
|
-{yymsp[1].minor.yy194 = 0;}
|
|
+ case 42: /* autoinc ::= */ yytestcase(yyruleno==42);
|
|
+ case 57: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==57);
|
|
+ case 67: /* defer_subclause_opt ::= */ yytestcase(yyruleno==67);
|
|
+ case 76: /* ifexists ::= */ yytestcase(yyruleno==76);
|
|
+ case 93: /* distinct ::= */ yytestcase(yyruleno==93);
|
|
+ case 226: /* collate ::= */ yytestcase(yyruleno==226);
|
|
+{yymsp[1].minor.yy70 = 0;}
|
|
break;
|
|
case 16: /* ifnotexists ::= IF NOT EXISTS */
|
|
-{yymsp[-2].minor.yy194 = 1;}
|
|
+{yymsp[-2].minor.yy70 = 1;}
|
|
break;
|
|
case 17: /* temp ::= TEMP */
|
|
- case 42: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==42);
|
|
-{yymsp[0].minor.yy194 = 1;}
|
|
+ case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43);
|
|
+{yymsp[0].minor.yy70 = 1;}
|
|
break;
|
|
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
|
|
{
|
|
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy194,0);
|
|
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy70,0);
|
|
}
|
|
break;
|
|
case 20: /* create_table_args ::= AS select */
|
|
{
|
|
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy243);
|
|
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy243);
|
|
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy489);
|
|
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489);
|
|
}
|
|
break;
|
|
case 22: /* table_options ::= WITHOUT nm */
|
|
{
|
|
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
|
|
- yymsp[-1].minor.yy194 = TF_WithoutRowid | TF_NoVisibleRowid;
|
|
+ yymsp[-1].minor.yy70 = TF_WithoutRowid | TF_NoVisibleRowid;
|
|
}else{
|
|
- yymsp[-1].minor.yy194 = 0;
|
|
+ yymsp[-1].minor.yy70 = 0;
|
|
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
|
|
}
|
|
}
|
|
@@ -138892,8 +149628,8 @@
|
|
{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
|
|
break;
|
|
case 24: /* typetoken ::= */
|
|
- case 59: /* conslist_opt ::= */ yytestcase(yyruleno==59);
|
|
- case 95: /* as ::= */ yytestcase(yyruleno==95);
|
|
+ case 60: /* conslist_opt ::= */ yytestcase(yyruleno==60);
|
|
+ case 99: /* as ::= */ yytestcase(yyruleno==99);
|
|
{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
|
|
break;
|
|
case 25: /* typetoken ::= typename LP signed RP */
|
|
@@ -138909,177 +149645,206 @@
|
|
case 27: /* typename ::= typename ID|STRING */
|
|
{yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
|
|
break;
|
|
- case 28: /* ccons ::= CONSTRAINT nm */
|
|
- case 61: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==61);
|
|
+ case 28: /* scanpt ::= */
|
|
+{
|
|
+ assert( yyLookahead!=YYNOCODE );
|
|
+ yymsp[1].minor.yy392 = yyLookaheadToken.z;
|
|
+}
|
|
+ break;
|
|
+ case 29: /* ccons ::= CONSTRAINT nm */
|
|
+ case 62: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==62);
|
|
{pParse->constraintName = yymsp[0].minor.yy0;}
|
|
break;
|
|
- case 29: /* ccons ::= DEFAULT term */
|
|
- case 31: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==31);
|
|
-{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy190);}
|
|
+ case 30: /* ccons ::= DEFAULT scanpt term scanpt */
|
|
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy392,yymsp[0].minor.yy392);}
|
|
break;
|
|
- case 30: /* ccons ::= DEFAULT LP expr RP */
|
|
-{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy190);}
|
|
+ case 31: /* ccons ::= DEFAULT LP expr RP */
|
|
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
|
|
break;
|
|
- case 32: /* ccons ::= DEFAULT MINUS term */
|
|
+ case 32: /* ccons ::= DEFAULT PLUS term scanpt */
|
|
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392);}
|
|
+ break;
|
|
+ case 33: /* ccons ::= DEFAULT MINUS term scanpt */
|
|
{
|
|
- ExprSpan v;
|
|
- v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy190.pExpr, 0);
|
|
- v.zStart = yymsp[-1].minor.yy0.z;
|
|
- v.zEnd = yymsp[0].minor.yy190.zEnd;
|
|
- sqlite3AddDefaultValue(pParse,&v);
|
|
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy18, 0);
|
|
+ sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392);
|
|
}
|
|
break;
|
|
- case 33: /* ccons ::= DEFAULT ID|INDEXED */
|
|
+ case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */
|
|
{
|
|
- ExprSpan v;
|
|
- spanExpr(&v, pParse, TK_STRING, yymsp[0].minor.yy0);
|
|
- sqlite3AddDefaultValue(pParse,&v);
|
|
+ Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0);
|
|
+ if( p ){
|
|
+ sqlite3ExprIdToTrueFalse(p);
|
|
+ testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) );
|
|
+ }
|
|
+ sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n);
|
|
}
|
|
break;
|
|
- case 34: /* ccons ::= NOT NULL onconf */
|
|
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy194);}
|
|
+ case 35: /* ccons ::= NOT NULL onconf */
|
|
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy70);}
|
|
break;
|
|
- case 35: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
|
|
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy194,yymsp[0].minor.yy194,yymsp[-2].minor.yy194);}
|
|
+ case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
|
|
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy70,yymsp[0].minor.yy70,yymsp[-2].minor.yy70);}
|
|
break;
|
|
- case 36: /* ccons ::= UNIQUE onconf */
|
|
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy194,0,0,0,0,
|
|
+ case 37: /* ccons ::= UNIQUE onconf */
|
|
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy70,0,0,0,0,
|
|
SQLITE_IDXTYPE_UNIQUE);}
|
|
break;
|
|
- case 37: /* ccons ::= CHECK LP expr RP */
|
|
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy190.pExpr);}
|
|
+ case 38: /* ccons ::= CHECK LP expr RP */
|
|
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy18);}
|
|
break;
|
|
- case 38: /* ccons ::= REFERENCES nm eidlist_opt refargs */
|
|
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy148,yymsp[0].minor.yy194);}
|
|
+ case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */
|
|
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy420,yymsp[0].minor.yy70);}
|
|
break;
|
|
- case 39: /* ccons ::= defer_subclause */
|
|
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy194);}
|
|
+ case 40: /* ccons ::= defer_subclause */
|
|
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy70);}
|
|
break;
|
|
- case 40: /* ccons ::= COLLATE ID|STRING */
|
|
+ case 41: /* ccons ::= COLLATE ID|STRING */
|
|
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
|
|
break;
|
|
- case 43: /* refargs ::= */
|
|
-{ yymsp[1].minor.yy194 = OE_None*0x0101; /* EV: R-19803-45884 */}
|
|
+ case 44: /* refargs ::= */
|
|
+{ yymsp[1].minor.yy70 = OE_None*0x0101; /* EV: R-19803-45884 */}
|
|
break;
|
|
- case 44: /* refargs ::= refargs refarg */
|
|
-{ yymsp[-1].minor.yy194 = (yymsp[-1].minor.yy194 & ~yymsp[0].minor.yy497.mask) | yymsp[0].minor.yy497.value; }
|
|
+ case 45: /* refargs ::= refargs refarg */
|
|
+{ yymsp[-1].minor.yy70 = (yymsp[-1].minor.yy70 & ~yymsp[0].minor.yy111.mask) | yymsp[0].minor.yy111.value; }
|
|
break;
|
|
- case 45: /* refarg ::= MATCH nm */
|
|
-{ yymsp[-1].minor.yy497.value = 0; yymsp[-1].minor.yy497.mask = 0x000000; }
|
|
+ case 46: /* refarg ::= MATCH nm */
|
|
+{ yymsp[-1].minor.yy111.value = 0; yymsp[-1].minor.yy111.mask = 0x000000; }
|
|
break;
|
|
- case 46: /* refarg ::= ON INSERT refact */
|
|
-{ yymsp[-2].minor.yy497.value = 0; yymsp[-2].minor.yy497.mask = 0x000000; }
|
|
+ case 47: /* refarg ::= ON INSERT refact */
|
|
+{ yymsp[-2].minor.yy111.value = 0; yymsp[-2].minor.yy111.mask = 0x000000; }
|
|
break;
|
|
- case 47: /* refarg ::= ON DELETE refact */
|
|
-{ yymsp[-2].minor.yy497.value = yymsp[0].minor.yy194; yymsp[-2].minor.yy497.mask = 0x0000ff; }
|
|
+ case 48: /* refarg ::= ON DELETE refact */
|
|
+{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70; yymsp[-2].minor.yy111.mask = 0x0000ff; }
|
|
break;
|
|
- case 48: /* refarg ::= ON UPDATE refact */
|
|
-{ yymsp[-2].minor.yy497.value = yymsp[0].minor.yy194<<8; yymsp[-2].minor.yy497.mask = 0x00ff00; }
|
|
+ case 49: /* refarg ::= ON UPDATE refact */
|
|
+{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70<<8; yymsp[-2].minor.yy111.mask = 0x00ff00; }
|
|
break;
|
|
- case 49: /* refact ::= SET NULL */
|
|
-{ yymsp[-1].minor.yy194 = OE_SetNull; /* EV: R-33326-45252 */}
|
|
+ case 50: /* refact ::= SET NULL */
|
|
+{ yymsp[-1].minor.yy70 = OE_SetNull; /* EV: R-33326-45252 */}
|
|
break;
|
|
- case 50: /* refact ::= SET DEFAULT */
|
|
-{ yymsp[-1].minor.yy194 = OE_SetDflt; /* EV: R-33326-45252 */}
|
|
+ case 51: /* refact ::= SET DEFAULT */
|
|
+{ yymsp[-1].minor.yy70 = OE_SetDflt; /* EV: R-33326-45252 */}
|
|
break;
|
|
- case 51: /* refact ::= CASCADE */
|
|
-{ yymsp[0].minor.yy194 = OE_Cascade; /* EV: R-33326-45252 */}
|
|
+ case 52: /* refact ::= CASCADE */
|
|
+{ yymsp[0].minor.yy70 = OE_Cascade; /* EV: R-33326-45252 */}
|
|
break;
|
|
- case 52: /* refact ::= RESTRICT */
|
|
-{ yymsp[0].minor.yy194 = OE_Restrict; /* EV: R-33326-45252 */}
|
|
+ case 53: /* refact ::= RESTRICT */
|
|
+{ yymsp[0].minor.yy70 = OE_Restrict; /* EV: R-33326-45252 */}
|
|
break;
|
|
- case 53: /* refact ::= NO ACTION */
|
|
-{ yymsp[-1].minor.yy194 = OE_None; /* EV: R-33326-45252 */}
|
|
+ case 54: /* refact ::= NO ACTION */
|
|
+{ yymsp[-1].minor.yy70 = OE_None; /* EV: R-33326-45252 */}
|
|
break;
|
|
- case 54: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
|
|
-{yymsp[-2].minor.yy194 = 0;}
|
|
+ case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
|
|
+{yymsp[-2].minor.yy70 = 0;}
|
|
break;
|
|
- case 55: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
|
|
- case 70: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==70);
|
|
- case 143: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==143);
|
|
-{yymsp[-1].minor.yy194 = yymsp[0].minor.yy194;}
|
|
+ case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
|
|
+ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71);
|
|
+ case 156: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==156);
|
|
+{yymsp[-1].minor.yy70 = yymsp[0].minor.yy70;}
|
|
break;
|
|
- case 57: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
|
|
- case 74: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==74);
|
|
- case 184: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==184);
|
|
- case 187: /* in_op ::= NOT IN */ yytestcase(yyruleno==187);
|
|
- case 213: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==213);
|
|
-{yymsp[-1].minor.yy194 = 1;}
|
|
+ case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
|
|
+ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75);
|
|
+ case 198: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==198);
|
|
+ case 201: /* in_op ::= NOT IN */ yytestcase(yyruleno==201);
|
|
+ case 227: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==227);
|
|
+{yymsp[-1].minor.yy70 = 1;}
|
|
break;
|
|
- case 58: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
|
|
-{yymsp[-1].minor.yy194 = 0;}
|
|
+ case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
|
|
+{yymsp[-1].minor.yy70 = 0;}
|
|
break;
|
|
- case 60: /* tconscomma ::= COMMA */
|
|
+ case 61: /* tconscomma ::= COMMA */
|
|
{pParse->constraintName.n = 0;}
|
|
break;
|
|
- case 62: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
|
|
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy148,yymsp[0].minor.yy194,yymsp[-2].minor.yy194,0);}
|
|
+ case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
|
|
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy420,yymsp[0].minor.yy70,yymsp[-2].minor.yy70,0);}
|
|
break;
|
|
- case 63: /* tcons ::= UNIQUE LP sortlist RP onconf */
|
|
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy148,yymsp[0].minor.yy194,0,0,0,0,
|
|
+ case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */
|
|
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy420,yymsp[0].minor.yy70,0,0,0,0,
|
|
SQLITE_IDXTYPE_UNIQUE);}
|
|
break;
|
|
- case 64: /* tcons ::= CHECK LP expr RP onconf */
|
|
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy190.pExpr);}
|
|
+ case 65: /* tcons ::= CHECK LP expr RP onconf */
|
|
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy18);}
|
|
break;
|
|
- case 65: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
|
|
+ case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
|
|
{
|
|
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy148, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy148, yymsp[-1].minor.yy194);
|
|
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy194);
|
|
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy420, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy70);
|
|
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy70);
|
|
}
|
|
break;
|
|
- case 67: /* onconf ::= */
|
|
- case 69: /* orconf ::= */ yytestcase(yyruleno==69);
|
|
-{yymsp[1].minor.yy194 = OE_Default;}
|
|
+ case 68: /* onconf ::= */
|
|
+ case 70: /* orconf ::= */ yytestcase(yyruleno==70);
|
|
+{yymsp[1].minor.yy70 = OE_Default;}
|
|
break;
|
|
- case 68: /* onconf ::= ON CONFLICT resolvetype */
|
|
-{yymsp[-2].minor.yy194 = yymsp[0].minor.yy194;}
|
|
+ case 69: /* onconf ::= ON CONFLICT resolvetype */
|
|
+{yymsp[-2].minor.yy70 = yymsp[0].minor.yy70;}
|
|
break;
|
|
- case 71: /* resolvetype ::= IGNORE */
|
|
-{yymsp[0].minor.yy194 = OE_Ignore;}
|
|
+ case 72: /* resolvetype ::= IGNORE */
|
|
+{yymsp[0].minor.yy70 = OE_Ignore;}
|
|
break;
|
|
- case 72: /* resolvetype ::= REPLACE */
|
|
- case 144: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==144);
|
|
-{yymsp[0].minor.yy194 = OE_Replace;}
|
|
+ case 73: /* resolvetype ::= REPLACE */
|
|
+ case 157: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==157);
|
|
+{yymsp[0].minor.yy70 = OE_Replace;}
|
|
break;
|
|
- case 73: /* cmd ::= DROP TABLE ifexists fullname */
|
|
+ case 74: /* cmd ::= DROP TABLE ifexists fullname */
|
|
{
|
|
- sqlite3DropTable(pParse, yymsp[0].minor.yy185, 0, yymsp[-1].minor.yy194);
|
|
+ sqlite3DropTable(pParse, yymsp[0].minor.yy135, 0, yymsp[-1].minor.yy70);
|
|
}
|
|
break;
|
|
- case 76: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
|
|
+ case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
|
|
{
|
|
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy148, yymsp[0].minor.yy243, yymsp[-7].minor.yy194, yymsp[-5].minor.yy194);
|
|
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[0].minor.yy489, yymsp[-7].minor.yy70, yymsp[-5].minor.yy70);
|
|
}
|
|
break;
|
|
- case 77: /* cmd ::= DROP VIEW ifexists fullname */
|
|
+ case 78: /* cmd ::= DROP VIEW ifexists fullname */
|
|
{
|
|
- sqlite3DropTable(pParse, yymsp[0].minor.yy185, 1, yymsp[-1].minor.yy194);
|
|
+ sqlite3DropTable(pParse, yymsp[0].minor.yy135, 1, yymsp[-1].minor.yy70);
|
|
}
|
|
break;
|
|
- case 78: /* cmd ::= select */
|
|
+ case 79: /* cmd ::= select */
|
|
{
|
|
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
|
|
- sqlite3Select(pParse, yymsp[0].minor.yy243, &dest);
|
|
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy243);
|
|
+ sqlite3Select(pParse, yymsp[0].minor.yy489, &dest);
|
|
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489);
|
|
}
|
|
break;
|
|
- case 79: /* select ::= with selectnowith */
|
|
+ case 80: /* select ::= WITH wqlist selectnowith */
|
|
{
|
|
- Select *p = yymsp[0].minor.yy243;
|
|
+ Select *p = yymsp[0].minor.yy489;
|
|
if( p ){
|
|
- p->pWith = yymsp[-1].minor.yy285;
|
|
+ p->pWith = yymsp[-1].minor.yy449;
|
|
parserDoubleLinkSelect(pParse, p);
|
|
}else{
|
|
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy285);
|
|
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449);
|
|
}
|
|
- yymsp[-1].minor.yy243 = p; /*A-overwrites-W*/
|
|
+ yymsp[-2].minor.yy489 = p;
|
|
}
|
|
break;
|
|
- case 80: /* selectnowith ::= selectnowith multiselect_op oneselect */
|
|
+ case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */
|
|
{
|
|
- Select *pRhs = yymsp[0].minor.yy243;
|
|
- Select *pLhs = yymsp[-2].minor.yy243;
|
|
+ Select *p = yymsp[0].minor.yy489;
|
|
+ if( p ){
|
|
+ p->pWith = yymsp[-1].minor.yy449;
|
|
+ parserDoubleLinkSelect(pParse, p);
|
|
+ }else{
|
|
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449);
|
|
+ }
|
|
+ yymsp[-3].minor.yy489 = p;
|
|
+}
|
|
+ break;
|
|
+ case 82: /* select ::= selectnowith */
|
|
+{
|
|
+ Select *p = yymsp[0].minor.yy489;
|
|
+ if( p ){
|
|
+ parserDoubleLinkSelect(pParse, p);
|
|
+ }
|
|
+ yymsp[0].minor.yy489 = p; /*A-overwrites-X*/
|
|
+}
|
|
+ break;
|
|
+ case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */
|
|
+{
|
|
+ Select *pRhs = yymsp[0].minor.yy489;
|
|
+ Select *pLhs = yymsp[-2].minor.yy489;
|
|
if( pRhs && pRhs->pPrior ){
|
|
SrcList *pFrom;
|
|
Token x;
|
|
@@ -139086,162 +149851,145 @@
|
|
x.n = 0;
|
|
parserDoubleLinkSelect(pParse, pRhs);
|
|
pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
|
|
- pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0);
|
|
+ pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
|
|
}
|
|
if( pRhs ){
|
|
- pRhs->op = (u8)yymsp[-1].minor.yy194;
|
|
+ pRhs->op = (u8)yymsp[-1].minor.yy70;
|
|
pRhs->pPrior = pLhs;
|
|
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
|
|
pRhs->selFlags &= ~SF_MultiValue;
|
|
- if( yymsp[-1].minor.yy194!=TK_ALL ) pParse->hasCompound = 1;
|
|
+ if( yymsp[-1].minor.yy70!=TK_ALL ) pParse->hasCompound = 1;
|
|
}else{
|
|
sqlite3SelectDelete(pParse->db, pLhs);
|
|
}
|
|
- yymsp[-2].minor.yy243 = pRhs;
|
|
+ yymsp[-2].minor.yy489 = pRhs;
|
|
}
|
|
break;
|
|
- case 81: /* multiselect_op ::= UNION */
|
|
- case 83: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==83);
|
|
-{yymsp[0].minor.yy194 = yymsp[0].major; /*A-overwrites-OP*/}
|
|
+ case 84: /* multiselect_op ::= UNION */
|
|
+ case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86);
|
|
+{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-OP*/}
|
|
break;
|
|
- case 82: /* multiselect_op ::= UNION ALL */
|
|
-{yymsp[-1].minor.yy194 = TK_ALL;}
|
|
+ case 85: /* multiselect_op ::= UNION ALL */
|
|
+{yymsp[-1].minor.yy70 = TK_ALL;}
|
|
break;
|
|
- case 84: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
|
|
+ case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
|
|
{
|
|
-#if SELECTTRACE_ENABLED
|
|
- Token s = yymsp[-8].minor.yy0; /*A-overwrites-S*/
|
|
-#endif
|
|
- yymsp[-8].minor.yy243 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy148,yymsp[-5].minor.yy185,yymsp[-4].minor.yy72,yymsp[-3].minor.yy148,yymsp[-2].minor.yy72,yymsp[-1].minor.yy148,yymsp[-7].minor.yy194,yymsp[0].minor.yy354.pLimit,yymsp[0].minor.yy354.pOffset);
|
|
-#if SELECTTRACE_ENABLED
|
|
- /* Populate the Select.zSelName[] string that is used to help with
|
|
- ** query planner debugging, to differentiate between multiple Select
|
|
- ** objects in a complex query.
|
|
- **
|
|
- ** If the SELECT keyword is immediately followed by a C-style comment
|
|
- ** then extract the first few alphanumeric characters from within that
|
|
- ** comment to be the zSelName value. Otherwise, the label is #N where
|
|
- ** is an integer that is incremented with each SELECT statement seen.
|
|
- */
|
|
- if( yymsp[-8].minor.yy243!=0 ){
|
|
- const char *z = s.z+6;
|
|
- int i;
|
|
- sqlite3_snprintf(sizeof(yymsp[-8].minor.yy243->zSelName), yymsp[-8].minor.yy243->zSelName, "#%d",
|
|
- ++pParse->nSelect);
|
|
- while( z[0]==' ' ) z++;
|
|
- if( z[0]=='/' && z[1]=='*' ){
|
|
- z += 2;
|
|
- while( z[0]==' ' ) z++;
|
|
- for(i=0; sqlite3Isalnum(z[i]); i++){}
|
|
- sqlite3_snprintf(sizeof(yymsp[-8].minor.yy243->zSelName), yymsp[-8].minor.yy243->zSelName, "%.*s", i, z);
|
|
- }
|
|
+ yymsp[-8].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy420,yymsp[-5].minor.yy135,yymsp[-4].minor.yy18,yymsp[-3].minor.yy420,yymsp[-2].minor.yy18,yymsp[-1].minor.yy420,yymsp[-7].minor.yy70,yymsp[0].minor.yy18);
|
|
+}
|
|
+ break;
|
|
+ case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
|
|
+{
|
|
+ yymsp[-9].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy420,yymsp[-6].minor.yy135,yymsp[-5].minor.yy18,yymsp[-4].minor.yy420,yymsp[-3].minor.yy18,yymsp[-1].minor.yy420,yymsp[-8].minor.yy70,yymsp[0].minor.yy18);
|
|
+ if( yymsp[-9].minor.yy489 ){
|
|
+ yymsp[-9].minor.yy489->pWinDefn = yymsp[-2].minor.yy327;
|
|
+ }else{
|
|
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy327);
|
|
}
|
|
-#endif /* SELECTRACE_ENABLED */
|
|
}
|
|
break;
|
|
- case 85: /* values ::= VALUES LP nexprlist RP */
|
|
+ case 89: /* values ::= VALUES LP nexprlist RP */
|
|
{
|
|
- yymsp[-3].minor.yy243 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy148,0,0,0,0,0,SF_Values,0,0);
|
|
+ yymsp[-3].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values,0);
|
|
}
|
|
break;
|
|
- case 86: /* values ::= values COMMA LP exprlist RP */
|
|
+ case 90: /* values ::= values COMMA LP nexprlist RP */
|
|
{
|
|
- Select *pRight, *pLeft = yymsp[-4].minor.yy243;
|
|
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy148,0,0,0,0,0,SF_Values|SF_MultiValue,0,0);
|
|
+ Select *pRight, *pLeft = yymsp[-4].minor.yy489;
|
|
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values|SF_MultiValue,0);
|
|
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
|
|
if( pRight ){
|
|
pRight->op = TK_ALL;
|
|
pRight->pPrior = pLeft;
|
|
- yymsp[-4].minor.yy243 = pRight;
|
|
+ yymsp[-4].minor.yy489 = pRight;
|
|
}else{
|
|
- yymsp[-4].minor.yy243 = pLeft;
|
|
+ yymsp[-4].minor.yy489 = pLeft;
|
|
}
|
|
}
|
|
break;
|
|
- case 87: /* distinct ::= DISTINCT */
|
|
-{yymsp[0].minor.yy194 = SF_Distinct;}
|
|
+ case 91: /* distinct ::= DISTINCT */
|
|
+{yymsp[0].minor.yy70 = SF_Distinct;}
|
|
break;
|
|
- case 88: /* distinct ::= ALL */
|
|
-{yymsp[0].minor.yy194 = SF_All;}
|
|
+ case 92: /* distinct ::= ALL */
|
|
+{yymsp[0].minor.yy70 = SF_All;}
|
|
break;
|
|
- case 90: /* sclp ::= */
|
|
- case 118: /* orderby_opt ::= */ yytestcase(yyruleno==118);
|
|
- case 125: /* groupby_opt ::= */ yytestcase(yyruleno==125);
|
|
- case 200: /* exprlist ::= */ yytestcase(yyruleno==200);
|
|
- case 203: /* paren_exprlist ::= */ yytestcase(yyruleno==203);
|
|
- case 208: /* eidlist_opt ::= */ yytestcase(yyruleno==208);
|
|
-{yymsp[1].minor.yy148 = 0;}
|
|
+ case 94: /* sclp ::= */
|
|
+ case 127: /* orderby_opt ::= */ yytestcase(yyruleno==127);
|
|
+ case 134: /* groupby_opt ::= */ yytestcase(yyruleno==134);
|
|
+ case 214: /* exprlist ::= */ yytestcase(yyruleno==214);
|
|
+ case 217: /* paren_exprlist ::= */ yytestcase(yyruleno==217);
|
|
+ case 222: /* eidlist_opt ::= */ yytestcase(yyruleno==222);
|
|
+{yymsp[1].minor.yy420 = 0;}
|
|
break;
|
|
- case 91: /* selcollist ::= sclp expr as */
|
|
+ case 95: /* selcollist ::= sclp scanpt expr scanpt as */
|
|
{
|
|
- yymsp[-2].minor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy148, yymsp[-1].minor.yy190.pExpr);
|
|
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-2].minor.yy148, &yymsp[0].minor.yy0, 1);
|
|
- sqlite3ExprListSetSpan(pParse,yymsp[-2].minor.yy148,&yymsp[-1].minor.yy190);
|
|
+ yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[-2].minor.yy18);
|
|
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[0].minor.yy0, 1);
|
|
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy420,yymsp[-3].minor.yy392,yymsp[-1].minor.yy392);
|
|
}
|
|
break;
|
|
- case 92: /* selcollist ::= sclp STAR */
|
|
+ case 96: /* selcollist ::= sclp scanpt STAR */
|
|
{
|
|
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
|
|
- yymsp[-1].minor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy148, p);
|
|
+ yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy420, p);
|
|
}
|
|
break;
|
|
- case 93: /* selcollist ::= sclp nm DOT STAR */
|
|
+ case 97: /* selcollist ::= sclp scanpt nm DOT STAR */
|
|
{
|
|
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
|
|
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
|
|
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
|
|
- yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, pDot);
|
|
+ yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, pDot);
|
|
}
|
|
break;
|
|
- case 94: /* as ::= AS nm */
|
|
- case 105: /* dbnm ::= DOT nm */ yytestcase(yyruleno==105);
|
|
- case 222: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==222);
|
|
- case 223: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==223);
|
|
+ case 98: /* as ::= AS nm */
|
|
+ case 109: /* dbnm ::= DOT nm */ yytestcase(yyruleno==109);
|
|
+ case 236: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==236);
|
|
+ case 237: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==237);
|
|
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
|
|
break;
|
|
- case 96: /* from ::= */
|
|
-{yymsp[1].minor.yy185 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy185));}
|
|
+ case 100: /* from ::= */
|
|
+{yymsp[1].minor.yy135 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy135));}
|
|
break;
|
|
- case 97: /* from ::= FROM seltablist */
|
|
+ case 101: /* from ::= FROM seltablist */
|
|
{
|
|
- yymsp[-1].minor.yy185 = yymsp[0].minor.yy185;
|
|
- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy185);
|
|
+ yymsp[-1].minor.yy135 = yymsp[0].minor.yy135;
|
|
+ sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy135);
|
|
}
|
|
break;
|
|
- case 98: /* stl_prefix ::= seltablist joinop */
|
|
+ case 102: /* stl_prefix ::= seltablist joinop */
|
|
{
|
|
- if( ALWAYS(yymsp[-1].minor.yy185 && yymsp[-1].minor.yy185->nSrc>0) ) yymsp[-1].minor.yy185->a[yymsp[-1].minor.yy185->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy194;
|
|
+ if( ALWAYS(yymsp[-1].minor.yy135 && yymsp[-1].minor.yy135->nSrc>0) ) yymsp[-1].minor.yy135->a[yymsp[-1].minor.yy135->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy70;
|
|
}
|
|
break;
|
|
- case 99: /* stl_prefix ::= */
|
|
-{yymsp[1].minor.yy185 = 0;}
|
|
+ case 103: /* stl_prefix ::= */
|
|
+{yymsp[1].minor.yy135 = 0;}
|
|
break;
|
|
- case 100: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
|
|
+ case 104: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
|
|
{
|
|
- yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
|
|
- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy185, &yymsp[-2].minor.yy0);
|
|
+ yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
|
|
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy135, &yymsp[-2].minor.yy0);
|
|
}
|
|
break;
|
|
- case 101: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
|
|
+ case 105: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
|
|
{
|
|
- yymsp[-8].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy185,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
|
|
- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy185, yymsp[-4].minor.yy148);
|
|
+ yymsp[-8].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy135,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
|
|
+ sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy135, yymsp[-4].minor.yy420);
|
|
}
|
|
break;
|
|
- case 102: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
|
|
+ case 106: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
|
|
{
|
|
- yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy243,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
|
|
+ yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy489,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
|
|
}
|
|
break;
|
|
- case 103: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
|
|
+ case 107: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
|
|
{
|
|
- if( yymsp[-6].minor.yy185==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy72==0 && yymsp[0].minor.yy254==0 ){
|
|
- yymsp[-6].minor.yy185 = yymsp[-4].minor.yy185;
|
|
- }else if( yymsp[-4].minor.yy185->nSrc==1 ){
|
|
- yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
|
|
- if( yymsp[-6].minor.yy185 ){
|
|
- struct SrcList_item *pNew = &yymsp[-6].minor.yy185->a[yymsp[-6].minor.yy185->nSrc-1];
|
|
- struct SrcList_item *pOld = yymsp[-4].minor.yy185->a;
|
|
+ if( yymsp[-6].minor.yy135==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy18==0 && yymsp[0].minor.yy48==0 ){
|
|
+ yymsp[-6].minor.yy135 = yymsp[-4].minor.yy135;
|
|
+ }else if( yymsp[-4].minor.yy135->nSrc==1 ){
|
|
+ yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
|
|
+ if( yymsp[-6].minor.yy135 ){
|
|
+ struct SrcList_item *pNew = &yymsp[-6].minor.yy135->a[yymsp[-6].minor.yy135->nSrc-1];
|
|
+ struct SrcList_item *pOld = yymsp[-4].minor.yy135->a;
|
|
pNew->zName = pOld->zName;
|
|
pNew->zDatabase = pOld->zDatabase;
|
|
pNew->pSelect = pOld->pSelect;
|
|
@@ -139248,199 +149996,240 @@
|
|
pOld->zName = pOld->zDatabase = 0;
|
|
pOld->pSelect = 0;
|
|
}
|
|
- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy185);
|
|
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy135);
|
|
}else{
|
|
Select *pSubquery;
|
|
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy185);
|
|
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy185,0,0,0,0,SF_NestedFrom,0,0);
|
|
- yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
|
|
+ sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy135);
|
|
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy135,0,0,0,0,SF_NestedFrom,0);
|
|
+ yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
|
|
}
|
|
}
|
|
break;
|
|
- case 104: /* dbnm ::= */
|
|
- case 113: /* indexed_opt ::= */ yytestcase(yyruleno==113);
|
|
+ case 108: /* dbnm ::= */
|
|
+ case 122: /* indexed_opt ::= */ yytestcase(yyruleno==122);
|
|
{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
|
|
break;
|
|
- case 106: /* fullname ::= nm dbnm */
|
|
-{yymsp[-1].minor.yy185 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
|
|
+ case 110: /* fullname ::= nm */
|
|
+{
|
|
+ yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0);
|
|
+ if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0);
|
|
+}
|
|
+ yymsp[0].minor.yy135 = yylhsminor.yy135;
|
|
break;
|
|
- case 107: /* joinop ::= COMMA|JOIN */
|
|
-{ yymsp[0].minor.yy194 = JT_INNER; }
|
|
+ case 111: /* fullname ::= nm DOT nm */
|
|
+{
|
|
+ yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
|
|
+ if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0);
|
|
+}
|
|
+ yymsp[-2].minor.yy135 = yylhsminor.yy135;
|
|
break;
|
|
- case 108: /* joinop ::= JOIN_KW JOIN */
|
|
-{yymsp[-1].minor.yy194 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
|
|
+ case 112: /* xfullname ::= nm */
|
|
+{yymsp[0].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
|
|
break;
|
|
- case 109: /* joinop ::= JOIN_KW nm JOIN */
|
|
-{yymsp[-2].minor.yy194 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
|
|
+ case 113: /* xfullname ::= nm DOT nm */
|
|
+{yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
|
|
break;
|
|
- case 110: /* joinop ::= JOIN_KW nm nm JOIN */
|
|
-{yymsp[-3].minor.yy194 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
|
|
+ case 114: /* xfullname ::= nm DOT nm AS nm */
|
|
+{
|
|
+ yymsp[-4].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
|
|
+ if( yymsp[-4].minor.yy135 ) yymsp[-4].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
|
|
+}
|
|
break;
|
|
- case 111: /* on_opt ::= ON expr */
|
|
- case 128: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==128);
|
|
- case 135: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==135);
|
|
- case 196: /* case_else ::= ELSE expr */ yytestcase(yyruleno==196);
|
|
-{yymsp[-1].minor.yy72 = yymsp[0].minor.yy190.pExpr;}
|
|
+ case 115: /* xfullname ::= nm AS nm */
|
|
+{
|
|
+ yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
|
|
+ if( yymsp[-2].minor.yy135 ) yymsp[-2].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
|
|
+}
|
|
break;
|
|
- case 112: /* on_opt ::= */
|
|
- case 127: /* having_opt ::= */ yytestcase(yyruleno==127);
|
|
- case 134: /* where_opt ::= */ yytestcase(yyruleno==134);
|
|
- case 197: /* case_else ::= */ yytestcase(yyruleno==197);
|
|
- case 199: /* case_operand ::= */ yytestcase(yyruleno==199);
|
|
-{yymsp[1].minor.yy72 = 0;}
|
|
+ case 116: /* joinop ::= COMMA|JOIN */
|
|
+{ yymsp[0].minor.yy70 = JT_INNER; }
|
|
break;
|
|
- case 114: /* indexed_opt ::= INDEXED BY nm */
|
|
+ case 117: /* joinop ::= JOIN_KW JOIN */
|
|
+{yymsp[-1].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
|
|
+ break;
|
|
+ case 118: /* joinop ::= JOIN_KW nm JOIN */
|
|
+{yymsp[-2].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
|
|
+ break;
|
|
+ case 119: /* joinop ::= JOIN_KW nm nm JOIN */
|
|
+{yymsp[-3].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
|
|
+ break;
|
|
+ case 120: /* on_opt ::= ON expr */
|
|
+ case 137: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==137);
|
|
+ case 144: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==144);
|
|
+ case 210: /* case_else ::= ELSE expr */ yytestcase(yyruleno==210);
|
|
+{yymsp[-1].minor.yy18 = yymsp[0].minor.yy18;}
|
|
+ break;
|
|
+ case 121: /* on_opt ::= */
|
|
+ case 136: /* having_opt ::= */ yytestcase(yyruleno==136);
|
|
+ case 138: /* limit_opt ::= */ yytestcase(yyruleno==138);
|
|
+ case 143: /* where_opt ::= */ yytestcase(yyruleno==143);
|
|
+ case 211: /* case_else ::= */ yytestcase(yyruleno==211);
|
|
+ case 213: /* case_operand ::= */ yytestcase(yyruleno==213);
|
|
+{yymsp[1].minor.yy18 = 0;}
|
|
+ break;
|
|
+ case 123: /* indexed_opt ::= INDEXED BY nm */
|
|
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
|
|
break;
|
|
- case 115: /* indexed_opt ::= NOT INDEXED */
|
|
+ case 124: /* indexed_opt ::= NOT INDEXED */
|
|
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
|
|
break;
|
|
- case 116: /* using_opt ::= USING LP idlist RP */
|
|
-{yymsp[-3].minor.yy254 = yymsp[-1].minor.yy254;}
|
|
+ case 125: /* using_opt ::= USING LP idlist RP */
|
|
+{yymsp[-3].minor.yy48 = yymsp[-1].minor.yy48;}
|
|
break;
|
|
- case 117: /* using_opt ::= */
|
|
- case 145: /* idlist_opt ::= */ yytestcase(yyruleno==145);
|
|
-{yymsp[1].minor.yy254 = 0;}
|
|
+ case 126: /* using_opt ::= */
|
|
+ case 158: /* idlist_opt ::= */ yytestcase(yyruleno==158);
|
|
+{yymsp[1].minor.yy48 = 0;}
|
|
break;
|
|
- case 119: /* orderby_opt ::= ORDER BY sortlist */
|
|
- case 126: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==126);
|
|
-{yymsp[-2].minor.yy148 = yymsp[0].minor.yy148;}
|
|
+ case 128: /* orderby_opt ::= ORDER BY sortlist */
|
|
+ case 135: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==135);
|
|
+{yymsp[-2].minor.yy420 = yymsp[0].minor.yy420;}
|
|
break;
|
|
- case 120: /* sortlist ::= sortlist COMMA expr sortorder */
|
|
+ case 129: /* sortlist ::= sortlist COMMA expr sortorder */
|
|
{
|
|
- yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148,yymsp[-1].minor.yy190.pExpr);
|
|
- sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy148,yymsp[0].minor.yy194);
|
|
+ yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420,yymsp[-1].minor.yy18);
|
|
+ sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy420,yymsp[0].minor.yy70);
|
|
}
|
|
break;
|
|
- case 121: /* sortlist ::= expr sortorder */
|
|
+ case 130: /* sortlist ::= expr sortorder */
|
|
{
|
|
- yymsp[-1].minor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy190.pExpr); /*A-overwrites-Y*/
|
|
- sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy148,yymsp[0].minor.yy194);
|
|
+ yymsp[-1].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy18); /*A-overwrites-Y*/
|
|
+ sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy420,yymsp[0].minor.yy70);
|
|
}
|
|
break;
|
|
- case 122: /* sortorder ::= ASC */
|
|
-{yymsp[0].minor.yy194 = SQLITE_SO_ASC;}
|
|
+ case 131: /* sortorder ::= ASC */
|
|
+{yymsp[0].minor.yy70 = SQLITE_SO_ASC;}
|
|
break;
|
|
- case 123: /* sortorder ::= DESC */
|
|
-{yymsp[0].minor.yy194 = SQLITE_SO_DESC;}
|
|
+ case 132: /* sortorder ::= DESC */
|
|
+{yymsp[0].minor.yy70 = SQLITE_SO_DESC;}
|
|
break;
|
|
- case 124: /* sortorder ::= */
|
|
-{yymsp[1].minor.yy194 = SQLITE_SO_UNDEFINED;}
|
|
+ case 133: /* sortorder ::= */
|
|
+{yymsp[1].minor.yy70 = SQLITE_SO_UNDEFINED;}
|
|
break;
|
|
- case 129: /* limit_opt ::= */
|
|
-{yymsp[1].minor.yy354.pLimit = 0; yymsp[1].minor.yy354.pOffset = 0;}
|
|
+ case 139: /* limit_opt ::= LIMIT expr */
|
|
+{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,0);}
|
|
break;
|
|
- case 130: /* limit_opt ::= LIMIT expr */
|
|
-{yymsp[-1].minor.yy354.pLimit = yymsp[0].minor.yy190.pExpr; yymsp[-1].minor.yy354.pOffset = 0;}
|
|
+ case 140: /* limit_opt ::= LIMIT expr OFFSET expr */
|
|
+{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);}
|
|
break;
|
|
- case 131: /* limit_opt ::= LIMIT expr OFFSET expr */
|
|
-{yymsp[-3].minor.yy354.pLimit = yymsp[-2].minor.yy190.pExpr; yymsp[-3].minor.yy354.pOffset = yymsp[0].minor.yy190.pExpr;}
|
|
+ case 141: /* limit_opt ::= LIMIT expr COMMA expr */
|
|
+{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,yymsp[-2].minor.yy18);}
|
|
break;
|
|
- case 132: /* limit_opt ::= LIMIT expr COMMA expr */
|
|
-{yymsp[-3].minor.yy354.pOffset = yymsp[-2].minor.yy190.pExpr; yymsp[-3].minor.yy354.pLimit = yymsp[0].minor.yy190.pExpr;}
|
|
- break;
|
|
- case 133: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */
|
|
+ case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
|
|
{
|
|
- sqlite3WithPush(pParse, yymsp[-5].minor.yy285, 1);
|
|
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy185, &yymsp[-1].minor.yy0);
|
|
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy185,yymsp[0].minor.yy72);
|
|
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy135, &yymsp[-1].minor.yy0);
|
|
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy135,yymsp[0].minor.yy18,0,0);
|
|
}
|
|
break;
|
|
- case 136: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */
|
|
+ case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
|
|
{
|
|
- sqlite3WithPush(pParse, yymsp[-7].minor.yy285, 1);
|
|
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy185, &yymsp[-3].minor.yy0);
|
|
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy148,"set list");
|
|
- sqlite3Update(pParse,yymsp[-4].minor.yy185,yymsp[-1].minor.yy148,yymsp[0].minor.yy72,yymsp[-5].minor.yy194);
|
|
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy135, &yymsp[-3].minor.yy0);
|
|
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy420,"set list");
|
|
+ sqlite3Update(pParse,yymsp[-4].minor.yy135,yymsp[-1].minor.yy420,yymsp[0].minor.yy18,yymsp[-5].minor.yy70,0,0,0);
|
|
}
|
|
break;
|
|
- case 137: /* setlist ::= setlist COMMA nm EQ expr */
|
|
+ case 146: /* setlist ::= setlist COMMA nm EQ expr */
|
|
{
|
|
- yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy148, yymsp[0].minor.yy190.pExpr);
|
|
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy148, &yymsp[-2].minor.yy0, 1);
|
|
+ yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[0].minor.yy18);
|
|
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, 1);
|
|
}
|
|
break;
|
|
- case 138: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
|
|
+ case 147: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
|
|
{
|
|
- yymsp[-6].minor.yy148 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy148, yymsp[-3].minor.yy254, yymsp[0].minor.yy190.pExpr);
|
|
+ yymsp[-6].minor.yy420 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy420, yymsp[-3].minor.yy48, yymsp[0].minor.yy18);
|
|
}
|
|
break;
|
|
- case 139: /* setlist ::= nm EQ expr */
|
|
+ case 148: /* setlist ::= nm EQ expr */
|
|
{
|
|
- yylhsminor.yy148 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy190.pExpr);
|
|
- sqlite3ExprListSetName(pParse, yylhsminor.yy148, &yymsp[-2].minor.yy0, 1);
|
|
+ yylhsminor.yy420 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy18);
|
|
+ sqlite3ExprListSetName(pParse, yylhsminor.yy420, &yymsp[-2].minor.yy0, 1);
|
|
}
|
|
- yymsp[-2].minor.yy148 = yylhsminor.yy148;
|
|
+ yymsp[-2].minor.yy420 = yylhsminor.yy420;
|
|
break;
|
|
- case 140: /* setlist ::= LP idlist RP EQ expr */
|
|
+ case 149: /* setlist ::= LP idlist RP EQ expr */
|
|
{
|
|
- yymsp[-4].minor.yy148 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy190.pExpr);
|
|
+ yymsp[-4].minor.yy420 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy48, yymsp[0].minor.yy18);
|
|
}
|
|
break;
|
|
- case 141: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */
|
|
+ case 150: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
|
|
{
|
|
- sqlite3WithPush(pParse, yymsp[-5].minor.yy285, 1);
|
|
- sqlite3Insert(pParse, yymsp[-2].minor.yy185, yymsp[0].minor.yy243, yymsp[-1].minor.yy254, yymsp[-4].minor.yy194);
|
|
+ sqlite3Insert(pParse, yymsp[-3].minor.yy135, yymsp[-1].minor.yy489, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, yymsp[0].minor.yy340);
|
|
}
|
|
break;
|
|
- case 142: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */
|
|
+ case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
|
|
{
|
|
- sqlite3WithPush(pParse, yymsp[-6].minor.yy285, 1);
|
|
- sqlite3Insert(pParse, yymsp[-3].minor.yy185, 0, yymsp[-2].minor.yy254, yymsp[-5].minor.yy194);
|
|
+ sqlite3Insert(pParse, yymsp[-3].minor.yy135, 0, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, 0);
|
|
}
|
|
break;
|
|
- case 146: /* idlist_opt ::= LP idlist RP */
|
|
-{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;}
|
|
+ case 152: /* upsert ::= */
|
|
+{ yymsp[1].minor.yy340 = 0; }
|
|
break;
|
|
- case 147: /* idlist ::= idlist COMMA nm */
|
|
-{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
|
|
+ case 153: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
|
|
+{ yymsp[-10].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy420,yymsp[-5].minor.yy18,yymsp[-1].minor.yy420,yymsp[0].minor.yy18);}
|
|
break;
|
|
- case 148: /* idlist ::= nm */
|
|
-{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
|
|
+ case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
|
|
+{ yymsp[-7].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy420,yymsp[-2].minor.yy18,0,0); }
|
|
break;
|
|
- case 149: /* expr ::= LP expr RP */
|
|
-{spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/ yymsp[-2].minor.yy190.pExpr = yymsp[-1].minor.yy190.pExpr;}
|
|
+ case 155: /* upsert ::= ON CONFLICT DO NOTHING */
|
|
+{ yymsp[-3].minor.yy340 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
|
|
break;
|
|
- case 150: /* expr ::= ID|INDEXED */
|
|
- case 151: /* expr ::= JOIN_KW */ yytestcase(yyruleno==151);
|
|
-{spanExpr(&yymsp[0].minor.yy190,pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
|
|
+ case 159: /* idlist_opt ::= LP idlist RP */
|
|
+{yymsp[-2].minor.yy48 = yymsp[-1].minor.yy48;}
|
|
break;
|
|
- case 152: /* expr ::= nm DOT nm */
|
|
+ case 160: /* idlist ::= idlist COMMA nm */
|
|
+{yymsp[-2].minor.yy48 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy48,&yymsp[0].minor.yy0);}
|
|
+ break;
|
|
+ case 161: /* idlist ::= nm */
|
|
+{yymsp[0].minor.yy48 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
|
|
+ break;
|
|
+ case 162: /* expr ::= LP expr RP */
|
|
+{yymsp[-2].minor.yy18 = yymsp[-1].minor.yy18;}
|
|
+ break;
|
|
+ case 163: /* expr ::= ID|INDEXED */
|
|
+ case 164: /* expr ::= JOIN_KW */ yytestcase(yyruleno==164);
|
|
+{yymsp[0].minor.yy18=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
|
|
+ break;
|
|
+ case 165: /* expr ::= nm DOT nm */
|
|
{
|
|
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
|
|
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
|
|
- spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
|
|
- yymsp[-2].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
|
|
+ sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
|
|
+ }
|
|
+ yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
|
|
}
|
|
+ yymsp[-2].minor.yy18 = yylhsminor.yy18;
|
|
break;
|
|
- case 153: /* expr ::= nm DOT nm DOT nm */
|
|
+ case 166: /* expr ::= nm DOT nm DOT nm */
|
|
{
|
|
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
|
|
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
|
|
Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
|
|
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
|
|
- spanSet(&yymsp[-4].minor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
|
|
+ if( IN_RENAME_OBJECT ){
|
|
+ sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
|
|
+ sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
|
|
+ }
|
|
+ yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
|
|
}
|
|
+ yymsp[-4].minor.yy18 = yylhsminor.yy18;
|
|
break;
|
|
- case 154: /* term ::= NULL|FLOAT|BLOB */
|
|
- case 155: /* term ::= STRING */ yytestcase(yyruleno==155);
|
|
-{spanExpr(&yymsp[0].minor.yy190,pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
|
|
+ case 167: /* term ::= NULL|FLOAT|BLOB */
|
|
+ case 168: /* term ::= STRING */ yytestcase(yyruleno==168);
|
|
+{yymsp[0].minor.yy18=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
|
|
break;
|
|
- case 156: /* term ::= INTEGER */
|
|
+ case 169: /* term ::= INTEGER */
|
|
{
|
|
- yylhsminor.yy190.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
|
|
- yylhsminor.yy190.zStart = yymsp[0].minor.yy0.z;
|
|
- yylhsminor.yy190.zEnd = yymsp[0].minor.yy0.z + yymsp[0].minor.yy0.n;
|
|
+ yylhsminor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
|
|
}
|
|
- yymsp[0].minor.yy190 = yylhsminor.yy190;
|
|
+ yymsp[0].minor.yy18 = yylhsminor.yy18;
|
|
break;
|
|
- case 157: /* expr ::= VARIABLE */
|
|
+ case 170: /* expr ::= VARIABLE */
|
|
{
|
|
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
|
|
u32 n = yymsp[0].minor.yy0.n;
|
|
- spanExpr(&yymsp[0].minor.yy190, pParse, TK_VARIABLE, yymsp[0].minor.yy0);
|
|
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy190.pExpr, n);
|
|
+ yymsp[0].minor.yy18 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
|
|
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy18, n);
|
|
}else{
|
|
/* When doing a nested parse, one can include terms in an expression
|
|
** that look like this: #1 #2 ... These terms refer to registers
|
|
@@ -139447,159 +150236,156 @@
|
|
** in the virtual machine. #N is the N-th register. */
|
|
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
|
|
assert( t.n>=2 );
|
|
- spanSet(&yymsp[0].minor.yy190, &t, &t);
|
|
if( pParse->nested==0 ){
|
|
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
|
|
- yymsp[0].minor.yy190.pExpr = 0;
|
|
+ yymsp[0].minor.yy18 = 0;
|
|
}else{
|
|
- yymsp[0].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
|
|
- if( yymsp[0].minor.yy190.pExpr ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy190.pExpr->iTable);
|
|
+ yymsp[0].minor.yy18 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
|
|
+ if( yymsp[0].minor.yy18 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy18->iTable);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
- case 158: /* expr ::= expr COLLATE ID|STRING */
|
|
+ case 171: /* expr ::= expr COLLATE ID|STRING */
|
|
{
|
|
- yymsp[-2].minor.yy190.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy190.pExpr, &yymsp[0].minor.yy0, 1);
|
|
- yymsp[-2].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
|
|
+ yymsp[-2].minor.yy18 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy18, &yymsp[0].minor.yy0, 1);
|
|
}
|
|
break;
|
|
- case 159: /* expr ::= CAST LP expr AS typetoken RP */
|
|
+ case 172: /* expr ::= CAST LP expr AS typetoken RP */
|
|
{
|
|
- spanSet(&yymsp[-5].minor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
|
|
- yymsp[-5].minor.yy190.pExpr = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
|
|
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy190.pExpr, yymsp[-3].minor.yy190.pExpr, 0);
|
|
+ yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
|
|
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy18, yymsp[-3].minor.yy18, 0);
|
|
}
|
|
break;
|
|
- case 160: /* expr ::= ID|INDEXED LP distinct exprlist RP */
|
|
+ case 173: /* expr ::= ID|INDEXED LP distinct exprlist RP */
|
|
{
|
|
- if( yymsp[-1].minor.yy148 && yymsp[-1].minor.yy148->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
|
|
- sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
|
|
- }
|
|
- yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy148, &yymsp[-4].minor.yy0);
|
|
- spanSet(&yylhsminor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
|
|
- if( yymsp[-2].minor.yy194==SF_Distinct && yylhsminor.yy190.pExpr ){
|
|
- yylhsminor.yy190.pExpr->flags |= EP_Distinct;
|
|
- }
|
|
+ yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy420, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy70);
|
|
}
|
|
- yymsp[-4].minor.yy190 = yylhsminor.yy190;
|
|
+ yymsp[-4].minor.yy18 = yylhsminor.yy18;
|
|
break;
|
|
- case 161: /* expr ::= ID|INDEXED LP STAR RP */
|
|
+ case 174: /* expr ::= ID|INDEXED LP STAR RP */
|
|
{
|
|
- yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
|
|
- spanSet(&yylhsminor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
|
|
+ yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
|
|
}
|
|
- yymsp[-3].minor.yy190 = yylhsminor.yy190;
|
|
+ yymsp[-3].minor.yy18 = yylhsminor.yy18;
|
|
break;
|
|
- case 162: /* term ::= CTIME_KW */
|
|
+ case 175: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
|
|
{
|
|
- yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0);
|
|
- spanSet(&yylhsminor.yy190, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
|
|
+ yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy420, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy70);
|
|
+ sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327);
|
|
}
|
|
- yymsp[0].minor.yy190 = yylhsminor.yy190;
|
|
+ yymsp[-5].minor.yy18 = yylhsminor.yy18;
|
|
break;
|
|
- case 163: /* expr ::= LP nexprlist COMMA expr RP */
|
|
+ case 176: /* expr ::= ID|INDEXED LP STAR RP over_clause */
|
|
{
|
|
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy148, yymsp[-1].minor.yy190.pExpr);
|
|
- yylhsminor.yy190.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
|
- if( yylhsminor.yy190.pExpr ){
|
|
- yylhsminor.yy190.pExpr->x.pList = pList;
|
|
- spanSet(&yylhsminor.yy190, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
|
|
+ yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
|
|
+ sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327);
|
|
+}
|
|
+ yymsp[-4].minor.yy18 = yylhsminor.yy18;
|
|
+ break;
|
|
+ case 177: /* term ::= CTIME_KW */
|
|
+{
|
|
+ yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
|
|
+}
|
|
+ yymsp[0].minor.yy18 = yylhsminor.yy18;
|
|
+ break;
|
|
+ case 178: /* expr ::= LP nexprlist COMMA expr RP */
|
|
+{
|
|
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy420, yymsp[-1].minor.yy18);
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
|
+ if( yymsp[-4].minor.yy18 ){
|
|
+ yymsp[-4].minor.yy18->x.pList = pList;
|
|
}else{
|
|
sqlite3ExprListDelete(pParse->db, pList);
|
|
}
|
|
}
|
|
- yymsp[-4].minor.yy190 = yylhsminor.yy190;
|
|
break;
|
|
- case 164: /* expr ::= expr AND expr */
|
|
- case 165: /* expr ::= expr OR expr */ yytestcase(yyruleno==165);
|
|
- case 166: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==166);
|
|
- case 167: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==167);
|
|
- case 168: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==168);
|
|
- case 169: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==169);
|
|
- case 170: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==170);
|
|
- case 171: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==171);
|
|
-{spanBinaryExpr(pParse,yymsp[-1].major,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);}
|
|
+ case 179: /* expr ::= expr AND expr */
|
|
+ case 180: /* expr ::= expr OR expr */ yytestcase(yyruleno==180);
|
|
+ case 181: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==181);
|
|
+ case 182: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==182);
|
|
+ case 183: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==183);
|
|
+ case 184: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==184);
|
|
+ case 185: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==185);
|
|
+ case 186: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==186);
|
|
+{yymsp[-2].minor.yy18=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);}
|
|
break;
|
|
- case 172: /* likeop ::= NOT LIKE_KW|MATCH */
|
|
+ case 187: /* likeop ::= NOT LIKE_KW|MATCH */
|
|
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
|
|
break;
|
|
- case 173: /* expr ::= expr likeop expr */
|
|
+ case 188: /* expr ::= expr likeop expr */
|
|
{
|
|
ExprList *pList;
|
|
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
|
|
yymsp[-1].minor.yy0.n &= 0x7fffffff;
|
|
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy190.pExpr);
|
|
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy190.pExpr);
|
|
- yymsp[-2].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0);
|
|
- exprNot(pParse, bNot, &yymsp[-2].minor.yy190);
|
|
- yymsp[-2].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
|
|
- if( yymsp[-2].minor.yy190.pExpr ) yymsp[-2].minor.yy190.pExpr->flags |= EP_InfixFunc;
|
|
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy18);
|
|
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy18);
|
|
+ yymsp[-2].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
|
|
+ if( bNot ) yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy18, 0);
|
|
+ if( yymsp[-2].minor.yy18 ) yymsp[-2].minor.yy18->flags |= EP_InfixFunc;
|
|
}
|
|
break;
|
|
- case 174: /* expr ::= expr likeop expr ESCAPE expr */
|
|
+ case 189: /* expr ::= expr likeop expr ESCAPE expr */
|
|
{
|
|
ExprList *pList;
|
|
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
|
|
yymsp[-3].minor.yy0.n &= 0x7fffffff;
|
|
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
|
|
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy190.pExpr);
|
|
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr);
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0);
|
|
- exprNot(pParse, bNot, &yymsp[-4].minor.yy190);
|
|
- yymsp[-4].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
|
|
- if( yymsp[-4].minor.yy190.pExpr ) yymsp[-4].minor.yy190.pExpr->flags |= EP_InfixFunc;
|
|
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18);
|
|
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy18);
|
|
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18);
|
|
+ yymsp[-4].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
|
|
+ if( bNot ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
|
|
+ if( yymsp[-4].minor.yy18 ) yymsp[-4].minor.yy18->flags |= EP_InfixFunc;
|
|
}
|
|
break;
|
|
- case 175: /* expr ::= expr ISNULL|NOTNULL */
|
|
-{spanUnaryPostfix(pParse,yymsp[0].major,&yymsp[-1].minor.yy190,&yymsp[0].minor.yy0);}
|
|
+ case 190: /* expr ::= expr ISNULL|NOTNULL */
|
|
+{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy18,0);}
|
|
break;
|
|
- case 176: /* expr ::= expr NOT NULL */
|
|
-{spanUnaryPostfix(pParse,TK_NOTNULL,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy0);}
|
|
+ case 191: /* expr ::= expr NOT NULL */
|
|
+{yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy18,0);}
|
|
break;
|
|
- case 177: /* expr ::= expr IS expr */
|
|
+ case 192: /* expr ::= expr IS expr */
|
|
{
|
|
- spanBinaryExpr(pParse,TK_IS,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);
|
|
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy190.pExpr, yymsp[-2].minor.yy190.pExpr, TK_ISNULL);
|
|
+ yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);
|
|
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-2].minor.yy18, TK_ISNULL);
|
|
}
|
|
break;
|
|
- case 178: /* expr ::= expr IS NOT expr */
|
|
+ case 193: /* expr ::= expr IS NOT expr */
|
|
{
|
|
- spanBinaryExpr(pParse,TK_ISNOT,&yymsp[-3].minor.yy190,&yymsp[0].minor.yy190);
|
|
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy190.pExpr, yymsp[-3].minor.yy190.pExpr, TK_NOTNULL);
|
|
+ yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy18,yymsp[0].minor.yy18);
|
|
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-3].minor.yy18, TK_NOTNULL);
|
|
}
|
|
break;
|
|
- case 179: /* expr ::= NOT expr */
|
|
- case 180: /* expr ::= BITNOT expr */ yytestcase(yyruleno==180);
|
|
-{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,yymsp[-1].major,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
|
|
+ case 194: /* expr ::= NOT expr */
|
|
+ case 195: /* expr ::= BITNOT expr */ yytestcase(yyruleno==195);
|
|
+{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy18, 0);/*A-overwrites-B*/}
|
|
break;
|
|
- case 181: /* expr ::= MINUS expr */
|
|
-{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,TK_UMINUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
|
|
+ case 196: /* expr ::= PLUS|MINUS expr */
|
|
+{
|
|
+ yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy18, 0);
|
|
+ /*A-overwrites-B*/
|
|
+}
|
|
break;
|
|
- case 182: /* expr ::= PLUS expr */
|
|
-{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,TK_UPLUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
|
|
+ case 197: /* between_op ::= BETWEEN */
|
|
+ case 200: /* in_op ::= IN */ yytestcase(yyruleno==200);
|
|
+{yymsp[0].minor.yy70 = 0;}
|
|
break;
|
|
- case 183: /* between_op ::= BETWEEN */
|
|
- case 186: /* in_op ::= IN */ yytestcase(yyruleno==186);
|
|
-{yymsp[0].minor.yy194 = 0;}
|
|
- break;
|
|
- case 185: /* expr ::= expr between_op expr AND expr */
|
|
+ case 199: /* expr ::= expr between_op expr AND expr */
|
|
{
|
|
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
|
|
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr);
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy190.pExpr, 0);
|
|
- if( yymsp[-4].minor.yy190.pExpr ){
|
|
- yymsp[-4].minor.yy190.pExpr->x.pList = pList;
|
|
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18);
|
|
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18);
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy18, 0);
|
|
+ if( yymsp[-4].minor.yy18 ){
|
|
+ yymsp[-4].minor.yy18->x.pList = pList;
|
|
}else{
|
|
sqlite3ExprListDelete(pParse->db, pList);
|
|
}
|
|
- exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
|
|
- yymsp[-4].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
|
|
+ if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
|
|
}
|
|
break;
|
|
- case 188: /* expr ::= expr in_op LP exprlist RP */
|
|
+ case 202: /* expr ::= expr in_op LP exprlist RP */
|
|
{
|
|
- if( yymsp[-1].minor.yy148==0 ){
|
|
+ if( yymsp[-1].minor.yy420==0 ){
|
|
/* Expressions of the form
|
|
**
|
|
** expr1 IN ()
|
|
@@ -139608,9 +150394,9 @@
|
|
** simplify to constants 0 (false) and 1 (true), respectively,
|
|
** regardless of the value of expr1.
|
|
*/
|
|
- sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy190.pExpr);
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy194],1);
|
|
- }else if( yymsp[-1].minor.yy148->nExpr==1 ){
|
|
+ sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy18);
|
|
+ yymsp[-4].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy70],1);
|
|
+ }else if( yymsp[-1].minor.yy420->nExpr==1 ){
|
|
/* Expressions of the form:
|
|
**
|
|
** expr1 IN (?1)
|
|
@@ -139627,9 +150413,9 @@
|
|
** affinity or the collating sequence to use for comparison. Otherwise,
|
|
** the semantics would be subtly different from IN or NOT IN.
|
|
*/
|
|
- Expr *pRHS = yymsp[-1].minor.yy148->a[0].pExpr;
|
|
- yymsp[-1].minor.yy148->a[0].pExpr = 0;
|
|
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy148);
|
|
+ Expr *pRHS = yymsp[-1].minor.yy420->a[0].pExpr;
|
|
+ yymsp[-1].minor.yy420->a[0].pExpr = 0;
|
|
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420);
|
|
/* pRHS cannot be NULL because a malloc error would have been detected
|
|
** before now and control would have never reached this point */
|
|
if( ALWAYS(pRHS) ){
|
|
@@ -139636,192 +150422,190 @@
|
|
pRHS->flags &= ~EP_Collate;
|
|
pRHS->flags |= EP_Generic;
|
|
}
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, yymsp[-3].minor.yy194 ? TK_NE : TK_EQ, yymsp[-4].minor.yy190.pExpr, pRHS);
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, yymsp[-3].minor.yy70 ? TK_NE : TK_EQ, yymsp[-4].minor.yy18, pRHS);
|
|
}else{
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0);
|
|
- if( yymsp[-4].minor.yy190.pExpr ){
|
|
- yymsp[-4].minor.yy190.pExpr->x.pList = yymsp[-1].minor.yy148;
|
|
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy190.pExpr);
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0);
|
|
+ if( yymsp[-4].minor.yy18 ){
|
|
+ yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy420;
|
|
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18);
|
|
}else{
|
|
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy148);
|
|
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420);
|
|
}
|
|
- exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
|
|
+ if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
|
|
}
|
|
- yymsp[-4].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
|
|
}
|
|
break;
|
|
- case 189: /* expr ::= LP select RP */
|
|
+ case 203: /* expr ::= LP select RP */
|
|
{
|
|
- spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
|
|
- yymsp[-2].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
|
|
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy190.pExpr, yymsp[-1].minor.yy243);
|
|
+ yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
|
|
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy18, yymsp[-1].minor.yy489);
|
|
}
|
|
break;
|
|
- case 190: /* expr ::= expr in_op LP select RP */
|
|
+ case 204: /* expr ::= expr in_op LP select RP */
|
|
{
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0);
|
|
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy190.pExpr, yymsp[-1].minor.yy243);
|
|
- exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
|
|
- yymsp[-4].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0);
|
|
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, yymsp[-1].minor.yy489);
|
|
+ if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
|
|
}
|
|
break;
|
|
- case 191: /* expr ::= expr in_op nm dbnm paren_exprlist */
|
|
+ case 205: /* expr ::= expr in_op nm dbnm paren_exprlist */
|
|
{
|
|
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
|
|
- Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
|
|
- if( yymsp[0].minor.yy148 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy148);
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0);
|
|
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy190.pExpr, pSelect);
|
|
- exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
|
|
- yymsp[-4].minor.yy190.zEnd = yymsp[-1].minor.yy0.z ? &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n] : &yymsp[-2].minor.yy0.z[yymsp[-2].minor.yy0.n];
|
|
+ Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
|
|
+ if( yymsp[0].minor.yy420 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy420);
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0);
|
|
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, pSelect);
|
|
+ if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
|
|
}
|
|
break;
|
|
- case 192: /* expr ::= EXISTS LP select RP */
|
|
+ case 206: /* expr ::= EXISTS LP select RP */
|
|
{
|
|
Expr *p;
|
|
- spanSet(&yymsp[-3].minor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
|
|
- p = yymsp[-3].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
|
|
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy243);
|
|
+ p = yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
|
|
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy489);
|
|
}
|
|
break;
|
|
- case 193: /* expr ::= CASE case_operand case_exprlist case_else END */
|
|
+ case 207: /* expr ::= CASE case_operand case_exprlist case_else END */
|
|
{
|
|
- spanSet(&yymsp[-4].minor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-C*/
|
|
- yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy72, 0);
|
|
- if( yymsp[-4].minor.yy190.pExpr ){
|
|
- yymsp[-4].minor.yy190.pExpr->x.pList = yymsp[-1].minor.yy72 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy148,yymsp[-1].minor.yy72) : yymsp[-2].minor.yy148;
|
|
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy190.pExpr);
|
|
+ yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy18, 0);
|
|
+ if( yymsp[-4].minor.yy18 ){
|
|
+ yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy18 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[-1].minor.yy18) : yymsp[-2].minor.yy420;
|
|
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18);
|
|
}else{
|
|
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy148);
|
|
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy72);
|
|
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy420);
|
|
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy18);
|
|
}
|
|
}
|
|
break;
|
|
- case 194: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
|
|
+ case 208: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
|
|
{
|
|
- yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[-2].minor.yy190.pExpr);
|
|
- yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[0].minor.yy190.pExpr);
|
|
+ yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[-2].minor.yy18);
|
|
+ yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[0].minor.yy18);
|
|
}
|
|
break;
|
|
- case 195: /* case_exprlist ::= WHEN expr THEN expr */
|
|
+ case 209: /* case_exprlist ::= WHEN expr THEN expr */
|
|
{
|
|
- yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
|
|
- yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, yymsp[0].minor.yy190.pExpr);
|
|
+ yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18);
|
|
+ yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420, yymsp[0].minor.yy18);
|
|
}
|
|
break;
|
|
- case 198: /* case_operand ::= expr */
|
|
-{yymsp[0].minor.yy72 = yymsp[0].minor.yy190.pExpr; /*A-overwrites-X*/}
|
|
+ case 212: /* case_operand ::= expr */
|
|
+{yymsp[0].minor.yy18 = yymsp[0].minor.yy18; /*A-overwrites-X*/}
|
|
break;
|
|
- case 201: /* nexprlist ::= nexprlist COMMA expr */
|
|
-{yymsp[-2].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy148,yymsp[0].minor.yy190.pExpr);}
|
|
+ case 215: /* nexprlist ::= nexprlist COMMA expr */
|
|
+{yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[0].minor.yy18);}
|
|
break;
|
|
- case 202: /* nexprlist ::= expr */
|
|
-{yymsp[0].minor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy190.pExpr); /*A-overwrites-Y*/}
|
|
+ case 216: /* nexprlist ::= expr */
|
|
+{yymsp[0].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy18); /*A-overwrites-Y*/}
|
|
break;
|
|
- case 204: /* paren_exprlist ::= LP exprlist RP */
|
|
- case 209: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==209);
|
|
-{yymsp[-2].minor.yy148 = yymsp[-1].minor.yy148;}
|
|
+ case 218: /* paren_exprlist ::= LP exprlist RP */
|
|
+ case 223: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==223);
|
|
+{yymsp[-2].minor.yy420 = yymsp[-1].minor.yy420;}
|
|
break;
|
|
- case 205: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
|
|
+ case 219: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
|
|
{
|
|
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
|
|
- sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy148, yymsp[-10].minor.yy194,
|
|
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy72, SQLITE_SO_ASC, yymsp[-8].minor.yy194, SQLITE_IDXTYPE_APPDEF);
|
|
+ sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy420, yymsp[-10].minor.yy70,
|
|
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy18, SQLITE_SO_ASC, yymsp[-8].minor.yy70, SQLITE_IDXTYPE_APPDEF);
|
|
+ if( IN_RENAME_OBJECT && pParse->pNewIndex ){
|
|
+ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
|
|
+ }
|
|
}
|
|
break;
|
|
- case 206: /* uniqueflag ::= UNIQUE */
|
|
- case 246: /* raisetype ::= ABORT */ yytestcase(yyruleno==246);
|
|
-{yymsp[0].minor.yy194 = OE_Abort;}
|
|
+ case 220: /* uniqueflag ::= UNIQUE */
|
|
+ case 260: /* raisetype ::= ABORT */ yytestcase(yyruleno==260);
|
|
+{yymsp[0].minor.yy70 = OE_Abort;}
|
|
break;
|
|
- case 207: /* uniqueflag ::= */
|
|
-{yymsp[1].minor.yy194 = OE_None;}
|
|
+ case 221: /* uniqueflag ::= */
|
|
+{yymsp[1].minor.yy70 = OE_None;}
|
|
break;
|
|
- case 210: /* eidlist ::= eidlist COMMA nm collate sortorder */
|
|
+ case 224: /* eidlist ::= eidlist COMMA nm collate sortorder */
|
|
{
|
|
- yymsp[-4].minor.yy148 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy148, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy194, yymsp[0].minor.yy194);
|
|
+ yymsp[-4].minor.yy420 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70);
|
|
}
|
|
break;
|
|
- case 211: /* eidlist ::= nm collate sortorder */
|
|
+ case 225: /* eidlist ::= nm collate sortorder */
|
|
{
|
|
- yymsp[-2].minor.yy148 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy194, yymsp[0].minor.yy194); /*A-overwrites-Y*/
|
|
+ yymsp[-2].minor.yy420 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70); /*A-overwrites-Y*/
|
|
}
|
|
break;
|
|
- case 214: /* cmd ::= DROP INDEX ifexists fullname */
|
|
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy185, yymsp[-1].minor.yy194);}
|
|
+ case 228: /* cmd ::= DROP INDEX ifexists fullname */
|
|
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy135, yymsp[-1].minor.yy70);}
|
|
break;
|
|
- case 215: /* cmd ::= VACUUM */
|
|
+ case 229: /* cmd ::= VACUUM */
|
|
{sqlite3Vacuum(pParse,0);}
|
|
break;
|
|
- case 216: /* cmd ::= VACUUM nm */
|
|
+ case 230: /* cmd ::= VACUUM nm */
|
|
{sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);}
|
|
break;
|
|
- case 217: /* cmd ::= PRAGMA nm dbnm */
|
|
+ case 231: /* cmd ::= PRAGMA nm dbnm */
|
|
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
|
|
break;
|
|
- case 218: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
|
|
+ case 232: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
|
|
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
|
|
break;
|
|
- case 219: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
|
|
+ case 233: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
|
|
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
|
|
break;
|
|
- case 220: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
|
|
+ case 234: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
|
|
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
|
|
break;
|
|
- case 221: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
|
|
+ case 235: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
|
|
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
|
|
break;
|
|
- case 224: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
|
|
+ case 238: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
|
|
{
|
|
Token all;
|
|
all.z = yymsp[-3].minor.yy0.z;
|
|
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
|
|
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy145, &all);
|
|
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy207, &all);
|
|
}
|
|
break;
|
|
- case 225: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
|
|
+ case 239: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
|
|
{
|
|
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy194, yymsp[-4].minor.yy332.a, yymsp[-4].minor.yy332.b, yymsp[-2].minor.yy185, yymsp[0].minor.yy72, yymsp[-10].minor.yy194, yymsp[-8].minor.yy194);
|
|
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy70, yymsp[-4].minor.yy34.a, yymsp[-4].minor.yy34.b, yymsp[-2].minor.yy135, yymsp[0].minor.yy18, yymsp[-10].minor.yy70, yymsp[-8].minor.yy70);
|
|
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
|
|
}
|
|
break;
|
|
- case 226: /* trigger_time ::= BEFORE|AFTER */
|
|
-{ yymsp[0].minor.yy194 = yymsp[0].major; /*A-overwrites-X*/ }
|
|
+ case 240: /* trigger_time ::= BEFORE|AFTER */
|
|
+{ yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/ }
|
|
break;
|
|
- case 227: /* trigger_time ::= INSTEAD OF */
|
|
-{ yymsp[-1].minor.yy194 = TK_INSTEAD;}
|
|
+ case 241: /* trigger_time ::= INSTEAD OF */
|
|
+{ yymsp[-1].minor.yy70 = TK_INSTEAD;}
|
|
break;
|
|
- case 228: /* trigger_time ::= */
|
|
-{ yymsp[1].minor.yy194 = TK_BEFORE; }
|
|
+ case 242: /* trigger_time ::= */
|
|
+{ yymsp[1].minor.yy70 = TK_BEFORE; }
|
|
break;
|
|
- case 229: /* trigger_event ::= DELETE|INSERT */
|
|
- case 230: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==230);
|
|
-{yymsp[0].minor.yy332.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy332.b = 0;}
|
|
+ case 243: /* trigger_event ::= DELETE|INSERT */
|
|
+ case 244: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==244);
|
|
+{yymsp[0].minor.yy34.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy34.b = 0;}
|
|
break;
|
|
- case 231: /* trigger_event ::= UPDATE OF idlist */
|
|
-{yymsp[-2].minor.yy332.a = TK_UPDATE; yymsp[-2].minor.yy332.b = yymsp[0].minor.yy254;}
|
|
+ case 245: /* trigger_event ::= UPDATE OF idlist */
|
|
+{yymsp[-2].minor.yy34.a = TK_UPDATE; yymsp[-2].minor.yy34.b = yymsp[0].minor.yy48;}
|
|
break;
|
|
- case 232: /* when_clause ::= */
|
|
- case 251: /* key_opt ::= */ yytestcase(yyruleno==251);
|
|
-{ yymsp[1].minor.yy72 = 0; }
|
|
+ case 246: /* when_clause ::= */
|
|
+ case 265: /* key_opt ::= */ yytestcase(yyruleno==265);
|
|
+ case 307: /* filter_opt ::= */ yytestcase(yyruleno==307);
|
|
+{ yymsp[1].minor.yy18 = 0; }
|
|
break;
|
|
- case 233: /* when_clause ::= WHEN expr */
|
|
- case 252: /* key_opt ::= KEY expr */ yytestcase(yyruleno==252);
|
|
-{ yymsp[-1].minor.yy72 = yymsp[0].minor.yy190.pExpr; }
|
|
+ case 247: /* when_clause ::= WHEN expr */
|
|
+ case 266: /* key_opt ::= KEY expr */ yytestcase(yyruleno==266);
|
|
+{ yymsp[-1].minor.yy18 = yymsp[0].minor.yy18; }
|
|
break;
|
|
- case 234: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
|
|
+ case 248: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
|
|
{
|
|
- assert( yymsp[-2].minor.yy145!=0 );
|
|
- yymsp[-2].minor.yy145->pLast->pNext = yymsp[-1].minor.yy145;
|
|
- yymsp[-2].minor.yy145->pLast = yymsp[-1].minor.yy145;
|
|
+ assert( yymsp[-2].minor.yy207!=0 );
|
|
+ yymsp[-2].minor.yy207->pLast->pNext = yymsp[-1].minor.yy207;
|
|
+ yymsp[-2].minor.yy207->pLast = yymsp[-1].minor.yy207;
|
|
}
|
|
break;
|
|
- case 235: /* trigger_cmd_list ::= trigger_cmd SEMI */
|
|
+ case 249: /* trigger_cmd_list ::= trigger_cmd SEMI */
|
|
{
|
|
- assert( yymsp[-1].minor.yy145!=0 );
|
|
- yymsp[-1].minor.yy145->pLast = yymsp[-1].minor.yy145;
|
|
+ assert( yymsp[-1].minor.yy207!=0 );
|
|
+ yymsp[-1].minor.yy207->pLast = yymsp[-1].minor.yy207;
|
|
}
|
|
break;
|
|
- case 236: /* trnm ::= nm DOT nm */
|
|
+ case 250: /* trnm ::= nm DOT nm */
|
|
{
|
|
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
|
|
sqlite3ErrorMsg(pParse,
|
|
@@ -139829,7 +150613,7 @@
|
|
"statements within triggers");
|
|
}
|
|
break;
|
|
- case 237: /* tridxby ::= INDEXED BY nm */
|
|
+ case 251: /* tridxby ::= INDEXED BY nm */
|
|
{
|
|
sqlite3ErrorMsg(pParse,
|
|
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
|
|
@@ -139836,7 +150620,7 @@
|
|
"within triggers");
|
|
}
|
|
break;
|
|
- case 238: /* tridxby ::= NOT INDEXED */
|
|
+ case 252: /* tridxby ::= NOT INDEXED */
|
|
{
|
|
sqlite3ErrorMsg(pParse,
|
|
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
|
|
@@ -139843,182 +150627,292 @@
|
|
"within triggers");
|
|
}
|
|
break;
|
|
- case 239: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
|
|
-{yymsp[-6].minor.yy145 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy148, yymsp[0].minor.yy72, yymsp[-5].minor.yy194);}
|
|
+ case 253: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
|
|
+{yylhsminor.yy207 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy18, yymsp[-6].minor.yy70, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy392);}
|
|
+ yymsp[-7].minor.yy207 = yylhsminor.yy207;
|
|
break;
|
|
- case 240: /* trigger_cmd ::= insert_cmd INTO trnm idlist_opt select */
|
|
-{yymsp[-4].minor.yy145 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy254, yymsp[0].minor.yy243, yymsp[-4].minor.yy194);/*A-overwrites-R*/}
|
|
+ case 254: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
|
|
+{
|
|
+ yylhsminor.yy207 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy48,yymsp[-2].minor.yy489,yymsp[-6].minor.yy70,yymsp[-1].minor.yy340,yymsp[-7].minor.yy392,yymsp[0].minor.yy392);/*yylhsminor.yy207-overwrites-yymsp[-6].minor.yy70*/
|
|
+}
|
|
+ yymsp[-7].minor.yy207 = yylhsminor.yy207;
|
|
break;
|
|
- case 241: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
|
|
-{yymsp[-4].minor.yy145 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy72);}
|
|
+ case 255: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
|
|
+{yylhsminor.yy207 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy18, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy392);}
|
|
+ yymsp[-5].minor.yy207 = yylhsminor.yy207;
|
|
break;
|
|
- case 242: /* trigger_cmd ::= select */
|
|
-{yymsp[0].minor.yy145 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy243); /*A-overwrites-X*/}
|
|
+ case 256: /* trigger_cmd ::= scanpt select scanpt */
|
|
+{yylhsminor.yy207 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy489, yymsp[-2].minor.yy392, yymsp[0].minor.yy392); /*yylhsminor.yy207-overwrites-yymsp[-1].minor.yy489*/}
|
|
+ yymsp[-2].minor.yy207 = yylhsminor.yy207;
|
|
break;
|
|
- case 243: /* expr ::= RAISE LP IGNORE RP */
|
|
+ case 257: /* expr ::= RAISE LP IGNORE RP */
|
|
{
|
|
- spanSet(&yymsp[-3].minor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
|
|
- yymsp[-3].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
|
|
- if( yymsp[-3].minor.yy190.pExpr ){
|
|
- yymsp[-3].minor.yy190.pExpr->affinity = OE_Ignore;
|
|
+ yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
|
|
+ if( yymsp[-3].minor.yy18 ){
|
|
+ yymsp[-3].minor.yy18->affinity = OE_Ignore;
|
|
}
|
|
}
|
|
break;
|
|
- case 244: /* expr ::= RAISE LP raisetype COMMA nm RP */
|
|
+ case 258: /* expr ::= RAISE LP raisetype COMMA nm RP */
|
|
{
|
|
- spanSet(&yymsp[-5].minor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
|
|
- yymsp[-5].minor.yy190.pExpr = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
|
|
- if( yymsp[-5].minor.yy190.pExpr ) {
|
|
- yymsp[-5].minor.yy190.pExpr->affinity = (char)yymsp[-3].minor.yy194;
|
|
+ yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
|
|
+ if( yymsp[-5].minor.yy18 ) {
|
|
+ yymsp[-5].minor.yy18->affinity = (char)yymsp[-3].minor.yy70;
|
|
}
|
|
}
|
|
break;
|
|
- case 245: /* raisetype ::= ROLLBACK */
|
|
-{yymsp[0].minor.yy194 = OE_Rollback;}
|
|
+ case 259: /* raisetype ::= ROLLBACK */
|
|
+{yymsp[0].minor.yy70 = OE_Rollback;}
|
|
break;
|
|
- case 247: /* raisetype ::= FAIL */
|
|
-{yymsp[0].minor.yy194 = OE_Fail;}
|
|
+ case 261: /* raisetype ::= FAIL */
|
|
+{yymsp[0].minor.yy70 = OE_Fail;}
|
|
break;
|
|
- case 248: /* cmd ::= DROP TRIGGER ifexists fullname */
|
|
+ case 262: /* cmd ::= DROP TRIGGER ifexists fullname */
|
|
{
|
|
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy185,yymsp[-1].minor.yy194);
|
|
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy135,yymsp[-1].minor.yy70);
|
|
}
|
|
break;
|
|
- case 249: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
|
|
+ case 263: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
|
|
{
|
|
- sqlite3Attach(pParse, yymsp[-3].minor.yy190.pExpr, yymsp[-1].minor.yy190.pExpr, yymsp[0].minor.yy72);
|
|
+ sqlite3Attach(pParse, yymsp[-3].minor.yy18, yymsp[-1].minor.yy18, yymsp[0].minor.yy18);
|
|
}
|
|
break;
|
|
- case 250: /* cmd ::= DETACH database_kw_opt expr */
|
|
+ case 264: /* cmd ::= DETACH database_kw_opt expr */
|
|
{
|
|
- sqlite3Detach(pParse, yymsp[0].minor.yy190.pExpr);
|
|
+ sqlite3Detach(pParse, yymsp[0].minor.yy18);
|
|
}
|
|
break;
|
|
- case 253: /* cmd ::= REINDEX */
|
|
+ case 267: /* cmd ::= REINDEX */
|
|
{sqlite3Reindex(pParse, 0, 0);}
|
|
break;
|
|
- case 254: /* cmd ::= REINDEX nm dbnm */
|
|
+ case 268: /* cmd ::= REINDEX nm dbnm */
|
|
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
|
|
break;
|
|
- case 255: /* cmd ::= ANALYZE */
|
|
+ case 269: /* cmd ::= ANALYZE */
|
|
{sqlite3Analyze(pParse, 0, 0);}
|
|
break;
|
|
- case 256: /* cmd ::= ANALYZE nm dbnm */
|
|
+ case 270: /* cmd ::= ANALYZE nm dbnm */
|
|
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
|
|
break;
|
|
- case 257: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
|
|
+ case 271: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
|
|
{
|
|
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy185,&yymsp[0].minor.yy0);
|
|
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy135,&yymsp[0].minor.yy0);
|
|
}
|
|
break;
|
|
- case 258: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
|
|
+ case 272: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
|
|
{
|
|
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
|
|
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
|
|
}
|
|
break;
|
|
- case 259: /* add_column_fullname ::= fullname */
|
|
+ case 273: /* add_column_fullname ::= fullname */
|
|
{
|
|
disableLookaside(pParse);
|
|
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy185);
|
|
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy135);
|
|
}
|
|
break;
|
|
- case 260: /* cmd ::= create_vtab */
|
|
+ case 274: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
|
|
+{
|
|
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy135, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
|
|
+}
|
|
+ break;
|
|
+ case 275: /* cmd ::= create_vtab */
|
|
{sqlite3VtabFinishParse(pParse,0);}
|
|
break;
|
|
- case 261: /* cmd ::= create_vtab LP vtabarglist RP */
|
|
+ case 276: /* cmd ::= create_vtab LP vtabarglist RP */
|
|
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
|
|
break;
|
|
- case 262: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
|
|
+ case 277: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
|
|
{
|
|
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy194);
|
|
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy70);
|
|
}
|
|
break;
|
|
- case 263: /* vtabarg ::= */
|
|
+ case 278: /* vtabarg ::= */
|
|
{sqlite3VtabArgInit(pParse);}
|
|
break;
|
|
- case 264: /* vtabargtoken ::= ANY */
|
|
- case 265: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==265);
|
|
- case 266: /* lp ::= LP */ yytestcase(yyruleno==266);
|
|
+ case 279: /* vtabargtoken ::= ANY */
|
|
+ case 280: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==280);
|
|
+ case 281: /* lp ::= LP */ yytestcase(yyruleno==281);
|
|
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
|
|
break;
|
|
- case 267: /* with ::= */
|
|
-{yymsp[1].minor.yy285 = 0;}
|
|
+ case 282: /* with ::= WITH wqlist */
|
|
+ case 283: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==283);
|
|
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy449, 1); }
|
|
break;
|
|
- case 268: /* with ::= WITH wqlist */
|
|
-{ yymsp[-1].minor.yy285 = yymsp[0].minor.yy285; }
|
|
+ case 284: /* wqlist ::= nm eidlist_opt AS LP select RP */
|
|
+{
|
|
+ yymsp[-5].minor.yy449 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489); /*A-overwrites-X*/
|
|
+}
|
|
break;
|
|
- case 269: /* with ::= WITH RECURSIVE wqlist */
|
|
-{ yymsp[-2].minor.yy285 = yymsp[0].minor.yy285; }
|
|
+ case 285: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
|
|
+{
|
|
+ yymsp[-7].minor.yy449 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy449, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489);
|
|
+}
|
|
break;
|
|
- case 270: /* wqlist ::= nm eidlist_opt AS LP select RP */
|
|
+ case 286: /* windowdefn_list ::= windowdefn */
|
|
+{ yylhsminor.yy327 = yymsp[0].minor.yy327; }
|
|
+ yymsp[0].minor.yy327 = yylhsminor.yy327;
|
|
+ break;
|
|
+ case 287: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
|
|
{
|
|
- yymsp[-5].minor.yy285 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy148, yymsp[-1].minor.yy243); /*A-overwrites-X*/
|
|
+ assert( yymsp[0].minor.yy327!=0 );
|
|
+ yymsp[0].minor.yy327->pNextWin = yymsp[-2].minor.yy327;
|
|
+ yylhsminor.yy327 = yymsp[0].minor.yy327;
|
|
}
|
|
+ yymsp[-2].minor.yy327 = yylhsminor.yy327;
|
|
break;
|
|
- case 271: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
|
|
+ case 288: /* windowdefn ::= nm AS window */
|
|
{
|
|
- yymsp[-7].minor.yy285 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy285, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy148, yymsp[-1].minor.yy243);
|
|
+ if( ALWAYS(yymsp[0].minor.yy327) ){
|
|
+ yymsp[0].minor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n);
|
|
+ }
|
|
+ yylhsminor.yy327 = yymsp[0].minor.yy327;
|
|
}
|
|
+ yymsp[-2].minor.yy327 = yylhsminor.yy327;
|
|
break;
|
|
+ case 289: /* window ::= LP part_opt orderby_opt frame_opt RP */
|
|
+{
|
|
+ yymsp[-4].minor.yy327 = yymsp[-1].minor.yy327;
|
|
+ if( ALWAYS(yymsp[-4].minor.yy327) ){
|
|
+ yymsp[-4].minor.yy327->pPartition = yymsp[-3].minor.yy420;
|
|
+ yymsp[-4].minor.yy327->pOrderBy = yymsp[-2].minor.yy420;
|
|
+ }
|
|
+}
|
|
+ break;
|
|
+ case 290: /* part_opt ::= PARTITION BY nexprlist */
|
|
+{ yymsp[-2].minor.yy420 = yymsp[0].minor.yy420; }
|
|
+ break;
|
|
+ case 291: /* part_opt ::= */
|
|
+{ yymsp[1].minor.yy420 = 0; }
|
|
+ break;
|
|
+ case 292: /* frame_opt ::= */
|
|
+{
|
|
+ yymsp[1].minor.yy327 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0);
|
|
+}
|
|
+ break;
|
|
+ case 293: /* frame_opt ::= range_or_rows frame_bound_s */
|
|
+{
|
|
+ yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy70, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr, TK_CURRENT, 0);
|
|
+}
|
|
+ yymsp[-1].minor.yy327 = yylhsminor.yy327;
|
|
+ break;
|
|
+ case 294: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
|
|
+{
|
|
+ yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy70, yymsp[-2].minor.yy119.eType, yymsp[-2].minor.yy119.pExpr, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr);
|
|
+}
|
|
+ yymsp[-4].minor.yy327 = yylhsminor.yy327;
|
|
+ break;
|
|
+ case 295: /* range_or_rows ::= RANGE */
|
|
+{ yymsp[0].minor.yy70 = TK_RANGE; }
|
|
+ break;
|
|
+ case 296: /* range_or_rows ::= ROWS */
|
|
+{ yymsp[0].minor.yy70 = TK_ROWS; }
|
|
+ break;
|
|
+ case 297: /* frame_bound_s ::= frame_bound */
|
|
+ case 299: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==299);
|
|
+{ yylhsminor.yy119 = yymsp[0].minor.yy119; }
|
|
+ yymsp[0].minor.yy119 = yylhsminor.yy119;
|
|
+ break;
|
|
+ case 298: /* frame_bound_s ::= UNBOUNDED PRECEDING */
|
|
+ case 300: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==300);
|
|
+{yymsp[-1].minor.yy119.eType = TK_UNBOUNDED; yymsp[-1].minor.yy119.pExpr = 0;}
|
|
+ break;
|
|
+ case 301: /* frame_bound ::= expr PRECEDING */
|
|
+{ yylhsminor.yy119.eType = TK_PRECEDING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; }
|
|
+ yymsp[-1].minor.yy119 = yylhsminor.yy119;
|
|
+ break;
|
|
+ case 302: /* frame_bound ::= CURRENT ROW */
|
|
+{ yymsp[-1].minor.yy119.eType = TK_CURRENT ; yymsp[-1].minor.yy119.pExpr = 0; }
|
|
+ break;
|
|
+ case 303: /* frame_bound ::= expr FOLLOWING */
|
|
+{ yylhsminor.yy119.eType = TK_FOLLOWING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; }
|
|
+ yymsp[-1].minor.yy119 = yylhsminor.yy119;
|
|
+ break;
|
|
+ case 304: /* window_clause ::= WINDOW windowdefn_list */
|
|
+{ yymsp[-1].minor.yy327 = yymsp[0].minor.yy327; }
|
|
+ break;
|
|
+ case 305: /* over_clause ::= filter_opt OVER window */
|
|
+{
|
|
+ yylhsminor.yy327 = yymsp[0].minor.yy327;
|
|
+ assert( yylhsminor.yy327!=0 );
|
|
+ yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18;
|
|
+}
|
|
+ yymsp[-2].minor.yy327 = yylhsminor.yy327;
|
|
+ break;
|
|
+ case 306: /* over_clause ::= filter_opt OVER nm */
|
|
+{
|
|
+ yylhsminor.yy327 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
|
|
+ if( yylhsminor.yy327 ){
|
|
+ yylhsminor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
|
|
+ yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18;
|
|
+ }else{
|
|
+ sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy18);
|
|
+ }
|
|
+}
|
|
+ yymsp[-2].minor.yy327 = yylhsminor.yy327;
|
|
+ break;
|
|
+ case 308: /* filter_opt ::= FILTER LP WHERE expr RP */
|
|
+{ yymsp[-4].minor.yy18 = yymsp[-1].minor.yy18; }
|
|
+ break;
|
|
default:
|
|
- /* (272) input ::= cmdlist */ yytestcase(yyruleno==272);
|
|
- /* (273) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==273);
|
|
- /* (274) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=274);
|
|
- /* (275) ecmd ::= SEMI */ yytestcase(yyruleno==275);
|
|
- /* (276) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==276);
|
|
- /* (277) explain ::= */ yytestcase(yyruleno==277);
|
|
- /* (278) trans_opt ::= */ yytestcase(yyruleno==278);
|
|
- /* (279) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==279);
|
|
- /* (280) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==280);
|
|
- /* (281) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==281);
|
|
- /* (282) savepoint_opt ::= */ yytestcase(yyruleno==282);
|
|
- /* (283) cmd ::= create_table create_table_args */ yytestcase(yyruleno==283);
|
|
- /* (284) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==284);
|
|
- /* (285) columnlist ::= columnname carglist */ yytestcase(yyruleno==285);
|
|
- /* (286) nm ::= ID|INDEXED */ yytestcase(yyruleno==286);
|
|
- /* (287) nm ::= STRING */ yytestcase(yyruleno==287);
|
|
- /* (288) nm ::= JOIN_KW */ yytestcase(yyruleno==288);
|
|
- /* (289) typetoken ::= typename */ yytestcase(yyruleno==289);
|
|
- /* (290) typename ::= ID|STRING */ yytestcase(yyruleno==290);
|
|
- /* (291) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=291);
|
|
- /* (292) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=292);
|
|
- /* (293) carglist ::= carglist ccons */ yytestcase(yyruleno==293);
|
|
- /* (294) carglist ::= */ yytestcase(yyruleno==294);
|
|
- /* (295) ccons ::= NULL onconf */ yytestcase(yyruleno==295);
|
|
- /* (296) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==296);
|
|
- /* (297) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==297);
|
|
- /* (298) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=298);
|
|
- /* (299) tconscomma ::= */ yytestcase(yyruleno==299);
|
|
- /* (300) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=300);
|
|
- /* (301) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=301);
|
|
- /* (302) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=302);
|
|
- /* (303) oneselect ::= values */ yytestcase(yyruleno==303);
|
|
- /* (304) sclp ::= selcollist COMMA */ yytestcase(yyruleno==304);
|
|
- /* (305) as ::= ID|STRING */ yytestcase(yyruleno==305);
|
|
- /* (306) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=306);
|
|
- /* (307) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==307);
|
|
- /* (308) exprlist ::= nexprlist */ yytestcase(yyruleno==308);
|
|
- /* (309) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=309);
|
|
- /* (310) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=310);
|
|
- /* (311) nmnum ::= ON */ yytestcase(yyruleno==311);
|
|
- /* (312) nmnum ::= DELETE */ yytestcase(yyruleno==312);
|
|
- /* (313) nmnum ::= DEFAULT */ yytestcase(yyruleno==313);
|
|
- /* (314) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==314);
|
|
- /* (315) foreach_clause ::= */ yytestcase(yyruleno==315);
|
|
- /* (316) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==316);
|
|
- /* (317) trnm ::= nm */ yytestcase(yyruleno==317);
|
|
- /* (318) tridxby ::= */ yytestcase(yyruleno==318);
|
|
- /* (319) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==319);
|
|
- /* (320) database_kw_opt ::= */ yytestcase(yyruleno==320);
|
|
- /* (321) kwcolumn_opt ::= */ yytestcase(yyruleno==321);
|
|
- /* (322) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==322);
|
|
- /* (323) vtabarglist ::= vtabarg */ yytestcase(yyruleno==323);
|
|
- /* (324) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==324);
|
|
- /* (325) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==325);
|
|
- /* (326) anylist ::= */ yytestcase(yyruleno==326);
|
|
- /* (327) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==327);
|
|
- /* (328) anylist ::= anylist ANY */ yytestcase(yyruleno==328);
|
|
+ /* (309) input ::= cmdlist */ yytestcase(yyruleno==309);
|
|
+ /* (310) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==310);
|
|
+ /* (311) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=311);
|
|
+ /* (312) ecmd ::= SEMI */ yytestcase(yyruleno==312);
|
|
+ /* (313) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==313);
|
|
+ /* (314) ecmd ::= explain cmdx */ yytestcase(yyruleno==314);
|
|
+ /* (315) trans_opt ::= */ yytestcase(yyruleno==315);
|
|
+ /* (316) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==316);
|
|
+ /* (317) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==317);
|
|
+ /* (318) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==318);
|
|
+ /* (319) savepoint_opt ::= */ yytestcase(yyruleno==319);
|
|
+ /* (320) cmd ::= create_table create_table_args */ yytestcase(yyruleno==320);
|
|
+ /* (321) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==321);
|
|
+ /* (322) columnlist ::= columnname carglist */ yytestcase(yyruleno==322);
|
|
+ /* (323) nm ::= ID|INDEXED */ yytestcase(yyruleno==323);
|
|
+ /* (324) nm ::= STRING */ yytestcase(yyruleno==324);
|
|
+ /* (325) nm ::= JOIN_KW */ yytestcase(yyruleno==325);
|
|
+ /* (326) typetoken ::= typename */ yytestcase(yyruleno==326);
|
|
+ /* (327) typename ::= ID|STRING */ yytestcase(yyruleno==327);
|
|
+ /* (328) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=328);
|
|
+ /* (329) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=329);
|
|
+ /* (330) carglist ::= carglist ccons */ yytestcase(yyruleno==330);
|
|
+ /* (331) carglist ::= */ yytestcase(yyruleno==331);
|
|
+ /* (332) ccons ::= NULL onconf */ yytestcase(yyruleno==332);
|
|
+ /* (333) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==333);
|
|
+ /* (334) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==334);
|
|
+ /* (335) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=335);
|
|
+ /* (336) tconscomma ::= */ yytestcase(yyruleno==336);
|
|
+ /* (337) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=337);
|
|
+ /* (338) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=338);
|
|
+ /* (339) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=339);
|
|
+ /* (340) oneselect ::= values */ yytestcase(yyruleno==340);
|
|
+ /* (341) sclp ::= selcollist COMMA */ yytestcase(yyruleno==341);
|
|
+ /* (342) as ::= ID|STRING */ yytestcase(yyruleno==342);
|
|
+ /* (343) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=343);
|
|
+ /* (344) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==344);
|
|
+ /* (345) exprlist ::= nexprlist */ yytestcase(yyruleno==345);
|
|
+ /* (346) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=346);
|
|
+ /* (347) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=347);
|
|
+ /* (348) nmnum ::= ON */ yytestcase(yyruleno==348);
|
|
+ /* (349) nmnum ::= DELETE */ yytestcase(yyruleno==349);
|
|
+ /* (350) nmnum ::= DEFAULT */ yytestcase(yyruleno==350);
|
|
+ /* (351) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==351);
|
|
+ /* (352) foreach_clause ::= */ yytestcase(yyruleno==352);
|
|
+ /* (353) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==353);
|
|
+ /* (354) trnm ::= nm */ yytestcase(yyruleno==354);
|
|
+ /* (355) tridxby ::= */ yytestcase(yyruleno==355);
|
|
+ /* (356) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==356);
|
|
+ /* (357) database_kw_opt ::= */ yytestcase(yyruleno==357);
|
|
+ /* (358) kwcolumn_opt ::= */ yytestcase(yyruleno==358);
|
|
+ /* (359) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==359);
|
|
+ /* (360) vtabarglist ::= vtabarg */ yytestcase(yyruleno==360);
|
|
+ /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==361);
|
|
+ /* (362) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==362);
|
|
+ /* (363) anylist ::= */ yytestcase(yyruleno==363);
|
|
+ /* (364) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==364);
|
|
+ /* (365) anylist ::= anylist ANY */ yytestcase(yyruleno==365);
|
|
+ /* (366) with ::= */ yytestcase(yyruleno==366);
|
|
break;
|
|
/********** End reduce actions ************************************************/
|
|
};
|
|
@@ -140034,16 +150928,12 @@
|
|
/* It is not possible for a REDUCE to be followed by an error */
|
|
assert( yyact!=YY_ERROR_ACTION );
|
|
|
|
- if( yyact==YY_ACCEPT_ACTION ){
|
|
- yypParser->yytos += yysize;
|
|
- yy_accept(yypParser);
|
|
- }else{
|
|
- yymsp += yysize+1;
|
|
- yypParser->yytos = yymsp;
|
|
- yymsp->stateno = (YYACTIONTYPE)yyact;
|
|
- yymsp->major = (YYCODETYPE)yygoto;
|
|
- yyTraceShift(yypParser, yyact);
|
|
- }
|
|
+ yymsp += yysize+1;
|
|
+ yypParser->yytos = yymsp;
|
|
+ yymsp->stateno = (YYACTIONTYPE)yyact;
|
|
+ yymsp->major = (YYCODETYPE)yygoto;
|
|
+ yyTraceShift(yypParser, yyact, "... then shift");
|
|
+ return yyact;
|
|
}
|
|
|
|
/*
|
|
@@ -140053,7 +150943,8 @@
|
|
static void yy_parse_failed(
|
|
yyParser *yypParser /* The parser */
|
|
){
|
|
- sqlite3ParserARG_FETCH;
|
|
+ sqlite3ParserARG_FETCH
|
|
+ sqlite3ParserCTX_FETCH
|
|
#ifndef NDEBUG
|
|
if( yyTraceFILE ){
|
|
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
|
|
@@ -140064,7 +150955,8 @@
|
|
** parser fails */
|
|
/************ Begin %parse_failure code ***************************************/
|
|
/************ End %parse_failure code *****************************************/
|
|
- sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3ParserCTX_STORE
|
|
}
|
|
#endif /* YYNOERRORRECOVERY */
|
|
|
|
@@ -140076,15 +150968,20 @@
|
|
int yymajor, /* The major type of the error token */
|
|
sqlite3ParserTOKENTYPE yyminor /* The minor type of the error token */
|
|
){
|
|
- sqlite3ParserARG_FETCH;
|
|
+ sqlite3ParserARG_FETCH
|
|
+ sqlite3ParserCTX_FETCH
|
|
#define TOKEN yyminor
|
|
/************ Begin %syntax_error code ****************************************/
|
|
|
|
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
|
|
- assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
|
|
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
|
|
+ if( TOKEN.z[0] ){
|
|
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
|
|
+ }else{
|
|
+ sqlite3ErrorMsg(pParse, "incomplete input");
|
|
+ }
|
|
/************ End %syntax_error code ******************************************/
|
|
- sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3ParserCTX_STORE
|
|
}
|
|
|
|
/*
|
|
@@ -140093,7 +150990,8 @@
|
|
static void yy_accept(
|
|
yyParser *yypParser /* The parser */
|
|
){
|
|
- sqlite3ParserARG_FETCH;
|
|
+ sqlite3ParserARG_FETCH
|
|
+ sqlite3ParserCTX_FETCH
|
|
#ifndef NDEBUG
|
|
if( yyTraceFILE ){
|
|
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
|
|
@@ -140107,7 +151005,8 @@
|
|
** parser accepts */
|
|
/*********** Begin %parse_accept code *****************************************/
|
|
/*********** End %parse_accept code *******************************************/
|
|
- sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3ParserCTX_STORE
|
|
}
|
|
|
|
/* The main parser program.
|
|
@@ -140136,7 +151035,7 @@
|
|
sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */
|
|
){
|
|
YYMINORTYPE yyminorunion;
|
|
- unsigned int yyact; /* The parser action. */
|
|
+ YYACTIONTYPE yyact; /* The parser action. */
|
|
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
|
int yyendofinput; /* True if we are at the end of input */
|
|
#endif
|
|
@@ -140143,31 +151042,44 @@
|
|
#ifdef YYERRORSYMBOL
|
|
int yyerrorhit = 0; /* True if yymajor has invoked an error */
|
|
#endif
|
|
- yyParser *yypParser; /* The parser */
|
|
+ yyParser *yypParser = (yyParser*)yyp; /* The parser */
|
|
+ sqlite3ParserCTX_FETCH
|
|
+ sqlite3ParserARG_STORE
|
|
|
|
- yypParser = (yyParser*)yyp;
|
|
assert( yypParser->yytos!=0 );
|
|
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
|
yyendofinput = (yymajor==0);
|
|
#endif
|
|
- sqlite3ParserARG_STORE;
|
|
|
|
+ yyact = yypParser->yytos->stateno;
|
|
#ifndef NDEBUG
|
|
if( yyTraceFILE ){
|
|
- fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
|
|
+ if( yyact < YY_MIN_REDUCE ){
|
|
+ fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
|
|
+ yyTracePrompt,yyTokenName[yymajor],yyact);
|
|
+ }else{
|
|
+ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
|
|
+ yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
|
|
+ }
|
|
}
|
|
#endif
|
|
|
|
do{
|
|
- yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
|
|
- if( yyact <= YY_MAX_SHIFTREDUCE ){
|
|
- yy_shift(yypParser,yyact,yymajor,yyminor);
|
|
+ assert( yyact==yypParser->yytos->stateno );
|
|
+ yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
|
|
+ if( yyact >= YY_MIN_REDUCE ){
|
|
+ yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,
|
|
+ yyminor sqlite3ParserCTX_PARAM);
|
|
+ }else if( yyact <= YY_MAX_SHIFTREDUCE ){
|
|
+ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
|
|
#ifndef YYNOERRORRECOVERY
|
|
yypParser->yyerrcnt--;
|
|
#endif
|
|
- yymajor = YYNOCODE;
|
|
- }else if( yyact <= YY_MAX_REDUCE ){
|
|
- yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
|
|
+ break;
|
|
+ }else if( yyact==YY_ACCEPT_ACTION ){
|
|
+ yypParser->yytos--;
|
|
+ yy_accept(yypParser);
|
|
+ return;
|
|
}else{
|
|
assert( yyact == YY_ERROR_ACTION );
|
|
yyminorunion.yy0 = yyminor;
|
|
@@ -140214,10 +151126,9 @@
|
|
yymajor = YYNOCODE;
|
|
}else{
|
|
while( yypParser->yytos >= yypParser->yystack
|
|
- && yymx != YYERRORSYMBOL
|
|
&& (yyact = yy_find_reduce_action(
|
|
yypParser->yytos->stateno,
|
|
- YYERRORSYMBOL)) >= YY_MIN_REDUCE
|
|
+ YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE
|
|
){
|
|
yy_pop_parser_stack(yypParser);
|
|
}
|
|
@@ -140234,6 +151145,8 @@
|
|
}
|
|
yypParser->yyerrcnt = 3;
|
|
yyerrorhit = 1;
|
|
+ if( yymajor==YYNOCODE ) break;
|
|
+ yyact = yypParser->yytos->stateno;
|
|
#elif defined(YYNOERRORRECOVERY)
|
|
/* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
|
|
** do any kind of error recovery. Instead, simply invoke the syntax
|
|
@@ -140244,8 +151157,7 @@
|
|
*/
|
|
yy_syntax_error(yypParser,yymajor, yyminor);
|
|
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
|
|
- yymajor = YYNOCODE;
|
|
-
|
|
+ break;
|
|
#else /* YYERRORSYMBOL is not defined */
|
|
/* This is what we do if the grammar does not define ERROR:
|
|
**
|
|
@@ -140267,10 +151179,10 @@
|
|
yypParser->yyerrcnt = -1;
|
|
#endif
|
|
}
|
|
- yymajor = YYNOCODE;
|
|
+ break;
|
|
#endif
|
|
}
|
|
- }while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack );
|
|
+ }while( yypParser->yytos>yypParser->yystack );
|
|
#ifndef NDEBUG
|
|
if( yyTraceFILE ){
|
|
yyStackEntry *i;
|
|
@@ -140286,6 +151198,21 @@
|
|
return;
|
|
}
|
|
|
|
+/*
|
|
+** Return the fallback token corresponding to canonical token iToken, or
|
|
+** 0 if iToken has no fallback.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){
|
|
+#ifdef YYFALLBACK
|
|
+ if( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ){
|
|
+ return yyFallback[iToken];
|
|
+ }
|
|
+#else
|
|
+ (void)iToken;
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/************** End of parse.c ***********************************************/
|
|
/************** Begin file tokenize.c ****************************************/
|
|
/*
|
|
@@ -140344,11 +151271,12 @@
|
|
#define CC_TILDA 25 /* '~' */
|
|
#define CC_DOT 26 /* '.' */
|
|
#define CC_ILLEGAL 27 /* Illegal character */
|
|
+#define CC_NUL 28 /* 0x00 */
|
|
|
|
static const unsigned char aiClass[] = {
|
|
#ifdef SQLITE_ASCII
|
|
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
|
|
-/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
|
|
+/* 0x */ 28, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
|
|
/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
|
/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
|
|
/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
|
|
@@ -140447,19 +151375,20 @@
|
|
** is substantially reduced. This is important for embedded applications
|
|
** on platforms with limited memory.
|
|
*/
|
|
-/* Hash score: 182 */
|
|
-/* zKWText[] encodes 834 bytes of keyword text in 554 bytes */
|
|
+/* Hash score: 208 */
|
|
+/* zKWText[] encodes 923 bytes of keyword text in 614 bytes */
|
|
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
|
|
/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */
|
|
/* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */
|
|
-/* UNIQUERYWITHOUTERELEASEATTACHAVINGROUPDATEBEGINNERECURSIVE */
|
|
-/* BETWEENOTNULLIKECASCADELETECASECOLLATECREATECURRENT_DATEDETACH */
|
|
-/* IMMEDIATEJOINSERTMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMITWHEN */
|
|
-/* WHERENAMEAFTEREPLACEANDEFAULTAUTOINCREMENTCASTCOLUMNCOMMIT */
|
|
-/* CONFLICTCROSSCURRENT_TIMESTAMPRIMARYDEFERREDISTINCTDROPFAIL */
|
|
-/* FROMFULLGLOBYIFISNULLORDERESTRICTRIGHTROLLBACKROWUNIONUSING */
|
|
-/* VACUUMVIEWINITIALLY */
|
|
-static const char zKWText[553] = {
|
|
+/* UNIQUERYWITHOUTERELEASEATTACHAVINGROUPDATEBEGINNERANGEBETWEEN */
|
|
+/* OTHINGLOBYCASCADELETECASECOLLATECREATECURRENT_DATEDETACH */
|
|
+/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMIT */
|
|
+/* WHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULTAUTOINCREMENTCAST */
|
|
+/* COLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMPARTITIONDEFERRED */
|
|
+/* ISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWINGFROMFULLIFISNULL */
|
|
+/* ORDERESTRICTOVERIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEW */
|
|
+/* INDOWINITIALLYPRIMARY */
|
|
+static const char zKWText[613] = {
|
|
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
|
|
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
|
|
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
|
|
@@ -140472,83 +151401,90 @@
|
|
'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q',
|
|
'U','E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S',
|
|
'E','A','T','T','A','C','H','A','V','I','N','G','R','O','U','P','D','A',
|
|
- 'T','E','B','E','G','I','N','N','E','R','E','C','U','R','S','I','V','E',
|
|
- 'B','E','T','W','E','E','N','O','T','N','U','L','L','I','K','E','C','A',
|
|
- 'S','C','A','D','E','L','E','T','E','C','A','S','E','C','O','L','L','A',
|
|
- 'T','E','C','R','E','A','T','E','C','U','R','R','E','N','T','_','D','A',
|
|
- 'T','E','D','E','T','A','C','H','I','M','M','E','D','I','A','T','E','J',
|
|
- 'O','I','N','S','E','R','T','M','A','T','C','H','P','L','A','N','A','L',
|
|
- 'Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U','E',
|
|
- 'S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','W','H',
|
|
- 'E','R','E','N','A','M','E','A','F','T','E','R','E','P','L','A','C','E',
|
|
- 'A','N','D','E','F','A','U','L','T','A','U','T','O','I','N','C','R','E',
|
|
- 'M','E','N','T','C','A','S','T','C','O','L','U','M','N','C','O','M','M',
|
|
- 'I','T','C','O','N','F','L','I','C','T','C','R','O','S','S','C','U','R',
|
|
- 'R','E','N','T','_','T','I','M','E','S','T','A','M','P','R','I','M','A',
|
|
- 'R','Y','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','D',
|
|
- 'R','O','P','F','A','I','L','F','R','O','M','F','U','L','L','G','L','O',
|
|
- 'B','Y','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S','T',
|
|
- 'R','I','C','T','R','I','G','H','T','R','O','L','L','B','A','C','K','R',
|
|
- 'O','W','U','N','I','O','N','U','S','I','N','G','V','A','C','U','U','M',
|
|
- 'V','I','E','W','I','N','I','T','I','A','L','L','Y',
|
|
+ 'T','E','B','E','G','I','N','N','E','R','A','N','G','E','B','E','T','W',
|
|
+ 'E','E','N','O','T','H','I','N','G','L','O','B','Y','C','A','S','C','A',
|
|
+ 'D','E','L','E','T','E','C','A','S','E','C','O','L','L','A','T','E','C',
|
|
+ 'R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E','D',
|
|
+ 'E','T','A','C','H','I','M','M','E','D','I','A','T','E','J','O','I','N',
|
|
+ 'S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N','A',
|
|
+ 'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U',
|
|
+ 'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','O',
|
|
+ 'T','N','U','L','L','W','H','E','R','E','C','U','R','S','I','V','E','A',
|
|
+ 'F','T','E','R','E','N','A','M','E','A','N','D','E','F','A','U','L','T',
|
|
+ 'A','U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C',
|
|
+ 'O','L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C',
|
|
+ 'T','C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E',
|
|
+ 'S','T','A','M','P','A','R','T','I','T','I','O','N','D','E','F','E','R',
|
|
+ 'R','E','D','I','S','T','I','N','C','T','D','R','O','P','R','E','C','E',
|
|
+ 'D','I','N','G','F','A','I','L','F','I','L','T','E','R','E','P','L','A',
|
|
+ 'C','E','F','O','L','L','O','W','I','N','G','F','R','O','M','F','U','L',
|
|
+ 'L','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S','T','R',
|
|
+ 'I','C','T','O','V','E','R','I','G','H','T','R','O','L','L','B','A','C',
|
|
+ 'K','R','O','W','S','U','N','B','O','U','N','D','E','D','U','N','I','O',
|
|
+ 'N','U','S','I','N','G','V','A','C','U','U','M','V','I','E','W','I','N',
|
|
+ 'D','O','W','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R',
|
|
+ 'Y',
|
|
};
|
|
/* aKWHash[i] is the hash value for the i-th keyword */
|
|
static const unsigned char aKWHash[127] = {
|
|
- 76, 105, 117, 74, 0, 45, 0, 0, 82, 0, 77, 0, 0,
|
|
- 42, 12, 78, 15, 0, 116, 85, 54, 112, 0, 19, 0, 0,
|
|
- 121, 0, 119, 115, 0, 22, 93, 0, 9, 0, 0, 70, 71,
|
|
- 0, 69, 6, 0, 48, 90, 102, 0, 118, 101, 0, 0, 44,
|
|
- 0, 103, 24, 0, 17, 0, 122, 53, 23, 0, 5, 110, 25,
|
|
- 96, 0, 0, 124, 106, 60, 123, 57, 28, 55, 0, 91, 0,
|
|
- 100, 26, 0, 99, 0, 0, 0, 95, 92, 97, 88, 109, 14,
|
|
- 39, 108, 0, 81, 0, 18, 89, 111, 32, 0, 120, 80, 113,
|
|
- 62, 46, 84, 0, 0, 94, 40, 59, 114, 0, 36, 0, 0,
|
|
- 29, 0, 86, 63, 64, 0, 20, 61, 0, 56,
|
|
+ 74, 109, 124, 72, 106, 45, 0, 0, 81, 0, 76, 61, 0,
|
|
+ 42, 12, 77, 15, 0, 123, 84, 54, 118, 125, 19, 0, 0,
|
|
+ 130, 0, 128, 121, 0, 22, 96, 0, 9, 0, 0, 115, 69,
|
|
+ 0, 67, 6, 0, 48, 93, 136, 0, 126, 104, 0, 0, 44,
|
|
+ 0, 107, 24, 0, 17, 0, 131, 53, 23, 0, 5, 62, 132,
|
|
+ 99, 0, 0, 135, 110, 60, 134, 57, 113, 55, 0, 94, 0,
|
|
+ 103, 26, 0, 102, 0, 0, 0, 98, 95, 100, 105, 117, 14,
|
|
+ 39, 116, 0, 80, 0, 133, 114, 92, 59, 0, 129, 79, 119,
|
|
+ 86, 46, 83, 0, 0, 97, 40, 122, 120, 0, 127, 0, 0,
|
|
+ 29, 0, 89, 87, 88, 0, 20, 85, 111, 56,
|
|
};
|
|
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
|
|
** then the i-th keyword has no more hash collisions. Otherwise,
|
|
** the next keyword with the same hash is aKWHash[i]-1. */
|
|
-static const unsigned char aKWNext[124] = {
|
|
+static const unsigned char aKWNext[136] = {
|
|
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
|
|
0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 0, 0, 50,
|
|
- 0, 43, 3, 47, 0, 0, 0, 0, 30, 0, 58, 0, 38,
|
|
- 0, 0, 0, 1, 66, 0, 0, 67, 0, 41, 0, 0, 0,
|
|
- 0, 0, 0, 49, 65, 0, 0, 0, 0, 31, 52, 16, 34,
|
|
- 10, 0, 0, 0, 0, 0, 0, 0, 11, 72, 79, 0, 8,
|
|
- 0, 104, 98, 0, 107, 0, 87, 0, 75, 51, 0, 27, 37,
|
|
- 73, 83, 0, 35, 68, 0, 0,
|
|
+ 0, 43, 3, 47, 0, 0, 32, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 1, 64, 0, 0, 65, 0, 41, 0, 38, 0, 0, 0,
|
|
+ 0, 0, 49, 75, 0, 0, 30, 0, 58, 0, 0, 0, 31,
|
|
+ 63, 16, 34, 10, 0, 0, 0, 0, 0, 0, 0, 11, 70,
|
|
+ 91, 0, 0, 8, 0, 108, 0, 101, 28, 52, 68, 0, 112,
|
|
+ 0, 73, 51, 0, 90, 27, 37, 0, 71, 36, 82, 0, 35,
|
|
+ 66, 25, 18, 0, 0, 78,
|
|
};
|
|
/* aKWLen[i] is the length (in bytes) of the i-th keyword */
|
|
-static const unsigned char aKWLen[124] = {
|
|
+static const unsigned char aKWLen[136] = {
|
|
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
|
|
7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6,
|
|
11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10,
|
|
4, 6, 2, 3, 9, 4, 2, 6, 5, 7, 4, 5, 7,
|
|
- 6, 6, 5, 6, 5, 5, 9, 7, 7, 3, 2, 4, 4,
|
|
- 7, 3, 6, 4, 7, 6, 12, 6, 9, 4, 6, 5, 4,
|
|
- 7, 6, 5, 6, 7, 5, 4, 5, 6, 5, 7, 3, 7,
|
|
- 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 8, 8,
|
|
- 2, 4, 4, 4, 4, 4, 2, 2, 6, 5, 8, 5, 8,
|
|
- 3, 5, 5, 6, 4, 9, 3,
|
|
+ 6, 6, 5, 6, 5, 5, 5, 7, 7, 4, 2, 7, 3,
|
|
+ 6, 4, 7, 6, 12, 6, 9, 4, 6, 4, 5, 4, 7,
|
|
+ 6, 5, 6, 7, 5, 4, 7, 3, 2, 4, 5, 9, 5,
|
|
+ 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12,
|
|
+ 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9, 4, 4,
|
|
+ 2, 6, 5, 8, 4, 5, 8, 4, 3, 9, 5, 5, 6,
|
|
+ 4, 6, 2, 9, 3, 7,
|
|
};
|
|
/* aKWOffset[i] is the index into zKWText[] of the start of
|
|
** the text for the i-th keyword. */
|
|
-static const unsigned short int aKWOffset[124] = {
|
|
+static const unsigned short int aKWOffset[136] = {
|
|
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
|
|
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
|
|
86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152,
|
|
159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 184, 188, 192,
|
|
- 199, 204, 209, 212, 218, 221, 225, 234, 240, 240, 240, 243, 246,
|
|
- 250, 251, 255, 261, 265, 272, 278, 290, 296, 305, 307, 313, 318,
|
|
- 320, 327, 332, 337, 343, 349, 354, 358, 361, 367, 371, 378, 380,
|
|
- 387, 389, 391, 400, 404, 410, 416, 424, 429, 429, 445, 452, 459,
|
|
- 460, 467, 471, 475, 479, 483, 486, 488, 490, 496, 500, 508, 513,
|
|
- 521, 524, 529, 534, 540, 544, 549,
|
|
+ 199, 204, 209, 212, 218, 221, 225, 230, 236, 242, 245, 247, 248,
|
|
+ 252, 258, 262, 269, 275, 287, 293, 302, 304, 310, 314, 319, 321,
|
|
+ 328, 333, 338, 344, 350, 355, 358, 358, 358, 361, 365, 368, 377,
|
|
+ 381, 387, 389, 396, 398, 400, 409, 413, 419, 425, 433, 438, 438,
|
|
+ 438, 454, 463, 470, 471, 478, 481, 490, 494, 499, 506, 515, 519,
|
|
+ 523, 525, 531, 535, 543, 546, 551, 559, 559, 563, 572, 577, 582,
|
|
+ 588, 591, 594, 597, 602, 606,
|
|
};
|
|
/* aKWCode[i] is the parser symbol code for the i-th keyword */
|
|
-static const unsigned char aKWCode[124] = {
|
|
+static const unsigned char aKWCode[136] = {
|
|
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
|
|
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
|
|
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
|
|
@@ -140560,20 +151496,23 @@
|
|
TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP,
|
|
TK_OR, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH,
|
|
TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_GROUP,
|
|
- TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RECURSIVE, TK_BETWEEN,
|
|
- TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, TK_LIKE_KW,
|
|
- TK_CASCADE, TK_ASC, TK_DELETE, TK_CASE, TK_COLLATE,
|
|
- TK_CREATE, TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE, TK_JOIN,
|
|
- TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA,
|
|
- TK_ABORT, TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN,
|
|
- TK_WHERE, TK_RENAME, TK_AFTER, TK_REPLACE, TK_AND,
|
|
- TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, TK_CAST,
|
|
- TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW,
|
|
- TK_CTIME_KW, TK_PRIMARY, TK_DEFERRED, TK_DISTINCT, TK_IS,
|
|
- TK_DROP, TK_FAIL, TK_FROM, TK_JOIN_KW, TK_LIKE_KW,
|
|
- TK_BY, TK_IF, TK_ISNULL, TK_ORDER, TK_RESTRICT,
|
|
- TK_JOIN_KW, TK_ROLLBACK, TK_ROW, TK_UNION, TK_USING,
|
|
- TK_VACUUM, TK_VIEW, TK_INITIALLY, TK_ALL,
|
|
+ TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RANGE, TK_BETWEEN,
|
|
+ TK_NOTHING, TK_LIKE_KW, TK_BY, TK_CASCADE, TK_ASC,
|
|
+ TK_DELETE, TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW,
|
|
+ TK_DETACH, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_LIKE_KW,
|
|
+ TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT,
|
|
+ TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN, TK_NOTNULL,
|
|
+ TK_NOT, TK_NO, TK_NULL, TK_WHERE, TK_RECURSIVE,
|
|
+ TK_AFTER, TK_RENAME, TK_AND, TK_DEFAULT, TK_AUTOINCR,
|
|
+ TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, TK_COMMIT,
|
|
+ TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT,
|
|
+ TK_PARTITION, TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP,
|
|
+ TK_PRECEDING, TK_FAIL, TK_FILTER, TK_REPLACE, TK_FOLLOWING,
|
|
+ TK_FROM, TK_JOIN_KW, TK_IF, TK_ISNULL, TK_ORDER,
|
|
+ TK_RESTRICT, TK_OVER, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS,
|
|
+ TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM,
|
|
+ TK_VIEW, TK_WINDOW, TK_DO, TK_INITIALLY, TK_ALL,
|
|
+ TK_PRIMARY,
|
|
};
|
|
/* Check to see if z[0..n-1] is a keyword. If it is, write the
|
|
** parser symbol code for that keyword into *pType. Always
|
|
@@ -140652,72 +151591,84 @@
|
|
testcase( i==55 ); /* UPDATE */
|
|
testcase( i==56 ); /* BEGIN */
|
|
testcase( i==57 ); /* INNER */
|
|
- testcase( i==58 ); /* RECURSIVE */
|
|
+ testcase( i==58 ); /* RANGE */
|
|
testcase( i==59 ); /* BETWEEN */
|
|
- testcase( i==60 ); /* NOTNULL */
|
|
- testcase( i==61 ); /* NOT */
|
|
- testcase( i==62 ); /* NO */
|
|
- testcase( i==63 ); /* NULL */
|
|
- testcase( i==64 ); /* LIKE */
|
|
- testcase( i==65 ); /* CASCADE */
|
|
- testcase( i==66 ); /* ASC */
|
|
- testcase( i==67 ); /* DELETE */
|
|
- testcase( i==68 ); /* CASE */
|
|
- testcase( i==69 ); /* COLLATE */
|
|
- testcase( i==70 ); /* CREATE */
|
|
- testcase( i==71 ); /* CURRENT_DATE */
|
|
- testcase( i==72 ); /* DETACH */
|
|
- testcase( i==73 ); /* IMMEDIATE */
|
|
- testcase( i==74 ); /* JOIN */
|
|
- testcase( i==75 ); /* INSERT */
|
|
- testcase( i==76 ); /* MATCH */
|
|
- testcase( i==77 ); /* PLAN */
|
|
- testcase( i==78 ); /* ANALYZE */
|
|
- testcase( i==79 ); /* PRAGMA */
|
|
- testcase( i==80 ); /* ABORT */
|
|
- testcase( i==81 ); /* VALUES */
|
|
- testcase( i==82 ); /* VIRTUAL */
|
|
- testcase( i==83 ); /* LIMIT */
|
|
- testcase( i==84 ); /* WHEN */
|
|
- testcase( i==85 ); /* WHERE */
|
|
- testcase( i==86 ); /* RENAME */
|
|
- testcase( i==87 ); /* AFTER */
|
|
- testcase( i==88 ); /* REPLACE */
|
|
- testcase( i==89 ); /* AND */
|
|
- testcase( i==90 ); /* DEFAULT */
|
|
- testcase( i==91 ); /* AUTOINCREMENT */
|
|
- testcase( i==92 ); /* TO */
|
|
- testcase( i==93 ); /* IN */
|
|
- testcase( i==94 ); /* CAST */
|
|
- testcase( i==95 ); /* COLUMN */
|
|
- testcase( i==96 ); /* COMMIT */
|
|
- testcase( i==97 ); /* CONFLICT */
|
|
- testcase( i==98 ); /* CROSS */
|
|
- testcase( i==99 ); /* CURRENT_TIMESTAMP */
|
|
- testcase( i==100 ); /* CURRENT_TIME */
|
|
- testcase( i==101 ); /* PRIMARY */
|
|
- testcase( i==102 ); /* DEFERRED */
|
|
- testcase( i==103 ); /* DISTINCT */
|
|
- testcase( i==104 ); /* IS */
|
|
- testcase( i==105 ); /* DROP */
|
|
- testcase( i==106 ); /* FAIL */
|
|
- testcase( i==107 ); /* FROM */
|
|
- testcase( i==108 ); /* FULL */
|
|
- testcase( i==109 ); /* GLOB */
|
|
- testcase( i==110 ); /* BY */
|
|
- testcase( i==111 ); /* IF */
|
|
- testcase( i==112 ); /* ISNULL */
|
|
- testcase( i==113 ); /* ORDER */
|
|
- testcase( i==114 ); /* RESTRICT */
|
|
- testcase( i==115 ); /* RIGHT */
|
|
- testcase( i==116 ); /* ROLLBACK */
|
|
- testcase( i==117 ); /* ROW */
|
|
- testcase( i==118 ); /* UNION */
|
|
- testcase( i==119 ); /* USING */
|
|
- testcase( i==120 ); /* VACUUM */
|
|
- testcase( i==121 ); /* VIEW */
|
|
- testcase( i==122 ); /* INITIALLY */
|
|
- testcase( i==123 ); /* ALL */
|
|
+ testcase( i==60 ); /* NOTHING */
|
|
+ testcase( i==61 ); /* GLOB */
|
|
+ testcase( i==62 ); /* BY */
|
|
+ testcase( i==63 ); /* CASCADE */
|
|
+ testcase( i==64 ); /* ASC */
|
|
+ testcase( i==65 ); /* DELETE */
|
|
+ testcase( i==66 ); /* CASE */
|
|
+ testcase( i==67 ); /* COLLATE */
|
|
+ testcase( i==68 ); /* CREATE */
|
|
+ testcase( i==69 ); /* CURRENT_DATE */
|
|
+ testcase( i==70 ); /* DETACH */
|
|
+ testcase( i==71 ); /* IMMEDIATE */
|
|
+ testcase( i==72 ); /* JOIN */
|
|
+ testcase( i==73 ); /* INSERT */
|
|
+ testcase( i==74 ); /* LIKE */
|
|
+ testcase( i==75 ); /* MATCH */
|
|
+ testcase( i==76 ); /* PLAN */
|
|
+ testcase( i==77 ); /* ANALYZE */
|
|
+ testcase( i==78 ); /* PRAGMA */
|
|
+ testcase( i==79 ); /* ABORT */
|
|
+ testcase( i==80 ); /* VALUES */
|
|
+ testcase( i==81 ); /* VIRTUAL */
|
|
+ testcase( i==82 ); /* LIMIT */
|
|
+ testcase( i==83 ); /* WHEN */
|
|
+ testcase( i==84 ); /* NOTNULL */
|
|
+ testcase( i==85 ); /* NOT */
|
|
+ testcase( i==86 ); /* NO */
|
|
+ testcase( i==87 ); /* NULL */
|
|
+ testcase( i==88 ); /* WHERE */
|
|
+ testcase( i==89 ); /* RECURSIVE */
|
|
+ testcase( i==90 ); /* AFTER */
|
|
+ testcase( i==91 ); /* RENAME */
|
|
+ testcase( i==92 ); /* AND */
|
|
+ testcase( i==93 ); /* DEFAULT */
|
|
+ testcase( i==94 ); /* AUTOINCREMENT */
|
|
+ testcase( i==95 ); /* TO */
|
|
+ testcase( i==96 ); /* IN */
|
|
+ testcase( i==97 ); /* CAST */
|
|
+ testcase( i==98 ); /* COLUMN */
|
|
+ testcase( i==99 ); /* COMMIT */
|
|
+ testcase( i==100 ); /* CONFLICT */
|
|
+ testcase( i==101 ); /* CROSS */
|
|
+ testcase( i==102 ); /* CURRENT_TIMESTAMP */
|
|
+ testcase( i==103 ); /* CURRENT_TIME */
|
|
+ testcase( i==104 ); /* CURRENT */
|
|
+ testcase( i==105 ); /* PARTITION */
|
|
+ testcase( i==106 ); /* DEFERRED */
|
|
+ testcase( i==107 ); /* DISTINCT */
|
|
+ testcase( i==108 ); /* IS */
|
|
+ testcase( i==109 ); /* DROP */
|
|
+ testcase( i==110 ); /* PRECEDING */
|
|
+ testcase( i==111 ); /* FAIL */
|
|
+ testcase( i==112 ); /* FILTER */
|
|
+ testcase( i==113 ); /* REPLACE */
|
|
+ testcase( i==114 ); /* FOLLOWING */
|
|
+ testcase( i==115 ); /* FROM */
|
|
+ testcase( i==116 ); /* FULL */
|
|
+ testcase( i==117 ); /* IF */
|
|
+ testcase( i==118 ); /* ISNULL */
|
|
+ testcase( i==119 ); /* ORDER */
|
|
+ testcase( i==120 ); /* RESTRICT */
|
|
+ testcase( i==121 ); /* OVER */
|
|
+ testcase( i==122 ); /* RIGHT */
|
|
+ testcase( i==123 ); /* ROLLBACK */
|
|
+ testcase( i==124 ); /* ROWS */
|
|
+ testcase( i==125 ); /* ROW */
|
|
+ testcase( i==126 ); /* UNBOUNDED */
|
|
+ testcase( i==127 ); /* UNION */
|
|
+ testcase( i==128 ); /* USING */
|
|
+ testcase( i==129 ); /* VACUUM */
|
|
+ testcase( i==130 ); /* VIEW */
|
|
+ testcase( i==131 ); /* WINDOW */
|
|
+ testcase( i==132 ); /* DO */
|
|
+ testcase( i==133 ); /* INITIALLY */
|
|
+ testcase( i==134 ); /* ALL */
|
|
+ testcase( i==135 ); /* PRIMARY */
|
|
*pType = aKWCode[i];
|
|
break;
|
|
}
|
|
@@ -140729,7 +151680,17 @@
|
|
keywordCode((char*)z, n, &id);
|
|
return id;
|
|
}
|
|
-#define SQLITE_N_KEYWORD 124
|
|
+#define SQLITE_N_KEYWORD 136
|
|
+SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){
|
|
+ if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR;
|
|
+ *pzName = zKWText + aKWOffset[i];
|
|
+ *pnName = aKWLen[i];
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+SQLITE_API int sqlite3_keyword_count(void){ return SQLITE_N_KEYWORD; }
|
|
+SQLITE_API int sqlite3_keyword_check(const char *zName, int nName){
|
|
+ return TK_ID!=sqlite3KeywordCode((const u8*)zName, nName);
|
|
+}
|
|
|
|
/************** End of keywordhash.h *****************************************/
|
|
/************** Continuing where we left off in tokenize.c *******************/
|
|
@@ -140773,13 +151734,87 @@
|
|
#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
|
|
#endif
|
|
|
|
-/* Make the IdChar function accessible from ctime.c */
|
|
-#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
|
+/* Make the IdChar function accessible from ctime.c and alter.c */
|
|
SQLITE_PRIVATE int sqlite3IsIdChar(u8 c){ return IdChar(c); }
|
|
-#endif
|
|
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+/*
|
|
+** Return the id of the next token in string (*pz). Before returning, set
|
|
+** (*pz) to point to the byte following the parsed token.
|
|
+*/
|
|
+static int getToken(const unsigned char **pz){
|
|
+ const unsigned char *z = *pz;
|
|
+ int t; /* Token type to return */
|
|
+ do {
|
|
+ z += sqlite3GetToken(z, &t);
|
|
+ }while( t==TK_SPACE );
|
|
+ if( t==TK_ID
|
|
+ || t==TK_STRING
|
|
+ || t==TK_JOIN_KW
|
|
+ || t==TK_WINDOW
|
|
+ || t==TK_OVER
|
|
+ || sqlite3ParserFallback(t)==TK_ID
|
|
+ ){
|
|
+ t = TK_ID;
|
|
+ }
|
|
+ *pz = z;
|
|
+ return t;
|
|
+}
|
|
|
|
/*
|
|
+** The following three functions are called immediately after the tokenizer
|
|
+** reads the keywords WINDOW, OVER and FILTER, respectively, to determine
|
|
+** whether the token should be treated as a keyword or an SQL identifier.
|
|
+** This cannot be handled by the usual lemon %fallback method, due to
|
|
+** the ambiguity in some constructions. e.g.
|
|
+**
|
|
+** SELECT sum(x) OVER ...
|
|
+**
|
|
+** In the above, "OVER" might be a keyword, or it might be an alias for the
|
|
+** sum(x) expression. If a "%fallback ID OVER" directive were added to
|
|
+** grammar, then SQLite would always treat "OVER" as an alias, making it
|
|
+** impossible to call a window-function without a FILTER clause.
|
|
+**
|
|
+** WINDOW is treated as a keyword if:
|
|
+**
|
|
+** * the following token is an identifier, or a keyword that can fallback
|
|
+** to being an identifier, and
|
|
+** * the token after than one is TK_AS.
|
|
+**
|
|
+** OVER is a keyword if:
|
|
+**
|
|
+** * the previous token was TK_RP, and
|
|
+** * the next token is either TK_LP or an identifier.
|
|
+**
|
|
+** FILTER is a keyword if:
|
|
+**
|
|
+** * the previous token was TK_RP, and
|
|
+** * the next token is TK_LP.
|
|
+*/
|
|
+static int analyzeWindowKeyword(const unsigned char *z){
|
|
+ int t;
|
|
+ t = getToken(&z);
|
|
+ if( t!=TK_ID ) return TK_ID;
|
|
+ t = getToken(&z);
|
|
+ if( t!=TK_AS ) return TK_ID;
|
|
+ return TK_WINDOW;
|
|
+}
|
|
+static int analyzeOverKeyword(const unsigned char *z, int lastToken){
|
|
+ if( lastToken==TK_RP ){
|
|
+ int t = getToken(&z);
|
|
+ if( t==TK_LP || t==TK_ID ) return TK_OVER;
|
|
+ }
|
|
+ return TK_ID;
|
|
+}
|
|
+static int analyzeFilterKeyword(const unsigned char *z, int lastToken){
|
|
+ if( lastToken==TK_RP && getToken(&z)==TK_LP ){
|
|
+ return TK_FILTER;
|
|
+ }
|
|
+ return TK_ID;
|
|
+}
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+
|
|
+/*
|
|
** Return the length (in bytes) of the token that begins at z[0].
|
|
** Store the token type in *tokenType before returning.
|
|
*/
|
|
@@ -141046,6 +152081,10 @@
|
|
i = 1;
|
|
break;
|
|
}
|
|
+ case CC_NUL: {
|
|
+ *tokenType = TK_ILLEGAL;
|
|
+ return 0;
|
|
+ }
|
|
default: {
|
|
*tokenType = TK_ILLEGAL;
|
|
return 1;
|
|
@@ -141056,7 +152095,74 @@
|
|
return i;
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_NORMALIZE
|
|
/*
|
|
+** Return the length (in bytes) of the token that begins at z[0].
|
|
+** Store the token type in *tokenType before returning. If flags has
|
|
+** SQLITE_TOKEN_NORMALIZE flag enabled, use the identifier token type
|
|
+** for keywords. Add SQLITE_TOKEN_QUOTED to flags if the token was
|
|
+** actually a quoted identifier. Add SQLITE_TOKEN_KEYWORD to flags
|
|
+** if the token was recognized as a keyword; this is useful when the
|
|
+** SQLITE_TOKEN_NORMALIZE flag is used, because it enables the caller
|
|
+** to differentiate between a keyword being treated as an identifier
|
|
+** (for normalization purposes) and an actual identifier.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3GetTokenNormalized(
|
|
+ const unsigned char *z,
|
|
+ int *tokenType,
|
|
+ int *flags
|
|
+){
|
|
+ int n;
|
|
+ unsigned char iClass = aiClass[*z];
|
|
+ if( iClass==CC_KYWD ){
|
|
+ int i;
|
|
+ for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
|
|
+ if( IdChar(z[i]) ){
|
|
+ /* This token started out using characters that can appear in keywords,
|
|
+ ** but z[i] is a character not allowed within keywords, so this must
|
|
+ ** be an identifier instead */
|
|
+ i++;
|
|
+ while( IdChar(z[i]) ){ i++; }
|
|
+ *tokenType = TK_ID;
|
|
+ return i;
|
|
+ }
|
|
+ *tokenType = TK_ID;
|
|
+ n = keywordCode((char*)z, i, tokenType);
|
|
+ /* If the token is no longer considered to be an identifier, then it is a
|
|
+ ** keyword of some kind. Make the token back into an identifier and then
|
|
+ ** set the SQLITE_TOKEN_KEYWORD flag. Several non-identifier tokens are
|
|
+ ** used verbatim, including IN, IS, NOT, and NULL. */
|
|
+ switch( *tokenType ){
|
|
+ case TK_ID: {
|
|
+ /* do nothing, handled by caller */
|
|
+ break;
|
|
+ }
|
|
+ case TK_IN:
|
|
+ case TK_IS:
|
|
+ case TK_NOT:
|
|
+ case TK_NULL: {
|
|
+ *flags |= SQLITE_TOKEN_KEYWORD;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ *tokenType = TK_ID;
|
|
+ *flags |= SQLITE_TOKEN_KEYWORD;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ n = sqlite3GetToken(z, tokenType);
|
|
+ /* If the token is considered to be an identifier and the character class
|
|
+ ** of the first character is a quote, set the SQLITE_TOKEN_QUOTED flag. */
|
|
+ if( *tokenType==TK_ID && (iClass==CC_QUOTE || iClass==CC_QUOTE2) ){
|
|
+ *flags |= SQLITE_TOKEN_QUOTED;
|
|
+ }
|
|
+ }
|
|
+ return n;
|
|
+}
|
|
+#endif /* SQLITE_ENABLE_NORMALIZE */
|
|
+
|
|
+/*
|
|
** Run the parser on the given SQL string. The parser structure is
|
|
** passed in. An SQLITE_ status code is returned. If an error occurs
|
|
** then an and attempt is made to write an error message into
|
|
@@ -141086,9 +152192,9 @@
|
|
/* sqlite3ParserTrace(stdout, "parser: "); */
|
|
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
|
|
pEngine = &sEngine;
|
|
- sqlite3ParserInit(pEngine);
|
|
+ sqlite3ParserInit(pEngine, pParse);
|
|
#else
|
|
- pEngine = sqlite3ParserAlloc(sqlite3Malloc);
|
|
+ pEngine = sqlite3ParserAlloc(sqlite3Malloc, pParse);
|
|
if( pEngine==0 ){
|
|
sqlite3OomFault(db);
|
|
return SQLITE_NOMEM_BKPT;
|
|
@@ -141099,47 +152205,64 @@
|
|
assert( pParse->nVar==0 );
|
|
assert( pParse->pVList==0 );
|
|
while( 1 ){
|
|
- if( zSql[0]!=0 ){
|
|
- n = sqlite3GetToken((u8*)zSql, &tokenType);
|
|
- mxSqlLen -= n;
|
|
- if( mxSqlLen<0 ){
|
|
- pParse->rc = SQLITE_TOOBIG;
|
|
- break;
|
|
- }
|
|
- }else{
|
|
- /* Upon reaching the end of input, call the parser two more times
|
|
- ** with tokens TK_SEMI and 0, in that order. */
|
|
- if( lastTokenParsed==TK_SEMI ){
|
|
- tokenType = 0;
|
|
- }else if( lastTokenParsed==0 ){
|
|
- break;
|
|
- }else{
|
|
- tokenType = TK_SEMI;
|
|
- }
|
|
- zSql -= n;
|
|
+ n = sqlite3GetToken((u8*)zSql, &tokenType);
|
|
+ mxSqlLen -= n;
|
|
+ if( mxSqlLen<0 ){
|
|
+ pParse->rc = SQLITE_TOOBIG;
|
|
+ break;
|
|
}
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ if( tokenType>=TK_WINDOW ){
|
|
+ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
|
|
+ || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
|
|
+ );
|
|
+#else
|
|
if( tokenType>=TK_SPACE ){
|
|
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
if( db->u1.isInterrupted ){
|
|
pParse->rc = SQLITE_INTERRUPT;
|
|
break;
|
|
}
|
|
- if( tokenType==TK_ILLEGAL ){
|
|
+ if( tokenType==TK_SPACE ){
|
|
+ zSql += n;
|
|
+ continue;
|
|
+ }
|
|
+ if( zSql[0]==0 ){
|
|
+ /* Upon reaching the end of input, call the parser two more times
|
|
+ ** with tokens TK_SEMI and 0, in that order. */
|
|
+ if( lastTokenParsed==TK_SEMI ){
|
|
+ tokenType = 0;
|
|
+ }else if( lastTokenParsed==0 ){
|
|
+ break;
|
|
+ }else{
|
|
+ tokenType = TK_SEMI;
|
|
+ }
|
|
+ n = 0;
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+ }else if( tokenType==TK_WINDOW ){
|
|
+ assert( n==6 );
|
|
+ tokenType = analyzeWindowKeyword((const u8*)&zSql[6]);
|
|
+ }else if( tokenType==TK_OVER ){
|
|
+ assert( n==4 );
|
|
+ tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
|
|
+ }else if( tokenType==TK_FILTER ){
|
|
+ assert( n==6 );
|
|
+ tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
|
|
+#endif /* SQLITE_OMIT_WINDOWFUNC */
|
|
+ }else{
|
|
sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql);
|
|
break;
|
|
}
|
|
- zSql += n;
|
|
- }else{
|
|
- pParse->sLastToken.z = zSql;
|
|
- pParse->sLastToken.n = n;
|
|
- sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
|
|
- lastTokenParsed = tokenType;
|
|
- zSql += n;
|
|
- if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
|
|
}
|
|
+ pParse->sLastToken.z = zSql;
|
|
+ pParse->sLastToken.n = n;
|
|
+ sqlite3Parser(pEngine, tokenType, pParse->sLastToken);
|
|
+ lastTokenParsed = tokenType;
|
|
+ zSql += n;
|
|
+ if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
|
|
}
|
|
assert( nErr==0 );
|
|
- pParse->zTail = zSql;
|
|
#ifdef YYTRACKMAXSTACKDEPTH
|
|
sqlite3_mutex_enter(sqlite3MallocMutex());
|
|
sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
|
|
@@ -141161,10 +152284,12 @@
|
|
assert( pzErrMsg!=0 );
|
|
if( pParse->zErrMsg ){
|
|
*pzErrMsg = pParse->zErrMsg;
|
|
- sqlite3_log(pParse->rc, "%s", *pzErrMsg);
|
|
+ sqlite3_log(pParse->rc, "%s in \"%s\"",
|
|
+ *pzErrMsg, pParse->zTail);
|
|
pParse->zErrMsg = 0;
|
|
nErr++;
|
|
}
|
|
+ pParse->zTail = zSql;
|
|
if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){
|
|
sqlite3VdbeDelete(pParse->pVdbe);
|
|
pParse->pVdbe = 0;
|
|
@@ -141180,7 +152305,7 @@
|
|
sqlite3_free(pParse->apVtabLock);
|
|
#endif
|
|
|
|
- if( !IN_DECLARE_VTAB ){
|
|
+ if( !IN_SPECIAL_PARSE ){
|
|
/* If the pParse->declareVtab flag is set, do not delete any table
|
|
** structure built up in pParse->pNewTable. The calling code (see vtab.c)
|
|
** will take responsibility for freeing the Table structure.
|
|
@@ -141187,9 +152312,11 @@
|
|
*/
|
|
sqlite3DeleteTable(db, pParse->pNewTable);
|
|
}
|
|
+ if( !IN_RENAME_OBJECT ){
|
|
+ sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
|
+ }
|
|
|
|
if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
|
|
- sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
|
sqlite3DbFree(db, pParse->pVList);
|
|
while( pParse->pAinc ){
|
|
AutoincInfo *p = pParse->pAinc;
|
|
@@ -141571,6 +152698,10 @@
|
|
*/
|
|
/* #include "sqlite3.h" */
|
|
|
|
+#ifdef SQLITE_OMIT_VIRTUALTABLE
|
|
+# undef SQLITE_ENABLE_RTREE
|
|
+#endif
|
|
+
|
|
#if 0
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
@@ -141584,7 +152715,7 @@
|
|
/************** End of rtree.h ***********************************************/
|
|
/************** Continuing where we left off in main.c ***********************/
|
|
#endif
|
|
-#ifdef SQLITE_ENABLE_ICU
|
|
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
|
/************** Include sqliteicu.h in the middle of main.c ******************/
|
|
/************** Begin file sqliteicu.h ***************************************/
|
|
/*
|
|
@@ -141640,11 +152771,13 @@
|
|
*/
|
|
SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; }
|
|
|
|
-/* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a
|
|
+/* IMPLEMENTATION-OF: R-25063-23286 The sqlite3_sourceid() function returns a
|
|
** pointer to a string constant whose value is the same as the
|
|
-** SQLITE_SOURCE_ID C preprocessor macro.
|
|
+** SQLITE_SOURCE_ID C preprocessor macro. Except if SQLite is built using
|
|
+** an edited copy of the amalgamation, then the last four characters of
|
|
+** the hash might be different from SQLITE_SOURCE_ID.
|
|
*/
|
|
-SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|
|
+/* SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } */
|
|
|
|
/* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function
|
|
** returns an integer equal to SQLITE_VERSION_NUMBER.
|
|
@@ -141830,7 +152963,12 @@
|
|
sqlite3GlobalConfig.isPCacheInit = 1;
|
|
rc = sqlite3OsInit();
|
|
}
|
|
+#ifdef SQLITE_ENABLE_DESERIALIZE
|
|
if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3MemdbInit();
|
|
+ }
|
|
+#endif
|
|
+ if( rc==SQLITE_OK ){
|
|
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
|
|
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
|
|
sqlite3GlobalConfig.isInit = 1;
|
|
@@ -141862,7 +153000,7 @@
|
|
#ifndef NDEBUG
|
|
#ifndef SQLITE_OMIT_FLOATING_POINT
|
|
/* This section of code's only "output" is via assert() statements. */
|
|
- if ( rc==SQLITE_OK ){
|
|
+ if( rc==SQLITE_OK ){
|
|
u64 x = (((u64)1)<<63)-1;
|
|
double y;
|
|
assert(sizeof(x)==8);
|
|
@@ -142029,14 +153167,8 @@
|
|
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
|
|
break;
|
|
}
|
|
- case SQLITE_CONFIG_SCRATCH: {
|
|
- /* EVIDENCE-OF: R-08404-60887 There are three arguments to
|
|
- ** SQLITE_CONFIG_SCRATCH: A pointer an 8-byte aligned memory buffer from
|
|
- ** which the scratch allocations will be drawn, the size of each scratch
|
|
- ** allocation (sz), and the maximum number of scratch allocations (N). */
|
|
- sqlite3GlobalConfig.pScratch = va_arg(ap, void*);
|
|
- sqlite3GlobalConfig.szScratch = va_arg(ap, int);
|
|
- sqlite3GlobalConfig.nScratch = va_arg(ap, int);
|
|
+ case SQLITE_CONFIG_SMALL_MALLOC: {
|
|
+ sqlite3GlobalConfig.bSmallMalloc = va_arg(ap, int);
|
|
break;
|
|
}
|
|
case SQLITE_CONFIG_PAGECACHE: {
|
|
@@ -142234,6 +153366,17 @@
|
|
break;
|
|
}
|
|
|
|
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
|
+ case SQLITE_CONFIG_SORTERREF_SIZE: {
|
|
+ int iVal = va_arg(ap, int);
|
|
+ if( iVal<0 ){
|
|
+ iVal = SQLITE_DEFAULT_SORTERREF_SIZE;
|
|
+ }
|
|
+ sqlite3GlobalConfig.szSorterRef = (u32)iVal;
|
|
+ break;
|
|
+ }
|
|
+#endif /* SQLITE_ENABLE_SORTER_REFERENCES */
|
|
+
|
|
default: {
|
|
rc = SQLITE_ERROR;
|
|
break;
|
|
@@ -142257,7 +153400,8 @@
|
|
static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
|
#ifndef SQLITE_OMIT_LOOKASIDE
|
|
void *pStart;
|
|
- if( db->lookaside.nOut ){
|
|
+
|
|
+ if( sqlite3LookasideUsed(db,0)>0 ){
|
|
return SQLITE_BUSY;
|
|
}
|
|
/* Free any existing lookaside buffer for this handle before
|
|
@@ -142285,6 +153429,7 @@
|
|
pStart = pBuf;
|
|
}
|
|
db->lookaside.pStart = pStart;
|
|
+ db->lookaside.pInit = 0;
|
|
db->lookaside.pFree = 0;
|
|
db->lookaside.sz = (u16)sz;
|
|
if( pStart ){
|
|
@@ -142291,10 +153436,11 @@
|
|
int i;
|
|
LookasideSlot *p;
|
|
assert( sz > (int)sizeof(LookasideSlot*) );
|
|
+ db->lookaside.nSlot = cnt;
|
|
p = (LookasideSlot*)pStart;
|
|
for(i=cnt-1; i>=0; i--){
|
|
- p->pNext = db->lookaside.pFree;
|
|
- db->lookaside.pFree = p;
|
|
+ p->pNext = db->lookaside.pInit;
|
|
+ db->lookaside.pInit = p;
|
|
p = (LookasideSlot*)&((u8*)p)[sz];
|
|
}
|
|
db->lookaside.pEnd = p;
|
|
@@ -142305,6 +153451,7 @@
|
|
db->lookaside.pEnd = db;
|
|
db->lookaside.bDisable = 1;
|
|
db->lookaside.bMalloced = 0;
|
|
+ db->lookaside.nSlot = 0;
|
|
}
|
|
#endif /* SQLITE_OMIT_LOOKASIDE */
|
|
return SQLITE_OK;
|
|
@@ -142410,6 +153557,9 @@
|
|
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
|
|
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
|
|
{ SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG },
|
|
+ { SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
|
|
+ { SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase },
|
|
+ { SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive },
|
|
};
|
|
unsigned int i;
|
|
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
|
@@ -142417,7 +153567,7 @@
|
|
if( aFlagOp[i].op==op ){
|
|
int onoff = va_arg(ap, int);
|
|
int *pRes = va_arg(ap, int*);
|
|
- int oldFlags = db->flags;
|
|
+ u32 oldFlags = db->flags;
|
|
if( onoff>0 ){
|
|
db->flags |= aFlagOp[i].mask;
|
|
}else if( onoff==0 ){
|
|
@@ -142424,7 +153574,7 @@
|
|
db->flags &= ~aFlagOp[i].mask;
|
|
}
|
|
if( oldFlags!=db->flags ){
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
}
|
|
if( pRes ){
|
|
*pRes = (db->flags & aFlagOp[i].mask)!=0;
|
|
@@ -142486,6 +153636,15 @@
|
|
}
|
|
|
|
/*
|
|
+** Return true if CollSeq is the default built-in BINARY.
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3IsBinary(const CollSeq *p){
|
|
+ assert( p==0 || p->xCmp!=binCollFunc || p->pUser!=0
|
|
+ || strcmp(p->zName,"BINARY")==0 );
|
|
+ return p==0 || (p->xCmp==binCollFunc && p->pUser==0);
|
|
+}
|
|
+
|
|
+/*
|
|
** Another built-in collating sequence: NOCASE.
|
|
**
|
|
** This collating sequence is intended to be used for "case independent
|
|
@@ -142606,7 +153765,7 @@
|
|
sqlite3BtreeEnterAll(db);
|
|
for(i=0; i<db->nDb; i++){
|
|
Schema *pSchema = db->aDb[i].pSchema;
|
|
- if( db->aDb[i].pSchema ){
|
|
+ if( pSchema ){
|
|
for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
|
|
Table *pTab = (Table *)sqliteHashData(p);
|
|
if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
|
|
@@ -142824,7 +153983,7 @@
|
|
sqlite3_mutex_leave(db->mutex);
|
|
db->magic = SQLITE_MAGIC_CLOSED;
|
|
sqlite3_mutex_free(db->mutex);
|
|
- assert( db->lookaside.nOut==0 ); /* Fails on a lookaside memory leak */
|
|
+ assert( sqlite3LookasideUsed(db,0)==0 );
|
|
if( db->lookaside.bMalloced ){
|
|
sqlite3_free(db->lookaside.pStart);
|
|
}
|
|
@@ -142852,7 +154011,7 @@
|
|
** the database rollback and schema reset, which can cause false
|
|
** corruption reports in some cases. */
|
|
sqlite3BtreeEnterAll(db);
|
|
- schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
|
|
+ schemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0 && db->init.busy==0;
|
|
|
|
for(i=0; i<db->nDb; i++){
|
|
Btree *p = db->aDb[i].pBt;
|
|
@@ -142866,8 +154025,8 @@
|
|
sqlite3VtabRollback(db);
|
|
sqlite3EndBenignMalloc();
|
|
|
|
- if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ if( schemaChange ){
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
sqlite3ResetAllSchemasOfConnection(db);
|
|
}
|
|
sqlite3BtreeLeaveAll(db);
|
|
@@ -142895,6 +154054,7 @@
|
|
switch( rc ){
|
|
case SQLITE_OK: zName = "SQLITE_OK"; break;
|
|
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
|
|
+ case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
|
|
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
|
|
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
|
|
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
|
|
@@ -142907,9 +154067,10 @@
|
|
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
|
|
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
|
|
case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
|
|
- case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
|
|
+ case SQLITE_READONLY_CANTINIT: zName = "SQLITE_READONLY_CANTINIT"; break;
|
|
case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break;
|
|
case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break;
|
|
+ case SQLITE_READONLY_DIRECTORY: zName = "SQLITE_READONLY_DIRECTORY";break;
|
|
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
|
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
|
case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
|
|
@@ -143029,6 +154190,8 @@
|
|
/* SQLITE_FORMAT */ 0,
|
|
/* SQLITE_RANGE */ "column index out of range",
|
|
/* SQLITE_NOTADB */ "file is not a database",
|
|
+ /* SQLITE_NOTICE */ "notification message",
|
|
+ /* SQLITE_WARNING */ "warning message",
|
|
};
|
|
const char *zErr = "unknown error";
|
|
switch( rc ){
|
|
@@ -143036,6 +154199,14 @@
|
|
zErr = "abort due to ROLLBACK";
|
|
break;
|
|
}
|
|
+ case SQLITE_ROW: {
|
|
+ zErr = "another row available";
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_DONE: {
|
|
+ zErr = "no more rows available";
|
|
+ break;
|
|
+ }
|
|
default: {
|
|
rc &= 0xff;
|
|
if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
|
|
@@ -143052,12 +154223,18 @@
|
|
** again until a timeout value is reached. The timeout value is
|
|
** an integer number of milliseconds passed in as the first
|
|
** argument.
|
|
+**
|
|
+** Return non-zero to retry the lock. Return zero to stop trying
|
|
+** and cause SQLite to return SQLITE_BUSY.
|
|
*/
|
|
static int sqliteDefaultBusyCallback(
|
|
- void *ptr, /* Database connection */
|
|
- int count /* Number of times table has been busy */
|
|
+ void *ptr, /* Database connection */
|
|
+ int count, /* Number of times table has been busy */
|
|
+ sqlite3_file *pFile /* The file on which the lock occurred */
|
|
){
|
|
#if SQLITE_OS_WIN || HAVE_USLEEP
|
|
+ /* This case is for systems that have support for sleeping for fractions of
|
|
+ ** a second. Examples: All windows systems, unix systems with usleep() */
|
|
static const u8 delays[] =
|
|
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
|
|
static const u8 totals[] =
|
|
@@ -143064,9 +154241,22 @@
|
|
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 };
|
|
# define NDELAY ArraySize(delays)
|
|
sqlite3 *db = (sqlite3 *)ptr;
|
|
- int timeout = db->busyTimeout;
|
|
+ int tmout = db->busyTimeout;
|
|
int delay, prior;
|
|
|
|
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
+ if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
|
|
+ if( count ){
|
|
+ tmout = 0;
|
|
+ sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
|
|
+ return 0;
|
|
+ }else{
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ UNUSED_PARAMETER(pFile);
|
|
+#endif
|
|
assert( count>=0 );
|
|
if( count < NDELAY ){
|
|
delay = delays[count];
|
|
@@ -143075,16 +154265,19 @@
|
|
delay = delays[NDELAY-1];
|
|
prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));
|
|
}
|
|
- if( prior + delay > timeout ){
|
|
- delay = timeout - prior;
|
|
+ if( prior + delay > tmout ){
|
|
+ delay = tmout - prior;
|
|
if( delay<=0 ) return 0;
|
|
}
|
|
sqlite3OsSleep(db->pVfs, delay*1000);
|
|
return 1;
|
|
#else
|
|
+ /* This case for unix systems that lack usleep() support. Sleeping
|
|
+ ** must be done in increments of whole seconds */
|
|
sqlite3 *db = (sqlite3 *)ptr;
|
|
- int timeout = ((sqlite3 *)ptr)->busyTimeout;
|
|
- if( (count+1)*1000 > timeout ){
|
|
+ int tmout = ((sqlite3 *)ptr)->busyTimeout;
|
|
+ UNUSED_PARAMETER(pFile);
|
|
+ if( (count+1)*1000 > tmout ){
|
|
return 0;
|
|
}
|
|
sqlite3OsSleep(db->pVfs, 1000000);
|
|
@@ -143095,14 +154288,25 @@
|
|
/*
|
|
** Invoke the given busy handler.
|
|
**
|
|
-** This routine is called when an operation failed with a lock.
|
|
+** This routine is called when an operation failed to acquire a
|
|
+** lock on VFS file pFile.
|
|
+**
|
|
** If this routine returns non-zero, the lock is retried. If it
|
|
** returns 0, the operation aborts with an SQLITE_BUSY error.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){
|
|
+SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
|
|
int rc;
|
|
- if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0;
|
|
- rc = p->xFunc(p->pArg, p->nBusy);
|
|
+ if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
|
|
+ if( p->bExtraFileArg ){
|
|
+ /* Add an extra parameter with the pFile pointer to the end of the
|
|
+ ** callback argument list */
|
|
+ int (*xTra)(void*,int,sqlite3_file*);
|
|
+ xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
|
|
+ rc = xTra(p->pBusyArg, p->nBusy, pFile);
|
|
+ }else{
|
|
+ /* Legacy style busy handler callback */
|
|
+ rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
|
|
+ }
|
|
if( rc==0 ){
|
|
p->nBusy = -1;
|
|
}else{
|
|
@@ -143124,9 +154328,10 @@
|
|
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
|
#endif
|
|
sqlite3_mutex_enter(db->mutex);
|
|
- db->busyHandler.xFunc = xBusy;
|
|
- db->busyHandler.pArg = pArg;
|
|
+ db->busyHandler.xBusyHandler = xBusy;
|
|
+ db->busyHandler.pBusyArg = pArg;
|
|
db->busyHandler.nBusy = 0;
|
|
+ db->busyHandler.bExtraFileArg = 0;
|
|
db->busyTimeout = 0;
|
|
sqlite3_mutex_leave(db->mutex);
|
|
return SQLITE_OK;
|
|
@@ -143174,8 +154379,10 @@
|
|
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
|
#endif
|
|
if( ms>0 ){
|
|
- sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
|
|
+ sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
|
|
+ (void*)db);
|
|
db->busyTimeout = ms;
|
|
+ db->busyHandler.bExtraFileArg = 1;
|
|
}else{
|
|
sqlite3_busy_handler(db, 0, 0);
|
|
}
|
|
@@ -143211,6 +154418,8 @@
|
|
void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
|
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
|
void (*xFinal)(sqlite3_context*),
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value **),
|
|
FuncDestructor *pDestructor
|
|
){
|
|
FuncDef *p;
|
|
@@ -143218,12 +154427,14 @@
|
|
int extraFlags;
|
|
|
|
assert( sqlite3_mutex_held(db->mutex) );
|
|
- if( zFunctionName==0 ||
|
|
- (xSFunc && (xFinal || xStep)) ||
|
|
- (!xSFunc && (xFinal && !xStep)) ||
|
|
- (!xSFunc && (!xFinal && xStep)) ||
|
|
- (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
|
|
- (255<(nName = sqlite3Strlen30( zFunctionName))) ){
|
|
+ assert( xValue==0 || xSFunc==0 );
|
|
+ if( zFunctionName==0 /* Must have a valid name */
|
|
+ || (xSFunc!=0 && xFinal!=0) /* Not both xSFunc and xFinal */
|
|
+ || ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */
|
|
+ || ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */
|
|
+ || (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG)
|
|
+ || (255<(nName = sqlite3Strlen30( zFunctionName)))
|
|
+ ){
|
|
return SQLITE_MISUSE_BKPT;
|
|
}
|
|
|
|
@@ -143244,10 +154455,10 @@
|
|
}else if( enc==SQLITE_ANY ){
|
|
int rc;
|
|
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
|
|
- pUserData, xSFunc, xStep, xFinal, pDestructor);
|
|
+ pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
|
|
- pUserData, xSFunc, xStep, xFinal, pDestructor);
|
|
+ pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
|
|
}
|
|
if( rc!=SQLITE_OK ){
|
|
return rc;
|
|
@@ -143264,7 +154475,7 @@
|
|
** operation to continue but invalidate all precompiled statements.
|
|
*/
|
|
p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0);
|
|
- if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
|
|
+ if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==(u32)enc && p->nArg==nArg ){
|
|
if( db->nVdbeActive ){
|
|
sqlite3ErrorWithMsg(db, SQLITE_BUSY,
|
|
"unable to delete/modify user-function due to active statements");
|
|
@@ -143271,7 +154482,7 @@
|
|
assert( !db->mallocFailed );
|
|
return SQLITE_BUSY;
|
|
}else{
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
}
|
|
}
|
|
|
|
@@ -143293,6 +154504,8 @@
|
|
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
|
|
p->xSFunc = xSFunc ? xSFunc : xStep;
|
|
p->xFinalize = xFinal;
|
|
+ p->xValue = xValue;
|
|
+ p->xInverse = xInverse;
|
|
p->pUserData = pUserData;
|
|
p->nArg = (u16)nArg;
|
|
return SQLITE_OK;
|
|
@@ -143299,32 +154512,24 @@
|
|
}
|
|
|
|
/*
|
|
-** Create new user functions.
|
|
+** Worker function used by utf-8 APIs that create new functions:
|
|
+**
|
|
+** sqlite3_create_function()
|
|
+** sqlite3_create_function_v2()
|
|
+** sqlite3_create_window_function()
|
|
*/
|
|
-SQLITE_API int sqlite3_create_function(
|
|
+static int createFunctionApi(
|
|
sqlite3 *db,
|
|
const char *zFunc,
|
|
int nArg,
|
|
int enc,
|
|
void *p,
|
|
- void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
|
- void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
|
- void (*xFinal)(sqlite3_context*)
|
|
-){
|
|
- return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
|
- xFinal, 0);
|
|
-}
|
|
-
|
|
-SQLITE_API int sqlite3_create_function_v2(
|
|
- sqlite3 *db,
|
|
- const char *zFunc,
|
|
- int nArg,
|
|
- int enc,
|
|
- void *p,
|
|
- void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
|
- void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
void (*xFinal)(sqlite3_context*),
|
|
- void (*xDestroy)(void *)
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void(*xDestroy)(void*)
|
|
){
|
|
int rc = SQLITE_ERROR;
|
|
FuncDestructor *pArg = 0;
|
|
@@ -143336,19 +154541,23 @@
|
|
#endif
|
|
sqlite3_mutex_enter(db->mutex);
|
|
if( xDestroy ){
|
|
- pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
|
|
+ pArg = (FuncDestructor *)sqlite3Malloc(sizeof(FuncDestructor));
|
|
if( !pArg ){
|
|
+ sqlite3OomFault(db);
|
|
xDestroy(p);
|
|
goto out;
|
|
}
|
|
+ pArg->nRef = 0;
|
|
pArg->xDestroy = xDestroy;
|
|
pArg->pUserData = p;
|
|
}
|
|
- rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xSFunc, xStep, xFinal, pArg);
|
|
+ rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p,
|
|
+ xSFunc, xStep, xFinal, xValue, xInverse, pArg
|
|
+ );
|
|
if( pArg && pArg->nRef==0 ){
|
|
assert( rc!=SQLITE_OK );
|
|
xDestroy(p);
|
|
- sqlite3DbFree(db, pArg);
|
|
+ sqlite3_free(pArg);
|
|
}
|
|
|
|
out:
|
|
@@ -143357,6 +154566,52 @@
|
|
return rc;
|
|
}
|
|
|
|
+/*
|
|
+** Create new user functions.
|
|
+*/
|
|
+SQLITE_API int sqlite3_create_function(
|
|
+ sqlite3 *db,
|
|
+ const char *zFunc,
|
|
+ int nArg,
|
|
+ int enc,
|
|
+ void *p,
|
|
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xFinal)(sqlite3_context*)
|
|
+){
|
|
+ return createFunctionApi(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
|
+ xFinal, 0, 0, 0);
|
|
+}
|
|
+SQLITE_API int sqlite3_create_function_v2(
|
|
+ sqlite3 *db,
|
|
+ const char *zFunc,
|
|
+ int nArg,
|
|
+ int enc,
|
|
+ void *p,
|
|
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xFinal)(sqlite3_context*),
|
|
+ void (*xDestroy)(void *)
|
|
+){
|
|
+ return createFunctionApi(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
|
+ xFinal, 0, 0, xDestroy);
|
|
+}
|
|
+SQLITE_API int sqlite3_create_window_function(
|
|
+ sqlite3 *db,
|
|
+ const char *zFunc,
|
|
+ int nArg,
|
|
+ int enc,
|
|
+ void *p,
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xFinal)(sqlite3_context*),
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value **),
|
|
+ void (*xDestroy)(void *)
|
|
+){
|
|
+ return createFunctionApi(db, zFunc, nArg, enc, p, 0, xStep,
|
|
+ xFinal, xValue, xInverse, xDestroy);
|
|
+}
|
|
+
|
|
#ifndef SQLITE_OMIT_UTF16
|
|
SQLITE_API int sqlite3_create_function16(
|
|
sqlite3 *db,
|
|
@@ -143377,7 +154632,7 @@
|
|
sqlite3_mutex_enter(db->mutex);
|
|
assert( !db->mallocFailed );
|
|
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
|
|
- rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0);
|
|
+ rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0,0,0);
|
|
sqlite3DbFree(db, zFunc8);
|
|
rc = sqlite3ApiExit(db, rc);
|
|
sqlite3_mutex_leave(db->mutex);
|
|
@@ -143387,6 +154642,28 @@
|
|
|
|
|
|
/*
|
|
+** The following is the implementation of an SQL function that always
|
|
+** fails with an error message stating that the function is used in the
|
|
+** wrong context. The sqlite3_overload_function() API might construct
|
|
+** SQL function that use this routine so that the functions will exist
|
|
+** for name resolution but are actually overloaded by the xFindFunction
|
|
+** method of virtual tables.
|
|
+*/
|
|
+static void sqlite3InvalidFunction(
|
|
+ sqlite3_context *context, /* The function calling context */
|
|
+ int NotUsed, /* Number of arguments to the function */
|
|
+ sqlite3_value **NotUsed2 /* Value of each argument */
|
|
+){
|
|
+ const char *zName = (const char*)sqlite3_user_data(context);
|
|
+ char *zErr;
|
|
+ UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
+ zErr = sqlite3_mprintf(
|
|
+ "unable to use function %s in the requested context", zName);
|
|
+ sqlite3_result_error(context, zErr, -1);
|
|
+ sqlite3_free(zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
** Declare that a function has been overloaded by a virtual table.
|
|
**
|
|
** If the function already exists as a regular global function, then
|
|
@@ -143403,7 +154680,8 @@
|
|
const char *zName,
|
|
int nArg
|
|
){
|
|
- int rc = SQLITE_OK;
|
|
+ int rc;
|
|
+ char *zCopy;
|
|
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !sqlite3SafetyCheckOk(db) || zName==0 || nArg<-2 ){
|
|
@@ -143411,13 +154689,13 @@
|
|
}
|
|
#endif
|
|
sqlite3_mutex_enter(db->mutex);
|
|
- if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){
|
|
- rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
|
- 0, sqlite3InvalidFunction, 0, 0, 0);
|
|
- }
|
|
- rc = sqlite3ApiExit(db, rc);
|
|
+ rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0;
|
|
sqlite3_mutex_leave(db->mutex);
|
|
- return rc;
|
|
+ if( rc ) return SQLITE_OK;
|
|
+ zCopy = sqlite3_mprintf(zName);
|
|
+ if( zCopy==0 ) return SQLITE_NOMEM;
|
|
+ return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8,
|
|
+ zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free);
|
|
}
|
|
|
|
#ifndef SQLITE_OMIT_TRACE
|
|
@@ -143768,7 +155046,8 @@
|
|
** checkpointed. If an error is encountered it is returned immediately -
|
|
** no attempt is made to checkpoint any remaining databases.
|
|
**
|
|
-** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
|
|
+** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART
|
|
+** or TRUNCATE.
|
|
*/
|
|
SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){
|
|
int rc = SQLITE_OK; /* Return code */
|
|
@@ -143978,7 +155257,7 @@
|
|
"unable to delete/modify collation sequence due to active statements");
|
|
return SQLITE_BUSY;
|
|
}
|
|
- sqlite3ExpirePreparedStatements(db);
|
|
+ sqlite3ExpirePreparedStatements(db, 0);
|
|
|
|
/* If collation sequence pColl was created directly by a call to
|
|
** sqlite3_create_collation, and not generated by synthCollSeq(),
|
|
@@ -144414,6 +155693,7 @@
|
|
}else{
|
|
isThreadsafe = sqlite3GlobalConfig.bFullMutex;
|
|
}
|
|
+
|
|
if( flags & SQLITE_OPEN_PRIVATECACHE ){
|
|
flags &= ~SQLITE_OPEN_SHAREDCACHE;
|
|
}else if( sqlite3GlobalConfig.sharedCacheEnabled ){
|
|
@@ -144446,7 +155726,11 @@
|
|
/* Allocate the sqlite data structure */
|
|
db = sqlite3MallocZero( sizeof(sqlite3) );
|
|
if( db==0 ) goto opendb_out;
|
|
- if( isThreadsafe ){
|
|
+ if( isThreadsafe
|
|
+#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|
|
+ || sqlite3GlobalConfig.bCoreMutex
|
|
+#endif
|
|
+ ){
|
|
db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
|
if( db->mutex==0 ){
|
|
sqlite3_free(db);
|
|
@@ -144453,6 +155737,9 @@
|
|
db = 0;
|
|
goto opendb_out;
|
|
}
|
|
+ if( isThreadsafe==0 ){
|
|
+ sqlite3MutexWarnOnContention(db->mutex);
|
|
+ }
|
|
}
|
|
sqlite3_mutex_enter(db->mutex);
|
|
db->errMask = 0xff;
|
|
@@ -144459,6 +155746,7 @@
|
|
db->nDb = 2;
|
|
db->magic = SQLITE_MAGIC_BUSY;
|
|
db->aDb = db->aDbStatic;
|
|
+ db->lookaside.bDisable = 1;
|
|
|
|
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
|
|
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
|
|
@@ -144499,6 +155787,9 @@
|
|
#if defined(SQLITE_ENABLE_QPSG)
|
|
| SQLITE_EnableQPSG
|
|
#endif
|
|
+#if defined(SQLITE_DEFAULT_DEFENSIVE)
|
|
+ | SQLITE_Defensive
|
|
+#endif
|
|
;
|
|
sqlite3HashInit(&db->aCollSeq);
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
@@ -144634,7 +155925,7 @@
|
|
}
|
|
#endif
|
|
|
|
-#ifdef SQLITE_ENABLE_ICU
|
|
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
|
if( !db->mallocFailed && rc==SQLITE_OK ){
|
|
rc = sqlite3IcuInit(db);
|
|
}
|
|
@@ -144646,6 +155937,12 @@
|
|
}
|
|
#endif
|
|
|
|
+#ifdef SQLITE_ENABLE_DBPAGE_VTAB
|
|
+ if( !db->mallocFailed && rc==SQLITE_OK){
|
|
+ rc = sqlite3DbpageRegister(db);
|
|
+ }
|
|
+#endif
|
|
+
|
|
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
|
|
if( !db->mallocFailed && rc==SQLITE_OK){
|
|
rc = sqlite3DbstatRegister(db);
|
|
@@ -144930,7 +156227,7 @@
|
|
** 2. Invoke sqlite3_log() to provide the source code location where
|
|
** a low-level error is first detected.
|
|
*/
|
|
-static int reportError(int iErr, int lineno, const char *zType){
|
|
+SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType){
|
|
sqlite3_log(iErr, "%s at line %d of [%.10s]",
|
|
zType, lineno, 20+sqlite3_sourceid());
|
|
return iErr;
|
|
@@ -144937,15 +156234,15 @@
|
|
}
|
|
SQLITE_PRIVATE int sqlite3CorruptError(int lineno){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- return reportError(SQLITE_CORRUPT, lineno, "database corruption");
|
|
+ return sqlite3ReportError(SQLITE_CORRUPT, lineno, "database corruption");
|
|
}
|
|
SQLITE_PRIVATE int sqlite3MisuseError(int lineno){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- return reportError(SQLITE_MISUSE, lineno, "misuse");
|
|
+ return sqlite3ReportError(SQLITE_MISUSE, lineno, "misuse");
|
|
}
|
|
SQLITE_PRIVATE int sqlite3CantopenError(int lineno){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- return reportError(SQLITE_CANTOPEN, lineno, "cannot open file");
|
|
+ return sqlite3ReportError(SQLITE_CANTOPEN, lineno, "cannot open file");
|
|
}
|
|
#ifdef SQLITE_DEBUG
|
|
SQLITE_PRIVATE int sqlite3CorruptPgnoError(int lineno, Pgno pgno){
|
|
@@ -144952,15 +156249,15 @@
|
|
char zMsg[100];
|
|
sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno);
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- return reportError(SQLITE_CORRUPT, lineno, zMsg);
|
|
+ return sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
|
|
}
|
|
SQLITE_PRIVATE int sqlite3NomemError(int lineno){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- return reportError(SQLITE_NOMEM, lineno, "OOM");
|
|
+ return sqlite3ReportError(SQLITE_NOMEM, lineno, "OOM");
|
|
}
|
|
SQLITE_PRIVATE int sqlite3IoerrnomemError(int lineno){
|
|
testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
- return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
|
|
+ return sqlite3ReportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
|
|
}
|
|
#endif
|
|
|
|
@@ -145153,10 +156450,11 @@
|
|
}else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
|
|
*(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
|
|
rc = SQLITE_OK;
|
|
- }else if( fd->pMethods ){
|
|
+ }else if( op==SQLITE_FCNTL_DATA_VERSION ){
|
|
+ *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
|
|
+ rc = SQLITE_OK;
|
|
+ }else{
|
|
rc = sqlite3OsFileControl(fd, op, pArg);
|
|
- }else{
|
|
- rc = SQLITE_NOTFOUND;
|
|
}
|
|
sqlite3BtreeLeave(pBtree);
|
|
}
|
|
@@ -145305,7 +156603,7 @@
|
|
** This action provides a run-time test to see how the ALWAYS and
|
|
** NEVER macros were defined at compile-time.
|
|
**
|
|
- ** The return value is ALWAYS(X).
|
|
+ ** The return value is ALWAYS(X) if X is true, or 0 if X is false.
|
|
**
|
|
** The recommended test is X==2. If the return value is 2, that means
|
|
** ALWAYS() and NEVER() are both no-op pass-through macros, which is the
|
|
@@ -145328,7 +156626,7 @@
|
|
*/
|
|
case SQLITE_TESTCTRL_ALWAYS: {
|
|
int x = va_arg(ap,int);
|
|
- rc = ALWAYS(x);
|
|
+ rc = x ? ALWAYS(x) : 0;
|
|
break;
|
|
}
|
|
|
|
@@ -145377,51 +156675,28 @@
|
|
break;
|
|
}
|
|
|
|
-#ifdef SQLITE_N_KEYWORD
|
|
- /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
|
|
+ /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
|
|
**
|
|
- ** If zWord is a keyword recognized by the parser, then return the
|
|
- ** number of keywords. Or if zWord is not a keyword, return 0.
|
|
- **
|
|
- ** This test feature is only available in the amalgamation since
|
|
- ** the SQLITE_N_KEYWORD macro is not defined in this file if SQLite
|
|
- ** is built using separate source files.
|
|
+ ** If parameter onoff is non-zero, subsequent calls to localtime()
|
|
+ ** and its variants fail. If onoff is zero, undo this setting.
|
|
*/
|
|
- case SQLITE_TESTCTRL_ISKEYWORD: {
|
|
- const char *zWord = va_arg(ap, const char*);
|
|
- int n = sqlite3Strlen30(zWord);
|
|
- rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0;
|
|
+ case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
|
|
+ sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
|
|
break;
|
|
}
|
|
-#endif
|
|
|
|
- /* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree);
|
|
+ /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff);
|
|
**
|
|
- ** Pass pFree into sqlite3ScratchFree().
|
|
- ** If sz>0 then allocate a scratch buffer into pNew.
|
|
+ ** 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.
|
|
*/
|
|
- case SQLITE_TESTCTRL_SCRATCHMALLOC: {
|
|
- void *pFree, **ppNew;
|
|
- int sz;
|
|
- sz = va_arg(ap, int);
|
|
- ppNew = va_arg(ap, void**);
|
|
- pFree = va_arg(ap, void*);
|
|
- if( sz ) *ppNew = sqlite3ScratchMalloc(sz);
|
|
- sqlite3ScratchFree(pFree);
|
|
+ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: {
|
|
+ sqlite3GlobalConfig.bInternalFunctions = va_arg(ap, int);
|
|
break;
|
|
}
|
|
|
|
- /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
|
|
- **
|
|
- ** If parameter onoff is non-zero, configure the wrappers so that all
|
|
- ** subsequent calls to localtime() and variants fail. If onoff is zero,
|
|
- ** undo this setting.
|
|
- */
|
|
- case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
|
|
- sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
|
|
- break;
|
|
- }
|
|
-
|
|
/* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int);
|
|
**
|
|
** Set or clear a flag that indicates that the database file is always well-
|
|
@@ -145452,7 +156727,8 @@
|
|
*/
|
|
case SQLITE_TESTCTRL_VDBE_COVERAGE: {
|
|
#ifdef SQLITE_VDBE_COVERAGE
|
|
- typedef void (*branch_callback)(void*,int,u8,u8);
|
|
+ typedef void (*branch_callback)(void*,unsigned int,
|
|
+ unsigned char,unsigned char);
|
|
sqlite3GlobalConfig.xVdbeBranch = va_arg(ap,branch_callback);
|
|
sqlite3GlobalConfig.pVdbeBranchArg = va_arg(ap,void*);
|
|
#endif
|
|
@@ -145504,6 +156780,22 @@
|
|
sqlite3_mutex_leave(db->mutex);
|
|
break;
|
|
}
|
|
+
|
|
+#if defined(YYCOVERAGE)
|
|
+ /* sqlite3_test_control(SQLITE_TESTCTRL_PARSER_COVERAGE, FILE *out)
|
|
+ **
|
|
+ ** This test control (only available when SQLite is compiled with
|
|
+ ** -DYYCOVERAGE) writes a report onto "out" that shows all
|
|
+ ** state/lookahead combinations in the parser state machine
|
|
+ ** which are never exercised. If any state is missed, make the
|
|
+ ** return code SQLITE_ERROR.
|
|
+ */
|
|
+ case SQLITE_TESTCTRL_PARSER_COVERAGE: {
|
|
+ FILE *out = va_arg(ap, FILE*);
|
|
+ if( sqlite3ParserCoverage(out) ) rc = SQLITE_ERROR;
|
|
+ break;
|
|
+ }
|
|
+#endif /* defined(YYCOVERAGE) */
|
|
}
|
|
va_end(ap);
|
|
#endif /* SQLITE_UNTESTABLE */
|
|
@@ -145552,7 +156844,7 @@
|
|
){
|
|
const char *z = sqlite3_uri_parameter(zFilename, zParam);
|
|
sqlite3_int64 v;
|
|
- if( z && sqlite3DecOrHexToI64(z, &v)==SQLITE_OK ){
|
|
+ if( z && sqlite3DecOrHexToI64(z, &v)==0 ){
|
|
bDflt = v;
|
|
}
|
|
return bDflt;
|
|
@@ -145623,7 +156915,7 @@
|
|
if( iDb==0 || iDb>1 ){
|
|
Btree *pBt = db->aDb[iDb].pBt;
|
|
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
|
- rc = sqlite3BtreeBeginTrans(pBt, 0);
|
|
+ rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
|
}
|
|
@@ -145658,12 +156950,30 @@
|
|
iDb = sqlite3FindDbName(db, zDb);
|
|
if( iDb==0 || iDb>1 ){
|
|
Btree *pBt = db->aDb[iDb].pBt;
|
|
- if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
|
|
- rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot);
|
|
+ if( sqlite3BtreeIsInTrans(pBt)==0 ){
|
|
+ Pager *pPager = sqlite3BtreePager(pBt);
|
|
+ int bUnlock = 0;
|
|
+ if( sqlite3BtreeIsInReadTrans(pBt) ){
|
|
+ if( db->nVdbeActive==0 ){
|
|
+ rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ bUnlock = 1;
|
|
+ rc = sqlite3BtreeCommit(pBt);
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ rc = SQLITE_OK;
|
|
+ }
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3BtreeBeginTrans(pBt, 0);
|
|
- sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0);
|
|
+ rc = sqlite3PagerSnapshotOpen(pPager, pSnapshot);
|
|
}
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
|
+ sqlite3PagerSnapshotOpen(pPager, 0);
|
|
+ }
|
|
+ if( bUnlock ){
|
|
+ sqlite3PagerSnapshotUnlock(pPager);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -145693,7 +157003,7 @@
|
|
if( iDb==0 || iDb>1 ){
|
|
Btree *pBt = db->aDb[iDb].pBt;
|
|
if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
|
|
- rc = sqlite3BtreeBeginTrans(pBt, 0);
|
|
+ rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
|
|
sqlite3BtreeCommit(pBt);
|
|
@@ -147261,7 +158571,7 @@
|
|
);
|
|
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
|
|
#ifdef SQLITE_TEST
|
|
-SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
|
|
+SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash*);
|
|
SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
|
|
#endif
|
|
|
|
@@ -148829,7 +160139,7 @@
|
|
const char *zCsr = zNode; /* Cursor to iterate through node */
|
|
const char *zEnd = &zCsr[nNode];/* End of interior node buffer */
|
|
char *zBuffer = 0; /* Buffer to load terms into */
|
|
- int nAlloc = 0; /* Size of allocated buffer */
|
|
+ i64 nAlloc = 0; /* Size of allocated buffer */
|
|
int isFirstTerm = 1; /* True when processing first term on page */
|
|
sqlite3_int64 iChild; /* Block id of child node to descend to */
|
|
|
|
@@ -148867,14 +160177,14 @@
|
|
zCsr += fts3GetVarint32(zCsr, &nSuffix);
|
|
|
|
assert( nPrefix>=0 && nSuffix>=0 );
|
|
- if( &zCsr[nSuffix]>zEnd ){
|
|
+ if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){
|
|
rc = FTS_CORRUPT_VTAB;
|
|
goto finish_scan;
|
|
}
|
|
- if( nPrefix+nSuffix>nAlloc ){
|
|
+ if( (i64)nPrefix+nSuffix>nAlloc ){
|
|
char *zNew;
|
|
- nAlloc = (nPrefix+nSuffix) * 2;
|
|
- zNew = (char *)sqlite3_realloc(zBuffer, nAlloc);
|
|
+ nAlloc = ((i64)nPrefix+nSuffix) * 2;
|
|
+ zNew = (char *)sqlite3_realloc64(zBuffer, nAlloc);
|
|
if( !zNew ){
|
|
rc = SQLITE_NOMEM;
|
|
goto finish_scan;
|
|
@@ -150816,7 +162126,7 @@
|
|
int rc = SQLITE_OK;
|
|
UNUSED_PARAMETER(iSavepoint);
|
|
assert( ((Fts3Table *)pVtab)->inTransaction );
|
|
- assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
|
|
+ assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
|
|
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
|
|
if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
|
|
rc = fts3SyncMethod(pVtab);
|
|
@@ -150854,8 +162164,23 @@
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
+/*
|
|
+** Return true if zName is the extension on one of the shadow tables used
|
|
+** by this module.
|
|
+*/
|
|
+static int fts3ShadowName(const char *zName){
|
|
+ static const char *azName[] = {
|
|
+ "content", "docsize", "segdir", "segments", "stat",
|
|
+ };
|
|
+ unsigned int i;
|
|
+ for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
|
|
+ if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static const sqlite3_module fts3Module = {
|
|
- /* iVersion */ 2,
|
|
+ /* iVersion */ 3,
|
|
/* xCreate */ fts3CreateMethod,
|
|
/* xConnect */ fts3ConnectMethod,
|
|
/* xBestIndex */ fts3BestIndexMethod,
|
|
@@ -150878,6 +162203,7 @@
|
|
/* xSavepoint */ fts3SavepointMethod,
|
|
/* xRelease */ fts3ReleaseMethod,
|
|
/* xRollbackTo */ fts3RollbackToMethod,
|
|
+ /* xShadowName */ fts3ShadowName,
|
|
};
|
|
|
|
/*
|
|
@@ -150971,7 +162297,7 @@
|
|
|
|
#ifdef SQLITE_TEST
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3Fts3ExprInitTestInterface(db);
|
|
+ rc = sqlite3Fts3ExprInitTestInterface(db, pHash);
|
|
}
|
|
#endif
|
|
|
|
@@ -151158,6 +162484,7 @@
|
|
return rc;
|
|
}
|
|
|
|
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
|
|
/*
|
|
** This function is called on each phrase after the position lists for
|
|
** any deferred tokens have been loaded into memory. It updates the phrases
|
|
@@ -151261,6 +162588,7 @@
|
|
|
|
return SQLITE_OK;
|
|
}
|
|
+#endif /* SQLITE_DISABLE_FTS4_DEFERRED */
|
|
|
|
/*
|
|
** Maximum number of tokens a phrase may have to be considered for the
|
|
@@ -153509,7 +164837,8 @@
|
|
0, /* xRename */
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
- 0 /* xRollbackTo */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
};
|
|
int rc; /* Return code */
|
|
|
|
@@ -154632,34 +165961,6 @@
|
|
/* #include <stdio.h> */
|
|
|
|
/*
|
|
-** Function to query the hash-table of tokenizers (see README.tokenizers).
|
|
-*/
|
|
-static int queryTestTokenizer(
|
|
- sqlite3 *db,
|
|
- const char *zName,
|
|
- const sqlite3_tokenizer_module **pp
|
|
-){
|
|
- int rc;
|
|
- sqlite3_stmt *pStmt;
|
|
- const char zSql[] = "SELECT fts3_tokenizer(?)";
|
|
-
|
|
- *pp = 0;
|
|
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
- if( rc!=SQLITE_OK ){
|
|
- return rc;
|
|
- }
|
|
-
|
|
- sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
|
|
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
- if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
|
|
- memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
|
|
- }
|
|
- }
|
|
-
|
|
- return sqlite3_finalize(pStmt);
|
|
-}
|
|
-
|
|
-/*
|
|
** Return a pointer to a buffer containing a text representation of the
|
|
** expression passed as the first argument. The buffer is obtained from
|
|
** sqlite3_malloc(). It is the responsibility of the caller to use
|
|
@@ -154726,12 +166027,12 @@
|
|
**
|
|
** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2');
|
|
*/
|
|
-static void fts3ExprTest(
|
|
+static void fts3ExprTestCommon(
|
|
+ int bRebalance,
|
|
sqlite3_context *context,
|
|
int argc,
|
|
sqlite3_value **argv
|
|
){
|
|
- sqlite3_tokenizer_module const *pModule = 0;
|
|
sqlite3_tokenizer *pTokenizer = 0;
|
|
int rc;
|
|
char **azCol = 0;
|
|
@@ -154741,7 +166042,9 @@
|
|
int ii;
|
|
Fts3Expr *pExpr;
|
|
char *zBuf = 0;
|
|
- sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ Fts3Hash *pHash = (Fts3Hash*)sqlite3_user_data(context);
|
|
+ const char *zTokenizer = 0;
|
|
+ char *zErr = 0;
|
|
|
|
if( argc<3 ){
|
|
sqlite3_result_error(context,
|
|
@@ -154750,24 +166053,18 @@
|
|
return;
|
|
}
|
|
|
|
- rc = queryTestTokenizer(db,
|
|
- (const char *)sqlite3_value_text(argv[0]), &pModule);
|
|
- if( rc==SQLITE_NOMEM ){
|
|
- sqlite3_result_error_nomem(context);
|
|
- goto exprtest_out;
|
|
- }else if( !pModule ){
|
|
- sqlite3_result_error(context, "No such tokenizer module", -1);
|
|
- goto exprtest_out;
|
|
+ zTokenizer = (const char*)sqlite3_value_text(argv[0]);
|
|
+ rc = sqlite3Fts3InitTokenizer(pHash, zTokenizer, &pTokenizer, &zErr);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ if( rc==SQLITE_NOMEM ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_error(context, zErr, -1);
|
|
+ }
|
|
+ sqlite3_free(zErr);
|
|
+ return;
|
|
}
|
|
|
|
- rc = pModule->xCreate(0, 0, &pTokenizer);
|
|
- assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
|
|
- if( rc==SQLITE_NOMEM ){
|
|
- sqlite3_result_error_nomem(context);
|
|
- goto exprtest_out;
|
|
- }
|
|
- pTokenizer->pModule = pModule;
|
|
-
|
|
zExpr = (const char *)sqlite3_value_text(argv[1]);
|
|
nExpr = sqlite3_value_bytes(argv[1]);
|
|
nCol = argc-2;
|
|
@@ -154780,7 +166077,7 @@
|
|
azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
|
|
}
|
|
|
|
- if( sqlite3_user_data(context) ){
|
|
+ if( bRebalance ){
|
|
char *zDummy = 0;
|
|
rc = sqlite3Fts3ExprParse(
|
|
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr, &zDummy
|
|
@@ -154806,23 +166103,38 @@
|
|
sqlite3Fts3ExprFree(pExpr);
|
|
|
|
exprtest_out:
|
|
- if( pModule && pTokenizer ){
|
|
- rc = pModule->xDestroy(pTokenizer);
|
|
+ if( pTokenizer ){
|
|
+ rc = pTokenizer->pModule->xDestroy(pTokenizer);
|
|
}
|
|
sqlite3_free(azCol);
|
|
}
|
|
|
|
+static void fts3ExprTest(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ fts3ExprTestCommon(0, context, argc, argv);
|
|
+}
|
|
+static void fts3ExprTestRebalance(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ fts3ExprTestCommon(1, context, argc, argv);
|
|
+}
|
|
+
|
|
/*
|
|
** Register the query expression parser test function fts3_exprtest()
|
|
** with database connection db.
|
|
*/
|
|
-SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3* db){
|
|
+SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash *pHash){
|
|
int rc = sqlite3_create_function(
|
|
- db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0
|
|
+ db, "fts3_exprtest", -1, SQLITE_UTF8, (void*)pHash, fts3ExprTest, 0, 0
|
|
);
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3_create_function(db, "fts3_exprtest_rebalance",
|
|
- -1, SQLITE_UTF8, (void *)1, fts3ExprTest, 0, 0
|
|
+ -1, SQLITE_UTF8, (void*)pHash, fts3ExprTestRebalance, 0, 0
|
|
);
|
|
}
|
|
return rc;
|
|
@@ -157085,7 +168397,8 @@
|
|
0, /* xRename */
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
- 0 /* xRollbackTo */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
};
|
|
int rc; /* Return code */
|
|
|
|
@@ -158473,15 +169786,19 @@
|
|
** safe (no risk of overread) even if the node data is corrupted. */
|
|
pNext += fts3GetVarint32(pNext, &nPrefix);
|
|
pNext += fts3GetVarint32(pNext, &nSuffix);
|
|
- if( nPrefix<0 || nSuffix<=0
|
|
- || &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
|
|
+ if( nSuffix<=0
|
|
+ || (&pReader->aNode[pReader->nNode] - pNext)<nSuffix
|
|
+ || nPrefix>pReader->nTermAlloc
|
|
){
|
|
return FTS_CORRUPT_VTAB;
|
|
}
|
|
|
|
- if( nPrefix+nSuffix>pReader->nTermAlloc ){
|
|
- int nNew = (nPrefix+nSuffix)*2;
|
|
- char *zNew = sqlite3_realloc(pReader->zTerm, nNew);
|
|
+ /* Both nPrefix and nSuffix were read by fts3GetVarint32() and so are
|
|
+ ** between 0 and 0x7FFFFFFF. But the sum of the two may cause integer
|
|
+ ** overflow - hence the (i64) casts. */
|
|
+ if( (i64)nPrefix+nSuffix>(i64)pReader->nTermAlloc ){
|
|
+ i64 nNew = ((i64)nPrefix+nSuffix)*2;
|
|
+ char *zNew = sqlite3_realloc64(pReader->zTerm, nNew);
|
|
if( !zNew ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -158503,7 +169820,7 @@
|
|
** b-tree node. And that the final byte of the doclist is 0x00. If either
|
|
** of these statements is untrue, then the data structure is corrupt.
|
|
*/
|
|
- if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
|
|
+ if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)<pReader->nDoclist
|
|
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
|
|
){
|
|
return FTS_CORRUPT_VTAB;
|
|
@@ -159007,6 +170324,7 @@
|
|
sqlite3_bind_blob(pStmt, 2, z, n, SQLITE_STATIC);
|
|
sqlite3_step(pStmt);
|
|
rc = sqlite3_reset(pStmt);
|
|
+ sqlite3_bind_null(pStmt, 2);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -159063,6 +170381,7 @@
|
|
sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC);
|
|
sqlite3_step(pStmt);
|
|
rc = sqlite3_reset(pStmt);
|
|
+ sqlite3_bind_null(pStmt, 6);
|
|
}
|
|
return rc;
|
|
}
|
|
@@ -160542,6 +171861,7 @@
|
|
sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, SQLITE_STATIC);
|
|
sqlite3_step(pStmt);
|
|
*pRC = sqlite3_reset(pStmt);
|
|
+ sqlite3_bind_null(pStmt, 2);
|
|
sqlite3_free(a);
|
|
}
|
|
|
|
@@ -160826,6 +172146,9 @@
|
|
}
|
|
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
|
|
|
|
+ if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){
|
|
+ return SQLITE_CORRUPT_VTAB;
|
|
+ }
|
|
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
|
|
if( rc==SQLITE_OK ){
|
|
memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix);
|
|
@@ -160833,6 +172156,9 @@
|
|
p->iOff += nSuffix;
|
|
if( p->iChild==0 ){
|
|
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
|
|
+ if( (p->nNode-p->iOff)<p->nDoclist ){
|
|
+ return SQLITE_CORRUPT_VTAB;
|
|
+ }
|
|
p->aDoclist = &p->aNode[p->iOff];
|
|
p->iOff += p->nDoclist;
|
|
}
|
|
@@ -160840,7 +172166,6 @@
|
|
}
|
|
|
|
assert( p->iOff<=p->nNode );
|
|
-
|
|
return rc;
|
|
}
|
|
|
|
@@ -161730,6 +173055,7 @@
|
|
sqlite3_bind_int(pChomp, 4, iIdx);
|
|
sqlite3_step(pChomp);
|
|
rc = sqlite3_reset(pChomp);
|
|
+ sqlite3_bind_null(pChomp, 2);
|
|
}
|
|
}
|
|
|
|
@@ -161809,6 +173135,7 @@
|
|
sqlite3_bind_blob(pReplace, 2, pHint->a, pHint->n, SQLITE_STATIC);
|
|
sqlite3_step(pReplace);
|
|
rc = sqlite3_reset(pReplace);
|
|
+ sqlite3_bind_null(pReplace, 2);
|
|
}
|
|
|
|
return rc;
|
|
@@ -162623,7 +173950,6 @@
|
|
){
|
|
Fts3Table *p = (Fts3Table *)pVtab;
|
|
int rc = SQLITE_OK; /* Return Code */
|
|
- int isRemove = 0; /* True for an UPDATE or DELETE */
|
|
u32 *aSzIns = 0; /* Sizes of inserted documents */
|
|
u32 *aSzDel = 0; /* Sizes of deleted documents */
|
|
int nChng = 0; /* Net change in number of documents */
|
|
@@ -162721,7 +174047,6 @@
|
|
if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
|
|
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
|
|
rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
|
|
- isRemove = 1;
|
|
}
|
|
|
|
/* If this is an INSERT or UPDATE operation, insert the new record. */
|
|
@@ -162733,7 +174058,7 @@
|
|
rc = FTS_CORRUPT_VTAB;
|
|
}
|
|
}
|
|
- if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
|
|
+ if( rc==SQLITE_OK ){
|
|
rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid);
|
|
}
|
|
if( rc==SQLITE_OK ){
|
|
@@ -165253,6 +176578,2550 @@
|
|
#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
|
|
|
|
/************** End of fts3_unicode2.c ***************************************/
|
|
+/************** Begin file json1.c *******************************************/
|
|
+/*
|
|
+** 2015-08-12
|
|
+**
|
|
+** 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 JSON functions. The interface is
|
|
+** modeled after MySQL JSON functions:
|
|
+**
|
|
+** https://dev.mysql.com/doc/refman/5.7/en/json.html
|
|
+**
|
|
+** For the time being, all JSON is stored as pure text. (We might add
|
|
+** a JSONB type in the future which stores a binary encoding of JSON in
|
|
+** a BLOB, but there is no support for JSONB in the current implementation.
|
|
+** This implementation parses JSON text at 250 MB/s, so it is hard to see
|
|
+** how JSONB might improve on that.)
|
|
+*/
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
|
|
+#if !defined(SQLITEINT_H)
|
|
+/* #include "sqlite3ext.h" */
|
|
+#endif
|
|
+SQLITE_EXTENSION_INIT1
|
|
+/* #include <assert.h> */
|
|
+/* #include <string.h> */
|
|
+/* #include <stdlib.h> */
|
|
+/* #include <stdarg.h> */
|
|
+
|
|
+/* Mark a function parameter as unused, to suppress nuisance compiler
|
|
+** warnings. */
|
|
+#ifndef UNUSED_PARAM
|
|
+# define UNUSED_PARAM(X) (void)(X)
|
|
+#endif
|
|
+
|
|
+#ifndef LARGEST_INT64
|
|
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
|
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
|
|
+** to pass signed char values.
|
|
+*/
|
|
+#ifdef sqlite3Isdigit
|
|
+ /* Use the SQLite core versions if this routine is part of the
|
|
+ ** SQLite amalgamation */
|
|
+# define safe_isdigit(x) sqlite3Isdigit(x)
|
|
+# define safe_isalnum(x) sqlite3Isalnum(x)
|
|
+# define safe_isxdigit(x) sqlite3Isxdigit(x)
|
|
+#else
|
|
+ /* Use the standard library for separate compilation */
|
|
+#include <ctype.h> /* amalgamator: keep */
|
|
+# define safe_isdigit(x) isdigit((unsigned char)(x))
|
|
+# define safe_isalnum(x) isalnum((unsigned char)(x))
|
|
+# define safe_isxdigit(x) isxdigit((unsigned char)(x))
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Growing our own isspace() routine this way is twice as fast as
|
|
+** the library isspace() function, resulting in a 7% overall performance
|
|
+** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
|
|
+*/
|
|
+static const char jsonIsSpace[] = {
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+};
|
|
+#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
|
|
+
|
|
+#ifndef SQLITE_AMALGAMATION
|
|
+ /* Unsigned integer types. These are already defined in the sqliteInt.h,
|
|
+ ** but the definitions need to be repeated for separate compilation. */
|
|
+ typedef sqlite3_uint64 u64;
|
|
+ typedef unsigned int u32;
|
|
+ typedef unsigned short int u16;
|
|
+ typedef unsigned char u8;
|
|
+#endif
|
|
+
|
|
+/* Objects */
|
|
+typedef struct JsonString JsonString;
|
|
+typedef struct JsonNode JsonNode;
|
|
+typedef struct JsonParse JsonParse;
|
|
+
|
|
+/* An instance of this object represents a JSON string
|
|
+** under construction. Really, this is a generic string accumulator
|
|
+** that can be and is used to create strings other than JSON.
|
|
+*/
|
|
+struct JsonString {
|
|
+ sqlite3_context *pCtx; /* Function context - put error messages here */
|
|
+ char *zBuf; /* Append JSON content here */
|
|
+ u64 nAlloc; /* Bytes of storage available in zBuf[] */
|
|
+ u64 nUsed; /* Bytes of zBuf[] currently used */
|
|
+ u8 bStatic; /* True if zBuf is static space */
|
|
+ u8 bErr; /* True if an error has been encountered */
|
|
+ char zSpace[100]; /* Initial static space */
|
|
+};
|
|
+
|
|
+/* JSON type values
|
|
+*/
|
|
+#define JSON_NULL 0
|
|
+#define JSON_TRUE 1
|
|
+#define JSON_FALSE 2
|
|
+#define JSON_INT 3
|
|
+#define JSON_REAL 4
|
|
+#define JSON_STRING 5
|
|
+#define JSON_ARRAY 6
|
|
+#define JSON_OBJECT 7
|
|
+
|
|
+/* The "subtype" set for JSON values */
|
|
+#define JSON_SUBTYPE 74 /* Ascii for "J" */
|
|
+
|
|
+/*
|
|
+** Names of the various JSON types:
|
|
+*/
|
|
+static const char * const jsonType[] = {
|
|
+ "null", "true", "false", "integer", "real", "text", "array", "object"
|
|
+};
|
|
+
|
|
+/* Bit values for the JsonNode.jnFlag field
|
|
+*/
|
|
+#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
|
|
+#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
|
|
+#define JNODE_REMOVE 0x04 /* Do not output */
|
|
+#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
|
|
+#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
|
|
+#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
|
|
+#define JNODE_LABEL 0x40 /* Is a label of an object */
|
|
+
|
|
+
|
|
+/* A single node of parsed JSON
|
|
+*/
|
|
+struct JsonNode {
|
|
+ u8 eType; /* One of the JSON_ type values */
|
|
+ u8 jnFlags; /* JNODE flags */
|
|
+ u32 n; /* Bytes of content, or number of sub-nodes */
|
|
+ union {
|
|
+ const char *zJContent; /* Content for INT, REAL, and STRING */
|
|
+ u32 iAppend; /* More terms for ARRAY and OBJECT */
|
|
+ u32 iKey; /* Key for ARRAY objects in json_tree() */
|
|
+ u32 iReplace; /* Replacement content for JNODE_REPLACE */
|
|
+ JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */
|
|
+ } u;
|
|
+};
|
|
+
|
|
+/* A completely parsed JSON string
|
|
+*/
|
|
+struct JsonParse {
|
|
+ u32 nNode; /* Number of slots of aNode[] used */
|
|
+ u32 nAlloc; /* Number of slots of aNode[] allocated */
|
|
+ JsonNode *aNode; /* Array of nodes containing the parse */
|
|
+ const char *zJson; /* Original JSON string */
|
|
+ u32 *aUp; /* Index of parent of each node */
|
|
+ u8 oom; /* Set to true if out of memory */
|
|
+ u8 nErr; /* Number of errors seen */
|
|
+ u16 iDepth; /* Nesting depth */
|
|
+ int nJson; /* Length of the zJson string in bytes */
|
|
+ u32 iHold; /* Replace cache line with the lowest iHold value */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Maximum nesting depth of JSON for this implementation.
|
|
+**
|
|
+** This limit is needed to avoid a stack overflow in the recursive
|
|
+** descent parser. A depth of 2000 is far deeper than any sane JSON
|
|
+** should go.
|
|
+*/
|
|
+#define JSON_MAX_DEPTH 2000
|
|
+
|
|
+/**************************************************************************
|
|
+** Utility routines for dealing with JsonString objects
|
|
+**************************************************************************/
|
|
+
|
|
+/* Set the JsonString object to an empty string
|
|
+*/
|
|
+static void jsonZero(JsonString *p){
|
|
+ p->zBuf = p->zSpace;
|
|
+ p->nAlloc = sizeof(p->zSpace);
|
|
+ p->nUsed = 0;
|
|
+ p->bStatic = 1;
|
|
+}
|
|
+
|
|
+/* Initialize the JsonString object
|
|
+*/
|
|
+static void jsonInit(JsonString *p, sqlite3_context *pCtx){
|
|
+ p->pCtx = pCtx;
|
|
+ p->bErr = 0;
|
|
+ jsonZero(p);
|
|
+}
|
|
+
|
|
+
|
|
+/* Free all allocated memory and reset the JsonString object back to its
|
|
+** initial state.
|
|
+*/
|
|
+static void jsonReset(JsonString *p){
|
|
+ if( !p->bStatic ) sqlite3_free(p->zBuf);
|
|
+ jsonZero(p);
|
|
+}
|
|
+
|
|
+
|
|
+/* Report an out-of-memory (OOM) condition
|
|
+*/
|
|
+static void jsonOom(JsonString *p){
|
|
+ p->bErr = 1;
|
|
+ sqlite3_result_error_nomem(p->pCtx);
|
|
+ jsonReset(p);
|
|
+}
|
|
+
|
|
+/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
|
|
+** Return zero on success. Return non-zero on an OOM error
|
|
+*/
|
|
+static int jsonGrow(JsonString *p, u32 N){
|
|
+ u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
|
|
+ char *zNew;
|
|
+ if( p->bStatic ){
|
|
+ if( p->bErr ) return 1;
|
|
+ zNew = sqlite3_malloc64(nTotal);
|
|
+ if( zNew==0 ){
|
|
+ jsonOom(p);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ memcpy(zNew, p->zBuf, (size_t)p->nUsed);
|
|
+ p->zBuf = zNew;
|
|
+ p->bStatic = 0;
|
|
+ }else{
|
|
+ zNew = sqlite3_realloc64(p->zBuf, nTotal);
|
|
+ if( zNew==0 ){
|
|
+ jsonOom(p);
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ p->zBuf = zNew;
|
|
+ }
|
|
+ p->nAlloc = nTotal;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Append N bytes from zIn onto the end of the JsonString string.
|
|
+*/
|
|
+static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
|
|
+ if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
|
|
+ memcpy(p->zBuf+p->nUsed, zIn, N);
|
|
+ p->nUsed += N;
|
|
+}
|
|
+
|
|
+/* Append formatted text (not to exceed N bytes) to the JsonString.
|
|
+*/
|
|
+static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
|
|
+ va_list ap;
|
|
+ if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
|
|
+ va_start(ap, zFormat);
|
|
+ sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
|
|
+ va_end(ap);
|
|
+ p->nUsed += (int)strlen(p->zBuf+p->nUsed);
|
|
+}
|
|
+
|
|
+/* Append a single character
|
|
+*/
|
|
+static void jsonAppendChar(JsonString *p, char c){
|
|
+ if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = c;
|
|
+}
|
|
+
|
|
+/* Append a comma separator to the output buffer, if the previous
|
|
+** character is not '[' or '{'.
|
|
+*/
|
|
+static void jsonAppendSeparator(JsonString *p){
|
|
+ char c;
|
|
+ if( p->nUsed==0 ) return;
|
|
+ c = p->zBuf[p->nUsed-1];
|
|
+ if( c!='[' && c!='{' ) jsonAppendChar(p, ',');
|
|
+}
|
|
+
|
|
+/* Append the N-byte string in zIn to the end of the JsonString string
|
|
+** under construction. Enclose the string in "..." and escape
|
|
+** any double-quotes or backslash characters contained within the
|
|
+** string.
|
|
+*/
|
|
+static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
|
|
+ u32 i;
|
|
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = '"';
|
|
+ for(i=0; i<N; i++){
|
|
+ unsigned char c = ((unsigned const char*)zIn)[i];
|
|
+ if( c=='"' || c=='\\' ){
|
|
+ json_simple_escape:
|
|
+ if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = '\\';
|
|
+ }else if( c<=0x1f ){
|
|
+ static const char aSpecial[] = {
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
+ };
|
|
+ assert( sizeof(aSpecial)==32 );
|
|
+ assert( aSpecial['\b']=='b' );
|
|
+ assert( aSpecial['\f']=='f' );
|
|
+ assert( aSpecial['\n']=='n' );
|
|
+ assert( aSpecial['\r']=='r' );
|
|
+ assert( aSpecial['\t']=='t' );
|
|
+ if( aSpecial[c] ){
|
|
+ c = aSpecial[c];
|
|
+ goto json_simple_escape;
|
|
+ }
|
|
+ if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
|
|
+ p->zBuf[p->nUsed++] = '\\';
|
|
+ p->zBuf[p->nUsed++] = 'u';
|
|
+ p->zBuf[p->nUsed++] = '0';
|
|
+ p->zBuf[p->nUsed++] = '0';
|
|
+ p->zBuf[p->nUsed++] = '0' + (c>>4);
|
|
+ c = "0123456789abcdef"[c&0xf];
|
|
+ }
|
|
+ p->zBuf[p->nUsed++] = c;
|
|
+ }
|
|
+ p->zBuf[p->nUsed++] = '"';
|
|
+ assert( p->nUsed<p->nAlloc );
|
|
+}
|
|
+
|
|
+/*
|
|
+** Append a function parameter value to the JSON string under
|
|
+** construction.
|
|
+*/
|
|
+static void jsonAppendValue(
|
|
+ JsonString *p, /* Append to this JSON string */
|
|
+ sqlite3_value *pValue /* Value to append */
|
|
+){
|
|
+ switch( sqlite3_value_type(pValue) ){
|
|
+ case SQLITE_NULL: {
|
|
+ jsonAppendRaw(p, "null", 4);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_INTEGER:
|
|
+ case SQLITE_FLOAT: {
|
|
+ const char *z = (const char*)sqlite3_value_text(pValue);
|
|
+ u32 n = (u32)sqlite3_value_bytes(pValue);
|
|
+ jsonAppendRaw(p, z, n);
|
|
+ break;
|
|
+ }
|
|
+ case SQLITE_TEXT: {
|
|
+ const char *z = (const char*)sqlite3_value_text(pValue);
|
|
+ u32 n = (u32)sqlite3_value_bytes(pValue);
|
|
+ if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
|
|
+ jsonAppendRaw(p, z, n);
|
|
+ }else{
|
|
+ jsonAppendString(p, z, n);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ if( p->bErr==0 ){
|
|
+ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
|
|
+ p->bErr = 2;
|
|
+ jsonReset(p);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/* Make the JSON in p the result of the SQL function.
|
|
+*/
|
|
+static void jsonResult(JsonString *p){
|
|
+ if( p->bErr==0 ){
|
|
+ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
|
|
+ p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
|
|
+ SQLITE_UTF8);
|
|
+ jsonZero(p);
|
|
+ }
|
|
+ assert( p->bStatic );
|
|
+}
|
|
+
|
|
+/**************************************************************************
|
|
+** Utility routines for dealing with JsonNode and JsonParse objects
|
|
+**************************************************************************/
|
|
+
|
|
+/*
|
|
+** Return the number of consecutive JsonNode slots need to represent
|
|
+** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
|
|
+** OBJECT types, the number might be larger.
|
|
+**
|
|
+** Appended elements are not counted. The value returned is the number
|
|
+** by which the JsonNode counter should increment in order to go to the
|
|
+** next peer value.
|
|
+*/
|
|
+static u32 jsonNodeSize(JsonNode *pNode){
|
|
+ return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Reclaim all memory allocated by a JsonParse object. But do not
|
|
+** delete the JsonParse object itself.
|
|
+*/
|
|
+static void jsonParseReset(JsonParse *pParse){
|
|
+ sqlite3_free(pParse->aNode);
|
|
+ pParse->aNode = 0;
|
|
+ pParse->nNode = 0;
|
|
+ pParse->nAlloc = 0;
|
|
+ sqlite3_free(pParse->aUp);
|
|
+ pParse->aUp = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Free a JsonParse object that was obtained from sqlite3_malloc().
|
|
+*/
|
|
+static void jsonParseFree(JsonParse *pParse){
|
|
+ jsonParseReset(pParse);
|
|
+ sqlite3_free(pParse);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Convert the JsonNode pNode into a pure JSON string and
|
|
+** append to pOut. Subsubstructure is also included. Return
|
|
+** the number of JsonNode objects that are encoded.
|
|
+*/
|
|
+static void jsonRenderNode(
|
|
+ JsonNode *pNode, /* The node to render */
|
|
+ JsonString *pOut, /* Write JSON here */
|
|
+ sqlite3_value **aReplace /* Replacement values */
|
|
+){
|
|
+ if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
|
|
+ if( pNode->jnFlags & JNODE_REPLACE ){
|
|
+ jsonAppendValue(pOut, aReplace[pNode->u.iReplace]);
|
|
+ return;
|
|
+ }
|
|
+ pNode = pNode->u.pPatch;
|
|
+ }
|
|
+ switch( pNode->eType ){
|
|
+ default: {
|
|
+ assert( pNode->eType==JSON_NULL );
|
|
+ jsonAppendRaw(pOut, "null", 4);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_TRUE: {
|
|
+ jsonAppendRaw(pOut, "true", 4);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_FALSE: {
|
|
+ jsonAppendRaw(pOut, "false", 5);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_STRING: {
|
|
+ if( pNode->jnFlags & JNODE_RAW ){
|
|
+ jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
|
|
+ break;
|
|
+ }
|
|
+ /* Fall through into the next case */
|
|
+ }
|
|
+ case JSON_REAL:
|
|
+ case JSON_INT: {
|
|
+ jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_ARRAY: {
|
|
+ u32 j = 1;
|
|
+ jsonAppendChar(pOut, '[');
|
|
+ for(;;){
|
|
+ while( j<=pNode->n ){
|
|
+ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){
|
|
+ jsonAppendSeparator(pOut);
|
|
+ jsonRenderNode(&pNode[j], pOut, aReplace);
|
|
+ }
|
|
+ j += jsonNodeSize(&pNode[j]);
|
|
+ }
|
|
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ pNode = &pNode[pNode->u.iAppend];
|
|
+ j = 1;
|
|
+ }
|
|
+ jsonAppendChar(pOut, ']');
|
|
+ break;
|
|
+ }
|
|
+ case JSON_OBJECT: {
|
|
+ u32 j = 1;
|
|
+ jsonAppendChar(pOut, '{');
|
|
+ for(;;){
|
|
+ while( j<=pNode->n ){
|
|
+ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
|
|
+ jsonAppendSeparator(pOut);
|
|
+ jsonRenderNode(&pNode[j], pOut, aReplace);
|
|
+ jsonAppendChar(pOut, ':');
|
|
+ jsonRenderNode(&pNode[j+1], pOut, aReplace);
|
|
+ }
|
|
+ j += 1 + jsonNodeSize(&pNode[j+1]);
|
|
+ }
|
|
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ pNode = &pNode[pNode->u.iAppend];
|
|
+ j = 1;
|
|
+ }
|
|
+ jsonAppendChar(pOut, '}');
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return a JsonNode and all its descendents as a JSON string.
|
|
+*/
|
|
+static void jsonReturnJson(
|
|
+ JsonNode *pNode, /* Node to return */
|
|
+ sqlite3_context *pCtx, /* Return value for this function */
|
|
+ sqlite3_value **aReplace /* Array of replacement values */
|
|
+){
|
|
+ JsonString s;
|
|
+ jsonInit(&s, pCtx);
|
|
+ jsonRenderNode(pNode, &s, aReplace);
|
|
+ jsonResult(&s);
|
|
+ sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Make the JsonNode the return value of the function.
|
|
+*/
|
|
+static void jsonReturn(
|
|
+ JsonNode *pNode, /* Node to return */
|
|
+ sqlite3_context *pCtx, /* Return value for this function */
|
|
+ sqlite3_value **aReplace /* Array of replacement values */
|
|
+){
|
|
+ switch( pNode->eType ){
|
|
+ default: {
|
|
+ assert( pNode->eType==JSON_NULL );
|
|
+ sqlite3_result_null(pCtx);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_TRUE: {
|
|
+ sqlite3_result_int(pCtx, 1);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_FALSE: {
|
|
+ sqlite3_result_int(pCtx, 0);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_INT: {
|
|
+ sqlite3_int64 i = 0;
|
|
+ const char *z = pNode->u.zJContent;
|
|
+ if( z[0]=='-' ){ z++; }
|
|
+ while( z[0]>='0' && z[0]<='9' ){
|
|
+ unsigned v = *(z++) - '0';
|
|
+ if( i>=LARGEST_INT64/10 ){
|
|
+ if( i>LARGEST_INT64/10 ) goto int_as_real;
|
|
+ if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
|
|
+ if( v==9 ) goto int_as_real;
|
|
+ if( v==8 ){
|
|
+ if( pNode->u.zJContent[0]=='-' ){
|
|
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
|
|
+ goto int_done;
|
|
+ }else{
|
|
+ goto int_as_real;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ i = i*10 + v;
|
|
+ }
|
|
+ if( pNode->u.zJContent[0]=='-' ){ i = -i; }
|
|
+ sqlite3_result_int64(pCtx, i);
|
|
+ int_done:
|
|
+ break;
|
|
+ int_as_real: /* fall through to real */;
|
|
+ }
|
|
+ case JSON_REAL: {
|
|
+ double r;
|
|
+#ifdef SQLITE_AMALGAMATION
|
|
+ const char *z = pNode->u.zJContent;
|
|
+ sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
|
|
+#else
|
|
+ r = strtod(pNode->u.zJContent, 0);
|
|
+#endif
|
|
+ sqlite3_result_double(pCtx, r);
|
|
+ break;
|
|
+ }
|
|
+ case JSON_STRING: {
|
|
+#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
|
|
+ ** json_insert() and json_replace() and those routines do not
|
|
+ ** call jsonReturn() */
|
|
+ if( pNode->jnFlags & JNODE_RAW ){
|
|
+ sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
|
|
+ SQLITE_TRANSIENT);
|
|
+ }else
|
|
+#endif
|
|
+ assert( (pNode->jnFlags & JNODE_RAW)==0 );
|
|
+ if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
|
|
+ /* JSON formatted without any backslash-escapes */
|
|
+ sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
|
|
+ SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ /* Translate JSON formatted string into raw text */
|
|
+ u32 i;
|
|
+ u32 n = pNode->n;
|
|
+ const char *z = pNode->u.zJContent;
|
|
+ char *zOut;
|
|
+ u32 j;
|
|
+ zOut = sqlite3_malloc( n+1 );
|
|
+ if( zOut==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ break;
|
|
+ }
|
|
+ for(i=1, j=0; i<n-1; i++){
|
|
+ char c = z[i];
|
|
+ if( c!='\\' ){
|
|
+ zOut[j++] = c;
|
|
+ }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;
|
|
+ }
|
|
+ if( v==0 ) break;
|
|
+ if( v<=0x7f ){
|
|
+ zOut[j++] = (char)v;
|
|
+ }else if( v<=0x7ff ){
|
|
+ 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);
|
|
+ }
|
|
+ }else{
|
|
+ if( c=='b' ){
|
|
+ c = '\b';
|
|
+ }else if( c=='f' ){
|
|
+ c = '\f';
|
|
+ }else if( c=='n' ){
|
|
+ c = '\n';
|
|
+ }else if( c=='r' ){
|
|
+ c = '\r';
|
|
+ }else if( c=='t' ){
|
|
+ c = '\t';
|
|
+ }
|
|
+ zOut[j++] = c;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ zOut[j] = 0;
|
|
+ sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JSON_ARRAY:
|
|
+ case JSON_OBJECT: {
|
|
+ jsonReturnJson(pNode, pCtx, aReplace);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Forward reference */
|
|
+static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
|
|
+
|
|
+/*
|
|
+** A macro to hint to the compiler that a function should not be
|
|
+** inlined.
|
|
+*/
|
|
+#if defined(__GNUC__)
|
|
+# define JSON_NOINLINE __attribute__((noinline))
|
|
+#elif defined(_MSC_VER) && _MSC_VER>=1310
|
|
+# define JSON_NOINLINE __declspec(noinline)
|
|
+#else
|
|
+# define JSON_NOINLINE
|
|
+#endif
|
|
+
|
|
+
|
|
+static JSON_NOINLINE int jsonParseAddNodeExpand(
|
|
+ JsonParse *pParse, /* Append the node to this object */
|
|
+ u32 eType, /* Node type */
|
|
+ u32 n, /* Content size or sub-node count */
|
|
+ const char *zContent /* Content */
|
|
+){
|
|
+ u32 nNew;
|
|
+ JsonNode *pNew;
|
|
+ assert( pParse->nNode>=pParse->nAlloc );
|
|
+ if( pParse->oom ) return -1;
|
|
+ nNew = pParse->nAlloc*2 + 10;
|
|
+ pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
|
|
+ if( pNew==0 ){
|
|
+ pParse->oom = 1;
|
|
+ return -1;
|
|
+ }
|
|
+ pParse->nAlloc = nNew;
|
|
+ pParse->aNode = pNew;
|
|
+ assert( pParse->nNode<pParse->nAlloc );
|
|
+ return jsonParseAddNode(pParse, eType, n, zContent);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Create a new JsonNode instance based on the arguments and append that
|
|
+** instance to the JsonParse. Return the index in pParse->aNode[] of the
|
|
+** new node, or -1 if a memory allocation fails.
|
|
+*/
|
|
+static int jsonParseAddNode(
|
|
+ JsonParse *pParse, /* Append the node to this object */
|
|
+ u32 eType, /* Node type */
|
|
+ u32 n, /* Content size or sub-node count */
|
|
+ const char *zContent /* Content */
|
|
+){
|
|
+ JsonNode *p;
|
|
+ if( pParse->nNode>=pParse->nAlloc ){
|
|
+ return jsonParseAddNodeExpand(pParse, eType, n, zContent);
|
|
+ }
|
|
+ p = &pParse->aNode[pParse->nNode];
|
|
+ p->eType = (u8)eType;
|
|
+ p->jnFlags = 0;
|
|
+ p->n = n;
|
|
+ p->u.zJContent = zContent;
|
|
+ return pParse->nNode++;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return true if z[] begins with 4 (or more) hexadecimal digits
|
|
+*/
|
|
+static int jsonIs4Hex(const char *z){
|
|
+ int i;
|
|
+ for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parse a single JSON value which begins at pParse->zJson[i]. Return the
|
|
+** index of the first character past the end of the value parsed.
|
|
+**
|
|
+** Return negative for a syntax error. Special cases: return -2 if the
|
|
+** first non-whitespace character is '}' and return -3 if the first
|
|
+** non-whitespace character is ']'.
|
|
+*/
|
|
+static int jsonParseValue(JsonParse *pParse, u32 i){
|
|
+ char c;
|
|
+ u32 j;
|
|
+ int iThis;
|
|
+ int x;
|
|
+ JsonNode *pNode;
|
|
+ const char *z = pParse->zJson;
|
|
+ while( safe_isspace(z[i]) ){ i++; }
|
|
+ if( (c = z[i])=='{' ){
|
|
+ /* Parse object */
|
|
+ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
|
+ if( iThis<0 ) return -1;
|
|
+ for(j=i+1;;j++){
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
|
|
+ x = jsonParseValue(pParse, j);
|
|
+ if( x<0 ){
|
|
+ pParse->iDepth--;
|
|
+ if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
|
+ return -1;
|
|
+ }
|
|
+ if( pParse->oom ) return -1;
|
|
+ pNode = &pParse->aNode[pParse->nNode-1];
|
|
+ if( pNode->eType!=JSON_STRING ) return -1;
|
|
+ pNode->jnFlags |= JNODE_LABEL;
|
|
+ j = x;
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ if( z[j]!=':' ) return -1;
|
|
+ j++;
|
|
+ x = jsonParseValue(pParse, j);
|
|
+ pParse->iDepth--;
|
|
+ if( x<0 ) return -1;
|
|
+ j = x;
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ c = z[j];
|
|
+ if( c==',' ) continue;
|
|
+ if( c!='}' ) return -1;
|
|
+ break;
|
|
+ }
|
|
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
|
|
+ return j+1;
|
|
+ }else if( c=='[' ){
|
|
+ /* Parse array */
|
|
+ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
|
+ if( iThis<0 ) return -1;
|
|
+ for(j=i+1;;j++){
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
|
|
+ x = jsonParseValue(pParse, j);
|
|
+ pParse->iDepth--;
|
|
+ if( x<0 ){
|
|
+ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
|
+ return -1;
|
|
+ }
|
|
+ j = x;
|
|
+ while( safe_isspace(z[j]) ){ j++; }
|
|
+ c = z[j];
|
|
+ if( c==',' ) continue;
|
|
+ if( c!=']' ) return -1;
|
|
+ break;
|
|
+ }
|
|
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
|
|
+ return j+1;
|
|
+ }else if( c=='"' ){
|
|
+ /* Parse string */
|
|
+ u8 jnFlags = 0;
|
|
+ j = i+1;
|
|
+ for(;;){
|
|
+ c = z[j];
|
|
+ if( (c & ~0x1f)==0 ){
|
|
+ /* Control characters are not allowed in strings */
|
|
+ return -1;
|
|
+ }
|
|
+ if( c=='\\' ){
|
|
+ c = z[++j];
|
|
+ if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|
|
+ || c=='n' || c=='r' || c=='t'
|
|
+ || (c=='u' && jsonIs4Hex(z+j+1)) ){
|
|
+ jnFlags = JNODE_ESCAPE;
|
|
+ }else{
|
|
+ return -1;
|
|
+ }
|
|
+ }else if( c=='"' ){
|
|
+ break;
|
|
+ }
|
|
+ j++;
|
|
+ }
|
|
+ jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]);
|
|
+ if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
|
|
+ return j+1;
|
|
+ }else if( c=='n'
|
|
+ && strncmp(z+i,"null",4)==0
|
|
+ && !safe_isalnum(z[i+4]) ){
|
|
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
|
+ return i+4;
|
|
+ }else if( c=='t'
|
|
+ && strncmp(z+i,"true",4)==0
|
|
+ && !safe_isalnum(z[i+4]) ){
|
|
+ jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
|
+ return i+4;
|
|
+ }else if( c=='f'
|
|
+ && strncmp(z+i,"false",5)==0
|
|
+ && !safe_isalnum(z[i+5]) ){
|
|
+ jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
|
|
+ return i+5;
|
|
+ }else if( c=='-' || (c>='0' && c<='9') ){
|
|
+ /* Parse number */
|
|
+ u8 seenDP = 0;
|
|
+ u8 seenE = 0;
|
|
+ assert( '-' < '0' );
|
|
+ if( c<='0' ){
|
|
+ j = c=='-' ? i+1 : i;
|
|
+ if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1;
|
|
+ }
|
|
+ j = i+1;
|
|
+ for(;; j++){
|
|
+ c = z[j];
|
|
+ if( c>='0' && c<='9' ) continue;
|
|
+ if( c=='.' ){
|
|
+ if( z[j-1]=='-' ) return -1;
|
|
+ if( seenDP ) return -1;
|
|
+ seenDP = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if( c=='e' || c=='E' ){
|
|
+ if( z[j-1]<'0' ) return -1;
|
|
+ if( seenE ) return -1;
|
|
+ seenDP = seenE = 1;
|
|
+ c = z[j+1];
|
|
+ if( c=='+' || c=='-' ){
|
|
+ j++;
|
|
+ c = z[j+1];
|
|
+ }
|
|
+ if( c<'0' || c>'9' ) return -1;
|
|
+ continue;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if( z[j-1]<'0' ) return -1;
|
|
+ jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
|
|
+ j - i, &z[i]);
|
|
+ return j;
|
|
+ }else if( c=='}' ){
|
|
+ return -2; /* End of {...} */
|
|
+ }else if( c==']' ){
|
|
+ return -3; /* End of [...] */
|
|
+ }else if( c==0 ){
|
|
+ return 0; /* End of file */
|
|
+ }else{
|
|
+ return -1; /* Syntax error */
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Parse a complete JSON string. Return 0 on success or non-zero if there
|
|
+** are any errors. If an error occurs, free all memory associated with
|
|
+** pParse.
|
|
+**
|
|
+** pParse is uninitialized when this routine is called.
|
|
+*/
|
|
+static int jsonParse(
|
|
+ JsonParse *pParse, /* Initialize and fill this JsonParse object */
|
|
+ sqlite3_context *pCtx, /* Report errors here */
|
|
+ const char *zJson /* Input JSON text to be parsed */
|
|
+){
|
|
+ int i;
|
|
+ memset(pParse, 0, sizeof(*pParse));
|
|
+ if( zJson==0 ) return 1;
|
|
+ pParse->zJson = zJson;
|
|
+ i = jsonParseValue(pParse, 0);
|
|
+ if( pParse->oom ) i = -1;
|
|
+ if( i>0 ){
|
|
+ assert( pParse->iDepth==0 );
|
|
+ while( safe_isspace(zJson[i]) ) i++;
|
|
+ if( zJson[i] ) i = -1;
|
|
+ }
|
|
+ if( i<=0 ){
|
|
+ if( pCtx!=0 ){
|
|
+ if( pParse->oom ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
|
|
+ }
|
|
+ }
|
|
+ jsonParseReset(pParse);
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Mark node i of pParse as being a child of iParent. Call recursively
|
|
+** to fill in all the descendants of node i.
|
|
+*/
|
|
+static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
|
|
+ JsonNode *pNode = &pParse->aNode[i];
|
|
+ u32 j;
|
|
+ pParse->aUp[i] = iParent;
|
|
+ switch( pNode->eType ){
|
|
+ case JSON_ARRAY: {
|
|
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
|
|
+ jsonParseFillInParentage(pParse, i+j, i);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JSON_OBJECT: {
|
|
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
|
|
+ pParse->aUp[i+j] = i;
|
|
+ jsonParseFillInParentage(pParse, i+j+1, i);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute the parentage of all nodes in a completed parse.
|
|
+*/
|
|
+static int jsonParseFindParents(JsonParse *pParse){
|
|
+ u32 *aUp;
|
|
+ assert( pParse->aUp==0 );
|
|
+ aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
|
|
+ if( aUp==0 ){
|
|
+ pParse->oom = 1;
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ jsonParseFillInParentage(pParse, 0, 0);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
|
|
+*/
|
|
+#define JSON_CACHE_ID (-429938) /* First cache entry */
|
|
+#define JSON_CACHE_SZ 4 /* Max number of cache entries */
|
|
+
|
|
+/*
|
|
+** Obtain a complete parse of the JSON found in the first argument
|
|
+** of the argv array. Use the sqlite3_get_auxdata() cache for this
|
|
+** parse if it is available. If the cache is not available or if it
|
|
+** is no longer valid, parse the JSON again and return the new parse,
|
|
+** and also register the new parse so that it will be available for
|
|
+** future sqlite3_get_auxdata() calls.
|
|
+*/
|
|
+static JsonParse *jsonParseCached(
|
|
+ sqlite3_context *pCtx,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite3_context *pErrCtx
|
|
+){
|
|
+ const char *zJson = (const char*)sqlite3_value_text(argv[0]);
|
|
+ int nJson = sqlite3_value_bytes(argv[0]);
|
|
+ JsonParse *p;
|
|
+ JsonParse *pMatch = 0;
|
|
+ int iKey;
|
|
+ int iMinKey = 0;
|
|
+ u32 iMinHold = 0xffffffff;
|
|
+ u32 iMaxHold = 0;
|
|
+ if( zJson==0 ) return 0;
|
|
+ for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
|
|
+ p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
|
|
+ if( p==0 ){
|
|
+ iMinKey = iKey;
|
|
+ break;
|
|
+ }
|
|
+ if( pMatch==0
|
|
+ && p->nJson==nJson
|
|
+ && memcmp(p->zJson,zJson,nJson)==0
|
|
+ ){
|
|
+ p->nErr = 0;
|
|
+ pMatch = p;
|
|
+ }else if( p->iHold<iMinHold ){
|
|
+ iMinHold = p->iHold;
|
|
+ iMinKey = iKey;
|
|
+ }
|
|
+ if( p->iHold>iMaxHold ){
|
|
+ iMaxHold = p->iHold;
|
|
+ }
|
|
+ }
|
|
+ if( pMatch ){
|
|
+ pMatch->nErr = 0;
|
|
+ pMatch->iHold = iMaxHold+1;
|
|
+ return pMatch;
|
|
+ }
|
|
+ p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
|
|
+ if( p==0 ){
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ return 0;
|
|
+ }
|
|
+ memset(p, 0, sizeof(*p));
|
|
+ p->zJson = (char*)&p[1];
|
|
+ memcpy((char*)p->zJson, zJson, nJson+1);
|
|
+ if( jsonParse(p, pErrCtx, p->zJson) ){
|
|
+ sqlite3_free(p);
|
|
+ return 0;
|
|
+ }
|
|
+ p->nJson = nJson;
|
|
+ p->iHold = iMaxHold+1;
|
|
+ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
|
|
+ (void(*)(void*))jsonParseFree);
|
|
+ return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compare the OBJECT label at pNode against zKey,nKey. Return true on
|
|
+** a match.
|
|
+*/
|
|
+static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
|
|
+ if( pNode->jnFlags & JNODE_RAW ){
|
|
+ if( pNode->n!=nKey ) return 0;
|
|
+ return strncmp(pNode->u.zJContent, zKey, nKey)==0;
|
|
+ }else{
|
|
+ if( pNode->n!=nKey+2 ) return 0;
|
|
+ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* forward declaration */
|
|
+static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
|
|
+
|
|
+/*
|
|
+** Search along zPath to find the node specified. Return a pointer
|
|
+** to that node, or NULL if zPath is malformed or if there is no such
|
|
+** node.
|
|
+**
|
|
+** If pApnd!=0, then try to append new nodes to complete zPath if it is
|
|
+** possible to do so and if no existing node corresponds to zPath. If
|
|
+** new nodes are appended *pApnd is set to 1.
|
|
+*/
|
|
+static JsonNode *jsonLookupStep(
|
|
+ JsonParse *pParse, /* The JSON to search */
|
|
+ u32 iRoot, /* Begin the search at this node */
|
|
+ const char *zPath, /* The path to search */
|
|
+ int *pApnd, /* Append nodes to complete path if not NULL */
|
|
+ const char **pzErr /* Make *pzErr point to any syntax error in zPath */
|
|
+){
|
|
+ u32 i, j, nKey;
|
|
+ const char *zKey;
|
|
+ JsonNode *pRoot = &pParse->aNode[iRoot];
|
|
+ if( zPath[0]==0 ) return pRoot;
|
|
+ if( zPath[0]=='.' ){
|
|
+ if( pRoot->eType!=JSON_OBJECT ) return 0;
|
|
+ zPath++;
|
|
+ if( zPath[0]=='"' ){
|
|
+ zKey = zPath + 1;
|
|
+ for(i=1; zPath[i] && zPath[i]!='"'; i++){}
|
|
+ nKey = i-1;
|
|
+ if( zPath[i] ){
|
|
+ i++;
|
|
+ }else{
|
|
+ *pzErr = zPath;
|
|
+ return 0;
|
|
+ }
|
|
+ }else{
|
|
+ zKey = zPath;
|
|
+ for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
|
|
+ nKey = i;
|
|
+ }
|
|
+ if( nKey==0 ){
|
|
+ *pzErr = zPath;
|
|
+ return 0;
|
|
+ }
|
|
+ j = 1;
|
|
+ for(;;){
|
|
+ while( j<=pRoot->n ){
|
|
+ if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
|
|
+ return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
|
|
+ }
|
|
+ j++;
|
|
+ j += jsonNodeSize(&pRoot[j]);
|
|
+ }
|
|
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ iRoot += pRoot->u.iAppend;
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ j = 1;
|
|
+ }
|
|
+ if( pApnd ){
|
|
+ u32 iStart, iLabel;
|
|
+ JsonNode *pNode;
|
|
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
|
+ iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
|
|
+ zPath += i;
|
|
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
|
+ if( pParse->oom ) return 0;
|
|
+ if( pNode ){
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ pRoot->u.iAppend = iStart - iRoot;
|
|
+ pRoot->jnFlags |= JNODE_APPEND;
|
|
+ pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
|
|
+ }
|
|
+ return pNode;
|
|
+ }
|
|
+ }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
|
|
+ if( pRoot->eType!=JSON_ARRAY ) return 0;
|
|
+ i = 0;
|
|
+ j = 1;
|
|
+ while( safe_isdigit(zPath[j]) ){
|
|
+ i = i*10 + zPath[j] - '0';
|
|
+ j++;
|
|
+ }
|
|
+ if( zPath[j]!=']' ){
|
|
+ *pzErr = zPath;
|
|
+ return 0;
|
|
+ }
|
|
+ zPath += j + 1;
|
|
+ j = 1;
|
|
+ for(;;){
|
|
+ while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
|
|
+ if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
|
|
+ j += jsonNodeSize(&pRoot[j]);
|
|
+ }
|
|
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
|
|
+ iRoot += pRoot->u.iAppend;
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ j = 1;
|
|
+ }
|
|
+ if( j<=pRoot->n ){
|
|
+ return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
|
|
+ }
|
|
+ if( i==0 && pApnd ){
|
|
+ u32 iStart;
|
|
+ JsonNode *pNode;
|
|
+ iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
|
|
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
|
+ if( pParse->oom ) return 0;
|
|
+ if( pNode ){
|
|
+ pRoot = &pParse->aNode[iRoot];
|
|
+ pRoot->u.iAppend = iStart - iRoot;
|
|
+ pRoot->jnFlags |= JNODE_APPEND;
|
|
+ }
|
|
+ return pNode;
|
|
+ }
|
|
+ }else{
|
|
+ *pzErr = zPath;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Append content to pParse that will complete zPath. Return a pointer
|
|
+** to the inserted node, or return NULL if the append fails.
|
|
+*/
|
|
+static JsonNode *jsonLookupAppend(
|
|
+ JsonParse *pParse, /* Append content to the JSON parse */
|
|
+ const char *zPath, /* Description of content to append */
|
|
+ int *pApnd, /* Set this flag to 1 */
|
|
+ const char **pzErr /* Make this point to any syntax error */
|
|
+){
|
|
+ *pApnd = 1;
|
|
+ if( zPath[0]==0 ){
|
|
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
|
+ return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
|
|
+ }
|
|
+ if( zPath[0]=='.' ){
|
|
+ jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
|
+ }else if( strncmp(zPath,"[0]",3)==0 ){
|
|
+ jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
|
+ }else{
|
|
+ return 0;
|
|
+ }
|
|
+ if( pParse->oom ) return 0;
|
|
+ return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Return the text of a syntax error message on a JSON path. Space is
|
|
+** obtained from sqlite3_malloc().
|
|
+*/
|
|
+static char *jsonPathSyntaxError(const char *zErr){
|
|
+ return sqlite3_mprintf("JSON path error near '%q'", zErr);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Do a node lookup using zPath. Return a pointer to the node on success.
|
|
+** Return NULL if not found or if there is an error.
|
|
+**
|
|
+** On an error, write an error message into pCtx and increment the
|
|
+** pParse->nErr counter.
|
|
+**
|
|
+** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
|
|
+** nodes are appended.
|
|
+*/
|
|
+static JsonNode *jsonLookup(
|
|
+ JsonParse *pParse, /* The JSON to search */
|
|
+ const char *zPath, /* The path to search */
|
|
+ int *pApnd, /* Append nodes to complete path if not NULL */
|
|
+ sqlite3_context *pCtx /* Report errors here, if not NULL */
|
|
+){
|
|
+ const char *zErr = 0;
|
|
+ JsonNode *pNode = 0;
|
|
+ char *zMsg;
|
|
+
|
|
+ if( zPath==0 ) return 0;
|
|
+ if( zPath[0]!='$' ){
|
|
+ zErr = zPath;
|
|
+ goto lookup_err;
|
|
+ }
|
|
+ zPath++;
|
|
+ pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
|
|
+ if( zErr==0 ) return pNode;
|
|
+
|
|
+lookup_err:
|
|
+ pParse->nErr++;
|
|
+ assert( zErr!=0 && pCtx!=0 );
|
|
+ zMsg = jsonPathSyntaxError(zErr);
|
|
+ if( zMsg ){
|
|
+ sqlite3_result_error(pCtx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+ }else{
|
|
+ sqlite3_result_error_nomem(pCtx);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Report the wrong number of arguments for json_insert(), json_replace()
|
|
+** or json_set().
|
|
+*/
|
|
+static void jsonWrongNumArgs(
|
|
+ sqlite3_context *pCtx,
|
|
+ const char *zFuncName
|
|
+){
|
|
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
|
|
+ zFuncName);
|
|
+ sqlite3_result_error(pCtx, zMsg, -1);
|
|
+ sqlite3_free(zMsg);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
|
|
+*/
|
|
+static void jsonRemoveAllNulls(JsonNode *pNode){
|
|
+ int i, n;
|
|
+ assert( pNode->eType==JSON_OBJECT );
|
|
+ n = pNode->n;
|
|
+ for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
|
|
+ switch( pNode[i].eType ){
|
|
+ case JSON_NULL:
|
|
+ pNode[i].jnFlags |= JNODE_REMOVE;
|
|
+ break;
|
|
+ case JSON_OBJECT:
|
|
+ jsonRemoveAllNulls(&pNode[i]);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/****************************************************************************
|
|
+** SQL functions used for testing and debugging
|
|
+****************************************************************************/
|
|
+
|
|
+#ifdef SQLITE_DEBUG
|
|
+/*
|
|
+** The json_parse(JSON) function returns a string which describes
|
|
+** a parse of the JSON provided. Or it returns NULL if JSON is not
|
|
+** well-formed.
|
|
+*/
|
|
+static void jsonParseFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString s; /* Output string - not real JSON */
|
|
+ JsonParse x; /* The parse */
|
|
+ u32 i;
|
|
+
|
|
+ assert( argc==1 );
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ jsonParseFindParents(&x);
|
|
+ jsonInit(&s, ctx);
|
|
+ for(i=0; i<x.nNode; i++){
|
|
+ const char *zType;
|
|
+ if( x.aNode[i].jnFlags & JNODE_LABEL ){
|
|
+ assert( x.aNode[i].eType==JSON_STRING );
|
|
+ zType = "label";
|
|
+ }else{
|
|
+ zType = jsonType[x.aNode[i].eType];
|
|
+ }
|
|
+ jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
|
|
+ i, zType, x.aNode[i].n, x.aUp[i]);
|
|
+ if( x.aNode[i].u.zJContent!=0 ){
|
|
+ jsonAppendRaw(&s, " ", 1);
|
|
+ jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
|
|
+ }
|
|
+ jsonAppendRaw(&s, "\n", 1);
|
|
+ }
|
|
+ jsonParseReset(&x);
|
|
+ jsonResult(&s);
|
|
+}
|
|
+
|
|
+/*
|
|
+** The json_test1(JSON) function return true (1) if the input is JSON
|
|
+** text generated by another json function. It returns (0) if the input
|
|
+** is not known to be JSON.
|
|
+*/
|
|
+static void jsonTest1Func(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ UNUSED_PARAM(argc);
|
|
+ sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
|
|
+}
|
|
+#endif /* SQLITE_DEBUG */
|
|
+
|
|
+/****************************************************************************
|
|
+** Scalar SQL function implementations
|
|
+****************************************************************************/
|
|
+
|
|
+/*
|
|
+** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
|
|
+** corresponding to the SQL value input. Mostly this means putting
|
|
+** double-quotes around strings and returning the unquoted string "null"
|
|
+** when given a NULL input.
|
|
+*/
|
|
+static void jsonQuoteFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString jx;
|
|
+ UNUSED_PARAM(argc);
|
|
+
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendValue(&jx, argv[0]);
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the json_array(VALUE,...) function. Return a JSON
|
|
+** array that contains all values given in arguments. Or if any argument
|
|
+** is a BLOB, throw an error.
|
|
+*/
|
|
+static void jsonArrayFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ JsonString jx;
|
|
+
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendChar(&jx, '[');
|
|
+ for(i=0; i<argc; i++){
|
|
+ jsonAppendSeparator(&jx);
|
|
+ jsonAppendValue(&jx, argv[i]);
|
|
+ }
|
|
+ jsonAppendChar(&jx, ']');
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** json_array_length(JSON)
|
|
+** json_array_length(JSON, PATH)
|
|
+**
|
|
+** Return the number of elements in the top-level JSON array.
|
|
+** Return 0 if the input is not a well-formed JSON array.
|
|
+*/
|
|
+static void jsonArrayLengthFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ sqlite3_int64 n = 0;
|
|
+ u32 i;
|
|
+ JsonNode *pNode;
|
|
+
|
|
+ p = jsonParseCached(ctx, argv, ctx);
|
|
+ if( p==0 ) return;
|
|
+ assert( p->nNode );
|
|
+ if( argc==2 ){
|
|
+ const char *zPath = (const char*)sqlite3_value_text(argv[1]);
|
|
+ pNode = jsonLookup(p, zPath, 0, ctx);
|
|
+ }else{
|
|
+ pNode = p->aNode;
|
|
+ }
|
|
+ if( pNode==0 ){
|
|
+ return;
|
|
+ }
|
|
+ if( pNode->eType==JSON_ARRAY ){
|
|
+ assert( (pNode->jnFlags & JNODE_APPEND)==0 );
|
|
+ for(i=1; i<=pNode->n; n++){
|
|
+ i += jsonNodeSize(&pNode[i]);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_int64(ctx, n);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_extract(JSON, PATH, ...)
|
|
+**
|
|
+** Return the element described by PATH. Return NULL if there is no
|
|
+** PATH element. If there are multiple PATHs, then return a JSON array
|
|
+** with the result from each path. Throw an error if the JSON or any PATH
|
|
+** is malformed.
|
|
+*/
|
|
+static void jsonExtractFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ JsonString jx;
|
|
+ int i;
|
|
+
|
|
+ if( argc<2 ) return;
|
|
+ p = jsonParseCached(ctx, argv, ctx);
|
|
+ if( p==0 ) return;
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendChar(&jx, '[');
|
|
+ for(i=1; i<argc; i++){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ pNode = jsonLookup(p, zPath, 0, ctx);
|
|
+ if( p->nErr ) break;
|
|
+ if( argc>2 ){
|
|
+ jsonAppendSeparator(&jx);
|
|
+ if( pNode ){
|
|
+ jsonRenderNode(pNode, &jx, 0);
|
|
+ }else{
|
|
+ jsonAppendRaw(&jx, "null", 4);
|
|
+ }
|
|
+ }else if( pNode ){
|
|
+ jsonReturn(pNode, ctx, 0);
|
|
+ }
|
|
+ }
|
|
+ if( argc>2 && i==argc ){
|
|
+ jsonAppendChar(&jx, ']');
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+ }
|
|
+ jsonReset(&jx);
|
|
+}
|
|
+
|
|
+/* This is the RFC 7396 MergePatch algorithm.
|
|
+*/
|
|
+static JsonNode *jsonMergePatch(
|
|
+ JsonParse *pParse, /* The JSON parser that contains the TARGET */
|
|
+ u32 iTarget, /* Node of the TARGET in pParse */
|
|
+ JsonNode *pPatch /* The PATCH */
|
|
+){
|
|
+ u32 i, j;
|
|
+ u32 iRoot;
|
|
+ JsonNode *pTarget;
|
|
+ if( pPatch->eType!=JSON_OBJECT ){
|
|
+ return pPatch;
|
|
+ }
|
|
+ assert( iTarget>=0 && iTarget<pParse->nNode );
|
|
+ pTarget = &pParse->aNode[iTarget];
|
|
+ assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
|
|
+ if( pTarget->eType!=JSON_OBJECT ){
|
|
+ jsonRemoveAllNulls(pPatch);
|
|
+ return pPatch;
|
|
+ }
|
|
+ iRoot = iTarget;
|
|
+ for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
|
|
+ u32 nKey;
|
|
+ const char *zKey;
|
|
+ assert( pPatch[i].eType==JSON_STRING );
|
|
+ assert( pPatch[i].jnFlags & JNODE_LABEL );
|
|
+ nKey = pPatch[i].n;
|
|
+ zKey = pPatch[i].u.zJContent;
|
|
+ assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
|
|
+ for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
|
|
+ assert( pTarget[j].eType==JSON_STRING );
|
|
+ assert( pTarget[j].jnFlags & JNODE_LABEL );
|
|
+ assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
|
|
+ if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){
|
|
+ if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break;
|
|
+ if( pPatch[i+1].eType==JSON_NULL ){
|
|
+ pTarget[j+1].jnFlags |= JNODE_REMOVE;
|
|
+ }else{
|
|
+ JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
|
|
+ if( pNew==0 ) return 0;
|
|
+ pTarget = &pParse->aNode[iTarget];
|
|
+ if( pNew!=&pTarget[j+1] ){
|
|
+ pTarget[j+1].u.pPatch = pNew;
|
|
+ pTarget[j+1].jnFlags |= JNODE_PATCH;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
|
|
+ int iStart, iPatch;
|
|
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
|
+ jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
|
|
+ iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
|
+ if( pParse->oom ) return 0;
|
|
+ jsonRemoveAllNulls(pPatch);
|
|
+ pTarget = &pParse->aNode[iTarget];
|
|
+ pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
|
|
+ pParse->aNode[iRoot].u.iAppend = iStart - iRoot;
|
|
+ iRoot = iStart;
|
|
+ pParse->aNode[iPatch].jnFlags |= JNODE_PATCH;
|
|
+ pParse->aNode[iPatch].u.pPatch = &pPatch[i+1];
|
|
+ }
|
|
+ }
|
|
+ return pTarget;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
|
|
+** object that is the result of running the RFC 7396 MergePatch() algorithm
|
|
+** on the two arguments.
|
|
+*/
|
|
+static void jsonPatchFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The JSON that is being patched */
|
|
+ JsonParse y; /* The patch */
|
|
+ JsonNode *pResult; /* The result of the merge */
|
|
+
|
|
+ UNUSED_PARAM(argc);
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){
|
|
+ jsonParseReset(&x);
|
|
+ return;
|
|
+ }
|
|
+ pResult = jsonMergePatch(&x, 0, y.aNode);
|
|
+ assert( pResult!=0 || x.oom );
|
|
+ if( pResult ){
|
|
+ jsonReturnJson(pResult, ctx, 0);
|
|
+ }else{
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ }
|
|
+ jsonParseReset(&x);
|
|
+ jsonParseReset(&y);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON
|
|
+** object that contains all name/value given in arguments. Or if any name
|
|
+** is not a string or if any value is a BLOB, throw an error.
|
|
+*/
|
|
+static void jsonObjectFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ JsonString jx;
|
|
+ const char *z;
|
|
+ u32 n;
|
|
+
|
|
+ if( argc&1 ){
|
|
+ sqlite3_result_error(ctx, "json_object() requires an even number "
|
|
+ "of arguments", -1);
|
|
+ return;
|
|
+ }
|
|
+ jsonInit(&jx, ctx);
|
|
+ jsonAppendChar(&jx, '{');
|
|
+ for(i=0; i<argc; i+=2){
|
|
+ if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
|
|
+ sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
|
|
+ jsonReset(&jx);
|
|
+ return;
|
|
+ }
|
|
+ jsonAppendSeparator(&jx);
|
|
+ z = (const char*)sqlite3_value_text(argv[i]);
|
|
+ n = (u32)sqlite3_value_bytes(argv[i]);
|
|
+ jsonAppendString(&jx, z, n);
|
|
+ jsonAppendChar(&jx, ':');
|
|
+ jsonAppendValue(&jx, argv[i+1]);
|
|
+ }
|
|
+ jsonAppendChar(&jx, '}');
|
|
+ jsonResult(&jx);
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** json_remove(JSON, PATH, ...)
|
|
+**
|
|
+** Remove the named elements from JSON and return the result. malformed
|
|
+** JSON or PATH arguments result in an error.
|
|
+*/
|
|
+static void jsonRemoveFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ u32 i;
|
|
+
|
|
+ if( argc<1 ) return;
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ assert( x.nNode );
|
|
+ for(i=1; i<(u32)argc; i++){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ if( zPath==0 ) goto remove_done;
|
|
+ pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
+ if( x.nErr ) goto remove_done;
|
|
+ if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
|
|
+ }
|
|
+ if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
|
+ jsonReturnJson(x.aNode, ctx, 0);
|
|
+ }
|
|
+remove_done:
|
|
+ jsonParseReset(&x);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_replace(JSON, PATH, VALUE, ...)
|
|
+**
|
|
+** Replace the value at PATH with VALUE. If PATH does not already exist,
|
|
+** this routine is a no-op. If JSON or PATH is malformed, throw an error.
|
|
+*/
|
|
+static void jsonReplaceFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ u32 i;
|
|
+
|
|
+ if( argc<1 ) return;
|
|
+ if( (argc&1)==0 ) {
|
|
+ jsonWrongNumArgs(ctx, "replace");
|
|
+ return;
|
|
+ }
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ assert( x.nNode );
|
|
+ for(i=1; i<(u32)argc; i+=2){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
+ if( x.nErr ) goto replace_err;
|
|
+ if( pNode ){
|
|
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
|
|
+ pNode->u.iReplace = i + 1;
|
|
+ }
|
|
+ }
|
|
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
|
+ sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
|
+ }else{
|
|
+ jsonReturnJson(x.aNode, ctx, argv);
|
|
+ }
|
|
+replace_err:
|
|
+ jsonParseReset(&x);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_set(JSON, PATH, VALUE, ...)
|
|
+**
|
|
+** Set the value at PATH to VALUE. Create the PATH if it does not already
|
|
+** exist. Overwrite existing values that do exist.
|
|
+** If JSON or PATH is malformed, throw an error.
|
|
+**
|
|
+** json_insert(JSON, PATH, VALUE, ...)
|
|
+**
|
|
+** Create PATH and initialize it to VALUE. If PATH already exists, this
|
|
+** routine is a no-op. If JSON or PATH is malformed, throw an error.
|
|
+*/
|
|
+static void jsonSetFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse x; /* The parse */
|
|
+ JsonNode *pNode;
|
|
+ const char *zPath;
|
|
+ u32 i;
|
|
+ int bApnd;
|
|
+ int bIsSet = *(int*)sqlite3_user_data(ctx);
|
|
+
|
|
+ if( argc<1 ) return;
|
|
+ if( (argc&1)==0 ) {
|
|
+ jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
|
|
+ return;
|
|
+ }
|
|
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
+ assert( x.nNode );
|
|
+ for(i=1; i<(u32)argc; i+=2){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
+ bApnd = 0;
|
|
+ pNode = jsonLookup(&x, zPath, &bApnd, ctx);
|
|
+ if( x.oom ){
|
|
+ sqlite3_result_error_nomem(ctx);
|
|
+ goto jsonSetDone;
|
|
+ }else if( x.nErr ){
|
|
+ goto jsonSetDone;
|
|
+ }else if( pNode && (bApnd || bIsSet) ){
|
|
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
|
|
+ pNode->u.iReplace = i + 1;
|
|
+ }
|
|
+ }
|
|
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
|
+ sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
|
+ }else{
|
|
+ jsonReturnJson(x.aNode, ctx, argv);
|
|
+ }
|
|
+jsonSetDone:
|
|
+ jsonParseReset(&x);
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_type(JSON)
|
|
+** json_type(JSON, PATH)
|
|
+**
|
|
+** Return the top-level "type" of a JSON string. Throw an error if
|
|
+** either the JSON or PATH inputs are not well-formed.
|
|
+*/
|
|
+static void jsonTypeFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ const char *zPath;
|
|
+ JsonNode *pNode;
|
|
+
|
|
+ p = jsonParseCached(ctx, argv, ctx);
|
|
+ if( p==0 ) return;
|
|
+ if( argc==2 ){
|
|
+ zPath = (const char*)sqlite3_value_text(argv[1]);
|
|
+ pNode = jsonLookup(p, zPath, 0, ctx);
|
|
+ }else{
|
|
+ pNode = p->aNode;
|
|
+ }
|
|
+ if( pNode ){
|
|
+ sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** json_valid(JSON)
|
|
+**
|
|
+** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
|
|
+** Return 0 otherwise.
|
|
+*/
|
|
+static void jsonValidFunc(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonParse *p; /* The parse */
|
|
+ UNUSED_PARAM(argc);
|
|
+ p = jsonParseCached(ctx, argv, 0);
|
|
+ sqlite3_result_int(ctx, p!=0);
|
|
+}
|
|
+
|
|
+
|
|
+/****************************************************************************
|
|
+** Aggregate SQL function implementations
|
|
+****************************************************************************/
|
|
+/*
|
|
+** json_group_array(VALUE)
|
|
+**
|
|
+** Return a JSON array composed of all values in the aggregate.
|
|
+*/
|
|
+static void jsonArrayStep(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString *pStr;
|
|
+ UNUSED_PARAM(argc);
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
|
|
+ if( pStr ){
|
|
+ if( pStr->zBuf==0 ){
|
|
+ jsonInit(pStr, ctx);
|
|
+ jsonAppendChar(pStr, '[');
|
|
+ }else{
|
|
+ jsonAppendChar(pStr, ',');
|
|
+ pStr->pCtx = ctx;
|
|
+ }
|
|
+ jsonAppendValue(pStr, argv[0]);
|
|
+ }
|
|
+}
|
|
+static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
|
|
+ JsonString *pStr;
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
+ if( pStr ){
|
|
+ pStr->pCtx = ctx;
|
|
+ jsonAppendChar(pStr, ']');
|
|
+ if( pStr->bErr ){
|
|
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
|
+ assert( pStr->bStatic );
|
|
+ }else if( isFinal ){
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
|
|
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
|
|
+ pStr->bStatic = 1;
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
|
|
+ pStr->nUsed--;
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
|
|
+ }
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+static void jsonArrayValue(sqlite3_context *ctx){
|
|
+ jsonArrayCompute(ctx, 0);
|
|
+}
|
|
+static void jsonArrayFinal(sqlite3_context *ctx){
|
|
+ jsonArrayCompute(ctx, 1);
|
|
+}
|
|
+
|
|
+#ifndef SQLITE_OMIT_WINDOWFUNC
|
|
+/*
|
|
+** This method works for both json_group_array() and json_group_object().
|
|
+** It works by removing the first element of the group by searching forward
|
|
+** to the first comma (",") that is not within a string and deleting all
|
|
+** text through that comma.
|
|
+*/
|
|
+static void jsonGroupInverse(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ int i;
|
|
+ int inStr = 0;
|
|
+ char *z;
|
|
+ JsonString *pStr;
|
|
+ UNUSED_PARAM(argc);
|
|
+ UNUSED_PARAM(argv);
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
+#ifdef NEVER
|
|
+ /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will
|
|
+ ** always have been called to initalize it */
|
|
+ if( NEVER(!pStr) ) return;
|
|
+#endif
|
|
+ z = pStr->zBuf;
|
|
+ for(i=1; z[i]!=',' || inStr; i++){
|
|
+ assert( i<pStr->nUsed );
|
|
+ if( z[i]=='"' ){
|
|
+ inStr = !inStr;
|
|
+ }else if( z[i]=='\\' ){
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ pStr->nUsed -= i;
|
|
+ memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1);
|
|
+}
|
|
+#else
|
|
+# define jsonGroupInverse 0
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+** json_group_obj(NAME,VALUE)
|
|
+**
|
|
+** Return a JSON object composed of all names and values in the aggregate.
|
|
+*/
|
|
+static void jsonObjectStep(
|
|
+ sqlite3_context *ctx,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ JsonString *pStr;
|
|
+ const char *z;
|
|
+ u32 n;
|
|
+ UNUSED_PARAM(argc);
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
|
|
+ if( pStr ){
|
|
+ if( pStr->zBuf==0 ){
|
|
+ jsonInit(pStr, ctx);
|
|
+ jsonAppendChar(pStr, '{');
|
|
+ }else{
|
|
+ jsonAppendChar(pStr, ',');
|
|
+ pStr->pCtx = ctx;
|
|
+ }
|
|
+ z = (const char*)sqlite3_value_text(argv[0]);
|
|
+ n = (u32)sqlite3_value_bytes(argv[0]);
|
|
+ jsonAppendString(pStr, z, n);
|
|
+ jsonAppendChar(pStr, ':');
|
|
+ jsonAppendValue(pStr, argv[1]);
|
|
+ }
|
|
+}
|
|
+static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
|
|
+ JsonString *pStr;
|
|
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
+ if( pStr ){
|
|
+ jsonAppendChar(pStr, '}');
|
|
+ if( pStr->bErr ){
|
|
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
|
+ assert( pStr->bStatic );
|
|
+ }else if( isFinal ){
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
|
|
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
|
|
+ pStr->bStatic = 1;
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
|
|
+ pStr->nUsed--;
|
|
+ }
|
|
+ }else{
|
|
+ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
|
|
+ }
|
|
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+}
|
|
+static void jsonObjectValue(sqlite3_context *ctx){
|
|
+ jsonObjectCompute(ctx, 0);
|
|
+}
|
|
+static void jsonObjectFinal(sqlite3_context *ctx){
|
|
+ jsonObjectCompute(ctx, 1);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+/****************************************************************************
|
|
+** The json_each virtual table
|
|
+****************************************************************************/
|
|
+typedef struct JsonEachCursor JsonEachCursor;
|
|
+struct JsonEachCursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
+ u32 iRowid; /* The rowid */
|
|
+ u32 iBegin; /* The first node of the scan */
|
|
+ u32 i; /* Index in sParse.aNode[] of current row */
|
|
+ u32 iEnd; /* EOF when i equals or exceeds this value */
|
|
+ u8 eType; /* Type of top-level element */
|
|
+ u8 bRecursive; /* True for json_tree(). False for json_each() */
|
|
+ char *zJson; /* Input JSON */
|
|
+ char *zRoot; /* Path by which to filter zJson */
|
|
+ JsonParse sParse; /* Parse of the input JSON */
|
|
+};
|
|
+
|
|
+/* Constructor for the json_each virtual table */
|
|
+static int jsonEachConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ sqlite3_vtab *pNew;
|
|
+ int rc;
|
|
+
|
|
+/* Column numbers */
|
|
+#define JEACH_KEY 0
|
|
+#define JEACH_VALUE 1
|
|
+#define JEACH_TYPE 2
|
|
+#define JEACH_ATOM 3
|
|
+#define JEACH_ID 4
|
|
+#define JEACH_PARENT 5
|
|
+#define JEACH_FULLKEY 6
|
|
+#define JEACH_PATH 7
|
|
+/* The xBestIndex method assumes that the JSON and ROOT columns are
|
|
+** the last two columns in the table. Should this ever changes, be
|
|
+** sure to update the xBestIndex method. */
|
|
+#define JEACH_JSON 8
|
|
+#define JEACH_ROOT 9
|
|
+
|
|
+ UNUSED_PARAM(pzErr);
|
|
+ UNUSED_PARAM(argv);
|
|
+ UNUSED_PARAM(argc);
|
|
+ UNUSED_PARAM(pAux);
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
|
|
+ "json HIDDEN,root HIDDEN)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
+ if( pNew==0 ) return SQLITE_NOMEM;
|
|
+ memset(pNew, 0, sizeof(*pNew));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* destructor for json_each virtual table */
|
|
+static int jsonEachDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* constructor for a JsonEachCursor object for json_each(). */
|
|
+static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ JsonEachCursor *pCur;
|
|
+
|
|
+ UNUSED_PARAM(p);
|
|
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
+ if( pCur==0 ) return SQLITE_NOMEM;
|
|
+ memset(pCur, 0, sizeof(*pCur));
|
|
+ *ppCursor = &pCur->base;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* constructor for a JsonEachCursor object for json_tree(). */
|
|
+static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
+ int rc = jsonEachOpenEach(p, ppCursor);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
|
|
+ pCur->bRecursive = 1;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* Reset a JsonEachCursor back to its original state. Free any memory
|
|
+** held. */
|
|
+static void jsonEachCursorReset(JsonEachCursor *p){
|
|
+ sqlite3_free(p->zJson);
|
|
+ sqlite3_free(p->zRoot);
|
|
+ jsonParseReset(&p->sParse);
|
|
+ p->iRowid = 0;
|
|
+ p->i = 0;
|
|
+ p->iEnd = 0;
|
|
+ p->eType = 0;
|
|
+ p->zJson = 0;
|
|
+ p->zRoot = 0;
|
|
+}
|
|
+
|
|
+/* Destructor for a jsonEachCursor object */
|
|
+static int jsonEachClose(sqlite3_vtab_cursor *cur){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ jsonEachCursorReset(p);
|
|
+ sqlite3_free(cur);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Return TRUE if the jsonEachCursor object has been advanced off the end
|
|
+** of the JSON object */
|
|
+static int jsonEachEof(sqlite3_vtab_cursor *cur){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ return p->i >= p->iEnd;
|
|
+}
|
|
+
|
|
+/* Advance the cursor to the next element for json_tree() */
|
|
+static int jsonEachNext(sqlite3_vtab_cursor *cur){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ if( p->bRecursive ){
|
|
+ if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
|
|
+ p->i++;
|
|
+ p->iRowid++;
|
|
+ if( p->i<p->iEnd ){
|
|
+ u32 iUp = p->sParse.aUp[p->i];
|
|
+ JsonNode *pUp = &p->sParse.aNode[iUp];
|
|
+ p->eType = pUp->eType;
|
|
+ if( pUp->eType==JSON_ARRAY ){
|
|
+ if( iUp==p->i-1 ){
|
|
+ pUp->u.iKey = 0;
|
|
+ }else{
|
|
+ pUp->u.iKey++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ switch( p->eType ){
|
|
+ case JSON_ARRAY: {
|
|
+ p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
|
|
+ p->iRowid++;
|
|
+ break;
|
|
+ }
|
|
+ case JSON_OBJECT: {
|
|
+ p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
|
|
+ p->iRowid++;
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ p->i = p->iEnd;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Append the name of the path for element i to pStr
|
|
+*/
|
|
+static void jsonEachComputePath(
|
|
+ JsonEachCursor *p, /* The cursor */
|
|
+ JsonString *pStr, /* Write the path here */
|
|
+ u32 i /* Path to this element */
|
|
+){
|
|
+ JsonNode *pNode, *pUp;
|
|
+ u32 iUp;
|
|
+ if( i==0 ){
|
|
+ jsonAppendChar(pStr, '$');
|
|
+ return;
|
|
+ }
|
|
+ iUp = p->sParse.aUp[i];
|
|
+ jsonEachComputePath(p, pStr, iUp);
|
|
+ pNode = &p->sParse.aNode[i];
|
|
+ pUp = &p->sParse.aNode[iUp];
|
|
+ if( pUp->eType==JSON_ARRAY ){
|
|
+ jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
|
|
+ }else{
|
|
+ assert( pUp->eType==JSON_OBJECT );
|
|
+ if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
|
|
+ assert( pNode->eType==JSON_STRING );
|
|
+ assert( pNode->jnFlags & JNODE_LABEL );
|
|
+ jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Return the value of a column */
|
|
+static int jsonEachColumn(
|
|
+ sqlite3_vtab_cursor *cur, /* The cursor */
|
|
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
+ int i /* Which column to return */
|
|
+){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ JsonNode *pThis = &p->sParse.aNode[p->i];
|
|
+ switch( i ){
|
|
+ case JEACH_KEY: {
|
|
+ if( p->i==0 ) break;
|
|
+ if( p->eType==JSON_OBJECT ){
|
|
+ jsonReturn(pThis, ctx, 0);
|
|
+ }else if( p->eType==JSON_ARRAY ){
|
|
+ u32 iKey;
|
|
+ if( p->bRecursive ){
|
|
+ if( p->iRowid==0 ) break;
|
|
+ iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
|
|
+ }else{
|
|
+ iKey = p->iRowid;
|
|
+ }
|
|
+ sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_VALUE: {
|
|
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
+ jsonReturn(pThis, ctx, 0);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_TYPE: {
|
|
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
+ sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_ATOM: {
|
|
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
+ if( pThis->eType>=JSON_ARRAY ) break;
|
|
+ jsonReturn(pThis, ctx, 0);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_ID: {
|
|
+ sqlite3_result_int64(ctx,
|
|
+ (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_PARENT: {
|
|
+ if( p->i>p->iBegin && p->bRecursive ){
|
|
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_FULLKEY: {
|
|
+ JsonString x;
|
|
+ jsonInit(&x, ctx);
|
|
+ if( p->bRecursive ){
|
|
+ jsonEachComputePath(p, &x, p->i);
|
|
+ }else{
|
|
+ if( p->zRoot ){
|
|
+ jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
|
|
+ }else{
|
|
+ jsonAppendChar(&x, '$');
|
|
+ }
|
|
+ if( p->eType==JSON_ARRAY ){
|
|
+ jsonPrintf(30, &x, "[%d]", p->iRowid);
|
|
+ }else if( p->eType==JSON_OBJECT ){
|
|
+ jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
|
|
+ }
|
|
+ }
|
|
+ jsonResult(&x);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_PATH: {
|
|
+ if( p->bRecursive ){
|
|
+ JsonString x;
|
|
+ jsonInit(&x, ctx);
|
|
+ jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
|
|
+ jsonResult(&x);
|
|
+ break;
|
|
+ }
|
|
+ /* For json_each() path and root are the same so fall through
|
|
+ ** into the root case */
|
|
+ }
|
|
+ default: {
|
|
+ const char *zRoot = p->zRoot;
|
|
+ if( zRoot==0 ) zRoot = "$";
|
|
+ sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ case JEACH_JSON: {
|
|
+ assert( i==JEACH_JSON );
|
|
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Return the current rowid value */
|
|
+static int jsonEachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ *pRowid = p->iRowid;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* The query strategy is to look for an equality constraint on the json
|
|
+** column. Without such a constraint, the table cannot operate. idxNum is
|
|
+** 1 if the constraint is found, 3 if the constraint and zRoot are found,
|
|
+** and 0 otherwise.
|
|
+*/
|
|
+static int jsonEachBestIndex(
|
|
+ sqlite3_vtab *tab,
|
|
+ sqlite3_index_info *pIdxInfo
|
|
+){
|
|
+ int i; /* Loop counter or computed array index */
|
|
+ int aIdx[2]; /* Index of constraints for JSON and ROOT */
|
|
+ int unusableMask = 0; /* Mask of unusable JSON and ROOT constraints */
|
|
+ int idxMask = 0; /* Mask of usable == constraints JSON and ROOT */
|
|
+ const struct sqlite3_index_constraint *pConstraint;
|
|
+
|
|
+ /* This implementation assumes that JSON and ROOT are the last two
|
|
+ ** columns in the table */
|
|
+ assert( JEACH_ROOT == JEACH_JSON+1 );
|
|
+ UNUSED_PARAM(tab);
|
|
+ aIdx[0] = aIdx[1] = -1;
|
|
+ pConstraint = pIdxInfo->aConstraint;
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
+ int iCol;
|
|
+ int iMask;
|
|
+ if( pConstraint->iColumn < JEACH_JSON ) continue;
|
|
+ iCol = pConstraint->iColumn - JEACH_JSON;
|
|
+ assert( iCol==0 || iCol==1 );
|
|
+ iMask = 1 << iCol;
|
|
+ if( pConstraint->usable==0 ){
|
|
+ unusableMask |= iMask;
|
|
+ }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ aIdx[iCol] = i;
|
|
+ idxMask |= iMask;
|
|
+ }
|
|
+ }
|
|
+ if( (unusableMask & ~idxMask)!=0 ){
|
|
+ /* If there are any unusable constraints on JSON or ROOT, then reject
|
|
+ ** this entire plan */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ if( aIdx[0]<0 ){
|
|
+ /* No JSON input. Leave estimatedCost at the huge value that it was
|
|
+ ** initialized to to discourage the query planner from selecting this
|
|
+ ** plan. */
|
|
+ pIdxInfo->idxNum = 0;
|
|
+ }else{
|
|
+ pIdxInfo->estimatedCost = 1.0;
|
|
+ i = aIdx[0];
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ if( aIdx[1]<0 ){
|
|
+ pIdxInfo->idxNum = 1; /* Only JSON supplied. Plan 1 */
|
|
+ }else{
|
|
+ i = aIdx[1];
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 2;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ pIdxInfo->idxNum = 3; /* Both JSON and ROOT are supplied. Plan 3 */
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* Start a search on a new JSON string */
|
|
+static int jsonEachFilter(
|
|
+ sqlite3_vtab_cursor *cur,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
+ const char *z;
|
|
+ const char *zRoot = 0;
|
|
+ sqlite3_int64 n;
|
|
+
|
|
+ UNUSED_PARAM(idxStr);
|
|
+ UNUSED_PARAM(argc);
|
|
+ jsonEachCursorReset(p);
|
|
+ if( idxNum==0 ) return SQLITE_OK;
|
|
+ z = (const char*)sqlite3_value_text(argv[0]);
|
|
+ if( z==0 ) return SQLITE_OK;
|
|
+ n = sqlite3_value_bytes(argv[0]);
|
|
+ p->zJson = sqlite3_malloc64( n+1 );
|
|
+ if( p->zJson==0 ) return SQLITE_NOMEM;
|
|
+ memcpy(p->zJson, z, (size_t)n+1);
|
|
+ if( jsonParse(&p->sParse, 0, p->zJson) ){
|
|
+ int rc = SQLITE_NOMEM;
|
|
+ if( p->sParse.oom==0 ){
|
|
+ sqlite3_free(cur->pVtab->zErrMsg);
|
|
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
|
|
+ if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
|
|
+ }
|
|
+ jsonEachCursorReset(p);
|
|
+ return rc;
|
|
+ }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
|
|
+ jsonEachCursorReset(p);
|
|
+ return SQLITE_NOMEM;
|
|
+ }else{
|
|
+ JsonNode *pNode = 0;
|
|
+ if( idxNum==3 ){
|
|
+ const char *zErr = 0;
|
|
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
|
|
+ if( zRoot==0 ) return SQLITE_OK;
|
|
+ n = sqlite3_value_bytes(argv[1]);
|
|
+ p->zRoot = sqlite3_malloc64( n+1 );
|
|
+ if( p->zRoot==0 ) return SQLITE_NOMEM;
|
|
+ memcpy(p->zRoot, zRoot, (size_t)n+1);
|
|
+ if( zRoot[0]!='$' ){
|
|
+ zErr = zRoot;
|
|
+ }else{
|
|
+ pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
|
|
+ }
|
|
+ if( zErr ){
|
|
+ sqlite3_free(cur->pVtab->zErrMsg);
|
|
+ cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
|
|
+ jsonEachCursorReset(p);
|
|
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
|
|
+ }else if( pNode==0 ){
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ }else{
|
|
+ pNode = p->sParse.aNode;
|
|
+ }
|
|
+ p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
|
|
+ p->eType = pNode->eType;
|
|
+ if( p->eType>=JSON_ARRAY ){
|
|
+ pNode->u.iKey = 0;
|
|
+ p->iEnd = p->i + pNode->n + 1;
|
|
+ if( p->bRecursive ){
|
|
+ p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
|
|
+ if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
|
|
+ p->i--;
|
|
+ }
|
|
+ }else{
|
|
+ p->i++;
|
|
+ }
|
|
+ }else{
|
|
+ p->iEnd = p->i+1;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/* The methods of the json_each virtual table */
|
|
+static sqlite3_module jsonEachModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ jsonEachConnect, /* xConnect */
|
|
+ jsonEachBestIndex, /* xBestIndex */
|
|
+ jsonEachDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ jsonEachOpenEach, /* xOpen - open a cursor */
|
|
+ jsonEachClose, /* xClose - close a cursor */
|
|
+ jsonEachFilter, /* xFilter - configure scan constraints */
|
|
+ jsonEachNext, /* xNext - advance a cursor */
|
|
+ jsonEachEof, /* xEof - check for end of scan */
|
|
+ jsonEachColumn, /* xColumn - read data */
|
|
+ jsonEachRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+
|
|
+/* The methods of the json_tree virtual table. */
|
|
+static sqlite3_module jsonTreeModule = {
|
|
+ 0, /* iVersion */
|
|
+ 0, /* xCreate */
|
|
+ jsonEachConnect, /* xConnect */
|
|
+ jsonEachBestIndex, /* xBestIndex */
|
|
+ jsonEachDisconnect, /* xDisconnect */
|
|
+ 0, /* xDestroy */
|
|
+ jsonEachOpenTree, /* xOpen - open a cursor */
|
|
+ jsonEachClose, /* xClose - close a cursor */
|
|
+ jsonEachFilter, /* xFilter - configure scan constraints */
|
|
+ jsonEachNext, /* xNext - advance a cursor */
|
|
+ jsonEachEof, /* xEof - check for end of scan */
|
|
+ jsonEachColumn, /* xColumn - read data */
|
|
+ jsonEachRowid, /* xRowid - read data */
|
|
+ 0, /* xUpdate */
|
|
+ 0, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+};
|
|
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
+
|
|
+/****************************************************************************
|
|
+** The following routines are the only publically visible identifiers in this
|
|
+** file. Call the following routines in order to register the various SQL
|
|
+** functions and the virtual table implemented by this file.
|
|
+****************************************************************************/
|
|
+
|
|
+SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+ unsigned int i;
|
|
+ static const struct {
|
|
+ const char *zName;
|
|
+ int nArg;
|
|
+ int flag;
|
|
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
|
+ } aFunc[] = {
|
|
+ { "json", 1, 0, jsonRemoveFunc },
|
|
+ { "json_array", -1, 0, jsonArrayFunc },
|
|
+ { "json_array_length", 1, 0, jsonArrayLengthFunc },
|
|
+ { "json_array_length", 2, 0, jsonArrayLengthFunc },
|
|
+ { "json_extract", -1, 0, jsonExtractFunc },
|
|
+ { "json_insert", -1, 0, jsonSetFunc },
|
|
+ { "json_object", -1, 0, jsonObjectFunc },
|
|
+ { "json_patch", 2, 0, jsonPatchFunc },
|
|
+ { "json_quote", 1, 0, jsonQuoteFunc },
|
|
+ { "json_remove", -1, 0, jsonRemoveFunc },
|
|
+ { "json_replace", -1, 0, jsonReplaceFunc },
|
|
+ { "json_set", -1, 1, jsonSetFunc },
|
|
+ { "json_type", 1, 0, jsonTypeFunc },
|
|
+ { "json_type", 2, 0, jsonTypeFunc },
|
|
+ { "json_valid", 1, 0, jsonValidFunc },
|
|
+
|
|
+#if SQLITE_DEBUG
|
|
+ /* DEBUG and TESTING functions */
|
|
+ { "json_parse", 1, 0, jsonParseFunc },
|
|
+ { "json_test1", 1, 0, jsonTest1Func },
|
|
+#endif
|
|
+ };
|
|
+ static const struct {
|
|
+ const char *zName;
|
|
+ int nArg;
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
|
|
+ void (*xFinal)(sqlite3_context*);
|
|
+ void (*xValue)(sqlite3_context*);
|
|
+ } aAgg[] = {
|
|
+ { "json_group_array", 1,
|
|
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue },
|
|
+ { "json_group_object", 2,
|
|
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue },
|
|
+ };
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ static const struct {
|
|
+ const char *zName;
|
|
+ sqlite3_module *pModule;
|
|
+ } aMod[] = {
|
|
+ { "json_each", &jsonEachModule },
|
|
+ { "json_tree", &jsonTreeModule },
|
|
+ };
|
|
+#endif
|
|
+ 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,
|
|
+ (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_UTF8 | SQLITE_DETERMINISTIC, 0,
|
|
+ aAgg[i].xStep, aAgg[i].xFinal,
|
|
+ aAgg[i].xValue, jsonGroupInverse, 0);
|
|
+ }
|
|
+#endif
|
|
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
+ for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
|
|
+ rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
|
|
+ }
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+#ifndef SQLITE_CORE
|
|
+#ifdef _WIN32
|
|
+__declspec(dllexport)
|
|
+#endif
|
|
+SQLITE_API int sqlite3_json_init(
|
|
+ sqlite3 *db,
|
|
+ char **pzErrMsg,
|
|
+ const sqlite3_api_routines *pApi
|
|
+){
|
|
+ SQLITE_EXTENSION_INIT2(pApi);
|
|
+ (void)pzErrMsg; /* Unused parameter */
|
|
+ return sqlite3Json1Init(db);
|
|
+}
|
|
+#endif
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) */
|
|
+
|
|
+/************** End of json1.c ***********************************************/
|
|
/************** Begin file rtree.c *******************************************/
|
|
/*
|
|
** 2001 September 15
|
|
@@ -165280,7 +179149,7 @@
|
|
**
|
|
** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB)
|
|
** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER)
|
|
-** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER)
|
|
+** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...)
|
|
**
|
|
** The data for each node of the r-tree structure is stored in the %_node
|
|
** table. For each node that is not the root node of the r-tree, there is
|
|
@@ -165287,7 +179156,8 @@
|
|
** an entry in the %_parent table associating the node with its parent.
|
|
** And for each row of data in the table, there is an entry in the %_rowid
|
|
** table that maps from the entries rowid to the id of the node that it
|
|
-** is stored on.
|
|
+** is stored on. If the r-tree contains auxiliary columns, those are stored
|
|
+** on the end of the %_rowid table.
|
|
**
|
|
** The root node of an r-tree always exists, even if the r-tree table is
|
|
** empty. The nodeno of the root node is always 1. All other nodes in the
|
|
@@ -165308,7 +179178,8 @@
|
|
** child page.
|
|
*/
|
|
|
|
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE)
|
|
+#if !defined(SQLITE_CORE) \
|
|
+ || (defined(SQLITE_ENABLE_RTREE) && !defined(SQLITE_OMIT_VIRTUALTABLE))
|
|
|
|
#ifndef SQLITE_CORE
|
|
/* #include "sqlite3ext.h" */
|
|
@@ -165349,6 +179220,9 @@
|
|
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
|
|
#define RTREE_MAX_DIMENSIONS 5
|
|
|
|
+/* Maximum number of auxiliary columns */
|
|
+#define RTREE_MAX_AUX_COLUMN 100
|
|
+
|
|
/* Size of hash table Rtree.aHash. This hash table is not expected to
|
|
** ever contain very many entries, so a fixed number of buckets is
|
|
** used.
|
|
@@ -165377,6 +179251,8 @@
|
|
u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
|
|
u8 nBytesPerCell; /* Bytes consumed per cell */
|
|
u8 inWrTrans; /* True if inside write transaction */
|
|
+ u8 nAux; /* # of auxiliary columns in %_rowid */
|
|
+ u8 nAuxNotNull; /* Number of initial not-null aux columns */
|
|
int iDepth; /* Current depth of the r-tree structure */
|
|
char *zDb; /* Name of database containing r-tree table */
|
|
char *zName; /* Name of r-tree table */
|
|
@@ -165383,6 +179259,8 @@
|
|
u32 nBusy; /* Current number of users of this structure */
|
|
i64 nRowEst; /* Estimated number of rows in this table */
|
|
u32 nCursor; /* Number of open cursors */
|
|
+ u32 nNodeRef; /* Number RtreeNodes with positive nRef */
|
|
+ char *zReadAuxSql; /* SQL for statement to read aux data */
|
|
|
|
/* List of nodes removed during a CondenseTree operation. List is
|
|
** linked together via the pointer normally used for hash chains -
|
|
@@ -165409,6 +179287,9 @@
|
|
sqlite3_stmt *pWriteParent;
|
|
sqlite3_stmt *pDeleteParent;
|
|
|
|
+ /* Statement for writing to the "aux:" fields, if there are any */
|
|
+ sqlite3_stmt *pWriteAux;
|
|
+
|
|
RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
|
|
};
|
|
|
|
@@ -165465,7 +179346,7 @@
|
|
** The smallest possible node-size is (512-64)==448 bytes. And the largest
|
|
** supported cell size is 48 bytes (8 byte rowid + ten 4 byte coordinates).
|
|
** Therefore all non-root nodes must contain at least 3 entries. Since
|
|
-** 2^40 is greater than 2^64, an r-tree structure always has a depth of
|
|
+** 3^40 is greater than 2^64, an r-tree structure always has a depth of
|
|
** 40 or less.
|
|
*/
|
|
#define RTREE_MAX_DEPTH 40
|
|
@@ -165485,6 +179366,7 @@
|
|
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
|
u8 atEOF; /* True if at end of search */
|
|
u8 bPoint; /* True if sPoint is valid */
|
|
+ u8 bAuxValid; /* True if pReadAux is valid */
|
|
int iStrategy; /* Copy of idxNum search parameter */
|
|
int nConstraint; /* Number of entries in aConstraint */
|
|
RtreeConstraint *aConstraint; /* Search constraints. */
|
|
@@ -165492,6 +179374,7 @@
|
|
int nPoint; /* Number of slots used in aPoint[] */
|
|
int mxLevel; /* iLevel value for root of the tree */
|
|
RtreeSearchPoint *aPoint; /* Priority queue for search points */
|
|
+ sqlite3_stmt *pReadAux; /* Statement to read aux-data */
|
|
RtreeSearchPoint sPoint; /* Cached next search point */
|
|
RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */
|
|
u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */
|
|
@@ -165778,6 +179661,7 @@
|
|
*/
|
|
static void nodeReference(RtreeNode *p){
|
|
if( p ){
|
|
+ assert( p->nRef>0 );
|
|
p->nRef++;
|
|
}
|
|
}
|
|
@@ -165845,6 +179729,7 @@
|
|
memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize);
|
|
pNode->zData = (u8 *)&pNode[1];
|
|
pNode->nRef = 1;
|
|
+ pRtree->nNodeRef++;
|
|
pNode->pParent = pParent;
|
|
pNode->isDirty = 1;
|
|
nodeReference(pParent);
|
|
@@ -165878,10 +179763,10 @@
|
|
/* Check if the requested node is already in the hash table. If so,
|
|
** increase its reference count and return it.
|
|
*/
|
|
- if( (pNode = nodeHashLookup(pRtree, iNode)) ){
|
|
+ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
|
|
assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
|
|
if( pParent && !pNode->pParent ){
|
|
- nodeReference(pParent);
|
|
+ pParent->nRef++;
|
|
pNode->pParent = pParent;
|
|
}
|
|
pNode->nRef++;
|
|
@@ -165920,6 +179805,7 @@
|
|
pNode->pParent = pParent;
|
|
pNode->zData = (u8 *)&pNode[1];
|
|
pNode->nRef = 1;
|
|
+ pRtree->nNodeRef++;
|
|
pNode->iNode = iNode;
|
|
pNode->isDirty = 0;
|
|
pNode->pNext = 0;
|
|
@@ -165960,7 +179846,10 @@
|
|
}
|
|
*ppNode = pNode;
|
|
}else{
|
|
- sqlite3_free(pNode);
|
|
+ if( pNode ){
|
|
+ pRtree->nNodeRef--;
|
|
+ sqlite3_free(pNode);
|
|
+ }
|
|
*ppNode = 0;
|
|
}
|
|
|
|
@@ -166040,6 +179929,7 @@
|
|
sqlite3_step(p);
|
|
pNode->isDirty = 0;
|
|
rc = sqlite3_reset(p);
|
|
+ sqlite3_bind_null(p, 2);
|
|
if( pNode->iNode==0 && rc==SQLITE_OK ){
|
|
pNode->iNode = sqlite3_last_insert_rowid(pRtree->db);
|
|
nodeHashInsert(pRtree, pNode);
|
|
@@ -166056,8 +179946,10 @@
|
|
int rc = SQLITE_OK;
|
|
if( pNode ){
|
|
assert( pNode->nRef>0 );
|
|
+ assert( pRtree->nNodeRef>0 );
|
|
pNode->nRef--;
|
|
if( pNode->nRef==0 ){
|
|
+ pRtree->nNodeRef--;
|
|
if( pNode->iNode==1 ){
|
|
pRtree->iDepth = -1;
|
|
}
|
|
@@ -166174,8 +180066,9 @@
|
|
pRtree->nBusy--;
|
|
if( pRtree->nBusy==0 ){
|
|
pRtree->inWrTrans = 0;
|
|
- pRtree->nCursor = 0;
|
|
+ assert( pRtree->nCursor==0 );
|
|
nodeBlobReset(pRtree);
|
|
+ assert( pRtree->nNodeRef==0 );
|
|
sqlite3_finalize(pRtree->pWriteNode);
|
|
sqlite3_finalize(pRtree->pDeleteNode);
|
|
sqlite3_finalize(pRtree->pReadRowid);
|
|
@@ -166184,6 +180077,8 @@
|
|
sqlite3_finalize(pRtree->pReadParent);
|
|
sqlite3_finalize(pRtree->pWriteParent);
|
|
sqlite3_finalize(pRtree->pDeleteParent);
|
|
+ sqlite3_finalize(pRtree->pWriteAux);
|
|
+ sqlite3_free(pRtree->zReadAuxSql);
|
|
sqlite3_free(pRtree);
|
|
}
|
|
}
|
|
@@ -166272,6 +180167,7 @@
|
|
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
|
assert( pRtree->nCursor>0 );
|
|
freeCursorConstraints(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);
|
|
@@ -166643,7 +180539,7 @@
|
|
if( ii<RTREE_CACHE_SZ ){
|
|
assert( pCur->aNode[ii]==0 );
|
|
pCur->aNode[ii] = pCur->aNode[0];
|
|
- }else{
|
|
+ }else{
|
|
nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
|
|
}
|
|
pCur->aNode[0] = 0;
|
|
@@ -166814,6 +180710,10 @@
|
|
|
|
/* Move to the next entry that matches the configured constraints. */
|
|
RTREE_QUEUE_TRACE(pCsr, "POP-Nx:");
|
|
+ if( pCsr->bAuxValid ){
|
|
+ pCsr->bAuxValid = 0;
|
|
+ sqlite3_reset(pCsr->pReadAux);
|
|
+ }
|
|
rtreeSearchPointPop(pCsr);
|
|
rc = rtreeStepToLeaf(pCsr);
|
|
return rc;
|
|
@@ -166848,7 +180748,7 @@
|
|
if( p==0 ) return SQLITE_OK;
|
|
if( i==0 ){
|
|
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
|
|
- }else{
|
|
+ }else if( i<=pRtree->nDim2 ){
|
|
nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
|
|
#ifndef SQLITE_RTREE_INT_ONLY
|
|
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
|
@@ -166859,7 +180759,27 @@
|
|
assert( pRtree->eCoordType==RTREE_COORD_INT32 );
|
|
sqlite3_result_int(ctx, c.i);
|
|
}
|
|
- }
|
|
+ }else{
|
|
+ if( !pCsr->bAuxValid ){
|
|
+ if( pCsr->pReadAux==0 ){
|
|
+ rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0,
|
|
+ &pCsr->pReadAux, 0);
|
|
+ if( rc ) return rc;
|
|
+ }
|
|
+ sqlite3_bind_int64(pCsr->pReadAux, 1,
|
|
+ nodeGetRowid(pRtree, pNode, p->iCell));
|
|
+ rc = sqlite3_step(pCsr->pReadAux);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ pCsr->bAuxValid = 1;
|
|
+ }else{
|
|
+ sqlite3_reset(pCsr->pReadAux);
|
|
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_value(ctx,
|
|
+ sqlite3_column_value(pCsr->pReadAux, i - pRtree->nDim2 + 1));
|
|
+ }
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
@@ -166937,6 +180857,7 @@
|
|
int ii;
|
|
int rc = SQLITE_OK;
|
|
int iCell = 0;
|
|
+ sqlite3_stmt *pStmt;
|
|
|
|
rtreeReference(pRtree);
|
|
|
|
@@ -166943,8 +180864,10 @@
|
|
/* 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;
|
|
|
|
pCsr->iStrategy = idxNum;
|
|
if( idxNum==1 ){
|
|
@@ -167107,10 +181030,14 @@
|
|
*/
|
|
pIdxInfo->estimatedCost = 30.0;
|
|
pIdxInfo->estimatedRows = 1;
|
|
+ pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
- if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){
|
|
+ if( p->usable
|
|
+ && ((p->iColumn>0 && p->iColumn<=pRtree->nDim2)
|
|
+ || p->op==SQLITE_INDEX_CONSTRAINT_MATCH)
|
|
+ ){
|
|
u8 op;
|
|
switch( p->op ){
|
|
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
|
|
@@ -167277,7 +181204,7 @@
|
|
){
|
|
int rc;
|
|
int ii;
|
|
- RtreeNode *pNode;
|
|
+ RtreeNode *pNode = 0;
|
|
rc = nodeAcquire(pRtree, 1, 0, &pNode);
|
|
|
|
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
|
|
@@ -167683,7 +181610,7 @@
|
|
}else{
|
|
pLeft = pNode;
|
|
pRight = nodeNew(pRtree, pLeft->pParent);
|
|
- nodeReference(pLeft);
|
|
+ pLeft->nRef++;
|
|
}
|
|
|
|
if( !pLeft || !pRight ){
|
|
@@ -168092,7 +182019,7 @@
|
|
/*
|
|
** Select a currently unused rowid for a new r-tree record.
|
|
*/
|
|
-static int newRowid(Rtree *pRtree, i64 *piRowid){
|
|
+static int rtreeNewRowid(Rtree *pRtree, i64 *piRowid){
|
|
int rc;
|
|
sqlite3_bind_null(pRtree->pWriteRowid, 1);
|
|
sqlite3_bind_null(pRtree->pWriteRowid, 2);
|
|
@@ -168109,7 +182036,7 @@
|
|
int rc; /* Return code */
|
|
RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */
|
|
int iCell; /* Index of iDelete cell in pLeaf */
|
|
- RtreeNode *pRoot; /* Root node of rtree structure */
|
|
+ RtreeNode *pRoot = 0; /* Root node of rtree structure */
|
|
|
|
|
|
/* Obtain a reference to the root node to initialize Rtree.iDepth */
|
|
@@ -168152,7 +182079,7 @@
|
|
*/
|
|
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
|
|
int rc2;
|
|
- RtreeNode *pChild;
|
|
+ RtreeNode *pChild = 0;
|
|
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
|
|
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
|
|
if( rc==SQLITE_OK ){
|
|
@@ -168173,6 +182100,7 @@
|
|
rc = reinsertNodeContent(pRtree, pLeaf);
|
|
}
|
|
pRtree->pDeleted = pLeaf->pNext;
|
|
+ pRtree->nNodeRef--;
|
|
sqlite3_free(pLeaf);
|
|
}
|
|
|
|
@@ -168269,7 +182197,7 @@
|
|
static int rtreeUpdate(
|
|
sqlite3_vtab *pVtab,
|
|
int nData,
|
|
- sqlite3_value **azData,
|
|
+ sqlite3_value **aData,
|
|
sqlite_int64 *pRowid
|
|
){
|
|
Rtree *pRtree = (Rtree *)pVtab;
|
|
@@ -168277,6 +182205,12 @@
|
|
RtreeCell cell; /* New cell to insert if nData>1 */
|
|
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
|
|
|
|
+ if( pRtree->nNodeRef ){
|
|
+ /* Unable to write to the btree while another cursor is reading from it,
|
|
+ ** since the write might do a rebalance which would disrupt the read
|
|
+ ** cursor. */
|
|
+ return SQLITE_LOCKED_VTAB;
|
|
+ }
|
|
rtreeReference(pRtree);
|
|
assert(nData>=1);
|
|
|
|
@@ -168295,8 +182229,10 @@
|
|
*/
|
|
if( nData>1 ){
|
|
int ii;
|
|
+ int nn = nData - 4;
|
|
|
|
- /* Populate the cell.aCoord[] array. The first coordinate is azData[3].
|
|
+ if( nn > pRtree->nDim2 ) nn = pRtree->nDim2;
|
|
+ /* Populate the cell.aCoord[] array. The first coordinate is aData[3].
|
|
**
|
|
** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared
|
|
** with "column" that are interpreted as table constraints.
|
|
@@ -168304,13 +182240,12 @@
|
|
** This problem was discovered after years of use, so we silently ignore
|
|
** these kinds of misdeclared tables to avoid breaking any legacy.
|
|
*/
|
|
- assert( nData<=(pRtree->nDim2 + 3) );
|
|
|
|
#ifndef SQLITE_RTREE_INT_ONLY
|
|
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
|
- for(ii=0; ii<nData-4; ii+=2){
|
|
- cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
|
|
- cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
|
|
+ for(ii=0; ii<nn; ii+=2){
|
|
+ cell.aCoord[ii].f = rtreeValueDown(aData[ii+3]);
|
|
+ cell.aCoord[ii+1].f = rtreeValueUp(aData[ii+4]);
|
|
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
|
|
rc = rtreeConstraintError(pRtree, ii+1);
|
|
goto constraint;
|
|
@@ -168319,9 +182254,9 @@
|
|
}else
|
|
#endif
|
|
{
|
|
- for(ii=0; ii<nData-4; ii+=2){
|
|
- cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
|
|
- cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
|
|
+ for(ii=0; ii<nn; ii+=2){
|
|
+ cell.aCoord[ii].i = sqlite3_value_int(aData[ii+3]);
|
|
+ cell.aCoord[ii+1].i = sqlite3_value_int(aData[ii+4]);
|
|
if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
|
|
rc = rtreeConstraintError(pRtree, ii+1);
|
|
goto constraint;
|
|
@@ -168331,10 +182266,10 @@
|
|
|
|
/* If a rowid value was supplied, check if it is already present in
|
|
** the table. If so, the constraint has failed. */
|
|
- if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
|
|
- cell.iRowid = sqlite3_value_int64(azData[2]);
|
|
- if( sqlite3_value_type(azData[0])==SQLITE_NULL
|
|
- || sqlite3_value_int64(azData[0])!=cell.iRowid
|
|
+ if( sqlite3_value_type(aData[2])!=SQLITE_NULL ){
|
|
+ cell.iRowid = sqlite3_value_int64(aData[2]);
|
|
+ if( sqlite3_value_type(aData[0])==SQLITE_NULL
|
|
+ || sqlite3_value_int64(aData[0])!=cell.iRowid
|
|
){
|
|
int steprc;
|
|
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
|
|
@@ -168353,16 +182288,16 @@
|
|
}
|
|
}
|
|
|
|
- /* If azData[0] is not an SQL NULL value, it is the rowid of a
|
|
+ /* If aData[0] is not an SQL NULL value, it is the rowid of a
|
|
** record to delete from the r-tree table. The following block does
|
|
** just that.
|
|
*/
|
|
- if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
|
|
- rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
|
|
+ if( sqlite3_value_type(aData[0])!=SQLITE_NULL ){
|
|
+ rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(aData[0]));
|
|
}
|
|
|
|
- /* If the azData[] array contains more than one element, elements
|
|
- ** (azData[2]..azData[argc-1]) contain a new record to insert into
|
|
+ /* If the aData[] array contains more than one element, elements
|
|
+ ** (aData[2]..aData[argc-1]) contain a new record to insert into
|
|
** the r-tree structure.
|
|
*/
|
|
if( rc==SQLITE_OK && nData>1 ){
|
|
@@ -168371,7 +182306,7 @@
|
|
|
|
/* Figure out the rowid of the new row. */
|
|
if( bHaveRowid==0 ){
|
|
- rc = newRowid(pRtree, &cell.iRowid);
|
|
+ rc = rtreeNewRowid(pRtree, &cell.iRowid);
|
|
}
|
|
*pRowid = cell.iRowid;
|
|
|
|
@@ -168387,6 +182322,16 @@
|
|
rc = rc2;
|
|
}
|
|
}
|
|
+ if( pRtree->nAux ){
|
|
+ sqlite3_stmt *pUp = pRtree->pWriteAux;
|
|
+ int jj;
|
|
+ sqlite3_bind_int64(pUp, 1, *pRowid);
|
|
+ for(jj=0; jj<pRtree->nAux; jj++){
|
|
+ sqlite3_bind_value(pUp, jj+2, aData[pRtree->nDim2+3+jj]);
|
|
+ }
|
|
+ sqlite3_step(pUp);
|
|
+ rc = sqlite3_reset(pUp);
|
|
+ }
|
|
}
|
|
|
|
constraint:
|
|
@@ -168453,7 +182398,7 @@
|
|
*/
|
|
static int rtreeSavepoint(sqlite3_vtab *pVtab, int iSavepoint){
|
|
Rtree *pRtree = (Rtree *)pVtab;
|
|
- int iwt = pRtree->inWrTrans;
|
|
+ u8 iwt = pRtree->inWrTrans;
|
|
UNUSED_PARAMETER(iSavepoint);
|
|
pRtree->inWrTrans = 0;
|
|
nodeBlobReset(pRtree);
|
|
@@ -168505,8 +182450,24 @@
|
|
return rc;
|
|
}
|
|
|
|
+
|
|
+/*
|
|
+** Return true if zName is the extension on one of the shadow tables used
|
|
+** by this module.
|
|
+*/
|
|
+static int rtreeShadowName(const char *zName){
|
|
+ static const char *azName[] = {
|
|
+ "node", "parent", "rowid"
|
|
+ };
|
|
+ unsigned int i;
|
|
+ for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
|
|
+ if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static sqlite3_module rtreeModule = {
|
|
- 2, /* iVersion */
|
|
+ 3, /* iVersion */
|
|
rtreeCreate, /* xCreate - create a table */
|
|
rtreeConnect, /* xConnect - connect to an existing table */
|
|
rtreeBestIndex, /* xBestIndex - Determine search strategy */
|
|
@@ -168529,6 +182490,7 @@
|
|
rtreeSavepoint, /* xSavepoint */
|
|
0, /* xRelease */
|
|
0, /* xRollbackTo */
|
|
+ rtreeShadowName /* xShadowName */
|
|
};
|
|
|
|
static int rtreeSqlInit(
|
|
@@ -168543,18 +182505,18 @@
|
|
#define N_STATEMENT 8
|
|
static const char *azSql[N_STATEMENT] = {
|
|
/* Write the xxx_node table */
|
|
- "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)",
|
|
- "DELETE FROM '%q'.'%q_node' WHERE nodeno = :1",
|
|
+ "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(?1, ?2)",
|
|
+ "DELETE FROM '%q'.'%q_node' WHERE nodeno = ?1",
|
|
|
|
/* Read and write the xxx_rowid table */
|
|
- "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = :1",
|
|
- "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(:1, :2)",
|
|
- "DELETE FROM '%q'.'%q_rowid' WHERE rowid = :1",
|
|
+ "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = ?1",
|
|
+ "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(?1, ?2)",
|
|
+ "DELETE FROM '%q'.'%q_rowid' WHERE rowid = ?1",
|
|
|
|
/* Read and write the xxx_parent table */
|
|
- "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = :1",
|
|
- "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(:1, :2)",
|
|
- "DELETE FROM '%q'.'%q_parent' WHERE nodeno = :1"
|
|
+ "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = ?1",
|
|
+ "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(?1, ?2)",
|
|
+ "DELETE FROM '%q'.'%q_parent' WHERE nodeno = ?1"
|
|
};
|
|
sqlite3_stmt **appStmt[N_STATEMENT];
|
|
int i;
|
|
@@ -168562,14 +182524,25 @@
|
|
pRtree->db = db;
|
|
|
|
if( isCreate ){
|
|
- char *zCreate = sqlite3_mprintf(
|
|
-"CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);"
|
|
-"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);"
|
|
-"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,"
|
|
- " parentnode INTEGER);"
|
|
-"INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))",
|
|
- zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize
|
|
- );
|
|
+ char *zCreate;
|
|
+ sqlite3_str *p = sqlite3_str_new(db);
|
|
+ int ii;
|
|
+ sqlite3_str_appendf(p,
|
|
+ "CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY,nodeno",
|
|
+ zDb, zPrefix);
|
|
+ for(ii=0; ii<pRtree->nAux; ii++){
|
|
+ sqlite3_str_appendf(p,",a%d",ii);
|
|
+ }
|
|
+ sqlite3_str_appendf(p,
|
|
+ ");CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY,data);",
|
|
+ zDb, zPrefix);
|
|
+ sqlite3_str_appendf(p,
|
|
+ "CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,parentnode);",
|
|
+ zDb, zPrefix);
|
|
+ sqlite3_str_appendf(p,
|
|
+ "INSERT INTO \"%w\".\"%w_node\"VALUES(1,zeroblob(%d))",
|
|
+ zDb, zPrefix, pRtree->iNodeSize);
|
|
+ zCreate = sqlite3_str_finish(p);
|
|
if( !zCreate ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
@@ -168591,7 +182564,17 @@
|
|
|
|
rc = rtreeQueryStat1(db, pRtree);
|
|
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
|
|
- char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
|
|
+ char *zSql;
|
|
+ const char *zFormat;
|
|
+ if( i!=3 || pRtree->nAux==0 ){
|
|
+ zFormat = azSql[i];
|
|
+ }else {
|
|
+ /* An UPSERT is very slightly slower than REPLACE, but it is needed
|
|
+ ** if there are auxiliary columns */
|
|
+ zFormat = "INSERT INTO\"%w\".\"%w_rowid\"(rowid,nodeno)VALUES(?1,?2)"
|
|
+ "ON CONFLICT(rowid)DO UPDATE SET nodeno=excluded.nodeno";
|
|
+ }
|
|
+ zSql = sqlite3_mprintf(zFormat, zDb, zPrefix);
|
|
if( zSql ){
|
|
rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
|
|
appStmt[i], 0);
|
|
@@ -168600,6 +182583,36 @@
|
|
}
|
|
sqlite3_free(zSql);
|
|
}
|
|
+ if( pRtree->nAux ){
|
|
+ pRtree->zReadAuxSql = sqlite3_mprintf(
|
|
+ "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
|
|
+ zDb, zPrefix);
|
|
+ if( pRtree->zReadAuxSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ sqlite3_str *p = sqlite3_str_new(db);
|
|
+ int ii;
|
|
+ char *zSql;
|
|
+ sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
|
|
+ for(ii=0; ii<pRtree->nAux; ii++){
|
|
+ if( ii ) sqlite3_str_append(p, ",", 1);
|
|
+ if( ii<pRtree->nAuxNotNull ){
|
|
+ sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii);
|
|
+ }else{
|
|
+ sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_str_appendf(p, " WHERE rowid=?1");
|
|
+ zSql = sqlite3_str_finish(p);
|
|
+ if( zSql==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
|
|
+ &pRtree->pWriteAux, 0);
|
|
+ sqlite3_free(zSql);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
|
|
return rc;
|
|
}
|
|
@@ -168670,7 +182683,7 @@
|
|
if( rc!=SQLITE_OK ){
|
|
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
}else if( pRtree->iNodeSize<(512-64) ){
|
|
- rc = SQLITE_CORRUPT;
|
|
+ rc = SQLITE_CORRUPT_VTAB;
|
|
*pzErr = sqlite3_mprintf("undersize RTree blobs in \"%q_node\"",
|
|
pRtree->zName);
|
|
}
|
|
@@ -168702,17 +182715,22 @@
|
|
int nDb; /* Length of string argv[1] */
|
|
int nName; /* Length of string argv[2] */
|
|
int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32);
|
|
+ sqlite3_str *pSql;
|
|
+ char *zSql;
|
|
+ int ii = 4;
|
|
+ int iErr;
|
|
|
|
const char *aErrMsg[] = {
|
|
0, /* 0 */
|
|
"Wrong number of columns for an rtree table", /* 1 */
|
|
"Too few columns for an rtree table", /* 2 */
|
|
- "Too many columns for an rtree table" /* 3 */
|
|
+ "Too many columns for an rtree table", /* 3 */
|
|
+ "Auxiliary rtree columns must be last" /* 4 */
|
|
};
|
|
|
|
- int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
|
|
- if( aErrMsg[iErr] ){
|
|
- *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
|
|
+ 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]);
|
|
return SQLITE_ERROR;
|
|
}
|
|
|
|
@@ -168730,53 +182748,73 @@
|
|
pRtree->base.pModule = &rtreeModule;
|
|
pRtree->zDb = (char *)&pRtree[1];
|
|
pRtree->zName = &pRtree->zDb[nDb+1];
|
|
- pRtree->nDim = (u8)((argc-4)/2);
|
|
- pRtree->nDim2 = pRtree->nDim*2;
|
|
- pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
|
pRtree->eCoordType = (u8)eCoordType;
|
|
memcpy(pRtree->zDb, argv[1], nDb);
|
|
memcpy(pRtree->zName, argv[2], nName);
|
|
|
|
- /* Figure out the node size to use. */
|
|
- rc = getNodeSize(db, pRtree, isCreate, pzErr);
|
|
|
|
/* Create/Connect to the underlying relational database schema. If
|
|
** that is successful, call sqlite3_declare_vtab() to configure
|
|
** the r-tree table schema.
|
|
*/
|
|
- if( rc==SQLITE_OK ){
|
|
- if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
|
|
- *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ pSql = sqlite3_str_new(db);
|
|
+ sqlite3_str_appendf(pSql, "CREATE TABLE x(%s", argv[3]);
|
|
+ for(ii=4; ii<argc; ii++){
|
|
+ if( argv[ii][0]=='+' ){
|
|
+ pRtree->nAux++;
|
|
+ sqlite3_str_appendf(pSql, ",%s", argv[ii]+1);
|
|
+ }else if( pRtree->nAux>0 ){
|
|
+ break;
|
|
}else{
|
|
- char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
|
|
- char *zTmp;
|
|
- int ii;
|
|
- for(ii=4; zSql && ii<argc; ii++){
|
|
- zTmp = zSql;
|
|
- zSql = sqlite3_mprintf("%s, %s", zTmp, argv[ii]);
|
|
- sqlite3_free(zTmp);
|
|
- }
|
|
- if( zSql ){
|
|
- zTmp = zSql;
|
|
- zSql = sqlite3_mprintf("%s);", zTmp);
|
|
- sqlite3_free(zTmp);
|
|
- }
|
|
- if( !zSql ){
|
|
- rc = SQLITE_NOMEM;
|
|
- }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
|
|
- *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
- }
|
|
- sqlite3_free(zSql);
|
|
+ pRtree->nDim2++;
|
|
+ sqlite3_str_appendf(pSql, ",%s", argv[ii]);
|
|
}
|
|
}
|
|
-
|
|
- if( rc==SQLITE_OK ){
|
|
- *ppVtab = (sqlite3_vtab *)pRtree;
|
|
+ sqlite3_str_appendf(pSql, ");");
|
|
+ zSql = sqlite3_str_finish(pSql);
|
|
+ if( !zSql ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else if( ii<argc ){
|
|
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[4]);
|
|
+ rc = SQLITE_ERROR;
|
|
+ }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) goto rtreeInit_fail;
|
|
+ pRtree->nDim = pRtree->nDim2/2;
|
|
+ if( pRtree->nDim<1 ){
|
|
+ iErr = 2;
|
|
+ }else if( pRtree->nDim2>RTREE_MAX_DIMENSIONS*2 ){
|
|
+ iErr = 3;
|
|
+ }else if( pRtree->nDim2 % 2 ){
|
|
+ iErr = 1;
|
|
}else{
|
|
- assert( *ppVtab==0 );
|
|
- assert( pRtree->nBusy==1 );
|
|
- rtreeRelease(pRtree);
|
|
+ iErr = 0;
|
|
}
|
|
+ if( iErr ){
|
|
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
|
|
+ goto rtreeInit_fail;
|
|
+ }
|
|
+ pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
|
+
|
|
+ /* Figure out the node size to use. */
|
|
+ rc = getNodeSize(db, pRtree, isCreate, pzErr);
|
|
+ if( rc ) goto rtreeInit_fail;
|
|
+ rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate);
|
|
+ if( rc ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ goto rtreeInit_fail;
|
|
+ }
|
|
+
|
|
+ *ppVtab = (sqlite3_vtab *)pRtree;
|
|
+ return SQLITE_OK;
|
|
+
|
|
+rtreeInit_fail:
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
|
|
+ assert( *ppVtab==0 );
|
|
+ assert( pRtree->nBusy==1 );
|
|
+ rtreeRelease(pRtree);
|
|
return rc;
|
|
}
|
|
|
|
@@ -168865,6 +182903,2279 @@
|
|
}
|
|
|
|
/*
|
|
+** Context object passed between the various routines that make up the
|
|
+** implementation of integrity-check function rtreecheck().
|
|
+*/
|
|
+typedef struct RtreeCheck RtreeCheck;
|
|
+struct RtreeCheck {
|
|
+ sqlite3 *db; /* Database handle */
|
|
+ const char *zDb; /* Database containing rtree table */
|
|
+ const char *zTab; /* Name of rtree table */
|
|
+ int bInt; /* True for rtree_i32 table */
|
|
+ int nDim; /* Number of dimensions for this rtree tbl */
|
|
+ sqlite3_stmt *pGetNode; /* Statement used to retrieve nodes */
|
|
+ sqlite3_stmt *aCheckMapping[2]; /* Statements to query %_parent/%_rowid */
|
|
+ int nLeaf; /* Number of leaf cells in table */
|
|
+ int nNonLeaf; /* Number of non-leaf cells in table */
|
|
+ int rc; /* Return code */
|
|
+ char *zReport; /* Message to report */
|
|
+ int nErr; /* Number of lines in zReport */
|
|
+};
|
|
+
|
|
+#define RTREE_CHECK_MAX_ERROR 100
|
|
+
|
|
+/*
|
|
+** Reset SQL statement pStmt. If the sqlite3_reset() call returns an error,
|
|
+** and RtreeCheck.rc==SQLITE_OK, set RtreeCheck.rc to the error code.
|
|
+*/
|
|
+static void rtreeCheckReset(RtreeCheck *pCheck, sqlite3_stmt *pStmt){
|
|
+ int rc = sqlite3_reset(pStmt);
|
|
+ if( pCheck->rc==SQLITE_OK ) pCheck->rc = rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The second and subsequent arguments to this function are a format string
|
|
+** and printf style arguments. This function formats the string and attempts
|
|
+** to compile it as an SQL statement.
|
|
+**
|
|
+** If successful, a pointer to the new SQL statement is returned. Otherwise,
|
|
+** NULL is returned and an error code left in RtreeCheck.rc.
|
|
+*/
|
|
+static sqlite3_stmt *rtreeCheckPrepare(
|
|
+ RtreeCheck *pCheck, /* RtreeCheck object */
|
|
+ const char *zFmt, ... /* Format string and trailing args */
|
|
+){
|
|
+ va_list ap;
|
|
+ char *z;
|
|
+ sqlite3_stmt *pRet = 0;
|
|
+
|
|
+ va_start(ap, zFmt);
|
|
+ z = sqlite3_vmprintf(zFmt, ap);
|
|
+
|
|
+ if( pCheck->rc==SQLITE_OK ){
|
|
+ if( z==0 ){
|
|
+ pCheck->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ pCheck->rc = sqlite3_prepare_v2(pCheck->db, z, -1, &pRet, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sqlite3_free(z);
|
|
+ va_end(ap);
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** The second and subsequent arguments to this function are a printf()
|
|
+** style format string and arguments. This function formats the string and
|
|
+** appends it to the report being accumuated in pCheck.
|
|
+*/
|
|
+static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
|
|
+ va_list ap;
|
|
+ va_start(ap, zFmt);
|
|
+ if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
|
|
+ char *z = sqlite3_vmprintf(zFmt, ap);
|
|
+ if( z==0 ){
|
|
+ pCheck->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ pCheck->zReport = sqlite3_mprintf("%z%s%z",
|
|
+ pCheck->zReport, (pCheck->zReport ? "\n" : ""), z
|
|
+ );
|
|
+ if( pCheck->zReport==0 ){
|
|
+ pCheck->rc = SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
+ pCheck->nErr++;
|
|
+ }
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is a no-op if there is already an error code stored
|
|
+** in the RtreeCheck object indicated by the first argument. NULL is
|
|
+** returned in this case.
|
|
+**
|
|
+** Otherwise, the contents of rtree table node iNode are loaded from
|
|
+** the database and copied into a buffer obtained from sqlite3_malloc().
|
|
+** If no error occurs, a pointer to the buffer is returned and (*pnNode)
|
|
+** is set to the size of the buffer in bytes.
|
|
+**
|
|
+** Or, if an error does occur, NULL is returned and an error code left
|
|
+** in the RtreeCheck object. The final value of *pnNode is undefined in
|
|
+** this case.
|
|
+*/
|
|
+static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){
|
|
+ u8 *pRet = 0; /* Return value */
|
|
+
|
|
+ assert( pCheck->rc==SQLITE_OK );
|
|
+ if( pCheck->pGetNode==0 ){
|
|
+ pCheck->pGetNode = rtreeCheckPrepare(pCheck,
|
|
+ "SELECT data FROM %Q.'%q_node' WHERE nodeno=?",
|
|
+ pCheck->zDb, pCheck->zTab
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( pCheck->rc==SQLITE_OK ){
|
|
+ sqlite3_bind_int64(pCheck->pGetNode, 1, iNode);
|
|
+ if( sqlite3_step(pCheck->pGetNode)==SQLITE_ROW ){
|
|
+ int nNode = sqlite3_column_bytes(pCheck->pGetNode, 0);
|
|
+ const u8 *pNode = (const u8*)sqlite3_column_blob(pCheck->pGetNode, 0);
|
|
+ pRet = sqlite3_malloc(nNode);
|
|
+ if( pRet==0 ){
|
|
+ pCheck->rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memcpy(pRet, pNode, nNode);
|
|
+ *pnNode = nNode;
|
|
+ }
|
|
+ }
|
|
+ rtreeCheckReset(pCheck, pCheck->pGetNode);
|
|
+ if( pCheck->rc==SQLITE_OK && pRet==0 ){
|
|
+ rtreeCheckAppendMsg(pCheck, "Node %lld missing from database", iNode);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return pRet;
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is used to check that the %_parent (if bLeaf==0) or %_rowid
|
|
+** (if bLeaf==1) table contains a specified entry. The schemas of the
|
|
+** two tables are:
|
|
+**
|
|
+** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER)
|
|
+** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...)
|
|
+**
|
|
+** In both cases, this function checks that there exists an entry with
|
|
+** IPK value iKey and the second column set to iVal.
|
|
+**
|
|
+*/
|
|
+static void rtreeCheckMapping(
|
|
+ RtreeCheck *pCheck, /* RtreeCheck object */
|
|
+ int bLeaf, /* True for a leaf cell, false for interior */
|
|
+ i64 iKey, /* Key for mapping */
|
|
+ i64 iVal /* Expected value for mapping */
|
|
+){
|
|
+ int rc;
|
|
+ sqlite3_stmt *pStmt;
|
|
+ const char *azSql[2] = {
|
|
+ "SELECT parentnode FROM %Q.'%q_parent' WHERE nodeno=?1",
|
|
+ "SELECT nodeno FROM %Q.'%q_rowid' WHERE rowid=?1"
|
|
+ };
|
|
+
|
|
+ assert( bLeaf==0 || bLeaf==1 );
|
|
+ if( pCheck->aCheckMapping[bLeaf]==0 ){
|
|
+ pCheck->aCheckMapping[bLeaf] = rtreeCheckPrepare(pCheck,
|
|
+ azSql[bLeaf], pCheck->zDb, pCheck->zTab
|
|
+ );
|
|
+ }
|
|
+ if( pCheck->rc!=SQLITE_OK ) return;
|
|
+
|
|
+ pStmt = pCheck->aCheckMapping[bLeaf];
|
|
+ sqlite3_bind_int64(pStmt, 1, iKey);
|
|
+ rc = sqlite3_step(pStmt);
|
|
+ if( rc==SQLITE_DONE ){
|
|
+ rtreeCheckAppendMsg(pCheck, "Mapping (%lld -> %lld) missing from %s table",
|
|
+ iKey, iVal, (bLeaf ? "%_rowid" : "%_parent")
|
|
+ );
|
|
+ }else if( rc==SQLITE_ROW ){
|
|
+ i64 ii = sqlite3_column_int64(pStmt, 0);
|
|
+ if( ii!=iVal ){
|
|
+ rtreeCheckAppendMsg(pCheck,
|
|
+ "Found (%lld -> %lld) in %s table, expected (%lld -> %lld)",
|
|
+ iKey, ii, (bLeaf ? "%_rowid" : "%_parent"), iKey, iVal
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ rtreeCheckReset(pCheck, pStmt);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Argument pCell points to an array of coordinates stored on an rtree page.
|
|
+** This function checks that the coordinates are internally consistent (no
|
|
+** x1>x2 conditions) and adds an error message to the RtreeCheck object
|
|
+** if they are not.
|
|
+**
|
|
+** Additionally, if pParent is not NULL, then it is assumed to point to
|
|
+** the array of coordinates on the parent page that bound the page
|
|
+** containing pCell. In this case it is also verified that the two
|
|
+** sets of coordinates are mutually consistent and an error message added
|
|
+** to the RtreeCheck object if they are not.
|
|
+*/
|
|
+static void rtreeCheckCellCoord(
|
|
+ RtreeCheck *pCheck,
|
|
+ i64 iNode, /* Node id to use in error messages */
|
|
+ int iCell, /* Cell number to use in error messages */
|
|
+ u8 *pCell, /* Pointer to cell coordinates */
|
|
+ u8 *pParent /* Pointer to parent coordinates */
|
|
+){
|
|
+ RtreeCoord c1, c2;
|
|
+ RtreeCoord p1, p2;
|
|
+ int i;
|
|
+
|
|
+ for(i=0; i<pCheck->nDim; i++){
|
|
+ readCoord(&pCell[4*2*i], &c1);
|
|
+ readCoord(&pCell[4*(2*i + 1)], &c2);
|
|
+
|
|
+ /* printf("%e, %e\n", c1.u.f, c2.u.f); */
|
|
+ if( pCheck->bInt ? c1.i>c2.i : c1.f>c2.f ){
|
|
+ rtreeCheckAppendMsg(pCheck,
|
|
+ "Dimension %d of cell %d on node %lld is corrupt", i, iCell, iNode
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if( pParent ){
|
|
+ readCoord(&pParent[4*2*i], &p1);
|
|
+ readCoord(&pParent[4*(2*i + 1)], &p2);
|
|
+
|
|
+ if( (pCheck->bInt ? c1.i<p1.i : c1.f<p1.f)
|
|
+ || (pCheck->bInt ? c2.i>p2.i : c2.f>p2.f)
|
|
+ ){
|
|
+ rtreeCheckAppendMsg(pCheck,
|
|
+ "Dimension %d of cell %d on node %lld is corrupt relative to parent"
|
|
+ , i, iCell, iNode
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Run rtreecheck() checks on node iNode, which is at depth iDepth within
|
|
+** the r-tree structure. Argument aParent points to the array of coordinates
|
|
+** that bound node iNode on the parent node.
|
|
+**
|
|
+** If any problems are discovered, an error message is appended to the
|
|
+** report accumulated in the RtreeCheck object.
|
|
+*/
|
|
+static void rtreeCheckNode(
|
|
+ RtreeCheck *pCheck,
|
|
+ int iDepth, /* Depth of iNode (0==leaf) */
|
|
+ u8 *aParent, /* Buffer containing parent coords */
|
|
+ i64 iNode /* Node to check */
|
|
+){
|
|
+ u8 *aNode = 0;
|
|
+ int nNode = 0;
|
|
+
|
|
+ assert( iNode==1 || aParent!=0 );
|
|
+ assert( pCheck->nDim>0 );
|
|
+
|
|
+ aNode = rtreeCheckGetNode(pCheck, iNode, &nNode);
|
|
+ if( aNode ){
|
|
+ if( nNode<4 ){
|
|
+ rtreeCheckAppendMsg(pCheck,
|
|
+ "Node %lld is too small (%d bytes)", iNode, nNode
|
|
+ );
|
|
+ }else{
|
|
+ int nCell; /* Number of cells on page */
|
|
+ int i; /* Used to iterate through cells */
|
|
+ if( aParent==0 ){
|
|
+ iDepth = readInt16(aNode);
|
|
+ if( iDepth>RTREE_MAX_DEPTH ){
|
|
+ rtreeCheckAppendMsg(pCheck, "Rtree depth out of range (%d)", iDepth);
|
|
+ sqlite3_free(aNode);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ nCell = readInt16(&aNode[2]);
|
|
+ if( (4 + nCell*(8 + pCheck->nDim*2*4))>nNode ){
|
|
+ rtreeCheckAppendMsg(pCheck,
|
|
+ "Node %lld is too small for cell count of %d (%d bytes)",
|
|
+ iNode, nCell, nNode
|
|
+ );
|
|
+ }else{
|
|
+ for(i=0; i<nCell; i++){
|
|
+ u8 *pCell = &aNode[4 + i*(8 + pCheck->nDim*2*4)];
|
|
+ i64 iVal = readInt64(pCell);
|
|
+ rtreeCheckCellCoord(pCheck, iNode, i, &pCell[8], aParent);
|
|
+
|
|
+ if( iDepth>0 ){
|
|
+ rtreeCheckMapping(pCheck, 0, iVal, iNode);
|
|
+ rtreeCheckNode(pCheck, iDepth-1, &pCell[8], iVal);
|
|
+ pCheck->nNonLeaf++;
|
|
+ }else{
|
|
+ rtreeCheckMapping(pCheck, 1, iVal, iNode);
|
|
+ pCheck->nLeaf++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(aNode);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** The second argument to this function must be either "_rowid" or
|
|
+** "_parent". This function checks that the number of entries in the
|
|
+** %_rowid or %_parent table is exactly nExpect. If not, it adds
|
|
+** an error message to the report in the RtreeCheck object indicated
|
|
+** by the first argument.
|
|
+*/
|
|
+static void rtreeCheckCount(RtreeCheck *pCheck, const char *zTbl, i64 nExpect){
|
|
+ if( pCheck->rc==SQLITE_OK ){
|
|
+ sqlite3_stmt *pCount;
|
|
+ pCount = rtreeCheckPrepare(pCheck, "SELECT count(*) FROM %Q.'%q%s'",
|
|
+ pCheck->zDb, pCheck->zTab, zTbl
|
|
+ );
|
|
+ if( pCount ){
|
|
+ if( sqlite3_step(pCount)==SQLITE_ROW ){
|
|
+ i64 nActual = sqlite3_column_int64(pCount, 0);
|
|
+ if( nActual!=nExpect ){
|
|
+ rtreeCheckAppendMsg(pCheck, "Wrong number of entries in %%%s table"
|
|
+ " - expected %lld, actual %lld" , zTbl, nExpect, nActual
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ pCheck->rc = sqlite3_finalize(pCount);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function does the bulk of the work for the rtree integrity-check.
|
|
+** It is called by rtreecheck(), which is the SQL function implementation.
|
|
+*/
|
|
+static int rtreeCheckTable(
|
|
+ sqlite3 *db, /* Database handle to access db through */
|
|
+ const char *zDb, /* Name of db ("main", "temp" etc.) */
|
|
+ const char *zTab, /* Name of rtree table to check */
|
|
+ char **pzReport /* OUT: sqlite3_malloc'd report text */
|
|
+){
|
|
+ RtreeCheck check; /* Common context for various routines */
|
|
+ sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
|
|
+ int bEnd = 0; /* True if transaction should be closed */
|
|
+ int nAux = 0; /* Number of extra columns. */
|
|
+
|
|
+ /* Initialize the context object */
|
|
+ memset(&check, 0, sizeof(check));
|
|
+ check.db = db;
|
|
+ check.zDb = zDb;
|
|
+ check.zTab = zTab;
|
|
+
|
|
+ /* If there is not already an open transaction, open one now. This is
|
|
+ ** to ensure that the queries run as part of this integrity-check operate
|
|
+ ** on a consistent snapshot. */
|
|
+ if( sqlite3_get_autocommit(db) ){
|
|
+ check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
|
+ bEnd = 1;
|
|
+ }
|
|
+
|
|
+ /* Find the number of auxiliary columns */
|
|
+ if( check.rc==SQLITE_OK ){
|
|
+ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
|
|
+ if( pStmt ){
|
|
+ nAux = sqlite3_column_count(pStmt) - 2;
|
|
+ sqlite3_finalize(pStmt);
|
|
+ }
|
|
+ check.rc = SQLITE_OK;
|
|
+ }
|
|
+
|
|
+ /* Find number of dimensions in the rtree table. */
|
|
+ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
|
|
+ if( pStmt ){
|
|
+ int rc;
|
|
+ check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;
|
|
+ if( check.nDim<1 ){
|
|
+ rtreeCheckAppendMsg(&check, "Schema corrupt or not an rtree");
|
|
+ }else if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
|
+ check.bInt = (sqlite3_column_type(pStmt, 1)==SQLITE_INTEGER);
|
|
+ }
|
|
+ rc = sqlite3_finalize(pStmt);
|
|
+ if( rc!=SQLITE_CORRUPT ) check.rc = rc;
|
|
+ }
|
|
+
|
|
+ /* Do the actual integrity-check */
|
|
+ if( check.nDim>=1 ){
|
|
+ if( check.rc==SQLITE_OK ){
|
|
+ rtreeCheckNode(&check, 0, 0, 1);
|
|
+ }
|
|
+ rtreeCheckCount(&check, "_rowid", check.nLeaf);
|
|
+ rtreeCheckCount(&check, "_parent", check.nNonLeaf);
|
|
+ }
|
|
+
|
|
+ /* Finalize SQL statements used by the integrity-check */
|
|
+ sqlite3_finalize(check.pGetNode);
|
|
+ sqlite3_finalize(check.aCheckMapping[0]);
|
|
+ sqlite3_finalize(check.aCheckMapping[1]);
|
|
+
|
|
+ /* If one was opened, close the transaction */
|
|
+ if( bEnd ){
|
|
+ int rc = sqlite3_exec(db, "END", 0, 0, 0);
|
|
+ if( check.rc==SQLITE_OK ) check.rc = rc;
|
|
+ }
|
|
+ *pzReport = check.zReport;
|
|
+ return check.rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Usage:
|
|
+**
|
|
+** rtreecheck(<rtree-table>);
|
|
+** rtreecheck(<database>, <rtree-table>);
|
|
+**
|
|
+** Invoking this SQL function runs an integrity-check on the named rtree
|
|
+** table. The integrity-check verifies the following:
|
|
+**
|
|
+** 1. For each cell in the r-tree structure (%_node table), that:
|
|
+**
|
|
+** a) for each dimension, (coord1 <= coord2).
|
|
+**
|
|
+** b) unless the cell is on the root node, that the cell is bounded
|
|
+** by the parent cell on the parent node.
|
|
+**
|
|
+** c) for leaf nodes, that there is an entry in the %_rowid
|
|
+** table corresponding to the cell's rowid value that
|
|
+** points to the correct node.
|
|
+**
|
|
+** d) for cells on non-leaf nodes, that there is an entry in the
|
|
+** %_parent table mapping from the cell's child node to the
|
|
+** node that it resides on.
|
|
+**
|
|
+** 2. That there are the same number of entries in the %_rowid table
|
|
+** as there are leaf cells in the r-tree structure, and that there
|
|
+** is a leaf cell that corresponds to each entry in the %_rowid table.
|
|
+**
|
|
+** 3. That there are the same number of entries in the %_parent table
|
|
+** as there are non-leaf cells in the r-tree structure, and that
|
|
+** there is a non-leaf cell that corresponds to each entry in the
|
|
+** %_parent table.
|
|
+*/
|
|
+static void rtreecheck(
|
|
+ sqlite3_context *ctx,
|
|
+ int nArg,
|
|
+ sqlite3_value **apArg
|
|
+){
|
|
+ if( nArg!=1 && nArg!=2 ){
|
|
+ sqlite3_result_error(ctx,
|
|
+ "wrong number of arguments to function rtreecheck()", -1
|
|
+ );
|
|
+ }else{
|
|
+ int rc;
|
|
+ char *zReport = 0;
|
|
+ const char *zDb = (const char*)sqlite3_value_text(apArg[0]);
|
|
+ const char *zTab;
|
|
+ if( nArg==1 ){
|
|
+ zTab = zDb;
|
|
+ zDb = "main";
|
|
+ }else{
|
|
+ zTab = (const char*)sqlite3_value_text(apArg[1]);
|
|
+ }
|
|
+ rc = rtreeCheckTable(sqlite3_context_db_handle(ctx), zDb, zTab, &zReport);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_result_text(ctx, zReport ? zReport : "ok", -1, SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ sqlite3_result_error_code(ctx, rc);
|
|
+ }
|
|
+ sqlite3_free(zReport);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Conditionally include the geopoly code */
|
|
+#ifdef SQLITE_ENABLE_GEOPOLY
|
|
+/************** Include geopoly.c in the middle of rtree.c *******************/
|
|
+/************** Begin file geopoly.c *****************************************/
|
|
+/*
|
|
+** 2018-05-25
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file implements an alternative R-Tree virtual table that
|
|
+** uses polygons to express the boundaries of 2-dimensional objects.
|
|
+**
|
|
+** This file is #include-ed onto the end of "rtree.c" so that it has
|
|
+** access to all of the R-Tree internals.
|
|
+*/
|
|
+/* #include <stdlib.h> */
|
|
+
|
|
+/* Enable -DGEOPOLY_ENABLE_DEBUG for debugging facilities */
|
|
+#ifdef GEOPOLY_ENABLE_DEBUG
|
|
+ static int geo_debug = 0;
|
|
+# define GEODEBUG(X) if(geo_debug)printf X
|
|
+#else
|
|
+# define GEODEBUG(X)
|
|
+#endif
|
|
+
|
|
+#ifndef JSON_NULL /* The following stuff repeats things found in json1 */
|
|
+/*
|
|
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
|
|
+** to pass signed char values.
|
|
+*/
|
|
+#ifdef sqlite3Isdigit
|
|
+ /* Use the SQLite core versions if this routine is part of the
|
|
+ ** SQLite amalgamation */
|
|
+# define safe_isdigit(x) sqlite3Isdigit(x)
|
|
+# define safe_isalnum(x) sqlite3Isalnum(x)
|
|
+# define safe_isxdigit(x) sqlite3Isxdigit(x)
|
|
+#else
|
|
+ /* Use the standard library for separate compilation */
|
|
+#include <ctype.h> /* amalgamator: keep */
|
|
+# define safe_isdigit(x) isdigit((unsigned char)(x))
|
|
+# define safe_isalnum(x) isalnum((unsigned char)(x))
|
|
+# define safe_isxdigit(x) isxdigit((unsigned char)(x))
|
|
+#endif
|
|
+
|
|
+/*
|
|
+** Growing our own isspace() routine this way is twice as fast as
|
|
+** the library isspace() function.
|
|
+*/
|
|
+static const char geopolyIsSpace[] = {
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+};
|
|
+#define safe_isspace(x) (geopolyIsSpace[(unsigned char)x])
|
|
+#endif /* JSON NULL - back to original code */
|
|
+
|
|
+/* Compiler and version */
|
|
+#ifndef GCC_VERSION
|
|
+#if defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
|
|
+# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
|
|
+#else
|
|
+# define GCC_VERSION 0
|
|
+#endif
|
|
+#endif
|
|
+#ifndef MSVC_VERSION
|
|
+#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
|
|
+# define MSVC_VERSION _MSC_VER
|
|
+#else
|
|
+# define MSVC_VERSION 0
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+/* Datatype for coordinates
|
|
+*/
|
|
+typedef float GeoCoord;
|
|
+
|
|
+/*
|
|
+** Internal representation of a polygon.
|
|
+**
|
|
+** The polygon consists of a sequence of vertexes. There is a line
|
|
+** segment between each pair of vertexes, and one final segment from
|
|
+** the last vertex back to the first. (This differs from the GeoJSON
|
|
+** standard in which the final vertex is a repeat of the first.)
|
|
+**
|
|
+** The polygon follows the right-hand rule. The area to the right of
|
|
+** each segment is "outside" and the area to the left is "inside".
|
|
+**
|
|
+** The on-disk representation consists of a 4-byte header followed by
|
|
+** the values. The 4-byte header is:
|
|
+**
|
|
+** encoding (1 byte) 0=big-endian, 1=little-endian
|
|
+** nvertex (3 bytes) Number of vertexes as a big-endian integer
|
|
+**
|
|
+** Enough space is allocated for 4 coordinates, to work around over-zealous
|
|
+** warnings coming from some compiler (notably, clang). In reality, the size
|
|
+** of each GeoPoly memory allocate is adjusted as necessary so that the
|
|
+** GeoPoly.a[] array at the end is the appropriate size.
|
|
+*/
|
|
+typedef struct GeoPoly GeoPoly;
|
|
+struct GeoPoly {
|
|
+ int nVertex; /* Number of vertexes */
|
|
+ unsigned char hdr[4]; /* Header for on-disk representation */
|
|
+ GeoCoord a[8]; /* 2*nVertex values. X (longitude) first, then Y */
|
|
+};
|
|
+
|
|
+/* The size of a memory allocation needed for a GeoPoly object sufficient
|
|
+** to hold N coordinate pairs.
|
|
+*/
|
|
+#define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4))
|
|
+
|
|
+/*
|
|
+** State of a parse of a GeoJSON input.
|
|
+*/
|
|
+typedef struct GeoParse GeoParse;
|
|
+struct GeoParse {
|
|
+ const unsigned char *z; /* Unparsed input */
|
|
+ int nVertex; /* Number of vertexes in a[] */
|
|
+ int nAlloc; /* Space allocated to a[] */
|
|
+ int nErr; /* Number of errors encountered */
|
|
+ GeoCoord *a; /* Array of vertexes. From sqlite3_malloc64() */
|
|
+};
|
|
+
|
|
+/* Do a 4-byte byte swap */
|
|
+static void geopolySwab32(unsigned char *a){
|
|
+ unsigned char t = a[0];
|
|
+ a[0] = a[3];
|
|
+ a[3] = t;
|
|
+ t = a[1];
|
|
+ a[1] = a[2];
|
|
+ a[2] = t;
|
|
+}
|
|
+
|
|
+/* Skip whitespace. Return the next non-whitespace character. */
|
|
+static char geopolySkipSpace(GeoParse *p){
|
|
+ while( safe_isspace(p->z[0]) ) p->z++;
|
|
+ return p->z[0];
|
|
+}
|
|
+
|
|
+/* Parse out a number. Write the value into *pVal if pVal!=0.
|
|
+** return non-zero on success and zero if the next token is not a number.
|
|
+*/
|
|
+static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
|
|
+ char c = geopolySkipSpace(p);
|
|
+ const unsigned char *z = p->z;
|
|
+ int j = 0;
|
|
+ int seenDP = 0;
|
|
+ int seenE = 0;
|
|
+ if( c=='-' ){
|
|
+ j = 1;
|
|
+ c = z[j];
|
|
+ }
|
|
+ if( c=='0' && z[j+1]>='0' && z[j+1]<='9' ) return 0;
|
|
+ for(;; j++){
|
|
+ c = z[j];
|
|
+ if( safe_isdigit(c) ) continue;
|
|
+ if( c=='.' ){
|
|
+ if( z[j-1]=='-' ) return 0;
|
|
+ if( seenDP ) return 0;
|
|
+ seenDP = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if( c=='e' || c=='E' ){
|
|
+ if( z[j-1]<'0' ) return 0;
|
|
+ if( seenE ) return -1;
|
|
+ seenDP = seenE = 1;
|
|
+ c = z[j+1];
|
|
+ if( c=='+' || c=='-' ){
|
|
+ j++;
|
|
+ c = z[j+1];
|
|
+ }
|
|
+ if( c<'0' || c>'9' ) return 0;
|
|
+ continue;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if( z[j-1]<'0' ) return 0;
|
|
+ if( pVal ){
|
|
+#ifdef SQLITE_AMALGAMATION
|
|
+ /* The sqlite3AtoF() routine is much much faster than atof(), if it
|
|
+ ** is available */
|
|
+ double r;
|
|
+ (void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
|
|
+ *pVal = r;
|
|
+#else
|
|
+ *pVal = (GeoCoord)atof((const char*)p->z);
|
|
+#endif
|
|
+ }
|
|
+ p->z += j;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+** If the input is a well-formed JSON array of coordinates with at least
|
|
+** four coordinates and where each coordinate is itself a two-value array,
|
|
+** then convert the JSON into a GeoPoly object and return a pointer to
|
|
+** that object.
|
|
+**
|
|
+** If any error occurs, return NULL.
|
|
+*/
|
|
+static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){
|
|
+ GeoParse s;
|
|
+ int rc = SQLITE_OK;
|
|
+ memset(&s, 0, sizeof(s));
|
|
+ s.z = z;
|
|
+ if( geopolySkipSpace(&s)=='[' ){
|
|
+ s.z++;
|
|
+ while( geopolySkipSpace(&s)=='[' ){
|
|
+ int ii = 0;
|
|
+ char c;
|
|
+ s.z++;
|
|
+ if( s.nVertex>=s.nAlloc ){
|
|
+ GeoCoord *aNew;
|
|
+ s.nAlloc = s.nAlloc*2 + 16;
|
|
+ aNew = sqlite3_realloc64(s.a, s.nAlloc*sizeof(GeoCoord)*2 );
|
|
+ if( aNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ s.nErr++;
|
|
+ break;
|
|
+ }
|
|
+ s.a = aNew;
|
|
+ }
|
|
+ while( geopolyParseNumber(&s, ii<=1 ? &s.a[s.nVertex*2+ii] : 0) ){
|
|
+ ii++;
|
|
+ if( ii==2 ) s.nVertex++;
|
|
+ c = geopolySkipSpace(&s);
|
|
+ s.z++;
|
|
+ if( c==',' ) continue;
|
|
+ if( c==']' && ii>=2 ) break;
|
|
+ s.nErr++;
|
|
+ rc = SQLITE_ERROR;
|
|
+ goto parse_json_err;
|
|
+ }
|
|
+ if( geopolySkipSpace(&s)==',' ){
|
|
+ s.z++;
|
|
+ continue;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ if( geopolySkipSpace(&s)==']'
|
|
+ && s.nVertex>=4
|
|
+ && s.a[0]==s.a[s.nVertex*2-2]
|
|
+ && s.a[1]==s.a[s.nVertex*2-1]
|
|
+ && (s.z++, geopolySkipSpace(&s)==0)
|
|
+ ){
|
|
+ GeoPoly *pOut;
|
|
+ int x = 1;
|
|
+ s.nVertex--; /* Remove the redundant vertex at the end */
|
|
+ pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) );
|
|
+ x = 1;
|
|
+ if( pOut==0 ) goto parse_json_err;
|
|
+ pOut->nVertex = s.nVertex;
|
|
+ memcpy(pOut->a, s.a, s.nVertex*2*sizeof(GeoCoord));
|
|
+ pOut->hdr[0] = *(unsigned char*)&x;
|
|
+ pOut->hdr[1] = (s.nVertex>>16)&0xff;
|
|
+ pOut->hdr[2] = (s.nVertex>>8)&0xff;
|
|
+ pOut->hdr[3] = s.nVertex&0xff;
|
|
+ sqlite3_free(s.a);
|
|
+ if( pRc ) *pRc = SQLITE_OK;
|
|
+ return pOut;
|
|
+ }else{
|
|
+ s.nErr++;
|
|
+ rc = SQLITE_ERROR;
|
|
+ }
|
|
+ }
|
|
+parse_json_err:
|
|
+ if( pRc ) *pRc = rc;
|
|
+ sqlite3_free(s.a);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Given a function parameter, try to interpret it as a polygon, either
|
|
+** in the binary format or JSON text. Compute a GeoPoly object and
|
|
+** return a pointer to that object. Or if the input is not a well-formed
|
|
+** polygon, put an error message in sqlite3_context and return NULL.
|
|
+*/
|
|
+static GeoPoly *geopolyFuncParam(
|
|
+ sqlite3_context *pCtx, /* Context for error messages */
|
|
+ sqlite3_value *pVal, /* The value to decode */
|
|
+ int *pRc /* Write error here */
|
|
+){
|
|
+ GeoPoly *p = 0;
|
|
+ int nByte;
|
|
+ if( sqlite3_value_type(pVal)==SQLITE_BLOB
|
|
+ && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
|
|
+ ){
|
|
+ const unsigned char *a = sqlite3_value_blob(pVal);
|
|
+ int nVertex;
|
|
+ nVertex = (a[1]<<16) + (a[2]<<8) + a[3];
|
|
+ if( (a[0]==0 || a[0]==1)
|
|
+ && (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte
|
|
+ ){
|
|
+ p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
|
|
+ if( p==0 ){
|
|
+ if( pRc ) *pRc = SQLITE_NOMEM;
|
|
+ if( pCtx ) sqlite3_result_error_nomem(pCtx);
|
|
+ }else{
|
|
+ int x = 1;
|
|
+ p->nVertex = nVertex;
|
|
+ memcpy(p->hdr, a, nByte);
|
|
+ if( a[0] != *(unsigned char*)&x ){
|
|
+ int ii;
|
|
+ for(ii=0; ii<nVertex*2; ii++){
|
|
+ geopolySwab32((unsigned char*)&p->a[ii]);
|
|
+ }
|
|
+ p->hdr[0] ^= 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( pRc ) *pRc = SQLITE_OK;
|
|
+ return p;
|
|
+ }else if( sqlite3_value_type(pVal)==SQLITE_TEXT ){
|
|
+ const unsigned char *zJson = sqlite3_value_text(pVal);
|
|
+ if( zJson==0 ){
|
|
+ if( pRc ) *pRc = SQLITE_NOMEM;
|
|
+ return 0;
|
|
+ }
|
|
+ return geopolyParseJson(zJson, pRc);
|
|
+ }else{
|
|
+ if( pRc ) *pRc = SQLITE_ERROR;
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the geopoly_blob(X) function.
|
|
+**
|
|
+** If the input is a well-formed Geopoly BLOB or JSON string
|
|
+** then return the BLOB representation of the polygon. Otherwise
|
|
+** return NULL.
|
|
+*/
|
|
+static void geopolyBlobFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
|
|
+ if( p ){
|
|
+ sqlite3_result_blob(context, p->hdr,
|
|
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: geopoly_json(X)
|
|
+**
|
|
+** Interpret X as a polygon and render it as a JSON array
|
|
+** of coordinates. Or, if X is not a valid polygon, return NULL.
|
|
+*/
|
|
+static void geopolyJsonFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
|
|
+ if( p ){
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ sqlite3_str *x = sqlite3_str_new(db);
|
|
+ int i;
|
|
+ sqlite3_str_append(x, "[", 1);
|
|
+ for(i=0; i<p->nVertex; i++){
|
|
+ sqlite3_str_appendf(x, "[%!g,%!g],", p->a[i*2], p->a[i*2+1]);
|
|
+ }
|
|
+ sqlite3_str_appendf(x, "[%!g,%!g]]", p->a[0], p->a[1]);
|
|
+ sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: geopoly_svg(X, ....)
|
|
+**
|
|
+** Interpret X as a polygon and render it as a SVG <polyline>.
|
|
+** Additional arguments are added as attributes to the <polyline>.
|
|
+*/
|
|
+static void geopolySvgFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
|
|
+ if( p ){
|
|
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
|
+ sqlite3_str *x = sqlite3_str_new(db);
|
|
+ int i;
|
|
+ char cSep = '\'';
|
|
+ sqlite3_str_appendf(x, "<polyline points=");
|
|
+ for(i=0; i<p->nVertex; i++){
|
|
+ sqlite3_str_appendf(x, "%c%g,%g", cSep, p->a[i*2], p->a[i*2+1]);
|
|
+ cSep = ' ';
|
|
+ }
|
|
+ sqlite3_str_appendf(x, " %g,%g'", p->a[0], p->a[1]);
|
|
+ for(i=1; i<argc; i++){
|
|
+ const char *z = (const char*)sqlite3_value_text(argv[i]);
|
|
+ if( z && z[0] ){
|
|
+ sqlite3_str_appendf(x, " %s", z);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_str_appendf(x, "></polyline>");
|
|
+ sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL Function: geopoly_xform(poly, A, B, C, D, E, F)
|
|
+**
|
|
+** Transform and/or translate a polygon as follows:
|
|
+**
|
|
+** x1 = A*x0 + B*y0 + E
|
|
+** y1 = C*x0 + D*y0 + F
|
|
+**
|
|
+** For a translation:
|
|
+**
|
|
+** geopoly_xform(poly, 1, 0, 0, 1, x-offset, y-offset)
|
|
+**
|
|
+** Rotate by R around the point (0,0):
|
|
+**
|
|
+** geopoly_xform(poly, cos(R), sin(R), -sin(R), cos(R), 0, 0)
|
|
+*/
|
|
+static void geopolyXformFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
|
|
+ double A = sqlite3_value_double(argv[1]);
|
|
+ double B = sqlite3_value_double(argv[2]);
|
|
+ double C = sqlite3_value_double(argv[3]);
|
|
+ double D = sqlite3_value_double(argv[4]);
|
|
+ double E = sqlite3_value_double(argv[5]);
|
|
+ double F = sqlite3_value_double(argv[6]);
|
|
+ GeoCoord x1, y1, x0, y0;
|
|
+ int ii;
|
|
+ if( p ){
|
|
+ for(ii=0; ii<p->nVertex; ii++){
|
|
+ x0 = p->a[ii*2];
|
|
+ y0 = p->a[ii*2+1];
|
|
+ x1 = (GeoCoord)(A*x0 + B*y0 + E);
|
|
+ y1 = (GeoCoord)(C*x0 + D*y0 + F);
|
|
+ p->a[ii*2] = x1;
|
|
+ p->a[ii*2+1] = y1;
|
|
+ }
|
|
+ sqlite3_result_blob(context, p->hdr,
|
|
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Compute the area enclosed by the polygon.
|
|
+**
|
|
+** This routine can also be used to detect polygons that rotate in
|
|
+** the wrong direction. Polygons are suppose to be counter-clockwise (CCW).
|
|
+** This routine returns a negative value for clockwise (CW) polygons.
|
|
+*/
|
|
+static double geopolyArea(GeoPoly *p){
|
|
+ double rArea = 0.0;
|
|
+ int ii;
|
|
+ for(ii=0; ii<p->nVertex-1; ii++){
|
|
+ rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */
|
|
+ * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */
|
|
+ * 0.5;
|
|
+ }
|
|
+ rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */
|
|
+ * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */
|
|
+ * 0.5;
|
|
+ return rArea;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the geopoly_area(X) function.
|
|
+**
|
|
+** If the input is a well-formed Geopoly BLOB then return the area
|
|
+** enclosed by the polygon. If the polygon circulates clockwise instead
|
|
+** of counterclockwise (as it should) then return the negative of the
|
|
+** enclosed area. Otherwise return NULL.
|
|
+*/
|
|
+static void geopolyAreaFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
|
|
+ if( p ){
|
|
+ sqlite3_result_double(context, geopolyArea(p));
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the geopoly_ccw(X) function.
|
|
+**
|
|
+** If the rotation of polygon X is clockwise (incorrect) instead of
|
|
+** counter-clockwise (the correct winding order according to RFC7946)
|
|
+** then reverse the order of the vertexes in polygon X.
|
|
+**
|
|
+** In other words, this routine returns a CCW polygon regardless of the
|
|
+** winding order of its input.
|
|
+**
|
|
+** Use this routine to sanitize historical inputs that that sometimes
|
|
+** contain polygons that wind in the wrong direction.
|
|
+*/
|
|
+static void geopolyCcwFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
|
|
+ if( p ){
|
|
+ if( geopolyArea(p)<0.0 ){
|
|
+ int ii, jj;
|
|
+ for(ii=2, jj=p->nVertex*2 - 2; ii<jj; ii+=2, jj-=2){
|
|
+ GeoCoord t = p->a[ii];
|
|
+ p->a[ii] = p->a[jj];
|
|
+ p->a[jj] = t;
|
|
+ t = p->a[ii+1];
|
|
+ p->a[ii+1] = p->a[jj+1];
|
|
+ p->a[jj+1] = t;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_blob(context, p->hdr,
|
|
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+#define GEOPOLY_PI 3.1415926535897932385
|
|
+
|
|
+/* Fast approximation for sine(X) for X between -0.5*pi and 2*pi
|
|
+*/
|
|
+static double geopolySine(double r){
|
|
+ assert( r>=-0.5*GEOPOLY_PI && r<=2.0*GEOPOLY_PI );
|
|
+ if( r>=1.5*GEOPOLY_PI ){
|
|
+ r -= 2.0*GEOPOLY_PI;
|
|
+ }
|
|
+ if( r>=0.5*GEOPOLY_PI ){
|
|
+ return -geopolySine(r-GEOPOLY_PI);
|
|
+ }else{
|
|
+ double r2 = r*r;
|
|
+ double r3 = r2*r;
|
|
+ double r5 = r3*r2;
|
|
+ return 0.9996949*r - 0.1656700*r3 + 0.0075134*r5;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** Function: geopoly_regular(X,Y,R,N)
|
|
+**
|
|
+** Construct a simple, convex, regular polygon centered at X, Y
|
|
+** with circumradius R and with N sides.
|
|
+*/
|
|
+static void geopolyRegularFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ double x = sqlite3_value_double(argv[0]);
|
|
+ double y = sqlite3_value_double(argv[1]);
|
|
+ double r = sqlite3_value_double(argv[2]);
|
|
+ int n = sqlite3_value_int(argv[3]);
|
|
+ int i;
|
|
+ GeoPoly *p;
|
|
+
|
|
+ if( n<3 || r<=0.0 ) return;
|
|
+ if( n>1000 ) n = 1000;
|
|
+ p = sqlite3_malloc64( sizeof(*p) + (n-1)*2*sizeof(GeoCoord) );
|
|
+ if( p==0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ return;
|
|
+ }
|
|
+ i = 1;
|
|
+ p->hdr[0] = *(unsigned char*)&i;
|
|
+ p->hdr[1] = 0;
|
|
+ p->hdr[2] = (n>>8)&0xff;
|
|
+ p->hdr[3] = n&0xff;
|
|
+ for(i=0; i<n; i++){
|
|
+ double rAngle = 2.0*GEOPOLY_PI*i/n;
|
|
+ p->a[i*2] = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI);
|
|
+ p->a[i*2+1] = y + r*geopolySine(rAngle);
|
|
+ }
|
|
+ sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(p);
|
|
+}
|
|
+
|
|
+/*
|
|
+** If pPoly is a polygon, compute its bounding box. Then:
|
|
+**
|
|
+** (1) if aCoord!=0 store the bounding box in aCoord, returning NULL
|
|
+** (2) otherwise, compute a GeoPoly for the bounding box and return the
|
|
+** new GeoPoly
|
|
+**
|
|
+** If pPoly is NULL but aCoord is not NULL, then compute a new GeoPoly from
|
|
+** the bounding box in aCoord and return a pointer to that GeoPoly.
|
|
+*/
|
|
+static GeoPoly *geopolyBBox(
|
|
+ sqlite3_context *context, /* For recording the error */
|
|
+ sqlite3_value *pPoly, /* The polygon */
|
|
+ RtreeCoord *aCoord, /* Results here */
|
|
+ int *pRc /* Error code here */
|
|
+){
|
|
+ GeoPoly *pOut = 0;
|
|
+ GeoPoly *p;
|
|
+ float mnX, mxX, mnY, mxY;
|
|
+ if( pPoly==0 && aCoord!=0 ){
|
|
+ p = 0;
|
|
+ mnX = aCoord[0].f;
|
|
+ mxX = aCoord[1].f;
|
|
+ mnY = aCoord[2].f;
|
|
+ mxY = aCoord[3].f;
|
|
+ goto geopolyBboxFill;
|
|
+ }else{
|
|
+ p = geopolyFuncParam(context, pPoly, pRc);
|
|
+ }
|
|
+ if( p ){
|
|
+ int ii;
|
|
+ mnX = mxX = p->a[0];
|
|
+ mnY = mxY = p->a[1];
|
|
+ for(ii=1; ii<p->nVertex; ii++){
|
|
+ double r = p->a[ii*2];
|
|
+ if( r<mnX ) mnX = (float)r;
|
|
+ else if( r>mxX ) mxX = (float)r;
|
|
+ r = p->a[ii*2+1];
|
|
+ if( r<mnY ) mnY = (float)r;
|
|
+ else if( r>mxY ) mxY = (float)r;
|
|
+ }
|
|
+ if( pRc ) *pRc = SQLITE_OK;
|
|
+ if( aCoord==0 ){
|
|
+ geopolyBboxFill:
|
|
+ pOut = sqlite3_realloc(p, GEOPOLY_SZ(4));
|
|
+ if( pOut==0 ){
|
|
+ sqlite3_free(p);
|
|
+ if( context ) sqlite3_result_error_nomem(context);
|
|
+ if( pRc ) *pRc = SQLITE_NOMEM;
|
|
+ return 0;
|
|
+ }
|
|
+ pOut->nVertex = 4;
|
|
+ ii = 1;
|
|
+ pOut->hdr[0] = *(unsigned char*)ⅈ
|
|
+ pOut->hdr[1] = 0;
|
|
+ pOut->hdr[2] = 0;
|
|
+ pOut->hdr[3] = 4;
|
|
+ pOut->a[0] = mnX;
|
|
+ pOut->a[1] = mnY;
|
|
+ pOut->a[2] = mxX;
|
|
+ pOut->a[3] = mnY;
|
|
+ pOut->a[4] = mxX;
|
|
+ pOut->a[5] = mxY;
|
|
+ pOut->a[6] = mnX;
|
|
+ pOut->a[7] = mxY;
|
|
+ }else{
|
|
+ sqlite3_free(p);
|
|
+ aCoord[0].f = mnX;
|
|
+ aCoord[1].f = mxX;
|
|
+ aCoord[2].f = mnY;
|
|
+ aCoord[3].f = mxY;
|
|
+ }
|
|
+ }
|
|
+ return pOut;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Implementation of the geopoly_bbox(X) SQL function.
|
|
+*/
|
|
+static void geopolyBBoxFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p = geopolyBBox(context, argv[0], 0, 0);
|
|
+ if( p ){
|
|
+ sqlite3_result_blob(context, p->hdr,
|
|
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+** State vector for the geopoly_group_bbox() aggregate function.
|
|
+*/
|
|
+typedef struct GeoBBox GeoBBox;
|
|
+struct GeoBBox {
|
|
+ int isInit;
|
|
+ RtreeCoord a[4];
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+** Implementation of the geopoly_group_bbox(X) aggregate SQL function.
|
|
+*/
|
|
+static void geopolyBBoxStep(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ RtreeCoord a[4];
|
|
+ int rc = SQLITE_OK;
|
|
+ (void)geopolyBBox(context, argv[0], a, &rc);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ GeoBBox *pBBox;
|
|
+ pBBox = (GeoBBox*)sqlite3_aggregate_context(context, sizeof(*pBBox));
|
|
+ if( pBBox==0 ) return;
|
|
+ if( pBBox->isInit==0 ){
|
|
+ pBBox->isInit = 1;
|
|
+ memcpy(pBBox->a, a, sizeof(RtreeCoord)*4);
|
|
+ }else{
|
|
+ if( a[0].f < pBBox->a[0].f ) pBBox->a[0] = a[0];
|
|
+ if( a[1].f > pBBox->a[1].f ) pBBox->a[1] = a[1];
|
|
+ if( a[2].f < pBBox->a[2].f ) pBBox->a[2] = a[2];
|
|
+ if( a[3].f > pBBox->a[3].f ) pBBox->a[3] = a[3];
|
|
+ }
|
|
+ }
|
|
+}
|
|
+static void geopolyBBoxFinal(
|
|
+ sqlite3_context *context
|
|
+){
|
|
+ GeoPoly *p;
|
|
+ GeoBBox *pBBox;
|
|
+ pBBox = (GeoBBox*)sqlite3_aggregate_context(context, 0);
|
|
+ if( pBBox==0 ) return;
|
|
+ p = geopolyBBox(context, 0, pBBox->a, 0);
|
|
+ if( p ){
|
|
+ sqlite3_result_blob(context, p->hdr,
|
|
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
|
|
+ sqlite3_free(p);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
|
|
+** Returns:
|
|
+**
|
|
+** +2 x0,y0 is on the line segement
|
|
+**
|
|
+** +1 x0,y0 is beneath line segment
|
|
+**
|
|
+** 0 x0,y0 is not on or beneath the line segment or the line segment
|
|
+** is vertical and x0,y0 is not on the line segment
|
|
+**
|
|
+** The left-most coordinate min(x1,x2) is not considered to be part of
|
|
+** the line segment for the purposes of this analysis.
|
|
+*/
|
|
+static int pointBeneathLine(
|
|
+ double x0, double y0,
|
|
+ double x1, double y1,
|
|
+ double x2, double y2
|
|
+){
|
|
+ double y;
|
|
+ if( x0==x1 && y0==y1 ) return 2;
|
|
+ if( x1<x2 ){
|
|
+ if( x0<=x1 || x0>x2 ) return 0;
|
|
+ }else if( x1>x2 ){
|
|
+ if( x0<=x2 || x0>x1 ) return 0;
|
|
+ }else{
|
|
+ /* Vertical line segment */
|
|
+ if( x0!=x1 ) return 0;
|
|
+ if( y0<y1 && y0<y2 ) return 0;
|
|
+ if( y0>y1 && y0>y2 ) return 0;
|
|
+ return 2;
|
|
+ }
|
|
+ y = y1 + (y2-y1)*(x0-x1)/(x2-x1);
|
|
+ if( y0==y ) return 2;
|
|
+ if( y0<y ) return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: geopoly_contains_point(P,X,Y)
|
|
+**
|
|
+** Return +2 if point X,Y is within polygon P.
|
|
+** Return +1 if point X,Y is on the polygon boundary.
|
|
+** Return 0 if point X,Y is outside the polygon
|
|
+*/
|
|
+static void geopolyContainsPointFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
|
|
+ double x0 = sqlite3_value_double(argv[1]);
|
|
+ double y0 = sqlite3_value_double(argv[2]);
|
|
+ int v = 0;
|
|
+ int cnt = 0;
|
|
+ int ii;
|
|
+ if( p1==0 ) return;
|
|
+ for(ii=0; ii<p1->nVertex-1; ii++){
|
|
+ v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1],
|
|
+ p1->a[ii*2+2],p1->a[ii*2+3]);
|
|
+ if( v==2 ) break;
|
|
+ cnt += v;
|
|
+ }
|
|
+ if( v!=2 ){
|
|
+ v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1],
|
|
+ p1->a[0],p1->a[1]);
|
|
+ }
|
|
+ if( v==2 ){
|
|
+ sqlite3_result_int(context, 1);
|
|
+ }else if( ((v+cnt)&1)==0 ){
|
|
+ sqlite3_result_int(context, 0);
|
|
+ }else{
|
|
+ sqlite3_result_int(context, 2);
|
|
+ }
|
|
+ sqlite3_free(p1);
|
|
+}
|
|
+
|
|
+/* Forward declaration */
|
|
+static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2);
|
|
+
|
|
+/*
|
|
+** SQL function: geopoly_within(P1,P2)
|
|
+**
|
|
+** Return +2 if P1 and P2 are the same polygon
|
|
+** Return +1 if P2 is contained within P1
|
|
+** Return 0 if any part of P2 is on the outside of P1
|
|
+**
|
|
+*/
|
|
+static void geopolyWithinFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
|
|
+ GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
|
|
+ if( p1 && p2 ){
|
|
+ int x = geopolyOverlap(p1, p2);
|
|
+ if( x<0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_int(context, x==2 ? 1 : x==4 ? 2 : 0);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(p1);
|
|
+ sqlite3_free(p2);
|
|
+}
|
|
+
|
|
+/* Objects used by the overlap algorihm. */
|
|
+typedef struct GeoEvent GeoEvent;
|
|
+typedef struct GeoSegment GeoSegment;
|
|
+typedef struct GeoOverlap GeoOverlap;
|
|
+struct GeoEvent {
|
|
+ double x; /* X coordinate at which event occurs */
|
|
+ int eType; /* 0 for ADD, 1 for REMOVE */
|
|
+ GeoSegment *pSeg; /* The segment to be added or removed */
|
|
+ GeoEvent *pNext; /* Next event in the sorted list */
|
|
+};
|
|
+struct GeoSegment {
|
|
+ double C, B; /* y = C*x + B */
|
|
+ double y; /* Current y value */
|
|
+ float y0; /* Initial y value */
|
|
+ unsigned char side; /* 1 for p1, 2 for p2 */
|
|
+ unsigned int idx; /* Which segment within the side */
|
|
+ GeoSegment *pNext; /* Next segment in a list sorted by y */
|
|
+};
|
|
+struct GeoOverlap {
|
|
+ GeoEvent *aEvent; /* Array of all events */
|
|
+ GeoSegment *aSegment; /* Array of all segments */
|
|
+ int nEvent; /* Number of events */
|
|
+ int nSegment; /* Number of segments */
|
|
+};
|
|
+
|
|
+/*
|
|
+** Add a single segment and its associated events.
|
|
+*/
|
|
+static void geopolyAddOneSegment(
|
|
+ GeoOverlap *p,
|
|
+ GeoCoord x0,
|
|
+ GeoCoord y0,
|
|
+ GeoCoord x1,
|
|
+ GeoCoord y1,
|
|
+ unsigned char side,
|
|
+ unsigned int idx
|
|
+){
|
|
+ GeoSegment *pSeg;
|
|
+ GeoEvent *pEvent;
|
|
+ if( x0==x1 ) return; /* Ignore vertical segments */
|
|
+ if( x0>x1 ){
|
|
+ GeoCoord t = x0;
|
|
+ x0 = x1;
|
|
+ x1 = t;
|
|
+ t = y0;
|
|
+ y0 = y1;
|
|
+ y1 = t;
|
|
+ }
|
|
+ pSeg = p->aSegment + p->nSegment;
|
|
+ p->nSegment++;
|
|
+ pSeg->C = (y1-y0)/(x1-x0);
|
|
+ pSeg->B = y1 - x1*pSeg->C;
|
|
+ pSeg->y0 = y0;
|
|
+ pSeg->side = side;
|
|
+ pSeg->idx = idx;
|
|
+ pEvent = p->aEvent + p->nEvent;
|
|
+ p->nEvent++;
|
|
+ pEvent->x = x0;
|
|
+ pEvent->eType = 0;
|
|
+ pEvent->pSeg = pSeg;
|
|
+ pEvent = p->aEvent + p->nEvent;
|
|
+ p->nEvent++;
|
|
+ pEvent->x = x1;
|
|
+ pEvent->eType = 1;
|
|
+ pEvent->pSeg = pSeg;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Insert all segments and events for polygon pPoly.
|
|
+*/
|
|
+static void geopolyAddSegments(
|
|
+ GeoOverlap *p, /* Add segments to this Overlap object */
|
|
+ GeoPoly *pPoly, /* Take all segments from this polygon */
|
|
+ unsigned char side /* The side of pPoly */
|
|
+){
|
|
+ unsigned int i;
|
|
+ GeoCoord *x;
|
|
+ for(i=0; i<(unsigned)pPoly->nVertex-1; i++){
|
|
+ x = pPoly->a + (i*2);
|
|
+ geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i);
|
|
+ }
|
|
+ x = pPoly->a + (i*2);
|
|
+ geopolyAddOneSegment(p, x[0], x[1], pPoly->a[0], pPoly->a[1], side, i);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Merge two lists of sorted events by X coordinate
|
|
+*/
|
|
+static GeoEvent *geopolyEventMerge(GeoEvent *pLeft, GeoEvent *pRight){
|
|
+ GeoEvent head, *pLast;
|
|
+ head.pNext = 0;
|
|
+ pLast = &head;
|
|
+ while( pRight && pLeft ){
|
|
+ if( pRight->x <= pLeft->x ){
|
|
+ pLast->pNext = pRight;
|
|
+ pLast = pRight;
|
|
+ pRight = pRight->pNext;
|
|
+ }else{
|
|
+ pLast->pNext = pLeft;
|
|
+ pLast = pLeft;
|
|
+ pLeft = pLeft->pNext;
|
|
+ }
|
|
+ }
|
|
+ pLast->pNext = pRight ? pRight : pLeft;
|
|
+ return head.pNext;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sort an array of nEvent event objects into a list.
|
|
+*/
|
|
+static GeoEvent *geopolySortEventsByX(GeoEvent *aEvent, int nEvent){
|
|
+ int mx = 0;
|
|
+ int i, j;
|
|
+ GeoEvent *p;
|
|
+ GeoEvent *a[50];
|
|
+ for(i=0; i<nEvent; i++){
|
|
+ p = &aEvent[i];
|
|
+ p->pNext = 0;
|
|
+ for(j=0; j<mx && a[j]; j++){
|
|
+ p = geopolyEventMerge(a[j], p);
|
|
+ a[j] = 0;
|
|
+ }
|
|
+ a[j] = p;
|
|
+ if( j>=mx ) mx = j+1;
|
|
+ }
|
|
+ p = 0;
|
|
+ for(i=0; i<mx; i++){
|
|
+ p = geopolyEventMerge(a[i], p);
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Merge two lists of sorted segments by Y, and then by C.
|
|
+*/
|
|
+static GeoSegment *geopolySegmentMerge(GeoSegment *pLeft, GeoSegment *pRight){
|
|
+ GeoSegment head, *pLast;
|
|
+ head.pNext = 0;
|
|
+ pLast = &head;
|
|
+ while( pRight && pLeft ){
|
|
+ double r = pRight->y - pLeft->y;
|
|
+ if( r==0.0 ) r = pRight->C - pLeft->C;
|
|
+ if( r<0.0 ){
|
|
+ pLast->pNext = pRight;
|
|
+ pLast = pRight;
|
|
+ pRight = pRight->pNext;
|
|
+ }else{
|
|
+ pLast->pNext = pLeft;
|
|
+ pLast = pLeft;
|
|
+ pLeft = pLeft->pNext;
|
|
+ }
|
|
+ }
|
|
+ pLast->pNext = pRight ? pRight : pLeft;
|
|
+ return head.pNext;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Sort a list of GeoSegments in order of increasing Y and in the event of
|
|
+** a tie, increasing C (slope).
|
|
+*/
|
|
+static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){
|
|
+ int mx = 0;
|
|
+ int i;
|
|
+ GeoSegment *p;
|
|
+ GeoSegment *a[50];
|
|
+ while( pList ){
|
|
+ p = pList;
|
|
+ pList = pList->pNext;
|
|
+ p->pNext = 0;
|
|
+ for(i=0; i<mx && a[i]; i++){
|
|
+ p = geopolySegmentMerge(a[i], p);
|
|
+ a[i] = 0;
|
|
+ }
|
|
+ a[i] = p;
|
|
+ if( i>=mx ) mx = i+1;
|
|
+ }
|
|
+ p = 0;
|
|
+ for(i=0; i<mx; i++){
|
|
+ p = geopolySegmentMerge(a[i], p);
|
|
+ }
|
|
+ return p;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Determine the overlap between two polygons
|
|
+*/
|
|
+static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
|
|
+ int nVertex = p1->nVertex + p2->nVertex + 2;
|
|
+ GeoOverlap *p;
|
|
+ int nByte;
|
|
+ GeoEvent *pThisEvent;
|
|
+ double rX;
|
|
+ int rc = 0;
|
|
+ int needSort = 0;
|
|
+ GeoSegment *pActive = 0;
|
|
+ GeoSegment *pSeg;
|
|
+ unsigned char aOverlap[4];
|
|
+
|
|
+ nByte = sizeof(GeoEvent)*nVertex*2
|
|
+ + sizeof(GeoSegment)*nVertex
|
|
+ + sizeof(GeoOverlap);
|
|
+ p = sqlite3_malloc( nByte );
|
|
+ if( p==0 ) return -1;
|
|
+ p->aEvent = (GeoEvent*)&p[1];
|
|
+ p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
|
|
+ p->nEvent = p->nSegment = 0;
|
|
+ geopolyAddSegments(p, p1, 1);
|
|
+ geopolyAddSegments(p, p2, 2);
|
|
+ pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
|
|
+ rX = pThisEvent->x==0.0 ? -1.0 : 0.0;
|
|
+ memset(aOverlap, 0, sizeof(aOverlap));
|
|
+ while( pThisEvent ){
|
|
+ if( pThisEvent->x!=rX ){
|
|
+ GeoSegment *pPrev = 0;
|
|
+ int iMask = 0;
|
|
+ GEODEBUG(("Distinct X: %g\n", pThisEvent->x));
|
|
+ rX = pThisEvent->x;
|
|
+ if( needSort ){
|
|
+ GEODEBUG(("SORT\n"));
|
|
+ pActive = geopolySortSegmentsByYAndC(pActive);
|
|
+ needSort = 0;
|
|
+ }
|
|
+ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
|
|
+ if( pPrev ){
|
|
+ if( pPrev->y!=pSeg->y ){
|
|
+ GEODEBUG(("MASK: %d\n", iMask));
|
|
+ aOverlap[iMask] = 1;
|
|
+ }
|
|
+ }
|
|
+ iMask ^= pSeg->side;
|
|
+ pPrev = pSeg;
|
|
+ }
|
|
+ pPrev = 0;
|
|
+ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
|
|
+ double y = pSeg->C*rX + pSeg->B;
|
|
+ GEODEBUG(("Segment %d.%d %g->%g\n", pSeg->side, pSeg->idx, pSeg->y, y));
|
|
+ pSeg->y = y;
|
|
+ if( pPrev ){
|
|
+ if( pPrev->y>pSeg->y && pPrev->side!=pSeg->side ){
|
|
+ rc = 1;
|
|
+ GEODEBUG(("Crossing: %d.%d and %d.%d\n",
|
|
+ pPrev->side, pPrev->idx,
|
|
+ pSeg->side, pSeg->idx));
|
|
+ goto geopolyOverlapDone;
|
|
+ }else if( pPrev->y!=pSeg->y ){
|
|
+ GEODEBUG(("MASK: %d\n", iMask));
|
|
+ aOverlap[iMask] = 1;
|
|
+ }
|
|
+ }
|
|
+ iMask ^= pSeg->side;
|
|
+ pPrev = pSeg;
|
|
+ }
|
|
+ }
|
|
+ GEODEBUG(("%s %d.%d C=%g B=%g\n",
|
|
+ pThisEvent->eType ? "RM " : "ADD",
|
|
+ pThisEvent->pSeg->side, pThisEvent->pSeg->idx,
|
|
+ pThisEvent->pSeg->C,
|
|
+ pThisEvent->pSeg->B));
|
|
+ if( pThisEvent->eType==0 ){
|
|
+ /* Add a segment */
|
|
+ pSeg = pThisEvent->pSeg;
|
|
+ pSeg->y = pSeg->y0;
|
|
+ pSeg->pNext = pActive;
|
|
+ pActive = pSeg;
|
|
+ needSort = 1;
|
|
+ }else{
|
|
+ /* Remove a segment */
|
|
+ if( pActive==pThisEvent->pSeg ){
|
|
+ pActive = pActive->pNext;
|
|
+ }else{
|
|
+ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
|
|
+ if( pSeg->pNext==pThisEvent->pSeg ){
|
|
+ pSeg->pNext = pSeg->pNext->pNext;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ pThisEvent = pThisEvent->pNext;
|
|
+ }
|
|
+ if( aOverlap[3]==0 ){
|
|
+ rc = 0;
|
|
+ }else if( aOverlap[1]!=0 && aOverlap[2]==0 ){
|
|
+ rc = 3;
|
|
+ }else if( aOverlap[1]==0 && aOverlap[2]!=0 ){
|
|
+ rc = 2;
|
|
+ }else if( aOverlap[1]==0 && aOverlap[2]==0 ){
|
|
+ rc = 4;
|
|
+ }else{
|
|
+ rc = 1;
|
|
+ }
|
|
+
|
|
+geopolyOverlapDone:
|
|
+ sqlite3_free(p);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** SQL function: geopoly_overlap(P1,P2)
|
|
+**
|
|
+** Determine whether or not P1 and P2 overlap. Return value:
|
|
+**
|
|
+** 0 The two polygons are disjoint
|
|
+** 1 They overlap
|
|
+** 2 P1 is completely contained within P2
|
|
+** 3 P2 is completely contained within P1
|
|
+** 4 P1 and P2 are the same polygon
|
|
+** NULL Either P1 or P2 or both are not valid polygons
|
|
+*/
|
|
+static void geopolyOverlapFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
|
|
+ GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
|
|
+ if( p1 && p2 ){
|
|
+ int x = geopolyOverlap(p1, p2);
|
|
+ if( x<0 ){
|
|
+ sqlite3_result_error_nomem(context);
|
|
+ }else{
|
|
+ sqlite3_result_int(context, x);
|
|
+ }
|
|
+ }
|
|
+ sqlite3_free(p1);
|
|
+ sqlite3_free(p2);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Enable or disable debugging output
|
|
+*/
|
|
+static void geopolyDebugFunc(
|
|
+ sqlite3_context *context,
|
|
+ int argc,
|
|
+ sqlite3_value **argv
|
|
+){
|
|
+#ifdef GEOPOLY_ENABLE_DEBUG
|
|
+ geo_debug = sqlite3_value_int(argv[0]);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+** This function is the implementation of both the xConnect and xCreate
|
|
+** methods of the geopoly virtual table.
|
|
+**
|
|
+** argv[0] -> module name
|
|
+** argv[1] -> database name
|
|
+** argv[2] -> table name
|
|
+** argv[...] -> column names...
|
|
+*/
|
|
+static int geopolyInit(
|
|
+ sqlite3 *db, /* Database connection */
|
|
+ void *pAux, /* One of the RTREE_COORD_* constants */
|
|
+ int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */
|
|
+ sqlite3_vtab **ppVtab, /* OUT: New virtual table */
|
|
+ char **pzErr, /* OUT: Error message, if any */
|
|
+ int isCreate /* True for xCreate, false for xConnect */
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ Rtree *pRtree;
|
|
+ int nDb; /* Length of string argv[1] */
|
|
+ int nName; /* Length of string argv[2] */
|
|
+ sqlite3_str *pSql;
|
|
+ char *zSql;
|
|
+ int ii;
|
|
+
|
|
+ sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
|
|
+
|
|
+ /* Allocate the sqlite3_vtab structure */
|
|
+ nDb = (int)strlen(argv[1]);
|
|
+ nName = (int)strlen(argv[2]);
|
|
+ pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
|
|
+ if( !pRtree ){
|
|
+ return SQLITE_NOMEM;
|
|
+ }
|
|
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
|
|
+ pRtree->nBusy = 1;
|
|
+ pRtree->base.pModule = &rtreeModule;
|
|
+ pRtree->zDb = (char *)&pRtree[1];
|
|
+ pRtree->zName = &pRtree->zDb[nDb+1];
|
|
+ pRtree->eCoordType = RTREE_COORD_REAL32;
|
|
+ pRtree->nDim = 2;
|
|
+ pRtree->nDim2 = 4;
|
|
+ memcpy(pRtree->zDb, argv[1], nDb);
|
|
+ memcpy(pRtree->zName, argv[2], nName);
|
|
+
|
|
+
|
|
+ /* Create/Connect to the underlying relational database schema. If
|
|
+ ** that is successful, call sqlite3_declare_vtab() to configure
|
|
+ ** the r-tree table schema.
|
|
+ */
|
|
+ pSql = sqlite3_str_new(db);
|
|
+ sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
|
|
+ pRtree->nAux = 1; /* Add one for _shape */
|
|
+ pRtree->nAuxNotNull = 1; /* The _shape column is always not-null */
|
|
+ for(ii=3; ii<argc; ii++){
|
|
+ pRtree->nAux++;
|
|
+ sqlite3_str_appendf(pSql, ",%s", argv[ii]);
|
|
+ }
|
|
+ sqlite3_str_appendf(pSql, ");");
|
|
+ zSql = sqlite3_str_finish(pSql);
|
|
+ if( !zSql ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ }
|
|
+ sqlite3_free(zSql);
|
|
+ if( rc ) goto geopolyInit_fail;
|
|
+ pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
|
+
|
|
+ /* Figure out the node size to use. */
|
|
+ rc = getNodeSize(db, pRtree, isCreate, pzErr);
|
|
+ if( rc ) goto geopolyInit_fail;
|
|
+ rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate);
|
|
+ if( rc ){
|
|
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
|
+ goto geopolyInit_fail;
|
|
+ }
|
|
+
|
|
+ *ppVtab = (sqlite3_vtab *)pRtree;
|
|
+ return SQLITE_OK;
|
|
+
|
|
+geopolyInit_fail:
|
|
+ if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
|
|
+ assert( *ppVtab==0 );
|
|
+ assert( pRtree->nBusy==1 );
|
|
+ rtreeRelease(pRtree);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** GEOPOLY virtual table module xCreate method.
|
|
+*/
|
|
+static int geopolyCreate(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
|
|
+}
|
|
+
|
|
+/*
|
|
+** GEOPOLY virtual table module xConnect method.
|
|
+*/
|
|
+static int geopolyConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 0);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** GEOPOLY virtual table module xFilter method.
|
|
+**
|
|
+** Query plans:
|
|
+**
|
|
+** 1 rowid lookup
|
|
+** 2 search for objects overlapping the same bounding box
|
|
+** that contains polygon argv[0]
|
|
+** 3 search for objects overlapping the same bounding box
|
|
+** that contains polygon argv[0]
|
|
+** 4 full table scan
|
|
+*/
|
|
+static int geopolyFilter(
|
|
+ sqlite3_vtab_cursor *pVtabCursor, /* The cursor to initialize */
|
|
+ int idxNum, /* Query plan */
|
|
+ const char *idxStr, /* Not Used */
|
|
+ int argc, sqlite3_value **argv /* Parameters to the query plan */
|
|
+){
|
|
+ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
|
|
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
|
|
+ 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;
|
|
+
|
|
+ pCsr->iStrategy = idxNum;
|
|
+ if( idxNum==1 ){
|
|
+ /* Special case - lookup by rowid. */
|
|
+ RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
|
+ RtreeSearchPoint *p; /* Search point for the leaf */
|
|
+ i64 iRowid = sqlite3_value_int64(argv[0]);
|
|
+ i64 iNode = 0;
|
|
+ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
|
+ if( rc==SQLITE_OK && pLeaf!=0 ){
|
|
+ p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
|
|
+ assert( p!=0 ); /* Always returns pCsr->sPoint */
|
|
+ pCsr->aNode[0] = pLeaf;
|
|
+ p->id = iNode;
|
|
+ p->eWithin = PARTLY_WITHIN;
|
|
+ rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
|
|
+ p->iCell = (u8)iCell;
|
|
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
|
|
+ }else{
|
|
+ pCsr->atEOF = 1;
|
|
+ }
|
|
+ }else{
|
|
+ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
|
|
+ ** with the configured constraints.
|
|
+ */
|
|
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
|
+ if( rc==SQLITE_OK && idxNum<=3 ){
|
|
+ RtreeCoord bbox[4];
|
|
+ RtreeConstraint *p;
|
|
+ assert( argc==1 );
|
|
+ geopolyBBox(0, argv[0], bbox, &rc);
|
|
+ if( rc ){
|
|
+ goto geopoly_filter_end;
|
|
+ }
|
|
+ pCsr->aConstraint = p = sqlite3_malloc(sizeof(RtreeConstraint)*4);
|
|
+ pCsr->nConstraint = 4;
|
|
+ if( p==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*4);
|
|
+ memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1));
|
|
+ if( idxNum==2 ){
|
|
+ /* Overlap query */
|
|
+ p->op = 'B';
|
|
+ p->iCoord = 0;
|
|
+ p->u.rValue = bbox[1].f;
|
|
+ p++;
|
|
+ p->op = 'D';
|
|
+ p->iCoord = 1;
|
|
+ p->u.rValue = bbox[0].f;
|
|
+ p++;
|
|
+ p->op = 'B';
|
|
+ p->iCoord = 2;
|
|
+ p->u.rValue = bbox[3].f;
|
|
+ p++;
|
|
+ p->op = 'D';
|
|
+ p->iCoord = 3;
|
|
+ p->u.rValue = bbox[2].f;
|
|
+ }else{
|
|
+ /* Within query */
|
|
+ p->op = 'D';
|
|
+ p->iCoord = 0;
|
|
+ p->u.rValue = bbox[0].f;
|
|
+ p++;
|
|
+ p->op = 'B';
|
|
+ p->iCoord = 1;
|
|
+ p->u.rValue = bbox[1].f;
|
|
+ p++;
|
|
+ p->op = 'D';
|
|
+ p->iCoord = 2;
|
|
+ p->u.rValue = bbox[2].f;
|
|
+ p++;
|
|
+ p->op = 'B';
|
|
+ p->iCoord = 3;
|
|
+ p->u.rValue = bbox[3].f;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ RtreeSearchPoint *pNew;
|
|
+ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ goto geopoly_filter_end;
|
|
+ }
|
|
+ pNew->id = 1;
|
|
+ pNew->iCell = 0;
|
|
+ pNew->eWithin = PARTLY_WITHIN;
|
|
+ assert( pCsr->bPoint==1 );
|
|
+ pCsr->aNode[0] = pRoot;
|
|
+ pRoot = 0;
|
|
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
|
|
+ rc = rtreeStepToLeaf(pCsr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+geopoly_filter_end:
|
|
+ nodeRelease(pRtree, pRoot);
|
|
+ rtreeRelease(pRtree);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Rtree virtual table module xBestIndex method. There are three
|
|
+** table scan strategies to choose from (in order from most to
|
|
+** least desirable):
|
|
+**
|
|
+** idxNum idxStr Strategy
|
|
+** ------------------------------------------------
|
|
+** 1 "rowid" Direct lookup by rowid.
|
|
+** 2 "rtree" R-tree overlap query using geopoly_overlap()
|
|
+** 3 "rtree" R-tree within query using geopoly_within()
|
|
+** 4 "fullscan" full-table scan.
|
|
+** ------------------------------------------------
|
|
+*/
|
|
+static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
|
+ int ii;
|
|
+ int iRowidTerm = -1;
|
|
+ int iFuncTerm = -1;
|
|
+ int idxNum = 0;
|
|
+
|
|
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
|
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
|
|
+ if( !p->usable ) continue;
|
|
+ if( p->iColumn<0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ iRowidTerm = ii;
|
|
+ break;
|
|
+ }
|
|
+ if( p->iColumn==0 && p->op>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){
|
|
+ /* p->op==SQLITE_INDEX_CONSTRAINT_FUNCTION for geopoly_overlap()
|
|
+ ** p->op==(SQLITE_INDEX_CONTRAINT_FUNCTION+1) for geopoly_within().
|
|
+ ** See geopolyFindFunction() */
|
|
+ iFuncTerm = ii;
|
|
+ idxNum = p->op - SQLITE_INDEX_CONSTRAINT_FUNCTION + 2;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( iRowidTerm>=0 ){
|
|
+ pIdxInfo->idxNum = 1;
|
|
+ pIdxInfo->idxStr = "rowid";
|
|
+ pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
|
|
+ pIdxInfo->estimatedCost = 30.0;
|
|
+ pIdxInfo->estimatedRows = 1;
|
|
+ pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ if( iFuncTerm>=0 ){
|
|
+ pIdxInfo->idxNum = idxNum;
|
|
+ pIdxInfo->idxStr = "rtree";
|
|
+ pIdxInfo->aConstraintUsage[iFuncTerm].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[iFuncTerm].omit = 0;
|
|
+ pIdxInfo->estimatedCost = 300.0;
|
|
+ pIdxInfo->estimatedRows = 10;
|
|
+ return SQLITE_OK;
|
|
+ }
|
|
+ pIdxInfo->idxNum = 4;
|
|
+ pIdxInfo->idxStr = "fullscan";
|
|
+ pIdxInfo->estimatedCost = 3000000.0;
|
|
+ pIdxInfo->estimatedRows = 100000;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** GEOPOLY virtual table module xColumn method.
|
|
+*/
|
|
+static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
|
+ Rtree *pRtree = (Rtree *)cur->pVtab;
|
|
+ RtreeCursor *pCsr = (RtreeCursor *)cur;
|
|
+ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
|
|
+ int rc = SQLITE_OK;
|
|
+ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
|
|
+
|
|
+ if( rc ) return rc;
|
|
+ if( p==0 ) return SQLITE_OK;
|
|
+ if( i==0 && sqlite3_vtab_nochange(ctx) ) return SQLITE_OK;
|
|
+ if( i<=pRtree->nAux ){
|
|
+ if( !pCsr->bAuxValid ){
|
|
+ if( pCsr->pReadAux==0 ){
|
|
+ rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0,
|
|
+ &pCsr->pReadAux, 0);
|
|
+ if( rc ) return rc;
|
|
+ }
|
|
+ sqlite3_bind_int64(pCsr->pReadAux, 1,
|
|
+ nodeGetRowid(pRtree, pNode, p->iCell));
|
|
+ rc = sqlite3_step(pCsr->pReadAux);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ pCsr->bAuxValid = 1;
|
|
+ }else{
|
|
+ sqlite3_reset(pCsr->pReadAux);
|
|
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pReadAux, i+2));
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** The xUpdate method for GEOPOLY module virtual tables.
|
|
+**
|
|
+** For DELETE:
|
|
+**
|
|
+** argv[0] = the rowid to be deleted
|
|
+**
|
|
+** For INSERT:
|
|
+**
|
|
+** argv[0] = SQL NULL
|
|
+** argv[1] = rowid to insert, or an SQL NULL to select automatically
|
|
+** argv[2] = _shape column
|
|
+** argv[3] = first application-defined column....
|
|
+**
|
|
+** For UPDATE:
|
|
+**
|
|
+** argv[0] = rowid to modify. Never NULL
|
|
+** argv[1] = rowid after the change. Never NULL
|
|
+** argv[2] = new value for _shape
|
|
+** argv[3] = new value for first application-defined column....
|
|
+*/
|
|
+static int geopolyUpdate(
|
|
+ sqlite3_vtab *pVtab,
|
|
+ int nData,
|
|
+ sqlite3_value **aData,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ Rtree *pRtree = (Rtree *)pVtab;
|
|
+ int rc = SQLITE_OK;
|
|
+ RtreeCell cell; /* New cell to insert if nData>1 */
|
|
+ i64 oldRowid; /* The old rowid */
|
|
+ int oldRowidValid; /* True if oldRowid is valid */
|
|
+ i64 newRowid; /* The new rowid */
|
|
+ int newRowidValid; /* True if newRowid is valid */
|
|
+ int coordChange = 0; /* Change in coordinates */
|
|
+
|
|
+ if( pRtree->nNodeRef ){
|
|
+ /* Unable to write to the btree while another cursor is reading from it,
|
|
+ ** since the write might do a rebalance which would disrupt the read
|
|
+ ** cursor. */
|
|
+ return SQLITE_LOCKED_VTAB;
|
|
+ }
|
|
+ rtreeReference(pRtree);
|
|
+ assert(nData>=1);
|
|
+
|
|
+ oldRowidValid = sqlite3_value_type(aData[0])!=SQLITE_NULL;;
|
|
+ oldRowid = oldRowidValid ? sqlite3_value_int64(aData[0]) : 0;
|
|
+ newRowidValid = nData>1 && sqlite3_value_type(aData[1])!=SQLITE_NULL;
|
|
+ newRowid = newRowidValid ? sqlite3_value_int64(aData[1]) : 0;
|
|
+ cell.iRowid = newRowid;
|
|
+
|
|
+ if( nData>1 /* not a DELETE */
|
|
+ && (!oldRowidValid /* INSERT */
|
|
+ || !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */
|
|
+ || oldRowid!=newRowid) /* Rowid change */
|
|
+ ){
|
|
+ geopolyBBox(0, aData[2], cell.aCoord, &rc);
|
|
+ if( rc ){
|
|
+ if( rc==SQLITE_ERROR ){
|
|
+ pVtab->zErrMsg =
|
|
+ sqlite3_mprintf("_shape does not contain a valid polygon");
|
|
+ }
|
|
+ goto geopoly_update_end;
|
|
+ }
|
|
+ coordChange = 1;
|
|
+
|
|
+ /* If a rowid value was supplied, check if it is already present in
|
|
+ ** the table. If so, the constraint has failed. */
|
|
+ if( newRowidValid && (!oldRowidValid || oldRowid!=newRowid) ){
|
|
+ int steprc;
|
|
+ sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
|
|
+ steprc = sqlite3_step(pRtree->pReadRowid);
|
|
+ rc = sqlite3_reset(pRtree->pReadRowid);
|
|
+ if( SQLITE_ROW==steprc ){
|
|
+ if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
|
|
+ rc = rtreeDeleteRowid(pRtree, cell.iRowid);
|
|
+ }else{
|
|
+ rc = rtreeConstraintError(pRtree, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If aData[0] is not an SQL NULL value, it is the rowid of a
|
|
+ ** record to delete from the r-tree table. The following block does
|
|
+ ** just that.
|
|
+ */
|
|
+ if( rc==SQLITE_OK && (nData==1 || (coordChange && oldRowidValid)) ){
|
|
+ rc = rtreeDeleteRowid(pRtree, oldRowid);
|
|
+ }
|
|
+
|
|
+ /* If the aData[] array contains more than one element, elements
|
|
+ ** (aData[2]..aData[argc-1]) contain a new record to insert into
|
|
+ ** the r-tree structure.
|
|
+ */
|
|
+ if( rc==SQLITE_OK && nData>1 && coordChange ){
|
|
+ /* Insert the new record into the r-tree */
|
|
+ RtreeNode *pLeaf = 0;
|
|
+ if( !newRowidValid ){
|
|
+ rc = rtreeNewRowid(pRtree, &cell.iRowid);
|
|
+ }
|
|
+ *pRowid = cell.iRowid;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ int rc2;
|
|
+ pRtree->iReinsertHeight = -1;
|
|
+ rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
|
|
+ rc2 = nodeRelease(pRtree, pLeaf);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = rc2;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Change the data */
|
|
+ if( rc==SQLITE_OK && nData>1 ){
|
|
+ sqlite3_stmt *pUp = pRtree->pWriteAux;
|
|
+ int jj;
|
|
+ int nChange = 0;
|
|
+ sqlite3_bind_int64(pUp, 1, cell.iRowid);
|
|
+ assert( pRtree->nAux>=1 );
|
|
+ if( sqlite3_value_nochange(aData[2]) ){
|
|
+ sqlite3_bind_null(pUp, 2);
|
|
+ }else{
|
|
+ GeoPoly *p = 0;
|
|
+ if( sqlite3_value_type(aData[2])==SQLITE_TEXT
|
|
+ && (p = geopolyFuncParam(0, aData[2], &rc))!=0
|
|
+ && rc==SQLITE_OK
|
|
+ ){
|
|
+ sqlite3_bind_blob(pUp, 2, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT);
|
|
+ }else{
|
|
+ sqlite3_bind_value(pUp, 2, aData[2]);
|
|
+ }
|
|
+ sqlite3_free(p);
|
|
+ nChange = 1;
|
|
+ }
|
|
+ for(jj=1; jj<pRtree->nAux; jj++){
|
|
+ nChange++;
|
|
+ sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
|
|
+ }
|
|
+ if( nChange ){
|
|
+ sqlite3_step(pUp);
|
|
+ rc = sqlite3_reset(pUp);
|
|
+ }
|
|
+ }
|
|
+
|
|
+geopoly_update_end:
|
|
+ rtreeRelease(pRtree);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Report that geopoly_overlap() is an overloaded function suitable
|
|
+** for use in xBestIndex.
|
|
+*/
|
|
+static int geopolyFindFunction(
|
|
+ sqlite3_vtab *pVtab,
|
|
+ int nArg,
|
|
+ const char *zName,
|
|
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void **ppArg
|
|
+){
|
|
+ if( sqlite3_stricmp(zName, "geopoly_overlap")==0 ){
|
|
+ *pxFunc = geopolyOverlapFunc;
|
|
+ *ppArg = 0;
|
|
+ return SQLITE_INDEX_CONSTRAINT_FUNCTION;
|
|
+ }
|
|
+ if( sqlite3_stricmp(zName, "geopoly_within")==0 ){
|
|
+ *pxFunc = geopolyWithinFunc;
|
|
+ *ppArg = 0;
|
|
+ return SQLITE_INDEX_CONSTRAINT_FUNCTION+1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static sqlite3_module geopolyModule = {
|
|
+ 3, /* iVersion */
|
|
+ geopolyCreate, /* xCreate - create a table */
|
|
+ geopolyConnect, /* xConnect - connect to an existing table */
|
|
+ geopolyBestIndex, /* xBestIndex - Determine search strategy */
|
|
+ rtreeDisconnect, /* xDisconnect - Disconnect from a table */
|
|
+ rtreeDestroy, /* xDestroy - Drop a table */
|
|
+ rtreeOpen, /* xOpen - open a cursor */
|
|
+ rtreeClose, /* xClose - close a cursor */
|
|
+ geopolyFilter, /* xFilter - configure scan constraints */
|
|
+ rtreeNext, /* xNext - advance a cursor */
|
|
+ rtreeEof, /* xEof */
|
|
+ geopolyColumn, /* xColumn - read data */
|
|
+ rtreeRowid, /* xRowid - read data */
|
|
+ geopolyUpdate, /* xUpdate - write data */
|
|
+ rtreeBeginTransaction, /* xBegin - begin transaction */
|
|
+ rtreeEndTransaction, /* xSync - sync transaction */
|
|
+ rtreeEndTransaction, /* xCommit - commit transaction */
|
|
+ rtreeEndTransaction, /* xRollback - rollback transaction */
|
|
+ geopolyFindFunction, /* xFindFunction - function overloading */
|
|
+ rtreeRename, /* xRename - rename the table */
|
|
+ rtreeSavepoint, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ rtreeShadowName /* xShadowName */
|
|
+};
|
|
+
|
|
+static int sqlite3_geopoly_init(sqlite3 *db){
|
|
+ int rc = SQLITE_OK;
|
|
+ static const struct {
|
|
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
|
+ signed char nArg;
|
|
+ unsigned char bPure;
|
|
+ const char *zName;
|
|
+ } aFunc[] = {
|
|
+ { geopolyAreaFunc, 1, 1, "geopoly_area" },
|
|
+ { geopolyBlobFunc, 1, 1, "geopoly_blob" },
|
|
+ { geopolyJsonFunc, 1, 1, "geopoly_json" },
|
|
+ { geopolySvgFunc, -1, 1, "geopoly_svg" },
|
|
+ { geopolyWithinFunc, 2, 1, "geopoly_within" },
|
|
+ { geopolyContainsPointFunc, 3, 1, "geopoly_contains_point" },
|
|
+ { geopolyOverlapFunc, 2, 1, "geopoly_overlap" },
|
|
+ { geopolyDebugFunc, 1, 0, "geopoly_debug" },
|
|
+ { geopolyBBoxFunc, 1, 1, "geopoly_bbox" },
|
|
+ { geopolyXformFunc, 7, 1, "geopoly_xform" },
|
|
+ { geopolyRegularFunc, 4, 1, "geopoly_regular" },
|
|
+ { geopolyCcwFunc, 1, 1, "geopoly_ccw" },
|
|
+ };
|
|
+ static const struct {
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
|
|
+ void (*xFinal)(sqlite3_context*);
|
|
+ const char *zName;
|
|
+ } aAgg[] = {
|
|
+ { geopolyBBoxStep, geopolyBBoxFinal, "geopoly_group_bbox" },
|
|
+ };
|
|
+ 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;
|
|
+ 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);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/************** End of geopoly.c *********************************************/
|
|
+/************** Continuing where we left off in rtree.c **********************/
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Register the r-tree module with database handle db. This creates the
|
|
** virtual table module "rtree" and the debugging/analysis scalar
|
|
** function "rtreenode".
|
|
@@ -168878,6 +185189,9 @@
|
|
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
|
|
}
|
|
if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_create_function(db, "rtreecheck", -1, utf8, 0,rtreecheck, 0,0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
#ifdef SQLITE_RTREE_INT_ONLY
|
|
void *c = (void *)RTREE_COORD_INT32;
|
|
#else
|
|
@@ -168889,6 +185203,11 @@
|
|
void *c = (void *)RTREE_COORD_INT32;
|
|
rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0);
|
|
}
|
|
+#ifdef SQLITE_ENABLE_GEOPOLY
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_geopoly_init(db);
|
|
+ }
|
|
+#endif
|
|
|
|
return rc;
|
|
}
|
|
@@ -169063,7 +185382,9 @@
|
|
** provide case-independent matching.
|
|
*/
|
|
|
|
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
|
+#if !defined(SQLITE_CORE) \
|
|
+ || defined(SQLITE_ENABLE_ICU) \
|
|
+ || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
|
|
|
/* Include ICU headers */
|
|
#include <unicode/utypes.h>
|
|
@@ -169081,6 +185402,26 @@
|
|
#endif
|
|
|
|
/*
|
|
+** This function is called when an ICU function called from within
|
|
+** the implementation of an SQL scalar function returns an error.
|
|
+**
|
|
+** The scalar function context passed as the first argument is
|
|
+** loaded with an error message based on the following two args.
|
|
+*/
|
|
+static void icuFunctionError(
|
|
+ sqlite3_context *pCtx, /* SQLite scalar function context */
|
|
+ const char *zName, /* Name of ICU function that failed */
|
|
+ UErrorCode e /* Error code returned by ICU function */
|
|
+){
|
|
+ char zBuf[128];
|
|
+ sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
|
|
+ zBuf[127] = '\0';
|
|
+ sqlite3_result_error(pCtx, zBuf, -1);
|
|
+}
|
|
+
|
|
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
|
+
|
|
+/*
|
|
** Maximum length (in bytes) of the pattern in a LIKE or GLOB
|
|
** operator.
|
|
*/
|
|
@@ -169137,8 +185478,8 @@
|
|
const uint8_t *zString, /* The UTF-8 string to compare against */
|
|
const UChar32 uEsc /* The escape character */
|
|
){
|
|
- static const int MATCH_ONE = (UChar32)'_';
|
|
- static const int MATCH_ALL = (UChar32)'%';
|
|
+ static const uint32_t MATCH_ONE = (uint32_t)'_';
|
|
+ static const uint32_t MATCH_ALL = (uint32_t)'%';
|
|
|
|
int prevEscape = 0; /* True if the previous character was uEsc */
|
|
|
|
@@ -169145,7 +185486,7 @@
|
|
while( 1 ){
|
|
|
|
/* Read (and consume) the next character from the input pattern. */
|
|
- UChar32 uPattern;
|
|
+ uint32_t uPattern;
|
|
SQLITE_ICU_READ_UTF8(zPattern, uPattern);
|
|
if( uPattern==0 ) break;
|
|
|
|
@@ -169187,16 +185528,16 @@
|
|
if( *zString==0 ) return 0;
|
|
SQLITE_ICU_SKIP_UTF8(zString);
|
|
|
|
- }else if( !prevEscape && uPattern==uEsc){
|
|
+ }else if( !prevEscape && uPattern==(uint32_t)uEsc){
|
|
/* Case 3. */
|
|
prevEscape = 1;
|
|
|
|
}else{
|
|
/* Case 4. */
|
|
- UChar32 uString;
|
|
+ uint32_t uString;
|
|
SQLITE_ICU_READ_UTF8(zString, uString);
|
|
- uString = u_foldCase(uString, U_FOLD_CASE_DEFAULT);
|
|
- uPattern = u_foldCase(uPattern, U_FOLD_CASE_DEFAULT);
|
|
+ uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT);
|
|
+ uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT);
|
|
if( uString!=uPattern ){
|
|
return 0;
|
|
}
|
|
@@ -169260,24 +185601,6 @@
|
|
}
|
|
|
|
/*
|
|
-** This function is called when an ICU function called from within
|
|
-** the implementation of an SQL scalar function returns an error.
|
|
-**
|
|
-** The scalar function context passed as the first argument is
|
|
-** loaded with an error message based on the following two args.
|
|
-*/
|
|
-static void icuFunctionError(
|
|
- sqlite3_context *pCtx, /* SQLite scalar function context */
|
|
- const char *zName, /* Name of ICU function that failed */
|
|
- UErrorCode e /* Error code returned by ICU function */
|
|
-){
|
|
- char zBuf[128];
|
|
- sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
|
|
- zBuf[127] = '\0';
|
|
- sqlite3_result_error(pCtx, zBuf, -1);
|
|
-}
|
|
-
|
|
-/*
|
|
** Function to delete compiled regexp objects. Registered as
|
|
** a destructor function with sqlite3_set_auxdata().
|
|
*/
|
|
@@ -169442,6 +185765,8 @@
|
|
assert( 0 ); /* Unreachable */
|
|
}
|
|
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
|
+
|
|
/*
|
|
** Collation sequence destructor function. The pCtx argument points to
|
|
** a UCollator structure previously allocated using ucol_open().
|
|
@@ -169536,6 +185861,7 @@
|
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
|
} scalars[] = {
|
|
{"icu_load_collation", 2, SQLITE_UTF8, 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},
|
|
@@ -169547,10 +185873,10 @@
|
|
{"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
|
{"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
|
{"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
|
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
|
};
|
|
int rc = SQLITE_OK;
|
|
int i;
|
|
-
|
|
|
|
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
|
const struct IcuScalar *p = &scalars[i];
|
|
@@ -170293,6 +186619,28 @@
|
|
);
|
|
|
|
/*
|
|
+** Configure a limit for the amount of temp space that may be used by
|
|
+** the RBU handle passed as the first argument. The new limit is specified
|
|
+** in bytes by the second parameter. If it is positive, the limit is updated.
|
|
+** If the second parameter to this function is passed zero, then the limit
|
|
+** is removed entirely. If the second parameter is negative, the limit is
|
|
+** not modified (this is useful for querying the current limit).
|
|
+**
|
|
+** In all cases the returned value is the current limit in bytes (zero
|
|
+** indicates unlimited).
|
|
+**
|
|
+** If the temp space limit is exceeded during operation, an SQLITE_FULL
|
|
+** error is returned.
|
|
+*/
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu*, sqlite3_int64);
|
|
+
|
|
+/*
|
|
+** Return the current amount of temp file space, in bytes, currently used by
|
|
+** the RBU handle passed as the only argument.
|
|
+*/
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu*);
|
|
+
|
|
+/*
|
|
** Internally, each RBU connection uses a separate SQLite database
|
|
** connection to access the target and rbu update databases. This
|
|
** API allows the application direct access to these database handles.
|
|
@@ -170418,7 +186766,7 @@
|
|
** table exists but is not correctly populated, the value of the *pnOne
|
|
** output variable during stage 1 is undefined.
|
|
*/
|
|
-SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
|
|
+SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
|
|
|
|
/*
|
|
** Obtain an indication as to the current stage of an RBU update or vacuum.
|
|
@@ -170528,6 +186876,13 @@
|
|
/* Maximum number of prepared UPDATE statements held by this module */
|
|
#define SQLITE_RBU_UPDATE_CACHESIZE 16
|
|
|
|
+/* Delta checksums disabled by default. Compile with -DRBU_ENABLE_DELTA_CKSUM
|
|
+** to enable checksum verification.
|
|
+*/
|
|
+#ifndef RBU_ENABLE_DELTA_CKSUM
|
|
+# define RBU_ENABLE_DELTA_CKSUM 0
|
|
+#endif
|
|
+
|
|
/*
|
|
** Swap two objects of type TYPE.
|
|
*/
|
|
@@ -170578,6 +186933,10 @@
|
|
**
|
|
** RBU_STATE_OALSZ:
|
|
** Valid if STAGE==1. The size in bytes of the *-oal file.
|
|
+**
|
|
+** RBU_STATE_DATATBL:
|
|
+** Only valid if STAGE==1. The RBU database name of the table
|
|
+** currently being read.
|
|
*/
|
|
#define RBU_STATE_STAGE 1
|
|
#define RBU_STATE_TBL 2
|
|
@@ -170588,6 +186947,7 @@
|
|
#define RBU_STATE_COOKIE 7
|
|
#define RBU_STATE_OALSZ 8
|
|
#define RBU_STATE_PHASEONESTEP 9
|
|
+#define RBU_STATE_DATATBL 10
|
|
|
|
#define RBU_STAGE_OAL 1
|
|
#define RBU_STAGE_MOVE 2
|
|
@@ -170630,6 +186990,7 @@
|
|
struct RbuState {
|
|
int eStage;
|
|
char *zTbl;
|
|
+ char *zDataTbl;
|
|
char *zIdx;
|
|
i64 iWalCksum;
|
|
int nRow;
|
|
@@ -170803,6 +187164,8 @@
|
|
int pgsz;
|
|
u8 *aBuf;
|
|
i64 iWalCksum;
|
|
+ i64 szTemp; /* Current size of all temp files in use */
|
|
+ i64 szTempLimit; /* Total size limit for temp files */
|
|
|
|
/* Used in RBU vacuum mode only */
|
|
int nRbu; /* Number of RBU VFS in the stack */
|
|
@@ -170811,17 +187174,27 @@
|
|
|
|
/*
|
|
** An rbu VFS is implemented using an instance of this structure.
|
|
+**
|
|
+** Variable pRbu is only non-NULL for automatically created RBU VFS objects.
|
|
+** It is NULL for RBU VFS objects created explicitly using
|
|
+** sqlite3rbu_create_vfs(). It is used to track the total amount of temp
|
|
+** space used by the RBU handle.
|
|
*/
|
|
struct rbu_vfs {
|
|
sqlite3_vfs base; /* rbu VFS shim methods */
|
|
sqlite3_vfs *pRealVfs; /* Underlying VFS */
|
|
sqlite3_mutex *mutex; /* Mutex to protect pMain */
|
|
- rbu_file *pMain; /* Linked list of main db files */
|
|
+ sqlite3rbu *pRbu; /* Owner RBU object */
|
|
+ rbu_file *pMain; /* List of main db files */
|
|
+ rbu_file *pMainRbu; /* List of main db files with pRbu!=0 */
|
|
};
|
|
|
|
/*
|
|
** Each file opened by an rbu VFS is represented by an instance of
|
|
** the following structure.
|
|
+**
|
|
+** If this is a temporary file (pRbu!=0 && flags&DELETE_ON_CLOSE), variable
|
|
+** "sz" is set to the current size of the database file.
|
|
*/
|
|
struct rbu_file {
|
|
sqlite3_file base; /* sqlite3_file methods */
|
|
@@ -170828,6 +187201,7 @@
|
|
sqlite3_file *pReal; /* Underlying file handle */
|
|
rbu_vfs *pRbuVfs; /* Pointer to the rbu_vfs object */
|
|
sqlite3rbu *pRbu; /* Pointer to rbu object (rbu target only) */
|
|
+ i64 sz; /* Size of file in bytes (temp only) */
|
|
|
|
int openFlags; /* Flags this file was opened with */
|
|
u32 iCookie; /* Cookie value for main db files */
|
|
@@ -170841,6 +187215,7 @@
|
|
const char *zWal; /* Wal filename for this main db file */
|
|
rbu_file *pWalFd; /* Wal file descriptor for this main db */
|
|
rbu_file *pMainNext; /* Next MAIN_DB file */
|
|
+ rbu_file *pMainRbuNext; /* Next MAIN_DB file with pRbu!=0 */
|
|
};
|
|
|
|
/*
|
|
@@ -170890,6 +187265,7 @@
|
|
return v;
|
|
}
|
|
|
|
+#if RBU_ENABLE_DELTA_CKSUM
|
|
/*
|
|
** Compute a 32-bit checksum on the N-byte buffer. Return the result.
|
|
*/
|
|
@@ -170924,6 +187300,7 @@
|
|
}
|
|
return sum3;
|
|
}
|
|
+#endif
|
|
|
|
/*
|
|
** Apply a delta.
|
|
@@ -170954,7 +187331,7 @@
|
|
){
|
|
unsigned int limit;
|
|
unsigned int total = 0;
|
|
-#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
|
|
+#if RBU_ENABLE_DELTA_CKSUM
|
|
char *zOrigOut = zOut;
|
|
#endif
|
|
|
|
@@ -171009,7 +187386,7 @@
|
|
case ';': {
|
|
zDelta++; lenDelta--;
|
|
zOut[0] = 0;
|
|
-#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
|
|
+#if RBU_ENABLE_DELTA_CKSUM
|
|
if( cnt!=rbuDeltaChecksum(zOrigOut, total) ){
|
|
/* ERROR: bad checksum */
|
|
return -1;
|
|
@@ -172217,7 +188594,7 @@
|
|
int iCid = sqlite3_column_int(pXInfo, 1);
|
|
int bDesc = sqlite3_column_int(pXInfo, 3);
|
|
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
|
|
- zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma,
|
|
+ zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma,
|
|
iCid, pIter->azTblType[iCid], zCollate
|
|
);
|
|
zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":"");
|
|
@@ -172278,7 +188655,7 @@
|
|
** "PRIMARY KEY" to the imposter table column declaration. */
|
|
zPk = "PRIMARY KEY ";
|
|
}
|
|
- zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %s%s",
|
|
+ zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s",
|
|
zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl,
|
|
(pIter->abNotNull[iCol] ? " NOT NULL" : "")
|
|
);
|
|
@@ -172679,6 +189056,7 @@
|
|
static void rbuFreeState(RbuState *p){
|
|
if( p ){
|
|
sqlite3_free(p->zTbl);
|
|
+ sqlite3_free(p->zDataTbl);
|
|
sqlite3_free(p->zIdx);
|
|
sqlite3_free(p);
|
|
}
|
|
@@ -172749,6 +189127,10 @@
|
|
pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
|
|
break;
|
|
|
|
+ case RBU_STATE_DATATBL:
|
|
+ pRet->zDataTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
|
|
+ break;
|
|
+
|
|
default:
|
|
rc = SQLITE_CORRUPT;
|
|
break;
|
|
@@ -173523,7 +189905,8 @@
|
|
"(%d, %lld), "
|
|
"(%d, %lld), "
|
|
"(%d, %lld), "
|
|
- "(%d, %lld) ",
|
|
+ "(%d, %lld), "
|
|
+ "(%d, %Q) ",
|
|
p->zStateDb,
|
|
RBU_STATE_STAGE, eStage,
|
|
RBU_STATE_TBL, p->objiter.zTbl,
|
|
@@ -173533,7 +189916,8 @@
|
|
RBU_STATE_CKPT, p->iWalCksum,
|
|
RBU_STATE_COOKIE, (i64)pFd->iCookie,
|
|
RBU_STATE_OALSZ, p->iOalSz,
|
|
- RBU_STATE_PHASEONESTEP, p->nPhaseOneStep
|
|
+ RBU_STATE_PHASEONESTEP, p->nPhaseOneStep,
|
|
+ RBU_STATE_DATATBL, p->objiter.zDataTbl
|
|
)
|
|
);
|
|
assert( pInsert==0 || rc==SQLITE_OK );
|
|
@@ -173789,7 +190173,8 @@
|
|
|
|
while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
|
|
|| rbuStrCompare(pIter->zIdx, pState->zIdx)
|
|
- || rbuStrCompare(pIter->zTbl, pState->zTbl)
|
|
+ || (pState->zDataTbl==0 && rbuStrCompare(pIter->zTbl, pState->zTbl))
|
|
+ || (pState->zDataTbl && rbuStrCompare(pIter->zDataTbl, pState->zDataTbl))
|
|
)){
|
|
rc = rbuObjIterNext(p, pIter);
|
|
}
|
|
@@ -173841,6 +190226,7 @@
|
|
sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd);
|
|
assert( pVfs );
|
|
p->zVfsName = pVfs->zName;
|
|
+ ((rbu_vfs*)pVfs)->pRbu = p;
|
|
}
|
|
}
|
|
|
|
@@ -174213,6 +190599,7 @@
|
|
/* Close the open database handle and VFS object. */
|
|
sqlite3_close(p->dbRbu);
|
|
sqlite3_close(p->dbMain);
|
|
+ assert( p->szTemp==0 );
|
|
rbuDeleteVfs(p);
|
|
sqlite3_free(p->aBuf);
|
|
sqlite3_free(p->aFrame);
|
|
@@ -174400,6 +190787,7 @@
|
|
*/
|
|
|
|
static void rbuUnlockShm(rbu_file *p){
|
|
+ assert( p->openFlags & SQLITE_OPEN_MAIN_DB );
|
|
if( p->pRbu ){
|
|
int (*xShmLock)(sqlite3_file*,int,int,int) = p->pReal->pMethods->xShmLock;
|
|
int i;
|
|
@@ -174413,6 +190801,81 @@
|
|
}
|
|
|
|
/*
|
|
+*/
|
|
+static int rbuUpdateTempSize(rbu_file *pFd, sqlite3_int64 nNew){
|
|
+ sqlite3rbu *pRbu = pFd->pRbu;
|
|
+ i64 nDiff = nNew - pFd->sz;
|
|
+ pRbu->szTemp += nDiff;
|
|
+ pFd->sz = nNew;
|
|
+ assert( pRbu->szTemp>=0 );
|
|
+ if( pRbu->szTempLimit && pRbu->szTemp>pRbu->szTempLimit ) return SQLITE_FULL;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Add an item to the main-db lists, if it is not already present.
|
|
+**
|
|
+** There are two main-db lists. One for all file descriptors, and one
|
|
+** for all file descriptors with rbu_file.pDb!=0. If the argument has
|
|
+** rbu_file.pDb!=0, then it is assumed to already be present on the
|
|
+** main list and is only added to the pDb!=0 list.
|
|
+*/
|
|
+static void rbuMainlistAdd(rbu_file *p){
|
|
+ rbu_vfs *pRbuVfs = p->pRbuVfs;
|
|
+ rbu_file *pIter;
|
|
+ assert( (p->openFlags & SQLITE_OPEN_MAIN_DB) );
|
|
+ sqlite3_mutex_enter(pRbuVfs->mutex);
|
|
+ if( p->pRbu==0 ){
|
|
+ for(pIter=pRbuVfs->pMain; pIter; pIter=pIter->pMainNext);
|
|
+ p->pMainNext = pRbuVfs->pMain;
|
|
+ pRbuVfs->pMain = p;
|
|
+ }else{
|
|
+ for(pIter=pRbuVfs->pMainRbu; pIter && pIter!=p; pIter=pIter->pMainRbuNext){}
|
|
+ if( pIter==0 ){
|
|
+ p->pMainRbuNext = pRbuVfs->pMainRbu;
|
|
+ pRbuVfs->pMainRbu = p;
|
|
+ }
|
|
+ }
|
|
+ sqlite3_mutex_leave(pRbuVfs->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Remove an item from the main-db lists.
|
|
+*/
|
|
+static void rbuMainlistRemove(rbu_file *p){
|
|
+ rbu_file **pp;
|
|
+ sqlite3_mutex_enter(p->pRbuVfs->mutex);
|
|
+ for(pp=&p->pRbuVfs->pMain; *pp && *pp!=p; pp=&((*pp)->pMainNext)){}
|
|
+ if( *pp ) *pp = p->pMainNext;
|
|
+ p->pMainNext = 0;
|
|
+ for(pp=&p->pRbuVfs->pMainRbu; *pp && *pp!=p; pp=&((*pp)->pMainRbuNext)){}
|
|
+ if( *pp ) *pp = p->pMainRbuNext;
|
|
+ p->pMainRbuNext = 0;
|
|
+ sqlite3_mutex_leave(p->pRbuVfs->mutex);
|
|
+}
|
|
+
|
|
+/*
|
|
+** Given that zWal points to a buffer containing a wal file name passed to
|
|
+** either the xOpen() or xAccess() VFS method, search the main-db list for
|
|
+** a file-handle opened by the same database connection on the corresponding
|
|
+** database file.
|
|
+**
|
|
+** If parameter bRbu is true, only search for file-descriptors with
|
|
+** rbu_file.pDb!=0.
|
|
+*/
|
|
+static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal, int bRbu){
|
|
+ rbu_file *pDb;
|
|
+ sqlite3_mutex_enter(pRbuVfs->mutex);
|
|
+ if( bRbu ){
|
|
+ for(pDb=pRbuVfs->pMainRbu; pDb && pDb->zWal!=zWal; pDb=pDb->pMainRbuNext){}
|
|
+ }else{
|
|
+ for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
|
|
+ }
|
|
+ sqlite3_mutex_leave(pRbuVfs->mutex);
|
|
+ return pDb;
|
|
+}
|
|
+
|
|
+/*
|
|
** Close an rbu file.
|
|
*/
|
|
static int rbuVfsClose(sqlite3_file *pFile){
|
|
@@ -174429,14 +190892,14 @@
|
|
sqlite3_free(p->zDel);
|
|
|
|
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
|
- rbu_file **pp;
|
|
- sqlite3_mutex_enter(p->pRbuVfs->mutex);
|
|
- for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext));
|
|
- *pp = p->pMainNext;
|
|
- sqlite3_mutex_leave(p->pRbuVfs->mutex);
|
|
+ rbuMainlistRemove(p);
|
|
rbuUnlockShm(p);
|
|
p->pReal->pMethods->xShmUnmap(p->pReal, 0);
|
|
}
|
|
+ else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
|
|
+ rbuUpdateTempSize(p, 0);
|
|
+ }
|
|
+ assert( p->pMainNext==0 && p->pRbuVfs->pMain!=p );
|
|
|
|
/* Close the underlying file handle */
|
|
rc = p->pReal->pMethods->xClose(p->pReal);
|
|
@@ -174554,11 +191017,19 @@
|
|
assert( p->openFlags & SQLITE_OPEN_MAIN_DB );
|
|
rc = rbuCaptureDbWrite(p->pRbu, iOfst);
|
|
}else{
|
|
- if( pRbu && pRbu->eStage==RBU_STAGE_OAL
|
|
- && (p->openFlags & SQLITE_OPEN_WAL)
|
|
- && iOfst>=pRbu->iOalSz
|
|
- ){
|
|
- pRbu->iOalSz = iAmt + iOfst;
|
|
+ if( pRbu ){
|
|
+ if( pRbu->eStage==RBU_STAGE_OAL
|
|
+ && (p->openFlags & SQLITE_OPEN_WAL)
|
|
+ && iOfst>=pRbu->iOalSz
|
|
+ ){
|
|
+ pRbu->iOalSz = iAmt + iOfst;
|
|
+ }else if( p->openFlags & SQLITE_OPEN_DELETEONCLOSE ){
|
|
+ i64 szNew = iAmt+iOfst;
|
|
+ if( szNew>p->sz ){
|
|
+ rc = rbuUpdateTempSize(p, szNew);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ }
|
|
+ }
|
|
}
|
|
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
|
|
if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
|
|
@@ -174577,6 +191048,10 @@
|
|
*/
|
|
static int rbuVfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
rbu_file *p = (rbu_file*)pFile;
|
|
+ if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
|
|
+ int rc = rbuUpdateTempSize(p, size);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+ }
|
|
return p->pReal->pMethods->xTruncate(p->pReal, size);
|
|
}
|
|
|
|
@@ -174683,6 +191158,9 @@
|
|
}else if( rc==SQLITE_NOTFOUND ){
|
|
pRbu->pTargetFd = p;
|
|
p->pRbu = pRbu;
|
|
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
|
+ rbuMainlistAdd(p);
|
|
+ }
|
|
if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
|
|
rc = SQLITE_OK;
|
|
}
|
|
@@ -174844,20 +191322,6 @@
|
|
return rc;
|
|
}
|
|
|
|
-/*
|
|
-** Given that zWal points to a buffer containing a wal file name passed to
|
|
-** either the xOpen() or xAccess() VFS method, return a pointer to the
|
|
-** file-handle opened by the same database connection on the corresponding
|
|
-** database file.
|
|
-*/
|
|
-static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal){
|
|
- rbu_file *pDb;
|
|
- sqlite3_mutex_enter(pRbuVfs->mutex);
|
|
- for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
|
|
- sqlite3_mutex_leave(pRbuVfs->mutex);
|
|
- return pDb;
|
|
-}
|
|
-
|
|
/*
|
|
** A main database named zName has just been opened. The following
|
|
** function returns a pointer to a buffer owned by SQLite that contains
|
|
@@ -174936,7 +191400,7 @@
|
|
pFd->zWal = rbuMainToWal(zName, flags);
|
|
}
|
|
else if( flags & SQLITE_OPEN_WAL ){
|
|
- rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName);
|
|
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0);
|
|
if( pDb ){
|
|
if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
|
|
/* This call is to open a *-wal file. Intead, open the *-oal. This
|
|
@@ -174966,6 +191430,8 @@
|
|
pDb->pWalFd = pFd;
|
|
}
|
|
}
|
|
+ }else{
|
|
+ pFd->pRbu = pRbuVfs->pRbu;
|
|
}
|
|
|
|
if( oflags & SQLITE_OPEN_MAIN_DB
|
|
@@ -174986,10 +191452,7 @@
|
|
** mutex protected linked list of all such files. */
|
|
pFile->pMethods = &rbuvfs_io_methods;
|
|
if( flags & SQLITE_OPEN_MAIN_DB ){
|
|
- sqlite3_mutex_enter(pRbuVfs->mutex);
|
|
- pFd->pMainNext = pRbuVfs->pMain;
|
|
- pRbuVfs->pMain = pFd;
|
|
- sqlite3_mutex_leave(pRbuVfs->mutex);
|
|
+ rbuMainlistAdd(pFd);
|
|
}
|
|
}else{
|
|
sqlite3_free(pFd->zDel);
|
|
@@ -175037,12 +191500,14 @@
|
|
** file opened instead.
|
|
*/
|
|
if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
|
|
- rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath);
|
|
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath, 1);
|
|
if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
|
|
if( *pResOut ){
|
|
rc = SQLITE_CANTOPEN;
|
|
}else{
|
|
- *pResOut = 1;
|
|
+ sqlite3_int64 sz = 0;
|
|
+ rc = rbuVfsFileSize(&pDb->base, &sz);
|
|
+ *pResOut = (sz>0);
|
|
}
|
|
}
|
|
}
|
|
@@ -175231,7 +191696,21 @@
|
|
return rc;
|
|
}
|
|
|
|
+/*
|
|
+** Configure the aggregate temp file size limit for this RBU handle.
|
|
+*/
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu *pRbu, sqlite3_int64 n){
|
|
+ if( n>=0 ){
|
|
+ pRbu->szTempLimit = n;
|
|
+ }
|
|
+ return pRbu->szTempLimit;
|
|
+}
|
|
|
|
+SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){
|
|
+ return pRbu->szTemp;
|
|
+}
|
|
+
|
|
+
|
|
/**************************************************************************/
|
|
|
|
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
|
|
@@ -175434,8 +191913,6 @@
|
|
static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
|
int i;
|
|
|
|
- pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */
|
|
-
|
|
/* 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
|
|
** lower the cost estimate to encourage the constrained version to be
|
|
@@ -175442,9 +191919,9 @@
|
|
** used.
|
|
*/
|
|
for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
- if( pIdxInfo->aConstraint[i].usable==0 ) continue;
|
|
+ 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;
|
|
- if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
|
|
pIdxInfo->idxNum = 1;
|
|
pIdxInfo->estimatedCost = 1.0;
|
|
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
@@ -175494,7 +191971,7 @@
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
-static void statClearPage(StatPage *p){
|
|
+static void statClearCells(StatPage *p){
|
|
int i;
|
|
if( p->aCell ){
|
|
for(i=0; i<p->nCell; i++){
|
|
@@ -175502,6 +191979,12 @@
|
|
}
|
|
sqlite3_free(p->aCell);
|
|
}
|
|
+ p->nCell = 0;
|
|
+ p->aCell = 0;
|
|
+}
|
|
+
|
|
+static void statClearPage(StatPage *p){
|
|
+ statClearCells(p);
|
|
sqlite3PagerUnref(p->pPg);
|
|
sqlite3_free(p->zPath);
|
|
memset(p, 0, sizeof(StatPage));
|
|
@@ -175564,22 +192047,33 @@
|
|
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
|
|
|
|
p->flags = aHdr[0];
|
|
+ if( p->flags==0x0A || p->flags==0x0D ){
|
|
+ isLeaf = 1;
|
|
+ nHdr = 8;
|
|
+ }else if( p->flags==0x05 || p->flags==0x02 ){
|
|
+ isLeaf = 0;
|
|
+ nHdr = 12;
|
|
+ }else{
|
|
+ goto statPageIsCorrupt;
|
|
+ }
|
|
+ if( p->iPgno==1 ) nHdr += 100;
|
|
p->nCell = get2byte(&aHdr[3]);
|
|
p->nMxPayload = 0;
|
|
+ szPage = sqlite3BtreeGetPageSize(pBt);
|
|
|
|
- isLeaf = (p->flags==0x0A || p->flags==0x0D);
|
|
- nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100;
|
|
-
|
|
nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell;
|
|
nUnused += (int)aHdr[7];
|
|
iOff = get2byte(&aHdr[1]);
|
|
while( iOff ){
|
|
+ int iNext;
|
|
+ if( iOff>=szPage ) goto statPageIsCorrupt;
|
|
nUnused += get2byte(&aData[iOff+2]);
|
|
- iOff = get2byte(&aData[iOff]);
|
|
+ iNext = get2byte(&aData[iOff]);
|
|
+ if( iNext<iOff+4 && iNext>0 ) goto statPageIsCorrupt;
|
|
+ iOff = iNext;
|
|
}
|
|
p->nUnused = nUnused;
|
|
p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
|
|
- szPage = sqlite3BtreeGetPageSize(pBt);
|
|
|
|
if( p->nCell ){
|
|
int i; /* Used to iterate through cells */
|
|
@@ -175596,6 +192090,7 @@
|
|
StatCell *pCell = &p->aCell[i];
|
|
|
|
iOff = get2byte(&aData[nHdr+i*2]);
|
|
+ if( iOff<nHdr || iOff>=szPage ) goto statPageIsCorrupt;
|
|
if( !isLeaf ){
|
|
pCell->iChildPg = sqlite3Get4byte(&aData[iOff]);
|
|
iOff += 4;
|
|
@@ -175612,13 +192107,14 @@
|
|
}
|
|
if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
|
|
getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
|
|
+ if( nLocal<0 ) goto statPageIsCorrupt;
|
|
pCell->nLocal = nLocal;
|
|
- assert( nLocal>=0 );
|
|
assert( nPayload>=(u32)nLocal );
|
|
assert( nLocal<=(nUsable-35) );
|
|
if( nPayload>(u32)nLocal ){
|
|
int j;
|
|
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
|
|
+ if( iOff+nLocal>nUsable ) goto statPageIsCorrupt;
|
|
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
|
|
pCell->nOvfl = nOvfl;
|
|
pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
|
|
@@ -175642,6 +192138,11 @@
|
|
}
|
|
|
|
return SQLITE_OK;
|
|
+
|
|
+statPageIsCorrupt:
|
|
+ p->flags = 0;
|
|
+ statClearCells(p);
|
|
+ return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
@@ -175664,7 +192165,7 @@
|
|
*/
|
|
fd = sqlite3PagerFile(pPager);
|
|
x[0] = pCsr->iPageno;
|
|
- if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
|
+ if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
|
pCsr->iOffset = x[0];
|
|
pCsr->szPage = (int)x[1];
|
|
}
|
|
@@ -175937,6 +192438,7 @@
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
};
|
|
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
|
|
}
|
|
@@ -175945,6 +192447,424 @@
|
|
#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
|
|
|
|
/************** End of dbstat.c **********************************************/
|
|
+/************** Begin file dbpage.c ******************************************/
|
|
+/*
|
|
+** 2017-10-11
|
|
+**
|
|
+** The author disclaims copyright to this source code. In place of
|
|
+** a legal notice, here is a blessing:
|
|
+**
|
|
+** May you do good and not evil.
|
|
+** May you find forgiveness for yourself and forgive others.
|
|
+** May you share freely, never taking more than you give.
|
|
+**
|
|
+******************************************************************************
|
|
+**
|
|
+** This file contains an implementation of the "sqlite_dbpage" virtual table.
|
|
+**
|
|
+** The sqlite_dbpage virtual table is used to read or write whole raw
|
|
+** pages of the database file. The pager interface is used so that
|
|
+** uncommitted changes and changes recorded in the WAL file are correctly
|
|
+** retrieved.
|
|
+**
|
|
+** Usage example:
|
|
+**
|
|
+** SELECT data FROM sqlite_dbpage('aux1') WHERE pgno=123;
|
|
+**
|
|
+** This is an eponymous virtual table so it does not need to be created before
|
|
+** use. The optional argument to the sqlite_dbpage() table name is the
|
|
+** schema for the database file that is to be read. The default schema is
|
|
+** "main".
|
|
+**
|
|
+** The data field of sqlite_dbpage table can be updated. The new
|
|
+** value must be a BLOB which is the correct page size, otherwise the
|
|
+** update fails. Rows may not be deleted or inserted.
|
|
+*/
|
|
+
|
|
+/* #include "sqliteInt.h" ** Requires access to internal data structures ** */
|
|
+#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
|
|
+ && !defined(SQLITE_OMIT_VIRTUALTABLE)
|
|
+
|
|
+typedef struct DbpageTable DbpageTable;
|
|
+typedef struct DbpageCursor DbpageCursor;
|
|
+
|
|
+struct DbpageCursor {
|
|
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
|
|
+ int pgno; /* Current page number */
|
|
+ int mxPgno; /* Last page to visit on this scan */
|
|
+ Pager *pPager; /* Pager being read/written */
|
|
+ DbPage *pPage1; /* Page 1 of the database */
|
|
+ int iDb; /* Index of database to analyze */
|
|
+ int szPage; /* Size of each page in bytes */
|
|
+};
|
|
+
|
|
+struct DbpageTable {
|
|
+ sqlite3_vtab base; /* Base class. Must be first */
|
|
+ sqlite3 *db; /* The database */
|
|
+};
|
|
+
|
|
+/* Columns */
|
|
+#define DBPAGE_COLUMN_PGNO 0
|
|
+#define DBPAGE_COLUMN_DATA 1
|
|
+#define DBPAGE_COLUMN_SCHEMA 2
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+** Connect to or create a dbpagevfs virtual table.
|
|
+*/
|
|
+static int dbpageConnect(
|
|
+ sqlite3 *db,
|
|
+ void *pAux,
|
|
+ int argc, const char *const*argv,
|
|
+ sqlite3_vtab **ppVtab,
|
|
+ char **pzErr
|
|
+){
|
|
+ DbpageTable *pTab = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ rc = sqlite3_declare_vtab(db,
|
|
+ "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
|
|
+ if( rc==SQLITE_OK ){
|
|
+ pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
|
|
+ if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
|
|
+ }
|
|
+
|
|
+ assert( rc==SQLITE_OK || pTab==0 );
|
|
+ if( rc==SQLITE_OK ){
|
|
+ memset(pTab, 0, sizeof(DbpageTable));
|
|
+ pTab->db = db;
|
|
+ }
|
|
+
|
|
+ *ppVtab = (sqlite3_vtab*)pTab;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Disconnect from or destroy a dbpagevfs virtual table.
|
|
+*/
|
|
+static int dbpageDisconnect(sqlite3_vtab *pVtab){
|
|
+ sqlite3_free(pVtab);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** idxNum:
|
|
+**
|
|
+** 0 schema=main, full table scan
|
|
+** 1 schema=main, pgno=?1
|
|
+** 2 schema=?1, full table scan
|
|
+** 3 schema=?1, pgno=?2
|
|
+*/
|
|
+static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
|
+ int i;
|
|
+ int iPlan = 0;
|
|
+
|
|
+ /* If there is a schema= constraint, it must be honored. Report a
|
|
+ ** ridiculously large estimated cost if the schema= constraint is
|
|
+ ** unavailable
|
|
+ */
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
|
+ if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
|
|
+ if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
+ if( !p->usable ){
|
|
+ /* No solution. */
|
|
+ return SQLITE_CONSTRAINT;
|
|
+ }
|
|
+ iPlan = 2;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* If we reach this point, it means that either there is no schema=
|
|
+ ** constraint (in which case we use the "main" schema) or else the
|
|
+ ** schema constraint was accepted. Lower the estimated cost accordingly
|
|
+ */
|
|
+ pIdxInfo->estimatedCost = 1.0e6;
|
|
+
|
|
+ /* Check for constraints against pgno */
|
|
+ for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
|
+ if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
+ pIdxInfo->estimatedRows = 1;
|
|
+ pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
|
+ pIdxInfo->estimatedCost = 1.0;
|
|
+ pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
|
|
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
+ iPlan |= 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ pIdxInfo->idxNum = iPlan;
|
|
+
|
|
+ if( pIdxInfo->nOrderBy>=1
|
|
+ && pIdxInfo->aOrderBy[0].iColumn<=0
|
|
+ && pIdxInfo->aOrderBy[0].desc==0
|
|
+ ){
|
|
+ pIdxInfo->orderByConsumed = 1;
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Open a new dbpagevfs cursor.
|
|
+*/
|
|
+static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|
+ DbpageCursor *pCsr;
|
|
+
|
|
+ pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
|
|
+ if( pCsr==0 ){
|
|
+ return SQLITE_NOMEM_BKPT;
|
|
+ }else{
|
|
+ memset(pCsr, 0, sizeof(DbpageCursor));
|
|
+ pCsr->base.pVtab = pVTab;
|
|
+ pCsr->pgno = -1;
|
|
+ }
|
|
+
|
|
+ *ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Close a dbpagevfs cursor.
|
|
+*/
|
|
+static int dbpageClose(sqlite3_vtab_cursor *pCursor){
|
|
+ DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
|
+ if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
|
|
+ sqlite3_free(pCsr);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+** Move a dbpagevfs cursor to the next entry in the file.
|
|
+*/
|
|
+static int dbpageNext(sqlite3_vtab_cursor *pCursor){
|
|
+ int rc = SQLITE_OK;
|
|
+ DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
|
+ pCsr->pgno++;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int dbpageEof(sqlite3_vtab_cursor *pCursor){
|
|
+ DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
|
+ return pCsr->pgno > pCsr->mxPgno;
|
|
+}
|
|
+
|
|
+/*
|
|
+** idxNum:
|
|
+**
|
|
+** 0 schema=main, full table scan
|
|
+** 1 schema=main, pgno=?1
|
|
+** 2 schema=?1, full table scan
|
|
+** 3 schema=?1, pgno=?2
|
|
+**
|
|
+** idxStr is not used
|
|
+*/
|
|
+static int dbpageFilter(
|
|
+ sqlite3_vtab_cursor *pCursor,
|
|
+ int idxNum, const char *idxStr,
|
|
+ int argc, sqlite3_value **argv
|
|
+){
|
|
+ DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
|
+ DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
|
|
+ int rc;
|
|
+ sqlite3 *db = pTab->db;
|
|
+ Btree *pBt;
|
|
+
|
|
+ /* Default setting is no rows of result */
|
|
+ pCsr->pgno = 1;
|
|
+ pCsr->mxPgno = 0;
|
|
+
|
|
+ if( idxNum & 2 ){
|
|
+ const char *zSchema;
|
|
+ assert( argc>=1 );
|
|
+ zSchema = (const char*)sqlite3_value_text(argv[0]);
|
|
+ pCsr->iDb = sqlite3FindDbName(db, zSchema);
|
|
+ if( pCsr->iDb<0 ) return SQLITE_OK;
|
|
+ }else{
|
|
+ pCsr->iDb = 0;
|
|
+ }
|
|
+ pBt = db->aDb[pCsr->iDb].pBt;
|
|
+ if( pBt==0 ) return SQLITE_OK;
|
|
+ pCsr->pPager = sqlite3BtreePager(pBt);
|
|
+ pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
|
+ pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
|
|
+ if( idxNum & 1 ){
|
|
+ assert( argc>(idxNum>>1) );
|
|
+ pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
|
|
+ if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
|
|
+ pCsr->pgno = 1;
|
|
+ pCsr->mxPgno = 0;
|
|
+ }else{
|
|
+ pCsr->mxPgno = pCsr->pgno;
|
|
+ }
|
|
+ }else{
|
|
+ assert( pCsr->pgno==1 );
|
|
+ }
|
|
+ if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
|
|
+ rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int dbpageColumn(
|
|
+ sqlite3_vtab_cursor *pCursor,
|
|
+ sqlite3_context *ctx,
|
|
+ int i
|
|
+){
|
|
+ DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
|
+ int rc = SQLITE_OK;
|
|
+ switch( i ){
|
|
+ case 0: { /* pgno */
|
|
+ sqlite3_result_int(ctx, pCsr->pgno);
|
|
+ break;
|
|
+ }
|
|
+ case 1: { /* data */
|
|
+ DbPage *pDbPage = 0;
|
|
+ rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
|
|
+ SQLITE_TRANSIENT);
|
|
+ }
|
|
+ sqlite3PagerUnref(pDbPage);
|
|
+ break;
|
|
+ }
|
|
+ default: { /* schema */
|
|
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
|
|
+ sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
|
+ DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
|
+ *pRowid = pCsr->pgno;
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+static int dbpageUpdate(
|
|
+ sqlite3_vtab *pVtab,
|
|
+ int argc,
|
|
+ sqlite3_value **argv,
|
|
+ sqlite_int64 *pRowid
|
|
+){
|
|
+ DbpageTable *pTab = (DbpageTable *)pVtab;
|
|
+ Pgno pgno;
|
|
+ DbPage *pDbPage = 0;
|
|
+ int rc = SQLITE_OK;
|
|
+ char *zErr = 0;
|
|
+ const char *zSchema;
|
|
+ int iDb;
|
|
+ Btree *pBt;
|
|
+ Pager *pPager;
|
|
+ int szPage;
|
|
+
|
|
+ if( pTab->db->flags & SQLITE_Defensive ){
|
|
+ zErr = "read-only";
|
|
+ goto update_fail;
|
|
+ }
|
|
+ if( argc==1 ){
|
|
+ zErr = "cannot delete";
|
|
+ goto update_fail;
|
|
+ }
|
|
+ pgno = sqlite3_value_int(argv[0]);
|
|
+ if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
|
|
+ zErr = "cannot insert";
|
|
+ goto update_fail;
|
|
+ }
|
|
+ zSchema = (const char*)sqlite3_value_text(argv[4]);
|
|
+ iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
|
|
+ if( iDb<0 ){
|
|
+ zErr = "no such schema";
|
|
+ goto update_fail;
|
|
+ }
|
|
+ pBt = pTab->db->aDb[iDb].pBt;
|
|
+ if( pgno<1 || pBt==0 || pgno>(int)sqlite3BtreeLastPage(pBt) ){
|
|
+ zErr = "bad page number";
|
|
+ goto update_fail;
|
|
+ }
|
|
+ szPage = sqlite3BtreeGetPageSize(pBt);
|
|
+ if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|
|
+ || sqlite3_value_bytes(argv[3])!=szPage
|
|
+ ){
|
|
+ zErr = "bad page value";
|
|
+ goto update_fail;
|
|
+ }
|
|
+ pPager = sqlite3BtreePager(pBt);
|
|
+ rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3PagerWrite(pDbPage);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ memcpy(sqlite3PagerGetData(pDbPage),
|
|
+ sqlite3_value_blob(argv[3]),
|
|
+ szPage);
|
|
+ }
|
|
+ }
|
|
+ sqlite3PagerUnref(pDbPage);
|
|
+ return rc;
|
|
+
|
|
+update_fail:
|
|
+ sqlite3_free(pVtab->zErrMsg);
|
|
+ pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
|
|
+ return SQLITE_ERROR;
|
|
+}
|
|
+
|
|
+/* Since we do not know in advance which database files will be
|
|
+** written by the sqlite_dbpage virtual table, start a write transaction
|
|
+** on them all.
|
|
+*/
|
|
+static int dbpageBegin(sqlite3_vtab *pVtab){
|
|
+ DbpageTable *pTab = (DbpageTable *)pVtab;
|
|
+ sqlite3 *db = pTab->db;
|
|
+ int i;
|
|
+ for(i=0; i<db->nDb; i++){
|
|
+ Btree *pBt = db->aDb[i].pBt;
|
|
+ if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0);
|
|
+ }
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+** Invoke this routine to register the "dbpage" virtual table module
|
|
+*/
|
|
+SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
|
|
+ static sqlite3_module dbpage_module = {
|
|
+ 0, /* iVersion */
|
|
+ dbpageConnect, /* xCreate */
|
|
+ dbpageConnect, /* xConnect */
|
|
+ dbpageBestIndex, /* xBestIndex */
|
|
+ dbpageDisconnect, /* xDisconnect */
|
|
+ dbpageDisconnect, /* xDestroy */
|
|
+ dbpageOpen, /* xOpen - open a cursor */
|
|
+ dbpageClose, /* xClose - close a cursor */
|
|
+ dbpageFilter, /* xFilter - configure scan constraints */
|
|
+ dbpageNext, /* xNext - advance a cursor */
|
|
+ dbpageEof, /* xEof - check for end of scan */
|
|
+ dbpageColumn, /* xColumn - read data */
|
|
+ dbpageRowid, /* xRowid - read data */
|
|
+ dbpageUpdate, /* xUpdate */
|
|
+ dbpageBegin, /* xBegin */
|
|
+ 0, /* xSync */
|
|
+ 0, /* xCommit */
|
|
+ 0, /* xRollback */
|
|
+ 0, /* xFindMethod */
|
|
+ 0, /* xRename */
|
|
+ 0, /* xSavepoint */
|
|
+ 0, /* xRelease */
|
|
+ 0, /* xRollbackTo */
|
|
+ 0 /* xShadowName */
|
|
+ };
|
|
+ return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
|
|
+}
|
|
+#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
|
+SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
|
|
+#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
|
|
+
|
|
+/************** End of dbpage.c **********************************************/
|
|
/************** Begin file sqlite3session.c **********************************/
|
|
|
|
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
|
@@ -175973,6 +192893,8 @@
|
|
# endif
|
|
#endif
|
|
|
|
+static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
|
|
+
|
|
typedef struct SessionHook SessionHook;
|
|
struct SessionHook {
|
|
void *pCtx;
|
|
@@ -175994,6 +192916,7 @@
|
|
int rc; /* Non-zero if an error has occurred */
|
|
void *pFilterCtx; /* First argument to pass to xTableFilter */
|
|
int (*xTableFilter)(void *pCtx, const char *zTab);
|
|
+ sqlite3_value *pZeroBlob; /* Value containing X'' */
|
|
sqlite3_session *pNext; /* Next session object on same db. */
|
|
SessionTable *pTable; /* List of attached tables */
|
|
SessionHook hook; /* APIs to grab new and old data with */
|
|
@@ -176015,7 +192938,7 @@
|
|
** sqlite3changeset_start_strm()).
|
|
*/
|
|
struct SessionInput {
|
|
- int bNoDiscard; /* If true, discard no data */
|
|
+ int bNoDiscard; /* If true, do not discard in InputBuffer() */
|
|
int iCurrent; /* Offset in aData[] of current change */
|
|
int iNext; /* Offset in aData[] of next change */
|
|
u8 *aData; /* Pointer to buffer containing changeset */
|
|
@@ -176034,6 +192957,7 @@
|
|
SessionInput in; /* Input buffer or stream */
|
|
SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
|
|
int bPatchset; /* True if this is a patchset */
|
|
+ int bInvert; /* True to invert changeset */
|
|
int rc; /* Iterator error code */
|
|
sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
|
|
char *zTab; /* Current table */
|
|
@@ -176061,6 +192985,7 @@
|
|
SessionTable *pNext;
|
|
char *zName; /* Local name of table */
|
|
int nCol; /* Number of columns in table zName */
|
|
+ int bStat1; /* True if this is sqlite_stat1 */
|
|
const char **azCol; /* Column names */
|
|
u8 *abPK; /* Array of primary key flags */
|
|
int nEntry; /* Total number of entries in hash table */
|
|
@@ -176178,8 +193103,8 @@
|
|
** statement.
|
|
**
|
|
** For a DELETE change, all fields within the record except those associated
|
|
-** with PRIMARY KEY columns are set to "undefined". The PRIMARY KEY fields
|
|
-** contain the values identifying the row to delete.
|
|
+** with PRIMARY KEY columns are omitted. The PRIMARY KEY fields contain the
|
|
+** values identifying the row to delete.
|
|
**
|
|
** For an UPDATE change, all fields except those associated with PRIMARY KEY
|
|
** columns and columns that are modified by the UPDATE are set to "undefined".
|
|
@@ -176189,6 +193114,42 @@
|
|
** The records associated with INSERT changes are in the same format as for
|
|
** changesets. It is not possible for a record associated with an INSERT
|
|
** change to contain a field set to "undefined".
|
|
+**
|
|
+** REBASE BLOB FORMAT:
|
|
+**
|
|
+** A rebase blob may be output by sqlite3changeset_apply_v2() and its
|
|
+** streaming equivalent for use with the sqlite3_rebaser APIs to rebase
|
|
+** existing changesets. A rebase blob contains one entry for each conflict
|
|
+** resolved using either the OMIT or REPLACE strategies within the apply_v2()
|
|
+** call.
|
|
+**
|
|
+** The format used for a rebase blob is very similar to that used for
|
|
+** changesets. All entries related to a single table are grouped together.
|
|
+**
|
|
+** Each group of entries begins with a table header in changeset format:
|
|
+**
|
|
+** 1 byte: Constant 0x54 (capital 'T')
|
|
+** Varint: Number of columns in the table.
|
|
+** nCol bytes: 0x01 for PK columns, 0x00 otherwise.
|
|
+** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
|
|
+**
|
|
+** Followed by one or more entries associated with the table.
|
|
+**
|
|
+** 1 byte: Either SQLITE_INSERT (0x12), DELETE (0x09).
|
|
+** 1 byte: Flag. 0x01 for REPLACE, 0x00 for OMIT.
|
|
+** record: (in the record format defined above).
|
|
+**
|
|
+** In a rebase blob, the first field is set to SQLITE_INSERT if the change
|
|
+** that caused the conflict was an INSERT or UPDATE, or to SQLITE_DELETE if
|
|
+** it was a DELETE. The second field is set to 0x01 if the conflict
|
|
+** resolution strategy was REPLACE, or 0x00 if it was OMIT.
|
|
+**
|
|
+** If the change that caused the conflict was a DELETE, then the single
|
|
+** record is a copy of the old.* record from the original changeset. If it
|
|
+** was an INSERT, then the single record is a copy of the new.* record. If
|
|
+** the conflicting change was an UPDATE, then the single record is a copy
|
|
+** of the new.* record with the PK fields filled in based on the original
|
|
+** old.* record.
|
|
*/
|
|
|
|
/*
|
|
@@ -176444,6 +193405,7 @@
|
|
h = sessionHashAppendBlob(h, n, z);
|
|
}else{
|
|
assert( eType==SQLITE_NULL );
|
|
+ assert( pTab->bStat1==0 || i!=1 );
|
|
*pbNullPK = 1;
|
|
}
|
|
}
|
|
@@ -176461,7 +193423,7 @@
|
|
static int sessionSerialLen(u8 *a){
|
|
int e = *a;
|
|
int n;
|
|
- if( e==0 ) return 1;
|
|
+ if( e==0 || e==0xFF ) return 1;
|
|
if( e==SQLITE_NULL ) return 1;
|
|
if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
|
|
return sessionVarintGet(&a[1], &n) + 1 + n;
|
|
@@ -176541,7 +193503,7 @@
|
|
int n1 = sessionSerialLen(a1);
|
|
int n2 = sessionSerialLen(a2);
|
|
|
|
- if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){
|
|
+ if( n1!=n2 || memcmp(a1, a2, n1) ){
|
|
return 0;
|
|
}
|
|
a1 += n1;
|
|
@@ -176784,9 +193746,8 @@
|
|
}else{
|
|
z = sqlite3_value_blob(pVal);
|
|
}
|
|
- if( memcmp(a, z, n) ) return 0;
|
|
+ if( n>0 && memcmp(a, z, n) ) return 0;
|
|
a += n;
|
|
- break;
|
|
}
|
|
}
|
|
}
|
|
@@ -176842,9 +193803,7 @@
|
|
|
|
/*
|
|
** This function queries the database for the names of the columns of table
|
|
-** zThis, in schema zDb. It is expected that the table has nCol columns. If
|
|
-** not, SQLITE_SCHEMA is returned and none of the output variables are
|
|
-** populated.
|
|
+** zThis, in schema zDb.
|
|
**
|
|
** Otherwise, if they are not NULL, variable *pnCol is set to the number
|
|
** of columns in the database table and variable *pzTab is set to point to a
|
|
@@ -176865,9 +193824,7 @@
|
|
** *pabPK = {1, 0, 0, 1}
|
|
**
|
|
** All returned buffers are part of the same single allocation, which must
|
|
-** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
|
|
-** pointer *pazCol should be freed to release all memory. Otherwise, pointer
|
|
-** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
|
|
+** be freed using sqlite3_free() by the caller
|
|
*/
|
|
static int sessionTableInfo(
|
|
sqlite3 *db, /* Database connection */
|
|
@@ -176892,7 +193849,23 @@
|
|
assert( pazCol && pabPK );
|
|
|
|
nThis = sqlite3Strlen30(zThis);
|
|
- zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
|
|
+ if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
|
|
+ rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ /* For sqlite_stat1, pretend that (tbl,idx) is the PRIMARY KEY. */
|
|
+ zPragma = sqlite3_mprintf(
|
|
+ "SELECT 0, 'tbl', '', 0, '', 1 UNION ALL "
|
|
+ "SELECT 1, 'idx', '', 0, '', 2 UNION ALL "
|
|
+ "SELECT 2, 'stat', '', 0, '', 0"
|
|
+ );
|
|
+ }else if( rc==SQLITE_ERROR ){
|
|
+ zPragma = sqlite3_mprintf("");
|
|
+ }else{
|
|
+ return rc;
|
|
+ }
|
|
+ }else{
|
|
+ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
|
|
+ }
|
|
if( !zPragma ) return SQLITE_NOMEM;
|
|
|
|
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
|
|
@@ -176984,6 +193957,9 @@
|
|
break;
|
|
}
|
|
}
|
|
+ if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){
|
|
+ pTab->bStat1 = 1;
|
|
+ }
|
|
}
|
|
}
|
|
return (pSession->rc || pTab->abPK==0);
|
|
@@ -176990,6 +193966,47 @@
|
|
}
|
|
|
|
/*
|
|
+** Versions of the four methods in object SessionHook for use with the
|
|
+** sqlite_stat1 table. The purpose of this is to substitute a zero-length
|
|
+** blob each time a NULL value is read from the "idx" column of the
|
|
+** sqlite_stat1 table.
|
|
+*/
|
|
+typedef struct SessionStat1Ctx SessionStat1Ctx;
|
|
+struct SessionStat1Ctx {
|
|
+ SessionHook hook;
|
|
+ sqlite3_session *pSession;
|
|
+};
|
|
+static int sessionStat1Old(void *pCtx, int iCol, sqlite3_value **ppVal){
|
|
+ SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx;
|
|
+ sqlite3_value *pVal = 0;
|
|
+ int rc = p->hook.xOld(p->hook.pCtx, iCol, &pVal);
|
|
+ if( rc==SQLITE_OK && iCol==1 && sqlite3_value_type(pVal)==SQLITE_NULL ){
|
|
+ pVal = p->pSession->pZeroBlob;
|
|
+ }
|
|
+ *ppVal = pVal;
|
|
+ return rc;
|
|
+}
|
|
+static int sessionStat1New(void *pCtx, int iCol, sqlite3_value **ppVal){
|
|
+ SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx;
|
|
+ sqlite3_value *pVal = 0;
|
|
+ int rc = p->hook.xNew(p->hook.pCtx, iCol, &pVal);
|
|
+ if( rc==SQLITE_OK && iCol==1 && sqlite3_value_type(pVal)==SQLITE_NULL ){
|
|
+ pVal = p->pSession->pZeroBlob;
|
|
+ }
|
|
+ *ppVal = pVal;
|
|
+ return rc;
|
|
+}
|
|
+static int sessionStat1Count(void *pCtx){
|
|
+ SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx;
|
|
+ return p->hook.xCount(p->hook.pCtx);
|
|
+}
|
|
+static int sessionStat1Depth(void *pCtx){
|
|
+ SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx;
|
|
+ return p->hook.xDepth(p->hook.pCtx);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
** This function is only called from with a pre-update-hook reporting a
|
|
** change on table pTab (attached to session pSession). The type of change
|
|
** (UPDATE, INSERT, DELETE) is specified by the first argument.
|
|
@@ -177005,6 +194022,7 @@
|
|
int iHash;
|
|
int bNull = 0;
|
|
int rc = SQLITE_OK;
|
|
+ SessionStat1Ctx stat1 = {0};
|
|
|
|
if( pSession->rc ) return;
|
|
|
|
@@ -177024,6 +194042,25 @@
|
|
return;
|
|
}
|
|
|
|
+ if( pTab->bStat1 ){
|
|
+ stat1.hook = pSession->hook;
|
|
+ stat1.pSession = pSession;
|
|
+ pSession->hook.pCtx = (void*)&stat1;
|
|
+ pSession->hook.xNew = sessionStat1New;
|
|
+ pSession->hook.xOld = sessionStat1Old;
|
|
+ pSession->hook.xCount = sessionStat1Count;
|
|
+ pSession->hook.xDepth = sessionStat1Depth;
|
|
+ if( pSession->pZeroBlob==0 ){
|
|
+ sqlite3_value *p = sqlite3ValueNew(0);
|
|
+ if( p==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ goto error_out;
|
|
+ }
|
|
+ sqlite3ValueSetStr(p, 0, "", 0, SQLITE_STATIC);
|
|
+ pSession->pZeroBlob = p;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* Calculate the hash-key for this change. If the primary key of the row
|
|
** includes a NULL value, exit early. Such changes are ignored by the
|
|
** session module. */
|
|
@@ -177113,6 +194150,9 @@
|
|
|
|
/* If an error has occurred, mark the session object as failed. */
|
|
error_out:
|
|
+ if( pTab->bStat1 ){
|
|
+ pSession->hook = stat1.hook;
|
|
+ }
|
|
if( rc!=SQLITE_OK ){
|
|
pSession->rc = rc;
|
|
}
|
|
@@ -177449,7 +194489,6 @@
|
|
if( abPK[i] ) bHasPk = 1;
|
|
}
|
|
}
|
|
-
|
|
}
|
|
sqlite3_free((char*)azCol);
|
|
if( bMismatch ){
|
|
@@ -177575,6 +194614,7 @@
|
|
}
|
|
}
|
|
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
|
+ sqlite3ValueFree(pSession->pZeroBlob);
|
|
|
|
/* Delete all attached table objects. And the contents of their
|
|
** associated hash-tables. */
|
|
@@ -177660,12 +194700,12 @@
|
|
static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
|
|
if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
|
|
u8 *aNew;
|
|
- int nNew = p->nAlloc ? p->nAlloc : 128;
|
|
+ i64 nNew = p->nAlloc ? p->nAlloc : 128;
|
|
do {
|
|
nNew = nNew*2;
|
|
- }while( nNew<(p->nBuf+nByte) );
|
|
+ }while( (nNew-p->nBuf)<nByte );
|
|
|
|
- aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
|
|
+ aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
|
|
if( 0==aNew ){
|
|
*pRc = SQLITE_NOMEM;
|
|
}else{
|
|
@@ -178042,28 +195082,42 @@
|
|
sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
|
|
){
|
|
int rc = SQLITE_OK;
|
|
- int i;
|
|
- const char *zSep = "";
|
|
- SessionBuffer buf = {0, 0, 0};
|
|
+ char *zSql = 0;
|
|
+ int nSql = -1;
|
|
|
|
- sessionAppendStr(&buf, "SELECT * FROM ", &rc);
|
|
- sessionAppendIdent(&buf, zDb, &rc);
|
|
- sessionAppendStr(&buf, ".", &rc);
|
|
- sessionAppendIdent(&buf, zTab, &rc);
|
|
- sessionAppendStr(&buf, " WHERE ", &rc);
|
|
- for(i=0; i<nCol; i++){
|
|
- if( abPK[i] ){
|
|
- sessionAppendStr(&buf, zSep, &rc);
|
|
- sessionAppendIdent(&buf, azCol[i], &rc);
|
|
- sessionAppendStr(&buf, " = ?", &rc);
|
|
- sessionAppendInteger(&buf, i+1, &rc);
|
|
- zSep = " AND ";
|
|
+ if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){
|
|
+ zSql = sqlite3_mprintf(
|
|
+ "SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
|
|
+ "idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
|
|
+ );
|
|
+ if( zSql==0 ) rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int i;
|
|
+ const char *zSep = "";
|
|
+ SessionBuffer buf = {0, 0, 0};
|
|
+
|
|
+ sessionAppendStr(&buf, "SELECT * FROM ", &rc);
|
|
+ sessionAppendIdent(&buf, zDb, &rc);
|
|
+ sessionAppendStr(&buf, ".", &rc);
|
|
+ sessionAppendIdent(&buf, zTab, &rc);
|
|
+ sessionAppendStr(&buf, " WHERE ", &rc);
|
|
+ for(i=0; i<nCol; i++){
|
|
+ if( abPK[i] ){
|
|
+ sessionAppendStr(&buf, zSep, &rc);
|
|
+ sessionAppendIdent(&buf, azCol[i], &rc);
|
|
+ sessionAppendStr(&buf, " IS ?", &rc);
|
|
+ sessionAppendInteger(&buf, i+1, &rc);
|
|
+ zSep = " AND ";
|
|
+ }
|
|
}
|
|
+ zSql = (char*)buf.aBuf;
|
|
+ nSql = buf.nBuf;
|
|
}
|
|
+
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
|
|
+ rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0);
|
|
}
|
|
- sqlite3_free(buf.aBuf);
|
|
+ sqlite3_free(zSql);
|
|
return rc;
|
|
}
|
|
|
|
@@ -178249,12 +195303,12 @@
|
|
rc = sqlite3_reset(pSel);
|
|
}
|
|
|
|
- /* If the buffer is now larger than SESSIONS_STRM_CHUNK_SIZE, pass
|
|
+ /* If the buffer is now larger than sessions_strm_chunk_size, pass
|
|
** its contents to the xOutput() callback. */
|
|
if( xOutput
|
|
&& rc==SQLITE_OK
|
|
&& buf.nBuf>nNoop
|
|
- && buf.nBuf>SESSIONS_STRM_CHUNK_SIZE
|
|
+ && buf.nBuf>sessions_strm_chunk_size
|
|
){
|
|
rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
|
|
nNoop = -1;
|
|
@@ -178393,7 +195447,8 @@
|
|
int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
void *pIn,
|
|
int nChangeset, /* Size of buffer pChangeset in bytes */
|
|
- void *pChangeset /* Pointer to buffer containing changeset */
|
|
+ void *pChangeset, /* Pointer to buffer containing changeset */
|
|
+ int bInvert /* True to invert changeset */
|
|
){
|
|
sqlite3_changeset_iter *pRet; /* Iterator to return */
|
|
int nByte; /* Number of bytes to allocate for iterator */
|
|
@@ -178413,6 +195468,7 @@
|
|
pRet->in.xInput = xInput;
|
|
pRet->in.pIn = pIn;
|
|
pRet->in.bEof = (xInput ? 0 : 1);
|
|
+ pRet->bInvert = bInvert;
|
|
|
|
/* Populate the output variable and return success. */
|
|
*pp = pRet;
|
|
@@ -178427,8 +195483,17 @@
|
|
int nChangeset, /* Size of buffer pChangeset in bytes */
|
|
void *pChangeset /* Pointer to buffer containing changeset */
|
|
){
|
|
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset);
|
|
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0);
|
|
}
|
|
+SQLITE_API int sqlite3changeset_start_v2(
|
|
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
|
|
+ int nChangeset, /* Size of buffer pChangeset in bytes */
|
|
+ void *pChangeset, /* Pointer to buffer containing changeset */
|
|
+ int flags
|
|
+){
|
|
+ int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
|
|
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert);
|
|
+}
|
|
|
|
/*
|
|
** Streaming version of sqlite3changeset_start().
|
|
@@ -178438,8 +195503,17 @@
|
|
int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
void *pIn
|
|
){
|
|
- return sessionChangesetStart(pp, xInput, pIn, 0, 0);
|
|
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0);
|
|
}
|
|
+SQLITE_API int sqlite3changeset_start_v2_strm(
|
|
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
+ void *pIn,
|
|
+ int flags
|
|
+){
|
|
+ int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
|
|
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert);
|
|
+}
|
|
|
|
/*
|
|
** If the SessionInput object passed as the only argument is a streaming
|
|
@@ -178446,7 +195520,7 @@
|
|
** object and the buffer is full, discard some data to free up space.
|
|
*/
|
|
static void sessionDiscardData(SessionInput *pIn){
|
|
- if( pIn->bEof && pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){
|
|
+ if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){
|
|
int nMove = pIn->buf.nBuf - pIn->iNext;
|
|
assert( nMove>=0 );
|
|
if( nMove>0 ){
|
|
@@ -178469,7 +195543,7 @@
|
|
int rc = SQLITE_OK;
|
|
if( pIn->xInput ){
|
|
while( !pIn->bEof && (pIn->iNext+nByte)>=pIn->nData && rc==SQLITE_OK ){
|
|
- int nNew = SESSIONS_STRM_CHUNK_SIZE;
|
|
+ int nNew = sessions_strm_chunk_size;
|
|
|
|
if( pIn->bNoDiscard==0 ) sessionDiscardData(pIn);
|
|
if( SQLITE_OK==sessionBufferGrow(&pIn->buf, nNew, &rc) ){
|
|
@@ -178574,15 +195648,18 @@
|
|
if( abPK && abPK[i]==0 ) continue;
|
|
rc = sessionInputBuffer(pIn, 9);
|
|
if( rc==SQLITE_OK ){
|
|
- eType = pIn->aData[pIn->iNext++];
|
|
+ if( pIn->iNext>=pIn->nData ){
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }else{
|
|
+ eType = pIn->aData[pIn->iNext++];
|
|
+ assert( apOut[i]==0 );
|
|
+ if( eType ){
|
|
+ apOut[i] = sqlite3ValueNew(0);
|
|
+ if( !apOut[i] ) rc = SQLITE_NOMEM;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
- assert( apOut[i]==0 );
|
|
- if( eType ){
|
|
- apOut[i] = sqlite3ValueNew(0);
|
|
- if( !apOut[i] ) rc = SQLITE_NOMEM;
|
|
- }
|
|
-
|
|
if( rc==SQLITE_OK ){
|
|
u8 *aVal = &pIn->aData[pIn->iNext];
|
|
if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
|
|
@@ -178590,10 +195667,14 @@
|
|
pIn->iNext += sessionVarintGet(aVal, &nByte);
|
|
rc = sessionInputBuffer(pIn, nByte);
|
|
if( rc==SQLITE_OK ){
|
|
- u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
|
|
- rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc);
|
|
+ if( nByte<0 || nByte>pIn->nData-pIn->iNext ){
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }else{
|
|
+ u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
|
|
+ rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc);
|
|
+ pIn->iNext += nByte;
|
|
+ }
|
|
}
|
|
- pIn->iNext += nByte;
|
|
}
|
|
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
|
sqlite3_int64 v = sessionGetI64(aVal);
|
|
@@ -178633,8 +195714,19 @@
|
|
rc = sessionInputBuffer(pIn, 9);
|
|
if( rc==SQLITE_OK ){
|
|
nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
|
|
- rc = sessionInputBuffer(pIn, nRead+nCol+100);
|
|
- nRead += nCol;
|
|
+ /* The hard upper limit for the number of columns in an SQLite
|
|
+ ** database table is, according to sqliteLimit.h, 32676. So
|
|
+ ** consider any table-header that purports to have more than 65536
|
|
+ ** columns to be corrupt. This is convenient because otherwise,
|
|
+ ** if the (nCol>65536) condition below were omitted, a sufficiently
|
|
+ ** large value for nCol may cause nRead to wrap around and become
|
|
+ ** negative. Leading to a crash. */
|
|
+ if( nCol<0 || nCol>65536 ){
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }else{
|
|
+ rc = sessionInputBuffer(pIn, nRead+nCol+100);
|
|
+ nRead += nCol;
|
|
+ }
|
|
}
|
|
|
|
while( rc==SQLITE_OK ){
|
|
@@ -178711,11 +195803,15 @@
|
|
int nByte;
|
|
int nVarint;
|
|
nVarint = sessionVarintGet(&p->in.aData[p->in.iNext], &p->nCol);
|
|
- nCopy -= nVarint;
|
|
- p->in.iNext += nVarint;
|
|
- nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy;
|
|
- p->tblhdr.nBuf = 0;
|
|
- sessionBufferGrow(&p->tblhdr, nByte, &rc);
|
|
+ if( p->nCol>0 ){
|
|
+ nCopy -= nVarint;
|
|
+ p->in.iNext += nVarint;
|
|
+ nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy;
|
|
+ p->tblhdr.nBuf = 0;
|
|
+ sessionBufferGrow(&p->tblhdr, nByte, &rc);
|
|
+ }else{
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }
|
|
}
|
|
|
|
if( rc==SQLITE_OK ){
|
|
@@ -178750,7 +195846,8 @@
|
|
static int sessionChangesetNext(
|
|
sqlite3_changeset_iter *p, /* Changeset iterator */
|
|
u8 **paRec, /* If non-NULL, store record pointer here */
|
|
- int *pnRec /* If non-NULL, store size of record here */
|
|
+ int *pnRec, /* If non-NULL, store size of record here */
|
|
+ int *pbNew /* If non-NULL, true if new table */
|
|
){
|
|
int i;
|
|
u8 op;
|
|
@@ -178785,6 +195882,7 @@
|
|
|
|
op = p->in.aData[p->in.iNext++];
|
|
while( op=='T' || op=='P' ){
|
|
+ if( pbNew ) *pbNew = 1;
|
|
p->bPatchset = (op=='P');
|
|
if( sessionChangesetReadTblhdr(p) ) return p->rc;
|
|
if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc;
|
|
@@ -178793,6 +195891,13 @@
|
|
op = p->in.aData[p->in.iNext++];
|
|
}
|
|
|
|
+ if( p->zTab==0 || (p->bPatchset && p->bInvert) ){
|
|
+ /* The first record in the changeset is not a table header. Must be a
|
|
+ ** corrupt changeset. */
|
|
+ assert( p->in.iNext==1 || p->zTab );
|
|
+ return (p->rc = SQLITE_CORRUPT_BKPT);
|
|
+ }
|
|
+
|
|
p->op = op;
|
|
p->bIndirect = p->in.aData[p->in.iNext++];
|
|
if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
|
|
@@ -178814,33 +195919,39 @@
|
|
*paRec = &p->in.aData[p->in.iNext];
|
|
p->in.iNext += *pnRec;
|
|
}else{
|
|
+ sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue);
|
|
+ sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]);
|
|
|
|
/* If this is an UPDATE or DELETE, read the old.* record. */
|
|
if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){
|
|
u8 *abPK = p->bPatchset ? p->abPK : 0;
|
|
- p->rc = sessionReadRecord(&p->in, p->nCol, abPK, p->apValue);
|
|
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld);
|
|
if( p->rc!=SQLITE_OK ) return p->rc;
|
|
}
|
|
|
|
/* If this is an INSERT or UPDATE, read the new.* record. */
|
|
if( p->op!=SQLITE_DELETE ){
|
|
- p->rc = sessionReadRecord(&p->in, p->nCol, 0, &p->apValue[p->nCol]);
|
|
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew);
|
|
if( p->rc!=SQLITE_OK ) return p->rc;
|
|
}
|
|
|
|
- if( p->bPatchset && p->op==SQLITE_UPDATE ){
|
|
+ if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){
|
|
/* If this is an UPDATE that is part of a patchset, then all PK and
|
|
** modified fields are present in the new.* record. The old.* record
|
|
** is currently completely empty. This block shifts the PK fields from
|
|
** new.* to old.*, to accommodate the code that reads these arrays. */
|
|
for(i=0; i<p->nCol; i++){
|
|
- assert( p->apValue[i]==0 );
|
|
- assert( p->abPK[i]==0 || p->apValue[i+p->nCol] );
|
|
+ assert( p->bPatchset==0 || p->apValue[i]==0 );
|
|
if( p->abPK[i] ){
|
|
+ assert( p->apValue[i]==0 );
|
|
p->apValue[i] = p->apValue[i+p->nCol];
|
|
+ if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT);
|
|
p->apValue[i+p->nCol] = 0;
|
|
}
|
|
}
|
|
+ }else if( p->bInvert ){
|
|
+ if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE;
|
|
+ else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT;
|
|
}
|
|
}
|
|
|
|
@@ -178856,7 +195967,7 @@
|
|
** callback by changeset_apply().
|
|
*/
|
|
SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *p){
|
|
- return sessionChangesetNext(p, 0, 0);
|
|
+ return sessionChangesetNext(p, 0, 0, 0);
|
|
}
|
|
|
|
/*
|
|
@@ -179157,7 +196268,7 @@
|
|
}
|
|
|
|
assert( rc==SQLITE_OK );
|
|
- if( xOutput && sOut.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
|
|
+ if( xOutput && sOut.nBuf>=sessions_strm_chunk_size ){
|
|
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
|
|
sOut.nBuf = 0;
|
|
if( rc!=SQLITE_OK ) goto finished_invert;
|
|
@@ -179232,9 +196343,12 @@
|
|
int nCol; /* Size of azCol[] and abPK[] arrays */
|
|
const char **azCol; /* Array of column names */
|
|
u8 *abPK; /* Boolean array - true if column is in PK */
|
|
-
|
|
+ int bStat1; /* True if table is sqlite_stat1 */
|
|
int bDeferConstraints; /* True to defer constraints */
|
|
SessionBuffer constraints; /* Deferred constraints are stored here */
|
|
+ SessionBuffer rebase; /* Rebase information (if any) here */
|
|
+ u8 bRebaseStarted; /* If table header is already in rebase */
|
|
+ u8 bRebase; /* True to collect rebase information */
|
|
};
|
|
|
|
/*
|
|
@@ -179402,6 +196516,7 @@
|
|
return rc;
|
|
}
|
|
|
|
+
|
|
/*
|
|
** Formulate and prepare an SQL statement to query table zTab by primary
|
|
** key. Assuming the following table structure:
|
|
@@ -179463,7 +196578,47 @@
|
|
return rc;
|
|
}
|
|
|
|
+static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){
|
|
+ return sqlite3_prepare_v2(db, zSql, -1, pp, 0);
|
|
+}
|
|
+
|
|
/*
|
|
+** Prepare statements for applying changes to the sqlite_stat1 table.
|
|
+** These are similar to those created by sessionSelectRow(),
|
|
+** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
|
|
+** other tables.
|
|
+*/
|
|
+static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
|
|
+ int rc = sessionSelectRow(db, "sqlite_stat1", p);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionPrepare(db, &p->pInsert,
|
|
+ "INSERT INTO main.sqlite_stat1 VALUES(?1, "
|
|
+ "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, "
|
|
+ "?3)"
|
|
+ );
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionPrepare(db, &p->pUpdate,
|
|
+ "UPDATE main.sqlite_stat1 SET "
|
|
+ "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, "
|
|
+ "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, "
|
|
+ "stat = CASE WHEN ?8 THEN ?9 ELSE stat END "
|
|
+ "WHERE tbl=?1 AND idx IS "
|
|
+ "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END "
|
|
+ "AND (?10 OR ?8=0 OR stat IS ?7)"
|
|
+ );
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionPrepare(db, &p->pDelete,
|
|
+ "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
|
|
+ "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
|
|
+ "AND (?4 OR stat IS ?3)"
|
|
+ );
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
** A wrapper around sqlite3_bind_value() that detects an extra problem.
|
|
** See comments in the body of this function for details.
|
|
*/
|
|
@@ -179520,7 +196675,13 @@
|
|
if( !abPK || abPK[i] ){
|
|
sqlite3_value *pVal;
|
|
(void)xValue(pIter, i, &pVal);
|
|
- rc = sessionBindValue(pStmt, i+1, pVal);
|
|
+ if( pVal==0 ){
|
|
+ /* The value in the changeset was "undefined". This indicates a
|
|
+ ** corrupt changeset blob. */
|
|
+ rc = SQLITE_CORRUPT_BKPT;
|
|
+ }else{
|
|
+ rc = sessionBindValue(pStmt, i+1, pVal);
|
|
+ }
|
|
}
|
|
}
|
|
return rc;
|
|
@@ -179569,6 +196730,55 @@
|
|
}
|
|
|
|
/*
|
|
+** This function is called from within sqlite3changset_apply_v2() when
|
|
+** a conflict is encountered and resolved using conflict resolution
|
|
+** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE)..
|
|
+** It adds a conflict resolution record to the buffer in
|
|
+** SessionApplyCtx.rebase, which will eventually be returned to the caller
|
|
+** of apply_v2() as the "rebase" buffer.
|
|
+**
|
|
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
|
|
+*/
|
|
+static int sessionRebaseAdd(
|
|
+ SessionApplyCtx *p, /* Apply context */
|
|
+ int eType, /* Conflict resolution (OMIT or REPLACE) */
|
|
+ sqlite3_changeset_iter *pIter /* Iterator pointing at current change */
|
|
+){
|
|
+ int rc = SQLITE_OK;
|
|
+ if( p->bRebase ){
|
|
+ int i;
|
|
+ int eOp = pIter->op;
|
|
+ if( p->bRebaseStarted==0 ){
|
|
+ /* Append a table-header to the rebase buffer */
|
|
+ const char *zTab = pIter->zTab;
|
|
+ sessionAppendByte(&p->rebase, 'T', &rc);
|
|
+ sessionAppendVarint(&p->rebase, p->nCol, &rc);
|
|
+ sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc);
|
|
+ sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc);
|
|
+ p->bRebaseStarted = 1;
|
|
+ }
|
|
+
|
|
+ assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT );
|
|
+ assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE );
|
|
+
|
|
+ sessionAppendByte(&p->rebase,
|
|
+ (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc
|
|
+ );
|
|
+ sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc);
|
|
+ for(i=0; i<p->nCol; i++){
|
|
+ sqlite3_value *pVal = 0;
|
|
+ if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){
|
|
+ sqlite3changeset_old(pIter, i, &pVal);
|
|
+ }else{
|
|
+ sqlite3changeset_new(pIter, i, &pVal);
|
|
+ }
|
|
+ sessionAppendValue(&p->rebase, pVal, &rc);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
** Invoke the conflict handler for the change that the changeset iterator
|
|
** currently points to.
|
|
**
|
|
@@ -179643,7 +196853,7 @@
|
|
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
|
|
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
|
|
sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
|
|
- res = SQLITE_CHANGESET_OMIT;
|
|
+ return SQLITE_OK;
|
|
}else{
|
|
/* No other row with the new.* primary key. */
|
|
res = xConflict(pCtx, eType+1, pIter);
|
|
@@ -179669,6 +196879,9 @@
|
|
rc = SQLITE_MISUSE;
|
|
break;
|
|
}
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionRebaseAdd(p, res, pIter);
|
|
+ }
|
|
}
|
|
|
|
return rc;
|
|
@@ -179793,11 +197006,25 @@
|
|
|
|
}else{
|
|
assert( op==SQLITE_INSERT );
|
|
- rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
|
|
- if( rc!=SQLITE_OK ) return rc;
|
|
+ if( p->bStat1 ){
|
|
+ /* Check if there is a conflicting row. For sqlite_stat1, this needs
|
|
+ ** to be done using a SELECT, as there is no PRIMARY KEY in the
|
|
+ ** database schema to throw an exception if a duplicate is inserted. */
|
|
+ rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
|
|
+ if( rc==SQLITE_ROW ){
|
|
+ rc = SQLITE_CONSTRAINT;
|
|
+ sqlite3_reset(p->pSelect);
|
|
+ }
|
|
+ }
|
|
|
|
- sqlite3_step(p->pInsert);
|
|
- rc = sqlite3_reset(p->pInsert);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
|
|
+ if( rc!=SQLITE_OK ) return rc;
|
|
+
|
|
+ sqlite3_step(p->pInsert);
|
|
+ rc = sqlite3_reset(p->pInsert);
|
|
+ }
|
|
+
|
|
if( (rc&0xff)==SQLITE_CONSTRAINT ){
|
|
rc = sessionConflictHandler(
|
|
SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace
|
|
@@ -179830,42 +197057,42 @@
|
|
int rc;
|
|
|
|
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry);
|
|
- assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) );
|
|
-
|
|
- /* If the bRetry flag is set, the change has not been applied due to an
|
|
- ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
|
|
- ** a row with the correct PK is present in the db, but one or more other
|
|
- ** fields do not contain the expected values) and the conflict handler
|
|
- ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
|
|
- ** but pass NULL as the final argument so that sessionApplyOneOp() ignores
|
|
- ** the SQLITE_CHANGESET_DATA problem. */
|
|
- if( bRetry ){
|
|
- assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
|
|
- rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
|
|
- }
|
|
-
|
|
- /* If the bReplace flag is set, the change is an INSERT that has not
|
|
- ** been performed because the database already contains a row with the
|
|
- ** specified primary key and the conflict handler returned
|
|
- ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
|
|
- ** before reattempting the INSERT. */
|
|
- else if( bReplace ){
|
|
- assert( pIter->op==SQLITE_INSERT );
|
|
- rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
|
|
- if( rc==SQLITE_OK ){
|
|
- rc = sessionBindRow(pIter,
|
|
- sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
|
|
- sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
|
|
- }
|
|
- if( rc==SQLITE_OK ){
|
|
- sqlite3_step(pApply->pDelete);
|
|
- rc = sqlite3_reset(pApply->pDelete);
|
|
- }
|
|
- if( rc==SQLITE_OK ){
|
|
+ if( rc==SQLITE_OK ){
|
|
+ /* If the bRetry flag is set, the change has not been applied due to an
|
|
+ ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
|
|
+ ** a row with the correct PK is present in the db, but one or more other
|
|
+ ** fields do not contain the expected values) and the conflict handler
|
|
+ ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
|
|
+ ** but pass NULL as the final argument so that sessionApplyOneOp() ignores
|
|
+ ** the SQLITE_CHANGESET_DATA problem. */
|
|
+ if( bRetry ){
|
|
+ assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
|
|
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
|
|
}
|
|
- if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
|
|
+
|
|
+ /* If the bReplace flag is set, the change is an INSERT that has not
|
|
+ ** been performed because the database already contains a row with the
|
|
+ ** specified primary key and the conflict handler returned
|
|
+ ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
|
|
+ ** before reattempting the INSERT. */
|
|
+ else if( bReplace ){
|
|
+ assert( pIter->op==SQLITE_INSERT );
|
|
+ rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionBindRow(pIter,
|
|
+ sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
|
|
+ sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ sqlite3_step(pApply->pDelete);
|
|
+ rc = sqlite3_reset(pApply->pDelete);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
|
|
+ }
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -179890,7 +197117,7 @@
|
|
SessionBuffer cons = pApply->constraints;
|
|
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
|
|
|
|
- rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf);
|
|
+ rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0);
|
|
if( rc==SQLITE_OK ){
|
|
int nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
|
|
int rc2;
|
|
@@ -179941,10 +197168,12 @@
|
|
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
),
|
|
- void *pCtx /* First argument passed to xConflict */
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase, /* OUT: Rebase information */
|
|
+ int flags /* SESSION_APPLY_XXX flags */
|
|
){
|
|
int schemaMismatch = 0;
|
|
- int rc; /* Return code */
|
|
+ int rc = SQLITE_OK; /* Return code */
|
|
const char *zTab = 0; /* Name of current table */
|
|
int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
|
|
SessionApplyCtx sApply; /* changeset_apply() context object */
|
|
@@ -179954,8 +197183,11 @@
|
|
|
|
pIter->in.bNoDiscard = 1;
|
|
memset(&sApply, 0, sizeof(sApply));
|
|
+ sApply.bRebase = (ppRebase && pnRebase);
|
|
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
|
- rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
|
|
+ if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
|
|
+ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
|
|
+ }
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
|
|
}
|
|
@@ -179979,9 +197211,18 @@
|
|
sqlite3_finalize(sApply.pUpdate);
|
|
sqlite3_finalize(sApply.pInsert);
|
|
sqlite3_finalize(sApply.pSelect);
|
|
- memset(&sApply, 0, sizeof(sApply));
|
|
sApply.db = db;
|
|
+ sApply.pDelete = 0;
|
|
+ sApply.pUpdate = 0;
|
|
+ sApply.pInsert = 0;
|
|
+ sApply.pSelect = 0;
|
|
+ sApply.nCol = 0;
|
|
+ sApply.azCol = 0;
|
|
+ sApply.abPK = 0;
|
|
+ sApply.bStat1 = 0;
|
|
sApply.bDeferConstraints = 1;
|
|
+ sApply.bRebaseStarted = 0;
|
|
+ memset(&sApply.constraints, 0, sizeof(SessionBuffer));
|
|
|
|
/* If an xFilter() callback was specified, invoke it now. If the
|
|
** xFilter callback returns zero, skip this table. If it returns
|
|
@@ -180030,12 +197271,20 @@
|
|
}
|
|
else{
|
|
sApply.nCol = nCol;
|
|
- if((rc = sessionSelectRow(db, zTab, &sApply))
|
|
- || (rc = sessionUpdateRow(db, zTab, &sApply))
|
|
- || (rc = sessionDeleteRow(db, zTab, &sApply))
|
|
- || (rc = sessionInsertRow(db, zTab, &sApply))
|
|
- ){
|
|
- break;
|
|
+ if( 0==sqlite3_stricmp(zTab, "sqlite_stat1") ){
|
|
+ if( (rc = sessionStat1Sql(db, &sApply) ) ){
|
|
+ break;
|
|
+ }
|
|
+ sApply.bStat1 = 1;
|
|
+ }else{
|
|
+ if((rc = sessionSelectRow(db, zTab, &sApply))
|
|
+ || (rc = sessionUpdateRow(db, zTab, &sApply))
|
|
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
|
|
+ || (rc = sessionInsertRow(db, zTab, &sApply))
|
|
+ ){
|
|
+ break;
|
|
+ }
|
|
+ sApply.bStat1 = 0;
|
|
}
|
|
}
|
|
nTab = sqlite3Strlen30(zTab);
|
|
@@ -180076,13 +197325,21 @@
|
|
}
|
|
sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
|
|
|
|
- if( rc==SQLITE_OK ){
|
|
- rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
|
|
- }else{
|
|
- sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
|
|
- sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
|
|
+ if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
|
|
+ }else{
|
|
+ sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
|
|
+ sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
|
|
+ }
|
|
}
|
|
|
|
+ assert( sApply.bRebase || sApply.rebase.nBuf==0 );
|
|
+ if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
|
|
+ *ppRebase = (void*)sApply.rebase.aBuf;
|
|
+ *pnRebase = sApply.rebase.nBuf;
|
|
+ sApply.rebase.aBuf = 0;
|
|
+ }
|
|
sqlite3_finalize(sApply.pInsert);
|
|
sqlite3_finalize(sApply.pDelete);
|
|
sqlite3_finalize(sApply.pUpdate);
|
|
@@ -180089,11 +197346,44 @@
|
|
sqlite3_finalize(sApply.pSelect);
|
|
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
|
|
sqlite3_free((char*)sApply.constraints.aBuf);
|
|
+ sqlite3_free((char*)sApply.rebase.aBuf);
|
|
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
+** Apply the changeset passed via pChangeset/nChangeset to the main
|
|
+** database attached to handle "db".
|
|
+*/
|
|
+SQLITE_API int sqlite3changeset_apply_v2(
|
|
+ sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
+ int nChangeset, /* Size of changeset in bytes */
|
|
+ void *pChangeset, /* Changeset blob */
|
|
+ int(*xFilter)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ const char *zTab /* Table name */
|
|
+ ),
|
|
+ int(*xConflict)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
+ ),
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase,
|
|
+ int flags
|
|
+){
|
|
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
|
+ int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
|
+ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionChangesetApply(
|
|
+ db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
|
+ );
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
** Apply the changeset passed via pChangeset/nChangeset to the main database
|
|
** attached to handle "db". Invoke the supplied conflict handler callback
|
|
** to resolve any conflicts encountered while applying the change.
|
|
@@ -180113,12 +197403,9 @@
|
|
),
|
|
void *pCtx /* First argument passed to xConflict */
|
|
){
|
|
- sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
|
- int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
|
|
- if( rc==SQLITE_OK ){
|
|
- rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
|
|
- }
|
|
- return rc;
|
|
+ return sqlite3changeset_apply_v2(
|
|
+ db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0
|
|
+ );
|
|
}
|
|
|
|
/*
|
|
@@ -180126,7 +197413,7 @@
|
|
** attached to handle "db". Invoke the supplied conflict handler callback
|
|
** to resolve any conflicts encountered while applying the change.
|
|
*/
|
|
-SQLITE_API int sqlite3changeset_apply_strm(
|
|
+SQLITE_API int sqlite3changeset_apply_v2_strm(
|
|
sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
|
void *pIn, /* First arg for xInput */
|
|
@@ -180139,15 +197426,39 @@
|
|
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
),
|
|
- void *pCtx /* First argument passed to xConflict */
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase,
|
|
+ int flags
|
|
){
|
|
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
|
- int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
|
|
+ int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
|
+ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
|
|
+ rc = sessionChangesetApply(
|
|
+ db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
|
+ );
|
|
}
|
|
return rc;
|
|
}
|
|
+SQLITE_API int sqlite3changeset_apply_strm(
|
|
+ sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
|
+ void *pIn, /* First arg for xInput */
|
|
+ int(*xFilter)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ const char *zTab /* Table name */
|
|
+ ),
|
|
+ int(*xConflict)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
+ ),
|
|
+ void *pCtx /* First argument passed to xConflict */
|
|
+){
|
|
+ return sqlite3changeset_apply_v2_strm(
|
|
+ db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0
|
|
+ );
|
|
+}
|
|
|
|
/*
|
|
** sqlite3_changegroup handle.
|
|
@@ -180165,6 +197476,7 @@
|
|
*/
|
|
static int sessionChangeMerge(
|
|
SessionTable *pTab, /* Table structure */
|
|
+ int bRebase, /* True for a rebase hash-table */
|
|
int bPatchset, /* True for patchsets */
|
|
SessionChange *pExist, /* Existing change */
|
|
int op2, /* Second change operation */
|
|
@@ -180174,6 +197486,7 @@
|
|
SessionChange **ppNew /* OUT: Merged change */
|
|
){
|
|
SessionChange *pNew = 0;
|
|
+ int rc = SQLITE_OK;
|
|
|
|
if( !pExist ){
|
|
pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec);
|
|
@@ -180183,9 +197496,66 @@
|
|
memset(pNew, 0, sizeof(SessionChange));
|
|
pNew->op = op2;
|
|
pNew->bIndirect = bIndirect;
|
|
- pNew->nRecord = nRec;
|
|
pNew->aRecord = (u8*)&pNew[1];
|
|
- memcpy(pNew->aRecord, aRec, nRec);
|
|
+ if( bIndirect==0 || bRebase==0 ){
|
|
+ pNew->nRecord = nRec;
|
|
+ memcpy(pNew->aRecord, aRec, nRec);
|
|
+ }else{
|
|
+ int i;
|
|
+ u8 *pIn = aRec;
|
|
+ u8 *pOut = pNew->aRecord;
|
|
+ for(i=0; i<pTab->nCol; i++){
|
|
+ int nIn = sessionSerialLen(pIn);
|
|
+ if( *pIn==0 ){
|
|
+ *pOut++ = 0;
|
|
+ }else if( pTab->abPK[i]==0 ){
|
|
+ *pOut++ = 0xFF;
|
|
+ }else{
|
|
+ memcpy(pOut, pIn, nIn);
|
|
+ pOut += nIn;
|
|
+ }
|
|
+ pIn += nIn;
|
|
+ }
|
|
+ pNew->nRecord = pOut - pNew->aRecord;
|
|
+ }
|
|
+ }else if( bRebase ){
|
|
+ if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){
|
|
+ *ppNew = pExist;
|
|
+ }else{
|
|
+ int nByte = nRec + pExist->nRecord + sizeof(SessionChange);
|
|
+ pNew = (SessionChange*)sqlite3_malloc(nByte);
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
+ }else{
|
|
+ int i;
|
|
+ u8 *a1 = pExist->aRecord;
|
|
+ u8 *a2 = aRec;
|
|
+ u8 *pOut;
|
|
+
|
|
+ memset(pNew, 0, nByte);
|
|
+ pNew->bIndirect = bIndirect || pExist->bIndirect;
|
|
+ pNew->op = op2;
|
|
+ pOut = pNew->aRecord = (u8*)&pNew[1];
|
|
+
|
|
+ for(i=0; i<pTab->nCol; i++){
|
|
+ int n1 = sessionSerialLen(a1);
|
|
+ int n2 = sessionSerialLen(a2);
|
|
+ if( *a1==0xFF || (pTab->abPK[i]==0 && bIndirect) ){
|
|
+ *pOut++ = 0xFF;
|
|
+ }else if( *a2==0 ){
|
|
+ memcpy(pOut, a1, n1);
|
|
+ pOut += n1;
|
|
+ }else{
|
|
+ memcpy(pOut, a2, n2);
|
|
+ pOut += n2;
|
|
+ }
|
|
+ a1 += n1;
|
|
+ a2 += n2;
|
|
+ }
|
|
+ pNew->nRecord = pOut - pNew->aRecord;
|
|
+ }
|
|
+ sqlite3_free(pExist);
|
|
+ }
|
|
}else{
|
|
int op1 = pExist->op;
|
|
|
|
@@ -180279,7 +197649,7 @@
|
|
}
|
|
|
|
*ppNew = pNew;
|
|
- return SQLITE_OK;
|
|
+ return rc;
|
|
}
|
|
|
|
/*
|
|
@@ -180288,7 +197658,8 @@
|
|
*/
|
|
static int sessionChangesetToHash(
|
|
sqlite3_changeset_iter *pIter, /* Iterator to read from */
|
|
- sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */
|
|
+ sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
|
|
+ int bRebase /* True if hash table is for rebasing */
|
|
){
|
|
u8 *aRec;
|
|
int nRec;
|
|
@@ -180295,8 +197666,7 @@
|
|
int rc = SQLITE_OK;
|
|
SessionTable *pTab = 0;
|
|
|
|
-
|
|
- while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
|
|
+ while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
|
|
const char *zNew;
|
|
int nCol;
|
|
int op;
|
|
@@ -180376,7 +197746,7 @@
|
|
}
|
|
}
|
|
|
|
- rc = sessionChangeMerge(pTab,
|
|
+ rc = sessionChangeMerge(pTab, bRebase,
|
|
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
|
|
);
|
|
if( rc ) break;
|
|
@@ -180435,13 +197805,12 @@
|
|
sessionAppendByte(&buf, p->op, &rc);
|
|
sessionAppendByte(&buf, p->bIndirect, &rc);
|
|
sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
|
|
+ if( rc==SQLITE_OK && xOutput && buf.nBuf>=sessions_strm_chunk_size ){
|
|
+ rc = xOutput(pOut, buf.aBuf, buf.nBuf);
|
|
+ buf.nBuf = 0;
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
- if( rc==SQLITE_OK && xOutput && buf.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
|
|
- rc = xOutput(pOut, buf.aBuf, buf.nBuf);
|
|
- buf.nBuf = 0;
|
|
- }
|
|
}
|
|
|
|
if( rc==SQLITE_OK ){
|
|
@@ -180484,7 +197853,7 @@
|
|
|
|
rc = sqlite3changeset_start(&pIter, nData, pData);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sessionChangesetToHash(pIter, pGrp);
|
|
+ rc = sessionChangesetToHash(pIter, pGrp, 0);
|
|
}
|
|
sqlite3changeset_finalize(pIter);
|
|
return rc;
|
|
@@ -180515,7 +197884,7 @@
|
|
|
|
rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
|
|
if( rc==SQLITE_OK ){
|
|
- rc = sessionChangesetToHash(pIter, pGrp);
|
|
+ rc = sessionChangesetToHash(pIter, pGrp, 0);
|
|
}
|
|
sqlite3changeset_finalize(pIter);
|
|
return rc;
|
|
@@ -180600,2439 +197969,373 @@
|
|
return rc;
|
|
}
|
|
|
|
-#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
|
|
-
|
|
-/************** End of sqlite3session.c **************************************/
|
|
-/************** Begin file json1.c *******************************************/
|
|
/*
|
|
-** 2015-08-12
|
|
-**
|
|
-** 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 JSON functions. The interface is
|
|
-** modeled after MySQL JSON functions:
|
|
-**
|
|
-** https://dev.mysql.com/doc/refman/5.7/en/json.html
|
|
-**
|
|
-** For the time being, all JSON is stored as pure text. (We might add
|
|
-** a JSONB type in the future which stores a binary encoding of JSON in
|
|
-** a BLOB, but there is no support for JSONB in the current implementation.
|
|
-** This implementation parses JSON text at 250 MB/s, so it is hard to see
|
|
-** how JSONB might improve on that.)
|
|
+** Changeset rebaser handle.
|
|
*/
|
|
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
|
|
-#if !defined(SQLITEINT_H)
|
|
-/* #include "sqlite3ext.h" */
|
|
-#endif
|
|
-SQLITE_EXTENSION_INIT1
|
|
-/* #include <assert.h> */
|
|
-/* #include <string.h> */
|
|
-/* #include <stdlib.h> */
|
|
-/* #include <stdarg.h> */
|
|
-
|
|
-/* Mark a function parameter as unused, to suppress nuisance compiler
|
|
-** warnings. */
|
|
-#ifndef UNUSED_PARAM
|
|
-# define UNUSED_PARAM(X) (void)(X)
|
|
-#endif
|
|
-
|
|
-#ifndef LARGEST_INT64
|
|
-# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
|
-# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
|
-#endif
|
|
-
|
|
-/*
|
|
-** Versions of isspace(), isalnum() and isdigit() to which it is safe
|
|
-** to pass signed char values.
|
|
-*/
|
|
-#ifdef sqlite3Isdigit
|
|
- /* Use the SQLite core versions if this routine is part of the
|
|
- ** SQLite amalgamation */
|
|
-# define safe_isdigit(x) sqlite3Isdigit(x)
|
|
-# define safe_isalnum(x) sqlite3Isalnum(x)
|
|
-# define safe_isxdigit(x) sqlite3Isxdigit(x)
|
|
-#else
|
|
- /* Use the standard library for separate compilation */
|
|
-#include <ctype.h> /* amalgamator: keep */
|
|
-# define safe_isdigit(x) isdigit((unsigned char)(x))
|
|
-# define safe_isalnum(x) isalnum((unsigned char)(x))
|
|
-# define safe_isxdigit(x) isxdigit((unsigned char)(x))
|
|
-#endif
|
|
-
|
|
-/*
|
|
-** Growing our own isspace() routine this way is twice as fast as
|
|
-** the library isspace() function, resulting in a 7% overall performance
|
|
-** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
|
|
-*/
|
|
-static const char jsonIsSpace[] = {
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+struct sqlite3_rebaser {
|
|
+ sqlite3_changegroup grp; /* Hash table */
|
|
};
|
|
-#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
|
|
|
|
-#ifndef SQLITE_AMALGAMATION
|
|
- /* Unsigned integer types. These are already defined in the sqliteInt.h,
|
|
- ** but the definitions need to be repeated for separate compilation. */
|
|
- typedef sqlite3_uint64 u64;
|
|
- typedef unsigned int u32;
|
|
- typedef unsigned short int u16;
|
|
- typedef unsigned char u8;
|
|
-#endif
|
|
-
|
|
-/* Objects */
|
|
-typedef struct JsonString JsonString;
|
|
-typedef struct JsonNode JsonNode;
|
|
-typedef struct JsonParse JsonParse;
|
|
-
|
|
-/* An instance of this object represents a JSON string
|
|
-** under construction. Really, this is a generic string accumulator
|
|
-** that can be and is used to create strings other than JSON.
|
|
-*/
|
|
-struct JsonString {
|
|
- sqlite3_context *pCtx; /* Function context - put error messages here */
|
|
- char *zBuf; /* Append JSON content here */
|
|
- u64 nAlloc; /* Bytes of storage available in zBuf[] */
|
|
- u64 nUsed; /* Bytes of zBuf[] currently used */
|
|
- u8 bStatic; /* True if zBuf is static space */
|
|
- u8 bErr; /* True if an error has been encountered */
|
|
- char zSpace[100]; /* Initial static space */
|
|
-};
|
|
-
|
|
-/* JSON type values
|
|
-*/
|
|
-#define JSON_NULL 0
|
|
-#define JSON_TRUE 1
|
|
-#define JSON_FALSE 2
|
|
-#define JSON_INT 3
|
|
-#define JSON_REAL 4
|
|
-#define JSON_STRING 5
|
|
-#define JSON_ARRAY 6
|
|
-#define JSON_OBJECT 7
|
|
-
|
|
-/* The "subtype" set for JSON values */
|
|
-#define JSON_SUBTYPE 74 /* Ascii for "J" */
|
|
-
|
|
/*
|
|
-** Names of the various JSON types:
|
|
+** Buffers a1 and a2 must both contain a sessions module record nCol
|
|
+** fields in size. This function appends an nCol sessions module
|
|
+** record to buffer pBuf that is a copy of a1, except that for
|
|
+** each field that is undefined in a1[], swap in the field from a2[].
|
|
*/
|
|
-static const char * const jsonType[] = {
|
|
- "null", "true", "false", "integer", "real", "text", "array", "object"
|
|
-};
|
|
-
|
|
-/* Bit values for the JsonNode.jnFlag field
|
|
-*/
|
|
-#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
|
|
-#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
|
|
-#define JNODE_REMOVE 0x04 /* Do not output */
|
|
-#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
|
|
-#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
|
|
-#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
|
|
-#define JNODE_LABEL 0x40 /* Is a label of an object */
|
|
-
|
|
-
|
|
-/* A single node of parsed JSON
|
|
-*/
|
|
-struct JsonNode {
|
|
- u8 eType; /* One of the JSON_ type values */
|
|
- u8 jnFlags; /* JNODE flags */
|
|
- u32 n; /* Bytes of content, or number of sub-nodes */
|
|
- union {
|
|
- const char *zJContent; /* Content for INT, REAL, and STRING */
|
|
- u32 iAppend; /* More terms for ARRAY and OBJECT */
|
|
- u32 iKey; /* Key for ARRAY objects in json_tree() */
|
|
- u32 iReplace; /* Replacement content for JNODE_REPLACE */
|
|
- JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */
|
|
- } u;
|
|
-};
|
|
-
|
|
-/* A completely parsed JSON string
|
|
-*/
|
|
-struct JsonParse {
|
|
- u32 nNode; /* Number of slots of aNode[] used */
|
|
- u32 nAlloc; /* Number of slots of aNode[] allocated */
|
|
- JsonNode *aNode; /* Array of nodes containing the parse */
|
|
- const char *zJson; /* Original JSON string */
|
|
- u32 *aUp; /* Index of parent of each node */
|
|
- u8 oom; /* Set to true if out of memory */
|
|
- u8 nErr; /* Number of errors seen */
|
|
- u16 iDepth; /* Nesting depth */
|
|
- int nJson; /* Length of the zJson string in bytes */
|
|
-};
|
|
-
|
|
-/*
|
|
-** Maximum nesting depth of JSON for this implementation.
|
|
-**
|
|
-** This limit is needed to avoid a stack overflow in the recursive
|
|
-** descent parser. A depth of 2000 is far deeper than any sane JSON
|
|
-** should go.
|
|
-*/
|
|
-#define JSON_MAX_DEPTH 2000
|
|
-
|
|
-/**************************************************************************
|
|
-** Utility routines for dealing with JsonString objects
|
|
-**************************************************************************/
|
|
-
|
|
-/* Set the JsonString object to an empty string
|
|
-*/
|
|
-static void jsonZero(JsonString *p){
|
|
- p->zBuf = p->zSpace;
|
|
- p->nAlloc = sizeof(p->zSpace);
|
|
- p->nUsed = 0;
|
|
- p->bStatic = 1;
|
|
-}
|
|
-
|
|
-/* Initialize the JsonString object
|
|
-*/
|
|
-static void jsonInit(JsonString *p, sqlite3_context *pCtx){
|
|
- p->pCtx = pCtx;
|
|
- p->bErr = 0;
|
|
- jsonZero(p);
|
|
-}
|
|
-
|
|
-
|
|
-/* Free all allocated memory and reset the JsonString object back to its
|
|
-** initial state.
|
|
-*/
|
|
-static void jsonReset(JsonString *p){
|
|
- if( !p->bStatic ) sqlite3_free(p->zBuf);
|
|
- jsonZero(p);
|
|
-}
|
|
-
|
|
-
|
|
-/* Report an out-of-memory (OOM) condition
|
|
-*/
|
|
-static void jsonOom(JsonString *p){
|
|
- p->bErr = 1;
|
|
- sqlite3_result_error_nomem(p->pCtx);
|
|
- jsonReset(p);
|
|
-}
|
|
-
|
|
-/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
|
|
-** Return zero on success. Return non-zero on an OOM error
|
|
-*/
|
|
-static int jsonGrow(JsonString *p, u32 N){
|
|
- u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
|
|
- char *zNew;
|
|
- if( p->bStatic ){
|
|
- if( p->bErr ) return 1;
|
|
- zNew = sqlite3_malloc64(nTotal);
|
|
- if( zNew==0 ){
|
|
- jsonOom(p);
|
|
- return SQLITE_NOMEM;
|
|
- }
|
|
- memcpy(zNew, p->zBuf, (size_t)p->nUsed);
|
|
- p->zBuf = zNew;
|
|
- p->bStatic = 0;
|
|
- }else{
|
|
- zNew = sqlite3_realloc64(p->zBuf, nTotal);
|
|
- if( zNew==0 ){
|
|
- jsonOom(p);
|
|
- return SQLITE_NOMEM;
|
|
- }
|
|
- p->zBuf = zNew;
|
|
- }
|
|
- p->nAlloc = nTotal;
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* Append N bytes from zIn onto the end of the JsonString string.
|
|
-*/
|
|
-static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
|
|
- if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
|
|
- memcpy(p->zBuf+p->nUsed, zIn, N);
|
|
- p->nUsed += N;
|
|
-}
|
|
-
|
|
-/* Append formatted text (not to exceed N bytes) to the JsonString.
|
|
-*/
|
|
-static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
|
|
- va_list ap;
|
|
- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
|
|
- va_start(ap, zFormat);
|
|
- sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
|
|
- va_end(ap);
|
|
- p->nUsed += (int)strlen(p->zBuf+p->nUsed);
|
|
-}
|
|
-
|
|
-/* Append a single character
|
|
-*/
|
|
-static void jsonAppendChar(JsonString *p, char c){
|
|
- if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return;
|
|
- p->zBuf[p->nUsed++] = c;
|
|
-}
|
|
-
|
|
-/* Append a comma separator to the output buffer, if the previous
|
|
-** character is not '[' or '{'.
|
|
-*/
|
|
-static void jsonAppendSeparator(JsonString *p){
|
|
- char c;
|
|
- if( p->nUsed==0 ) return;
|
|
- c = p->zBuf[p->nUsed-1];
|
|
- if( c!='[' && c!='{' ) jsonAppendChar(p, ',');
|
|
-}
|
|
-
|
|
-/* Append the N-byte string in zIn to the end of the JsonString string
|
|
-** under construction. Enclose the string in "..." and escape
|
|
-** any double-quotes or backslash characters contained within the
|
|
-** string.
|
|
-*/
|
|
-static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
|
|
- u32 i;
|
|
- if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
|
|
- p->zBuf[p->nUsed++] = '"';
|
|
- for(i=0; i<N; i++){
|
|
- unsigned char c = ((unsigned const char*)zIn)[i];
|
|
- if( c=='"' || c=='\\' ){
|
|
- json_simple_escape:
|
|
- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
|
|
- p->zBuf[p->nUsed++] = '\\';
|
|
- }else if( c<=0x1f ){
|
|
- static const char aSpecial[] = {
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
- };
|
|
- assert( sizeof(aSpecial)==32 );
|
|
- assert( aSpecial['\b']=='b' );
|
|
- assert( aSpecial['\f']=='f' );
|
|
- assert( aSpecial['\n']=='n' );
|
|
- assert( aSpecial['\r']=='r' );
|
|
- assert( aSpecial['\t']=='t' );
|
|
- if( aSpecial[c] ){
|
|
- c = aSpecial[c];
|
|
- goto json_simple_escape;
|
|
- }
|
|
- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
|
|
- p->zBuf[p->nUsed++] = '\\';
|
|
- p->zBuf[p->nUsed++] = 'u';
|
|
- p->zBuf[p->nUsed++] = '0';
|
|
- p->zBuf[p->nUsed++] = '0';
|
|
- p->zBuf[p->nUsed++] = '0' + (c>>4);
|
|
- c = "0123456789abcdef"[c&0xf];
|
|
- }
|
|
- p->zBuf[p->nUsed++] = c;
|
|
- }
|
|
- p->zBuf[p->nUsed++] = '"';
|
|
- assert( p->nUsed<p->nAlloc );
|
|
-}
|
|
-
|
|
-/*
|
|
-** Append a function parameter value to the JSON string under
|
|
-** construction.
|
|
-*/
|
|
-static void jsonAppendValue(
|
|
- JsonString *p, /* Append to this JSON string */
|
|
- sqlite3_value *pValue /* Value to append */
|
|
+static void sessionAppendRecordMerge(
|
|
+ SessionBuffer *pBuf, /* Buffer to append to */
|
|
+ int nCol, /* Number of columns in each record */
|
|
+ u8 *a1, int n1, /* Record 1 */
|
|
+ u8 *a2, int n2, /* Record 2 */
|
|
+ int *pRc /* IN/OUT: error code */
|
|
){
|
|
- switch( sqlite3_value_type(pValue) ){
|
|
- case SQLITE_NULL: {
|
|
- jsonAppendRaw(p, "null", 4);
|
|
- break;
|
|
- }
|
|
- case SQLITE_INTEGER:
|
|
- case SQLITE_FLOAT: {
|
|
- const char *z = (const char*)sqlite3_value_text(pValue);
|
|
- u32 n = (u32)sqlite3_value_bytes(pValue);
|
|
- jsonAppendRaw(p, z, n);
|
|
- break;
|
|
- }
|
|
- case SQLITE_TEXT: {
|
|
- const char *z = (const char*)sqlite3_value_text(pValue);
|
|
- u32 n = (u32)sqlite3_value_bytes(pValue);
|
|
- if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
|
|
- jsonAppendRaw(p, z, n);
|
|
+ sessionBufferGrow(pBuf, n1+n2, pRc);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ int i;
|
|
+ u8 *pOut = &pBuf->aBuf[pBuf->nBuf];
|
|
+ for(i=0; i<nCol; i++){
|
|
+ int nn1 = sessionSerialLen(a1);
|
|
+ int nn2 = sessionSerialLen(a2);
|
|
+ if( *a1==0 || *a1==0xFF ){
|
|
+ memcpy(pOut, a2, nn2);
|
|
+ pOut += nn2;
|
|
}else{
|
|
- jsonAppendString(p, z, n);
|
|
+ memcpy(pOut, a1, nn1);
|
|
+ pOut += nn1;
|
|
}
|
|
- break;
|
|
+ a1 += nn1;
|
|
+ a2 += nn2;
|
|
}
|
|
- default: {
|
|
- if( p->bErr==0 ){
|
|
- sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
|
|
- p->bErr = 2;
|
|
- jsonReset(p);
|
|
- }
|
|
- break;
|
|
- }
|
|
- }
|
|
-}
|
|
|
|
-
|
|
-/* Make the JSON in p the result of the SQL function.
|
|
-*/
|
|
-static void jsonResult(JsonString *p){
|
|
- if( p->bErr==0 ){
|
|
- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
|
|
- p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
|
|
- SQLITE_UTF8);
|
|
- jsonZero(p);
|
|
+ pBuf->nBuf = pOut-pBuf->aBuf;
|
|
+ assert( pBuf->nBuf<=pBuf->nAlloc );
|
|
}
|
|
- assert( p->bStatic );
|
|
}
|
|
|
|
-/**************************************************************************
|
|
-** Utility routines for dealing with JsonNode and JsonParse objects
|
|
-**************************************************************************/
|
|
-
|
|
/*
|
|
-** Return the number of consecutive JsonNode slots need to represent
|
|
-** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
|
|
-** OBJECT types, the number might be larger.
|
|
+** This function is called when rebasing a local UPDATE change against one
|
|
+** or more remote UPDATE changes. The aRec/nRec buffer contains the current
|
|
+** old.* and new.* records for the change. The rebase buffer (a single
|
|
+** record) is in aChange/nChange. The rebased change is appended to buffer
|
|
+** pBuf.
|
|
**
|
|
-** Appended elements are not counted. The value returned is the number
|
|
-** by which the JsonNode counter should increment in order to go to the
|
|
-** next peer value.
|
|
+** Rebasing the UPDATE involves:
|
|
+**
|
|
+** * Removing any changes to fields for which the corresponding field
|
|
+** in the rebase buffer is set to "replaced" (type 0xFF). If this
|
|
+** means the UPDATE change updates no fields, nothing is appended
|
|
+** to the output buffer.
|
|
+**
|
|
+** * For each field modified by the local change for which the
|
|
+** corresponding field in the rebase buffer is not "undefined" (0x00)
|
|
+** or "replaced" (0xFF), the old.* value is replaced by the value
|
|
+** in the rebase buffer.
|
|
*/
|
|
-static u32 jsonNodeSize(JsonNode *pNode){
|
|
- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Reclaim all memory allocated by a JsonParse object. But do not
|
|
-** delete the JsonParse object itself.
|
|
-*/
|
|
-static void jsonParseReset(JsonParse *pParse){
|
|
- sqlite3_free(pParse->aNode);
|
|
- pParse->aNode = 0;
|
|
- pParse->nNode = 0;
|
|
- pParse->nAlloc = 0;
|
|
- sqlite3_free(pParse->aUp);
|
|
- pParse->aUp = 0;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Free a JsonParse object that was obtained from sqlite3_malloc().
|
|
-*/
|
|
-static void jsonParseFree(JsonParse *pParse){
|
|
- jsonParseReset(pParse);
|
|
- sqlite3_free(pParse);
|
|
-}
|
|
-
|
|
-/*
|
|
-** Convert the JsonNode pNode into a pure JSON string and
|
|
-** append to pOut. Subsubstructure is also included. Return
|
|
-** the number of JsonNode objects that are encoded.
|
|
-*/
|
|
-static void jsonRenderNode(
|
|
- JsonNode *pNode, /* The node to render */
|
|
- JsonString *pOut, /* Write JSON here */
|
|
- sqlite3_value **aReplace /* Replacement values */
|
|
+static void sessionAppendPartialUpdate(
|
|
+ SessionBuffer *pBuf, /* Append record here */
|
|
+ sqlite3_changeset_iter *pIter, /* Iterator pointed at local change */
|
|
+ u8 *aRec, int nRec, /* Local change */
|
|
+ u8 *aChange, int nChange, /* Record to rebase against */
|
|
+ int *pRc /* IN/OUT: Return Code */
|
|
){
|
|
- if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
|
|
- if( pNode->jnFlags & JNODE_REPLACE ){
|
|
- jsonAppendValue(pOut, aReplace[pNode->u.iReplace]);
|
|
- return;
|
|
- }
|
|
- pNode = pNode->u.pPatch;
|
|
- }
|
|
- switch( pNode->eType ){
|
|
- default: {
|
|
- assert( pNode->eType==JSON_NULL );
|
|
- jsonAppendRaw(pOut, "null", 4);
|
|
- break;
|
|
- }
|
|
- case JSON_TRUE: {
|
|
- jsonAppendRaw(pOut, "true", 4);
|
|
- break;
|
|
- }
|
|
- case JSON_FALSE: {
|
|
- jsonAppendRaw(pOut, "false", 5);
|
|
- break;
|
|
- }
|
|
- case JSON_STRING: {
|
|
- if( pNode->jnFlags & JNODE_RAW ){
|
|
- jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
|
|
- break;
|
|
- }
|
|
- /* Fall through into the next case */
|
|
- }
|
|
- case JSON_REAL:
|
|
- case JSON_INT: {
|
|
- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
|
|
- break;
|
|
- }
|
|
- case JSON_ARRAY: {
|
|
- u32 j = 1;
|
|
- jsonAppendChar(pOut, '[');
|
|
- for(;;){
|
|
- while( j<=pNode->n ){
|
|
- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){
|
|
- jsonAppendSeparator(pOut);
|
|
- jsonRenderNode(&pNode[j], pOut, aReplace);
|
|
- }
|
|
- j += jsonNodeSize(&pNode[j]);
|
|
- }
|
|
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
|
|
- pNode = &pNode[pNode->u.iAppend];
|
|
- j = 1;
|
|
- }
|
|
- jsonAppendChar(pOut, ']');
|
|
- break;
|
|
- }
|
|
- case JSON_OBJECT: {
|
|
- u32 j = 1;
|
|
- jsonAppendChar(pOut, '{');
|
|
- for(;;){
|
|
- while( j<=pNode->n ){
|
|
- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
|
|
- jsonAppendSeparator(pOut);
|
|
- jsonRenderNode(&pNode[j], pOut, aReplace);
|
|
- jsonAppendChar(pOut, ':');
|
|
- jsonRenderNode(&pNode[j+1], pOut, aReplace);
|
|
- }
|
|
- j += 1 + jsonNodeSize(&pNode[j+1]);
|
|
- }
|
|
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
|
|
- pNode = &pNode[pNode->u.iAppend];
|
|
- j = 1;
|
|
- }
|
|
- jsonAppendChar(pOut, '}');
|
|
- break;
|
|
- }
|
|
- }
|
|
-}
|
|
+ sessionBufferGrow(pBuf, 2+nRec+nChange, pRc);
|
|
+ if( *pRc==SQLITE_OK ){
|
|
+ int bData = 0;
|
|
+ u8 *pOut = &pBuf->aBuf[pBuf->nBuf];
|
|
+ int i;
|
|
+ u8 *a1 = aRec;
|
|
+ u8 *a2 = aChange;
|
|
|
|
-/*
|
|
-** Return a JsonNode and all its descendents as a JSON string.
|
|
-*/
|
|
-static void jsonReturnJson(
|
|
- JsonNode *pNode, /* Node to return */
|
|
- sqlite3_context *pCtx, /* Return value for this function */
|
|
- sqlite3_value **aReplace /* Array of replacement values */
|
|
-){
|
|
- JsonString s;
|
|
- jsonInit(&s, pCtx);
|
|
- jsonRenderNode(pNode, &s, aReplace);
|
|
- jsonResult(&s);
|
|
- sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
|
|
-}
|
|
-
|
|
-/*
|
|
-** Make the JsonNode the return value of the function.
|
|
-*/
|
|
-static void jsonReturn(
|
|
- JsonNode *pNode, /* Node to return */
|
|
- sqlite3_context *pCtx, /* Return value for this function */
|
|
- sqlite3_value **aReplace /* Array of replacement values */
|
|
-){
|
|
- switch( pNode->eType ){
|
|
- default: {
|
|
- assert( pNode->eType==JSON_NULL );
|
|
- sqlite3_result_null(pCtx);
|
|
- break;
|
|
- }
|
|
- case JSON_TRUE: {
|
|
- sqlite3_result_int(pCtx, 1);
|
|
- break;
|
|
- }
|
|
- case JSON_FALSE: {
|
|
- sqlite3_result_int(pCtx, 0);
|
|
- break;
|
|
- }
|
|
- case JSON_INT: {
|
|
- sqlite3_int64 i = 0;
|
|
- const char *z = pNode->u.zJContent;
|
|
- if( z[0]=='-' ){ z++; }
|
|
- while( z[0]>='0' && z[0]<='9' ){
|
|
- unsigned v = *(z++) - '0';
|
|
- if( i>=LARGEST_INT64/10 ){
|
|
- if( i>LARGEST_INT64/10 ) goto int_as_real;
|
|
- if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
|
|
- if( v==9 ) goto int_as_real;
|
|
- if( v==8 ){
|
|
- if( pNode->u.zJContent[0]=='-' ){
|
|
- sqlite3_result_int64(pCtx, SMALLEST_INT64);
|
|
- goto int_done;
|
|
- }else{
|
|
- goto int_as_real;
|
|
- }
|
|
- }
|
|
- }
|
|
- i = i*10 + v;
|
|
+ *pOut++ = SQLITE_UPDATE;
|
|
+ *pOut++ = pIter->bIndirect;
|
|
+ for(i=0; i<pIter->nCol; i++){
|
|
+ int n1 = sessionSerialLen(a1);
|
|
+ int n2 = sessionSerialLen(a2);
|
|
+ if( pIter->abPK[i] || a2[0]==0 ){
|
|
+ if( !pIter->abPK[i] ) bData = 1;
|
|
+ memcpy(pOut, a1, n1);
|
|
+ pOut += n1;
|
|
+ }else if( a2[0]!=0xFF ){
|
|
+ bData = 1;
|
|
+ memcpy(pOut, a2, n2);
|
|
+ pOut += n2;
|
|
+ }else{
|
|
+ *pOut++ = '\0';
|
|
}
|
|
- if( pNode->u.zJContent[0]=='-' ){ i = -i; }
|
|
- sqlite3_result_int64(pCtx, i);
|
|
- int_done:
|
|
- break;
|
|
- int_as_real: /* fall through to real */;
|
|
+ a1 += n1;
|
|
+ a2 += n2;
|
|
}
|
|
- case JSON_REAL: {
|
|
- double r;
|
|
-#ifdef SQLITE_AMALGAMATION
|
|
- const char *z = pNode->u.zJContent;
|
|
- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
|
|
-#else
|
|
- r = strtod(pNode->u.zJContent, 0);
|
|
-#endif
|
|
- sqlite3_result_double(pCtx, r);
|
|
- break;
|
|
- }
|
|
- case JSON_STRING: {
|
|
-#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
|
|
- ** json_insert() and json_replace() and those routines do not
|
|
- ** call jsonReturn() */
|
|
- if( pNode->jnFlags & JNODE_RAW ){
|
|
- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
|
|
- SQLITE_TRANSIENT);
|
|
- }else
|
|
-#endif
|
|
- assert( (pNode->jnFlags & JNODE_RAW)==0 );
|
|
- if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
|
|
- /* JSON formatted without any backslash-escapes */
|
|
- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
|
|
- SQLITE_TRANSIENT);
|
|
- }else{
|
|
- /* Translate JSON formatted string into raw text */
|
|
- u32 i;
|
|
- u32 n = pNode->n;
|
|
- const char *z = pNode->u.zJContent;
|
|
- char *zOut;
|
|
- u32 j;
|
|
- zOut = sqlite3_malloc( n+1 );
|
|
- if( zOut==0 ){
|
|
- sqlite3_result_error_nomem(pCtx);
|
|
- break;
|
|
+ if( bData ){
|
|
+ a2 = aChange;
|
|
+ for(i=0; i<pIter->nCol; i++){
|
|
+ int n1 = sessionSerialLen(a1);
|
|
+ int n2 = sessionSerialLen(a2);
|
|
+ if( pIter->abPK[i] || a2[0]!=0xFF ){
|
|
+ memcpy(pOut, a1, n1);
|
|
+ pOut += n1;
|
|
+ }else{
|
|
+ *pOut++ = '\0';
|
|
}
|
|
- for(i=1, j=0; i<n-1; i++){
|
|
- char c = z[i];
|
|
- if( c!='\\' ){
|
|
- zOut[j++] = c;
|
|
- }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;
|
|
- }
|
|
- if( v==0 ) break;
|
|
- if( v<=0x7f ){
|
|
- zOut[j++] = (char)v;
|
|
- }else if( v<=0x7ff ){
|
|
- 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);
|
|
- }
|
|
- }else{
|
|
- if( c=='b' ){
|
|
- c = '\b';
|
|
- }else if( c=='f' ){
|
|
- c = '\f';
|
|
- }else if( c=='n' ){
|
|
- c = '\n';
|
|
- }else if( c=='r' ){
|
|
- c = '\r';
|
|
- }else if( c=='t' ){
|
|
- c = '\t';
|
|
- }
|
|
- zOut[j++] = c;
|
|
- }
|
|
- }
|
|
- }
|
|
- zOut[j] = 0;
|
|
- sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
|
|
+ a1 += n1;
|
|
+ a2 += n2;
|
|
}
|
|
- break;
|
|
+ pBuf->nBuf = (pOut - pBuf->aBuf);
|
|
}
|
|
- case JSON_ARRAY:
|
|
- case JSON_OBJECT: {
|
|
- jsonReturnJson(pNode, pCtx, aReplace);
|
|
- break;
|
|
- }
|
|
}
|
|
}
|
|
|
|
-/* Forward reference */
|
|
-static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
|
|
-
|
|
/*
|
|
-** A macro to hint to the compiler that a function should not be
|
|
-** inlined.
|
|
+** pIter is configured to iterate through a changeset. This function rebases
|
|
+** that changeset according to the current configuration of the rebaser
|
|
+** object passed as the first argument. If no error occurs and argument xOutput
|
|
+** is not NULL, then the changeset is returned to the caller by invoking
|
|
+** xOutput zero or more times and SQLITE_OK returned. Or, if xOutput is NULL,
|
|
+** then (*ppOut) is set to point to a buffer containing the rebased changeset
|
|
+** before this function returns. In this case (*pnOut) is set to the size of
|
|
+** the buffer in bytes. It is the responsibility of the caller to eventually
|
|
+** free the (*ppOut) buffer using sqlite3_free().
|
|
+**
|
|
+** If an error occurs, an SQLite error code is returned. If ppOut and
|
|
+** pnOut are not NULL, then the two output parameters are set to 0 before
|
|
+** returning.
|
|
*/
|
|
-#if defined(__GNUC__)
|
|
-# define JSON_NOINLINE __attribute__((noinline))
|
|
-#elif defined(_MSC_VER) && _MSC_VER>=1310
|
|
-# define JSON_NOINLINE __declspec(noinline)
|
|
-#else
|
|
-# define JSON_NOINLINE
|
|
-#endif
|
|
-
|
|
-
|
|
-static JSON_NOINLINE int jsonParseAddNodeExpand(
|
|
- JsonParse *pParse, /* Append the node to this object */
|
|
- u32 eType, /* Node type */
|
|
- u32 n, /* Content size or sub-node count */
|
|
- const char *zContent /* Content */
|
|
+static int sessionRebase(
|
|
+ sqlite3_rebaser *p, /* Rebaser hash table */
|
|
+ sqlite3_changeset_iter *pIter, /* Input data */
|
|
+ int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
+ void *pOut, /* Context for xOutput callback */
|
|
+ int *pnOut, /* OUT: Number of bytes in output changeset */
|
|
+ void **ppOut /* OUT: Inverse of pChangeset */
|
|
){
|
|
- u32 nNew;
|
|
- JsonNode *pNew;
|
|
- assert( pParse->nNode>=pParse->nAlloc );
|
|
- if( pParse->oom ) return -1;
|
|
- nNew = pParse->nAlloc*2 + 10;
|
|
- pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
|
|
- if( pNew==0 ){
|
|
- pParse->oom = 1;
|
|
- return -1;
|
|
- }
|
|
- pParse->nAlloc = nNew;
|
|
- pParse->aNode = pNew;
|
|
- assert( pParse->nNode<pParse->nAlloc );
|
|
- return jsonParseAddNode(pParse, eType, n, zContent);
|
|
-}
|
|
+ int rc = SQLITE_OK;
|
|
+ u8 *aRec = 0;
|
|
+ int nRec = 0;
|
|
+ int bNew = 0;
|
|
+ SessionTable *pTab = 0;
|
|
+ SessionBuffer sOut = {0,0,0};
|
|
|
|
-/*
|
|
-** Create a new JsonNode instance based on the arguments and append that
|
|
-** instance to the JsonParse. Return the index in pParse->aNode[] of the
|
|
-** new node, or -1 if a memory allocation fails.
|
|
-*/
|
|
-static int jsonParseAddNode(
|
|
- JsonParse *pParse, /* Append the node to this object */
|
|
- u32 eType, /* Node type */
|
|
- u32 n, /* Content size or sub-node count */
|
|
- const char *zContent /* Content */
|
|
-){
|
|
- JsonNode *p;
|
|
- if( pParse->nNode>=pParse->nAlloc ){
|
|
- return jsonParseAddNodeExpand(pParse, eType, n, zContent);
|
|
- }
|
|
- p = &pParse->aNode[pParse->nNode];
|
|
- p->eType = (u8)eType;
|
|
- p->jnFlags = 0;
|
|
- p->n = n;
|
|
- p->u.zJContent = zContent;
|
|
- return pParse->nNode++;
|
|
-}
|
|
+ while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){
|
|
+ SessionChange *pChange = 0;
|
|
+ int bDone = 0;
|
|
|
|
-/*
|
|
-** Return true if z[] begins with 4 (or more) hexadecimal digits
|
|
-*/
|
|
-static int jsonIs4Hex(const char *z){
|
|
- int i;
|
|
- for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0;
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Parse a single JSON value which begins at pParse->zJson[i]. Return the
|
|
-** index of the first character past the end of the value parsed.
|
|
-**
|
|
-** Return negative for a syntax error. Special cases: return -2 if the
|
|
-** first non-whitespace character is '}' and return -3 if the first
|
|
-** non-whitespace character is ']'.
|
|
-*/
|
|
-static int jsonParseValue(JsonParse *pParse, u32 i){
|
|
- char c;
|
|
- u32 j;
|
|
- int iThis;
|
|
- int x;
|
|
- JsonNode *pNode;
|
|
- const char *z = pParse->zJson;
|
|
- while( safe_isspace(z[i]) ){ i++; }
|
|
- if( (c = z[i])=='{' ){
|
|
- /* Parse object */
|
|
- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
|
- if( iThis<0 ) return -1;
|
|
- for(j=i+1;;j++){
|
|
- while( safe_isspace(z[j]) ){ j++; }
|
|
- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
|
|
- x = jsonParseValue(pParse, j);
|
|
- if( x<0 ){
|
|
- pParse->iDepth--;
|
|
- if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
|
- return -1;
|
|
+ if( bNew ){
|
|
+ const char *zTab = pIter->zTab;
|
|
+ for(pTab=p->grp.pList; pTab; pTab=pTab->pNext){
|
|
+ if( 0==sqlite3_stricmp(pTab->zName, zTab) ) break;
|
|
}
|
|
- if( pParse->oom ) return -1;
|
|
- pNode = &pParse->aNode[pParse->nNode-1];
|
|
- if( pNode->eType!=JSON_STRING ) return -1;
|
|
- pNode->jnFlags |= JNODE_LABEL;
|
|
- j = x;
|
|
- while( safe_isspace(z[j]) ){ j++; }
|
|
- if( z[j]!=':' ) return -1;
|
|
- j++;
|
|
- x = jsonParseValue(pParse, j);
|
|
- pParse->iDepth--;
|
|
- if( x<0 ) return -1;
|
|
- j = x;
|
|
- while( safe_isspace(z[j]) ){ j++; }
|
|
- c = z[j];
|
|
- if( c==',' ) continue;
|
|
- if( c!='}' ) return -1;
|
|
- break;
|
|
- }
|
|
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
|
|
- return j+1;
|
|
- }else if( c=='[' ){
|
|
- /* Parse array */
|
|
- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
|
- if( iThis<0 ) return -1;
|
|
- for(j=i+1;;j++){
|
|
- while( safe_isspace(z[j]) ){ j++; }
|
|
- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
|
|
- x = jsonParseValue(pParse, j);
|
|
- pParse->iDepth--;
|
|
- if( x<0 ){
|
|
- if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
|
- return -1;
|
|
- }
|
|
- j = x;
|
|
- while( safe_isspace(z[j]) ){ j++; }
|
|
- c = z[j];
|
|
- if( c==',' ) continue;
|
|
- if( c!=']' ) return -1;
|
|
- break;
|
|
- }
|
|
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
|
|
- return j+1;
|
|
- }else if( c=='"' ){
|
|
- /* Parse string */
|
|
- u8 jnFlags = 0;
|
|
- j = i+1;
|
|
- for(;;){
|
|
- c = z[j];
|
|
- if( (c & ~0x1f)==0 ){
|
|
- /* Control characters are not allowed in strings */
|
|
- return -1;
|
|
- }
|
|
- if( c=='\\' ){
|
|
- c = z[++j];
|
|
- if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|
|
- || c=='n' || c=='r' || c=='t'
|
|
- || (c=='u' && jsonIs4Hex(z+j+1)) ){
|
|
- jnFlags = JNODE_ESCAPE;
|
|
- }else{
|
|
- return -1;
|
|
- }
|
|
- }else if( c=='"' ){
|
|
- break;
|
|
- }
|
|
- j++;
|
|
- }
|
|
- jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]);
|
|
- if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
|
|
- return j+1;
|
|
- }else if( c=='n'
|
|
- && strncmp(z+i,"null",4)==0
|
|
- && !safe_isalnum(z[i+4]) ){
|
|
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
|
- return i+4;
|
|
- }else if( c=='t'
|
|
- && strncmp(z+i,"true",4)==0
|
|
- && !safe_isalnum(z[i+4]) ){
|
|
- jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
|
- return i+4;
|
|
- }else if( c=='f'
|
|
- && strncmp(z+i,"false",5)==0
|
|
- && !safe_isalnum(z[i+5]) ){
|
|
- jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
|
|
- return i+5;
|
|
- }else if( c=='-' || (c>='0' && c<='9') ){
|
|
- /* Parse number */
|
|
- u8 seenDP = 0;
|
|
- u8 seenE = 0;
|
|
- assert( '-' < '0' );
|
|
- if( c<='0' ){
|
|
- j = c=='-' ? i+1 : i;
|
|
- if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1;
|
|
- }
|
|
- j = i+1;
|
|
- for(;; j++){
|
|
- c = z[j];
|
|
- if( c>='0' && c<='9' ) continue;
|
|
- if( c=='.' ){
|
|
- if( z[j-1]=='-' ) return -1;
|
|
- if( seenDP ) return -1;
|
|
- seenDP = 1;
|
|
- continue;
|
|
- }
|
|
- if( c=='e' || c=='E' ){
|
|
- if( z[j-1]<'0' ) return -1;
|
|
- if( seenE ) return -1;
|
|
- seenDP = seenE = 1;
|
|
- c = z[j+1];
|
|
- if( c=='+' || c=='-' ){
|
|
- j++;
|
|
- c = z[j+1];
|
|
- }
|
|
- if( c<'0' || c>'9' ) return -1;
|
|
- continue;
|
|
- }
|
|
- break;
|
|
- }
|
|
- if( z[j-1]<'0' ) return -1;
|
|
- jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
|
|
- j - i, &z[i]);
|
|
- return j;
|
|
- }else if( c=='}' ){
|
|
- return -2; /* End of {...} */
|
|
- }else if( c==']' ){
|
|
- return -3; /* End of [...] */
|
|
- }else if( c==0 ){
|
|
- return 0; /* End of file */
|
|
- }else{
|
|
- return -1; /* Syntax error */
|
|
- }
|
|
-}
|
|
+ bNew = 0;
|
|
|
|
-/*
|
|
-** Parse a complete JSON string. Return 0 on success or non-zero if there
|
|
-** are any errors. If an error occurs, free all memory associated with
|
|
-** pParse.
|
|
-**
|
|
-** pParse is uninitialized when this routine is called.
|
|
-*/
|
|
-static int jsonParse(
|
|
- JsonParse *pParse, /* Initialize and fill this JsonParse object */
|
|
- sqlite3_context *pCtx, /* Report errors here */
|
|
- const char *zJson /* Input JSON text to be parsed */
|
|
-){
|
|
- int i;
|
|
- memset(pParse, 0, sizeof(*pParse));
|
|
- if( zJson==0 ) return 1;
|
|
- pParse->zJson = zJson;
|
|
- i = jsonParseValue(pParse, 0);
|
|
- if( pParse->oom ) i = -1;
|
|
- if( i>0 ){
|
|
- assert( pParse->iDepth==0 );
|
|
- while( safe_isspace(zJson[i]) ) i++;
|
|
- if( zJson[i] ) i = -1;
|
|
- }
|
|
- if( i<=0 ){
|
|
- if( pCtx!=0 ){
|
|
- if( pParse->oom ){
|
|
- sqlite3_result_error_nomem(pCtx);
|
|
- }else{
|
|
- sqlite3_result_error(pCtx, "malformed JSON", -1);
|
|
+ /* A patchset may not be rebased */
|
|
+ if( pIter->bPatchset ){
|
|
+ rc = SQLITE_ERROR;
|
|
}
|
|
- }
|
|
- jsonParseReset(pParse);
|
|
- return 1;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
|
|
-/* Mark node i of pParse as being a child of iParent. Call recursively
|
|
-** to fill in all the descendants of node i.
|
|
-*/
|
|
-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
|
|
- JsonNode *pNode = &pParse->aNode[i];
|
|
- u32 j;
|
|
- pParse->aUp[i] = iParent;
|
|
- switch( pNode->eType ){
|
|
- case JSON_ARRAY: {
|
|
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
|
|
- jsonParseFillInParentage(pParse, i+j, i);
|
|
- }
|
|
- break;
|
|
+ /* Append a table header to the output for this new table */
|
|
+ sessionAppendByte(&sOut, pIter->bPatchset ? 'P' : 'T', &rc);
|
|
+ sessionAppendVarint(&sOut, pIter->nCol, &rc);
|
|
+ sessionAppendBlob(&sOut, pIter->abPK, pIter->nCol, &rc);
|
|
+ sessionAppendBlob(&sOut,(u8*)pIter->zTab,(int)strlen(pIter->zTab)+1,&rc);
|
|
}
|
|
- case JSON_OBJECT: {
|
|
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
|
|
- pParse->aUp[i+j] = i;
|
|
- jsonParseFillInParentage(pParse, i+j+1, i);
|
|
- }
|
|
- break;
|
|
- }
|
|
- default: {
|
|
- break;
|
|
- }
|
|
- }
|
|
-}
|
|
|
|
-/*
|
|
-** Compute the parentage of all nodes in a completed parse.
|
|
-*/
|
|
-static int jsonParseFindParents(JsonParse *pParse){
|
|
- u32 *aUp;
|
|
- assert( pParse->aUp==0 );
|
|
- aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
|
|
- if( aUp==0 ){
|
|
- pParse->oom = 1;
|
|
- return SQLITE_NOMEM;
|
|
- }
|
|
- jsonParseFillInParentage(pParse, 0, 0);
|
|
- return SQLITE_OK;
|
|
-}
|
|
+ if( pTab && rc==SQLITE_OK ){
|
|
+ int iHash = sessionChangeHash(pTab, 0, aRec, pTab->nChange);
|
|
|
|
-/*
|
|
-** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
|
|
-*/
|
|
-#define JSON_CACHE_ID (-429938)
|
|
-
|
|
-/*
|
|
-** Obtain a complete parse of the JSON found in the first argument
|
|
-** of the argv array. Use the sqlite3_get_auxdata() cache for this
|
|
-** parse if it is available. If the cache is not available or if it
|
|
-** is no longer valid, parse the JSON again and return the new parse,
|
|
-** and also register the new parse so that it will be available for
|
|
-** future sqlite3_get_auxdata() calls.
|
|
-*/
|
|
-static JsonParse *jsonParseCached(
|
|
- sqlite3_context *pCtx,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- const char *zJson = (const char*)sqlite3_value_text(argv[0]);
|
|
- int nJson = sqlite3_value_bytes(argv[0]);
|
|
- JsonParse *p;
|
|
- if( zJson==0 ) return 0;
|
|
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID);
|
|
- if( p && p->nJson==nJson && memcmp(p->zJson,zJson,nJson)==0 ){
|
|
- p->nErr = 0;
|
|
- return p; /* The cached entry matches, so return it */
|
|
- }
|
|
- p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
|
|
- if( p==0 ){
|
|
- sqlite3_result_error_nomem(pCtx);
|
|
- return 0;
|
|
- }
|
|
- memset(p, 0, sizeof(*p));
|
|
- p->zJson = (char*)&p[1];
|
|
- memcpy((char*)p->zJson, zJson, nJson+1);
|
|
- if( jsonParse(p, pCtx, p->zJson) ){
|
|
- sqlite3_free(p);
|
|
- return 0;
|
|
- }
|
|
- p->nJson = nJson;
|
|
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID, p, (void(*)(void*))jsonParseFree);
|
|
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID);
|
|
-}
|
|
-
|
|
-/*
|
|
-** Compare the OBJECT label at pNode against zKey,nKey. Return true on
|
|
-** a match.
|
|
-*/
|
|
-static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
|
|
- if( pNode->jnFlags & JNODE_RAW ){
|
|
- if( pNode->n!=nKey ) return 0;
|
|
- return strncmp(pNode->u.zJContent, zKey, nKey)==0;
|
|
- }else{
|
|
- if( pNode->n!=nKey+2 ) return 0;
|
|
- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
|
|
- }
|
|
-}
|
|
-
|
|
-/* forward declaration */
|
|
-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
|
|
-
|
|
-/*
|
|
-** Search along zPath to find the node specified. Return a pointer
|
|
-** to that node, or NULL if zPath is malformed or if there is no such
|
|
-** node.
|
|
-**
|
|
-** If pApnd!=0, then try to append new nodes to complete zPath if it is
|
|
-** possible to do so and if no existing node corresponds to zPath. If
|
|
-** new nodes are appended *pApnd is set to 1.
|
|
-*/
|
|
-static JsonNode *jsonLookupStep(
|
|
- JsonParse *pParse, /* The JSON to search */
|
|
- u32 iRoot, /* Begin the search at this node */
|
|
- const char *zPath, /* The path to search */
|
|
- int *pApnd, /* Append nodes to complete path if not NULL */
|
|
- const char **pzErr /* Make *pzErr point to any syntax error in zPath */
|
|
-){
|
|
- u32 i, j, nKey;
|
|
- const char *zKey;
|
|
- JsonNode *pRoot = &pParse->aNode[iRoot];
|
|
- if( zPath[0]==0 ) return pRoot;
|
|
- if( zPath[0]=='.' ){
|
|
- if( pRoot->eType!=JSON_OBJECT ) return 0;
|
|
- zPath++;
|
|
- if( zPath[0]=='"' ){
|
|
- zKey = zPath + 1;
|
|
- for(i=1; zPath[i] && zPath[i]!='"'; i++){}
|
|
- nKey = i-1;
|
|
- if( zPath[i] ){
|
|
- i++;
|
|
- }else{
|
|
- *pzErr = zPath;
|
|
- return 0;
|
|
- }
|
|
- }else{
|
|
- zKey = zPath;
|
|
- for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
|
|
- nKey = i;
|
|
- }
|
|
- if( nKey==0 ){
|
|
- *pzErr = zPath;
|
|
- return 0;
|
|
- }
|
|
- j = 1;
|
|
- for(;;){
|
|
- while( j<=pRoot->n ){
|
|
- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
|
|
- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
|
|
+ for(pChange=pTab->apChange[iHash]; pChange; pChange=pChange->pNext){
|
|
+ if( sessionChangeEqual(pTab, 0, aRec, 0, pChange->aRecord) ){
|
|
+ break;
|
|
}
|
|
- j++;
|
|
- j += jsonNodeSize(&pRoot[j]);
|
|
}
|
|
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
|
|
- iRoot += pRoot->u.iAppend;
|
|
- pRoot = &pParse->aNode[iRoot];
|
|
- j = 1;
|
|
}
|
|
- if( pApnd ){
|
|
- u32 iStart, iLabel;
|
|
- JsonNode *pNode;
|
|
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
|
- iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
|
|
- zPath += i;
|
|
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
|
- if( pParse->oom ) return 0;
|
|
- if( pNode ){
|
|
- pRoot = &pParse->aNode[iRoot];
|
|
- pRoot->u.iAppend = iStart - iRoot;
|
|
- pRoot->jnFlags |= JNODE_APPEND;
|
|
- pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
|
|
- }
|
|
- return pNode;
|
|
- }
|
|
- }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
|
|
- if( pRoot->eType!=JSON_ARRAY ) return 0;
|
|
- i = 0;
|
|
- j = 1;
|
|
- while( safe_isdigit(zPath[j]) ){
|
|
- i = i*10 + zPath[j] - '0';
|
|
- j++;
|
|
- }
|
|
- if( zPath[j]!=']' ){
|
|
- *pzErr = zPath;
|
|
- return 0;
|
|
- }
|
|
- zPath += j + 1;
|
|
- j = 1;
|
|
- for(;;){
|
|
- while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
|
|
- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
|
|
- j += jsonNodeSize(&pRoot[j]);
|
|
- }
|
|
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
|
|
- iRoot += pRoot->u.iAppend;
|
|
- pRoot = &pParse->aNode[iRoot];
|
|
- j = 1;
|
|
- }
|
|
- if( j<=pRoot->n ){
|
|
- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
|
|
- }
|
|
- if( i==0 && pApnd ){
|
|
- u32 iStart;
|
|
- JsonNode *pNode;
|
|
- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
|
|
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
|
- if( pParse->oom ) return 0;
|
|
- if( pNode ){
|
|
- pRoot = &pParse->aNode[iRoot];
|
|
- pRoot->u.iAppend = iStart - iRoot;
|
|
- pRoot->jnFlags |= JNODE_APPEND;
|
|
- }
|
|
- return pNode;
|
|
- }
|
|
- }else{
|
|
- *pzErr = zPath;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
|
|
-/*
|
|
-** Append content to pParse that will complete zPath. Return a pointer
|
|
-** to the inserted node, or return NULL if the append fails.
|
|
-*/
|
|
-static JsonNode *jsonLookupAppend(
|
|
- JsonParse *pParse, /* Append content to the JSON parse */
|
|
- const char *zPath, /* Description of content to append */
|
|
- int *pApnd, /* Set this flag to 1 */
|
|
- const char **pzErr /* Make this point to any syntax error */
|
|
-){
|
|
- *pApnd = 1;
|
|
- if( zPath[0]==0 ){
|
|
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
|
- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
|
|
- }
|
|
- if( zPath[0]=='.' ){
|
|
- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
|
- }else if( strncmp(zPath,"[0]",3)==0 ){
|
|
- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
|
- }else{
|
|
- return 0;
|
|
- }
|
|
- if( pParse->oom ) return 0;
|
|
- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
|
|
-}
|
|
+ if( pChange ){
|
|
+ assert( pChange->op==SQLITE_DELETE || pChange->op==SQLITE_INSERT );
|
|
+ switch( pIter->op ){
|
|
+ case SQLITE_INSERT:
|
|
+ if( pChange->op==SQLITE_INSERT ){
|
|
+ bDone = 1;
|
|
+ if( pChange->bIndirect==0 ){
|
|
+ sessionAppendByte(&sOut, SQLITE_UPDATE, &rc);
|
|
+ sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
+ sessionAppendBlob(&sOut, pChange->aRecord, pChange->nRecord, &rc);
|
|
+ sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
|
|
-/*
|
|
-** Return the text of a syntax error message on a JSON path. Space is
|
|
-** obtained from sqlite3_malloc().
|
|
-*/
|
|
-static char *jsonPathSyntaxError(const char *zErr){
|
|
- return sqlite3_mprintf("JSON path error near '%q'", zErr);
|
|
-}
|
|
+ case SQLITE_UPDATE:
|
|
+ bDone = 1;
|
|
+ if( pChange->op==SQLITE_DELETE ){
|
|
+ if( pChange->bIndirect==0 ){
|
|
+ u8 *pCsr = aRec;
|
|
+ sessionSkipRecord(&pCsr, pIter->nCol);
|
|
+ sessionAppendByte(&sOut, SQLITE_INSERT, &rc);
|
|
+ sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
+ sessionAppendRecordMerge(&sOut, pIter->nCol,
|
|
+ pCsr, nRec-(pCsr-aRec),
|
|
+ pChange->aRecord, pChange->nRecord, &rc
|
|
+ );
|
|
+ }
|
|
+ }else{
|
|
+ sessionAppendPartialUpdate(&sOut, pIter,
|
|
+ aRec, nRec, pChange->aRecord, pChange->nRecord, &rc
|
|
+ );
|
|
+ }
|
|
+ break;
|
|
|
|
-/*
|
|
-** Do a node lookup using zPath. Return a pointer to the node on success.
|
|
-** Return NULL if not found or if there is an error.
|
|
-**
|
|
-** On an error, write an error message into pCtx and increment the
|
|
-** pParse->nErr counter.
|
|
-**
|
|
-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
|
|
-** nodes are appended.
|
|
-*/
|
|
-static JsonNode *jsonLookup(
|
|
- JsonParse *pParse, /* The JSON to search */
|
|
- const char *zPath, /* The path to search */
|
|
- int *pApnd, /* Append nodes to complete path if not NULL */
|
|
- sqlite3_context *pCtx /* Report errors here, if not NULL */
|
|
-){
|
|
- const char *zErr = 0;
|
|
- JsonNode *pNode = 0;
|
|
- char *zMsg;
|
|
-
|
|
- if( zPath==0 ) return 0;
|
|
- if( zPath[0]!='$' ){
|
|
- zErr = zPath;
|
|
- goto lookup_err;
|
|
- }
|
|
- zPath++;
|
|
- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
|
|
- if( zErr==0 ) return pNode;
|
|
-
|
|
-lookup_err:
|
|
- pParse->nErr++;
|
|
- assert( zErr!=0 && pCtx!=0 );
|
|
- zMsg = jsonPathSyntaxError(zErr);
|
|
- if( zMsg ){
|
|
- sqlite3_result_error(pCtx, zMsg, -1);
|
|
- sqlite3_free(zMsg);
|
|
- }else{
|
|
- sqlite3_result_error_nomem(pCtx);
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-
|
|
-/*
|
|
-** Report the wrong number of arguments for json_insert(), json_replace()
|
|
-** or json_set().
|
|
-*/
|
|
-static void jsonWrongNumArgs(
|
|
- sqlite3_context *pCtx,
|
|
- const char *zFuncName
|
|
-){
|
|
- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
|
|
- zFuncName);
|
|
- sqlite3_result_error(pCtx, zMsg, -1);
|
|
- sqlite3_free(zMsg);
|
|
-}
|
|
-
|
|
-/*
|
|
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
|
|
-*/
|
|
-static void jsonRemoveAllNulls(JsonNode *pNode){
|
|
- int i, n;
|
|
- assert( pNode->eType==JSON_OBJECT );
|
|
- n = pNode->n;
|
|
- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
|
|
- switch( pNode[i].eType ){
|
|
- case JSON_NULL:
|
|
- pNode[i].jnFlags |= JNODE_REMOVE;
|
|
- break;
|
|
- case JSON_OBJECT:
|
|
- jsonRemoveAllNulls(&pNode[i]);
|
|
- break;
|
|
+ default:
|
|
+ assert( pIter->op==SQLITE_DELETE );
|
|
+ bDone = 1;
|
|
+ if( pChange->op==SQLITE_INSERT ){
|
|
+ sessionAppendByte(&sOut, SQLITE_DELETE, &rc);
|
|
+ sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
+ sessionAppendRecordMerge(&sOut, pIter->nCol,
|
|
+ pChange->aRecord, pChange->nRecord, aRec, nRec, &rc
|
|
+ );
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
}
|
|
- }
|
|
-}
|
|
|
|
-
|
|
-/****************************************************************************
|
|
-** SQL functions used for testing and debugging
|
|
-****************************************************************************/
|
|
-
|
|
-#ifdef SQLITE_DEBUG
|
|
-/*
|
|
-** The json_parse(JSON) function returns a string which describes
|
|
-** a parse of the JSON provided. Or it returns NULL if JSON is not
|
|
-** well-formed.
|
|
-*/
|
|
-static void jsonParseFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonString s; /* Output string - not real JSON */
|
|
- JsonParse x; /* The parse */
|
|
- u32 i;
|
|
-
|
|
- assert( argc==1 );
|
|
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
- jsonParseFindParents(&x);
|
|
- jsonInit(&s, ctx);
|
|
- for(i=0; i<x.nNode; i++){
|
|
- const char *zType;
|
|
- if( x.aNode[i].jnFlags & JNODE_LABEL ){
|
|
- assert( x.aNode[i].eType==JSON_STRING );
|
|
- zType = "label";
|
|
- }else{
|
|
- zType = jsonType[x.aNode[i].eType];
|
|
+ if( bDone==0 ){
|
|
+ sessionAppendByte(&sOut, pIter->op, &rc);
|
|
+ sessionAppendByte(&sOut, pIter->bIndirect, &rc);
|
|
+ sessionAppendBlob(&sOut, aRec, nRec, &rc);
|
|
}
|
|
- jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
|
|
- i, zType, x.aNode[i].n, x.aUp[i]);
|
|
- if( x.aNode[i].u.zJContent!=0 ){
|
|
- jsonAppendRaw(&s, " ", 1);
|
|
- jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
|
|
+ if( rc==SQLITE_OK && xOutput && sOut.nBuf>sessions_strm_chunk_size ){
|
|
+ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
|
|
+ sOut.nBuf = 0;
|
|
}
|
|
- jsonAppendRaw(&s, "\n", 1);
|
|
+ if( rc ) break;
|
|
}
|
|
- jsonParseReset(&x);
|
|
- jsonResult(&s);
|
|
-}
|
|
|
|
-/*
|
|
-** The json_test1(JSON) function return true (1) if the input is JSON
|
|
-** text generated by another json function. It returns (0) if the input
|
|
-** is not known to be JSON.
|
|
-*/
|
|
-static void jsonTest1Func(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- UNUSED_PARAM(argc);
|
|
- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
|
|
-}
|
|
-#endif /* SQLITE_DEBUG */
|
|
-
|
|
-/****************************************************************************
|
|
-** Scalar SQL function implementations
|
|
-****************************************************************************/
|
|
-
|
|
-/*
|
|
-** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
|
|
-** corresponding to the SQL value input. Mostly this means putting
|
|
-** double-quotes around strings and returning the unquoted string "null"
|
|
-** when given a NULL input.
|
|
-*/
|
|
-static void jsonQuoteFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonString jx;
|
|
- UNUSED_PARAM(argc);
|
|
-
|
|
- jsonInit(&jx, ctx);
|
|
- jsonAppendValue(&jx, argv[0]);
|
|
- jsonResult(&jx);
|
|
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
-}
|
|
-
|
|
-/*
|
|
-** Implementation of the json_array(VALUE,...) function. Return a JSON
|
|
-** array that contains all values given in arguments. Or if any argument
|
|
-** is a BLOB, throw an error.
|
|
-*/
|
|
-static void jsonArrayFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- int i;
|
|
- JsonString jx;
|
|
-
|
|
- jsonInit(&jx, ctx);
|
|
- jsonAppendChar(&jx, '[');
|
|
- for(i=0; i<argc; i++){
|
|
- jsonAppendSeparator(&jx);
|
|
- jsonAppendValue(&jx, argv[i]);
|
|
+ if( rc!=SQLITE_OK ){
|
|
+ sqlite3_free(sOut.aBuf);
|
|
+ memset(&sOut, 0, sizeof(sOut));
|
|
}
|
|
- jsonAppendChar(&jx, ']');
|
|
- jsonResult(&jx);
|
|
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
-}
|
|
|
|
-
|
|
-/*
|
|
-** json_array_length(JSON)
|
|
-** json_array_length(JSON, PATH)
|
|
-**
|
|
-** Return the number of elements in the top-level JSON array.
|
|
-** Return 0 if the input is not a well-formed JSON array.
|
|
-*/
|
|
-static void jsonArrayLengthFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonParse *p; /* The parse */
|
|
- sqlite3_int64 n = 0;
|
|
- u32 i;
|
|
- JsonNode *pNode;
|
|
-
|
|
- p = jsonParseCached(ctx, argv);
|
|
- if( p==0 ) return;
|
|
- assert( p->nNode );
|
|
- if( argc==2 ){
|
|
- const char *zPath = (const char*)sqlite3_value_text(argv[1]);
|
|
- pNode = jsonLookup(p, zPath, 0, ctx);
|
|
- }else{
|
|
- pNode = p->aNode;
|
|
- }
|
|
- if( pNode==0 ){
|
|
- return;
|
|
- }
|
|
- if( pNode->eType==JSON_ARRAY ){
|
|
- assert( (pNode->jnFlags & JNODE_APPEND)==0 );
|
|
- for(i=1; i<=pNode->n; n++){
|
|
- i += jsonNodeSize(&pNode[i]);
|
|
- }
|
|
- }
|
|
- sqlite3_result_int64(ctx, n);
|
|
-}
|
|
-
|
|
-/*
|
|
-** json_extract(JSON, PATH, ...)
|
|
-**
|
|
-** Return the element described by PATH. Return NULL if there is no
|
|
-** PATH element. If there are multiple PATHs, then return a JSON array
|
|
-** with the result from each path. Throw an error if the JSON or any PATH
|
|
-** is malformed.
|
|
-*/
|
|
-static void jsonExtractFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonParse *p; /* The parse */
|
|
- JsonNode *pNode;
|
|
- const char *zPath;
|
|
- JsonString jx;
|
|
- int i;
|
|
-
|
|
- if( argc<2 ) return;
|
|
- p = jsonParseCached(ctx, argv);
|
|
- if( p==0 ) return;
|
|
- jsonInit(&jx, ctx);
|
|
- jsonAppendChar(&jx, '[');
|
|
- for(i=1; i<argc; i++){
|
|
- zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
- pNode = jsonLookup(p, zPath, 0, ctx);
|
|
- if( p->nErr ) break;
|
|
- if( argc>2 ){
|
|
- jsonAppendSeparator(&jx);
|
|
- if( pNode ){
|
|
- jsonRenderNode(pNode, &jx, 0);
|
|
- }else{
|
|
- jsonAppendRaw(&jx, "null", 4);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ if( xOutput ){
|
|
+ if( sOut.nBuf>0 ){
|
|
+ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
|
|
}
|
|
- }else if( pNode ){
|
|
- jsonReturn(pNode, ctx, 0);
|
|
+ }else{
|
|
+ *ppOut = (void*)sOut.aBuf;
|
|
+ *pnOut = sOut.nBuf;
|
|
+ sOut.aBuf = 0;
|
|
}
|
|
}
|
|
- if( argc>2 && i==argc ){
|
|
- jsonAppendChar(&jx, ']');
|
|
- jsonResult(&jx);
|
|
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
- }
|
|
- jsonReset(&jx);
|
|
+ sqlite3_free(sOut.aBuf);
|
|
+ return rc;
|
|
}
|
|
|
|
-/* This is the RFC 7396 MergePatch algorithm.
|
|
+/*
|
|
+** Create a new rebaser object.
|
|
*/
|
|
-static JsonNode *jsonMergePatch(
|
|
- JsonParse *pParse, /* The JSON parser that contains the TARGET */
|
|
- u32 iTarget, /* Node of the TARGET in pParse */
|
|
- JsonNode *pPatch /* The PATCH */
|
|
-){
|
|
- u32 i, j;
|
|
- u32 iRoot;
|
|
- JsonNode *pTarget;
|
|
- if( pPatch->eType!=JSON_OBJECT ){
|
|
- return pPatch;
|
|
- }
|
|
- assert( iTarget>=0 && iTarget<pParse->nNode );
|
|
- pTarget = &pParse->aNode[iTarget];
|
|
- assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
|
|
- if( pTarget->eType!=JSON_OBJECT ){
|
|
- jsonRemoveAllNulls(pPatch);
|
|
- return pPatch;
|
|
- }
|
|
- iRoot = iTarget;
|
|
- for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
|
|
- u32 nKey;
|
|
- const char *zKey;
|
|
- assert( pPatch[i].eType==JSON_STRING );
|
|
- assert( pPatch[i].jnFlags & JNODE_LABEL );
|
|
- nKey = pPatch[i].n;
|
|
- zKey = pPatch[i].u.zJContent;
|
|
- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
|
|
- for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
|
|
- assert( pTarget[j].eType==JSON_STRING );
|
|
- assert( pTarget[j].jnFlags & JNODE_LABEL );
|
|
- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
|
|
- if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){
|
|
- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break;
|
|
- if( pPatch[i+1].eType==JSON_NULL ){
|
|
- pTarget[j+1].jnFlags |= JNODE_REMOVE;
|
|
- }else{
|
|
- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
|
|
- if( pNew==0 ) return 0;
|
|
- pTarget = &pParse->aNode[iTarget];
|
|
- if( pNew!=&pTarget[j+1] ){
|
|
- pTarget[j+1].u.pPatch = pNew;
|
|
- pTarget[j+1].jnFlags |= JNODE_PATCH;
|
|
- }
|
|
- }
|
|
- break;
|
|
- }
|
|
- }
|
|
- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
|
|
- int iStart, iPatch;
|
|
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
|
- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
|
|
- iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
|
- if( pParse->oom ) return 0;
|
|
- jsonRemoveAllNulls(pPatch);
|
|
- pTarget = &pParse->aNode[iTarget];
|
|
- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
|
|
- pParse->aNode[iRoot].u.iAppend = iStart - iRoot;
|
|
- iRoot = iStart;
|
|
- pParse->aNode[iPatch].jnFlags |= JNODE_PATCH;
|
|
- pParse->aNode[iPatch].u.pPatch = &pPatch[i+1];
|
|
- }
|
|
- }
|
|
- return pTarget;
|
|
-}
|
|
+SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew){
|
|
+ int rc = SQLITE_OK;
|
|
+ sqlite3_rebaser *pNew;
|
|
|
|
-/*
|
|
-** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
|
|
-** object that is the result of running the RFC 7396 MergePatch() algorithm
|
|
-** on the two arguments.
|
|
-*/
|
|
-static void jsonPatchFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonParse x; /* The JSON that is being patched */
|
|
- JsonParse y; /* The patch */
|
|
- JsonNode *pResult; /* The result of the merge */
|
|
-
|
|
- UNUSED_PARAM(argc);
|
|
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
- if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){
|
|
- jsonParseReset(&x);
|
|
- return;
|
|
- }
|
|
- pResult = jsonMergePatch(&x, 0, y.aNode);
|
|
- assert( pResult!=0 || x.oom );
|
|
- if( pResult ){
|
|
- jsonReturnJson(pResult, ctx, 0);
|
|
+ pNew = sqlite3_malloc(sizeof(sqlite3_rebaser));
|
|
+ if( pNew==0 ){
|
|
+ rc = SQLITE_NOMEM;
|
|
}else{
|
|
- sqlite3_result_error_nomem(ctx);
|
|
+ memset(pNew, 0, sizeof(sqlite3_rebaser));
|
|
}
|
|
- jsonParseReset(&x);
|
|
- jsonParseReset(&y);
|
|
+ *ppNew = pNew;
|
|
+ return rc;
|
|
}
|
|
|
|
-
|
|
-/*
|
|
-** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON
|
|
-** object that contains all name/value given in arguments. Or if any name
|
|
-** is not a string or if any value is a BLOB, throw an error.
|
|
+/*
|
|
+** Call this one or more times to configure a rebaser.
|
|
*/
|
|
-static void jsonObjectFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
+SQLITE_API int sqlite3rebaser_configure(
|
|
+ sqlite3_rebaser *p,
|
|
+ int nRebase, const void *pRebase
|
|
){
|
|
- int i;
|
|
- JsonString jx;
|
|
- const char *z;
|
|
- u32 n;
|
|
-
|
|
- if( argc&1 ){
|
|
- sqlite3_result_error(ctx, "json_object() requires an even number "
|
|
- "of arguments", -1);
|
|
- return;
|
|
+ sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */
|
|
+ int rc; /* Return code */
|
|
+ rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionChangesetToHash(pIter, &p->grp, 1);
|
|
}
|
|
- jsonInit(&jx, ctx);
|
|
- jsonAppendChar(&jx, '{');
|
|
- for(i=0; i<argc; i+=2){
|
|
- if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
|
|
- sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
|
|
- jsonReset(&jx);
|
|
- return;
|
|
- }
|
|
- jsonAppendSeparator(&jx);
|
|
- z = (const char*)sqlite3_value_text(argv[i]);
|
|
- n = (u32)sqlite3_value_bytes(argv[i]);
|
|
- jsonAppendString(&jx, z, n);
|
|
- jsonAppendChar(&jx, ':');
|
|
- jsonAppendValue(&jx, argv[i+1]);
|
|
- }
|
|
- jsonAppendChar(&jx, '}');
|
|
- jsonResult(&jx);
|
|
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
+ sqlite3changeset_finalize(pIter);
|
|
+ return rc;
|
|
}
|
|
|
|
-
|
|
-/*
|
|
-** json_remove(JSON, PATH, ...)
|
|
-**
|
|
-** Remove the named elements from JSON and return the result. malformed
|
|
-** JSON or PATH arguments result in an error.
|
|
+/*
|
|
+** Rebase a changeset according to current rebaser configuration
|
|
*/
|
|
-static void jsonRemoveFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
+SQLITE_API int sqlite3rebaser_rebase(
|
|
+ sqlite3_rebaser *p,
|
|
+ int nIn, const void *pIn,
|
|
+ int *pnOut, void **ppOut
|
|
){
|
|
- JsonParse x; /* The parse */
|
|
- JsonNode *pNode;
|
|
- const char *zPath;
|
|
- u32 i;
|
|
+ sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
|
|
+ int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn);
|
|
|
|
- if( argc<1 ) return;
|
|
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
- assert( x.nNode );
|
|
- for(i=1; i<(u32)argc; i++){
|
|
- zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
- if( zPath==0 ) goto remove_done;
|
|
- pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
- if( x.nErr ) goto remove_done;
|
|
- if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = sessionRebase(p, pIter, 0, 0, pnOut, ppOut);
|
|
+ sqlite3changeset_finalize(pIter);
|
|
}
|
|
- if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
|
- jsonReturnJson(x.aNode, ctx, 0);
|
|
- }
|
|
-remove_done:
|
|
- jsonParseReset(&x);
|
|
-}
|
|
|
|
-/*
|
|
-** json_replace(JSON, PATH, VALUE, ...)
|
|
-**
|
|
-** Replace the value at PATH with VALUE. If PATH does not already exist,
|
|
-** this routine is a no-op. If JSON or PATH is malformed, throw an error.
|
|
-*/
|
|
-static void jsonReplaceFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonParse x; /* The parse */
|
|
- JsonNode *pNode;
|
|
- const char *zPath;
|
|
- u32 i;
|
|
-
|
|
- if( argc<1 ) return;
|
|
- if( (argc&1)==0 ) {
|
|
- jsonWrongNumArgs(ctx, "replace");
|
|
- return;
|
|
- }
|
|
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
- assert( x.nNode );
|
|
- for(i=1; i<(u32)argc; i+=2){
|
|
- zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
- pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
- if( x.nErr ) goto replace_err;
|
|
- if( pNode ){
|
|
- pNode->jnFlags |= (u8)JNODE_REPLACE;
|
|
- pNode->u.iReplace = i + 1;
|
|
- }
|
|
- }
|
|
- if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
|
- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
|
- }else{
|
|
- jsonReturnJson(x.aNode, ctx, argv);
|
|
- }
|
|
-replace_err:
|
|
- jsonParseReset(&x);
|
|
+ return rc;
|
|
}
|
|
|
|
-/*
|
|
-** json_set(JSON, PATH, VALUE, ...)
|
|
-**
|
|
-** Set the value at PATH to VALUE. Create the PATH if it does not already
|
|
-** exist. Overwrite existing values that do exist.
|
|
-** If JSON or PATH is malformed, throw an error.
|
|
-**
|
|
-** json_insert(JSON, PATH, VALUE, ...)
|
|
-**
|
|
-** Create PATH and initialize it to VALUE. If PATH already exists, this
|
|
-** routine is a no-op. If JSON or PATH is malformed, throw an error.
|
|
+/*
|
|
+** Rebase a changeset according to current rebaser configuration
|
|
*/
|
|
-static void jsonSetFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
+SQLITE_API int sqlite3rebaser_rebase_strm(
|
|
+ sqlite3_rebaser *p,
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
+ void *pIn,
|
|
+ int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
+ void *pOut
|
|
){
|
|
- JsonParse x; /* The parse */
|
|
- JsonNode *pNode;
|
|
- const char *zPath;
|
|
- u32 i;
|
|
- int bApnd;
|
|
- int bIsSet = *(int*)sqlite3_user_data(ctx);
|
|
+ sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
|
|
+ int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
|
|
|
|
- if( argc<1 ) return;
|
|
- if( (argc&1)==0 ) {
|
|
- jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
|
|
- return;
|
|
- }
|
|
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
- assert( x.nNode );
|
|
- for(i=1; i<(u32)argc; i+=2){
|
|
- zPath = (const char*)sqlite3_value_text(argv[i]);
|
|
- bApnd = 0;
|
|
- pNode = jsonLookup(&x, zPath, &bApnd, ctx);
|
|
- if( x.oom ){
|
|
- sqlite3_result_error_nomem(ctx);
|
|
- goto jsonSetDone;
|
|
- }else if( x.nErr ){
|
|
- goto jsonSetDone;
|
|
- }else if( pNode && (bApnd || bIsSet) ){
|
|
- pNode->jnFlags |= (u8)JNODE_REPLACE;
|
|
- pNode->u.iReplace = i + 1;
|
|
- }
|
|
- }
|
|
- if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
|
- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
|
|
- }else{
|
|
- jsonReturnJson(x.aNode, ctx, argv);
|
|
- }
|
|
-jsonSetDone:
|
|
- jsonParseReset(&x);
|
|
-}
|
|
-
|
|
-/*
|
|
-** json_type(JSON)
|
|
-** json_type(JSON, PATH)
|
|
-**
|
|
-** Return the top-level "type" of a JSON string. Throw an error if
|
|
-** either the JSON or PATH inputs are not well-formed.
|
|
-*/
|
|
-static void jsonTypeFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonParse x; /* The parse */
|
|
- const char *zPath;
|
|
- JsonNode *pNode;
|
|
-
|
|
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
|
- assert( x.nNode );
|
|
- if( argc==2 ){
|
|
- zPath = (const char*)sqlite3_value_text(argv[1]);
|
|
- pNode = jsonLookup(&x, zPath, 0, ctx);
|
|
- }else{
|
|
- pNode = x.aNode;
|
|
- }
|
|
- if( pNode ){
|
|
- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
|
|
- }
|
|
- jsonParseReset(&x);
|
|
-}
|
|
-
|
|
-/*
|
|
-** json_valid(JSON)
|
|
-**
|
|
-** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
|
|
-** Return 0 otherwise.
|
|
-*/
|
|
-static void jsonValidFunc(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonParse x; /* The parse */
|
|
- int rc = 0;
|
|
-
|
|
- UNUSED_PARAM(argc);
|
|
- if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 ){
|
|
- rc = 1;
|
|
- }
|
|
- jsonParseReset(&x);
|
|
- sqlite3_result_int(ctx, rc);
|
|
-}
|
|
-
|
|
-
|
|
-/****************************************************************************
|
|
-** Aggregate SQL function implementations
|
|
-****************************************************************************/
|
|
-/*
|
|
-** json_group_array(VALUE)
|
|
-**
|
|
-** Return a JSON array composed of all values in the aggregate.
|
|
-*/
|
|
-static void jsonArrayStep(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonString *pStr;
|
|
- UNUSED_PARAM(argc);
|
|
- pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
|
|
- if( pStr ){
|
|
- if( pStr->zBuf==0 ){
|
|
- jsonInit(pStr, ctx);
|
|
- jsonAppendChar(pStr, '[');
|
|
- }else{
|
|
- jsonAppendChar(pStr, ',');
|
|
- pStr->pCtx = ctx;
|
|
- }
|
|
- jsonAppendValue(pStr, argv[0]);
|
|
- }
|
|
-}
|
|
-static void jsonArrayFinal(sqlite3_context *ctx){
|
|
- JsonString *pStr;
|
|
- pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
- if( pStr ){
|
|
- pStr->pCtx = ctx;
|
|
- jsonAppendChar(pStr, ']');
|
|
- if( pStr->bErr ){
|
|
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
|
- assert( pStr->bStatic );
|
|
- }else{
|
|
- sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
|
|
- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
|
|
- pStr->bStatic = 1;
|
|
- }
|
|
- }else{
|
|
- sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
|
|
- }
|
|
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
-}
|
|
-
|
|
-/*
|
|
-** json_group_obj(NAME,VALUE)
|
|
-**
|
|
-** Return a JSON object composed of all names and values in the aggregate.
|
|
-*/
|
|
-static void jsonObjectStep(
|
|
- sqlite3_context *ctx,
|
|
- int argc,
|
|
- sqlite3_value **argv
|
|
-){
|
|
- JsonString *pStr;
|
|
- const char *z;
|
|
- u32 n;
|
|
- UNUSED_PARAM(argc);
|
|
- pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
|
|
- if( pStr ){
|
|
- if( pStr->zBuf==0 ){
|
|
- jsonInit(pStr, ctx);
|
|
- jsonAppendChar(pStr, '{');
|
|
- }else{
|
|
- jsonAppendChar(pStr, ',');
|
|
- pStr->pCtx = ctx;
|
|
- }
|
|
- z = (const char*)sqlite3_value_text(argv[0]);
|
|
- n = (u32)sqlite3_value_bytes(argv[0]);
|
|
- jsonAppendString(pStr, z, n);
|
|
- jsonAppendChar(pStr, ':');
|
|
- jsonAppendValue(pStr, argv[1]);
|
|
- }
|
|
-}
|
|
-static void jsonObjectFinal(sqlite3_context *ctx){
|
|
- JsonString *pStr;
|
|
- pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
|
|
- if( pStr ){
|
|
- jsonAppendChar(pStr, '}');
|
|
- if( pStr->bErr ){
|
|
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
|
- assert( pStr->bStatic );
|
|
- }else{
|
|
- sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
|
|
- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
|
|
- pStr->bStatic = 1;
|
|
- }
|
|
- }else{
|
|
- sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
|
|
- }
|
|
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
|
-}
|
|
-
|
|
-
|
|
-#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
-/****************************************************************************
|
|
-** The json_each virtual table
|
|
-****************************************************************************/
|
|
-typedef struct JsonEachCursor JsonEachCursor;
|
|
-struct JsonEachCursor {
|
|
- sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
- u32 iRowid; /* The rowid */
|
|
- u32 iBegin; /* The first node of the scan */
|
|
- u32 i; /* Index in sParse.aNode[] of current row */
|
|
- u32 iEnd; /* EOF when i equals or exceeds this value */
|
|
- u8 eType; /* Type of top-level element */
|
|
- u8 bRecursive; /* True for json_tree(). False for json_each() */
|
|
- char *zJson; /* Input JSON */
|
|
- char *zRoot; /* Path by which to filter zJson */
|
|
- JsonParse sParse; /* Parse of the input JSON */
|
|
-};
|
|
-
|
|
-/* Constructor for the json_each virtual table */
|
|
-static int jsonEachConnect(
|
|
- sqlite3 *db,
|
|
- void *pAux,
|
|
- int argc, const char *const*argv,
|
|
- sqlite3_vtab **ppVtab,
|
|
- char **pzErr
|
|
-){
|
|
- sqlite3_vtab *pNew;
|
|
- int rc;
|
|
-
|
|
-/* Column numbers */
|
|
-#define JEACH_KEY 0
|
|
-#define JEACH_VALUE 1
|
|
-#define JEACH_TYPE 2
|
|
-#define JEACH_ATOM 3
|
|
-#define JEACH_ID 4
|
|
-#define JEACH_PARENT 5
|
|
-#define JEACH_FULLKEY 6
|
|
-#define JEACH_PATH 7
|
|
-#define JEACH_JSON 8
|
|
-#define JEACH_ROOT 9
|
|
-
|
|
- UNUSED_PARAM(pzErr);
|
|
- UNUSED_PARAM(argv);
|
|
- UNUSED_PARAM(argc);
|
|
- UNUSED_PARAM(pAux);
|
|
- rc = sqlite3_declare_vtab(db,
|
|
- "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
|
|
- "json HIDDEN,root HIDDEN)");
|
|
if( rc==SQLITE_OK ){
|
|
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
|
- if( pNew==0 ) return SQLITE_NOMEM;
|
|
- memset(pNew, 0, sizeof(*pNew));
|
|
+ rc = sessionRebase(p, pIter, xOutput, pOut, 0, 0);
|
|
+ sqlite3changeset_finalize(pIter);
|
|
}
|
|
- return rc;
|
|
-}
|
|
|
|
-/* destructor for json_each virtual table */
|
|
-static int jsonEachDisconnect(sqlite3_vtab *pVtab){
|
|
- sqlite3_free(pVtab);
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* constructor for a JsonEachCursor object for json_each(). */
|
|
-static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
- JsonEachCursor *pCur;
|
|
-
|
|
- UNUSED_PARAM(p);
|
|
- pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
- if( pCur==0 ) return SQLITE_NOMEM;
|
|
- memset(pCur, 0, sizeof(*pCur));
|
|
- *ppCursor = &pCur->base;
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* constructor for a JsonEachCursor object for json_tree(). */
|
|
-static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
- int rc = jsonEachOpenEach(p, ppCursor);
|
|
- if( rc==SQLITE_OK ){
|
|
- JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
|
|
- pCur->bRecursive = 1;
|
|
- }
|
|
return rc;
|
|
}
|
|
|
|
-/* Reset a JsonEachCursor back to its original state. Free any memory
|
|
-** held. */
|
|
-static void jsonEachCursorReset(JsonEachCursor *p){
|
|
- sqlite3_free(p->zJson);
|
|
- sqlite3_free(p->zRoot);
|
|
- jsonParseReset(&p->sParse);
|
|
- p->iRowid = 0;
|
|
- p->i = 0;
|
|
- p->iEnd = 0;
|
|
- p->eType = 0;
|
|
- p->zJson = 0;
|
|
- p->zRoot = 0;
|
|
-}
|
|
-
|
|
-/* Destructor for a jsonEachCursor object */
|
|
-static int jsonEachClose(sqlite3_vtab_cursor *cur){
|
|
- JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
- jsonEachCursorReset(p);
|
|
- sqlite3_free(cur);
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* Return TRUE if the jsonEachCursor object has been advanced off the end
|
|
-** of the JSON object */
|
|
-static int jsonEachEof(sqlite3_vtab_cursor *cur){
|
|
- JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
- return p->i >= p->iEnd;
|
|
-}
|
|
-
|
|
-/* Advance the cursor to the next element for json_tree() */
|
|
-static int jsonEachNext(sqlite3_vtab_cursor *cur){
|
|
- JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
- if( p->bRecursive ){
|
|
- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
|
|
- p->i++;
|
|
- p->iRowid++;
|
|
- if( p->i<p->iEnd ){
|
|
- u32 iUp = p->sParse.aUp[p->i];
|
|
- JsonNode *pUp = &p->sParse.aNode[iUp];
|
|
- p->eType = pUp->eType;
|
|
- if( pUp->eType==JSON_ARRAY ){
|
|
- if( iUp==p->i-1 ){
|
|
- pUp->u.iKey = 0;
|
|
- }else{
|
|
- pUp->u.iKey++;
|
|
- }
|
|
- }
|
|
- }
|
|
- }else{
|
|
- switch( p->eType ){
|
|
- case JSON_ARRAY: {
|
|
- p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
|
|
- p->iRowid++;
|
|
- break;
|
|
- }
|
|
- case JSON_OBJECT: {
|
|
- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
|
|
- p->iRowid++;
|
|
- break;
|
|
- }
|
|
- default: {
|
|
- p->i = p->iEnd;
|
|
- break;
|
|
- }
|
|
- }
|
|
+/*
|
|
+** Destroy a rebaser object
|
|
+*/
|
|
+SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
|
|
+ if( p ){
|
|
+ sessionDeleteTable(p->grp.pList);
|
|
+ sqlite3_free(p);
|
|
}
|
|
- return SQLITE_OK;
|
|
}
|
|
|
|
-/* Append the name of the path for element i to pStr
|
|
+/*
|
|
+** Global configuration
|
|
*/
|
|
-static void jsonEachComputePath(
|
|
- JsonEachCursor *p, /* The cursor */
|
|
- JsonString *pStr, /* Write the path here */
|
|
- u32 i /* Path to this element */
|
|
-){
|
|
- JsonNode *pNode, *pUp;
|
|
- u32 iUp;
|
|
- if( i==0 ){
|
|
- jsonAppendChar(pStr, '$');
|
|
- return;
|
|
- }
|
|
- iUp = p->sParse.aUp[i];
|
|
- jsonEachComputePath(p, pStr, iUp);
|
|
- pNode = &p->sParse.aNode[i];
|
|
- pUp = &p->sParse.aNode[iUp];
|
|
- if( pUp->eType==JSON_ARRAY ){
|
|
- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
|
|
- }else{
|
|
- assert( pUp->eType==JSON_OBJECT );
|
|
- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
|
|
- assert( pNode->eType==JSON_STRING );
|
|
- assert( pNode->jnFlags & JNODE_LABEL );
|
|
- jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
|
|
- }
|
|
-}
|
|
-
|
|
-/* Return the value of a column */
|
|
-static int jsonEachColumn(
|
|
- sqlite3_vtab_cursor *cur, /* The cursor */
|
|
- sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
- int i /* Which column to return */
|
|
-){
|
|
- JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
- JsonNode *pThis = &p->sParse.aNode[p->i];
|
|
- switch( i ){
|
|
- case JEACH_KEY: {
|
|
- if( p->i==0 ) break;
|
|
- if( p->eType==JSON_OBJECT ){
|
|
- jsonReturn(pThis, ctx, 0);
|
|
- }else if( p->eType==JSON_ARRAY ){
|
|
- u32 iKey;
|
|
- if( p->bRecursive ){
|
|
- if( p->iRowid==0 ) break;
|
|
- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
|
|
- }else{
|
|
- iKey = p->iRowid;
|
|
- }
|
|
- sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
|
|
+SQLITE_API int sqlite3session_config(int op, void *pArg){
|
|
+ int rc = SQLITE_OK;
|
|
+ switch( op ){
|
|
+ case SQLITE_SESSION_CONFIG_STRMSIZE: {
|
|
+ int *pInt = (int*)pArg;
|
|
+ if( *pInt>0 ){
|
|
+ sessions_strm_chunk_size = *pInt;
|
|
}
|
|
+ *pInt = sessions_strm_chunk_size;
|
|
break;
|
|
}
|
|
- case JEACH_VALUE: {
|
|
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
- jsonReturn(pThis, ctx, 0);
|
|
+ default:
|
|
+ rc = SQLITE_MISUSE;
|
|
break;
|
|
- }
|
|
- case JEACH_TYPE: {
|
|
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
|
|
- break;
|
|
- }
|
|
- case JEACH_ATOM: {
|
|
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
|
|
- if( pThis->eType>=JSON_ARRAY ) break;
|
|
- jsonReturn(pThis, ctx, 0);
|
|
- break;
|
|
- }
|
|
- case JEACH_ID: {
|
|
- sqlite3_result_int64(ctx,
|
|
- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
|
|
- break;
|
|
- }
|
|
- case JEACH_PARENT: {
|
|
- if( p->i>p->iBegin && p->bRecursive ){
|
|
- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
|
|
- }
|
|
- break;
|
|
- }
|
|
- case JEACH_FULLKEY: {
|
|
- JsonString x;
|
|
- jsonInit(&x, ctx);
|
|
- if( p->bRecursive ){
|
|
- jsonEachComputePath(p, &x, p->i);
|
|
- }else{
|
|
- if( p->zRoot ){
|
|
- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
|
|
- }else{
|
|
- jsonAppendChar(&x, '$');
|
|
- }
|
|
- if( p->eType==JSON_ARRAY ){
|
|
- jsonPrintf(30, &x, "[%d]", p->iRowid);
|
|
- }else{
|
|
- jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
|
|
- }
|
|
- }
|
|
- jsonResult(&x);
|
|
- break;
|
|
- }
|
|
- case JEACH_PATH: {
|
|
- if( p->bRecursive ){
|
|
- JsonString x;
|
|
- jsonInit(&x, ctx);
|
|
- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
|
|
- jsonResult(&x);
|
|
- break;
|
|
- }
|
|
- /* For json_each() path and root are the same so fall through
|
|
- ** into the root case */
|
|
- }
|
|
- default: {
|
|
- const char *zRoot = p->zRoot;
|
|
- if( zRoot==0 ) zRoot = "$";
|
|
- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
|
|
- break;
|
|
- }
|
|
- case JEACH_JSON: {
|
|
- assert( i==JEACH_JSON );
|
|
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
|
|
- break;
|
|
- }
|
|
}
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* Return the current rowid value */
|
|
-static int jsonEachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
- JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
- *pRowid = p->iRowid;
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* The query strategy is to look for an equality constraint on the json
|
|
-** column. Without such a constraint, the table cannot operate. idxNum is
|
|
-** 1 if the constraint is found, 3 if the constraint and zRoot are found,
|
|
-** and 0 otherwise.
|
|
-*/
|
|
-static int jsonEachBestIndex(
|
|
- sqlite3_vtab *tab,
|
|
- sqlite3_index_info *pIdxInfo
|
|
-){
|
|
- int i;
|
|
- int jsonIdx = -1;
|
|
- int rootIdx = -1;
|
|
- const struct sqlite3_index_constraint *pConstraint;
|
|
-
|
|
- UNUSED_PARAM(tab);
|
|
- pConstraint = pIdxInfo->aConstraint;
|
|
- for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
|
- if( pConstraint->usable==0 ) continue;
|
|
- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
- switch( pConstraint->iColumn ){
|
|
- case JEACH_JSON: jsonIdx = i; break;
|
|
- case JEACH_ROOT: rootIdx = i; break;
|
|
- default: /* no-op */ break;
|
|
- }
|
|
- }
|
|
- if( jsonIdx<0 ){
|
|
- pIdxInfo->idxNum = 0;
|
|
- pIdxInfo->estimatedCost = 1e99;
|
|
- }else{
|
|
- pIdxInfo->estimatedCost = 1.0;
|
|
- pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1;
|
|
- pIdxInfo->aConstraintUsage[jsonIdx].omit = 1;
|
|
- if( rootIdx<0 ){
|
|
- pIdxInfo->idxNum = 1;
|
|
- }else{
|
|
- pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2;
|
|
- pIdxInfo->aConstraintUsage[rootIdx].omit = 1;
|
|
- pIdxInfo->idxNum = 3;
|
|
- }
|
|
- }
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* Start a search on a new JSON string */
|
|
-static int jsonEachFilter(
|
|
- sqlite3_vtab_cursor *cur,
|
|
- int idxNum, const char *idxStr,
|
|
- int argc, sqlite3_value **argv
|
|
-){
|
|
- JsonEachCursor *p = (JsonEachCursor*)cur;
|
|
- const char *z;
|
|
- const char *zRoot = 0;
|
|
- sqlite3_int64 n;
|
|
-
|
|
- UNUSED_PARAM(idxStr);
|
|
- UNUSED_PARAM(argc);
|
|
- jsonEachCursorReset(p);
|
|
- if( idxNum==0 ) return SQLITE_OK;
|
|
- z = (const char*)sqlite3_value_text(argv[0]);
|
|
- if( z==0 ) return SQLITE_OK;
|
|
- n = sqlite3_value_bytes(argv[0]);
|
|
- p->zJson = sqlite3_malloc64( n+1 );
|
|
- if( p->zJson==0 ) return SQLITE_NOMEM;
|
|
- memcpy(p->zJson, z, (size_t)n+1);
|
|
- if( jsonParse(&p->sParse, 0, p->zJson) ){
|
|
- int rc = SQLITE_NOMEM;
|
|
- if( p->sParse.oom==0 ){
|
|
- sqlite3_free(cur->pVtab->zErrMsg);
|
|
- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
|
|
- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
|
|
- }
|
|
- jsonEachCursorReset(p);
|
|
- return rc;
|
|
- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
|
|
- jsonEachCursorReset(p);
|
|
- return SQLITE_NOMEM;
|
|
- }else{
|
|
- JsonNode *pNode = 0;
|
|
- if( idxNum==3 ){
|
|
- const char *zErr = 0;
|
|
- zRoot = (const char*)sqlite3_value_text(argv[1]);
|
|
- if( zRoot==0 ) return SQLITE_OK;
|
|
- n = sqlite3_value_bytes(argv[1]);
|
|
- p->zRoot = sqlite3_malloc64( n+1 );
|
|
- if( p->zRoot==0 ) return SQLITE_NOMEM;
|
|
- memcpy(p->zRoot, zRoot, (size_t)n+1);
|
|
- if( zRoot[0]!='$' ){
|
|
- zErr = zRoot;
|
|
- }else{
|
|
- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
|
|
- }
|
|
- if( zErr ){
|
|
- sqlite3_free(cur->pVtab->zErrMsg);
|
|
- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
|
|
- jsonEachCursorReset(p);
|
|
- return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
|
|
- }else if( pNode==0 ){
|
|
- return SQLITE_OK;
|
|
- }
|
|
- }else{
|
|
- pNode = p->sParse.aNode;
|
|
- }
|
|
- p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
|
|
- p->eType = pNode->eType;
|
|
- if( p->eType>=JSON_ARRAY ){
|
|
- pNode->u.iKey = 0;
|
|
- p->iEnd = p->i + pNode->n + 1;
|
|
- if( p->bRecursive ){
|
|
- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
|
|
- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
|
|
- p->i--;
|
|
- }
|
|
- }else{
|
|
- p->i++;
|
|
- }
|
|
- }else{
|
|
- p->iEnd = p->i+1;
|
|
- }
|
|
- }
|
|
- return SQLITE_OK;
|
|
-}
|
|
-
|
|
-/* The methods of the json_each virtual table */
|
|
-static sqlite3_module jsonEachModule = {
|
|
- 0, /* iVersion */
|
|
- 0, /* xCreate */
|
|
- jsonEachConnect, /* xConnect */
|
|
- jsonEachBestIndex, /* xBestIndex */
|
|
- jsonEachDisconnect, /* xDisconnect */
|
|
- 0, /* xDestroy */
|
|
- jsonEachOpenEach, /* xOpen - open a cursor */
|
|
- jsonEachClose, /* xClose - close a cursor */
|
|
- jsonEachFilter, /* xFilter - configure scan constraints */
|
|
- jsonEachNext, /* xNext - advance a cursor */
|
|
- jsonEachEof, /* xEof - check for end of scan */
|
|
- jsonEachColumn, /* xColumn - read data */
|
|
- jsonEachRowid, /* xRowid - read data */
|
|
- 0, /* xUpdate */
|
|
- 0, /* xBegin */
|
|
- 0, /* xSync */
|
|
- 0, /* xCommit */
|
|
- 0, /* xRollback */
|
|
- 0, /* xFindMethod */
|
|
- 0, /* xRename */
|
|
- 0, /* xSavepoint */
|
|
- 0, /* xRelease */
|
|
- 0 /* xRollbackTo */
|
|
-};
|
|
-
|
|
-/* The methods of the json_tree virtual table. */
|
|
-static sqlite3_module jsonTreeModule = {
|
|
- 0, /* iVersion */
|
|
- 0, /* xCreate */
|
|
- jsonEachConnect, /* xConnect */
|
|
- jsonEachBestIndex, /* xBestIndex */
|
|
- jsonEachDisconnect, /* xDisconnect */
|
|
- 0, /* xDestroy */
|
|
- jsonEachOpenTree, /* xOpen - open a cursor */
|
|
- jsonEachClose, /* xClose - close a cursor */
|
|
- jsonEachFilter, /* xFilter - configure scan constraints */
|
|
- jsonEachNext, /* xNext - advance a cursor */
|
|
- jsonEachEof, /* xEof - check for end of scan */
|
|
- jsonEachColumn, /* xColumn - read data */
|
|
- jsonEachRowid, /* xRowid - read data */
|
|
- 0, /* xUpdate */
|
|
- 0, /* xBegin */
|
|
- 0, /* xSync */
|
|
- 0, /* xCommit */
|
|
- 0, /* xRollback */
|
|
- 0, /* xFindMethod */
|
|
- 0, /* xRename */
|
|
- 0, /* xSavepoint */
|
|
- 0, /* xRelease */
|
|
- 0 /* xRollbackTo */
|
|
-};
|
|
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
-
|
|
-/****************************************************************************
|
|
-** The following routines are the only publically visible identifiers in this
|
|
-** file. Call the following routines in order to register the various SQL
|
|
-** functions and the virtual table implemented by this file.
|
|
-****************************************************************************/
|
|
-
|
|
-SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
|
|
- int rc = SQLITE_OK;
|
|
- unsigned int i;
|
|
- static const struct {
|
|
- const char *zName;
|
|
- int nArg;
|
|
- int flag;
|
|
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
|
- } aFunc[] = {
|
|
- { "json", 1, 0, jsonRemoveFunc },
|
|
- { "json_array", -1, 0, jsonArrayFunc },
|
|
- { "json_array_length", 1, 0, jsonArrayLengthFunc },
|
|
- { "json_array_length", 2, 0, jsonArrayLengthFunc },
|
|
- { "json_extract", -1, 0, jsonExtractFunc },
|
|
- { "json_insert", -1, 0, jsonSetFunc },
|
|
- { "json_object", -1, 0, jsonObjectFunc },
|
|
- { "json_patch", 2, 0, jsonPatchFunc },
|
|
- { "json_quote", 1, 0, jsonQuoteFunc },
|
|
- { "json_remove", -1, 0, jsonRemoveFunc },
|
|
- { "json_replace", -1, 0, jsonReplaceFunc },
|
|
- { "json_set", -1, 1, jsonSetFunc },
|
|
- { "json_type", 1, 0, jsonTypeFunc },
|
|
- { "json_type", 2, 0, jsonTypeFunc },
|
|
- { "json_valid", 1, 0, jsonValidFunc },
|
|
-
|
|
-#if SQLITE_DEBUG
|
|
- /* DEBUG and TESTING functions */
|
|
- { "json_parse", 1, 0, jsonParseFunc },
|
|
- { "json_test1", 1, 0, jsonTest1Func },
|
|
-#endif
|
|
- };
|
|
- static const struct {
|
|
- const char *zName;
|
|
- int nArg;
|
|
- void (*xStep)(sqlite3_context*,int,sqlite3_value**);
|
|
- void (*xFinal)(sqlite3_context*);
|
|
- } aAgg[] = {
|
|
- { "json_group_array", 1, jsonArrayStep, jsonArrayFinal },
|
|
- { "json_group_object", 2, jsonObjectStep, jsonObjectFinal },
|
|
- };
|
|
-#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- static const struct {
|
|
- const char *zName;
|
|
- sqlite3_module *pModule;
|
|
- } aMod[] = {
|
|
- { "json_each", &jsonEachModule },
|
|
- { "json_tree", &jsonTreeModule },
|
|
- };
|
|
-#endif
|
|
- 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,
|
|
- (void*)&aFunc[i].flag,
|
|
- 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, aAgg[i].nArg,
|
|
- SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
|
|
- 0, aAgg[i].xStep, aAgg[i].xFinal);
|
|
- }
|
|
-#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
- for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
|
|
- rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
|
|
- }
|
|
-#endif
|
|
return rc;
|
|
}
|
|
|
|
+#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
|
|
|
|
-#ifndef SQLITE_CORE
|
|
-#ifdef _WIN32
|
|
-__declspec(dllexport)
|
|
-#endif
|
|
-SQLITE_API int sqlite3_json_init(
|
|
- sqlite3 *db,
|
|
- char **pzErrMsg,
|
|
- const sqlite3_api_routines *pApi
|
|
-){
|
|
- SQLITE_EXTENSION_INIT2(pApi);
|
|
- (void)pzErrMsg; /* Unused parameter */
|
|
- return sqlite3Json1Init(db);
|
|
-}
|
|
-#endif
|
|
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) */
|
|
-
|
|
-/************** End of json1.c ***********************************************/
|
|
+/************** End of sqlite3session.c **************************************/
|
|
/************** Begin file fts5.c ********************************************/
|
|
|
|
|
|
@@ -183491,7 +198794,7 @@
|
|
** This way, even if the tokenizer does not provide synonyms
|
|
** when tokenizing query text (it should not - to do would be
|
|
** inefficient), it doesn't matter if the user queries for
|
|
-** 'first + place' or '1st + place', as there are entires in the
|
|
+** 'first + place' or '1st + place', as there are entries in the
|
|
** FTS index corresponding to both forms of the first token.
|
|
** </ol>
|
|
**
|
|
@@ -183519,7 +198822,7 @@
|
|
** extra data to the FTS index or require FTS5 to query for multiple terms,
|
|
** so it is efficient in terms of disk space and query speed. However, it
|
|
** does not support prefix queries very well. If, as suggested above, the
|
|
-** token "first" is subsituted for "1st" by the tokenizer, then the query:
|
|
+** token "first" is substituted for "1st" by the tokenizer, then the query:
|
|
**
|
|
** <codeblock>
|
|
** ... MATCH '1s*'</codeblock>
|
|
@@ -184349,6 +199652,8 @@
|
|
int bPrefix
|
|
);
|
|
|
|
+static void sqlite3Fts5ParseSetCaret(Fts5ExprPhrase*);
|
|
+
|
|
static Fts5ExprNearset *sqlite3Fts5ParseNearset(
|
|
Fts5Parse*,
|
|
Fts5ExprNearset*,
|
|
@@ -184409,9 +199714,12 @@
|
|
/**************************************************************************
|
|
** Interface to automatically generated code in fts5_unicode2.c.
|
|
*/
|
|
-static int sqlite3Fts5UnicodeIsalnum(int c);
|
|
static int sqlite3Fts5UnicodeIsdiacritic(int c);
|
|
static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
|
|
+
|
|
+static int sqlite3Fts5UnicodeCatParse(const char*, u8*);
|
|
+static int sqlite3Fts5UnicodeCategory(int iCode);
|
|
+static void sqlite3Fts5UnicodeAscii(u8*, u8*);
|
|
/*
|
|
** End of interface to code in fts5_unicode2.c.
|
|
**************************************************************************/
|
|
@@ -184429,9 +199737,10 @@
|
|
#define FTS5_STRING 9
|
|
#define FTS5_LP 10
|
|
#define FTS5_RP 11
|
|
-#define FTS5_COMMA 12
|
|
-#define FTS5_PLUS 13
|
|
-#define FTS5_STAR 14
|
|
+#define FTS5_CARET 12
|
|
+#define FTS5_COMMA 13
|
|
+#define FTS5_PLUS 14
|
|
+#define FTS5_STAR 15
|
|
|
|
/*
|
|
** 2000-05-29
|
|
@@ -184458,6 +199767,7 @@
|
|
** input grammar file:
|
|
*/
|
|
/* #include <stdio.h> */
|
|
+/* #include <assert.h> */
|
|
/************ Begin %include sections from the grammar ************************/
|
|
|
|
/* #include "fts5Int.h" */
|
|
@@ -184526,19 +199836,23 @@
|
|
** zero the stack is dynamically sized using realloc()
|
|
** sqlite3Fts5ParserARG_SDECL A static variable declaration for the %extra_argument
|
|
** sqlite3Fts5ParserARG_PDECL A parameter declaration for the %extra_argument
|
|
+** sqlite3Fts5ParserARG_PARAM Code to pass %extra_argument as a subroutine parameter
|
|
** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser
|
|
** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser
|
|
+** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context
|
|
** fts5YYERRORSYMBOL is the code number of the error symbol. If not
|
|
** defined, then do no error processing.
|
|
** fts5YYNSTATE the combined number of states.
|
|
** fts5YYNRULE the number of rules in the grammar
|
|
+** fts5YYNFTS5TOKEN Number of terminal symbols
|
|
** fts5YY_MAX_SHIFT Maximum value for shift actions
|
|
** fts5YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
|
|
** fts5YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
|
|
-** fts5YY_MIN_REDUCE Maximum value for reduce actions
|
|
** fts5YY_ERROR_ACTION The fts5yy_action[] code for syntax error
|
|
** fts5YY_ACCEPT_ACTION The fts5yy_action[] code for accept
|
|
** fts5YY_NO_ACTION The fts5yy_action[] code for no-op
|
|
+** fts5YY_MIN_REDUCE Minimum value for reduce actions
|
|
+** fts5YY_MAX_REDUCE Maximum value for reduce actions
|
|
*/
|
|
#ifndef INTERFACE
|
|
# define INTERFACE 1
|
|
@@ -184545,7 +199859,7 @@
|
|
#endif
|
|
/************* Begin control #defines *****************************************/
|
|
#define fts5YYCODETYPE unsigned char
|
|
-#define fts5YYNOCODE 28
|
|
+#define fts5YYNOCODE 27
|
|
#define fts5YYACTIONTYPE unsigned char
|
|
#define sqlite3Fts5ParserFTS5TOKENTYPE Fts5Token
|
|
typedef union {
|
|
@@ -184562,19 +199876,27 @@
|
|
#endif
|
|
#define sqlite3Fts5ParserARG_SDECL Fts5Parse *pParse;
|
|
#define sqlite3Fts5ParserARG_PDECL ,Fts5Parse *pParse
|
|
-#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse = fts5yypParser->pParse
|
|
-#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse = pParse
|
|
-#define fts5YYNSTATE 33
|
|
-#define fts5YYNRULE 27
|
|
-#define fts5YY_MAX_SHIFT 32
|
|
-#define fts5YY_MIN_SHIFTREDUCE 50
|
|
-#define fts5YY_MAX_SHIFTREDUCE 76
|
|
-#define fts5YY_MIN_REDUCE 77
|
|
-#define fts5YY_MAX_REDUCE 103
|
|
-#define fts5YY_ERROR_ACTION 104
|
|
-#define fts5YY_ACCEPT_ACTION 105
|
|
-#define fts5YY_NO_ACTION 106
|
|
+#define sqlite3Fts5ParserARG_PARAM ,pParse
|
|
+#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse;
|
|
+#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse;
|
|
+#define sqlite3Fts5ParserCTX_SDECL
|
|
+#define sqlite3Fts5ParserCTX_PDECL
|
|
+#define sqlite3Fts5ParserCTX_PARAM
|
|
+#define sqlite3Fts5ParserCTX_FETCH
|
|
+#define sqlite3Fts5ParserCTX_STORE
|
|
+#define fts5YYNSTATE 35
|
|
+#define fts5YYNRULE 28
|
|
+#define fts5YYNFTS5TOKEN 16
|
|
+#define fts5YY_MAX_SHIFT 34
|
|
+#define fts5YY_MIN_SHIFTREDUCE 52
|
|
+#define fts5YY_MAX_SHIFTREDUCE 79
|
|
+#define fts5YY_ERROR_ACTION 80
|
|
+#define fts5YY_ACCEPT_ACTION 81
|
|
+#define fts5YY_NO_ACTION 82
|
|
+#define fts5YY_MIN_REDUCE 83
|
|
+#define fts5YY_MAX_REDUCE 110
|
|
/************* End control #defines *******************************************/
|
|
+#define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])))
|
|
|
|
/* Define the fts5yytestcase() macro to be a no-op if is not already defined
|
|
** otherwise.
|
|
@@ -184603,9 +199925,6 @@
|
|
** N between fts5YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
|
|
** and fts5YY_MAX_SHIFTREDUCE reduce by rule N-fts5YY_MIN_SHIFTREDUCE.
|
|
**
|
|
-** N between fts5YY_MIN_REDUCE Reduce by rule N-fts5YY_MIN_REDUCE
|
|
-** and fts5YY_MAX_REDUCE
|
|
-**
|
|
** N == fts5YY_ERROR_ACTION A syntax error has occurred.
|
|
**
|
|
** N == fts5YY_ACCEPT_ACTION The parser accepts its input.
|
|
@@ -184613,6 +199932,9 @@
|
|
** N == fts5YY_NO_ACTION No such action. Denotes unused
|
|
** slots in the fts5yy_action[] table.
|
|
**
|
|
+** N between fts5YY_MIN_REDUCE Reduce by rule N-fts5YY_MIN_REDUCE
|
|
+** and fts5YY_MAX_REDUCE
|
|
+**
|
|
** The action table is constructed as a single large table named fts5yy_action[].
|
|
** Given state S and lookahead X, the action is computed as either:
|
|
**
|
|
@@ -184619,19 +199941,13 @@
|
|
** (A) N = fts5yy_action[ fts5yy_shift_ofst[S] + X ]
|
|
** (B) N = fts5yy_default[S]
|
|
**
|
|
-** The (A) formula is preferred. The B formula is used instead if:
|
|
-** (1) The fts5yy_shift_ofst[S]+X value is out of range, or
|
|
-** (2) fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X, or
|
|
-** (3) fts5yy_shift_ofst[S] equal fts5YY_SHIFT_USE_DFLT.
|
|
-** (Implementation note: fts5YY_SHIFT_USE_DFLT is chosen so that
|
|
-** fts5YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
|
-** Hence only tests (1) and (2) need to be evaluated.)
|
|
+** The (A) formula is preferred. The B formula is used instead if
|
|
+** fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X.
|
|
**
|
|
** The formulas above are for computing the action when the lookahead is
|
|
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
|
** a reduce action) then the fts5yy_reduce_ofst[] array is used in place of
|
|
-** the fts5yy_shift_ofst[] array and fts5YY_REDUCE_USE_DFLT is used in place of
|
|
-** fts5YY_SHIFT_USE_DFLT.
|
|
+** the fts5yy_shift_ofst[] array.
|
|
**
|
|
** The following are the tables generated in this section:
|
|
**
|
|
@@ -184645,54 +199961,56 @@
|
|
** fts5yy_default[] Default action for each state.
|
|
**
|
|
*********** Begin parsing tables **********************************************/
|
|
-#define fts5YY_ACTTAB_COUNT (98)
|
|
+#define fts5YY_ACTTAB_COUNT (105)
|
|
static const fts5YYACTIONTYPE fts5yy_action[] = {
|
|
- /* 0 */ 105, 19, 90, 6, 26, 93, 92, 24, 24, 17,
|
|
- /* 10 */ 90, 6, 26, 16, 92, 54, 24, 18, 90, 6,
|
|
- /* 20 */ 26, 10, 92, 12, 24, 75, 86, 90, 6, 26,
|
|
- /* 30 */ 13, 92, 75, 24, 20, 90, 6, 26, 101, 92,
|
|
- /* 40 */ 56, 24, 27, 90, 6, 26, 100, 92, 21, 24,
|
|
- /* 50 */ 23, 15, 30, 11, 1, 91, 22, 25, 9, 92,
|
|
- /* 60 */ 7, 24, 3, 4, 5, 3, 4, 5, 3, 77,
|
|
- /* 70 */ 4, 5, 3, 61, 23, 15, 60, 11, 80, 12,
|
|
- /* 80 */ 2, 13, 68, 10, 29, 52, 55, 75, 31, 32,
|
|
- /* 90 */ 8, 28, 5, 3, 51, 55, 72, 14,
|
|
+ /* 0 */ 81, 20, 96, 6, 28, 99, 98, 26, 26, 18,
|
|
+ /* 10 */ 96, 6, 28, 17, 98, 56, 26, 19, 96, 6,
|
|
+ /* 20 */ 28, 14, 98, 14, 26, 31, 92, 96, 6, 28,
|
|
+ /* 30 */ 108, 98, 25, 26, 21, 96, 6, 28, 78, 98,
|
|
+ /* 40 */ 58, 26, 29, 96, 6, 28, 107, 98, 22, 26,
|
|
+ /* 50 */ 24, 16, 12, 11, 1, 13, 13, 24, 16, 23,
|
|
+ /* 60 */ 11, 33, 34, 13, 97, 8, 27, 32, 98, 7,
|
|
+ /* 70 */ 26, 3, 4, 5, 3, 4, 5, 3, 83, 4,
|
|
+ /* 80 */ 5, 3, 63, 5, 3, 62, 12, 2, 86, 13,
|
|
+ /* 90 */ 9, 30, 10, 10, 54, 57, 75, 78, 78, 53,
|
|
+ /* 100 */ 57, 15, 82, 82, 71,
|
|
};
|
|
static const fts5YYCODETYPE fts5yy_lookahead[] = {
|
|
/* 0 */ 16, 17, 18, 19, 20, 22, 22, 24, 24, 17,
|
|
/* 10 */ 18, 19, 20, 7, 22, 9, 24, 17, 18, 19,
|
|
- /* 20 */ 20, 10, 22, 9, 24, 14, 17, 18, 19, 20,
|
|
- /* 30 */ 9, 22, 14, 24, 17, 18, 19, 20, 26, 22,
|
|
+ /* 20 */ 20, 9, 22, 9, 24, 13, 17, 18, 19, 20,
|
|
+ /* 30 */ 26, 22, 24, 24, 17, 18, 19, 20, 15, 22,
|
|
/* 40 */ 9, 24, 17, 18, 19, 20, 26, 22, 21, 24,
|
|
- /* 50 */ 6, 7, 13, 9, 10, 18, 21, 20, 5, 22,
|
|
- /* 60 */ 5, 24, 3, 1, 2, 3, 1, 2, 3, 0,
|
|
- /* 70 */ 1, 2, 3, 11, 6, 7, 11, 9, 5, 9,
|
|
- /* 80 */ 10, 9, 11, 10, 12, 8, 9, 14, 24, 25,
|
|
- /* 90 */ 23, 24, 2, 3, 8, 9, 9, 9,
|
|
+ /* 50 */ 6, 7, 9, 9, 10, 12, 12, 6, 7, 21,
|
|
+ /* 60 */ 9, 24, 25, 12, 18, 5, 20, 14, 22, 5,
|
|
+ /* 70 */ 24, 3, 1, 2, 3, 1, 2, 3, 0, 1,
|
|
+ /* 80 */ 2, 3, 11, 2, 3, 11, 9, 10, 5, 12,
|
|
+ /* 90 */ 23, 24, 10, 10, 8, 9, 9, 15, 15, 8,
|
|
+ /* 100 */ 9, 9, 27, 27, 11, 27, 27, 27, 27, 27,
|
|
+ /* 110 */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
|
+ /* 120 */ 27,
|
|
};
|
|
-#define fts5YY_SHIFT_USE_DFLT (98)
|
|
-#define fts5YY_SHIFT_COUNT (32)
|
|
+#define fts5YY_SHIFT_COUNT (34)
|
|
#define fts5YY_SHIFT_MIN (0)
|
|
-#define fts5YY_SHIFT_MAX (90)
|
|
+#define fts5YY_SHIFT_MAX (93)
|
|
static const unsigned char fts5yy_shift_ofst[] = {
|
|
- /* 0 */ 44, 44, 44, 44, 44, 44, 68, 70, 72, 14,
|
|
- /* 10 */ 21, 73, 11, 18, 18, 31, 31, 62, 65, 69,
|
|
- /* 20 */ 90, 77, 86, 6, 39, 53, 55, 59, 39, 87,
|
|
- /* 30 */ 88, 39, 71,
|
|
+ /* 0 */ 44, 44, 44, 44, 44, 44, 51, 77, 43, 12,
|
|
+ /* 10 */ 14, 83, 82, 14, 23, 23, 31, 31, 71, 74,
|
|
+ /* 20 */ 78, 81, 86, 91, 6, 53, 53, 60, 64, 68,
|
|
+ /* 30 */ 53, 87, 92, 53, 93,
|
|
};
|
|
-#define fts5YY_REDUCE_USE_DFLT (-18)
|
|
-#define fts5YY_REDUCE_COUNT (16)
|
|
+#define fts5YY_REDUCE_COUNT (17)
|
|
#define fts5YY_REDUCE_MIN (-17)
|
|
#define fts5YY_REDUCE_MAX (67)
|
|
static const signed char fts5yy_reduce_ofst[] = {
|
|
- /* 0 */ -16, -8, 0, 9, 17, 25, 37, -17, 64, -17,
|
|
- /* 10 */ 67, 12, 12, 12, 20, 27, 35,
|
|
+ /* 0 */ -16, -8, 0, 9, 17, 25, 46, -17, -17, 37,
|
|
+ /* 10 */ 67, 4, 4, 8, 4, 20, 27, 38,
|
|
};
|
|
static const fts5YYACTIONTYPE fts5yy_default[] = {
|
|
- /* 0 */ 104, 104, 104, 104, 104, 104, 89, 104, 98, 104,
|
|
- /* 10 */ 104, 103, 103, 103, 103, 104, 104, 104, 104, 104,
|
|
- /* 20 */ 85, 104, 104, 104, 94, 104, 104, 84, 96, 104,
|
|
- /* 30 */ 104, 97, 104,
|
|
+ /* 0 */ 80, 80, 80, 80, 80, 80, 95, 80, 80, 105,
|
|
+ /* 10 */ 80, 110, 110, 80, 110, 110, 80, 80, 80, 80,
|
|
+ /* 20 */ 80, 91, 80, 80, 80, 101, 100, 80, 80, 90,
|
|
+ /* 30 */ 103, 80, 80, 104, 80,
|
|
};
|
|
/********** End of lemon-generated parsing tables *****************************/
|
|
|
|
@@ -184751,6 +200069,7 @@
|
|
int fts5yyerrcnt; /* Shifts left before out of the error */
|
|
#endif
|
|
sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */
|
|
+ sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */
|
|
#if fts5YYSTACKDEPTH<=0
|
|
int fts5yystksz; /* Current side of the stack */
|
|
fts5yyStackEntry *fts5yystack; /* The parser's stack */
|
|
@@ -184794,19 +200113,39 @@
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
-#ifndef NDEBUG
|
|
+#if defined(fts5YYCOVERAGE) || !defined(NDEBUG)
|
|
/* For tracing shifts, the names of all terminals and nonterminals
|
|
** are required. The following table supplies these names */
|
|
static const char *const fts5yyTokenName[] = {
|
|
- "$", "OR", "AND", "NOT",
|
|
- "TERM", "COLON", "MINUS", "LCP",
|
|
- "RCP", "STRING", "LP", "RP",
|
|
- "COMMA", "PLUS", "STAR", "error",
|
|
- "input", "expr", "cnearset", "exprlist",
|
|
- "colset", "colsetlist", "nearset", "nearphrases",
|
|
- "phrase", "neardist_opt", "star_opt",
|
|
+ /* 0 */ "$",
|
|
+ /* 1 */ "OR",
|
|
+ /* 2 */ "AND",
|
|
+ /* 3 */ "NOT",
|
|
+ /* 4 */ "TERM",
|
|
+ /* 5 */ "COLON",
|
|
+ /* 6 */ "MINUS",
|
|
+ /* 7 */ "LCP",
|
|
+ /* 8 */ "RCP",
|
|
+ /* 9 */ "STRING",
|
|
+ /* 10 */ "LP",
|
|
+ /* 11 */ "RP",
|
|
+ /* 12 */ "CARET",
|
|
+ /* 13 */ "COMMA",
|
|
+ /* 14 */ "PLUS",
|
|
+ /* 15 */ "STAR",
|
|
+ /* 16 */ "input",
|
|
+ /* 17 */ "expr",
|
|
+ /* 18 */ "cnearset",
|
|
+ /* 19 */ "exprlist",
|
|
+ /* 20 */ "colset",
|
|
+ /* 21 */ "colsetlist",
|
|
+ /* 22 */ "nearset",
|
|
+ /* 23 */ "nearphrases",
|
|
+ /* 24 */ "phrase",
|
|
+ /* 25 */ "neardist_opt",
|
|
+ /* 26 */ "star_opt",
|
|
};
|
|
-#endif /* NDEBUG */
|
|
+#endif /* defined(fts5YYCOVERAGE) || !defined(NDEBUG) */
|
|
|
|
#ifndef NDEBUG
|
|
/* For tracing reduce actions, the names of all rules are required.
|
|
@@ -184830,15 +200169,16 @@
|
|
/* 15 */ "cnearset ::= nearset",
|
|
/* 16 */ "cnearset ::= colset COLON nearset",
|
|
/* 17 */ "nearset ::= phrase",
|
|
- /* 18 */ "nearset ::= STRING LP nearphrases neardist_opt RP",
|
|
- /* 19 */ "nearphrases ::= phrase",
|
|
- /* 20 */ "nearphrases ::= nearphrases phrase",
|
|
- /* 21 */ "neardist_opt ::=",
|
|
- /* 22 */ "neardist_opt ::= COMMA STRING",
|
|
- /* 23 */ "phrase ::= phrase PLUS STRING star_opt",
|
|
- /* 24 */ "phrase ::= STRING star_opt",
|
|
- /* 25 */ "star_opt ::= STAR",
|
|
- /* 26 */ "star_opt ::=",
|
|
+ /* 18 */ "nearset ::= CARET phrase",
|
|
+ /* 19 */ "nearset ::= STRING LP nearphrases neardist_opt RP",
|
|
+ /* 20 */ "nearphrases ::= phrase",
|
|
+ /* 21 */ "nearphrases ::= nearphrases phrase",
|
|
+ /* 22 */ "neardist_opt ::=",
|
|
+ /* 23 */ "neardist_opt ::= COMMA STRING",
|
|
+ /* 24 */ "phrase ::= phrase PLUS STRING star_opt",
|
|
+ /* 25 */ "phrase ::= STRING star_opt",
|
|
+ /* 26 */ "star_opt ::= STAR",
|
|
+ /* 27 */ "star_opt ::=",
|
|
};
|
|
#endif /* NDEBUG */
|
|
|
|
@@ -184887,28 +200227,29 @@
|
|
|
|
/* Initialize a new parser that has already been allocated.
|
|
*/
|
|
-static void sqlite3Fts5ParserInit(void *fts5yypParser){
|
|
- fts5yyParser *pParser = (fts5yyParser*)fts5yypParser;
|
|
+static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PDECL){
|
|
+ fts5yyParser *fts5yypParser = (fts5yyParser*)fts5yypRawParser;
|
|
+ sqlite3Fts5ParserCTX_STORE
|
|
#ifdef fts5YYTRACKMAXSTACKDEPTH
|
|
- pParser->fts5yyhwm = 0;
|
|
+ fts5yypParser->fts5yyhwm = 0;
|
|
#endif
|
|
#if fts5YYSTACKDEPTH<=0
|
|
- pParser->fts5yytos = NULL;
|
|
- pParser->fts5yystack = NULL;
|
|
- pParser->fts5yystksz = 0;
|
|
- if( fts5yyGrowStack(pParser) ){
|
|
- pParser->fts5yystack = &pParser->fts5yystk0;
|
|
- pParser->fts5yystksz = 1;
|
|
+ fts5yypParser->fts5yytos = NULL;
|
|
+ fts5yypParser->fts5yystack = NULL;
|
|
+ fts5yypParser->fts5yystksz = 0;
|
|
+ if( fts5yyGrowStack(fts5yypParser) ){
|
|
+ fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0;
|
|
+ fts5yypParser->fts5yystksz = 1;
|
|
}
|
|
#endif
|
|
#ifndef fts5YYNOERRORRECOVERY
|
|
- pParser->fts5yyerrcnt = -1;
|
|
+ fts5yypParser->fts5yyerrcnt = -1;
|
|
#endif
|
|
- pParser->fts5yytos = pParser->fts5yystack;
|
|
- pParser->fts5yystack[0].stateno = 0;
|
|
- pParser->fts5yystack[0].major = 0;
|
|
+ fts5yypParser->fts5yytos = fts5yypParser->fts5yystack;
|
|
+ fts5yypParser->fts5yystack[0].stateno = 0;
|
|
+ fts5yypParser->fts5yystack[0].major = 0;
|
|
#if fts5YYSTACKDEPTH>0
|
|
- pParser->fts5yystackEnd = &pParser->fts5yystack[fts5YYSTACKDEPTH-1];
|
|
+ fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1];
|
|
#endif
|
|
}
|
|
|
|
@@ -184925,11 +200266,14 @@
|
|
** A pointer to a parser. This pointer is used in subsequent calls
|
|
** to sqlite3Fts5Parser and sqlite3Fts5ParserFree.
|
|
*/
|
|
-static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(fts5YYMALLOCARGTYPE)){
|
|
- fts5yyParser *pParser;
|
|
- pParser = (fts5yyParser*)(*mallocProc)( (fts5YYMALLOCARGTYPE)sizeof(fts5yyParser) );
|
|
- if( pParser ) sqlite3Fts5ParserInit(pParser);
|
|
- return pParser;
|
|
+static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(fts5YYMALLOCARGTYPE) sqlite3Fts5ParserCTX_PDECL){
|
|
+ fts5yyParser *fts5yypParser;
|
|
+ fts5yypParser = (fts5yyParser*)(*mallocProc)( (fts5YYMALLOCARGTYPE)sizeof(fts5yyParser) );
|
|
+ if( fts5yypParser ){
|
|
+ sqlite3Fts5ParserCTX_STORE
|
|
+ sqlite3Fts5ParserInit(fts5yypParser sqlite3Fts5ParserCTX_PARAM);
|
|
+ }
|
|
+ return (void*)fts5yypParser;
|
|
}
|
|
#endif /* sqlite3Fts5Parser_ENGINEALWAYSONSTACK */
|
|
|
|
@@ -184946,7 +200290,8 @@
|
|
fts5YYCODETYPE fts5yymajor, /* Type code for object to destroy */
|
|
fts5YYMINORTYPE *fts5yypminor /* The object to be destroyed */
|
|
){
|
|
- sqlite3Fts5ParserARG_FETCH;
|
|
+ sqlite3Fts5ParserARG_FETCH
|
|
+ sqlite3Fts5ParserCTX_FETCH
|
|
switch( fts5yymajor ){
|
|
/* Here is inserted the actions which take place when a
|
|
** terminal or non-terminal is destroyed. This can happen
|
|
@@ -185056,24 +200401,66 @@
|
|
}
|
|
#endif
|
|
|
|
+/* This array of booleans keeps track of the parser statement
|
|
+** coverage. The element fts5yycoverage[X][Y] is set when the parser
|
|
+** is in state X and has a lookahead token Y. In a well-tested
|
|
+** systems, every element of this matrix should end up being set.
|
|
+*/
|
|
+#if defined(fts5YYCOVERAGE)
|
|
+static unsigned char fts5yycoverage[fts5YYNSTATE][fts5YYNFTS5TOKEN];
|
|
+#endif
|
|
+
|
|
/*
|
|
+** Write into out a description of every state/lookahead combination that
|
|
+**
|
|
+** (1) has not been used by the parser, and
|
|
+** (2) is not a syntax error.
|
|
+**
|
|
+** Return the number of missed state/lookahead combinations.
|
|
+*/
|
|
+#if defined(fts5YYCOVERAGE)
|
|
+static int sqlite3Fts5ParserCoverage(FILE *out){
|
|
+ int stateno, iLookAhead, i;
|
|
+ int nMissed = 0;
|
|
+ for(stateno=0; stateno<fts5YYNSTATE; stateno++){
|
|
+ i = fts5yy_shift_ofst[stateno];
|
|
+ for(iLookAhead=0; iLookAhead<fts5YYNFTS5TOKEN; iLookAhead++){
|
|
+ if( fts5yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
|
|
+ if( fts5yycoverage[stateno][iLookAhead]==0 ) nMissed++;
|
|
+ if( out ){
|
|
+ fprintf(out,"State %d lookahead %s %s\n", stateno,
|
|
+ fts5yyTokenName[iLookAhead],
|
|
+ fts5yycoverage[stateno][iLookAhead] ? "ok" : "missed");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return nMissed;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
** Find the appropriate action for a parser given the terminal
|
|
** look-ahead token iLookAhead.
|
|
*/
|
|
-static unsigned int fts5yy_find_shift_action(
|
|
- fts5yyParser *pParser, /* The parser */
|
|
- fts5YYCODETYPE iLookAhead /* The look-ahead token */
|
|
+static fts5YYACTIONTYPE fts5yy_find_shift_action(
|
|
+ fts5YYCODETYPE iLookAhead, /* The look-ahead token */
|
|
+ fts5YYACTIONTYPE stateno /* Current state number */
|
|
){
|
|
int i;
|
|
- int stateno = pParser->fts5yytos->stateno;
|
|
-
|
|
- if( stateno>=fts5YY_MIN_REDUCE ) return stateno;
|
|
+
|
|
+ if( stateno>fts5YY_MAX_SHIFT ) return stateno;
|
|
assert( stateno <= fts5YY_SHIFT_COUNT );
|
|
+#if defined(fts5YYCOVERAGE)
|
|
+ fts5yycoverage[stateno][iLookAhead] = 1;
|
|
+#endif
|
|
do{
|
|
i = fts5yy_shift_ofst[stateno];
|
|
+ assert( i>=0 );
|
|
+ /* assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD ); */
|
|
assert( iLookAhead!=fts5YYNOCODE );
|
|
+ assert( iLookAhead < fts5YYNFTS5TOKEN );
|
|
i += iLookAhead;
|
|
- if( i<0 || i>=fts5YY_ACTTAB_COUNT || fts5yy_lookahead[i]!=iLookAhead ){
|
|
+ if( i>=fts5YY_NLOOKAHEAD || fts5yy_lookahead[i]!=iLookAhead ){
|
|
#ifdef fts5YYFALLBACK
|
|
fts5YYCODETYPE iFallback; /* Fallback token */
|
|
if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
|
|
@@ -185099,6 +200486,7 @@
|
|
#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
|
|
j<fts5YY_ACTTAB_COUNT &&
|
|
#endif
|
|
+ j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) &&
|
|
fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0
|
|
){
|
|
#ifndef NDEBUG
|
|
@@ -185123,8 +200511,8 @@
|
|
** Find the appropriate action for a parser given the non-terminal
|
|
** look-ahead token iLookAhead.
|
|
*/
|
|
-static int fts5yy_find_reduce_action(
|
|
- int stateno, /* Current state number */
|
|
+static fts5YYACTIONTYPE fts5yy_find_reduce_action(
|
|
+ fts5YYACTIONTYPE stateno, /* Current state number */
|
|
fts5YYCODETYPE iLookAhead /* The look-ahead token */
|
|
){
|
|
int i;
|
|
@@ -185136,7 +200524,6 @@
|
|
assert( stateno<=fts5YY_REDUCE_COUNT );
|
|
#endif
|
|
i = fts5yy_reduce_ofst[stateno];
|
|
- assert( i!=fts5YY_REDUCE_USE_DFLT );
|
|
assert( iLookAhead!=fts5YYNOCODE );
|
|
i += iLookAhead;
|
|
#ifdef fts5YYERRORSYMBOL
|
|
@@ -185154,7 +200541,8 @@
|
|
** The following routine is called if the stack overflows.
|
|
*/
|
|
static void fts5yyStackOverflow(fts5yyParser *fts5yypParser){
|
|
- sqlite3Fts5ParserARG_FETCH;
|
|
+ sqlite3Fts5ParserARG_FETCH
|
|
+ sqlite3Fts5ParserCTX_FETCH
|
|
#ifndef NDEBUG
|
|
if( fts5yyTraceFILE ){
|
|
fprintf(fts5yyTraceFILE,"%sStack Overflow!\n",fts5yyTracePrompt);
|
|
@@ -185167,7 +200555,8 @@
|
|
|
|
sqlite3Fts5ParseError(pParse, "fts5: parser stack overflow");
|
|
/******** End %stack_overflow code ********************************************/
|
|
- sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
|
|
+ sqlite3Fts5ParserARG_STORE /* Suppress warning about unused %extra_argument var */
|
|
+ sqlite3Fts5ParserCTX_STORE
|
|
}
|
|
|
|
/*
|
|
@@ -185174,20 +200563,21 @@
|
|
** Print tracing information for a SHIFT action
|
|
*/
|
|
#ifndef NDEBUG
|
|
-static void fts5yyTraceShift(fts5yyParser *fts5yypParser, int fts5yyNewState){
|
|
+static void fts5yyTraceShift(fts5yyParser *fts5yypParser, int fts5yyNewState, const char *zTag){
|
|
if( fts5yyTraceFILE ){
|
|
if( fts5yyNewState<fts5YYNSTATE ){
|
|
- fprintf(fts5yyTraceFILE,"%sShift '%s', go to state %d\n",
|
|
- fts5yyTracePrompt,fts5yyTokenName[fts5yypParser->fts5yytos->major],
|
|
+ fprintf(fts5yyTraceFILE,"%s%s '%s', go to state %d\n",
|
|
+ fts5yyTracePrompt, zTag, fts5yyTokenName[fts5yypParser->fts5yytos->major],
|
|
fts5yyNewState);
|
|
}else{
|
|
- fprintf(fts5yyTraceFILE,"%sShift '%s'\n",
|
|
- fts5yyTracePrompt,fts5yyTokenName[fts5yypParser->fts5yytos->major]);
|
|
+ fprintf(fts5yyTraceFILE,"%s%s '%s', pending reduce %d\n",
|
|
+ fts5yyTracePrompt, zTag, fts5yyTokenName[fts5yypParser->fts5yytos->major],
|
|
+ fts5yyNewState - fts5YY_MIN_REDUCE);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
-# define fts5yyTraceShift(X,Y)
|
|
+# define fts5yyTraceShift(X,Y,Z)
|
|
#endif
|
|
|
|
/*
|
|
@@ -185195,8 +200585,8 @@
|
|
*/
|
|
static void fts5yy_shift(
|
|
fts5yyParser *fts5yypParser, /* The parser to be shifted */
|
|
- int fts5yyNewState, /* The new state to shift in */
|
|
- int fts5yyMajor, /* The major token to shift in */
|
|
+ fts5YYACTIONTYPE fts5yyNewState, /* The new state to shift in */
|
|
+ fts5YYCODETYPE fts5yyMajor, /* The major token to shift in */
|
|
sqlite3Fts5ParserFTS5TOKENTYPE fts5yyMinor /* The minor token to shift in */
|
|
){
|
|
fts5yyStackEntry *fts5yytos;
|
|
@@ -185226,10 +200616,10 @@
|
|
fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
|
|
}
|
|
fts5yytos = fts5yypParser->fts5yytos;
|
|
- fts5yytos->stateno = (fts5YYACTIONTYPE)fts5yyNewState;
|
|
- fts5yytos->major = (fts5YYCODETYPE)fts5yyMajor;
|
|
+ fts5yytos->stateno = fts5yyNewState;
|
|
+ fts5yytos->major = fts5yyMajor;
|
|
fts5yytos->minor.fts5yy0 = fts5yyMinor;
|
|
- fts5yyTraceShift(fts5yypParser, fts5yyNewState);
|
|
+ fts5yyTraceShift(fts5yypParser, fts5yyNewState, "Shift");
|
|
}
|
|
|
|
/* The following table contains information about every rule that
|
|
@@ -185239,33 +200629,34 @@
|
|
fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
|
|
signed char nrhs; /* Negative of the number of RHS symbols in the rule */
|
|
} fts5yyRuleInfo[] = {
|
|
- { 16, -1 },
|
|
- { 20, -4 },
|
|
- { 20, -3 },
|
|
- { 20, -1 },
|
|
- { 20, -2 },
|
|
- { 21, -2 },
|
|
- { 21, -1 },
|
|
- { 17, -3 },
|
|
- { 17, -3 },
|
|
- { 17, -3 },
|
|
- { 17, -5 },
|
|
- { 17, -3 },
|
|
- { 17, -1 },
|
|
- { 19, -1 },
|
|
- { 19, -2 },
|
|
- { 18, -1 },
|
|
- { 18, -3 },
|
|
- { 22, -1 },
|
|
- { 22, -5 },
|
|
- { 23, -1 },
|
|
- { 23, -2 },
|
|
- { 25, 0 },
|
|
- { 25, -2 },
|
|
- { 24, -4 },
|
|
- { 24, -2 },
|
|
- { 26, -1 },
|
|
- { 26, 0 },
|
|
+ { 16, -1 }, /* (0) input ::= expr */
|
|
+ { 20, -4 }, /* (1) colset ::= MINUS LCP colsetlist RCP */
|
|
+ { 20, -3 }, /* (2) colset ::= LCP colsetlist RCP */
|
|
+ { 20, -1 }, /* (3) colset ::= STRING */
|
|
+ { 20, -2 }, /* (4) colset ::= MINUS STRING */
|
|
+ { 21, -2 }, /* (5) colsetlist ::= colsetlist STRING */
|
|
+ { 21, -1 }, /* (6) colsetlist ::= STRING */
|
|
+ { 17, -3 }, /* (7) expr ::= expr AND expr */
|
|
+ { 17, -3 }, /* (8) expr ::= expr OR expr */
|
|
+ { 17, -3 }, /* (9) expr ::= expr NOT expr */
|
|
+ { 17, -5 }, /* (10) expr ::= colset COLON LP expr RP */
|
|
+ { 17, -3 }, /* (11) expr ::= LP expr RP */
|
|
+ { 17, -1 }, /* (12) expr ::= exprlist */
|
|
+ { 19, -1 }, /* (13) exprlist ::= cnearset */
|
|
+ { 19, -2 }, /* (14) exprlist ::= exprlist cnearset */
|
|
+ { 18, -1 }, /* (15) cnearset ::= nearset */
|
|
+ { 18, -3 }, /* (16) cnearset ::= colset COLON nearset */
|
|
+ { 22, -1 }, /* (17) nearset ::= phrase */
|
|
+ { 22, -2 }, /* (18) nearset ::= CARET phrase */
|
|
+ { 22, -5 }, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */
|
|
+ { 23, -1 }, /* (20) nearphrases ::= phrase */
|
|
+ { 23, -2 }, /* (21) nearphrases ::= nearphrases phrase */
|
|
+ { 25, 0 }, /* (22) neardist_opt ::= */
|
|
+ { 25, -2 }, /* (23) neardist_opt ::= COMMA STRING */
|
|
+ { 24, -4 }, /* (24) phrase ::= phrase PLUS STRING star_opt */
|
|
+ { 24, -2 }, /* (25) phrase ::= STRING star_opt */
|
|
+ { 26, -1 }, /* (26) star_opt ::= STAR */
|
|
+ { 26, 0 }, /* (27) star_opt ::= */
|
|
};
|
|
|
|
static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */
|
|
@@ -185273,22 +200664,39 @@
|
|
/*
|
|
** Perform a reduce action and the shift that must immediately
|
|
** follow the reduce.
|
|
+**
|
|
+** The fts5yyLookahead and fts5yyLookaheadToken parameters provide reduce actions
|
|
+** access to the lookahead token (if any). The fts5yyLookahead will be fts5YYNOCODE
|
|
+** if the lookahead token has already been consumed. As this procedure is
|
|
+** only called from one place, optimizing compilers will in-line it, which
|
|
+** means that the extra parameters have no performance impact.
|
|
*/
|
|
-static void fts5yy_reduce(
|
|
+static fts5YYACTIONTYPE fts5yy_reduce(
|
|
fts5yyParser *fts5yypParser, /* The parser */
|
|
- unsigned int fts5yyruleno /* Number of the rule by which to reduce */
|
|
+ unsigned int fts5yyruleno, /* Number of the rule by which to reduce */
|
|
+ int fts5yyLookahead, /* Lookahead token, or fts5YYNOCODE if none */
|
|
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyLookaheadToken /* Value of the lookahead token */
|
|
+ sqlite3Fts5ParserCTX_PDECL /* %extra_context */
|
|
){
|
|
int fts5yygoto; /* The next state */
|
|
- int fts5yyact; /* The next action */
|
|
+ fts5YYACTIONTYPE fts5yyact; /* The next action */
|
|
fts5yyStackEntry *fts5yymsp; /* The top of the parser's stack */
|
|
int fts5yysize; /* Amount to pop the stack */
|
|
- sqlite3Fts5ParserARG_FETCH;
|
|
+ sqlite3Fts5ParserARG_FETCH
|
|
+ (void)fts5yyLookahead;
|
|
+ (void)fts5yyLookaheadToken;
|
|
fts5yymsp = fts5yypParser->fts5yytos;
|
|
#ifndef NDEBUG
|
|
if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){
|
|
fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
|
|
- fprintf(fts5yyTraceFILE, "%sReduce [%s], go to state %d.\n", fts5yyTracePrompt,
|
|
- fts5yyRuleName[fts5yyruleno], fts5yymsp[fts5yysize].stateno);
|
|
+ if( fts5yysize ){
|
|
+ fprintf(fts5yyTraceFILE, "%sReduce %d [%s], go to state %d.\n",
|
|
+ fts5yyTracePrompt,
|
|
+ fts5yyruleno, fts5yyRuleName[fts5yyruleno], fts5yymsp[fts5yysize].stateno);
|
|
+ }else{
|
|
+ fprintf(fts5yyTraceFILE, "%sReduce %d [%s].\n",
|
|
+ fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno]);
|
|
+ }
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
@@ -185305,13 +200713,19 @@
|
|
#if fts5YYSTACKDEPTH>0
|
|
if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
|
|
fts5yyStackOverflow(fts5yypParser);
|
|
- return;
|
|
+ /* The call to fts5yyStackOverflow() above pops the stack until it is
|
|
+ ** empty, causing the main parser loop to exit. So the return value
|
|
+ ** is never used and does not matter. */
|
|
+ return 0;
|
|
}
|
|
#else
|
|
if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
|
|
if( fts5yyGrowStack(fts5yypParser) ){
|
|
fts5yyStackOverflow(fts5yypParser);
|
|
- return;
|
|
+ /* The call to fts5yyStackOverflow() above pops the stack until it is
|
|
+ ** empty, causing the main parser loop to exit. So the return value
|
|
+ ** is never used and does not matter. */
|
|
+ return 0;
|
|
}
|
|
fts5yymsp = fts5yypParser->fts5yytos;
|
|
}
|
|
@@ -185419,7 +200833,13 @@
|
|
{ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53); }
|
|
fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
|
|
break;
|
|
- case 18: /* nearset ::= STRING LP nearphrases neardist_opt RP */
|
|
+ case 18: /* nearset ::= CARET phrase */
|
|
+{
|
|
+ sqlite3Fts5ParseSetCaret(fts5yymsp[0].minor.fts5yy53);
|
|
+ fts5yymsp[-1].minor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
|
|
+}
|
|
+ break;
|
|
+ case 19: /* nearset ::= STRING LP nearphrases neardist_opt RP */
|
|
{
|
|
sqlite3Fts5ParseNear(pParse, &fts5yymsp[-4].minor.fts5yy0);
|
|
sqlite3Fts5ParseSetDistance(pParse, fts5yymsp[-2].minor.fts5yy46, &fts5yymsp[-1].minor.fts5yy0);
|
|
@@ -185427,40 +200847,40 @@
|
|
}
|
|
fts5yymsp[-4].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
|
|
break;
|
|
- case 19: /* nearphrases ::= phrase */
|
|
+ case 20: /* nearphrases ::= phrase */
|
|
{
|
|
fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
|
|
}
|
|
fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
|
|
break;
|
|
- case 20: /* nearphrases ::= nearphrases phrase */
|
|
+ case 21: /* nearphrases ::= nearphrases phrase */
|
|
{
|
|
fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, fts5yymsp[-1].minor.fts5yy46, fts5yymsp[0].minor.fts5yy53);
|
|
}
|
|
fts5yymsp[-1].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
|
|
break;
|
|
- case 21: /* neardist_opt ::= */
|
|
+ case 22: /* neardist_opt ::= */
|
|
{ fts5yymsp[1].minor.fts5yy0.p = 0; fts5yymsp[1].minor.fts5yy0.n = 0; }
|
|
break;
|
|
- case 22: /* neardist_opt ::= COMMA STRING */
|
|
+ case 23: /* neardist_opt ::= COMMA STRING */
|
|
{ fts5yymsp[-1].minor.fts5yy0 = fts5yymsp[0].minor.fts5yy0; }
|
|
break;
|
|
- case 23: /* phrase ::= phrase PLUS STRING star_opt */
|
|
+ case 24: /* phrase ::= phrase PLUS STRING star_opt */
|
|
{
|
|
fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy53, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
|
|
}
|
|
fts5yymsp[-3].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
|
|
break;
|
|
- case 24: /* phrase ::= STRING star_opt */
|
|
+ case 25: /* phrase ::= STRING star_opt */
|
|
{
|
|
fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
|
|
}
|
|
fts5yymsp[-1].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
|
|
break;
|
|
- case 25: /* star_opt ::= STAR */
|
|
+ case 26: /* star_opt ::= STAR */
|
|
{ fts5yymsp[0].minor.fts5yy4 = 1; }
|
|
break;
|
|
- case 26: /* star_opt ::= */
|
|
+ case 27: /* star_opt ::= */
|
|
{ fts5yymsp[1].minor.fts5yy4 = 0; }
|
|
break;
|
|
default:
|
|
@@ -185479,16 +200899,12 @@
|
|
/* It is not possible for a REDUCE to be followed by an error */
|
|
assert( fts5yyact!=fts5YY_ERROR_ACTION );
|
|
|
|
- if( fts5yyact==fts5YY_ACCEPT_ACTION ){
|
|
- fts5yypParser->fts5yytos += fts5yysize;
|
|
- fts5yy_accept(fts5yypParser);
|
|
- }else{
|
|
- fts5yymsp += fts5yysize+1;
|
|
- fts5yypParser->fts5yytos = fts5yymsp;
|
|
- fts5yymsp->stateno = (fts5YYACTIONTYPE)fts5yyact;
|
|
- fts5yymsp->major = (fts5YYCODETYPE)fts5yygoto;
|
|
- fts5yyTraceShift(fts5yypParser, fts5yyact);
|
|
- }
|
|
+ fts5yymsp += fts5yysize+1;
|
|
+ fts5yypParser->fts5yytos = fts5yymsp;
|
|
+ fts5yymsp->stateno = (fts5YYACTIONTYPE)fts5yyact;
|
|
+ fts5yymsp->major = (fts5YYCODETYPE)fts5yygoto;
|
|
+ fts5yyTraceShift(fts5yypParser, fts5yyact, "... then shift");
|
|
+ return fts5yyact;
|
|
}
|
|
|
|
/*
|
|
@@ -185498,7 +200914,8 @@
|
|
static void fts5yy_parse_failed(
|
|
fts5yyParser *fts5yypParser /* The parser */
|
|
){
|
|
- sqlite3Fts5ParserARG_FETCH;
|
|
+ sqlite3Fts5ParserARG_FETCH
|
|
+ sqlite3Fts5ParserCTX_FETCH
|
|
#ifndef NDEBUG
|
|
if( fts5yyTraceFILE ){
|
|
fprintf(fts5yyTraceFILE,"%sFail!\n",fts5yyTracePrompt);
|
|
@@ -185509,7 +200926,8 @@
|
|
** parser fails */
|
|
/************ Begin %parse_failure code ***************************************/
|
|
/************ End %parse_failure code *****************************************/
|
|
- sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3Fts5ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3Fts5ParserCTX_STORE
|
|
}
|
|
#endif /* fts5YYNOERRORRECOVERY */
|
|
|
|
@@ -185521,7 +200939,8 @@
|
|
int fts5yymajor, /* The major type of the error token */
|
|
sqlite3Fts5ParserFTS5TOKENTYPE fts5yyminor /* The minor type of the error token */
|
|
){
|
|
- sqlite3Fts5ParserARG_FETCH;
|
|
+ sqlite3Fts5ParserARG_FETCH
|
|
+ sqlite3Fts5ParserCTX_FETCH
|
|
#define FTS5TOKEN fts5yyminor
|
|
/************ Begin %syntax_error code ****************************************/
|
|
|
|
@@ -185530,7 +200949,8 @@
|
|
pParse, "fts5: syntax error near \"%.*s\"",FTS5TOKEN.n,FTS5TOKEN.p
|
|
);
|
|
/************ End %syntax_error code ******************************************/
|
|
- sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3Fts5ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3Fts5ParserCTX_STORE
|
|
}
|
|
|
|
/*
|
|
@@ -185539,7 +200959,8 @@
|
|
static void fts5yy_accept(
|
|
fts5yyParser *fts5yypParser /* The parser */
|
|
){
|
|
- sqlite3Fts5ParserARG_FETCH;
|
|
+ sqlite3Fts5ParserARG_FETCH
|
|
+ sqlite3Fts5ParserCTX_FETCH
|
|
#ifndef NDEBUG
|
|
if( fts5yyTraceFILE ){
|
|
fprintf(fts5yyTraceFILE,"%sAccept!\n",fts5yyTracePrompt);
|
|
@@ -185553,7 +200974,8 @@
|
|
** parser accepts */
|
|
/*********** Begin %parse_accept code *****************************************/
|
|
/*********** End %parse_accept code *******************************************/
|
|
- sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3Fts5ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
|
|
+ sqlite3Fts5ParserCTX_STORE
|
|
}
|
|
|
|
/* The main parser program.
|
|
@@ -185582,7 +201004,7 @@
|
|
sqlite3Fts5ParserARG_PDECL /* Optional %extra_argument parameter */
|
|
){
|
|
fts5YYMINORTYPE fts5yyminorunion;
|
|
- unsigned int fts5yyact; /* The parser action. */
|
|
+ fts5YYACTIONTYPE fts5yyact; /* The parser action. */
|
|
#if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY)
|
|
int fts5yyendofinput; /* True if we are at the end of input */
|
|
#endif
|
|
@@ -185589,31 +201011,44 @@
|
|
#ifdef fts5YYERRORSYMBOL
|
|
int fts5yyerrorhit = 0; /* True if fts5yymajor has invoked an error */
|
|
#endif
|
|
- fts5yyParser *fts5yypParser; /* The parser */
|
|
+ fts5yyParser *fts5yypParser = (fts5yyParser*)fts5yyp; /* The parser */
|
|
+ sqlite3Fts5ParserCTX_FETCH
|
|
+ sqlite3Fts5ParserARG_STORE
|
|
|
|
- fts5yypParser = (fts5yyParser*)fts5yyp;
|
|
assert( fts5yypParser->fts5yytos!=0 );
|
|
#if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY)
|
|
fts5yyendofinput = (fts5yymajor==0);
|
|
#endif
|
|
- sqlite3Fts5ParserARG_STORE;
|
|
|
|
+ fts5yyact = fts5yypParser->fts5yytos->stateno;
|
|
#ifndef NDEBUG
|
|
if( fts5yyTraceFILE ){
|
|
- fprintf(fts5yyTraceFILE,"%sInput '%s'\n",fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
|
|
+ if( fts5yyact < fts5YY_MIN_REDUCE ){
|
|
+ fprintf(fts5yyTraceFILE,"%sInput '%s' in state %d\n",
|
|
+ fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact);
|
|
+ }else{
|
|
+ fprintf(fts5yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
|
|
+ fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact-fts5YY_MIN_REDUCE);
|
|
+ }
|
|
}
|
|
#endif
|
|
|
|
do{
|
|
- fts5yyact = fts5yy_find_shift_action(fts5yypParser,(fts5YYCODETYPE)fts5yymajor);
|
|
- if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
|
|
- fts5yy_shift(fts5yypParser,fts5yyact,fts5yymajor,fts5yyminor);
|
|
+ assert( fts5yyact==fts5yypParser->fts5yytos->stateno );
|
|
+ fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact);
|
|
+ if( fts5yyact >= fts5YY_MIN_REDUCE ){
|
|
+ fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE,fts5yymajor,
|
|
+ fts5yyminor sqlite3Fts5ParserCTX_PARAM);
|
|
+ }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
|
|
+ fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor);
|
|
#ifndef fts5YYNOERRORRECOVERY
|
|
fts5yypParser->fts5yyerrcnt--;
|
|
#endif
|
|
- fts5yymajor = fts5YYNOCODE;
|
|
- }else if( fts5yyact <= fts5YY_MAX_REDUCE ){
|
|
- fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE);
|
|
+ break;
|
|
+ }else if( fts5yyact==fts5YY_ACCEPT_ACTION ){
|
|
+ fts5yypParser->fts5yytos--;
|
|
+ fts5yy_accept(fts5yypParser);
|
|
+ return;
|
|
}else{
|
|
assert( fts5yyact == fts5YY_ERROR_ACTION );
|
|
fts5yyminorunion.fts5yy0 = fts5yyminor;
|
|
@@ -185660,10 +201095,9 @@
|
|
fts5yymajor = fts5YYNOCODE;
|
|
}else{
|
|
while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack
|
|
- && fts5yymx != fts5YYERRORSYMBOL
|
|
&& (fts5yyact = fts5yy_find_reduce_action(
|
|
fts5yypParser->fts5yytos->stateno,
|
|
- fts5YYERRORSYMBOL)) >= fts5YY_MIN_REDUCE
|
|
+ fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE
|
|
){
|
|
fts5yy_pop_parser_stack(fts5yypParser);
|
|
}
|
|
@@ -185680,6 +201114,8 @@
|
|
}
|
|
fts5yypParser->fts5yyerrcnt = 3;
|
|
fts5yyerrorhit = 1;
|
|
+ if( fts5yymajor==fts5YYNOCODE ) break;
|
|
+ fts5yyact = fts5yypParser->fts5yytos->stateno;
|
|
#elif defined(fts5YYNOERRORRECOVERY)
|
|
/* If the fts5YYNOERRORRECOVERY macro is defined, then do not attempt to
|
|
** do any kind of error recovery. Instead, simply invoke the syntax
|
|
@@ -185690,8 +201126,7 @@
|
|
*/
|
|
fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor);
|
|
fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
|
|
- fts5yymajor = fts5YYNOCODE;
|
|
-
|
|
+ break;
|
|
#else /* fts5YYERRORSYMBOL is not defined */
|
|
/* This is what we do if the grammar does not define ERROR:
|
|
**
|
|
@@ -185713,10 +201148,10 @@
|
|
fts5yypParser->fts5yyerrcnt = -1;
|
|
#endif
|
|
}
|
|
- fts5yymajor = fts5YYNOCODE;
|
|
+ break;
|
|
#endif
|
|
}
|
|
- }while( fts5yymajor!=fts5YYNOCODE && fts5yypParser->fts5yytos>fts5yypParser->fts5yystack );
|
|
+ }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack );
|
|
#ifndef NDEBUG
|
|
if( fts5yyTraceFILE ){
|
|
fts5yyStackEntry *i;
|
|
@@ -185733,6 +201168,21 @@
|
|
}
|
|
|
|
/*
|
|
+** Return the fallback token corresponding to canonical token iToken, or
|
|
+** 0 if iToken has no fallback.
|
|
+*/
|
|
+static int sqlite3Fts5ParserFallback(int iToken){
|
|
+#ifdef fts5YYFALLBACK
|
|
+ if( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) ){
|
|
+ return fts5yyFallback[iToken];
|
|
+ }
|
|
+#else
|
|
+ (void)iToken;
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
** 2014 May 31
|
|
**
|
|
** The author disclaims copyright to this source code. In place of
|
|
@@ -186093,6 +201543,16 @@
|
|
}
|
|
|
|
/*
|
|
+** Return the value in pVal interpreted as utf-8 text. Except, if pVal
|
|
+** contains a NULL value, return a pointer to a static string zero
|
|
+** bytes in length instead of a NULL pointer.
|
|
+*/
|
|
+static const char *fts5ValueToText(sqlite3_value *pVal){
|
|
+ const char *zRet = (const char*)sqlite3_value_text(pVal);
|
|
+ return zRet ? zRet : "";
|
|
+}
|
|
+
|
|
+/*
|
|
** Implementation of snippet() function.
|
|
*/
|
|
static void fts5SnippetFunction(
|
|
@@ -186127,9 +201587,9 @@
|
|
nCol = pApi->xColumnCount(pFts);
|
|
memset(&ctx, 0, sizeof(HighlightContext));
|
|
iCol = sqlite3_value_int(apVal[0]);
|
|
- ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
|
|
- ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
|
|
- zEllips = (const char*)sqlite3_value_text(apVal[3]);
|
|
+ ctx.zOpen = fts5ValueToText(apVal[1]);
|
|
+ ctx.zClose = fts5ValueToText(apVal[2]);
|
|
+ zEllips = fts5ValueToText(apVal[3]);
|
|
nToken = sqlite3_value_int(apVal[4]);
|
|
|
|
iBestCol = (iCol>=0 ? iCol : 0);
|
|
@@ -187832,6 +203292,7 @@
|
|
/* #include <stdio.h> */
|
|
static void sqlite3Fts5ParserTrace(FILE*, char*);
|
|
#endif
|
|
+static int sqlite3Fts5ParserFallback(int);
|
|
|
|
|
|
struct Fts5Expr {
|
|
@@ -187883,7 +203344,8 @@
|
|
** or term prefix.
|
|
*/
|
|
struct Fts5ExprTerm {
|
|
- int bPrefix; /* True for a prefix term */
|
|
+ u8 bPrefix; /* True for a prefix term */
|
|
+ u8 bFirst; /* True if token must be first in column */
|
|
char *zTerm; /* nul-terminated term */
|
|
Fts5IndexIter *pIter; /* Iterator for this term */
|
|
Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
|
|
@@ -187964,6 +203426,7 @@
|
|
case '+': tok = FTS5_PLUS; break;
|
|
case '*': tok = FTS5_STAR; break;
|
|
case '-': tok = FTS5_MINUS; break;
|
|
+ case '^': tok = FTS5_CARET; break;
|
|
case '\0': tok = FTS5_EOF; break;
|
|
|
|
case '"': {
|
|
@@ -188223,6 +203686,7 @@
|
|
Fts5PoslistReader *aIter = aStatic;
|
|
int i;
|
|
int rc = SQLITE_OK;
|
|
+ int bFirst = pPhrase->aTerm[0].bFirst;
|
|
|
|
fts5BufferZero(&pPhrase->poslist);
|
|
|
|
@@ -188277,8 +203741,10 @@
|
|
}while( bMatch==0 );
|
|
|
|
/* Append position iPos to the output */
|
|
- rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos);
|
|
- if( rc!=SQLITE_OK ) goto ismatch_out;
|
|
+ if( bFirst==0 || FTS5_POS2OFFSET(iPos)==0 ){
|
|
+ rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos);
|
|
+ if( rc!=SQLITE_OK ) goto ismatch_out;
|
|
+ }
|
|
|
|
for(i=0; i<pPhrase->nTerm; i++){
|
|
if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out;
|
|
@@ -188532,7 +203998,9 @@
|
|
** phrase is not a match, break out of the loop early. */
|
|
for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
|
|
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
|
|
- if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){
|
|
+ if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym
|
|
+ || pNear->pColset || pPhrase->aTerm[0].bFirst
|
|
+ ){
|
|
int bMatch = 0;
|
|
rc = fts5ExprPhraseIsMatch(pNode, pPhrase, &bMatch);
|
|
if( bMatch==0 ) break;
|
|
@@ -188713,6 +204181,7 @@
|
|
assert( pNear->nPhrase>1
|
|
|| pNear->apPhrase[0]->nTerm>1
|
|
|| pNear->apPhrase[0]->aTerm[0].pSynonym
|
|
+ || pNear->apPhrase[0]->aTerm[0].bFirst
|
|
);
|
|
|
|
/* Initialize iLast, the "lastest" rowid any iterator points to. If the
|
|
@@ -189238,6 +204707,16 @@
|
|
}
|
|
|
|
/*
|
|
+** Set the "bFirst" flag on the first token of the phrase passed as the
|
|
+** only argument.
|
|
+*/
|
|
+static void sqlite3Fts5ParseSetCaret(Fts5ExprPhrase *pPhrase){
|
|
+ if( pPhrase && pPhrase->nTerm ){
|
|
+ pPhrase->aTerm[0].bFirst = 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
** If argument pNear is NULL, then a new Fts5ExprNearset object is allocated
|
|
** and populated with pPhrase. Or, if pNear is not NULL, phrase pPhrase is
|
|
** appended to it and the results returned.
|
|
@@ -189454,7 +204933,7 @@
|
|
** no token characters at all. (e.g ... MATCH '""'). */
|
|
sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
|
|
}else if( sCtx.pPhrase->nTerm ){
|
|
- sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix;
|
|
+ sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
|
|
}
|
|
pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
|
|
}
|
|
@@ -189515,6 +204994,7 @@
|
|
}
|
|
if( rc==SQLITE_OK ){
|
|
sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
|
|
+ sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
|
|
}
|
|
}
|
|
}else{
|
|
@@ -189533,7 +205013,10 @@
|
|
pNew->pRoot->pNear->nPhrase = 1;
|
|
sCtx.pPhrase->pNode = pNew->pRoot;
|
|
|
|
- if( pOrig->nTerm==1 && pOrig->aTerm[0].pSynonym==0 ){
|
|
+ if( pOrig->nTerm==1
|
|
+ && pOrig->aTerm[0].pSynonym==0
|
|
+ && pOrig->aTerm[0].bFirst==0
|
|
+ ){
|
|
pNew->pRoot->eType = FTS5_TERM;
|
|
pNew->pRoot->xNext = fts5ExprNodeNext_TERM;
|
|
}else{
|
|
@@ -189807,6 +205290,7 @@
|
|
Fts5ExprNearset *pNear = pNode->pNear;
|
|
if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1
|
|
&& pNear->apPhrase[0]->aTerm[0].pSynonym==0
|
|
+ && pNear->apPhrase[0]->aTerm[0].bFirst==0
|
|
){
|
|
pNode->eType = FTS5_TERM;
|
|
pNode->xNext = fts5ExprNodeNext_TERM;
|
|
@@ -189893,20 +205377,23 @@
|
|
}
|
|
}
|
|
|
|
- if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL
|
|
- && (pNear->nPhrase!=1 || pNear->apPhrase[0]->nTerm>1)
|
|
- ){
|
|
- assert( pParse->rc==SQLITE_OK );
|
|
- pParse->rc = SQLITE_ERROR;
|
|
- assert( pParse->zErr==0 );
|
|
- pParse->zErr = sqlite3_mprintf(
|
|
- "fts5: %s queries are not supported (detail!=full)",
|
|
- pNear->nPhrase==1 ? "phrase": "NEAR"
|
|
- );
|
|
- sqlite3_free(pRet);
|
|
- pRet = 0;
|
|
+ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
|
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
|
|
+ if( pNear->nPhrase!=1
|
|
+ || pPhrase->nTerm>1
|
|
+ || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
|
|
+ ){
|
|
+ assert( pParse->rc==SQLITE_OK );
|
|
+ pParse->rc = SQLITE_ERROR;
|
|
+ assert( pParse->zErr==0 );
|
|
+ pParse->zErr = sqlite3_mprintf(
|
|
+ "fts5: %s queries are not supported (detail!=full)",
|
|
+ pNear->nPhrase==1 ? "phrase": "NEAR"
|
|
+ );
|
|
+ sqlite3_free(pRet);
|
|
+ pRet = 0;
|
|
+ }
|
|
}
|
|
-
|
|
}else{
|
|
fts5ExprAddChildren(pRet, pLeft);
|
|
fts5ExprAddChildren(pRet, pRight);
|
|
@@ -190310,6 +205797,7 @@
|
|
sqlite3_value **apVal /* Function arguments */
|
|
){
|
|
int iCode;
|
|
+ u8 aArr[32];
|
|
if( nArg!=1 ){
|
|
sqlite3_result_error(pCtx,
|
|
"wrong number of arguments to function fts5_isalnum", -1
|
|
@@ -190316,8 +205804,12 @@
|
|
);
|
|
return;
|
|
}
|
|
+ memset(aArr, 0, sizeof(aArr));
|
|
+ sqlite3Fts5UnicodeCatParse("L*", aArr);
|
|
+ sqlite3Fts5UnicodeCatParse("N*", aArr);
|
|
+ sqlite3Fts5UnicodeCatParse("Co", aArr);
|
|
iCode = sqlite3_value_int(apVal[0]);
|
|
- sqlite3_result_int(pCtx, sqlite3Fts5UnicodeIsalnum(iCode));
|
|
+ sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory(iCode)]);
|
|
}
|
|
|
|
static void fts5ExprFold(
|
|
@@ -190361,10 +205853,12 @@
|
|
rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0);
|
|
}
|
|
|
|
- /* Avoid a warning indicating that sqlite3Fts5ParserTrace() is unused */
|
|
+ /* Avoid warnings indicating that sqlite3Fts5ParserTrace() and
|
|
+ ** sqlite3Fts5ParserFallback() are unused */
|
|
#ifndef NDEBUG
|
|
(void)sqlite3Fts5ParserTrace;
|
|
#endif
|
|
+ (void)sqlite3Fts5ParserFallback;
|
|
|
|
return rc;
|
|
}
|
|
@@ -191909,6 +207403,7 @@
|
|
sqlite3_bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC);
|
|
sqlite3_step(p->pWriter);
|
|
p->rc = sqlite3_reset(p->pWriter);
|
|
+ sqlite3_bind_null(p->pWriter, 2);
|
|
}
|
|
|
|
/*
|
|
@@ -193537,6 +209032,7 @@
|
|
bDlidx = (val & 0x0001);
|
|
}
|
|
p->rc = sqlite3_reset(pIdxSelect);
|
|
+ sqlite3_bind_null(pIdxSelect, 2);
|
|
|
|
if( iPg<pSeg->pgnoFirst ){
|
|
iPg = pSeg->pgnoFirst;
|
|
@@ -194749,6 +210245,7 @@
|
|
sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC);
|
|
assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW );
|
|
p->rc = sqlite3_reset(pIdxSelect);
|
|
+ sqlite3_bind_null(pIdxSelect, 2);
|
|
}
|
|
}
|
|
#endif
|
|
@@ -194875,6 +210372,7 @@
|
|
sqlite3_bind_int64(p->pIdxWriter, 3, bFlag + ((i64)pWriter->iBtPage<<1));
|
|
sqlite3_step(p->pIdxWriter);
|
|
p->rc = sqlite3_reset(p->pIdxWriter);
|
|
+ sqlite3_bind_null(p->pIdxWriter, 2);
|
|
}
|
|
pWriter->iBtPage = 0;
|
|
}
|
|
@@ -196060,7 +211558,13 @@
|
|
Fts5Buffer out = {0, 0, 0};
|
|
Fts5Buffer tmp = {0, 0, 0};
|
|
|
|
- if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n) ) return;
|
|
+ /* The maximum size of the output is equal to the sum of the two
|
|
+ ** input sizes + 1 varint (9 bytes). The extra varint is because if the
|
|
+ ** first rowid in one input is a large negative number, and the first in
|
|
+ ** the other a non-negative number, the delta for the non-negative
|
|
+ ** number will be larger on disk than the literal integer value
|
|
+ ** was. */
|
|
+ if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9) ) return;
|
|
fts5DoclistIterInit(p1, &i1);
|
|
fts5DoclistIterInit(p2, &i2);
|
|
|
|
@@ -196154,6 +211658,7 @@
|
|
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
|
|
fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
|
|
}
|
|
+ assert( out.n<=(p1->n+p2->n+9) );
|
|
|
|
fts5BufferSet(&p->rc, p1, out.n, out.p);
|
|
fts5BufferFree(&tmp);
|
|
@@ -196401,7 +211906,10 @@
|
|
for(i=0; i<nChar; i++){
|
|
if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */
|
|
if( (unsigned char)p[n++]>=0xc0 ){
|
|
- while( (p[n] & 0xc0)==0x80 ) n++;
|
|
+ while( (p[n] & 0xc0)==0x80 ){
|
|
+ n++;
|
|
+ if( n>=nByte ) break;
|
|
+ }
|
|
}
|
|
}
|
|
return n;
|
|
@@ -196539,7 +212047,7 @@
|
|
fts5CloseReader(p);
|
|
}
|
|
|
|
- *ppIter = &pRet->base;
|
|
+ *ppIter = (Fts5IndexIter*)pRet;
|
|
sqlite3Fts5BufferFree(&buf);
|
|
}
|
|
return fts5IndexReturn(p);
|
|
@@ -197926,7 +213434,7 @@
|
|
case FTS5_SAVEPOINT:
|
|
assert( p->ts.eState==1 );
|
|
assert( iSavepoint>=0 );
|
|
- assert( iSavepoint>p->ts.iSavepoint );
|
|
+ assert( iSavepoint>=p->ts.iSavepoint );
|
|
p->ts.iSavepoint = iSavepoint;
|
|
break;
|
|
|
|
@@ -198181,6 +213689,12 @@
|
|
aColMap[1] = nCol;
|
|
aColMap[2] = nCol+1;
|
|
|
|
+ assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
+ assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
+ assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
+ assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
+ assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
|
|
+
|
|
/* Set idxFlags flags for all WHERE clause terms that will be used. */
|
|
for(i=0; i<pInfo->nConstraint; i++){
|
|
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
|
|
@@ -198199,11 +213713,11 @@
|
|
pInfo->estimatedCost = 1e50;
|
|
return SQLITE_OK;
|
|
}
|
|
- }else{
|
|
+ }else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){
|
|
int j;
|
|
for(j=1; j<ArraySize(aConstraint); j++){
|
|
struct Constraint *pC = &aConstraint[j];
|
|
- if( iCol==aColMap[pC->iCol] && p->op & pC->op && p->usable ){
|
|
+ if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){
|
|
pC->iConsIndex = i;
|
|
idxFlags |= pC->fts5op;
|
|
}
|
|
@@ -198845,6 +214359,13 @@
|
|
assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
|
|
assert( pCsr->iLastRowid==LARGEST_INT64 );
|
|
assert( pCsr->iFirstRowid==SMALLEST_INT64 );
|
|
+ if( pTab->pSortCsr->bDesc ){
|
|
+ pCsr->iLastRowid = pTab->pSortCsr->iFirstRowid;
|
|
+ pCsr->iFirstRowid = pTab->pSortCsr->iLastRowid;
|
|
+ }else{
|
|
+ pCsr->iLastRowid = pTab->pSortCsr->iLastRowid;
|
|
+ pCsr->iFirstRowid = pTab->pSortCsr->iFirstRowid;
|
|
+ }
|
|
pCsr->ePlan = FTS5_PLAN_SOURCE;
|
|
pCsr->pExpr = pTab->pSortCsr->pExpr;
|
|
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
|
@@ -200275,12 +215796,27 @@
|
|
){
|
|
assert( nArg==0 );
|
|
UNUSED_PARAM2(nArg, apUnused);
|
|
- sqlite3_result_text(pCtx, "fts5: 2017-08-01 13:24:15 9501e22dfeebdcefa783575e47c60b514d7c2e0cad73b2a496c0bc4b680900a8", -1, SQLITE_TRANSIENT);
|
|
+ sqlite3_result_text(pCtx, "fts5: 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9", -1, SQLITE_TRANSIENT);
|
|
}
|
|
|
|
+/*
|
|
+** Return true if zName is the extension on one of the shadow tables used
|
|
+** by this module.
|
|
+*/
|
|
+static int fts5ShadowName(const char *zName){
|
|
+ static const char *azName[] = {
|
|
+ "config", "content", "data", "docsize", "idx"
|
|
+ };
|
|
+ unsigned int i;
|
|
+ for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
|
|
+ if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int fts5Init(sqlite3 *db){
|
|
static const sqlite3_module fts5Mod = {
|
|
- /* iVersion */ 2,
|
|
+ /* iVersion */ 3,
|
|
/* xCreate */ fts5CreateMethod,
|
|
/* xConnect */ fts5ConnectMethod,
|
|
/* xBestIndex */ fts5BestIndexMethod,
|
|
@@ -200303,6 +215839,7 @@
|
|
/* xSavepoint */ fts5SavepointMethod,
|
|
/* xRelease */ fts5ReleaseMethod,
|
|
/* xRollbackTo */ fts5RollbackToMethod,
|
|
+ /* xShadowName */ fts5ShadowName
|
|
};
|
|
|
|
int rc;
|
|
@@ -200851,6 +216388,7 @@
|
|
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
|
sqlite3_step(pReplace);
|
|
rc = sqlite3_reset(pReplace);
|
|
+ sqlite3_bind_null(pReplace, 2);
|
|
}
|
|
}
|
|
return rc;
|
|
@@ -201511,6 +217049,7 @@
|
|
}
|
|
sqlite3_step(pReplace);
|
|
rc = sqlite3_reset(pReplace);
|
|
+ sqlite3_bind_null(pReplace, 1);
|
|
}
|
|
if( rc==SQLITE_OK && pVal ){
|
|
int iNew = p->pConfig->iCookie + 1;
|
|
@@ -201761,6 +217300,8 @@
|
|
int bRemoveDiacritic; /* True if remove_diacritics=1 is set */
|
|
int nException;
|
|
int *aiException;
|
|
+
|
|
+ unsigned char aCategory[32]; /* True for token char categories */
|
|
};
|
|
|
|
static int fts5UnicodeAddExceptions(
|
|
@@ -201785,7 +217326,7 @@
|
|
if( iCode<128 ){
|
|
p->aTokenChar[iCode] = (unsigned char)bTokenChars;
|
|
}else{
|
|
- bToken = sqlite3Fts5UnicodeIsalnum(iCode);
|
|
+ bToken = p->aCategory[sqlite3Fts5UnicodeCategory(iCode)];
|
|
assert( (bToken==0 || bToken==1) );
|
|
assert( (bTokenChars==0 || bTokenChars==1) );
|
|
if( bToken!=bTokenChars && sqlite3Fts5UnicodeIsdiacritic(iCode)==0 ){
|
|
@@ -201846,6 +217387,21 @@
|
|
return;
|
|
}
|
|
|
|
+static int unicodeSetCategories(Unicode61Tokenizer *p, const char *zCat){
|
|
+ const char *z = zCat;
|
|
+
|
|
+ while( *z ){
|
|
+ while( *z==' ' || *z=='\t' ) z++;
|
|
+ if( *z && sqlite3Fts5UnicodeCatParse(z, p->aCategory) ){
|
|
+ return SQLITE_ERROR;
|
|
+ }
|
|
+ while( *z!=' ' && *z!='\t' && *z!='\0' ) z++;
|
|
+ }
|
|
+
|
|
+ sqlite3Fts5UnicodeAscii(p->aCategory, p->aTokenChar);
|
|
+ return SQLITE_OK;
|
|
+}
|
|
+
|
|
/*
|
|
** Create a "unicode61" tokenizer.
|
|
*/
|
|
@@ -201864,9 +217420,10 @@
|
|
}else{
|
|
p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
|
|
if( p ){
|
|
+ const char *zCat = "L* N* Co";
|
|
int i;
|
|
memset(p, 0, sizeof(Unicode61Tokenizer));
|
|
- memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
|
|
+
|
|
p->bRemoveDiacritic = 1;
|
|
p->nFold = 64;
|
|
p->aFold = sqlite3_malloc(p->nFold * sizeof(char));
|
|
@@ -201873,7 +217430,19 @@
|
|
if( p->aFold==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}
|
|
+
|
|
+ /* Search for a "categories" argument */
|
|
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
|
+ if( 0==sqlite3_stricmp(azArg[i], "categories") ){
|
|
+ zCat = azArg[i+1];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = unicodeSetCategories(p, zCat);
|
|
+ }
|
|
+
|
|
+ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
|
const char *zArg = azArg[i+1];
|
|
if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
|
|
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
|
|
@@ -201886,10 +217455,14 @@
|
|
}else
|
|
if( 0==sqlite3_stricmp(azArg[i], "separators") ){
|
|
rc = fts5UnicodeAddExceptions(p, zArg, 0);
|
|
+ }else
|
|
+ if( 0==sqlite3_stricmp(azArg[i], "categories") ){
|
|
+ /* no-op */
|
|
}else{
|
|
rc = SQLITE_ERROR;
|
|
}
|
|
}
|
|
+
|
|
}else{
|
|
rc = SQLITE_NOMEM;
|
|
}
|
|
@@ -201908,8 +217481,10 @@
|
|
** character (not a separator).
|
|
*/
|
|
static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){
|
|
- assert( (sqlite3Fts5UnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
|
|
- return sqlite3Fts5UnicodeIsalnum(iCode) ^ fts5UnicodeIsException(p, iCode);
|
|
+ return (
|
|
+ p->aCategory[sqlite3Fts5UnicodeCategory(iCode)]
|
|
+ ^ fts5UnicodeIsException(p, iCode)
|
|
+ );
|
|
}
|
|
|
|
static int fts5UnicodeTokenize(
|
|
@@ -202785,137 +218360,8 @@
|
|
|
|
/* #include <assert.h> */
|
|
|
|
-/*
|
|
-** Return true if the argument corresponds to a unicode codepoint
|
|
-** classified as either a letter or a number. Otherwise false.
|
|
-**
|
|
-** The results are undefined if the value passed to this function
|
|
-** is less than zero.
|
|
-*/
|
|
-static int sqlite3Fts5UnicodeIsalnum(int c){
|
|
- /* Each unsigned integer in the following array corresponds to a contiguous
|
|
- ** range of unicode codepoints that are not either letters or numbers (i.e.
|
|
- ** codepoints for which this function should return 0).
|
|
- **
|
|
- ** The most significant 22 bits in each 32-bit value contain the first
|
|
- ** codepoint in the range. The least significant 10 bits are used to store
|
|
- ** the size of the range (always at least 1). In other words, the value
|
|
- ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
|
|
- ** C. It is not possible to represent a range larger than 1023 codepoints
|
|
- ** using this format.
|
|
- */
|
|
- static const unsigned int aEntry[] = {
|
|
- 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
|
|
- 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
|
|
- 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
|
|
- 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
|
|
- 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
|
|
- 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
|
|
- 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
|
|
- 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
|
|
- 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
|
|
- 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
|
|
- 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
|
|
- 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
|
|
- 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
|
|
- 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
|
|
- 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
|
|
- 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
|
|
- 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
|
|
- 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
|
|
- 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
|
|
- 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
|
|
- 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
|
|
- 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
|
|
- 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
|
|
- 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
|
|
- 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
|
|
- 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
|
|
- 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
|
|
- 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
|
|
- 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
|
|
- 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
|
|
- 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
|
|
- 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
|
|
- 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
|
|
- 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
|
|
- 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
|
|
- 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
|
|
- 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
|
|
- 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
|
|
- 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
|
|
- 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
|
|
- 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
|
|
- 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
|
|
- 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
|
|
- 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
|
|
- 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
|
|
- 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
|
|
- 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
|
|
- 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
|
|
- 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
|
|
- 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
|
|
- 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
|
|
- 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
|
|
- 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
|
|
- 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
|
|
- 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
|
|
- 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
|
|
- 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
|
|
- 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
|
|
- 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
|
|
- 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
|
|
- 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
|
|
- 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
|
|
- 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
|
|
- 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
|
|
- 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
|
|
- 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
|
|
- 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
|
|
- 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
|
|
- 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
|
|
- 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
|
|
- 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
|
|
- 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
|
|
- 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
|
|
- 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
|
|
- 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
|
|
- 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
|
|
- 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
|
|
- 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
|
|
- 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
|
|
- 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
|
|
- 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
|
|
- 0x380400F0,
|
|
- };
|
|
- static const unsigned int aAscii[4] = {
|
|
- 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
|
|
- };
|
|
|
|
- if( (unsigned int)c<128 ){
|
|
- return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
|
|
- }else if( (unsigned int)c<(1<<22) ){
|
|
- unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
|
|
- int iRes = 0;
|
|
- int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
|
|
- int iLo = 0;
|
|
- while( iHi>=iLo ){
|
|
- int iTest = (iHi + iLo) / 2;
|
|
- if( key >= aEntry[iTest] ){
|
|
- iRes = iTest;
|
|
- iLo = iTest+1;
|
|
- }else{
|
|
- iHi = iTest-1;
|
|
- }
|
|
- }
|
|
- assert( aEntry[0]<key );
|
|
- assert( key>=aEntry[iRes] );
|
|
- return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
|
|
- }
|
|
- return 1;
|
|
-}
|
|
|
|
-
|
|
/*
|
|
** If the argument is a codepoint corresponding to a lowercase letter
|
|
** in the ASCII range with a diacritic added, return the codepoint
|
|
@@ -203126,6 +218572,539 @@
|
|
return ret;
|
|
}
|
|
|
|
+
|
|
+#if 0
|
|
+static int sqlite3Fts5UnicodeNCat(void) {
|
|
+ return 32;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
|
|
+ aArray[0] = 1;
|
|
+ switch( zCat[0] ){
|
|
+ case 'C':
|
|
+ switch( zCat[1] ){
|
|
+ case 'c': aArray[1] = 1; break;
|
|
+ case 'f': aArray[2] = 1; break;
|
|
+ case 'n': aArray[3] = 1; break;
|
|
+ case 's': aArray[4] = 1; break;
|
|
+ case 'o': aArray[31] = 1; break;
|
|
+ case '*':
|
|
+ aArray[1] = 1;
|
|
+ aArray[2] = 1;
|
|
+ aArray[3] = 1;
|
|
+ aArray[4] = 1;
|
|
+ aArray[31] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ case 'L':
|
|
+ switch( zCat[1] ){
|
|
+ case 'l': aArray[5] = 1; break;
|
|
+ case 'm': aArray[6] = 1; break;
|
|
+ case 'o': aArray[7] = 1; break;
|
|
+ case 't': aArray[8] = 1; break;
|
|
+ case 'u': aArray[9] = 1; break;
|
|
+ case 'C': aArray[30] = 1; break;
|
|
+ case '*':
|
|
+ aArray[5] = 1;
|
|
+ aArray[6] = 1;
|
|
+ aArray[7] = 1;
|
|
+ aArray[8] = 1;
|
|
+ aArray[9] = 1;
|
|
+ aArray[30] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ case 'M':
|
|
+ switch( zCat[1] ){
|
|
+ case 'c': aArray[10] = 1; break;
|
|
+ case 'e': aArray[11] = 1; break;
|
|
+ case 'n': aArray[12] = 1; break;
|
|
+ case '*':
|
|
+ aArray[10] = 1;
|
|
+ aArray[11] = 1;
|
|
+ aArray[12] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ case 'N':
|
|
+ switch( zCat[1] ){
|
|
+ case 'd': aArray[13] = 1; break;
|
|
+ case 'l': aArray[14] = 1; break;
|
|
+ case 'o': aArray[15] = 1; break;
|
|
+ case '*':
|
|
+ aArray[13] = 1;
|
|
+ aArray[14] = 1;
|
|
+ aArray[15] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ case 'P':
|
|
+ switch( zCat[1] ){
|
|
+ case 'c': aArray[16] = 1; break;
|
|
+ case 'd': aArray[17] = 1; break;
|
|
+ case 'e': aArray[18] = 1; break;
|
|
+ case 'f': aArray[19] = 1; break;
|
|
+ case 'i': aArray[20] = 1; break;
|
|
+ case 'o': aArray[21] = 1; break;
|
|
+ case 's': aArray[22] = 1; break;
|
|
+ case '*':
|
|
+ aArray[16] = 1;
|
|
+ aArray[17] = 1;
|
|
+ aArray[18] = 1;
|
|
+ aArray[19] = 1;
|
|
+ aArray[20] = 1;
|
|
+ aArray[21] = 1;
|
|
+ aArray[22] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ case 'S':
|
|
+ switch( zCat[1] ){
|
|
+ case 'c': aArray[23] = 1; break;
|
|
+ case 'k': aArray[24] = 1; break;
|
|
+ case 'm': aArray[25] = 1; break;
|
|
+ case 'o': aArray[26] = 1; break;
|
|
+ case '*':
|
|
+ aArray[23] = 1;
|
|
+ aArray[24] = 1;
|
|
+ aArray[25] = 1;
|
|
+ aArray[26] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ case 'Z':
|
|
+ switch( zCat[1] ){
|
|
+ case 'l': aArray[27] = 1; break;
|
|
+ case 'p': aArray[28] = 1; break;
|
|
+ case 's': aArray[29] = 1; break;
|
|
+ case '*':
|
|
+ aArray[27] = 1;
|
|
+ aArray[28] = 1;
|
|
+ aArray[29] = 1;
|
|
+ break;
|
|
+ default: return 1; }
|
|
+ break;
|
|
+
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u16 aFts5UnicodeBlock[] = {
|
|
+ 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760,
|
|
+ 1760, 1760, 1760, 1760, 1760, 1763, 1765,
|
|
+ };
|
|
+static u16 aFts5UnicodeMap[] = {
|
|
+ 0, 32, 33, 36, 37, 40, 41, 42, 43, 44,
|
|
+ 45, 46, 48, 58, 60, 63, 65, 91, 92, 93,
|
|
+ 94, 95, 96, 97, 123, 124, 125, 126, 127, 160,
|
|
+ 161, 162, 166, 167, 168, 169, 170, 171, 172, 173,
|
|
+ 174, 175, 176, 177, 178, 180, 181, 182, 184, 185,
|
|
+ 186, 187, 188, 191, 192, 215, 216, 223, 247, 248,
|
|
+ 256, 312, 313, 329, 330, 377, 383, 385, 387, 388,
|
|
+ 391, 394, 396, 398, 402, 403, 405, 406, 409, 412,
|
|
+ 414, 415, 417, 418, 423, 427, 428, 431, 434, 436,
|
|
+ 437, 440, 442, 443, 444, 446, 448, 452, 453, 454,
|
|
+ 455, 456, 457, 458, 459, 460, 461, 477, 478, 496,
|
|
+ 497, 498, 499, 500, 503, 505, 506, 564, 570, 572,
|
|
+ 573, 575, 577, 580, 583, 584, 592, 660, 661, 688,
|
|
+ 706, 710, 722, 736, 741, 748, 749, 750, 751, 768,
|
|
+ 880, 884, 885, 886, 890, 891, 894, 900, 902, 903,
|
|
+ 904, 908, 910, 912, 913, 931, 940, 975, 977, 978,
|
|
+ 981, 984, 1008, 1012, 1014, 1015, 1018, 1020, 1021, 1072,
|
|
+ 1120, 1154, 1155, 1160, 1162, 1217, 1231, 1232, 1329, 1369,
|
|
+ 1370, 1377, 1417, 1418, 1423, 1425, 1470, 1471, 1472, 1473,
|
|
+ 1475, 1476, 1478, 1479, 1488, 1520, 1523, 1536, 1542, 1545,
|
|
+ 1547, 1548, 1550, 1552, 1563, 1566, 1568, 1600, 1601, 1611,
|
|
+ 1632, 1642, 1646, 1648, 1649, 1748, 1749, 1750, 1757, 1758,
|
|
+ 1759, 1765, 1767, 1769, 1770, 1774, 1776, 1786, 1789, 1791,
|
|
+ 1792, 1807, 1808, 1809, 1810, 1840, 1869, 1958, 1969, 1984,
|
|
+ 1994, 2027, 2036, 2038, 2039, 2042, 2048, 2070, 2074, 2075,
|
|
+ 2084, 2085, 2088, 2089, 2096, 2112, 2137, 2142, 2208, 2210,
|
|
+ 2276, 2304, 2307, 2308, 2362, 2363, 2364, 2365, 2366, 2369,
|
|
+ 2377, 2381, 2382, 2384, 2385, 2392, 2402, 2404, 2406, 2416,
|
|
+ 2417, 2418, 2425, 2433, 2434, 2437, 2447, 2451, 2474, 2482,
|
|
+ 2486, 2492, 2493, 2494, 2497, 2503, 2507, 2509, 2510, 2519,
|
|
+ 2524, 2527, 2530, 2534, 2544, 2546, 2548, 2554, 2555, 2561,
|
|
+ 2563, 2565, 2575, 2579, 2602, 2610, 2613, 2616, 2620, 2622,
|
|
+ 2625, 2631, 2635, 2641, 2649, 2654, 2662, 2672, 2674, 2677,
|
|
+ 2689, 2691, 2693, 2703, 2707, 2730, 2738, 2741, 2748, 2749,
|
|
+ 2750, 2753, 2759, 2761, 2763, 2765, 2768, 2784, 2786, 2790,
|
|
+ 2800, 2801, 2817, 2818, 2821, 2831, 2835, 2858, 2866, 2869,
|
|
+ 2876, 2877, 2878, 2879, 2880, 2881, 2887, 2891, 2893, 2902,
|
|
+ 2903, 2908, 2911, 2914, 2918, 2928, 2929, 2930, 2946, 2947,
|
|
+ 2949, 2958, 2962, 2969, 2972, 2974, 2979, 2984, 2990, 3006,
|
|
+ 3008, 3009, 3014, 3018, 3021, 3024, 3031, 3046, 3056, 3059,
|
|
+ 3065, 3066, 3073, 3077, 3086, 3090, 3114, 3125, 3133, 3134,
|
|
+ 3137, 3142, 3146, 3157, 3160, 3168, 3170, 3174, 3192, 3199,
|
|
+ 3202, 3205, 3214, 3218, 3242, 3253, 3260, 3261, 3262, 3263,
|
|
+ 3264, 3270, 3271, 3274, 3276, 3285, 3294, 3296, 3298, 3302,
|
|
+ 3313, 3330, 3333, 3342, 3346, 3389, 3390, 3393, 3398, 3402,
|
|
+ 3405, 3406, 3415, 3424, 3426, 3430, 3440, 3449, 3450, 3458,
|
|
+ 3461, 3482, 3507, 3517, 3520, 3530, 3535, 3538, 3542, 3544,
|
|
+ 3570, 3572, 3585, 3633, 3634, 3636, 3647, 3648, 3654, 3655,
|
|
+ 3663, 3664, 3674, 3713, 3716, 3719, 3722, 3725, 3732, 3737,
|
|
+ 3745, 3749, 3751, 3754, 3757, 3761, 3762, 3764, 3771, 3773,
|
|
+ 3776, 3782, 3784, 3792, 3804, 3840, 3841, 3844, 3859, 3860,
|
|
+ 3861, 3864, 3866, 3872, 3882, 3892, 3893, 3894, 3895, 3896,
|
|
+ 3897, 3898, 3899, 3900, 3901, 3902, 3904, 3913, 3953, 3967,
|
|
+ 3968, 3973, 3974, 3976, 3981, 3993, 4030, 4038, 4039, 4046,
|
|
+ 4048, 4053, 4057, 4096, 4139, 4141, 4145, 4146, 4152, 4153,
|
|
+ 4155, 4157, 4159, 4160, 4170, 4176, 4182, 4184, 4186, 4190,
|
|
+ 4193, 4194, 4197, 4199, 4206, 4209, 4213, 4226, 4227, 4229,
|
|
+ 4231, 4237, 4238, 4239, 4240, 4250, 4253, 4254, 4256, 4295,
|
|
+ 4301, 4304, 4347, 4348, 4349, 4682, 4688, 4696, 4698, 4704,
|
|
+ 4746, 4752, 4786, 4792, 4800, 4802, 4808, 4824, 4882, 4888,
|
|
+ 4957, 4960, 4969, 4992, 5008, 5024, 5120, 5121, 5741, 5743,
|
|
+ 5760, 5761, 5787, 5788, 5792, 5867, 5870, 5888, 5902, 5906,
|
|
+ 5920, 5938, 5941, 5952, 5970, 5984, 5998, 6002, 6016, 6068,
|
|
+ 6070, 6071, 6078, 6086, 6087, 6089, 6100, 6103, 6104, 6107,
|
|
+ 6108, 6109, 6112, 6128, 6144, 6150, 6151, 6155, 6158, 6160,
|
|
+ 6176, 6211, 6212, 6272, 6313, 6314, 6320, 6400, 6432, 6435,
|
|
+ 6439, 6441, 6448, 6450, 6451, 6457, 6464, 6468, 6470, 6480,
|
|
+ 6512, 6528, 6576, 6593, 6600, 6608, 6618, 6622, 6656, 6679,
|
|
+ 6681, 6686, 6688, 6741, 6742, 6743, 6744, 6752, 6753, 6754,
|
|
+ 6755, 6757, 6765, 6771, 6783, 6784, 6800, 6816, 6823, 6824,
|
|
+ 6912, 6916, 6917, 6964, 6965, 6966, 6971, 6972, 6973, 6978,
|
|
+ 6979, 6981, 6992, 7002, 7009, 7019, 7028, 7040, 7042, 7043,
|
|
+ 7073, 7074, 7078, 7080, 7082, 7083, 7084, 7086, 7088, 7098,
|
|
+ 7142, 7143, 7144, 7146, 7149, 7150, 7151, 7154, 7164, 7168,
|
|
+ 7204, 7212, 7220, 7222, 7227, 7232, 7245, 7248, 7258, 7288,
|
|
+ 7294, 7360, 7376, 7379, 7380, 7393, 7394, 7401, 7405, 7406,
|
|
+ 7410, 7412, 7413, 7424, 7468, 7531, 7544, 7545, 7579, 7616,
|
|
+ 7676, 7680, 7830, 7838, 7936, 7944, 7952, 7960, 7968, 7976,
|
|
+ 7984, 7992, 8000, 8008, 8016, 8025, 8027, 8029, 8031, 8033,
|
|
+ 8040, 8048, 8064, 8072, 8080, 8088, 8096, 8104, 8112, 8118,
|
|
+ 8120, 8124, 8125, 8126, 8127, 8130, 8134, 8136, 8140, 8141,
|
|
+ 8144, 8150, 8152, 8157, 8160, 8168, 8173, 8178, 8182, 8184,
|
|
+ 8188, 8189, 8192, 8203, 8208, 8214, 8216, 8217, 8218, 8219,
|
|
+ 8221, 8222, 8223, 8224, 8232, 8233, 8234, 8239, 8240, 8249,
|
|
+ 8250, 8251, 8255, 8257, 8260, 8261, 8262, 8263, 8274, 8275,
|
|
+ 8276, 8277, 8287, 8288, 8298, 8304, 8305, 8308, 8314, 8317,
|
|
+ 8318, 8319, 8320, 8330, 8333, 8334, 8336, 8352, 8400, 8413,
|
|
+ 8417, 8418, 8421, 8448, 8450, 8451, 8455, 8456, 8458, 8459,
|
|
+ 8462, 8464, 8467, 8468, 8469, 8470, 8472, 8473, 8478, 8484,
|
|
+ 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8496, 8500,
|
|
+ 8501, 8505, 8506, 8508, 8510, 8512, 8517, 8519, 8522, 8523,
|
|
+ 8524, 8526, 8527, 8528, 8544, 8579, 8581, 8585, 8592, 8597,
|
|
+ 8602, 8604, 8608, 8609, 8611, 8612, 8614, 8615, 8622, 8623,
|
|
+ 8654, 8656, 8658, 8659, 8660, 8661, 8692, 8960, 8968, 8972,
|
|
+ 8992, 8994, 9001, 9002, 9003, 9084, 9085, 9115, 9140, 9180,
|
|
+ 9186, 9216, 9280, 9312, 9372, 9450, 9472, 9655, 9656, 9665,
|
|
+ 9666, 9720, 9728, 9839, 9840, 9985, 10088, 10089, 10090, 10091,
|
|
+ 10092, 10093, 10094, 10095, 10096, 10097, 10098, 10099, 10100, 10101,
|
|
+ 10102, 10132, 10176, 10181, 10182, 10183, 10214, 10215, 10216, 10217,
|
|
+ 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10240, 10496, 10627,
|
|
+ 10628, 10629, 10630, 10631, 10632, 10633, 10634, 10635, 10636, 10637,
|
|
+ 10638, 10639, 10640, 10641, 10642, 10643, 10644, 10645, 10646, 10647,
|
|
+ 10648, 10649, 10712, 10713, 10714, 10715, 10716, 10748, 10749, 10750,
|
|
+ 11008, 11056, 11077, 11079, 11088, 11264, 11312, 11360, 11363, 11365,
|
|
+ 11367, 11374, 11377, 11378, 11380, 11381, 11383, 11388, 11390, 11393,
|
|
+ 11394, 11492, 11493, 11499, 11503, 11506, 11513, 11517, 11518, 11520,
|
|
+ 11559, 11565, 11568, 11631, 11632, 11647, 11648, 11680, 11688, 11696,
|
|
+ 11704, 11712, 11720, 11728, 11736, 11744, 11776, 11778, 11779, 11780,
|
|
+ 11781, 11782, 11785, 11786, 11787, 11788, 11789, 11790, 11799, 11800,
|
|
+ 11802, 11803, 11804, 11805, 11806, 11808, 11809, 11810, 11811, 11812,
|
|
+ 11813, 11814, 11815, 11816, 11817, 11818, 11823, 11824, 11834, 11904,
|
|
+ 11931, 12032, 12272, 12288, 12289, 12292, 12293, 12294, 12295, 12296,
|
|
+ 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12306,
|
|
+ 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12317,
|
|
+ 12318, 12320, 12321, 12330, 12334, 12336, 12337, 12342, 12344, 12347,
|
|
+ 12348, 12349, 12350, 12353, 12441, 12443, 12445, 12447, 12448, 12449,
|
|
+ 12539, 12540, 12543, 12549, 12593, 12688, 12690, 12694, 12704, 12736,
|
|
+ 12784, 12800, 12832, 12842, 12872, 12880, 12881, 12896, 12928, 12938,
|
|
+ 12977, 12992, 13056, 13312, 19893, 19904, 19968, 40908, 40960, 40981,
|
|
+ 40982, 42128, 42192, 42232, 42238, 42240, 42508, 42509, 42512, 42528,
|
|
+ 42538, 42560, 42606, 42607, 42608, 42611, 42612, 42622, 42623, 42624,
|
|
+ 42655, 42656, 42726, 42736, 42738, 42752, 42775, 42784, 42786, 42800,
|
|
+ 42802, 42864, 42865, 42873, 42878, 42888, 42889, 42891, 42896, 42912,
|
|
+ 43000, 43002, 43003, 43010, 43011, 43014, 43015, 43019, 43020, 43043,
|
|
+ 43045, 43047, 43048, 43056, 43062, 43064, 43065, 43072, 43124, 43136,
|
|
+ 43138, 43188, 43204, 43214, 43216, 43232, 43250, 43256, 43259, 43264,
|
|
+ 43274, 43302, 43310, 43312, 43335, 43346, 43359, 43360, 43392, 43395,
|
|
+ 43396, 43443, 43444, 43446, 43450, 43452, 43453, 43457, 43471, 43472,
|
|
+ 43486, 43520, 43561, 43567, 43569, 43571, 43573, 43584, 43587, 43588,
|
|
+ 43596, 43597, 43600, 43612, 43616, 43632, 43633, 43639, 43642, 43643,
|
|
+ 43648, 43696, 43697, 43698, 43701, 43703, 43705, 43710, 43712, 43713,
|
|
+ 43714, 43739, 43741, 43742, 43744, 43755, 43756, 43758, 43760, 43762,
|
|
+ 43763, 43765, 43766, 43777, 43785, 43793, 43808, 43816, 43968, 44003,
|
|
+ 44005, 44006, 44008, 44009, 44011, 44012, 44013, 44016, 44032, 55203,
|
|
+ 55216, 55243, 55296, 56191, 56319, 57343, 57344, 63743, 63744, 64112,
|
|
+ 64256, 64275, 64285, 64286, 64287, 64297, 64298, 64312, 64318, 64320,
|
|
+ 64323, 64326, 64434, 64467, 64830, 64831, 64848, 64914, 65008, 65020,
|
|
+ 65021, 65024, 65040, 65047, 65048, 65049, 65056, 65072, 65073, 65075,
|
|
+ 65077, 65078, 65079, 65080, 65081, 65082, 65083, 65084, 65085, 65086,
|
|
+ 65087, 65088, 65089, 65090, 65091, 65092, 65093, 65095, 65096, 65097,
|
|
+ 65101, 65104, 65108, 65112, 65113, 65114, 65115, 65116, 65117, 65118,
|
|
+ 65119, 65122, 65123, 65124, 65128, 65129, 65130, 65136, 65142, 65279,
|
|
+ 65281, 65284, 65285, 65288, 65289, 65290, 65291, 65292, 65293, 65294,
|
|
+ 65296, 65306, 65308, 65311, 65313, 65339, 65340, 65341, 65342, 65343,
|
|
+ 65344, 65345, 65371, 65372, 65373, 65374, 65375, 65376, 65377, 65378,
|
|
+ 65379, 65380, 65382, 65392, 65393, 65438, 65440, 65474, 65482, 65490,
|
|
+ 65498, 65504, 65506, 65507, 65508, 65509, 65512, 65513, 65517, 65529,
|
|
+ 65532, 0, 13, 40, 60, 63, 80, 128, 256, 263,
|
|
+ 311, 320, 373, 377, 394, 400, 464, 509, 640, 672,
|
|
+ 768, 800, 816, 833, 834, 842, 896, 927, 928, 968,
|
|
+ 976, 977, 1024, 1064, 1104, 1184, 2048, 2056, 2058, 2103,
|
|
+ 2108, 2111, 2135, 2136, 2304, 2326, 2335, 2336, 2367, 2432,
|
|
+ 2494, 2560, 2561, 2565, 2572, 2576, 2581, 2585, 2616, 2623,
|
|
+ 2624, 2640, 2656, 2685, 2687, 2816, 2873, 2880, 2904, 2912,
|
|
+ 2936, 3072, 3680, 4096, 4097, 4098, 4099, 4152, 4167, 4178,
|
|
+ 4198, 4224, 4226, 4227, 4272, 4275, 4279, 4281, 4283, 4285,
|
|
+ 4286, 4304, 4336, 4352, 4355, 4391, 4396, 4397, 4406, 4416,
|
|
+ 4480, 4482, 4483, 4531, 4534, 4543, 4545, 4549, 4560, 5760,
|
|
+ 5803, 5804, 5805, 5806, 5808, 5814, 5815, 5824, 8192, 9216,
|
|
+ 9328, 12288, 26624, 28416, 28496, 28497, 28559, 28563, 45056, 53248,
|
|
+ 53504, 53545, 53605, 53607, 53610, 53613, 53619, 53627, 53635, 53637,
|
|
+ 53644, 53674, 53678, 53760, 53826, 53829, 54016, 54112, 54272, 54298,
|
|
+ 54324, 54350, 54358, 54376, 54402, 54428, 54430, 54434, 54437, 54441,
|
|
+ 54446, 54454, 54459, 54461, 54469, 54480, 54506, 54532, 54535, 54541,
|
|
+ 54550, 54558, 54584, 54587, 54592, 54598, 54602, 54610, 54636, 54662,
|
|
+ 54688, 54714, 54740, 54766, 54792, 54818, 54844, 54870, 54896, 54922,
|
|
+ 54952, 54977, 54978, 55003, 55004, 55010, 55035, 55036, 55061, 55062,
|
|
+ 55068, 55093, 55094, 55119, 55120, 55126, 55151, 55152, 55177, 55178,
|
|
+ 55184, 55209, 55210, 55235, 55236, 55242, 55246, 60928, 60933, 60961,
|
|
+ 60964, 60967, 60969, 60980, 60985, 60987, 60994, 60999, 61001, 61003,
|
|
+ 61005, 61009, 61012, 61015, 61017, 61019, 61021, 61023, 61025, 61028,
|
|
+ 61031, 61036, 61044, 61049, 61054, 61056, 61067, 61089, 61093, 61099,
|
|
+ 61168, 61440, 61488, 61600, 61617, 61633, 61649, 61696, 61712, 61744,
|
|
+ 61808, 61926, 61968, 62016, 62032, 62208, 62256, 62263, 62336, 62368,
|
|
+ 62406, 62432, 62464, 62528, 62530, 62713, 62720, 62784, 62800, 62971,
|
|
+ 63045, 63104, 63232, 0, 42710, 42752, 46900, 46912, 47133, 63488,
|
|
+ 1, 32, 256, 0, 65533,
|
|
+ };
|
|
+static u16 aFts5UnicodeData[] = {
|
|
+ 1025, 61, 117, 55, 117, 54, 50, 53, 57, 53,
|
|
+ 49, 85, 333, 85, 121, 85, 841, 54, 53, 50,
|
|
+ 56, 48, 56, 837, 54, 57, 50, 57, 1057, 61,
|
|
+ 53, 151, 58, 53, 56, 58, 39, 52, 57, 34,
|
|
+ 58, 56, 58, 57, 79, 56, 37, 85, 56, 47,
|
|
+ 39, 51, 111, 53, 745, 57, 233, 773, 57, 261,
|
|
+ 1822, 37, 542, 37, 1534, 222, 69, 73, 37, 126,
|
|
+ 126, 73, 69, 137, 37, 73, 37, 105, 101, 73,
|
|
+ 37, 73, 37, 190, 158, 37, 126, 126, 73, 37,
|
|
+ 126, 94, 37, 39, 94, 69, 135, 41, 40, 37,
|
|
+ 41, 40, 37, 41, 40, 37, 542, 37, 606, 37,
|
|
+ 41, 40, 37, 126, 73, 37, 1886, 197, 73, 37,
|
|
+ 73, 69, 126, 105, 37, 286, 2181, 39, 869, 582,
|
|
+ 152, 390, 472, 166, 248, 38, 56, 38, 568, 3596,
|
|
+ 158, 38, 56, 94, 38, 101, 53, 88, 41, 53,
|
|
+ 105, 41, 73, 37, 553, 297, 1125, 94, 37, 105,
|
|
+ 101, 798, 133, 94, 57, 126, 94, 37, 1641, 1541,
|
|
+ 1118, 58, 172, 75, 1790, 478, 37, 2846, 1225, 38,
|
|
+ 213, 1253, 53, 49, 55, 1452, 49, 44, 53, 76,
|
|
+ 53, 76, 53, 44, 871, 103, 85, 162, 121, 85,
|
|
+ 55, 85, 90, 364, 53, 85, 1031, 38, 327, 684,
|
|
+ 333, 149, 71, 44, 3175, 53, 39, 236, 34, 58,
|
|
+ 204, 70, 76, 58, 140, 71, 333, 103, 90, 39,
|
|
+ 469, 34, 39, 44, 967, 876, 2855, 364, 39, 333,
|
|
+ 1063, 300, 70, 58, 117, 38, 711, 140, 38, 300,
|
|
+ 38, 108, 38, 172, 501, 807, 108, 53, 39, 359,
|
|
+ 876, 108, 42, 1735, 44, 42, 44, 39, 106, 268,
|
|
+ 138, 44, 74, 39, 236, 327, 76, 85, 333, 53,
|
|
+ 38, 199, 231, 44, 74, 263, 71, 711, 231, 39,
|
|
+ 135, 44, 39, 106, 140, 74, 74, 44, 39, 42,
|
|
+ 71, 103, 76, 333, 71, 87, 207, 58, 55, 76,
|
|
+ 42, 199, 71, 711, 231, 71, 71, 71, 44, 106,
|
|
+ 76, 76, 108, 44, 135, 39, 333, 76, 103, 44,
|
|
+ 76, 42, 295, 103, 711, 231, 71, 167, 44, 39,
|
|
+ 106, 172, 76, 42, 74, 44, 39, 71, 76, 333,
|
|
+ 53, 55, 44, 74, 263, 71, 711, 231, 71, 167,
|
|
+ 44, 39, 42, 44, 42, 140, 74, 74, 44, 44,
|
|
+ 42, 71, 103, 76, 333, 58, 39, 207, 44, 39,
|
|
+ 199, 103, 135, 71, 39, 71, 71, 103, 391, 74,
|
|
+ 44, 74, 106, 106, 44, 39, 42, 333, 111, 218,
|
|
+ 55, 58, 106, 263, 103, 743, 327, 167, 39, 108,
|
|
+ 138, 108, 140, 76, 71, 71, 76, 333, 239, 58,
|
|
+ 74, 263, 103, 743, 327, 167, 44, 39, 42, 44,
|
|
+ 170, 44, 74, 74, 76, 74, 39, 71, 76, 333,
|
|
+ 71, 74, 263, 103, 1319, 39, 106, 140, 106, 106,
|
|
+ 44, 39, 42, 71, 76, 333, 207, 58, 199, 74,
|
|
+ 583, 775, 295, 39, 231, 44, 106, 108, 44, 266,
|
|
+ 74, 53, 1543, 44, 71, 236, 55, 199, 38, 268,
|
|
+ 53, 333, 85, 71, 39, 71, 39, 39, 135, 231,
|
|
+ 103, 39, 39, 71, 135, 44, 71, 204, 76, 39,
|
|
+ 167, 38, 204, 333, 135, 39, 122, 501, 58, 53,
|
|
+ 122, 76, 218, 333, 335, 58, 44, 58, 44, 58,
|
|
+ 44, 54, 50, 54, 50, 74, 263, 1159, 460, 42,
|
|
+ 172, 53, 76, 167, 364, 1164, 282, 44, 218, 90,
|
|
+ 181, 154, 85, 1383, 74, 140, 42, 204, 42, 76,
|
|
+ 74, 76, 39, 333, 213, 199, 74, 76, 135, 108,
|
|
+ 39, 106, 71, 234, 103, 140, 423, 44, 74, 76,
|
|
+ 202, 44, 39, 42, 333, 106, 44, 90, 1225, 41,
|
|
+ 41, 1383, 53, 38, 10631, 135, 231, 39, 135, 1319,
|
|
+ 135, 1063, 135, 231, 39, 135, 487, 1831, 135, 2151,
|
|
+ 108, 309, 655, 519, 346, 2727, 49, 19847, 85, 551,
|
|
+ 61, 839, 54, 50, 2407, 117, 110, 423, 135, 108,
|
|
+ 583, 108, 85, 583, 76, 423, 103, 76, 1671, 76,
|
|
+ 42, 236, 266, 44, 74, 364, 117, 38, 117, 55,
|
|
+ 39, 44, 333, 335, 213, 49, 149, 108, 61, 333,
|
|
+ 1127, 38, 1671, 1319, 44, 39, 2247, 935, 108, 138,
|
|
+ 76, 106, 74, 44, 202, 108, 58, 85, 333, 967,
|
|
+ 167, 1415, 554, 231, 74, 333, 47, 1114, 743, 76,
|
|
+ 106, 85, 1703, 42, 44, 42, 236, 44, 42, 44,
|
|
+ 74, 268, 202, 332, 44, 333, 333, 245, 38, 213,
|
|
+ 140, 42, 1511, 44, 42, 172, 42, 44, 170, 44,
|
|
+ 74, 231, 333, 245, 346, 300, 314, 76, 42, 967,
|
|
+ 42, 140, 74, 76, 42, 44, 74, 71, 333, 1415,
|
|
+ 44, 42, 76, 106, 44, 42, 108, 74, 149, 1159,
|
|
+ 266, 268, 74, 76, 181, 333, 103, 333, 967, 198,
|
|
+ 85, 277, 108, 53, 428, 42, 236, 135, 44, 135,
|
|
+ 74, 44, 71, 1413, 2022, 421, 38, 1093, 1190, 1260,
|
|
+ 140, 4830, 261, 3166, 261, 265, 197, 201, 261, 265,
|
|
+ 261, 265, 197, 201, 261, 41, 41, 41, 94, 229,
|
|
+ 265, 453, 261, 264, 261, 264, 261, 264, 165, 69,
|
|
+ 137, 40, 56, 37, 120, 101, 69, 137, 40, 120,
|
|
+ 133, 69, 137, 120, 261, 169, 120, 101, 69, 137,
|
|
+ 40, 88, 381, 162, 209, 85, 52, 51, 54, 84,
|
|
+ 51, 54, 52, 277, 59, 60, 162, 61, 309, 52,
|
|
+ 51, 149, 80, 117, 57, 54, 50, 373, 57, 53,
|
|
+ 48, 341, 61, 162, 194, 47, 38, 207, 121, 54,
|
|
+ 50, 38, 335, 121, 54, 50, 422, 855, 428, 139,
|
|
+ 44, 107, 396, 90, 41, 154, 41, 90, 37, 105,
|
|
+ 69, 105, 37, 58, 41, 90, 57, 169, 218, 41,
|
|
+ 58, 41, 58, 41, 58, 137, 58, 37, 137, 37,
|
|
+ 135, 37, 90, 69, 73, 185, 94, 101, 58, 57,
|
|
+ 90, 37, 58, 527, 1134, 94, 142, 47, 185, 186,
|
|
+ 89, 154, 57, 90, 57, 90, 57, 250, 57, 1018,
|
|
+ 89, 90, 57, 58, 57, 1018, 8601, 282, 153, 666,
|
|
+ 89, 250, 54, 50, 2618, 57, 986, 825, 1306, 217,
|
|
+ 602, 1274, 378, 1935, 2522, 719, 5882, 57, 314, 57,
|
|
+ 1754, 281, 3578, 57, 4634, 3322, 54, 50, 54, 50,
|
|
+ 54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
|
|
+ 975, 1434, 185, 54, 50, 1017, 54, 50, 54, 50,
|
|
+ 54, 50, 54, 50, 54, 50, 537, 8218, 4217, 54,
|
|
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
|
|
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
|
|
+ 50, 2041, 54, 50, 54, 50, 1049, 54, 50, 8281,
|
|
+ 1562, 697, 90, 217, 346, 1513, 1509, 126, 73, 69,
|
|
+ 254, 105, 37, 94, 37, 94, 165, 70, 105, 37,
|
|
+ 3166, 37, 218, 158, 108, 94, 149, 47, 85, 1221,
|
|
+ 37, 37, 1799, 38, 53, 44, 743, 231, 231, 231,
|
|
+ 231, 231, 231, 231, 231, 1036, 85, 52, 51, 52,
|
|
+ 51, 117, 52, 51, 53, 52, 51, 309, 49, 85,
|
|
+ 49, 53, 52, 51, 85, 52, 51, 54, 50, 54,
|
|
+ 50, 54, 50, 54, 50, 181, 38, 341, 81, 858,
|
|
+ 2874, 6874, 410, 61, 117, 58, 38, 39, 46, 54,
|
|
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 90,
|
|
+ 54, 50, 54, 50, 54, 50, 54, 50, 49, 54,
|
|
+ 82, 58, 302, 140, 74, 49, 166, 90, 110, 38,
|
|
+ 39, 53, 90, 2759, 76, 88, 70, 39, 49, 2887,
|
|
+ 53, 102, 39, 1319, 3015, 90, 143, 346, 871, 1178,
|
|
+ 519, 1018, 335, 986, 271, 58, 495, 1050, 335, 1274,
|
|
+ 495, 2042, 8218, 39, 39, 2074, 39, 39, 679, 38,
|
|
+ 36583, 1786, 1287, 198, 85, 8583, 38, 117, 519, 333,
|
|
+ 71, 1502, 39, 44, 107, 53, 332, 53, 38, 798,
|
|
+ 44, 2247, 334, 76, 213, 760, 294, 88, 478, 69,
|
|
+ 2014, 38, 261, 190, 350, 38, 88, 158, 158, 382,
|
|
+ 70, 37, 231, 44, 103, 44, 135, 44, 743, 74,
|
|
+ 76, 42, 154, 207, 90, 55, 58, 1671, 149, 74,
|
|
+ 1607, 522, 44, 85, 333, 588, 199, 117, 39, 333,
|
|
+ 903, 268, 85, 743, 364, 74, 53, 935, 108, 42,
|
|
+ 1511, 44, 74, 140, 74, 44, 138, 437, 38, 333,
|
|
+ 85, 1319, 204, 74, 76, 74, 76, 103, 44, 263,
|
|
+ 44, 42, 333, 149, 519, 38, 199, 122, 39, 42,
|
|
+ 1543, 44, 39, 108, 71, 76, 167, 76, 39, 44,
|
|
+ 39, 71, 38, 85, 359, 42, 76, 74, 85, 39,
|
|
+ 70, 42, 44, 199, 199, 199, 231, 231, 1127, 74,
|
|
+ 44, 74, 44, 74, 53, 42, 44, 333, 39, 39,
|
|
+ 743, 1575, 36, 68, 68, 36, 63, 63, 11719, 3399,
|
|
+ 229, 165, 39, 44, 327, 57, 423, 167, 39, 71,
|
|
+ 71, 3463, 536, 11623, 54, 50, 2055, 1735, 391, 55,
|
|
+ 58, 524, 245, 54, 50, 53, 236, 53, 81, 80,
|
|
+ 54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
|
|
+ 54, 50, 54, 50, 54, 50, 85, 54, 50, 149,
|
|
+ 112, 117, 149, 49, 54, 50, 54, 50, 54, 50,
|
|
+ 117, 57, 49, 121, 53, 55, 85, 167, 4327, 34,
|
|
+ 117, 55, 117, 54, 50, 53, 57, 53, 49, 85,
|
|
+ 333, 85, 121, 85, 841, 54, 53, 50, 56, 48,
|
|
+ 56, 837, 54, 57, 50, 57, 54, 50, 53, 54,
|
|
+ 50, 85, 327, 38, 1447, 70, 999, 199, 199, 199,
|
|
+ 103, 87, 57, 56, 58, 87, 58, 153, 90, 98,
|
|
+ 90, 391, 839, 615, 71, 487, 455, 3943, 117, 1455,
|
|
+ 314, 1710, 143, 570, 47, 410, 1466, 44, 935, 1575,
|
|
+ 999, 143, 551, 46, 263, 46, 967, 53, 1159, 263,
|
|
+ 53, 174, 1289, 1285, 2503, 333, 199, 39, 1415, 71,
|
|
+ 39, 743, 53, 271, 711, 207, 53, 839, 53, 1799,
|
|
+ 71, 39, 108, 76, 140, 135, 103, 871, 108, 44,
|
|
+ 271, 309, 935, 79, 53, 1735, 245, 711, 271, 615,
|
|
+ 271, 2343, 1007, 42, 44, 42, 1703, 492, 245, 655,
|
|
+ 333, 76, 42, 1447, 106, 140, 74, 76, 85, 34,
|
|
+ 149, 807, 333, 108, 1159, 172, 42, 268, 333, 149,
|
|
+ 76, 42, 1543, 106, 300, 74, 135, 149, 333, 1383,
|
|
+ 44, 42, 44, 74, 204, 42, 44, 333, 28135, 3182,
|
|
+ 149, 34279, 18215, 2215, 39, 1482, 140, 422, 71, 7898,
|
|
+ 1274, 1946, 74, 108, 122, 202, 258, 268, 90, 236,
|
|
+ 986, 140, 1562, 2138, 108, 58, 2810, 591, 841, 837,
|
|
+ 841, 229, 581, 841, 837, 41, 73, 41, 73, 137,
|
|
+ 265, 133, 37, 229, 357, 841, 837, 73, 137, 265,
|
|
+ 233, 837, 73, 137, 169, 41, 233, 837, 841, 837,
|
|
+ 841, 837, 841, 837, 841, 837, 841, 837, 841, 901,
|
|
+ 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
|
|
+ 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
|
|
+ 809, 57, 805, 57, 197, 94, 1613, 135, 871, 71,
|
|
+ 39, 39, 327, 135, 39, 39, 39, 39, 39, 39,
|
|
+ 103, 71, 39, 39, 39, 39, 39, 39, 71, 39,
|
|
+ 135, 231, 135, 135, 39, 327, 551, 103, 167, 551,
|
|
+ 89, 1434, 3226, 506, 474, 506, 506, 367, 1018, 1946,
|
|
+ 1402, 954, 1402, 314, 90, 1082, 218, 2266, 666, 1210,
|
|
+ 186, 570, 2042, 58, 5850, 154, 2010, 154, 794, 2266,
|
|
+ 378, 2266, 3738, 39, 39, 39, 39, 39, 39, 17351,
|
|
+ 34, 3074, 7692, 63, 63,
|
|
+ };
|
|
+
|
|
+static int sqlite3Fts5UnicodeCategory(int iCode) {
|
|
+ int iRes = -1;
|
|
+ int iHi;
|
|
+ int iLo;
|
|
+ int ret;
|
|
+ u16 iKey;
|
|
+
|
|
+ if( iCode>=(1<<20) ){
|
|
+ return 0;
|
|
+ }
|
|
+ iLo = aFts5UnicodeBlock[(iCode>>16)];
|
|
+ iHi = aFts5UnicodeBlock[1+(iCode>>16)];
|
|
+ iKey = (iCode & 0xFFFF);
|
|
+ while( iHi>iLo ){
|
|
+ int iTest = (iHi + iLo) / 2;
|
|
+ assert( iTest>=iLo && iTest<iHi );
|
|
+ if( iKey>=aFts5UnicodeMap[iTest] ){
|
|
+ iRes = iTest;
|
|
+ iLo = iTest+1;
|
|
+ }else{
|
|
+ iHi = iTest;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( iRes<0 ) return 0;
|
|
+ if( iKey>=(aFts5UnicodeMap[iRes]+(aFts5UnicodeData[iRes]>>5)) ) return 0;
|
|
+ ret = aFts5UnicodeData[iRes] & 0x1F;
|
|
+ if( ret!=30 ) return ret;
|
|
+ return ((iKey - aFts5UnicodeMap[iRes]) & 0x01) ? 5 : 9;
|
|
+}
|
|
+
|
|
+static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
|
|
+ int i = 0;
|
|
+ int iTbl = 0;
|
|
+ while( i<128 ){
|
|
+ int bToken = aArray[ aFts5UnicodeData[iTbl] & 0x1F ];
|
|
+ int n = (aFts5UnicodeData[iTbl] >> 5) + i;
|
|
+ for(; i<128 && i<n; i++){
|
|
+ aAscii[i] = (u8)bToken;
|
|
+ }
|
|
+ iTbl++;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
/*
|
|
** 2015 May 30
|
|
**
|
|
@@ -203503,6 +219482,11 @@
|
|
** the number of fts5 rows that contain at least one instance of term
|
|
** $term. Field $cnt is set to the total number of instances of term
|
|
** $term in the database.
|
|
+**
|
|
+** instance:
|
|
+** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>));
|
|
+**
|
|
+** One row for each term instance in the database.
|
|
*/
|
|
|
|
|
|
@@ -203518,7 +219502,7 @@
|
|
char *zFts5Db; /* Db containing fts5 table */
|
|
sqlite3 *db; /* Database handle */
|
|
Fts5Global *pGlobal; /* FTS5 global object for this database */
|
|
- int eType; /* FTS5_VOCAB_COL or ROW */
|
|
+ int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */
|
|
};
|
|
|
|
struct Fts5VocabCursor {
|
|
@@ -203538,16 +219522,22 @@
|
|
i64 *aCnt;
|
|
i64 *aDoc;
|
|
|
|
- /* Output values used by 'row' and 'col' tables */
|
|
+ /* Output values used by all tables. */
|
|
i64 rowid; /* This table's current rowid value */
|
|
Fts5Buffer term; /* Current value of 'term' column */
|
|
+
|
|
+ /* Output values Used by 'instance' tables only */
|
|
+ i64 iInstPos;
|
|
+ int iInstOff;
|
|
};
|
|
|
|
-#define FTS5_VOCAB_COL 0
|
|
-#define FTS5_VOCAB_ROW 1
|
|
+#define FTS5_VOCAB_COL 0
|
|
+#define FTS5_VOCAB_ROW 1
|
|
+#define FTS5_VOCAB_INSTANCE 2
|
|
|
|
#define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt"
|
|
#define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt"
|
|
+#define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset"
|
|
|
|
/*
|
|
** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
|
|
@@ -203575,6 +219565,9 @@
|
|
if( sqlite3_stricmp(zCopy, "row")==0 ){
|
|
*peType = FTS5_VOCAB_ROW;
|
|
}else
|
|
+ if( sqlite3_stricmp(zCopy, "instance")==0 ){
|
|
+ *peType = FTS5_VOCAB_INSTANCE;
|
|
+ }else
|
|
{
|
|
*pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy);
|
|
rc = SQLITE_ERROR;
|
|
@@ -203635,7 +219628,8 @@
|
|
){
|
|
const char *azSchema[] = {
|
|
"CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
|
|
- "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")"
|
|
+ "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")",
|
|
+ "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")"
|
|
};
|
|
|
|
Fts5VocabTable *pRet = 0;
|
|
@@ -203709,6 +219703,15 @@
|
|
|
|
/*
|
|
** Implementation of the xBestIndex method.
|
|
+**
|
|
+** Only constraints of the form:
|
|
+**
|
|
+** term <= ?
|
|
+** term == ?
|
|
+** term >= ?
|
|
+**
|
|
+** are interpreted. Less-than and less-than-or-equal are treated
|
|
+** identically, as are greater-than and greater-than-or-equal.
|
|
*/
|
|
static int fts5VocabBestIndexMethod(
|
|
sqlite3_vtab *pUnused,
|
|
@@ -203852,7 +219855,57 @@
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
+static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){
|
|
+ int rc = SQLITE_OK;
|
|
+
|
|
+ if( sqlite3Fts5IterEof(pCsr->pIter) ){
|
|
+ pCsr->bEof = 1;
|
|
+ }else{
|
|
+ const char *zTerm;
|
|
+ int nTerm;
|
|
+ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
|
|
+ if( pCsr->nLeTerm>=0 ){
|
|
+ int nCmp = MIN(nTerm, pCsr->nLeTerm);
|
|
+ int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
|
|
+ if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){
|
|
+ pCsr->bEof = 1;
|
|
+ }
|
|
+ }
|
|
|
|
+ sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
|
|
+ int eDetail = pCsr->pConfig->eDetail;
|
|
+ int rc = SQLITE_OK;
|
|
+ Fts5IndexIter *pIter = pCsr->pIter;
|
|
+ i64 *pp = &pCsr->iInstPos;
|
|
+ int *po = &pCsr->iInstOff;
|
|
+
|
|
+ assert( sqlite3Fts5IterEof(pIter)==0 );
|
|
+ assert( pCsr->bEof==0 );
|
|
+ while( eDetail==FTS5_DETAIL_NONE
|
|
+ || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp)
|
|
+ ){
|
|
+ pCsr->iInstPos = 0;
|
|
+ pCsr->iInstOff = 0;
|
|
+
|
|
+ rc = sqlite3Fts5IterNextScan(pCsr->pIter);
|
|
+ if( rc==SQLITE_OK ){
|
|
+ rc = fts5VocabInstanceNewTerm(pCsr);
|
|
+ if( pCsr->bEof || eDetail==FTS5_DETAIL_NONE ) break;
|
|
+ }
|
|
+ if( rc ){
|
|
+ pCsr->bEof = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
/*
|
|
** Advance the cursor to the next row in the table.
|
|
*/
|
|
@@ -203864,6 +219917,10 @@
|
|
|
|
pCsr->rowid++;
|
|
|
|
+ if( pTab->eType==FTS5_VOCAB_INSTANCE ){
|
|
+ return fts5VocabInstanceNext(pCsr);
|
|
+ }
|
|
+
|
|
if( pTab->eType==FTS5_VOCAB_COL ){
|
|
for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){
|
|
if( pCsr->aDoc[pCsr->iCol] ) break;
|
|
@@ -203870,7 +219927,7 @@
|
|
}
|
|
}
|
|
|
|
- if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){
|
|
+ if( pTab->eType!=FTS5_VOCAB_COL || pCsr->iCol>=nCol ){
|
|
if( sqlite3Fts5IterEof(pCsr->pIter) ){
|
|
pCsr->bEof = 1;
|
|
}else{
|
|
@@ -203894,6 +219951,7 @@
|
|
|
|
assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
|
|
while( rc==SQLITE_OK ){
|
|
+ int eDetail = pCsr->pConfig->eDetail;
|
|
const u8 *pPos; int nPos; /* Position list */
|
|
i64 iPos = 0; /* 64-bit position read from poslist */
|
|
int iOff = 0; /* Current offset within position list */
|
|
@@ -203900,16 +219958,19 @@
|
|
|
|
pPos = pCsr->pIter->pData;
|
|
nPos = pCsr->pIter->nData;
|
|
- switch( pCsr->pConfig->eDetail ){
|
|
- case FTS5_DETAIL_FULL:
|
|
- pPos = pCsr->pIter->pData;
|
|
- nPos = pCsr->pIter->nData;
|
|
- if( pTab->eType==FTS5_VOCAB_ROW ){
|
|
+
|
|
+ switch( pTab->eType ){
|
|
+ case FTS5_VOCAB_ROW:
|
|
+ if( eDetail==FTS5_DETAIL_FULL ){
|
|
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
|
|
pCsr->aCnt[0]++;
|
|
}
|
|
- pCsr->aDoc[0]++;
|
|
- }else{
|
|
+ }
|
|
+ pCsr->aDoc[0]++;
|
|
+ break;
|
|
+
|
|
+ case FTS5_VOCAB_COL:
|
|
+ if( eDetail==FTS5_DETAIL_FULL ){
|
|
int iCol = -1;
|
|
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
|
|
int ii = FTS5_POS2COLUMN(iPos);
|
|
@@ -203923,13 +219984,7 @@
|
|
iCol = ii;
|
|
}
|
|
}
|
|
- }
|
|
- break;
|
|
-
|
|
- case FTS5_DETAIL_COLUMNS:
|
|
- if( pTab->eType==FTS5_VOCAB_ROW ){
|
|
- pCsr->aDoc[0]++;
|
|
- }else{
|
|
+ }else if( eDetail==FTS5_DETAIL_COLUMNS ){
|
|
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
|
|
assert_nc( iPos>=0 && iPos<nCol );
|
|
if( iPos>=nCol ){
|
|
@@ -203938,12 +219993,14 @@
|
|
}
|
|
pCsr->aDoc[iPos]++;
|
|
}
|
|
+ }else{
|
|
+ assert( eDetail==FTS5_DETAIL_NONE );
|
|
+ pCsr->aDoc[0]++;
|
|
}
|
|
break;
|
|
|
|
- default:
|
|
- assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE );
|
|
- pCsr->aDoc[0]++;
|
|
+ default:
|
|
+ assert( pTab->eType==FTS5_VOCAB_INSTANCE );
|
|
break;
|
|
}
|
|
|
|
@@ -203950,6 +220007,7 @@
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3Fts5IterNextScan(pCsr->pIter);
|
|
}
|
|
+ if( pTab->eType==FTS5_VOCAB_INSTANCE ) break;
|
|
|
|
if( rc==SQLITE_OK ){
|
|
zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
|
|
@@ -203979,7 +220037,9 @@
|
|
int nUnused, /* Number of elements in apVal */
|
|
sqlite3_value **apVal /* Arguments for the indexing scheme */
|
|
){
|
|
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
|
|
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
|
|
+ int eType = pTab->eType;
|
|
int rc = SQLITE_OK;
|
|
|
|
int iVal = 0;
|
|
@@ -204019,11 +220079,16 @@
|
|
}
|
|
}
|
|
|
|
-
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
|
|
}
|
|
- if( rc==SQLITE_OK ){
|
|
+ if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){
|
|
+ rc = fts5VocabInstanceNewTerm(pCsr);
|
|
+ }
|
|
+ if( rc==SQLITE_OK
|
|
+ && !pCsr->bEof
|
|
+ && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE)
|
|
+ ){
|
|
rc = fts5VocabNextMethod(pCursor);
|
|
}
|
|
|
|
@@ -204065,7 +220130,7 @@
|
|
}else{
|
|
iVal = pCsr->aCnt[pCsr->iCol];
|
|
}
|
|
- }else{
|
|
+ }else if( eType==FTS5_VOCAB_ROW ){
|
|
assert( iCol==1 || iCol==2 );
|
|
if( iCol==1 ){
|
|
iVal = pCsr->aDoc[0];
|
|
@@ -204072,6 +220137,34 @@
|
|
}else{
|
|
iVal = pCsr->aCnt[0];
|
|
}
|
|
+ }else{
|
|
+ assert( eType==FTS5_VOCAB_INSTANCE );
|
|
+ switch( iCol ){
|
|
+ case 1:
|
|
+ sqlite3_result_int64(pCtx, pCsr->pIter->iRowid);
|
|
+ break;
|
|
+ case 2: {
|
|
+ int ii = -1;
|
|
+ if( eDetail==FTS5_DETAIL_FULL ){
|
|
+ ii = FTS5_POS2COLUMN(pCsr->iInstPos);
|
|
+ }else if( eDetail==FTS5_DETAIL_COLUMNS ){
|
|
+ ii = (int)pCsr->iInstPos;
|
|
+ }
|
|
+ if( ii>=0 && ii<pCsr->pConfig->nCol ){
|
|
+ const char *z = pCsr->pConfig->azCol[ii];
|
|
+ sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ assert( iCol==3 );
|
|
+ if( eDetail==FTS5_DETAIL_FULL ){
|
|
+ int ii = FTS5_POS2OFFSET(pCsr->iInstPos);
|
|
+ sqlite3_result_int(pCtx, ii);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
if( iVal>0 ) sqlite3_result_int64(pCtx, iVal);
|
|
@@ -204117,6 +220210,7 @@
|
|
/* xSavepoint */ 0,
|
|
/* xRelease */ 0,
|
|
/* xRollbackTo */ 0,
|
|
+ /* xShadowName */ 0
|
|
};
|
|
void *p = (void*)pGlobal;
|
|
|
|
@@ -204124,8 +220218,6 @@
|
|
}
|
|
|
|
|
|
-
|
|
-
|
|
|
|
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */
|
|
|
|
@@ -204399,6 +220491,7 @@
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
0, /* xRollbackTo */
|
|
+ 0, /* xShadowName */
|
|
};
|
|
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
@@ -204431,3 +220524,10 @@
|
|
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
|
|
|
|
/************** End of stmt.c ************************************************/
|
|
+#if __LINE__!=220527
|
|
+#undef SQLITE_SOURCE_ID
|
|
+#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2"
|
|
+#endif
|
|
+/* Return the source-id for this library */
|
|
+SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|
|
+/************************** End of sqlite3.c ******************************/
|
|
--- contrib/sqlite3/sqlite3.h.orig
|
|
+++ contrib/sqlite3/sqlite3.h
|
|
@@ -115,15 +115,17 @@
|
|
** a string which identifies a particular check-in of SQLite
|
|
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
|
** string contains the date and time of the check-in (UTC) and a SHA1
|
|
-** or SHA3-256 hash of the entire source tree.
|
|
+** or SHA3-256 hash of the entire source tree. If the source code has
|
|
+** been edited in any way since it was last checked in, then the last
|
|
+** four hexadecimal digits of the hash may be modified.
|
|
**
|
|
** See also: [sqlite3_libversion()],
|
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
|
** [sqlite_version()] and [sqlite_source_id()].
|
|
*/
|
|
-#define SQLITE_VERSION "3.20.0"
|
|
-#define SQLITE_VERSION_NUMBER 3020000
|
|
-#define SQLITE_SOURCE_ID "2017-08-01 13:24:15 9501e22dfeebdcefa783575e47c60b514d7c2e0cad73b2a496c0bc4b680900a8"
|
|
+#define SQLITE_VERSION "3.26.0"
|
|
+#define SQLITE_VERSION_NUMBER 3026000
|
|
+#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
|
|
|
|
/*
|
|
** CAPI3REF: Run-Time Library Version Numbers
|
|
@@ -139,7 +141,7 @@
|
|
**
|
|
** <blockquote><pre>
|
|
** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
|
|
-** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
|
|
+** assert( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,80)==0 );
|
|
** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
|
|
** </pre></blockquote>)^
|
|
**
|
|
@@ -149,9 +151,11 @@
|
|
** function is provided for use in DLLs since DLL users usually do not have
|
|
** direct access to string constants within the DLL. ^The
|
|
** sqlite3_libversion_number() function returns an integer equal to
|
|
-** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns
|
|
+** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns
|
|
** a pointer to a string constant whose value is the same as the
|
|
-** [SQLITE_SOURCE_ID] C preprocessor macro.
|
|
+** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built
|
|
+** using an edited copy of [the amalgamation], then the last four characters
|
|
+** of the hash might be different from [SQLITE_SOURCE_ID].)^
|
|
**
|
|
** See also: [sqlite_version()] and [sqlite_source_id()].
|
|
*/
|
|
@@ -432,7 +436,7 @@
|
|
#define SQLITE_FULL 13 /* Insertion failed because database is full */
|
|
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
|
|
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
|
|
-#define SQLITE_EMPTY 16 /* Not used */
|
|
+#define SQLITE_EMPTY 16 /* Internal use only */
|
|
#define SQLITE_SCHEMA 17 /* The database schema changed */
|
|
#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
|
|
#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */
|
|
@@ -466,6 +470,9 @@
|
|
** the most recent error can be obtained using
|
|
** [sqlite3_extended_errcode()].
|
|
*/
|
|
+#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
|
|
+#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
|
|
+#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
|
|
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
|
|
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
|
|
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
|
|
@@ -494,7 +501,11 @@
|
|
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
|
|
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
|
|
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
|
|
+#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
|
|
+#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
|
|
+#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
|
|
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
|
+#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
|
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
|
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
|
|
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
|
@@ -501,11 +512,15 @@
|
|
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
|
|
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
|
|
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
|
|
+#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
|
|
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
|
+#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
|
|
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
|
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
|
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
|
#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
|
|
+#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
|
|
+#define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6<<8))
|
|
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
|
#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
|
|
#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
|
|
@@ -580,6 +595,11 @@
|
|
** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
|
|
** read-only media and cannot be changed even by processes with
|
|
** elevated privileges.
|
|
+**
|
|
+** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying
|
|
+** filesystem supports doing multiple write operations atomically when those
|
|
+** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and
|
|
+** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE].
|
|
*/
|
|
#define SQLITE_IOCAP_ATOMIC 0x00000001
|
|
#define SQLITE_IOCAP_ATOMIC512 0x00000002
|
|
@@ -595,6 +615,7 @@
|
|
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
|
|
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
|
|
#define SQLITE_IOCAP_IMMUTABLE 0x00002000
|
|
+#define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000
|
|
|
|
/*
|
|
** CAPI3REF: File Locking Levels
|
|
@@ -729,6 +750,7 @@
|
|
** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN]
|
|
** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE]
|
|
** <li> [SQLITE_IOCAP_IMMUTABLE]
|
|
+** <li> [SQLITE_IOCAP_BATCH_ATOMIC]
|
|
** </ul>
|
|
**
|
|
** The SQLITE_IOCAP_ATOMIC property means that all writes of
|
|
@@ -866,7 +888,8 @@
|
|
** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
|
|
** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
|
|
** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary
|
|
-** write ahead log and shared memory files used for transaction control
|
|
+** write ahead log ([WAL file]) and shared memory
|
|
+** files used for transaction control
|
|
** are automatically deleted when the latest connection to the database
|
|
** closes. Setting persistent WAL mode causes those files to persist after
|
|
** close. Persisting the files is useful when other processes that do not
|
|
@@ -1012,6 +1035,66 @@
|
|
** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
|
|
** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
|
|
** this opcode.
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]]
|
|
+** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then
|
|
+** the file descriptor is placed in "batch write mode", which
|
|
+** means all subsequent write operations will be deferred and done
|
|
+** atomically at the next [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. Systems
|
|
+** that do not support batch atomic writes will return SQLITE_NOTFOUND.
|
|
+** ^Following a successful SQLITE_FCNTL_BEGIN_ATOMIC_WRITE and prior to
|
|
+** the closing [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] or
|
|
+** [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE], SQLite will make
|
|
+** no VFS interface calls on the same [sqlite3_file] file descriptor
|
|
+** except for calls to the xWrite method and the xFileControl method
|
|
+** with [SQLITE_FCNTL_SIZE_HINT].
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]]
|
|
+** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write
|
|
+** operations since the previous successful call to
|
|
+** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically.
|
|
+** This file control returns [SQLITE_OK] if and only if the writes were
|
|
+** all performed successfully and have been committed to persistent storage.
|
|
+** ^Regardless of whether or not it is successful, this file control takes
|
|
+** the file descriptor out of batch write mode so that all subsequent
|
|
+** write operations are independent.
|
|
+** ^SQLite will never invoke SQLITE_FCNTL_COMMIT_ATOMIC_WRITE without
|
|
+** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]]
|
|
+** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write
|
|
+** operations since the previous successful call to
|
|
+** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back.
|
|
+** ^This file control takes the file descriptor out of batch write mode
|
|
+** so that all subsequent write operations are independent.
|
|
+** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
|
|
+** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
|
|
+** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
|
|
+** a file lock using the xLock or xShmLock methods of the VFS to wait
|
|
+** for up to M milliseconds before failing, where M is the single
|
|
+** unsigned integer parameter.
|
|
+**
|
|
+** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
|
+** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
|
+** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
|
+** The "data version" for the pager is written into the pointer. The
|
|
+** "data version" changes whenever any change occurs to the corresponding
|
|
+** database file, either through SQL statements on the same database
|
|
+** connection or through transactions committed by separate database
|
|
+** connections possibly in other processes. The [sqlite3_total_changes()]
|
|
+** interface can be used to find if any database on the connection has changed,
|
|
+** but that interface responds to changes on TEMP as well as MAIN and does
|
|
+** not provide a mechanism to detect changes to MAIN only. Also, the
|
|
+** [sqlite3_total_changes()] interface responds to internal changes only and
|
|
+** omits changes made by other database connections. The
|
|
+** [PRAGMA data_version] command provide a mechanism to detect changes to
|
|
+** a single attached database that occur due to other database connections,
|
|
+** but omits changes implemented by the database connection on which it is
|
|
+** called. This file control is the only mechanism to detect changes that
|
|
+** happen either internally or externally and that are associated with
|
|
+** a particular attached database.
|
|
** </ul>
|
|
*/
|
|
#define SQLITE_FCNTL_LOCKSTATE 1
|
|
@@ -1043,6 +1126,11 @@
|
|
#define SQLITE_FCNTL_JOURNAL_POINTER 28
|
|
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
|
|
#define SQLITE_FCNTL_PDB 30
|
|
+#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31
|
|
+#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32
|
|
+#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
|
|
+#define SQLITE_FCNTL_LOCK_TIMEOUT 34
|
|
+#define SQLITE_FCNTL_DATA_VERSION 35
|
|
|
|
/* deprecated names */
|
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
|
@@ -1080,12 +1168,18 @@
|
|
** in the name of the object stands for "virtual file system". See
|
|
** the [VFS | VFS documentation] for further information.
|
|
**
|
|
-** The value of the iVersion field is initially 1 but may be larger in
|
|
-** future versions of SQLite. Additional fields may be appended to this
|
|
-** object when the iVersion value is increased. Note that the structure
|
|
-** of the sqlite3_vfs object changes in the transaction between
|
|
-** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not
|
|
-** modified.
|
|
+** The VFS interface is sometimes extended by adding new methods onto
|
|
+** the end. Each time such an extension occurs, the iVersion field
|
|
+** is incremented. The iVersion value started out as 1 in
|
|
+** SQLite [version 3.5.0] on [dateof:3.5.0], then increased to 2
|
|
+** with SQLite [version 3.7.0] on [dateof:3.7.0], and then increased
|
|
+** to 3 with SQLite [version 3.7.6] on [dateof:3.7.6]. Additional fields
|
|
+** may be appended to the sqlite3_vfs object and the iVersion value
|
|
+** may increase again in future versions of SQLite.
|
|
+** Note that the structure
|
|
+** of the sqlite3_vfs object changes in the transition from
|
|
+** SQLite [version 3.5.9] to [version 3.6.0] on [dateof:3.6.0]
|
|
+** and yet the iVersion field was not modified.
|
|
**
|
|
** The szOsFile field is the size of the subclassed [sqlite3_file]
|
|
** structure used by this VFS. mxPathname is the maximum length of
|
|
@@ -1613,6 +1707,16 @@
|
|
** routines with a wrapper that simulations memory allocation failure or
|
|
** tracks memory usage, for example. </dd>
|
|
**
|
|
+** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt>
|
|
+** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes single argument of
|
|
+** type int, interpreted as a boolean, which if true provides a hint to
|
|
+** SQLite that it should avoid large memory allocations if possible.
|
|
+** SQLite will run faster if it is free to make large memory allocations,
|
|
+** but some application might prefer to run slower in exchange for
|
|
+** guarantees about memory fragmentation that are possible if large
|
|
+** allocations are avoided. This hint is normally off.
|
|
+** </dd>
|
|
+**
|
|
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
|
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
|
|
** interpreted as a boolean, which enables or disables the collection of
|
|
@@ -1630,25 +1734,7 @@
|
|
** </dd>
|
|
**
|
|
** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
|
|
-** <dd> ^The SQLITE_CONFIG_SCRATCH option specifies a static memory buffer
|
|
-** that SQLite can use for scratch memory. ^(There are three arguments
|
|
-** to SQLITE_CONFIG_SCRATCH: A pointer an 8-byte
|
|
-** aligned memory buffer from which the scratch allocations will be
|
|
-** drawn, the size of each scratch allocation (sz),
|
|
-** and the maximum number of scratch allocations (N).)^
|
|
-** The first argument must be a pointer to an 8-byte aligned buffer
|
|
-** of at least sz*N bytes of memory.
|
|
-** ^SQLite will not use more than one scratch buffers per thread.
|
|
-** ^SQLite will never request a scratch buffer that is more than 6
|
|
-** times the database page size.
|
|
-** ^If SQLite needs needs additional
|
|
-** scratch memory beyond what is provided by this configuration option, then
|
|
-** [sqlite3_malloc()] will be used to obtain the memory needed.<p>
|
|
-** ^When the application provides any amount of scratch memory using
|
|
-** SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary large
|
|
-** [sqlite3_malloc|heap allocations].
|
|
-** This can help [Robson proof|prevent memory allocation failures] due to heap
|
|
-** fragmentation in low-memory embedded systems.
|
|
+** <dd> The SQLITE_CONFIG_SCRATCH option is no longer used.
|
|
** </dd>
|
|
**
|
|
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
|
|
@@ -1684,8 +1770,7 @@
|
|
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
|
|
** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
|
|
** that SQLite will use for all of its dynamic memory allocation needs
|
|
-** beyond those provided for by [SQLITE_CONFIG_SCRATCH] and
|
|
-** [SQLITE_CONFIG_PAGECACHE].
|
|
+** beyond those provided for by [SQLITE_CONFIG_PAGECACHE].
|
|
** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled
|
|
** with either [SQLITE_ENABLE_MEMSYS3] or [SQLITE_ENABLE_MEMSYS5] and returns
|
|
** [SQLITE_ERROR] if invoked otherwise.
|
|
@@ -1871,6 +1956,22 @@
|
|
** I/O required to support statement rollback.
|
|
** The default value for this setting is controlled by the
|
|
** [SQLITE_STMTJRNL_SPILL] compile-time option.
|
|
+**
|
|
+** [[SQLITE_CONFIG_SORTERREF_SIZE]]
|
|
+** <dt>SQLITE_CONFIG_SORTERREF_SIZE
|
|
+** <dd>The SQLITE_CONFIG_SORTERREF_SIZE option accepts a single parameter
|
|
+** of type (int) - the new value of the sorter-reference size threshold.
|
|
+** Usually, when SQLite uses an external sort to order records according
|
|
+** to an ORDER BY clause, all fields required by the caller are present in the
|
|
+** sorted records. However, if SQLite determines based on the declared type
|
|
+** of a table column that its values are likely to be very large - larger
|
|
+** than the configured sorter-reference size threshold - then a reference
|
|
+** is stored in each sorted record and the required column values loaded
|
|
+** from the database as records are returned in sorted order. The default
|
|
+** value for this option is to never use this optimization. Specifying a
|
|
+** negative value for this option restores the default behaviour.
|
|
+** This option is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
|
|
** </dl>
|
|
*/
|
|
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
|
@@ -1878,7 +1979,7 @@
|
|
#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
|
|
#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
|
|
#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
|
|
-#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */
|
|
+#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
|
|
#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
|
|
#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
|
|
#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
|
|
@@ -1899,6 +2000,8 @@
|
|
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
|
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
|
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
|
|
+#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
|
|
+#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
|
|
|
|
/*
|
|
** CAPI3REF: Database Connection Configuration Options
|
|
@@ -1914,6 +2017,7 @@
|
|
** is invoked.
|
|
**
|
|
** <dl>
|
|
+** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
|
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
|
** <dd> ^This option takes three additional arguments that determine the
|
|
** [lookaside memory allocator] configuration for the [database connection].
|
|
@@ -1936,6 +2040,7 @@
|
|
** memory is in use leaves the configuration unchanged and returns
|
|
** [SQLITE_BUSY].)^</dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
|
** <dd> ^This option is used to enable or disable the enforcement of
|
|
** [foreign key constraints]. There should be two additional arguments.
|
|
@@ -1946,6 +2051,7 @@
|
|
** following this call. The second parameter may be a NULL pointer, in
|
|
** which case the FK enforcement setting is not reported back. </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
|
|
** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
|
|
** There should be two additional arguments.
|
|
@@ -1956,6 +2062,7 @@
|
|
** following this call. The second parameter may be a NULL pointer, in
|
|
** which case the trigger setting is not reported back. </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
|
** <dd> ^This option is used to enable or disable the two-argument
|
|
** version of the [fts3_tokenizer()] function which is part of the
|
|
@@ -1969,6 +2076,7 @@
|
|
** following this call. The second parameter may be a NULL pointer, in
|
|
** which case the new setting is not reported back. </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
|
|
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
|
|
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
|
|
** interface independently of the [load_extension()] SQL function.
|
|
@@ -1986,7 +2094,7 @@
|
|
** be a NULL pointer, in which case the new setting is not reported back.
|
|
** </dd>
|
|
**
|
|
-** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
|
+** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
|
** <dd> ^This option is used to change the name of the "main" database
|
|
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
|
** which will become the new schema name in place of "main". ^SQLite
|
|
@@ -1995,6 +2103,7 @@
|
|
** until after the database connection closes.
|
|
** </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
|
|
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
|
|
** <dd> Usually, when a database in wal mode is closed or detached from a
|
|
** database handle, SQLite checks if this will mean that there are now no
|
|
@@ -2001,13 +2110,14 @@
|
|
** connections at all to the database. If so, it performs a checkpoint
|
|
** operation before closing the connection. This option may be used to
|
|
** override this behaviour. The first parameter passed to this operation
|
|
-** is an integer - non-zero to disable checkpoints-on-close, or zero (the
|
|
-** default) to enable them. The second parameter is a pointer to an integer
|
|
+** is an integer - positive to disable checkpoints-on-close, or zero (the
|
|
+** default) to enable them, and negative to leave the setting unchanged.
|
|
+** The second parameter is a pointer to an integer
|
|
** into which is written 0 or 1 to indicate whether checkpoints-on-close
|
|
** have been disabled - 0 if they are not disabled, 1 if they are.
|
|
** </dd>
|
|
**
|
|
-** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
|
|
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]] <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
|
|
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
|
|
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
|
|
** a single SQL query statement will always use the same algorithm regardless
|
|
@@ -2016,8 +2126,57 @@
|
|
** slower. But the QPSG has the advantage of more predictable behavior. With
|
|
** the QPSG active, SQLite will always use the same query plan in the field as
|
|
** was used during testing in the lab.
|
|
+** The first argument to this setting is an integer which is 0 to disable
|
|
+** the QPSG, positive to enable QPSG, or negative to leave the setting
|
|
+** unchanged. The second parameter is a pointer to an integer into which
|
|
+** is written 0 or 1 to indicate whether the QPSG is disabled or enabled
|
|
+** following this call.
|
|
** </dd>
|
|
**
|
|
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
|
|
+** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
|
|
+** include output for any operations performed by trigger programs. This
|
|
+** option is used to set or clear (the default) a flag that governs this
|
|
+** behavior. The first parameter passed to this operation is an integer -
|
|
+** positive to enable output for trigger programs, or zero to disable it,
|
|
+** or negative to leave the setting unchanged.
|
|
+** The second parameter is a pointer to an integer into which is written
|
|
+** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
|
|
+** it is not disabled, 1 if it is.
|
|
+** </dd>
|
|
+**
|
|
+** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
|
|
+** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
|
|
+** [VACUUM] in order to reset a database back to an empty database
|
|
+** with no schema and no content. The following process works even for
|
|
+** a badly corrupted database file:
|
|
+** <ol>
|
|
+** <li> If the database connection is newly opened, make sure it has read the
|
|
+** database schema by preparing then discarding some query against the
|
|
+** database, or calling sqlite3_table_column_metadata(), ignoring any
|
|
+** errors. This step is only necessary if the application desires to keep
|
|
+** the database in WAL mode after the reset if it was in WAL mode before
|
|
+** the reset.
|
|
+** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
|
|
+** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0);
|
|
+** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
|
+** </ol>
|
|
+** Because resetting a database is destructive and irreversible, the
|
|
+** process requires the use of this obscure API and multiple steps to help
|
|
+** ensure that it does not happen by accident.
|
|
+**
|
|
+** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
|
|
+** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
|
|
+** "defensive" flag for a database connection. When the defensive
|
|
+** flag is enabled, language features that allow ordinary SQL to
|
|
+** deliberately corrupt the database file are disabled. The disabled
|
|
+** features include but are not limited to the following:
|
|
+** <ul>
|
|
+** <li> The [PRAGMA writable_schema=ON] statement.
|
|
+** <li> Writes to the [sqlite_dbpage] virtual table.
|
|
+** <li> Direct writes to [shadow tables].
|
|
+** </ul>
|
|
+** </dd>
|
|
** </dl>
|
|
*/
|
|
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
|
@@ -2028,8 +2187,11 @@
|
|
#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
|
|
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
|
|
#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */
|
|
+#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
|
|
+#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
|
|
+#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
|
|
+#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
|
|
|
|
-
|
|
/*
|
|
** CAPI3REF: Enable Or Disable Extended Result Codes
|
|
** METHOD: sqlite3
|
|
@@ -2156,12 +2318,17 @@
|
|
** program, the value returned reflects the number of rows modified by the
|
|
** previous INSERT, UPDATE or DELETE statement within the same trigger.
|
|
**
|
|
-** See also the [sqlite3_total_changes()] interface, the
|
|
-** [count_changes pragma], and the [changes() SQL function].
|
|
-**
|
|
** If a separate thread makes changes on the same database connection
|
|
** while [sqlite3_changes()] is running then the value returned
|
|
** is unpredictable and not meaningful.
|
|
+**
|
|
+** See also:
|
|
+** <ul>
|
|
+** <li> the [sqlite3_total_changes()] interface
|
|
+** <li> the [count_changes pragma]
|
|
+** <li> the [changes() SQL function]
|
|
+** <li> the [data_version pragma]
|
|
+** </ul>
|
|
*/
|
|
SQLITE_API int sqlite3_changes(sqlite3*);
|
|
|
|
@@ -2179,13 +2346,26 @@
|
|
** count, but those made as part of REPLACE constraint resolution are
|
|
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
|
|
** are not counted.
|
|
+**
|
|
+** This the [sqlite3_total_changes(D)] interface only reports the number
|
|
+** of rows that changed due to SQL statement run against database
|
|
+** connection D. Any changes by other database connections are ignored.
|
|
+** To detect changes against a database file from other database
|
|
+** connections use the [PRAGMA data_version] command or the
|
|
+** [SQLITE_FCNTL_DATA_VERSION] [file control].
|
|
**
|
|
-** See also the [sqlite3_changes()] interface, the
|
|
-** [count_changes pragma], and the [total_changes() SQL function].
|
|
-**
|
|
** If a separate thread makes changes on the same database connection
|
|
** while [sqlite3_total_changes()] is running then the value
|
|
** returned is unpredictable and not meaningful.
|
|
+**
|
|
+** See also:
|
|
+** <ul>
|
|
+** <li> the [sqlite3_changes()] interface
|
|
+** <li> the [count_changes pragma]
|
|
+** <li> the [changes() SQL function]
|
|
+** <li> the [data_version pragma]
|
|
+** <li> the [SQLITE_FCNTL_DATA_VERSION] [file control]
|
|
+** </ul>
|
|
*/
|
|
SQLITE_API int sqlite3_total_changes(sqlite3*);
|
|
|
|
@@ -2434,16 +2614,16 @@
|
|
**
|
|
** These routines are work-alikes of the "printf()" family of functions
|
|
** from the standard C library.
|
|
-** These routines understand most of the common K&R formatting options,
|
|
-** plus some additional non-standard formats, detailed below.
|
|
-** Note that some of the more obscure formatting options from recent
|
|
-** C-library standards are omitted from this implementation.
|
|
+** These routines understand most of the common formatting options from
|
|
+** the standard library printf()
|
|
+** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]).
|
|
+** See the [built-in printf()] documentation for details.
|
|
**
|
|
** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
|
|
-** results into memory obtained from [sqlite3_malloc()].
|
|
+** results into memory obtained from [sqlite3_malloc64()].
|
|
** The strings returned by these two routines should be
|
|
** released by [sqlite3_free()]. ^Both routines return a
|
|
-** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
|
|
+** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough
|
|
** memory to hold the resulting string.
|
|
**
|
|
** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from
|
|
@@ -2467,71 +2647,7 @@
|
|
**
|
|
** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf().
|
|
**
|
|
-** These routines all implement some additional formatting
|
|
-** options that are useful for constructing SQL statements.
|
|
-** All of the usual printf() formatting options apply. In addition, there
|
|
-** is are "%q", "%Q", "%w" and "%z" options.
|
|
-**
|
|
-** ^(The %q option works like %s in that it substitutes a nul-terminated
|
|
-** string from the argument list. But %q also doubles every '\'' character.
|
|
-** %q is designed for use inside a string literal.)^ By doubling each '\''
|
|
-** character it escapes that character and allows it to be inserted into
|
|
-** the string.
|
|
-**
|
|
-** For example, assume the string variable zText contains text as follows:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** char *zText = "It's a happy day!";
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** One can use this text in an SQL statement as follows:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
|
|
-** sqlite3_exec(db, zSQL, 0, 0, 0);
|
|
-** sqlite3_free(zSQL);
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** Because the %q format string is used, the '\'' character in zText
|
|
-** is escaped and the SQL generated is as follows:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** INSERT INTO table1 VALUES('It''s a happy day!')
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** This is correct. Had we used %s instead of %q, the generated SQL
|
|
-** would have looked like this:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** INSERT INTO table1 VALUES('It's a happy day!');
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** This second example is an SQL syntax error. As a general rule you should
|
|
-** always use %q instead of %s when inserting text into a string literal.
|
|
-**
|
|
-** ^(The %Q option works like %q except it also adds single quotes around
|
|
-** the outside of the total string. Additionally, if the parameter in the
|
|
-** argument list is a NULL pointer, %Q substitutes the text "NULL" (without
|
|
-** single quotes).)^ So, for example, one could say:
|
|
-**
|
|
-** <blockquote><pre>
|
|
-** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
|
|
-** sqlite3_exec(db, zSQL, 0, 0, 0);
|
|
-** sqlite3_free(zSQL);
|
|
-** </pre></blockquote>
|
|
-**
|
|
-** The code above will render a correct SQL statement in the zSQL
|
|
-** variable even if the zText variable is a NULL pointer.
|
|
-**
|
|
-** ^(The "%w" formatting option is like "%q" except that it expects to
|
|
-** be contained within double-quotes instead of single quotes, and it
|
|
-** escapes the double-quote character instead of the single-quote
|
|
-** character.)^ The "%w" formatting option is intended for safely inserting
|
|
-** table and column names into a constructed SQL statement.
|
|
-**
|
|
-** ^(The "%z" formatting option works like "%s" but with the
|
|
-** addition that after the string has been read and copied into
|
|
-** the result, [sqlite3_free()] is called on the input string.)^
|
|
+** See also: [built-in printf()], [printf() SQL function]
|
|
*/
|
|
SQLITE_API char *sqlite3_mprintf(const char*,...);
|
|
SQLITE_API char *sqlite3_vmprintf(const char*, va_list);
|
|
@@ -2889,8 +3005,8 @@
|
|
** KEYWORDS: SQLITE_TRACE
|
|
**
|
|
** These constants identify classes of events that can be monitored
|
|
-** using the [sqlite3_trace_v2()] tracing logic. The third argument
|
|
-** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of
|
|
+** using the [sqlite3_trace_v2()] tracing logic. The M argument
|
|
+** to [sqlite3_trace_v2(D,M,X,P)] is an OR-ed combination of one or more of
|
|
** the following constants. ^The first argument to the trace callback
|
|
** is one of the following constants.
|
|
**
|
|
@@ -3099,10 +3215,10 @@
|
|
** ^If [URI filename] interpretation is enabled, and the filename argument
|
|
** begins with "file:", then the filename is interpreted as a URI. ^URI
|
|
** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is
|
|
-** set in the fourth argument to sqlite3_open_v2(), or if it has
|
|
+** set in the third argument to sqlite3_open_v2(), or if it has
|
|
** been enabled globally using the [SQLITE_CONFIG_URI] option with the
|
|
** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option.
|
|
-** As of SQLite version 3.7.7, URI filename interpretation is turned off
|
|
+** URI filename interpretation is turned off
|
|
** by default, but future releases of SQLite might enable URI filename
|
|
** interpretation by default. See "[URI filenames]" for additional
|
|
** information.
|
|
@@ -3305,13 +3421,24 @@
|
|
** [database connection] D failed, then the sqlite3_errcode(D) interface
|
|
** returns the numeric [result code] or [extended result code] for that
|
|
** API call.
|
|
-** If the most recent API call was successful,
|
|
-** then the return value from sqlite3_errcode() is undefined.
|
|
** ^The sqlite3_extended_errcode()
|
|
** interface is the same except that it always returns the
|
|
** [extended result code] even when extended result codes are
|
|
** disabled.
|
|
**
|
|
+** The values returned by sqlite3_errcode() and/or
|
|
+** sqlite3_extended_errcode() might change with each API call.
|
|
+** Except, there are some interfaces that are guaranteed to never
|
|
+** change the value of the error code. The error-code preserving
|
|
+** interfaces are:
|
|
+**
|
|
+** <ul>
|
|
+** <li> sqlite3_errcode()
|
|
+** <li> sqlite3_extended_errcode()
|
|
+** <li> sqlite3_errmsg()
|
|
+** <li> sqlite3_errmsg16()
|
|
+** </ul>
|
|
+**
|
|
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
|
|
** text that describes the error, as either UTF-8 or UTF-16 respectively.
|
|
** ^(Memory to hold the error message string is managed internally.
|
|
@@ -3501,9 +3628,19 @@
|
|
** on this hint by avoiding the use of [lookaside memory] so as not to
|
|
** deplete the limited store of lookaside memory. Future versions of
|
|
** SQLite may act on this hint differently.
|
|
+**
|
|
+** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
|
|
+** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
|
|
+** representation of the SQL statement should be calculated and then
|
|
+** associated with the prepared statement, which can be obtained via
|
|
+** the [sqlite3_normalized_sql()] interface.)^ The semantics used to
|
|
+** normalize a SQL statement are unspecified and subject to change.
|
|
+** At a minimum, literal values will be replaced with suitable
|
|
+** placeholders.
|
|
** </dl>
|
|
*/
|
|
#define SQLITE_PREPARE_PERSISTENT 0x01
|
|
+#define SQLITE_PREPARE_NORMALIZE 0x02
|
|
|
|
/*
|
|
** CAPI3REF: Compiling An SQL Statement
|
|
@@ -3597,6 +3734,7 @@
|
|
** or [GLOB] operator or if the parameter is compared to an indexed column
|
|
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
|
|
** </li>
|
|
+** </ol>
|
|
**
|
|
** <p>^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having
|
|
** the extra prepFlags parameter, which is a bit array consisting of zero or
|
|
@@ -3603,7 +3741,6 @@
|
|
** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The
|
|
** sqlite3_prepare_v2() interface works exactly the same as
|
|
** sqlite3_prepare_v3() with a zero prepFlags parameter.
|
|
-** </ol>
|
|
*/
|
|
SQLITE_API int sqlite3_prepare(
|
|
sqlite3 *db, /* Database handle */
|
|
@@ -3661,6 +3798,11 @@
|
|
** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
|
|
** string containing the SQL text of prepared statement P with
|
|
** [bound parameters] expanded.
|
|
+** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8
|
|
+** string containing the normalized SQL text of prepared statement P. The
|
|
+** semantics used to normalize a SQL statement are unspecified and subject
|
|
+** to change. At a minimum, literal values will be replaced with suitable
|
|
+** placeholders.
|
|
**
|
|
** ^(For example, if a prepared statement is created using the SQL
|
|
** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
|
|
@@ -3676,8 +3818,9 @@
|
|
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
|
** option causes sqlite3_expanded_sql() to always return NULL.
|
|
**
|
|
-** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
|
|
-** automatically freed when the prepared statement is finalized.
|
|
+** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P)
|
|
+** are managed by SQLite and are automatically freed when the prepared
|
|
+** statement is finalized.
|
|
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
|
|
** is obtained from [sqlite3_malloc()] and must be free by the application
|
|
** by passing it to [sqlite3_free()].
|
|
@@ -3684,6 +3827,7 @@
|
|
*/
|
|
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
|
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
|
|
+SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
|
|
|
|
/*
|
|
** CAPI3REF: Determine If An SQL Statement Writes The Database
|
|
@@ -3776,8 +3920,9 @@
|
|
** implementation of [application-defined SQL functions] are protected.
|
|
** ^The sqlite3_value object returned by
|
|
** [sqlite3_column_value()] is unprotected.
|
|
-** Unprotected sqlite3_value objects may only be used with
|
|
-** [sqlite3_result_value()] and [sqlite3_bind_value()].
|
|
+** Unprotected sqlite3_value objects may only be used as arguments
|
|
+** to [sqlite3_result_value()], [sqlite3_bind_value()], and
|
|
+** [sqlite3_value_dup()].
|
|
** The [sqlite3_value_blob | sqlite3_value_type()] family of
|
|
** interfaces require protected sqlite3_value objects.
|
|
*/
|
|
@@ -4199,7 +4344,7 @@
|
|
** other than [SQLITE_ROW] before any subsequent invocation of
|
|
** sqlite3_step(). Failure to reset the prepared statement using
|
|
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
|
-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
|
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
|
** sqlite3_step() began
|
|
** calling [sqlite3_reset()] automatically in this circumstance rather
|
|
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
|
@@ -4464,11 +4609,25 @@
|
|
** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
|
|
** [sqlite3_free()].
|
|
**
|
|
-** ^(If a memory allocation error occurs during the evaluation of any
|
|
-** of these routines, a default value is returned. The default value
|
|
-** is either the integer 0, the floating point number 0.0, or a NULL
|
|
-** pointer. Subsequent calls to [sqlite3_errcode()] will return
|
|
-** [SQLITE_NOMEM].)^
|
|
+** As long as the input parameters are correct, these routines will only
|
|
+** fail if an out-of-memory error occurs during a format conversion.
|
|
+** Only the following subset of interfaces are subject to out-of-memory
|
|
+** errors:
|
|
+**
|
|
+** <ul>
|
|
+** <li> sqlite3_column_blob()
|
|
+** <li> sqlite3_column_text()
|
|
+** <li> sqlite3_column_text16()
|
|
+** <li> sqlite3_column_bytes()
|
|
+** <li> sqlite3_column_bytes16()
|
|
+** </ul>
|
|
+**
|
|
+** If an out-of-memory error occurs, then the return value from these
|
|
+** routines is the same as if the column had contained an SQL NULL value.
|
|
+** Valid SQL NULL returns can be distinguished from out-of-memory errors
|
|
+** by invoking the [sqlite3_errcode()] immediately after the suspect
|
|
+** return value is obtained and before any
|
|
+** other SQLite interface is called on the same [database connection].
|
|
*/
|
|
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
|
|
SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);
|
|
@@ -4545,11 +4704,13 @@
|
|
**
|
|
** ^These functions (collectively known as "function creation routines")
|
|
** are used to add SQL functions or aggregates or to redefine the behavior
|
|
-** of existing SQL functions or aggregates. The only differences between
|
|
-** these routines are the text encoding expected for
|
|
-** the second parameter (the name of the function being created)
|
|
-** and the presence or absence of a destructor callback for
|
|
-** the application data pointer.
|
|
+** of existing SQL functions or aggregates. The only differences between
|
|
+** the three "sqlite3_create_function*" routines are the text encoding
|
|
+** expected for the second parameter (the name of the function being
|
|
+** created) and the presence or absence of a destructor callback for
|
|
+** the application data pointer. Function sqlite3_create_window_function()
|
|
+** is similar, but allows the user to supply the extra callback functions
|
|
+** needed by [aggregate window functions].
|
|
**
|
|
** ^The first parameter is the [database connection] to which the SQL
|
|
** function is to be added. ^If an application uses more than one database
|
|
@@ -4595,7 +4756,8 @@
|
|
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
|
|
** function can gain access to this pointer using [sqlite3_user_data()].)^
|
|
**
|
|
-** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are
|
|
+** ^The sixth, seventh and eighth parameters passed to the three
|
|
+** "sqlite3_create_function*" functions, xFunc, xStep and xFinal, are
|
|
** pointers to C-language functions that implement the SQL function or
|
|
** aggregate. ^A scalar SQL function requires an implementation of the xFunc
|
|
** callback only; NULL pointers must be passed as the xStep and xFinal
|
|
@@ -4604,16 +4766,25 @@
|
|
** SQL function or aggregate, pass NULL pointers for all three function
|
|
** callbacks.
|
|
**
|
|
-** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL,
|
|
-** then it is destructor for the application data pointer.
|
|
-** The destructor is invoked when the function is deleted, either by being
|
|
-** overloaded or when the database connection closes.)^
|
|
-** ^The destructor is also invoked if the call to
|
|
-** sqlite3_create_function_v2() fails.
|
|
-** ^When the destructor callback of the tenth parameter is invoked, it
|
|
-** is passed a single argument which is a copy of the application data
|
|
-** pointer which was the fifth parameter to sqlite3_create_function_v2().
|
|
+** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue
|
|
+** and xInverse) passed to sqlite3_create_window_function are pointers to
|
|
+** C-language callbacks that implement the new function. xStep and xFinal
|
|
+** must both be non-NULL. xValue and xInverse may either both be NULL, in
|
|
+** which case a regular aggregate function is created, or must both be
|
|
+** non-NULL, in which case the new function may be used as either an aggregate
|
|
+** or aggregate window function. More details regarding the implementation
|
|
+** of aggregate window functions are
|
|
+** [user-defined window functions|available here].
|
|
**
|
|
+** ^(If the final parameter to sqlite3_create_function_v2() or
|
|
+** sqlite3_create_window_function() is not NULL, then it is destructor for
|
|
+** the application data pointer. The destructor is invoked when the function
|
|
+** is deleted, either by being overloaded or when the database connection
|
|
+** closes.)^ ^The destructor is also invoked if the call to
|
|
+** sqlite3_create_function_v2() fails. ^When the destructor callback is
|
|
+** invoked, it is passed a single argument which is a copy of the application
|
|
+** data pointer which was the fifth parameter to sqlite3_create_function_v2().
|
|
+**
|
|
** ^It is permitted to register multiple implementations of the same
|
|
** functions with the same name but with either differing numbers of
|
|
** arguments or differing preferred text encodings. ^SQLite will use
|
|
@@ -4665,6 +4836,18 @@
|
|
void (*xFinal)(sqlite3_context*),
|
|
void(*xDestroy)(void*)
|
|
);
|
|
+SQLITE_API int sqlite3_create_window_function(
|
|
+ sqlite3 *db,
|
|
+ const char *zFunctionName,
|
|
+ int nArg,
|
|
+ int eTextRep,
|
|
+ void *pApp,
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void (*xFinal)(sqlite3_context*),
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void(*xDestroy)(void*)
|
|
+);
|
|
|
|
/*
|
|
** CAPI3REF: Text Encodings
|
|
@@ -4735,6 +4918,9 @@
|
|
** datatype of the value
|
|
** <tr><td><b>sqlite3_value_numeric_type </b>
|
|
** <td>→ <td>Best numeric datatype of the value
|
|
+** <tr><td><b>sqlite3_value_nochange </b>
|
|
+** <td>→ <td>True if the column is unchanged in an UPDATE
|
|
+** against a virtual table.
|
|
** </table></blockquote>
|
|
**
|
|
** <b>Details:</b>
|
|
@@ -4783,6 +4969,19 @@
|
|
** then the conversion is performed. Otherwise no conversion occurs.
|
|
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
|
|
**
|
|
+** ^Within the [xUpdate] method of a [virtual table], the
|
|
+** sqlite3_value_nochange(X) interface returns true if and only if
|
|
+** the column corresponding to X is unchanged by the UPDATE operation
|
|
+** that the xUpdate method call was invoked to implement and if
|
|
+** and the prior [xColumn] method call that was invoked to extracted
|
|
+** the value for that column returned without setting a result (probably
|
|
+** because it queried [sqlite3_vtab_nochange()] and found that the column
|
|
+** was unchanging). ^Within an [xUpdate] method, any value for which
|
|
+** sqlite3_value_nochange(X) is true will in all other respects appear
|
|
+** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other
|
|
+** than within an [xUpdate] method call for an UPDATE statement, then
|
|
+** the return value is arbitrary and meaningless.
|
|
+**
|
|
** Please pay particular attention to the fact that the pointer returned
|
|
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
|
|
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
|
|
@@ -4791,6 +4990,28 @@
|
|
**
|
|
** These routines must be called from the same thread as
|
|
** the SQL function that supplied the [sqlite3_value*] parameters.
|
|
+**
|
|
+** As long as the input parameter is correct, these routines can only
|
|
+** fail if an out-of-memory error occurs during a format conversion.
|
|
+** Only the following subset of interfaces are subject to out-of-memory
|
|
+** errors:
|
|
+**
|
|
+** <ul>
|
|
+** <li> sqlite3_value_blob()
|
|
+** <li> sqlite3_value_text()
|
|
+** <li> sqlite3_value_text16()
|
|
+** <li> sqlite3_value_text16le()
|
|
+** <li> sqlite3_value_text16be()
|
|
+** <li> sqlite3_value_bytes()
|
|
+** <li> sqlite3_value_bytes16()
|
|
+** </ul>
|
|
+**
|
|
+** If an out-of-memory error occurs, then the return value from these
|
|
+** routines is the same as if the column had contained an SQL NULL value.
|
|
+** Valid SQL NULL returns can be distinguished from out-of-memory errors
|
|
+** by invoking the [sqlite3_errcode()] immediately after the suspect
|
|
+** return value is obtained and before any
|
|
+** other SQLite interface is called on the same [database connection].
|
|
*/
|
|
SQLITE_API const void *sqlite3_value_blob(sqlite3_value*);
|
|
SQLITE_API double sqlite3_value_double(sqlite3_value*);
|
|
@@ -4805,6 +5026,7 @@
|
|
SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
|
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
|
+SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
|
|
|
/*
|
|
** CAPI3REF: Finding The Subtype Of SQL Values
|
|
@@ -5461,6 +5683,41 @@
|
|
SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory;
|
|
|
|
/*
|
|
+** CAPI3REF: Win32 Specific Interface
|
|
+**
|
|
+** These interfaces are available only on Windows. The
|
|
+** [sqlite3_win32_set_directory] interface is used to set the value associated
|
|
+** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to
|
|
+** zValue, depending on the value of the type parameter. The zValue parameter
|
|
+** should be NULL to cause the previous value to be freed via [sqlite3_free];
|
|
+** a non-NULL value will be copied into memory obtained from [sqlite3_malloc]
|
|
+** prior to being used. The [sqlite3_win32_set_directory] interface returns
|
|
+** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported,
|
|
+** or [SQLITE_NOMEM] if memory could not be allocated. The value of the
|
|
+** [sqlite3_data_directory] variable is intended to act as a replacement for
|
|
+** the current directory on the sub-platforms of Win32 where that concept is
|
|
+** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and
|
|
+** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the
|
|
+** sqlite3_win32_set_directory interface except the string parameter must be
|
|
+** UTF-8 or UTF-16, respectively.
|
|
+*/
|
|
+SQLITE_API int sqlite3_win32_set_directory(
|
|
+ unsigned long type, /* Identifier for directory being set or reset */
|
|
+ void *zValue /* New value for directory being set or reset */
|
|
+);
|
|
+SQLITE_API int sqlite3_win32_set_directory8(unsigned long type, const char *zValue);
|
|
+SQLITE_API int sqlite3_win32_set_directory16(unsigned long type, const void *zValue);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Win32 Directory Types
|
|
+**
|
|
+** These macros are only available on Windows. They define the allowed values
|
|
+** for the type argument to the [sqlite3_win32_set_directory] interface.
|
|
+*/
|
|
+#define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1
|
|
+#define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2
|
|
+
|
|
+/*
|
|
** CAPI3REF: Test For Auto-Commit Mode
|
|
** KEYWORDS: {autocommit mode}
|
|
** METHOD: sqlite3
|
|
@@ -6060,6 +6317,9 @@
|
|
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
|
|
int (*xRelease)(sqlite3_vtab *pVTab, int);
|
|
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
|
|
+ /* The methods above are in versions 1 and 2 of the sqlite_module object.
|
|
+ ** Those below are for version 3 and greater. */
|
|
+ int (*xShadowName)(const char*);
|
|
};
|
|
|
|
/*
|
|
@@ -6192,6 +6452,10 @@
|
|
|
|
/*
|
|
** CAPI3REF: Virtual Table Scan Flags
|
|
+**
|
|
+** Virtual table implementations are allowed to set the
|
|
+** [sqlite3_index_info].idxFlags field to some combination of
|
|
+** these bits.
|
|
*/
|
|
#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
|
|
|
|
@@ -6203,15 +6467,21 @@
|
|
** an operator that is part of a constraint term in the wHERE clause of
|
|
** a query that uses a [virtual table].
|
|
*/
|
|
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
|
-#define SQLITE_INDEX_CONSTRAINT_GT 4
|
|
-#define SQLITE_INDEX_CONSTRAINT_LE 8
|
|
-#define SQLITE_INDEX_CONSTRAINT_LT 16
|
|
-#define SQLITE_INDEX_CONSTRAINT_GE 32
|
|
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
|
|
-#define SQLITE_INDEX_CONSTRAINT_LIKE 65
|
|
-#define SQLITE_INDEX_CONSTRAINT_GLOB 66
|
|
-#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
|
|
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
|
+#define SQLITE_INDEX_CONSTRAINT_GT 4
|
|
+#define SQLITE_INDEX_CONSTRAINT_LE 8
|
|
+#define SQLITE_INDEX_CONSTRAINT_LT 16
|
|
+#define SQLITE_INDEX_CONSTRAINT_GE 32
|
|
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
|
|
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
|
|
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
|
|
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
|
|
+#define SQLITE_INDEX_CONSTRAINT_NE 68
|
|
+#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
|
|
+#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
|
|
+#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
|
|
+#define SQLITE_INDEX_CONSTRAINT_IS 72
|
|
+#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
|
|
|
|
/*
|
|
** CAPI3REF: Register A Virtual Table Implementation
|
|
@@ -6888,6 +7158,7 @@
|
|
/*
|
|
** CAPI3REF: Low-Level Control Of Database Files
|
|
** METHOD: sqlite3
|
|
+** KEYWORDS: {file control}
|
|
**
|
|
** ^The [sqlite3_file_control()] interface makes a direct call to the
|
|
** xFileControl method for the [sqlite3_io_methods] object associated
|
|
@@ -6902,11 +7173,18 @@
|
|
** the xFileControl method. ^The return value of the xFileControl
|
|
** method becomes the return value of this routine.
|
|
**
|
|
-** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes
|
|
+** A few opcodes for [sqlite3_file_control()] are handled directly
|
|
+** by the SQLite core and never invoke the
|
|
+** sqlite3_io_methods.xFileControl method.
|
|
+** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes
|
|
** a pointer to the underlying [sqlite3_file] object to be written into
|
|
-** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER
|
|
-** case is a short-circuit path which does not actually invoke the
|
|
-** underlying sqlite3_io_methods.xFileControl method.
|
|
+** the space pointed to by the 4th parameter. The
|
|
+** [SQLITE_FCNTL_JOURNAL_POINTER] works similarly except that it returns
|
|
+** the [sqlite3_file] object associated with the journal file instead of
|
|
+** the main database. The [SQLITE_FCNTL_VFS_POINTER] opcode returns
|
|
+** a pointer to the underlying [sqlite3_vfs] object for the file.
|
|
+** The [SQLITE_FCNTL_DATA_VERSION] returns the data version counter
|
|
+** from the pager.
|
|
**
|
|
** ^If the second parameter (zDbName) does not match the name of any
|
|
** open database file, then SQLITE_ERROR is returned. ^This error
|
|
@@ -6916,7 +7194,7 @@
|
|
** an incorrect zDbName and an SQLITE_ERROR return from the underlying
|
|
** xFileControl method.
|
|
**
|
|
-** See also: [SQLITE_FCNTL_LOCKSTATE]
|
|
+** See also: [file control opcodes]
|
|
*/
|
|
SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);
|
|
|
|
@@ -6962,8 +7240,9 @@
|
|
#define SQLITE_TESTCTRL_ALWAYS 13
|
|
#define SQLITE_TESTCTRL_RESERVE 14
|
|
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
|
|
-#define SQLITE_TESTCTRL_ISKEYWORD 16
|
|
-#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
|
+#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
|
|
+#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
|
|
+#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
|
|
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
|
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
|
|
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
|
|
@@ -6973,9 +7252,193 @@
|
|
#define SQLITE_TESTCTRL_ISINIT 23
|
|
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
|
#define SQLITE_TESTCTRL_IMPOSTER 25
|
|
-#define SQLITE_TESTCTRL_LAST 25
|
|
+#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
|
+#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
|
|
|
/*
|
|
+** CAPI3REF: SQL Keyword Checking
|
|
+**
|
|
+** These routines provide access to the set of SQL language keywords
|
|
+** recognized by SQLite. Applications can uses these routines to determine
|
|
+** whether or not a specific identifier needs to be escaped (for example,
|
|
+** by enclosing in double-quotes) so as not to confuse the parser.
|
|
+**
|
|
+** The sqlite3_keyword_count() interface returns the number of distinct
|
|
+** keywords understood by SQLite.
|
|
+**
|
|
+** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
|
|
+** makes *Z point to that keyword expressed as UTF8 and writes the number
|
|
+** of bytes in the keyword into *L. The string that *Z points to is not
|
|
+** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
|
|
+** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z
|
|
+** or L are NULL or invalid pointers then calls to
|
|
+** sqlite3_keyword_name(N,Z,L) result in undefined behavior.
|
|
+**
|
|
+** The sqlite3_keyword_check(Z,L) interface checks to see whether or not
|
|
+** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero
|
|
+** if it is and zero if not.
|
|
+**
|
|
+** The parser used by SQLite is forgiving. It is often possible to use
|
|
+** a keyword as an identifier as long as such use does not result in a
|
|
+** parsing ambiguity. For example, the statement
|
|
+** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and
|
|
+** creates a new table named "BEGIN" with three columns named
|
|
+** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid
|
|
+** using keywords as identifiers. Common techniques used to avoid keyword
|
|
+** name collisions include:
|
|
+** <ul>
|
|
+** <li> Put all identifier names inside double-quotes. This is the official
|
|
+** SQL way to escape identifier names.
|
|
+** <li> Put identifier names inside [...]. This is not standard SQL,
|
|
+** but it is what SQL Server does and so lots of programmers use this
|
|
+** technique.
|
|
+** <li> Begin every identifier with the letter "Z" as no SQL keywords start
|
|
+** with "Z".
|
|
+** <li> Include a digit somewhere in every identifier name.
|
|
+** </ul>
|
|
+**
|
|
+** Note that the number of keywords understood by SQLite can depend on
|
|
+** compile-time options. For example, "VACUUM" is not a keyword if
|
|
+** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also,
|
|
+** new keywords may be added to future releases of SQLite.
|
|
+*/
|
|
+SQLITE_API int sqlite3_keyword_count(void);
|
|
+SQLITE_API int sqlite3_keyword_name(int,const char**,int*);
|
|
+SQLITE_API int sqlite3_keyword_check(const char*,int);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Dynamic String Object
|
|
+** KEYWORDS: {dynamic string}
|
|
+**
|
|
+** An instance of the sqlite3_str object contains a dynamically-sized
|
|
+** string under construction.
|
|
+**
|
|
+** The lifecycle of an sqlite3_str object is as follows:
|
|
+** <ol>
|
|
+** <li> ^The sqlite3_str object is created using [sqlite3_str_new()].
|
|
+** <li> ^Text is appended to the sqlite3_str object using various
|
|
+** methods, such as [sqlite3_str_appendf()].
|
|
+** <li> ^The sqlite3_str object is destroyed and the string it created
|
|
+** is returned using the [sqlite3_str_finish()] interface.
|
|
+** </ol>
|
|
+*/
|
|
+typedef struct sqlite3_str sqlite3_str;
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Create A New Dynamic String Object
|
|
+** CONSTRUCTOR: sqlite3_str
|
|
+**
|
|
+** ^The [sqlite3_str_new(D)] interface allocates and initializes
|
|
+** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
|
|
+** [sqlite3_str_new()] must be freed by a subsequent call to
|
|
+** [sqlite3_str_finish(X)].
|
|
+**
|
|
+** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
|
|
+** valid [sqlite3_str] object, though in the event of an out-of-memory
|
|
+** error the returned object might be a special singleton that will
|
|
+** silently reject new text, always return SQLITE_NOMEM from
|
|
+** [sqlite3_str_errcode()], always return 0 for
|
|
+** [sqlite3_str_length()], and always return NULL from
|
|
+** [sqlite3_str_finish(X)]. It is always safe to use the value
|
|
+** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
|
|
+** to any of the other [sqlite3_str] methods.
|
|
+**
|
|
+** The D parameter to [sqlite3_str_new(D)] may be NULL. If the
|
|
+** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum
|
|
+** length of the string contained in the [sqlite3_str] object will be
|
|
+** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead
|
|
+** of [SQLITE_MAX_LENGTH].
|
|
+*/
|
|
+SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Finalize A Dynamic String
|
|
+** DESTRUCTOR: sqlite3_str
|
|
+**
|
|
+** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X
|
|
+** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()]
|
|
+** that contains the constructed string. The calling application should
|
|
+** pass the returned value to [sqlite3_free()] to avoid a memory leak.
|
|
+** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
|
|
+** errors were encountered during construction of the string. ^The
|
|
+** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
|
|
+** string in [sqlite3_str] object X is zero bytes long.
|
|
+*/
|
|
+SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Add Content To A Dynamic String
|
|
+** METHOD: sqlite3_str
|
|
+**
|
|
+** These interfaces add content to an sqlite3_str object previously obtained
|
|
+** from [sqlite3_str_new()].
|
|
+**
|
|
+** ^The [sqlite3_str_appendf(X,F,...)] and
|
|
+** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
|
|
+** functionality of SQLite to append formatted text onto the end of
|
|
+** [sqlite3_str] object X.
|
|
+**
|
|
+** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
|
|
+** onto the end of the [sqlite3_str] object X. N must be non-negative.
|
|
+** S must contain at least N non-zero bytes of content. To append a
|
|
+** zero-terminated string in its entirety, use the [sqlite3_str_appendall()]
|
|
+** method instead.
|
|
+**
|
|
+** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of
|
|
+** zero-terminated string S onto the end of [sqlite3_str] object X.
|
|
+**
|
|
+** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the
|
|
+** single-byte character C onto the end of [sqlite3_str] object X.
|
|
+** ^This method can be used, for example, to add whitespace indentation.
|
|
+**
|
|
+** ^The [sqlite3_str_reset(X)] method resets the string under construction
|
|
+** inside [sqlite3_str] object X back to zero bytes in length.
|
|
+**
|
|
+** These methods do not return a result code. ^If an error occurs, that fact
|
|
+** is recorded in the [sqlite3_str] object and can be recovered by a
|
|
+** subsequent call to [sqlite3_str_errcode(X)].
|
|
+*/
|
|
+SQLITE_API void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...);
|
|
+SQLITE_API void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list);
|
|
+SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N);
|
|
+SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn);
|
|
+SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C);
|
|
+SQLITE_API void sqlite3_str_reset(sqlite3_str*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Status Of A Dynamic String
|
|
+** METHOD: sqlite3_str
|
|
+**
|
|
+** These interfaces return the current status of an [sqlite3_str] object.
|
|
+**
|
|
+** ^If any prior errors have occurred while constructing the dynamic string
|
|
+** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return
|
|
+** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns
|
|
+** [SQLITE_NOMEM] following any out-of-memory error, or
|
|
+** [SQLITE_TOOBIG] if the size of the dynamic string exceeds
|
|
+** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors.
|
|
+**
|
|
+** ^The [sqlite3_str_length(X)] method returns the current length, in bytes,
|
|
+** of the dynamic string under construction in [sqlite3_str] object X.
|
|
+** ^The length returned by [sqlite3_str_length(X)] does not include the
|
|
+** zero-termination byte.
|
|
+**
|
|
+** ^The [sqlite3_str_value(X)] method returns a pointer to the current
|
|
+** content of the dynamic string under construction in X. The value
|
|
+** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
|
|
+** and might be freed or altered by any subsequent method on the same
|
|
+** [sqlite3_str] object. Applications must not used the pointer returned
|
|
+** [sqlite3_str_value(X)] after any subsequent method call on the same
|
|
+** object. ^Applications may change the content of the string returned
|
|
+** by [sqlite3_str_value(X)] as long as they do not write into any bytes
|
|
+** outside the range of 0 to [sqlite3_str_length(X)] and do not read or
|
|
+** write any byte after any subsequent sqlite3_str method call.
|
|
+*/
|
|
+SQLITE_API int sqlite3_str_errcode(sqlite3_str*);
|
|
+SQLITE_API int sqlite3_str_length(sqlite3_str*);
|
|
+SQLITE_API char *sqlite3_str_value(sqlite3_str*);
|
|
+
|
|
+/*
|
|
** CAPI3REF: SQLite Runtime Status
|
|
**
|
|
** ^These interfaces are used to retrieve runtime status information
|
|
@@ -7022,8 +7485,7 @@
|
|
** <dd>This parameter is the current amount of memory checked out
|
|
** using [sqlite3_malloc()], either directly or indirectly. The
|
|
** figure includes calls made to [sqlite3_malloc()] by the application
|
|
-** and internal memory usage by the SQLite library. Scratch memory
|
|
-** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache
|
|
+** and internal memory usage by the SQLite library. Auxiliary page-cache
|
|
** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in
|
|
** this parameter. The amount returned is the sum of the allocation
|
|
** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^
|
|
@@ -7061,29 +7523,14 @@
|
|
** *pHighwater parameter to [sqlite3_status()] is of interest.
|
|
** The value written into the *pCurrent parameter is undefined.</dd>)^
|
|
**
|
|
-** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
|
|
-** <dd>This parameter returns the number of allocations used out of the
|
|
-** [scratch memory allocator] configured using
|
|
-** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not
|
|
-** in bytes. Since a single thread may only have one scratch allocation
|
|
-** outstanding at time, this parameter also reports the number of threads
|
|
-** using scratch memory at the same time.</dd>)^
|
|
+** [[SQLITE_STATUS_SCRATCH_USED]] <dt>SQLITE_STATUS_SCRATCH_USED</dt>
|
|
+** <dd>No longer used.</dd>
|
|
**
|
|
** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
|
|
-** <dd>This parameter returns the number of bytes of scratch memory
|
|
-** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH]
|
|
-** buffer and where forced to overflow to [sqlite3_malloc()]. The values
|
|
-** returned include overflows because the requested allocation was too
|
|
-** larger (that is, because the requested allocation was larger than the
|
|
-** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer
|
|
-** slots were available.
|
|
-** </dd>)^
|
|
+** <dd>No longer used.</dd>
|
|
**
|
|
-** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
|
|
-** <dd>This parameter records the largest memory allocation request
|
|
-** handed to [scratch memory allocator]. Only the value returned in the
|
|
-** *pHighwater parameter to [sqlite3_status()] is of interest.
|
|
-** The value written into the *pCurrent parameter is undefined.</dd>)^
|
|
+** [[SQLITE_STATUS_SCRATCH_SIZE]] <dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
|
|
+** <dd>No longer used.</dd>
|
|
**
|
|
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
|
|
** <dd>The *pHighwater parameter records the deepest parser stack.
|
|
@@ -7096,12 +7543,12 @@
|
|
#define SQLITE_STATUS_MEMORY_USED 0
|
|
#define SQLITE_STATUS_PAGECACHE_USED 1
|
|
#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2
|
|
-#define SQLITE_STATUS_SCRATCH_USED 3
|
|
-#define SQLITE_STATUS_SCRATCH_OVERFLOW 4
|
|
+#define SQLITE_STATUS_SCRATCH_USED 3 /* NOT USED */
|
|
+#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 /* NOT USED */
|
|
#define SQLITE_STATUS_MALLOC_SIZE 5
|
|
#define SQLITE_STATUS_PARSER_STACK 6
|
|
#define SQLITE_STATUS_PAGECACHE_SIZE 7
|
|
-#define SQLITE_STATUS_SCRATCH_SIZE 8
|
|
+#define SQLITE_STATUS_SCRATCH_SIZE 8 /* NOT USED */
|
|
#define SQLITE_STATUS_MALLOC_COUNT 9
|
|
|
|
/*
|
|
@@ -7224,6 +7671,15 @@
|
|
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
|
|
** </dd>
|
|
**
|
|
+** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
|
|
+** <dd>This parameter returns the number of dirty cache entries that have
|
|
+** been written to disk in the middle of a transaction due to the page
|
|
+** cache overflowing. Transactions are more efficient if they are written
|
|
+** to disk all at once. When pages spill mid-transaction, that introduces
|
|
+** additional overhead. This parameter can be used help identify
|
|
+** inefficiencies that can be resolve by increasing the cache size.
|
|
+** </dd>
|
|
+**
|
|
** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
|
|
** <dd>This parameter returns zero for the current value if and only if
|
|
** all foreign key constraints (deferred or immediate) have been
|
|
@@ -7243,7 +7699,8 @@
|
|
#define SQLITE_DBSTATUS_CACHE_WRITE 9
|
|
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
|
|
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
|
|
-#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */
|
|
+#define SQLITE_DBSTATUS_CACHE_SPILL 12
|
|
+#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
|
|
|
|
|
|
/*
|
|
@@ -8198,6 +8655,7 @@
|
|
** can use to customize and optimize their behavior.
|
|
**
|
|
** <dl>
|
|
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
|
|
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
|
|
** <dd>Calls of the form
|
|
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
|
|
@@ -8244,6 +8702,40 @@
|
|
SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
|
|
|
|
/*
|
|
+** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE
|
|
+**
|
|
+** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn]
|
|
+** method of a [virtual table], then it returns true if and only if the
|
|
+** column is being fetched as part of an UPDATE operation during which the
|
|
+** column value will not change. Applications might use this to substitute
|
|
+** a return value that is less expensive to compute and that the corresponding
|
|
+** [xUpdate] method understands as a "no-change" value.
|
|
+**
|
|
+** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
|
|
+** the column is not changed by the UPDATE statement, then the xColumn
|
|
+** method can optionally return without setting a result, without calling
|
|
+** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
|
|
+** In that case, [sqlite3_value_nochange(X)] will return true for the
|
|
+** same column in the [xUpdate] method.
|
|
+*/
|
|
+SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Determine The Collation For a Virtual Table Constraint
|
|
+**
|
|
+** This function may only be called from within a call to the [xBestIndex]
|
|
+** method of a [virtual table].
|
|
+**
|
|
+** The first argument must be the sqlite3_index_info object that is the
|
|
+** first parameter to the xBestIndex() method. The second argument must be
|
|
+** an index into the aConstraint[] array belonging to the sqlite3_index_info
|
|
+** structure passed to xBestIndex. This function returns a pointer to a buffer
|
|
+** containing the name of the collation sequence for the corresponding
|
|
+** constraint.
|
|
+*/
|
|
+SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
|
+
|
|
+/*
|
|
** CAPI3REF: Conflict resolution modes
|
|
** KEYWORDS: {conflict resolution mode}
|
|
**
|
|
@@ -8513,7 +9005,6 @@
|
|
/*
|
|
** CAPI3REF: Database Snapshot
|
|
** KEYWORDS: {snapshot} {sqlite3_snapshot}
|
|
-** EXPERIMENTAL
|
|
**
|
|
** An instance of the snapshot object records the state of a [WAL mode]
|
|
** database for some specific point in history.
|
|
@@ -8530,11 +9021,6 @@
|
|
** version of the database file so that it is possible to later open a new read
|
|
** transaction that sees that historical version of the database rather than
|
|
** the most recent version.
|
|
-**
|
|
-** The constructor for this object is [sqlite3_snapshot_get()]. The
|
|
-** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
|
|
-** to an historical snapshot (if possible). The destructor for
|
|
-** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
|
|
*/
|
|
typedef struct sqlite3_snapshot {
|
|
unsigned char hidden[48];
|
|
@@ -8542,7 +9028,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Record A Database Snapshot
|
|
-** EXPERIMENTAL
|
|
+** CONSTRUCTOR: sqlite3_snapshot
|
|
**
|
|
** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
|
|
** new [sqlite3_snapshot] object that records the current state of
|
|
@@ -8558,7 +9044,7 @@
|
|
** in this case.
|
|
**
|
|
** <ul>
|
|
-** <li> The database handle must be in [autocommit mode].
|
|
+** <li> The database handle must not be in [autocommit mode].
|
|
**
|
|
** <li> Schema S of [database connection] D must be a [WAL mode] database.
|
|
**
|
|
@@ -8581,7 +9067,7 @@
|
|
** to avoid a memory leak.
|
|
**
|
|
** The [sqlite3_snapshot_get()] interface is only available when the
|
|
-** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
|
+** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
|
|
sqlite3 *db,
|
|
@@ -8591,24 +9077,35 @@
|
|
|
|
/*
|
|
** CAPI3REF: Start a read transaction on an historical snapshot
|
|
-** EXPERIMENTAL
|
|
+** METHOD: sqlite3_snapshot
|
|
**
|
|
-** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
|
|
-** read transaction for schema S of
|
|
-** [database connection] D such that the read transaction
|
|
-** refers to historical [snapshot] P, rather than the most
|
|
-** recent change to the database.
|
|
-** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
|
|
-** or an appropriate [error code] if it fails.
|
|
+** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read
|
|
+** transaction or upgrades an existing one for schema S of
|
|
+** [database connection] D such that the read transaction refers to
|
|
+** historical [snapshot] P, rather than the most recent change to the
|
|
+** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK
|
|
+** on success or an appropriate [error code] if it fails.
|
|
**
|
|
-** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
|
|
-** the first operation following the [BEGIN] that takes the schema S
|
|
-** out of [autocommit mode].
|
|
-** ^In other words, schema S must not currently be in
|
|
-** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
|
|
-** database connection D must be out of [autocommit mode].
|
|
-** ^A [snapshot] will fail to open if it has been overwritten by a
|
|
-** [checkpoint].
|
|
+** ^In order to succeed, the database connection must not be in
|
|
+** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there
|
|
+** is already a read transaction open on schema S, then the database handle
|
|
+** must have no active statements (SELECT statements that have been passed
|
|
+** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()).
|
|
+** SQLITE_ERROR is returned if either of these conditions is violated, or
|
|
+** if schema S does not exist, or if the snapshot object is invalid.
|
|
+**
|
|
+** ^A call to sqlite3_snapshot_open() will fail to open if the specified
|
|
+** snapshot has been overwritten by a [checkpoint]. In this case
|
|
+** SQLITE_ERROR_SNAPSHOT is returned.
|
|
+**
|
|
+** If there is already a read transaction open when this function is
|
|
+** invoked, then the same read transaction remains open (on the same
|
|
+** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
|
|
+** is returned. If another error code - for example SQLITE_PROTOCOL or an
|
|
+** SQLITE_IOERR error code - is returned, then the final state of the
|
|
+** read transaction is undefined. If SQLITE_OK is returned, then the
|
|
+** read transaction is now open on database snapshot P.
|
|
+**
|
|
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
|
|
** database connection D does not know that the database file for
|
|
** schema S is in [WAL mode]. A database connection might not know
|
|
@@ -8619,7 +9116,7 @@
|
|
** database connection in order to make it ready to use snapshots.)
|
|
**
|
|
** The [sqlite3_snapshot_open()] interface is only available when the
|
|
-** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
|
+** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
|
|
sqlite3 *db,
|
|
@@ -8629,7 +9126,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Destroy a snapshot
|
|
-** EXPERIMENTAL
|
|
+** DESTRUCTOR: sqlite3_snapshot
|
|
**
|
|
** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
|
|
** The application must eventually free every [sqlite3_snapshot] object
|
|
@@ -8636,13 +9133,13 @@
|
|
** using this routine to avoid a memory leak.
|
|
**
|
|
** The [sqlite3_snapshot_free()] interface is only available when the
|
|
-** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
|
|
+** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
|
|
|
|
/*
|
|
** CAPI3REF: Compare the ages of two snapshot handles.
|
|
-** EXPERIMENTAL
|
|
+** METHOD: sqlite3_snapshot
|
|
**
|
|
** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
|
|
** of two valid snapshot handles.
|
|
@@ -8661,6 +9158,9 @@
|
|
** Otherwise, this API returns a negative value if P1 refers to an older
|
|
** snapshot than P2, zero if the two handles refer to the same database
|
|
** snapshot, and a positive value if P1 is a newer snapshot than P2.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_SNAPSHOT] option.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
|
sqlite3_snapshot *p1,
|
|
@@ -8669,27 +9169,152 @@
|
|
|
|
/*
|
|
** CAPI3REF: Recover snapshots from a wal file
|
|
-** EXPERIMENTAL
|
|
+** METHOD: sqlite3_snapshot
|
|
**
|
|
-** If all connections disconnect from a database file but do not perform
|
|
-** a checkpoint, the existing wal file is opened along with the database
|
|
-** file the next time the database is opened. At this point it is only
|
|
-** possible to successfully call sqlite3_snapshot_open() to open the most
|
|
-** recent snapshot of the database (the one at the head of the wal file),
|
|
-** even though the wal file may contain other valid snapshots for which
|
|
-** clients have sqlite3_snapshot handles.
|
|
+** If a [WAL file] remains on disk after all database connections close
|
|
+** (either through the use of the [SQLITE_FCNTL_PERSIST_WAL] [file control]
|
|
+** or because the last process to have the database opened exited without
|
|
+** calling [sqlite3_close()]) and a new connection is subsequently opened
|
|
+** on that database and [WAL file], the [sqlite3_snapshot_open()] interface
|
|
+** will only be able to open the last transaction added to the WAL file
|
|
+** even though the WAL file contains other valid transactions.
|
|
**
|
|
-** This function attempts to scan the wal file associated with database zDb
|
|
+** This function attempts to scan the WAL file associated with database zDb
|
|
** of database handle db and make all valid snapshots available to
|
|
** sqlite3_snapshot_open(). It is an error if there is already a read
|
|
-** transaction open on the database, or if the database is not a wal mode
|
|
+** transaction open on the database, or if the database is not a WAL mode
|
|
** database.
|
|
**
|
|
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_SNAPSHOT] option.
|
|
*/
|
|
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
|
|
|
|
/*
|
|
+** CAPI3REF: Serialize a database
|
|
+**
|
|
+** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
|
|
+** that is a serialization of the S database on [database connection] D.
|
|
+** If P is not a NULL pointer, then the size of the database in bytes
|
|
+** is written into *P.
|
|
+**
|
|
+** For an ordinary on-disk database file, the serialization is just a
|
|
+** copy of the disk file. For an in-memory database or a "TEMP" database,
|
|
+** the serialization is the same sequence of bytes which would be written
|
|
+** to disk if that database where backed up to disk.
|
|
+**
|
|
+** The usual case is that sqlite3_serialize() copies the serialization of
|
|
+** the database into memory obtained from [sqlite3_malloc64()] and returns
|
|
+** a pointer to that memory. The caller is responsible for freeing the
|
|
+** returned value to avoid a memory leak. However, if the F argument
|
|
+** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations
|
|
+** are made, and the sqlite3_serialize() function will return a pointer
|
|
+** to the contiguous memory representation of the database that SQLite
|
|
+** is currently using for that database, or NULL if the no such contiguous
|
|
+** memory representation of the database exists. A contiguous memory
|
|
+** representation of the database will usually only exist if there has
|
|
+** been a prior call to [sqlite3_deserialize(D,S,...)] with the same
|
|
+** values of D and S.
|
|
+** The size of the database is written into *P even if the
|
|
+** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
|
|
+** of the database exists.
|
|
+**
|
|
+** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
|
|
+** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
|
|
+** allocation error occurs.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_DESERIALIZE] option.
|
|
+*/
|
|
+SQLITE_API unsigned char *sqlite3_serialize(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */
|
|
+ sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */
|
|
+ unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3_serialize
|
|
+**
|
|
+** Zero or more of the following constants can be OR-ed together for
|
|
+** the F argument to [sqlite3_serialize(D,S,P,F)].
|
|
+**
|
|
+** SQLITE_SERIALIZE_NOCOPY means that [sqlite3_serialize()] will return
|
|
+** a pointer to contiguous in-memory database that it is currently using,
|
|
+** without making a copy of the database. If SQLite is not currently using
|
|
+** a contiguous in-memory database, then this option causes
|
|
+** [sqlite3_serialize()] to return a NULL pointer. SQLite will only be
|
|
+** using a contiguous in-memory database if it has been initialized by a
|
|
+** prior call to [sqlite3_deserialize()].
|
|
+*/
|
|
+#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Deserialize a database
|
|
+**
|
|
+** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
|
|
+** [database connection] D to disconnect from database S and then
|
|
+** reopen S as an in-memory database based on the serialization contained
|
|
+** in P. The serialized database P is N bytes in size. M is the size of
|
|
+** the buffer P, which might be larger than N. If M is larger than N, and
|
|
+** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
|
|
+** permitted to add content to the in-memory database as long as the total
|
|
+** size does not exceed M bytes.
|
|
+**
|
|
+** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
|
|
+** invoke sqlite3_free() on the serialization buffer when the database
|
|
+** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then
|
|
+** SQLite will try to increase the buffer size using sqlite3_realloc64()
|
|
+** if writes on the database cause it to grow larger than M bytes.
|
|
+**
|
|
+** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
|
|
+** database is currently in a read transaction or is involved in a backup
|
|
+** operation.
|
|
+**
|
|
+** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
|
|
+** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
|
|
+** [sqlite3_free()] is invoked on argument P prior to returning.
|
|
+**
|
|
+** This interface is only available if SQLite is compiled with the
|
|
+** [SQLITE_ENABLE_DESERIALIZE] option.
|
|
+*/
|
|
+SQLITE_API int sqlite3_deserialize(
|
|
+ sqlite3 *db, /* The database connection */
|
|
+ const char *zSchema, /* Which DB to reopen with the deserialization */
|
|
+ unsigned char *pData, /* The serialized database content */
|
|
+ sqlite3_int64 szDb, /* Number bytes in the deserialization */
|
|
+ sqlite3_int64 szBuf, /* Total size of buffer pData[] */
|
|
+ unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3_deserialize()
|
|
+**
|
|
+** The following are allowed values for 6th argument (the F argument) to
|
|
+** the [sqlite3_deserialize(D,S,P,N,M,F)] interface.
|
|
+**
|
|
+** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization
|
|
+** in the P argument is held in memory obtained from [sqlite3_malloc64()]
|
|
+** and that SQLite should take ownership of this memory and automatically
|
|
+** free it when it has finished using it. Without this flag, the caller
|
|
+** is responsible for freeing any dynamically allocated memory.
|
|
+**
|
|
+** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
|
|
+** grow the size of the database using calls to [sqlite3_realloc64()]. This
|
|
+** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
|
|
+** Without this flag, the deserialized database cannot increase in size beyond
|
|
+** the number of bytes specified by the M parameter.
|
|
+**
|
|
+** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database
|
|
+** should be treated as read-only.
|
|
+*/
|
|
+#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */
|
|
+#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
|
|
+#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
|
|
+
|
|
+/*
|
|
** Undo the hack that converts floating point types to integer for
|
|
** builds on processors without floating point support.
|
|
*/
|
|
@@ -8800,7 +9425,7 @@
|
|
sqlite3_int64 iRowid; /* Rowid for current entry */
|
|
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
|
|
int eParentWithin; /* Visibility of parent node */
|
|
- int eWithin; /* OUT: Visiblity */
|
|
+ int eWithin; /* OUT: Visibility */
|
|
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
|
|
/* The following fields are only available in 3.8.11 and later */
|
|
sqlite3_value **apSqlParam; /* Original SQL values of parameters */
|
|
@@ -8836,16 +9461,23 @@
|
|
|
|
/*
|
|
** CAPI3REF: Session Object Handle
|
|
+**
|
|
+** An instance of this object is a [session] that can be used to
|
|
+** record changes to a database.
|
|
*/
|
|
typedef struct sqlite3_session sqlite3_session;
|
|
|
|
/*
|
|
** CAPI3REF: Changeset Iterator Handle
|
|
+**
|
|
+** An instance of this object acts as a cursor for iterating
|
|
+** over the elements of a [changeset] or [patchset].
|
|
*/
|
|
typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
|
|
|
|
/*
|
|
** CAPI3REF: Create A New Session Object
|
|
+** CONSTRUCTOR: sqlite3_session
|
|
**
|
|
** Create a new session object attached to database handle db. If successful,
|
|
** a pointer to the new object is written to *ppSession and SQLITE_OK is
|
|
@@ -8882,6 +9514,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Delete A Session Object
|
|
+** DESTRUCTOR: sqlite3_session
|
|
**
|
|
** Delete a session object previously allocated using
|
|
** [sqlite3session_create()]. Once a session object has been deleted, the
|
|
@@ -8897,6 +9530,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Enable Or Disable A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** Enable or disable the recording of changes by a session object. When
|
|
** enabled, a session object records changes made to the database. When
|
|
@@ -8916,6 +9550,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Set Or Clear the Indirect Change Flag
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** Each change recorded by a session object is marked as either direct or
|
|
** indirect. A change is marked as indirect if either:
|
|
@@ -8945,6 +9580,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Attach A Table To A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** If argument zTab is not NULL, then it is the name of a table to attach
|
|
** to the session object passed as the first argument. All subsequent changes
|
|
@@ -8970,6 +9606,35 @@
|
|
**
|
|
** SQLITE_OK is returned if the call completes without error. Or, if an error
|
|
** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
|
|
+**
|
|
+** <h3>Special sqlite_stat1 Handling</h3>
|
|
+**
|
|
+** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to
|
|
+** some of the rules above. In SQLite, the schema of sqlite_stat1 is:
|
|
+** <pre>
|
|
+** CREATE TABLE sqlite_stat1(tbl,idx,stat)
|
|
+** </pre>
|
|
+**
|
|
+** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are
|
|
+** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes
|
|
+** are recorded for rows for which (idx IS NULL) is true. However, for such
|
|
+** rows a zero-length blob (SQL value X'') is stored in the changeset or
|
|
+** patchset instead of a NULL value. This allows such changesets to be
|
|
+** manipulated by legacy implementations of sqlite3changeset_invert(),
|
|
+** concat() and similar.
|
|
+**
|
|
+** The sqlite3changeset_apply() function automatically converts the
|
|
+** zero-length blob back to a NULL value when updating the sqlite_stat1
|
|
+** table. However, if the application calls sqlite3changeset_new(),
|
|
+** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset
|
|
+** iterator directly (including on a changeset iterator passed to a
|
|
+** conflict-handler callback) then the X'' value is returned. The application
|
|
+** must translate X'' to NULL itself if required.
|
|
+**
|
|
+** Legacy (older than 3.22.0) versions of the sessions module cannot capture
|
|
+** changes made to the sqlite_stat1 table. Legacy versions of the
|
|
+** sqlite3changeset_apply() function silently ignore any modifications to the
|
|
+** sqlite_stat1 table that are part of a changeset or patchset.
|
|
*/
|
|
SQLITE_API int sqlite3session_attach(
|
|
sqlite3_session *pSession, /* Session object */
|
|
@@ -8978,6 +9643,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Set a table filter on a Session Object.
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** 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
|
|
@@ -8996,6 +9662,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Generate A Changeset From A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** Obtain a changeset containing changes to the tables attached to the
|
|
** session object passed as the first argument. If successful,
|
|
@@ -9105,7 +9772,8 @@
|
|
);
|
|
|
|
/*
|
|
-** CAPI3REF: Load The Difference Between Tables Into A Session
|
|
+** CAPI3REF: Load The Difference Between Tables Into A Session
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** If it is not already attached to the session object passed as the first
|
|
** argument, this function attaches table zTbl in the same manner as the
|
|
@@ -9170,6 +9838,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Generate A Patchset From A Session Object
|
|
+** METHOD: sqlite3_session
|
|
**
|
|
** The differences between a patchset and a changeset are that:
|
|
**
|
|
@@ -9198,8 +9867,8 @@
|
|
*/
|
|
SQLITE_API int sqlite3session_patchset(
|
|
sqlite3_session *pSession, /* Session object */
|
|
- int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
|
|
- void **ppPatchset /* OUT: Buffer containing changeset */
|
|
+ int *pnPatchset, /* OUT: Size of buffer at *ppPatchset */
|
|
+ void **ppPatchset /* OUT: Buffer containing patchset */
|
|
);
|
|
|
|
/*
|
|
@@ -9221,6 +9890,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Create An Iterator To Traverse A Changeset
|
|
+** CONSTRUCTOR: sqlite3_changeset_iter
|
|
**
|
|
** Create an iterator used to iterate through the contents of a changeset.
|
|
** If successful, *pp is set to point to the iterator handle and SQLITE_OK
|
|
@@ -9251,6 +9921,13 @@
|
|
** consecutively. There is no chance that the iterator will visit a change
|
|
** the applies to table X, then one for table Y, and then later on visit
|
|
** another change for table X.
|
|
+**
|
|
+** The behavior of sqlite3changeset_start_v2() and its streaming equivalent
|
|
+** may be modified by passing a combination of
|
|
+** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter.
|
|
+**
|
|
+** Note that the sqlite3changeset_start_v2() API is still <b>experimental</b>
|
|
+** and therefore subject to change.
|
|
*/
|
|
SQLITE_API int sqlite3changeset_start(
|
|
sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
|
|
@@ -9257,10 +9934,30 @@
|
|
int nChangeset, /* Size of changeset blob in bytes */
|
|
void *pChangeset /* Pointer to blob containing changeset */
|
|
);
|
|
+SQLITE_API int sqlite3changeset_start_v2(
|
|
+ sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
|
|
+ int nChangeset, /* Size of changeset blob in bytes */
|
|
+ void *pChangeset, /* Pointer to blob containing changeset */
|
|
+ int flags /* SESSION_CHANGESETSTART_* flags */
|
|
+);
|
|
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3changeset_start_v2
|
|
+**
|
|
+** The following flags may passed via the 4th parameter to
|
|
+** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
|
+**
|
|
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
|
+** Invert the changeset while iterating through it. This is equivalent to
|
|
+** inverting a changeset using sqlite3changeset_invert() before applying it.
|
|
+** It is an error to specify this flag with a patchset.
|
|
+*/
|
|
+#define SQLITE_CHANGESETSTART_INVERT 0x0002
|
|
|
|
+
|
|
/*
|
|
** CAPI3REF: Advance A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function may only be used with iterators created by function
|
|
** [sqlite3changeset_start()]. If it is called on an iterator passed to
|
|
@@ -9285,6 +9982,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain The Current Operation From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** The pIter argument passed to this function may either be an iterator
|
|
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
|
|
@@ -9319,6 +10017,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain The Primary Key Definition Of A Table
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** For each modified table, a changeset includes the following:
|
|
**
|
|
@@ -9350,6 +10049,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain old.* Values From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** The pIter argument passed to this function may either be an iterator
|
|
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
|
|
@@ -9380,6 +10080,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain new.* Values From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** The pIter argument passed to this function may either be an iterator
|
|
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
|
|
@@ -9413,6 +10114,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function should only be used with iterator objects passed to a
|
|
** conflict-handler callback by [sqlite3changeset_apply()] with either
|
|
@@ -9440,6 +10142,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function may only be called with an iterator passed to an
|
|
** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
|
|
@@ -9456,6 +10159,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Finalize A Changeset Iterator
|
|
+** METHOD: sqlite3_changeset_iter
|
|
**
|
|
** This function is used to finalize an iterator allocated with
|
|
** [sqlite3changeset_start()].
|
|
@@ -9472,6 +10176,7 @@
|
|
** to that error is returned by this function. Otherwise, SQLITE_OK is
|
|
** returned. This is to allow the following pattern (pseudo-code):
|
|
**
|
|
+** <pre>
|
|
** sqlite3changeset_start();
|
|
** while( SQLITE_ROW==sqlite3changeset_next() ){
|
|
** // Do something with change.
|
|
@@ -9480,6 +10185,7 @@
|
|
** if( rc!=SQLITE_OK ){
|
|
** // An error has occurred
|
|
** }
|
|
+** </pre>
|
|
*/
|
|
SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
|
|
|
|
@@ -9527,6 +10233,7 @@
|
|
** sqlite3_changegroup object. Calling it produces similar results as the
|
|
** following code fragment:
|
|
**
|
|
+** <pre>
|
|
** sqlite3_changegroup *pGrp;
|
|
** rc = sqlite3_changegroup_new(&pGrp);
|
|
** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
|
|
@@ -9537,6 +10244,7 @@
|
|
** *ppOut = 0;
|
|
** *pnOut = 0;
|
|
** }
|
|
+** </pre>
|
|
**
|
|
** Refer to the sqlite3_changegroup documentation below for details.
|
|
*/
|
|
@@ -9552,11 +10260,15 @@
|
|
|
|
/*
|
|
** CAPI3REF: Changegroup Handle
|
|
+**
|
|
+** A changegroup is an object used to combine two or more
|
|
+** [changesets] or [patchsets]
|
|
*/
|
|
typedef struct sqlite3_changegroup sqlite3_changegroup;
|
|
|
|
/*
|
|
** CAPI3REF: Create A New Changegroup Object
|
|
+** CONSTRUCTOR: sqlite3_changegroup
|
|
**
|
|
** An sqlite3_changegroup object is used to combine two or more changesets
|
|
** (or patchsets) into a single changeset (or patchset). A single changegroup
|
|
@@ -9594,6 +10306,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Add A Changeset To A Changegroup
|
|
+** METHOD: sqlite3_changegroup
|
|
**
|
|
** Add all changes within the changeset (or patchset) in buffer pData (size
|
|
** nData bytes) to the changegroup.
|
|
@@ -9671,6 +10384,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
|
|
+** METHOD: sqlite3_changegroup
|
|
**
|
|
** Obtain a buffer containing a changeset (or patchset) representing the
|
|
** current contents of the changegroup. If the inputs to the changegroup
|
|
@@ -9701,6 +10415,7 @@
|
|
|
|
/*
|
|
** CAPI3REF: Delete A Changegroup Object
|
|
+** DESTRUCTOR: sqlite3_changegroup
|
|
*/
|
|
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
|
|
|
|
@@ -9707,19 +10422,18 @@
|
|
/*
|
|
** CAPI3REF: Apply A Changeset To A Database
|
|
**
|
|
-** Apply a changeset to a database. This function attempts to update the
|
|
-** "main" database attached to handle db with the changes found in the
|
|
-** changeset passed via the second and third arguments.
|
|
+** Apply a changeset or patchset to a database. These functions attempt to
|
|
+** update the "main" database attached to handle db with the changes found in
|
|
+** the changeset passed via the second and third arguments.
|
|
**
|
|
-** The fourth argument (xFilter) passed to this function is the "filter
|
|
+** The fourth argument (xFilter) passed to these functions is the "filter
|
|
** callback". If it is not NULL, then for each table affected by at least one
|
|
** change in the changeset, the filter callback is invoked with
|
|
** the table name as the second argument, and a copy of the context pointer
|
|
-** passed as the sixth argument to this function as the first. If the "filter
|
|
-** callback" returns zero, then no attempt is made to apply any changes to
|
|
-** the table. Otherwise, if the return value is non-zero or the xFilter
|
|
-** argument to this function is NULL, all changes related to the table are
|
|
-** attempted.
|
|
+** passed as the sixth argument as the first. If the "filter callback"
|
|
+** returns zero, then no attempt is made to apply any changes to the table.
|
|
+** Otherwise, if the return value is non-zero or the xFilter argument to
|
|
+** is NULL, all changes related to the table are attempted.
|
|
**
|
|
** For each table that is not excluded by the filter callback, this function
|
|
** tests that the target database contains a compatible table. A table is
|
|
@@ -9764,7 +10478,7 @@
|
|
**
|
|
** <dl>
|
|
** <dt>DELETE Changes<dd>
|
|
-** For each DELETE change, this function checks if the target database
|
|
+** For each DELETE change, the function checks if the target database
|
|
** contains a row with the same primary key value (or values) as the
|
|
** original row values stored in the changeset. If it does, and the values
|
|
** stored in all non-primary key columns also match the values stored in
|
|
@@ -9809,7 +10523,7 @@
|
|
** [SQLITE_CHANGESET_REPLACE].
|
|
**
|
|
** <dt>UPDATE Changes<dd>
|
|
-** For each UPDATE change, this function checks if the target database
|
|
+** For each UPDATE change, the function checks if the target database
|
|
** contains a row with the same primary key value (or values) as the
|
|
** original row values stored in the changeset. If it does, and the values
|
|
** stored in all modified non-primary key columns also match the values
|
|
@@ -9840,11 +10554,28 @@
|
|
** This can be used to further customize the applications conflict
|
|
** resolution strategy.
|
|
**
|
|
-** All changes made by this function are enclosed in a savepoint transaction.
|
|
+** All changes made by these functions are enclosed in a savepoint transaction.
|
|
** If any other error (aside from a constraint failure when attempting to
|
|
** write to the target database) occurs, then the savepoint transaction is
|
|
** rolled back, restoring the target database to its original state, and an
|
|
** SQLite error code returned.
|
|
+**
|
|
+** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
|
|
+** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
|
|
+** may set (*ppRebase) to point to a "rebase" that may be used with the
|
|
+** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
|
|
+** is set to the size of the buffer in bytes. It is the responsibility of the
|
|
+** caller to eventually free any such buffer using sqlite3_free(). The buffer
|
|
+** is only allocated and populated if one or more conflicts were encountered
|
|
+** while applying the patchset. See comments surrounding the sqlite3_rebaser
|
|
+** APIs for further details.
|
|
+**
|
|
+** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent
|
|
+** may be modified by passing a combination of
|
|
+** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter.
|
|
+**
|
|
+** Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b>
|
|
+** and therefore subject to change.
|
|
*/
|
|
SQLITE_API int sqlite3changeset_apply(
|
|
sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
@@ -9861,7 +10592,48 @@
|
|
),
|
|
void *pCtx /* First argument passed to xConflict */
|
|
);
|
|
+SQLITE_API int sqlite3changeset_apply_v2(
|
|
+ sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
+ int nChangeset, /* Size of changeset in bytes */
|
|
+ void *pChangeset, /* Changeset blob */
|
|
+ int(*xFilter)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ const char *zTab /* Table name */
|
|
+ ),
|
|
+ int(*xConflict)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
+ ),
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
|
+ int flags /* SESSION_CHANGESETAPPLY_* flags */
|
|
+);
|
|
|
|
+/*
|
|
+** CAPI3REF: Flags for sqlite3changeset_apply_v2
|
|
+**
|
|
+** The following flags may passed via the 9th parameter to
|
|
+** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]:
|
|
+**
|
|
+** <dl>
|
|
+** <dt>SQLITE_CHANGESETAPPLY_NOSAVEPOINT <dd>
|
|
+** Usually, the sessions module encloses all operations performed by
|
|
+** a single call to apply_v2() or apply_v2_strm() in a [SAVEPOINT]. The
|
|
+** SAVEPOINT is committed if the changeset or patchset is successfully
|
|
+** applied, or rolled back if an error occurs. Specifying this flag
|
|
+** causes the sessions module to omit this savepoint. In this case, if the
|
|
+** caller has an open transaction or savepoint when apply_v2() is called,
|
|
+** it may revert the partially applied changeset by rolling it back.
|
|
+**
|
|
+** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
|
+** Invert the changeset before applying it. This is equivalent to inverting
|
|
+** a changeset using sqlite3changeset_invert() before applying it. It is
|
|
+** an error to specify this flag with a patchset.
|
|
+*/
|
|
+#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
|
+#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
|
+
|
|
/*
|
|
** CAPI3REF: Constants Passed To The Conflict Handler
|
|
**
|
|
@@ -9958,7 +10730,162 @@
|
|
#define SQLITE_CHANGESET_REPLACE 1
|
|
#define SQLITE_CHANGESET_ABORT 2
|
|
|
|
+/*
|
|
+** CAPI3REF: Rebasing changesets
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Suppose there is a site hosting a database in state S0. And that
|
|
+** modifications are made that move that database to state S1 and a
|
|
+** changeset recorded (the "local" changeset). Then, a changeset based
|
|
+** on S0 is received from another site (the "remote" changeset) and
|
|
+** applied to the database. The database is then in state
|
|
+** (S1+"remote"), where the exact state depends on any conflict
|
|
+** resolution decisions (OMIT or REPLACE) made while applying "remote".
|
|
+** Rebasing a changeset is to update it to take those conflict
|
|
+** resolution decisions into account, so that the same conflicts
|
|
+** do not have to be resolved elsewhere in the network.
|
|
+**
|
|
+** For example, if both the local and remote changesets contain an
|
|
+** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)":
|
|
+**
|
|
+** local: INSERT INTO t1 VALUES(1, 'v1');
|
|
+** remote: INSERT INTO t1 VALUES(1, 'v2');
|
|
+**
|
|
+** and the conflict resolution is REPLACE, then the INSERT change is
|
|
+** removed from the local changeset (it was overridden). Or, if the
|
|
+** conflict resolution was "OMIT", then the local changeset is modified
|
|
+** to instead contain:
|
|
+**
|
|
+** UPDATE t1 SET b = 'v2' WHERE a=1;
|
|
+**
|
|
+** Changes within the local changeset are rebased as follows:
|
|
+**
|
|
+** <dl>
|
|
+** <dt>Local INSERT<dd>
|
|
+** This may only conflict with a remote INSERT. If the conflict
|
|
+** resolution was OMIT, then add an UPDATE change to the rebased
|
|
+** changeset. Or, if the conflict resolution was REPLACE, add
|
|
+** nothing to the rebased changeset.
|
|
+**
|
|
+** <dt>Local DELETE<dd>
|
|
+** This may conflict with a remote UPDATE or DELETE. In both cases the
|
|
+** only possible resolution is OMIT. If the remote operation was a
|
|
+** DELETE, then add no change to the rebased changeset. If the remote
|
|
+** operation was an UPDATE, then the old.* fields of change are updated
|
|
+** to reflect the new.* values in the UPDATE.
|
|
+**
|
|
+** <dt>Local UPDATE<dd>
|
|
+** This may conflict with a remote UPDATE or DELETE. If it conflicts
|
|
+** with a DELETE, and the conflict resolution was OMIT, then the update
|
|
+** is changed into an INSERT. Any undefined values in the new.* record
|
|
+** from the update change are filled in using the old.* values from
|
|
+** the conflicting DELETE. Or, if the conflict resolution was REPLACE,
|
|
+** the UPDATE change is simply omitted from the rebased changeset.
|
|
+**
|
|
+** If conflict is with a remote UPDATE and the resolution is OMIT, then
|
|
+** the old.* values are rebased using the new.* values in the remote
|
|
+** change. Or, if the resolution is REPLACE, then the change is copied
|
|
+** into the rebased changeset with updates to columns also updated by
|
|
+** the conflicting remote UPDATE removed. If this means no columns would
|
|
+** be updated, the change is omitted.
|
|
+** </dl>
|
|
+**
|
|
+** A local change may be rebased against multiple remote changes
|
|
+** simultaneously. If a single key is modified by multiple remote
|
|
+** changesets, they are combined as follows before the local changeset
|
|
+** is rebased:
|
|
+**
|
|
+** <ul>
|
|
+** <li> If there has been one or more REPLACE resolutions on a
|
|
+** key, it is rebased according to a REPLACE.
|
|
+**
|
|
+** <li> If there have been no REPLACE resolutions on a key, then
|
|
+** the local changeset is rebased according to the most recent
|
|
+** of the OMIT resolutions.
|
|
+** </ul>
|
|
+**
|
|
+** Note that conflict resolutions from multiple remote changesets are
|
|
+** combined on a per-field basis, not per-row. This means that in the
|
|
+** case of multiple remote UPDATE operations, some fields of a single
|
|
+** local change may be rebased for REPLACE while others are rebased for
|
|
+** OMIT.
|
|
+**
|
|
+** In order to rebase a local changeset, the remote changeset must first
|
|
+** be applied to the local database using sqlite3changeset_apply_v2() and
|
|
+** the buffer of rebase information captured. Then:
|
|
+**
|
|
+** <ol>
|
|
+** <li> An sqlite3_rebaser object is created by calling
|
|
+** sqlite3rebaser_create().
|
|
+** <li> The new object is configured with the rebase buffer obtained from
|
|
+** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure().
|
|
+** If the local changeset is to be rebased against multiple remote
|
|
+** changesets, then sqlite3rebaser_configure() should be called
|
|
+** multiple times, in the same order that the multiple
|
|
+** sqlite3changeset_apply_v2() calls were made.
|
|
+** <li> Each local changeset is rebased by calling sqlite3rebaser_rebase().
|
|
+** <li> The sqlite3_rebaser object is deleted by calling
|
|
+** sqlite3rebaser_delete().
|
|
+** </ol>
|
|
+*/
|
|
+typedef struct sqlite3_rebaser sqlite3_rebaser;
|
|
+
|
|
/*
|
|
+** CAPI3REF: Create a changeset rebaser object.
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Allocate a new changeset rebaser object. If successful, set (*ppNew) to
|
|
+** point to the new object and return SQLITE_OK. Otherwise, if an error
|
|
+** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
|
|
+** to NULL.
|
|
+*/
|
|
+SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Configure a changeset rebaser object.
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Configure the changeset rebaser object to rebase changesets according
|
|
+** to the conflict resolutions described by buffer pRebase (size nRebase
|
|
+** bytes), which must have been obtained from a previous call to
|
|
+** sqlite3changeset_apply_v2().
|
|
+*/
|
|
+SQLITE_API int sqlite3rebaser_configure(
|
|
+ sqlite3_rebaser*,
|
|
+ int nRebase, const void *pRebase
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Rebase a changeset
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** 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
|
|
+** rebaser object passed as the first argument. If successful, (*ppOut)
|
|
+** is set to point to the new buffer containing the rebased changset and
|
|
+** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
|
|
+** responsibility of the caller to eventually free the new buffer using
|
|
+** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
|
|
+** are set to zero and an SQLite error code returned.
|
|
+*/
|
|
+SQLITE_API int sqlite3rebaser_rebase(
|
|
+ sqlite3_rebaser*,
|
|
+ int nIn, const void *pIn,
|
|
+ int *pnOut, void **ppOut
|
|
+);
|
|
+
|
|
+/*
|
|
+** CAPI3REF: Delete a changeset rebaser object.
|
|
+** EXPERIMENTAL
|
|
+**
|
|
+** Delete the changeset rebaser object and all associated resources. There
|
|
+** should be one call to this function for each successful invocation
|
|
+** of sqlite3rebaser_create().
|
|
+*/
|
|
+SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
|
|
+
|
|
+/*
|
|
** CAPI3REF: Streaming Versions of API functions.
|
|
**
|
|
** The six streaming API xxx_strm() functions serve similar purposes to the
|
|
@@ -9966,12 +10893,13 @@
|
|
**
|
|
** <table border=1 style="margin-left:8ex;margin-right:8ex">
|
|
** <tr><th>Streaming function<th>Non-streaming equivalent</th>
|
|
-** <tr><td>sqlite3changeset_apply_str<td>[sqlite3changeset_apply]
|
|
-** <tr><td>sqlite3changeset_concat_str<td>[sqlite3changeset_concat]
|
|
-** <tr><td>sqlite3changeset_invert_str<td>[sqlite3changeset_invert]
|
|
-** <tr><td>sqlite3changeset_start_str<td>[sqlite3changeset_start]
|
|
-** <tr><td>sqlite3session_changeset_str<td>[sqlite3session_changeset]
|
|
-** <tr><td>sqlite3session_patchset_str<td>[sqlite3session_patchset]
|
|
+** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
|
|
+** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
|
|
+** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
|
|
+** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
|
|
+** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
|
|
+** <tr><td>sqlite3session_changeset_strm<td>[sqlite3session_changeset]
|
|
+** <tr><td>sqlite3session_patchset_strm<td>[sqlite3session_patchset]
|
|
** </table>
|
|
**
|
|
** Non-streaming functions that accept changesets (or patchsets) as input
|
|
@@ -10062,6 +10990,23 @@
|
|
),
|
|
void *pCtx /* First argument passed to xConflict */
|
|
);
|
|
+SQLITE_API int sqlite3changeset_apply_v2_strm(
|
|
+ sqlite3 *db, /* Apply change to "main" db of this handle */
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
|
+ void *pIn, /* First arg for xInput */
|
|
+ int(*xFilter)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ const char *zTab /* Table name */
|
|
+ ),
|
|
+ int(*xConflict)(
|
|
+ void *pCtx, /* Copy of sixth arg to _apply() */
|
|
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
|
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
|
+ ),
|
|
+ void *pCtx, /* First argument passed to xConflict */
|
|
+ void **ppRebase, int *pnRebase,
|
|
+ int flags
|
|
+);
|
|
SQLITE_API int sqlite3changeset_concat_strm(
|
|
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
|
void *pInA,
|
|
@@ -10081,6 +11026,12 @@
|
|
int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
void *pIn
|
|
);
|
|
+SQLITE_API int sqlite3changeset_start_v2_strm(
|
|
+ sqlite3_changeset_iter **pp,
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
+ void *pIn,
|
|
+ int flags
|
|
+);
|
|
SQLITE_API int sqlite3session_changeset_strm(
|
|
sqlite3_session *pSession,
|
|
int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
@@ -10099,9 +11050,55 @@
|
|
int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
void *pOut
|
|
);
|
|
+SQLITE_API int sqlite3rebaser_rebase_strm(
|
|
+ sqlite3_rebaser *pRebaser,
|
|
+ int (*xInput)(void *pIn, void *pData, int *pnData),
|
|
+ void *pIn,
|
|
+ int (*xOutput)(void *pOut, const void *pData, int nData),
|
|
+ void *pOut
|
|
+);
|
|
|
|
+/*
|
|
+** CAPI3REF: Configure global parameters
|
|
+**
|
|
+** The sqlite3session_config() interface is used to make global configuration
|
|
+** changes to the sessions module in order to tune it to the specific needs
|
|
+** of the application.
|
|
+**
|
|
+** The sqlite3session_config() interface is not threadsafe. If it is invoked
|
|
+** while any other thread is inside any other sessions method then the
|
|
+** results are undefined. Furthermore, if it is invoked after any sessions
|
|
+** related objects have been created, the results are also undefined.
|
|
+**
|
|
+** The first argument to the sqlite3session_config() function must be one
|
|
+** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
|
|
+** interpretation of the (void*) value passed as the second parameter and
|
|
+** the effect of calling this function depends on the value of the first
|
|
+** parameter.
|
|
+**
|
|
+** <dl>
|
|
+** <dt>SQLITE_SESSION_CONFIG_STRMSIZE<dd>
|
|
+** By default, the sessions module streaming interfaces attempt to input
|
|
+** and output data in approximately 1 KiB chunks. This operand may be used
|
|
+** to set and query the value of this configuration setting. The pointer
|
|
+** passed as the second argument must point to a value of type (int).
|
|
+** If this value is greater than 0, it is used as the new streaming data
|
|
+** chunk size for both input and output. Before returning, the (int) value
|
|
+** pointed to by pArg is set to the final value of the streaming interface
|
|
+** chunk size.
|
|
+** </dl>
|
|
+**
|
|
+** This function returns SQLITE_OK if successful, or an SQLite error code
|
|
+** otherwise.
|
|
+*/
|
|
+SQLITE_API int sqlite3session_config(int op, void *pArg);
|
|
|
|
/*
|
|
+** CAPI3REF: Values for sqlite3session_config().
|
|
+*/
|
|
+#define SQLITE_SESSION_CONFIG_STRMSIZE 1
|
|
+
|
|
+/*
|
|
** Make sure we can call this stuff from C++.
|
|
*/
|
|
#ifdef __cplusplus
|
|
@@ -10557,7 +11554,7 @@
|
|
** This way, even if the tokenizer does not provide synonyms
|
|
** when tokenizing query text (it should not - to do would be
|
|
** inefficient), it doesn't matter if the user queries for
|
|
-** 'first + place' or '1st + place', as there are entires in the
|
|
+** 'first + place' or '1st + place', as there are entries in the
|
|
** FTS index corresponding to both forms of the first token.
|
|
** </ol>
|
|
**
|
|
@@ -10585,7 +11582,7 @@
|
|
** extra data to the FTS index or require FTS5 to query for multiple terms,
|
|
** so it is efficient in terms of disk space and query speed. However, it
|
|
** does not support prefix queries very well. If, as suggested above, the
|
|
-** token "first" is subsituted for "1st" by the tokenizer, then the query:
|
|
+** token "first" is substituted for "1st" by the tokenizer, then the query:
|
|
**
|
|
** <codeblock>
|
|
** ... MATCH '1s*'</codeblock>
|
|
--- contrib/sqlite3/sqlite3ext.h.orig
|
|
+++ contrib/sqlite3/sqlite3ext.h
|
|
@@ -134,7 +134,7 @@
|
|
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
|
const char*,const char*),void*);
|
|
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
|
- char * (*snprintf)(int,char*,const char*,...);
|
|
+ char * (*xsnprintf)(int,char*,const char*,...);
|
|
int (*step)(sqlite3_stmt*);
|
|
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
|
char const**,char const**,int*,int*,int*);
|
|
@@ -246,7 +246,7 @@
|
|
int (*uri_boolean)(const char*,const char*,int);
|
|
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
|
const char *(*uri_parameter)(const char*,const char*);
|
|
- char *(*vsnprintf)(int,char*,const char*,va_list);
|
|
+ char *(*xvsnprintf)(int,char*,const char*,va_list);
|
|
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
|
/* Version 3.8.7 and later */
|
|
int (*auto_extension)(void(*)(void));
|
|
@@ -292,6 +292,33 @@
|
|
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
|
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
|
void *(*value_pointer)(sqlite3_value*,const char*);
|
|
+ int (*vtab_nochange)(sqlite3_context*);
|
|
+ int (*value_nochange)(sqlite3_value*);
|
|
+ const char *(*vtab_collation)(sqlite3_index_info*,int);
|
|
+ /* Version 3.24.0 and later */
|
|
+ int (*keyword_count)(void);
|
|
+ int (*keyword_name)(int,const char**,int*);
|
|
+ int (*keyword_check)(const char*,int);
|
|
+ sqlite3_str *(*str_new)(sqlite3*);
|
|
+ char *(*str_finish)(sqlite3_str*);
|
|
+ void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
|
+ void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
|
+ void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
|
+ void (*str_appendall)(sqlite3_str*, const char *zIn);
|
|
+ void (*str_appendchar)(sqlite3_str*, int N, char C);
|
|
+ void (*str_reset)(sqlite3_str*);
|
|
+ int (*str_errcode)(sqlite3_str*);
|
|
+ int (*str_length)(sqlite3_str*);
|
|
+ char *(*str_value)(sqlite3_str*);
|
|
+ /* Version 3.25.0 and later */
|
|
+ int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
|
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void (*xFinal)(sqlite3_context*),
|
|
+ void (*xValue)(sqlite3_context*),
|
|
+ void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
|
+ void(*xDestroy)(void*));
|
|
+ /* Version 3.26.0 and later */
|
|
+ const char *(*normalized_sql)(sqlite3_stmt*);
|
|
};
|
|
|
|
/*
|
|
@@ -418,7 +445,7 @@
|
|
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
|
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
|
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
|
-#define sqlite3_snprintf sqlite3_api->snprintf
|
|
+#define sqlite3_snprintf sqlite3_api->xsnprintf
|
|
#define sqlite3_step sqlite3_api->step
|
|
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
|
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
|
@@ -442,7 +469,7 @@
|
|
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
|
#define sqlite3_value_type sqlite3_api->value_type
|
|
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
|
-#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
|
+#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
|
#define sqlite3_overload_function sqlite3_api->overload_function
|
|
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
@@ -518,7 +545,7 @@
|
|
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
|
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
|
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
|
-#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
|
+#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
|
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
|
/* Version 3.8.7 and later */
|
|
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
|
@@ -558,6 +585,29 @@
|
|
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
|
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
|
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
|
+/* Version 3.22.0 and later */
|
|
+#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
|
+#define sqlite3_value_nochange sqlite3_api->value_nochange
|
|
+#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
|
+/* Version 3.24.0 and later */
|
|
+#define sqlite3_keyword_count sqlite3_api->keyword_count
|
|
+#define sqlite3_keyword_name sqlite3_api->keyword_name
|
|
+#define sqlite3_keyword_check sqlite3_api->keyword_check
|
|
+#define sqlite3_str_new sqlite3_api->str_new
|
|
+#define sqlite3_str_finish sqlite3_api->str_finish
|
|
+#define sqlite3_str_appendf sqlite3_api->str_appendf
|
|
+#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
|
+#define sqlite3_str_append sqlite3_api->str_append
|
|
+#define sqlite3_str_appendall sqlite3_api->str_appendall
|
|
+#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
|
+#define sqlite3_str_reset sqlite3_api->str_reset
|
|
+#define sqlite3_str_errcode sqlite3_api->str_errcode
|
|
+#define sqlite3_str_length sqlite3_api->str_length
|
|
+#define sqlite3_str_value sqlite3_api->str_value
|
|
+/* Version 3.25.0 and later */
|
|
+#define sqlite3_create_window_function sqlite3_api->create_window_function
|
|
+/* Version 3.26.0 and later */
|
|
+#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
|
|
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
--- contrib/sqlite3/tea/configure.orig
|
|
+++ contrib/sqlite3/tea/configure
|
|
@@ -1,6 +1,6 @@
|
|
#! /bin/sh
|
|
# Guess values for system-dependent variables and create Makefiles.
|
|
-# Generated by GNU Autoconf 2.69 for sqlite 3.20.0.
|
|
+# Generated by GNU Autoconf 2.69 for sqlite 3.26.0.
|
|
#
|
|
#
|
|
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
|
@@ -577,8 +577,8 @@
|
|
# Identity of this package.
|
|
PACKAGE_NAME='sqlite'
|
|
PACKAGE_TARNAME='sqlite'
|
|
-PACKAGE_VERSION='3.20.0'
|
|
-PACKAGE_STRING='sqlite 3.20.0'
|
|
+PACKAGE_VERSION='3.26.0'
|
|
+PACKAGE_STRING='sqlite 3.26.0'
|
|
PACKAGE_BUGREPORT=''
|
|
PACKAGE_URL=''
|
|
|
|
@@ -1292,7 +1292,7 @@
|
|
# Omit some internal or obsolete options to make the list less imposing.
|
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
|
cat <<_ACEOF
|
|
-\`configure' configures sqlite 3.20.0 to adapt to many kinds of systems.
|
|
+\`configure' configures sqlite 3.26.0 to adapt to many kinds of systems.
|
|
|
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
|
|
|
@@ -1353,7 +1353,7 @@
|
|
|
|
if test -n "$ac_init_help"; then
|
|
case $ac_init_help in
|
|
- short | recursive ) echo "Configuration of sqlite 3.20.0:";;
|
|
+ short | recursive ) echo "Configuration of sqlite 3.26.0:";;
|
|
esac
|
|
cat <<\_ACEOF
|
|
|
|
@@ -1455,7 +1455,7 @@
|
|
test -n "$ac_init_help" && exit $ac_status
|
|
if $ac_init_version; then
|
|
cat <<\_ACEOF
|
|
-sqlite configure 3.20.0
|
|
+sqlite configure 3.26.0
|
|
generated by GNU Autoconf 2.69
|
|
|
|
Copyright (C) 2012 Free Software Foundation, Inc.
|
|
@@ -1866,7 +1866,7 @@
|
|
This file contains any messages produced by compilers while
|
|
running configure, to aid debugging if configure makes a mistake.
|
|
|
|
-It was created by sqlite $as_me 3.20.0, which was
|
|
+It was created by sqlite $as_me 3.26.0, which was
|
|
generated by GNU Autoconf 2.69. Invocation command line was
|
|
|
|
$ $0 $@
|
|
@@ -9361,7 +9361,7 @@
|
|
# report actual input values of CONFIG_FILES etc. instead of their
|
|
# values after options handling.
|
|
ac_log="
|
|
-This file was extended by sqlite $as_me 3.20.0, which was
|
|
+This file was extended by sqlite $as_me 3.26.0, which was
|
|
generated by GNU Autoconf 2.69. Invocation command line was
|
|
|
|
CONFIG_FILES = $CONFIG_FILES
|
|
@@ -9414,7 +9414,7 @@
|
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
|
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
|
ac_cs_version="\\
|
|
-sqlite config.status 3.20.0
|
|
+sqlite config.status 3.26.0
|
|
configured by $0, generated by GNU Autoconf 2.69,
|
|
with options \\"\$ac_cs_config\\"
|
|
|
|
--- contrib/sqlite3/tea/configure.ac.orig
|
|
+++ contrib/sqlite3/tea/configure.ac
|
|
@@ -19,7 +19,7 @@
|
|
# so you can encode the package version directly into the source files.
|
|
#-----------------------------------------------------------------------
|
|
|
|
-AC_INIT([sqlite], [3.20.0])
|
|
+AC_INIT([sqlite], [3.26.0])
|
|
|
|
#--------------------------------------------------------------------
|
|
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
|
--- contrib/sqlite3/tea/generic/tclsqlite3.c.orig
|
|
+++ contrib/sqlite3/tea/generic/tclsqlite3.c
|
|
@@ -19,17 +19,19 @@
|
|
**
|
|
** Compile-time options:
|
|
**
|
|
-** -DTCLSH=1 Add a "main()" routine that works as a tclsh.
|
|
+** -DTCLSH Add a "main()" routine that works as a tclsh.
|
|
**
|
|
-** -DSQLITE_TCLMD5 When used in conjuction with -DTCLSH=1, add
|
|
-** four new commands to the TCL interpreter for
|
|
-** generating MD5 checksums: md5, md5file,
|
|
-** md5-10x8, and md5file-10x8.
|
|
+** -DTCLSH_INIT_PROC=name
|
|
**
|
|
-** -DSQLITE_TEST When used in conjuction with -DTCLSH=1, add
|
|
-** hundreds of new commands used for testing
|
|
-** SQLite. This option implies -DSQLITE_TCLMD5.
|
|
+** Invoke name(interp) to initialize the Tcl interpreter.
|
|
+** If name(interp) returns a non-NULL string, then run
|
|
+** that string as a Tcl script to launch the application.
|
|
+** If name(interp) returns NULL, then run the regular
|
|
+** tclsh-emulator code.
|
|
*/
|
|
+#ifdef TCLSH_INIT_PROC
|
|
+# define TCLSH 1
|
|
+#endif
|
|
|
|
/*
|
|
** If requested, include the SQLite compiler options file for MSVC.
|
|
@@ -63,13 +65,18 @@
|
|
|
|
/* Used to get the current process ID */
|
|
#if !defined(_WIN32)
|
|
+# include <signal.h>
|
|
# include <unistd.h>
|
|
# define GETPID getpid
|
|
#elif !defined(_WIN32_WCE)
|
|
# ifndef SQLITE_AMALGAMATION
|
|
-# define WIN32_LEAN_AND_MEAN
|
|
+# ifndef WIN32_LEAN_AND_MEAN
|
|
+# define WIN32_LEAN_AND_MEAN
|
|
+# endif
|
|
# include <windows.h>
|
|
# endif
|
|
+# include <io.h>
|
|
+# define isatty(h) _isatty(h)
|
|
# define GETPID (int)GetCurrentProcessId
|
|
#endif
|
|
|
|
@@ -649,7 +656,7 @@
|
|
}
|
|
case SQLITE_TRACE_PROFILE: {
|
|
sqlite3_stmt *pStmt = (sqlite3_stmt *)pd;
|
|
- sqlite3_int64 ns = (sqlite3_int64)xd;
|
|
+ sqlite3_int64 ns = *(sqlite3_int64*)xd;
|
|
|
|
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
|
|
Tcl_IncrRefCount(pCmd);
|
|
@@ -1849,35 +1856,35 @@
|
|
int choice;
|
|
int rc = TCL_OK;
|
|
static const char *DB_strs[] = {
|
|
- "authorizer", "backup", "busy",
|
|
- "cache", "changes", "close",
|
|
- "collate", "collation_needed", "commit_hook",
|
|
- "complete", "copy", "enable_load_extension",
|
|
- "errorcode", "eval", "exists",
|
|
- "function", "incrblob", "interrupt",
|
|
- "last_insert_rowid", "nullvalue", "onecolumn",
|
|
- "preupdate", "profile", "progress",
|
|
- "rekey", "restore", "rollback_hook",
|
|
- "status", "timeout", "total_changes",
|
|
- "trace", "trace_v2", "transaction",
|
|
- "unlock_notify", "update_hook", "version",
|
|
- "wal_hook",
|
|
- 0
|
|
+ "authorizer", "backup", "busy",
|
|
+ "cache", "changes", "close",
|
|
+ "collate", "collation_needed", "commit_hook",
|
|
+ "complete", "copy", "deserialize",
|
|
+ "enable_load_extension", "errorcode", "eval",
|
|
+ "exists", "function", "incrblob",
|
|
+ "interrupt", "last_insert_rowid", "nullvalue",
|
|
+ "onecolumn", "preupdate", "profile",
|
|
+ "progress", "rekey", "restore",
|
|
+ "rollback_hook", "serialize", "status",
|
|
+ "timeout", "total_changes", "trace",
|
|
+ "trace_v2", "transaction", "unlock_notify",
|
|
+ "update_hook", "version", "wal_hook",
|
|
+ 0
|
|
};
|
|
enum DB_enum {
|
|
- DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
|
|
- DB_CACHE, DB_CHANGES, DB_CLOSE,
|
|
- DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK,
|
|
- DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION,
|
|
- DB_ERRORCODE, DB_EVAL, DB_EXISTS,
|
|
- DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT,
|
|
- DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN,
|
|
- DB_PREUPDATE, DB_PROFILE, DB_PROGRESS,
|
|
- DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK,
|
|
- DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
|
|
- DB_TRACE, DB_TRACE_V2, DB_TRANSACTION,
|
|
- DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION,
|
|
- DB_WAL_HOOK,
|
|
+ DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
|
|
+ DB_CACHE, DB_CHANGES, DB_CLOSE,
|
|
+ DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK,
|
|
+ DB_COMPLETE, DB_COPY, DB_DESERIALIZE,
|
|
+ DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL,
|
|
+ DB_EXISTS, DB_FUNCTION, DB_INCRBLOB,
|
|
+ DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE,
|
|
+ DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE,
|
|
+ DB_PROGRESS, DB_REKEY, DB_RESTORE,
|
|
+ DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS,
|
|
+ DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
|
|
+ DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
|
|
+ DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK
|
|
};
|
|
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
|
|
|
|
@@ -2416,6 +2423,53 @@
|
|
}
|
|
|
|
/*
|
|
+ ** $db deserialize ?DATABASE? VALUE
|
|
+ **
|
|
+ ** Reopen DATABASE (default "main") using the content in $VALUE
|
|
+ */
|
|
+ case DB_DESERIALIZE: {
|
|
+#ifndef SQLITE_ENABLE_DESERIALIZE
|
|
+ Tcl_AppendResult(interp, "MEMDB not available in this build",
|
|
+ (char*)0);
|
|
+ rc = TCL_ERROR;
|
|
+#else
|
|
+ const char *zSchema;
|
|
+ Tcl_Obj *pValue;
|
|
+ unsigned char *pBA;
|
|
+ unsigned char *pData;
|
|
+ int len, xrc;
|
|
+
|
|
+ if( objc==3 ){
|
|
+ zSchema = 0;
|
|
+ pValue = objv[2];
|
|
+ }else if( objc==4 ){
|
|
+ zSchema = Tcl_GetString(objv[2]);
|
|
+ pValue = objv[3];
|
|
+ }else{
|
|
+ Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? VALUE");
|
|
+ rc = TCL_ERROR;
|
|
+ break;
|
|
+ }
|
|
+ pBA = Tcl_GetByteArrayFromObj(pValue, &len);
|
|
+ pData = sqlite3_malloc64( len );
|
|
+ if( pData==0 && len>0 ){
|
|
+ Tcl_AppendResult(interp, "out of memory", (char*)0);
|
|
+ rc = TCL_ERROR;
|
|
+ }else{
|
|
+ if( len>0 ) memcpy(pData, pBA, len);
|
|
+ xrc = sqlite3_deserialize(pDb->db, zSchema, pData, len, len,
|
|
+ SQLITE_DESERIALIZE_FREEONCLOSE |
|
|
+ SQLITE_DESERIALIZE_RESIZEABLE);
|
|
+ if( xrc ){
|
|
+ Tcl_AppendResult(interp, "unable to set MEMDB content", (char*)0);
|
|
+ rc = TCL_ERROR;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
** $db enable_load_extension BOOLEAN
|
|
**
|
|
** Turn the extension loading feature on or off. It if off by
|
|
@@ -2891,6 +2945,39 @@
|
|
}
|
|
|
|
/*
|
|
+ ** $db serialize ?DATABASE?
|
|
+ **
|
|
+ ** Return a serialization of a database.
|
|
+ */
|
|
+ case DB_SERIALIZE: {
|
|
+#ifndef SQLITE_ENABLE_DESERIALIZE
|
|
+ Tcl_AppendResult(interp, "MEMDB not available in this build",
|
|
+ (char*)0);
|
|
+ rc = TCL_ERROR;
|
|
+#else
|
|
+ const char *zSchema = objc>=3 ? Tcl_GetString(objv[2]) : "main";
|
|
+ sqlite3_int64 sz = 0;
|
|
+ unsigned char *pData;
|
|
+ if( objc!=2 && objc!=3 ){
|
|
+ Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE?");
|
|
+ rc = TCL_ERROR;
|
|
+ }else{
|
|
+ int needFree;
|
|
+ pData = sqlite3_serialize(pDb->db, zSchema, &sz, SQLITE_SERIALIZE_NOCOPY);
|
|
+ if( pData ){
|
|
+ needFree = 0;
|
|
+ }else{
|
|
+ pData = sqlite3_serialize(pDb->db, zSchema, &sz, 0);
|
|
+ needFree = 1;
|
|
+ }
|
|
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pData,sz));
|
|
+ if( needFree ) sqlite3_free(pData);
|
|
+ }
|
|
+#endif
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
** $db status (step|sort|autoindex|vmstep)
|
|
**
|
|
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or
|
|
@@ -3291,7 +3378,42 @@
|
|
** Return the version string for this database.
|
|
*/
|
|
case DB_VERSION: {
|
|
- Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC);
|
|
+ int i;
|
|
+ for(i=2; i<objc; i++){
|
|
+ const char *zArg = Tcl_GetString(objv[i]);
|
|
+ /* Optional arguments to $db version are used for testing purpose */
|
|
+#ifdef SQLITE_TEST
|
|
+ /* $db version -use-legacy-prepare BOOLEAN
|
|
+ **
|
|
+ ** Turn the use of legacy sqlite3_prepare() on or off.
|
|
+ */
|
|
+ if( strcmp(zArg, "-use-legacy-prepare")==0 && i+1<objc ){
|
|
+ i++;
|
|
+ if( Tcl_GetBooleanFromObj(interp, objv[i], &pDb->bLegacyPrepare) ){
|
|
+ return TCL_ERROR;
|
|
+ }
|
|
+ }else
|
|
+
|
|
+ /* $db version -last-stmt-ptr
|
|
+ **
|
|
+ ** Return a string which is a hex encoding of the pointer to the
|
|
+ ** most recent sqlite3_stmt in the statement cache.
|
|
+ */
|
|
+ if( strcmp(zArg, "-last-stmt-ptr")==0 ){
|
|
+ char zBuf[100];
|
|
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%p",
|
|
+ pDb->stmtList ? pDb->stmtList->pStmt: 0);
|
|
+ Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
|
+ }else
|
|
+#endif /* SQLITE_TEST */
|
|
+ {
|
|
+ Tcl_AppendResult(interp, "unknown argument: ", zArg, (char*)0);
|
|
+ return TCL_ERROR;
|
|
+ }
|
|
+ }
|
|
+ if( i==2 ){
|
|
+ Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC);
|
|
+ }
|
|
break;
|
|
}
|
|
|
|
@@ -3316,6 +3438,24 @@
|
|
#endif /* SQLITE_TCL_NRE */
|
|
|
|
/*
|
|
+** Issue the usage message when the "sqlite3" command arguments are
|
|
+** incorrect.
|
|
+*/
|
|
+static int sqliteCmdUsage(
|
|
+ Tcl_Interp *interp,
|
|
+ Tcl_Obj *const*objv
|
|
+){
|
|
+ Tcl_WrongNumArgs(interp, 1, objv,
|
|
+ "HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
|
|
+ " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
|
|
+#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
|
|
+ " ?-key CODECKEY?"
|
|
+#endif
|
|
+ );
|
|
+ return TCL_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN?
|
|
** ?-create BOOLEAN? ?-nomutex BOOLEAN?
|
|
**
|
|
@@ -3340,7 +3480,7 @@
|
|
const char *zArg;
|
|
char *zErrMsg;
|
|
int i;
|
|
- const char *zFile;
|
|
+ const char *zFile = 0;
|
|
const char *zVfs = 0;
|
|
int flags;
|
|
Tcl_DString translatedFilename;
|
|
@@ -3351,7 +3491,7 @@
|
|
int rc;
|
|
|
|
/* In normal use, each TCL interpreter runs in a single thread. So
|
|
- ** by default, we can turn of mutexing on SQLite database connections.
|
|
+ ** by default, we can turn off mutexing on SQLite database connections.
|
|
** However, for testing purposes it is useful to have mutexes turned
|
|
** on. So, by default, mutexes default off. But if compiled with
|
|
** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on.
|
|
@@ -3362,6 +3502,7 @@
|
|
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX;
|
|
#endif
|
|
|
|
+ if( objc==1 ) return sqliteCmdUsage(interp, objv);
|
|
if( objc==2 ){
|
|
zArg = Tcl_GetStringFromObj(objv[1], 0);
|
|
if( strcmp(zArg,"-version")==0 ){
|
|
@@ -3380,18 +3521,26 @@
|
|
#endif
|
|
return TCL_OK;
|
|
}
|
|
+ if( zArg[0]=='-' ) return sqliteCmdUsage(interp, objv);
|
|
}
|
|
- for(i=3; i+1<objc; i+=2){
|
|
+ for(i=2; i<objc; i++){
|
|
zArg = Tcl_GetString(objv[i]);
|
|
+ if( zArg[0]!='-' ){
|
|
+ if( zFile!=0 ) return sqliteCmdUsage(interp, objv);
|
|
+ zFile = zArg;
|
|
+ continue;
|
|
+ }
|
|
+ if( i==objc-1 ) return sqliteCmdUsage(interp, objv);
|
|
+ i++;
|
|
if( strcmp(zArg,"-key")==0 ){
|
|
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
|
|
- pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey);
|
|
+ pKey = Tcl_GetByteArrayFromObj(objv[i], &nKey);
|
|
#endif
|
|
}else if( strcmp(zArg, "-vfs")==0 ){
|
|
- zVfs = Tcl_GetString(objv[i+1]);
|
|
+ zVfs = Tcl_GetString(objv[i]);
|
|
}else if( strcmp(zArg, "-readonly")==0 ){
|
|
int b;
|
|
- if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
|
+ if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR;
|
|
if( b ){
|
|
flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
|
|
flags |= SQLITE_OPEN_READONLY;
|
|
@@ -3401,7 +3550,7 @@
|
|
}
|
|
}else if( strcmp(zArg, "-create")==0 ){
|
|
int b;
|
|
- if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
|
+ if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR;
|
|
if( b && (flags & SQLITE_OPEN_READONLY)==0 ){
|
|
flags |= SQLITE_OPEN_CREATE;
|
|
}else{
|
|
@@ -3409,7 +3558,7 @@
|
|
}
|
|
}else if( strcmp(zArg, "-nomutex")==0 ){
|
|
int b;
|
|
- if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
|
+ if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR;
|
|
if( b ){
|
|
flags |= SQLITE_OPEN_NOMUTEX;
|
|
flags &= ~SQLITE_OPEN_FULLMUTEX;
|
|
@@ -3418,7 +3567,7 @@
|
|
}
|
|
}else if( strcmp(zArg, "-fullmutex")==0 ){
|
|
int b;
|
|
- if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
|
+ if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR;
|
|
if( b ){
|
|
flags |= SQLITE_OPEN_FULLMUTEX;
|
|
flags &= ~SQLITE_OPEN_NOMUTEX;
|
|
@@ -3427,7 +3576,7 @@
|
|
}
|
|
}else if( strcmp(zArg, "-uri")==0 ){
|
|
int b;
|
|
- if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
|
+ if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR;
|
|
if( b ){
|
|
flags |= SQLITE_OPEN_URI;
|
|
}else{
|
|
@@ -3438,20 +3587,10 @@
|
|
return TCL_ERROR;
|
|
}
|
|
}
|
|
- if( objc<3 || (objc&1)!=1 ){
|
|
- Tcl_WrongNumArgs(interp, 1, objv,
|
|
- "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
|
|
- " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
|
|
-#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
|
|
- " ?-key CODECKEY?"
|
|
-#endif
|
|
- );
|
|
- return TCL_ERROR;
|
|
- }
|
|
zErrMsg = 0;
|
|
p = (SqliteDb*)Tcl_Alloc( sizeof(*p) );
|
|
memset(p, 0, sizeof(*p));
|
|
- zFile = Tcl_GetStringFromObj(objv[2], 0);
|
|
+ if( zFile==0 ) zFile = "";
|
|
zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename);
|
|
rc = sqlite3_open_v2(zFile, &p->db, flags, zVfs);
|
|
Tcl_DStringFree(&translatedFilename);
|
|
@@ -3551,731 +3690,74 @@
|
|
int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
|
#endif
|
|
|
|
-#ifdef TCLSH
|
|
-/*****************************************************************************
|
|
-** All of the code that follows is used to build standalone TCL interpreters
|
|
-** that are statically linked with SQLite. Enable these by compiling
|
|
-** with -DTCLSH=n where n can be 1 or 2. An n of 1 generates a standard
|
|
-** tclsh but with SQLite built in. An n of 2 generates the SQLite space
|
|
-** analysis program.
|
|
-*/
|
|
-
|
|
-#if defined(SQLITE_TEST) || defined(SQLITE_TCLMD5)
|
|
/*
|
|
- * This code implements the MD5 message-digest algorithm.
|
|
- * The algorithm is due to Ron Rivest. This code was
|
|
- * written by Colin Plumb in 1993, no copyright is claimed.
|
|
- * This code is in the public domain; do with it what you wish.
|
|
- *
|
|
- * Equivalent code is available from RSA Data Security, Inc.
|
|
- * This code has been tested against that, and is equivalent,
|
|
- * except that you don't need to include two pages of legalese
|
|
- * with every copy.
|
|
- *
|
|
- * To compute the message digest of a chunk of bytes, declare an
|
|
- * MD5Context structure, pass it to MD5Init, call MD5Update as
|
|
- * needed on buffers full of bytes, and then call MD5Final, which
|
|
- * will fill a supplied 16-byte array with the digest.
|
|
- */
|
|
-
|
|
-/*
|
|
- * If compiled on a machine that doesn't have a 32-bit integer,
|
|
- * you just set "uint32" to the appropriate datatype for an
|
|
- * unsigned 32-bit integer. For example:
|
|
- *
|
|
- * cc -Duint32='unsigned long' md5.c
|
|
- *
|
|
- */
|
|
-#ifndef uint32
|
|
-# define uint32 unsigned int
|
|
-#endif
|
|
-
|
|
-struct MD5Context {
|
|
- int isInit;
|
|
- uint32 buf[4];
|
|
- uint32 bits[2];
|
|
- unsigned char in[64];
|
|
-};
|
|
-typedef struct MD5Context MD5Context;
|
|
-
|
|
-/*
|
|
- * Note: this code is harmless on little-endian machines.
|
|
- */
|
|
-static void byteReverse (unsigned char *buf, unsigned longs){
|
|
- uint32 t;
|
|
- do {
|
|
- t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
|
|
- ((unsigned)buf[1]<<8 | buf[0]);
|
|
- *(uint32 *)buf = t;
|
|
- buf += 4;
|
|
- } while (--longs);
|
|
-}
|
|
-/* The four core functions - F1 is optimized somewhat */
|
|
-
|
|
-/* #define F1(x, y, z) (x & y | ~x & z) */
|
|
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
|
-#define F2(x, y, z) F1(z, x, y)
|
|
-#define F3(x, y, z) (x ^ y ^ z)
|
|
-#define F4(x, y, z) (y ^ (x | ~z))
|
|
-
|
|
-/* This is the central step in the MD5 algorithm. */
|
|
-#define MD5STEP(f, w, x, y, z, data, s) \
|
|
- ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
|
-
|
|
-/*
|
|
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
|
|
- * reflect the addition of 16 longwords of new data. MD5Update blocks
|
|
- * the data and converts bytes into longwords for this routine.
|
|
- */
|
|
-static void MD5Transform(uint32 buf[4], const uint32 in[16]){
|
|
- register uint32 a, b, c, d;
|
|
-
|
|
- a = buf[0];
|
|
- b = buf[1];
|
|
- c = buf[2];
|
|
- d = buf[3];
|
|
-
|
|
- MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
|
|
- MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
|
|
- MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
|
|
- MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
|
|
- MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
|
|
- MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
|
|
- MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
|
|
- MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
|
|
- MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
|
|
- MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
|
|
- MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
|
|
- MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
|
|
- MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
|
|
- MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
|
|
- MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
|
|
- MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
|
|
-
|
|
- MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
|
|
- MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
|
|
- MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
|
|
- MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
|
|
- MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
|
|
- MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
|
|
- MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
|
|
- MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
|
|
- MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
|
|
- MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
|
|
- MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
|
|
- MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
|
|
- MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
|
|
- MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
|
|
- MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
|
|
- MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
|
|
-
|
|
- MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
|
|
- MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
|
|
- MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
|
|
- MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
|
|
- MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
|
|
- MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
|
|
- MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
|
|
- MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
|
|
- MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
|
|
- MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
|
|
- MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
|
|
- MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
|
|
- MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
|
|
- MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
|
|
- MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
|
|
- MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
|
|
-
|
|
- MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
|
|
- MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
|
|
- MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
|
|
- MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
|
|
- MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
|
|
- MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
|
|
- MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
|
|
- MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
|
|
- MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
|
|
- MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
|
|
- MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
|
|
- MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
|
|
- MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
|
|
- MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
|
|
- MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
|
|
- MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
|
|
-
|
|
- buf[0] += a;
|
|
- buf[1] += b;
|
|
- buf[2] += c;
|
|
- buf[3] += d;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
|
- * initialization constants.
|
|
- */
|
|
-static void MD5Init(MD5Context *ctx){
|
|
- ctx->isInit = 1;
|
|
- ctx->buf[0] = 0x67452301;
|
|
- ctx->buf[1] = 0xefcdab89;
|
|
- ctx->buf[2] = 0x98badcfe;
|
|
- ctx->buf[3] = 0x10325476;
|
|
- ctx->bits[0] = 0;
|
|
- ctx->bits[1] = 0;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Update context to reflect the concatenation of another buffer full
|
|
- * of bytes.
|
|
- */
|
|
-static
|
|
-void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
|
- uint32 t;
|
|
-
|
|
- /* Update bitcount */
|
|
-
|
|
- t = ctx->bits[0];
|
|
- if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
|
|
- ctx->bits[1]++; /* Carry from low to high */
|
|
- ctx->bits[1] += len >> 29;
|
|
-
|
|
- t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
|
-
|
|
- /* Handle any leading odd-sized chunks */
|
|
-
|
|
- if ( t ) {
|
|
- unsigned char *p = (unsigned char *)ctx->in + t;
|
|
-
|
|
- t = 64-t;
|
|
- if (len < t) {
|
|
- memcpy(p, buf, len);
|
|
- return;
|
|
- }
|
|
- memcpy(p, buf, t);
|
|
- byteReverse(ctx->in, 16);
|
|
- MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
|
- buf += t;
|
|
- len -= t;
|
|
- }
|
|
-
|
|
- /* Process data in 64-byte chunks */
|
|
-
|
|
- while (len >= 64) {
|
|
- memcpy(ctx->in, buf, 64);
|
|
- byteReverse(ctx->in, 16);
|
|
- MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
|
- buf += 64;
|
|
- len -= 64;
|
|
- }
|
|
-
|
|
- /* Handle any remaining bytes of data. */
|
|
-
|
|
- memcpy(ctx->in, buf, len);
|
|
-}
|
|
-
|
|
-/*
|
|
- * Final wrapup - pad to 64-byte boundary with the bit pattern
|
|
- * 1 0* (64-bit count of bits processed, MSB-first)
|
|
- */
|
|
-static void MD5Final(unsigned char digest[16], MD5Context *ctx){
|
|
- unsigned count;
|
|
- unsigned char *p;
|
|
-
|
|
- /* Compute number of bytes mod 64 */
|
|
- count = (ctx->bits[0] >> 3) & 0x3F;
|
|
-
|
|
- /* Set the first char of padding to 0x80. This is safe since there is
|
|
- always at least one byte free */
|
|
- p = ctx->in + count;
|
|
- *p++ = 0x80;
|
|
-
|
|
- /* Bytes of padding needed to make 64 bytes */
|
|
- count = 64 - 1 - count;
|
|
-
|
|
- /* Pad out to 56 mod 64 */
|
|
- if (count < 8) {
|
|
- /* Two lots of padding: Pad the first block to 64 bytes */
|
|
- memset(p, 0, count);
|
|
- byteReverse(ctx->in, 16);
|
|
- MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
|
-
|
|
- /* Now fill the next block with 56 bytes */
|
|
- memset(ctx->in, 0, 56);
|
|
- } else {
|
|
- /* Pad block to 56 bytes */
|
|
- memset(p, 0, count-8);
|
|
- }
|
|
- byteReverse(ctx->in, 14);
|
|
-
|
|
- /* Append length in bits and transform */
|
|
- memcpy(ctx->in + 14*4, ctx->bits, 8);
|
|
-
|
|
- MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
|
- byteReverse((unsigned char *)ctx->buf, 4);
|
|
- memcpy(digest, ctx->buf, 16);
|
|
-}
|
|
-
|
|
-/*
|
|
-** Convert a 128-bit MD5 digest into a 32-digit base-16 number.
|
|
+** If the TCLSH macro is defined, add code to make a stand-alone program.
|
|
*/
|
|
-static void MD5DigestToBase16(unsigned char *digest, char *zBuf){
|
|
- static char const zEncode[] = "0123456789abcdef";
|
|
- int i, j;
|
|
+#if defined(TCLSH)
|
|
|
|
- for(j=i=0; i<16; i++){
|
|
- int a = digest[i];
|
|
- zBuf[j++] = zEncode[(a>>4)&0xf];
|
|
- zBuf[j++] = zEncode[a & 0xf];
|
|
- }
|
|
- zBuf[j] = 0;
|
|
-}
|
|
-
|
|
-
|
|
-/*
|
|
-** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers
|
|
-** each representing 16 bits of the digest and separated from each
|
|
-** other by a "-" character.
|
|
+/* This is the main routine for an ordinary TCL shell. If there are
|
|
+** are arguments, run the first argument as a script. Otherwise,
|
|
+** read TCL commands from standard input
|
|
*/
|
|
-static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){
|
|
- int i, j;
|
|
- unsigned int x;
|
|
- for(i=j=0; i<16; i+=2){
|
|
- x = digest[i]*256 + digest[i+1];
|
|
- if( i>0 ) zDigest[j++] = '-';
|
|
- sqlite3_snprintf(50-j, &zDigest[j], "%05u", x);
|
|
- j += 5;
|
|
- }
|
|
- zDigest[j] = 0;
|
|
-}
|
|
-
|
|
-/*
|
|
-** A TCL command for md5. The argument is the text to be hashed. The
|
|
-** Result is the hash in base64.
|
|
-*/
|
|
-static int SQLITE_TCLAPI md5_cmd(
|
|
- void*cd,
|
|
- Tcl_Interp *interp,
|
|
- int argc,
|
|
- const char **argv
|
|
-){
|
|
- MD5Context ctx;
|
|
- unsigned char digest[16];
|
|
- char zBuf[50];
|
|
- void (*converter)(unsigned char*, char*);
|
|
-
|
|
- if( argc!=2 ){
|
|
- Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
|
- " TEXT\"", (char*)0);
|
|
- return TCL_ERROR;
|
|
- }
|
|
- MD5Init(&ctx);
|
|
- MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
|
|
- MD5Final(digest, &ctx);
|
|
- converter = (void(*)(unsigned char*,char*))cd;
|
|
- converter(digest, zBuf);
|
|
- Tcl_AppendResult(interp, zBuf, (char*)0);
|
|
- return TCL_OK;
|
|
-}
|
|
-
|
|
-/*
|
|
-** A TCL command to take the md5 hash of a file. The argument is the
|
|
-** name of the file.
|
|
-*/
|
|
-static int SQLITE_TCLAPI md5file_cmd(
|
|
- void*cd,
|
|
- Tcl_Interp *interp,
|
|
- int argc,
|
|
- const char **argv
|
|
-){
|
|
- FILE *in;
|
|
- MD5Context ctx;
|
|
- void (*converter)(unsigned char*, char*);
|
|
- unsigned char digest[16];
|
|
- char zBuf[10240];
|
|
-
|
|
- if( argc!=2 ){
|
|
- Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
|
- " FILENAME\"", (char*)0);
|
|
- return TCL_ERROR;
|
|
- }
|
|
- in = fopen(argv[1],"rb");
|
|
- if( in==0 ){
|
|
- Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
|
- "\" for reading", (char*)0);
|
|
- return TCL_ERROR;
|
|
- }
|
|
- MD5Init(&ctx);
|
|
- for(;;){
|
|
- int n;
|
|
- n = (int)fread(zBuf, 1, sizeof(zBuf), in);
|
|
- if( n<=0 ) break;
|
|
- MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
|
|
- }
|
|
- fclose(in);
|
|
- MD5Final(digest, &ctx);
|
|
- converter = (void(*)(unsigned char*,char*))cd;
|
|
- converter(digest, zBuf);
|
|
- Tcl_AppendResult(interp, zBuf, (char*)0);
|
|
- return TCL_OK;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Register the four new TCL commands for generating MD5 checksums
|
|
-** with the TCL interpreter.
|
|
-*/
|
|
-int Md5_Init(Tcl_Interp *interp){
|
|
- Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd,
|
|
- MD5DigestToBase16, 0);
|
|
- Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc*)md5_cmd,
|
|
- MD5DigestToBase10x8, 0);
|
|
- Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd,
|
|
- MD5DigestToBase16, 0);
|
|
- Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc*)md5file_cmd,
|
|
- MD5DigestToBase10x8, 0);
|
|
- return TCL_OK;
|
|
-}
|
|
-#endif /* defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) */
|
|
-
|
|
-#if defined(SQLITE_TEST)
|
|
-/*
|
|
-** During testing, the special md5sum() aggregate function is available.
|
|
-** inside SQLite. The following routines implement that function.
|
|
-*/
|
|
-static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
|
|
- MD5Context *p;
|
|
- int i;
|
|
- if( argc<1 ) return;
|
|
- p = sqlite3_aggregate_context(context, sizeof(*p));
|
|
- if( p==0 ) return;
|
|
- if( !p->isInit ){
|
|
- MD5Init(p);
|
|
- }
|
|
- for(i=0; i<argc; i++){
|
|
- const char *zData = (char*)sqlite3_value_text(argv[i]);
|
|
- if( zData ){
|
|
- MD5Update(p, (unsigned char*)zData, (int)strlen(zData));
|
|
- }
|
|
- }
|
|
-}
|
|
-static void md5finalize(sqlite3_context *context){
|
|
- MD5Context *p;
|
|
- unsigned char digest[16];
|
|
- char zBuf[33];
|
|
- p = sqlite3_aggregate_context(context, sizeof(*p));
|
|
- MD5Final(digest,p);
|
|
- MD5DigestToBase16(digest, zBuf);
|
|
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
|
-}
|
|
-int Md5_Register(
|
|
- sqlite3 *db,
|
|
- char **pzErrMsg,
|
|
- const sqlite3_api_routines *pThunk
|
|
-){
|
|
- int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
|
|
- md5step, md5finalize);
|
|
- sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */
|
|
- return rc;
|
|
-}
|
|
-#endif /* defined(SQLITE_TEST) */
|
|
-
|
|
-
|
|
-/*
|
|
-** If the macro TCLSH is one, then put in code this for the
|
|
-** "main" routine that will initialize Tcl and take input from
|
|
-** standard input, or if a file is named on the command line
|
|
-** the TCL interpreter reads and evaluates that file.
|
|
-*/
|
|
-#if TCLSH==1
|
|
static const char *tclsh_main_loop(void){
|
|
static const char zMainloop[] =
|
|
- "set line {}\n"
|
|
- "while {![eof stdin]} {\n"
|
|
- "if {$line!=\"\"} {\n"
|
|
- "puts -nonewline \"> \"\n"
|
|
- "} else {\n"
|
|
- "puts -nonewline \"% \"\n"
|
|
- "}\n"
|
|
- "flush stdout\n"
|
|
- "append line [gets stdin]\n"
|
|
- "if {[info complete $line]} {\n"
|
|
- "if {[catch {uplevel #0 $line} result]} {\n"
|
|
- "puts stderr \"Error: $result\"\n"
|
|
- "} elseif {$result!=\"\"} {\n"
|
|
- "puts $result\n"
|
|
+ "if {[llength $argv]>=1} {\n"
|
|
+ "set argv0 [lindex $argv 0]\n"
|
|
+ "set argv [lrange $argv 1 end]\n"
|
|
+ "source $argv0\n"
|
|
+ "} else {\n"
|
|
+ "set line {}\n"
|
|
+ "while {![eof stdin]} {\n"
|
|
+ "if {$line!=\"\"} {\n"
|
|
+ "puts -nonewline \"> \"\n"
|
|
+ "} else {\n"
|
|
+ "puts -nonewline \"% \"\n"
|
|
"}\n"
|
|
- "set line {}\n"
|
|
- "} else {\n"
|
|
- "append line \\n\n"
|
|
+ "flush stdout\n"
|
|
+ "append line [gets stdin]\n"
|
|
+ "if {[info complete $line]} {\n"
|
|
+ "if {[catch {uplevel #0 $line} result]} {\n"
|
|
+ "puts stderr \"Error: $result\"\n"
|
|
+ "} elseif {$result!=\"\"} {\n"
|
|
+ "puts $result\n"
|
|
+ "}\n"
|
|
+ "set line {}\n"
|
|
+ "} else {\n"
|
|
+ "append line \\n\n"
|
|
+ "}\n"
|
|
"}\n"
|
|
"}\n"
|
|
;
|
|
return zMainloop;
|
|
}
|
|
-#endif
|
|
-#if TCLSH==2
|
|
-static const char *tclsh_main_loop(void);
|
|
-#endif
|
|
|
|
-#ifdef SQLITE_TEST
|
|
-static void init_all(Tcl_Interp *);
|
|
-static int SQLITE_TCLAPI init_all_cmd(
|
|
- ClientData cd,
|
|
- Tcl_Interp *interp,
|
|
- int objc,
|
|
- Tcl_Obj *CONST objv[]
|
|
-){
|
|
-
|
|
- Tcl_Interp *slave;
|
|
- if( objc!=2 ){
|
|
- Tcl_WrongNumArgs(interp, 1, objv, "SLAVE");
|
|
- return TCL_ERROR;
|
|
- }
|
|
-
|
|
- slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1]));
|
|
- if( !slave ){
|
|
- return TCL_ERROR;
|
|
- }
|
|
-
|
|
- init_all(slave);
|
|
- return TCL_OK;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Tclcmd: db_use_legacy_prepare DB BOOLEAN
|
|
-**
|
|
-** The first argument to this command must be a database command created by
|
|
-** [sqlite3]. If the second argument is true, then the handle is configured
|
|
-** to use the sqlite3_prepare_v2() function to prepare statements. If it
|
|
-** is false, sqlite3_prepare().
|
|
-*/
|
|
-static int SQLITE_TCLAPI db_use_legacy_prepare_cmd(
|
|
- ClientData cd,
|
|
- Tcl_Interp *interp,
|
|
- int objc,
|
|
- Tcl_Obj *CONST objv[]
|
|
-){
|
|
- Tcl_CmdInfo cmdInfo;
|
|
- SqliteDb *pDb;
|
|
- int bPrepare;
|
|
-
|
|
- if( objc!=3 ){
|
|
- Tcl_WrongNumArgs(interp, 1, objv, "DB BOOLEAN");
|
|
- return TCL_ERROR;
|
|
- }
|
|
-
|
|
- if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
|
|
- Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0);
|
|
- return TCL_ERROR;
|
|
- }
|
|
- pDb = (SqliteDb*)cmdInfo.objClientData;
|
|
- if( Tcl_GetBooleanFromObj(interp, objv[2], &bPrepare) ){
|
|
- return TCL_ERROR;
|
|
- }
|
|
-
|
|
- pDb->bLegacyPrepare = bPrepare;
|
|
-
|
|
- Tcl_ResetResult(interp);
|
|
- return TCL_OK;
|
|
-}
|
|
-
|
|
-/*
|
|
-** Tclcmd: db_last_stmt_ptr DB
|
|
-**
|
|
-** If the statement cache associated with database DB is not empty,
|
|
-** return the text representation of the most recently used statement
|
|
-** handle.
|
|
-*/
|
|
-static int SQLITE_TCLAPI db_last_stmt_ptr(
|
|
- ClientData cd,
|
|
- Tcl_Interp *interp,
|
|
- int objc,
|
|
- Tcl_Obj *CONST objv[]
|
|
-){
|
|
- extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
|
|
- Tcl_CmdInfo cmdInfo;
|
|
- SqliteDb *pDb;
|
|
- sqlite3_stmt *pStmt = 0;
|
|
- char zBuf[100];
|
|
-
|
|
- if( objc!=2 ){
|
|
- Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
|
- return TCL_ERROR;
|
|
- }
|
|
-
|
|
- if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
|
|
- Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0);
|
|
- return TCL_ERROR;
|
|
- }
|
|
- pDb = (SqliteDb*)cmdInfo.objClientData;
|
|
-
|
|
- if( pDb->stmtList ) pStmt = pDb->stmtList->pStmt;
|
|
- if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ){
|
|
- return TCL_ERROR;
|
|
- }
|
|
- Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
|
-
|
|
- return TCL_OK;
|
|
-}
|
|
-#endif /* SQLITE_TEST */
|
|
-
|
|
-/*
|
|
-** Configure the interpreter passed as the first argument to have access
|
|
-** to the commands and linked variables that make up:
|
|
-**
|
|
-** * the [sqlite3] extension itself,
|
|
-**
|
|
-** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and
|
|
-**
|
|
-** * If SQLITE_TEST is set, the various test interfaces used by the Tcl
|
|
-** test suite.
|
|
-*/
|
|
-static void init_all(Tcl_Interp *interp){
|
|
- Sqlite3_Init(interp);
|
|
-
|
|
-#if defined(SQLITE_TEST) || defined(SQLITE_TCLMD5)
|
|
- Md5_Init(interp);
|
|
-#endif
|
|
-
|
|
-#ifdef SQLITE_TEST
|
|
- {
|
|
- extern int Sqliteconfig_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest1_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest2_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest3_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest4_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest5_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest6_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest7_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest8_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest9_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestasync_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_autoext_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_blob_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
|
|
- extern int Sqlitetest_func_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_hexio_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_init_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_malloc_Init(Tcl_Interp*);
|
|
- extern int Sqlitetest_mutex_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestschema_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestsse_Init(Tcl_Interp*);
|
|
- extern int Sqlitetesttclvar_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestfs_Init(Tcl_Interp*);
|
|
- extern int SqlitetestThread_Init(Tcl_Interp*);
|
|
- extern int SqlitetestOnefile_Init();
|
|
- extern int SqlitetestOsinst_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestbackup_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestintarray_Init(Tcl_Interp*);
|
|
- extern int Sqlitetestvfs_Init(Tcl_Interp *);
|
|
- extern int Sqlitetestrtree_Init(Tcl_Interp*);
|
|
- extern int Sqlitequota_Init(Tcl_Interp*);
|
|
- extern int Sqlitemultiplex_Init(Tcl_Interp*);
|
|
- extern int SqliteSuperlock_Init(Tcl_Interp*);
|
|
- extern int SqlitetestSyscall_Init(Tcl_Interp*);
|
|
-#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
|
- extern int TestSession_Init(Tcl_Interp*);
|
|
-#endif
|
|
- extern int Fts5tcl_Init(Tcl_Interp *);
|
|
- extern int SqliteRbu_Init(Tcl_Interp*);
|
|
- extern int Sqlitetesttcl_Init(Tcl_Interp*);
|
|
-#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
|
- extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
|
|
-#endif
|
|
-
|
|
-#ifdef SQLITE_ENABLE_ZIPVFS
|
|
- extern int Zipvfs_Init(Tcl_Interp*);
|
|
- Zipvfs_Init(interp);
|
|
-#endif
|
|
-
|
|
- Sqliteconfig_Init(interp);
|
|
- Sqlitetest1_Init(interp);
|
|
- Sqlitetest2_Init(interp);
|
|
- Sqlitetest3_Init(interp);
|
|
- Sqlitetest4_Init(interp);
|
|
- Sqlitetest5_Init(interp);
|
|
- Sqlitetest6_Init(interp);
|
|
- Sqlitetest7_Init(interp);
|
|
- Sqlitetest8_Init(interp);
|
|
- Sqlitetest9_Init(interp);
|
|
- Sqlitetestasync_Init(interp);
|
|
- Sqlitetest_autoext_Init(interp);
|
|
- Sqlitetest_blob_Init(interp);
|
|
- Sqlitetest_demovfs_Init(interp);
|
|
- Sqlitetest_func_Init(interp);
|
|
- Sqlitetest_hexio_Init(interp);
|
|
- Sqlitetest_init_Init(interp);
|
|
- Sqlitetest_malloc_Init(interp);
|
|
- Sqlitetest_mutex_Init(interp);
|
|
- Sqlitetestschema_Init(interp);
|
|
- Sqlitetesttclvar_Init(interp);
|
|
- Sqlitetestfs_Init(interp);
|
|
- SqlitetestThread_Init(interp);
|
|
- SqlitetestOnefile_Init();
|
|
- SqlitetestOsinst_Init(interp);
|
|
- Sqlitetestbackup_Init(interp);
|
|
- Sqlitetestintarray_Init(interp);
|
|
- Sqlitetestvfs_Init(interp);
|
|
- Sqlitetestrtree_Init(interp);
|
|
- Sqlitequota_Init(interp);
|
|
- Sqlitemultiplex_Init(interp);
|
|
- SqliteSuperlock_Init(interp);
|
|
- SqlitetestSyscall_Init(interp);
|
|
-#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
|
- TestSession_Init(interp);
|
|
-#endif
|
|
- Fts5tcl_Init(interp);
|
|
- SqliteRbu_Init(interp);
|
|
- Sqlitetesttcl_Init(interp);
|
|
-
|
|
-#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
|
- Sqlitetestfts3_Init(interp);
|
|
-#endif
|
|
-
|
|
- Tcl_CreateObjCommand(
|
|
- interp, "load_testfixture_extensions", init_all_cmd, 0, 0
|
|
- );
|
|
- Tcl_CreateObjCommand(
|
|
- interp, "db_use_legacy_prepare", db_use_legacy_prepare_cmd, 0, 0
|
|
- );
|
|
- Tcl_CreateObjCommand(
|
|
- interp, "db_last_stmt_ptr", db_last_stmt_ptr, 0, 0
|
|
- );
|
|
-
|
|
-#ifdef SQLITE_SSE
|
|
- Sqlitetestsse_Init(interp);
|
|
-#endif
|
|
- }
|
|
-#endif
|
|
-}
|
|
-
|
|
-/* Needed for the setrlimit() system call on unix */
|
|
-#if defined(unix)
|
|
-#include <sys/resource.h>
|
|
-#endif
|
|
-
|
|
#define TCLSH_MAIN main /* Needed to fake out mktclapp */
|
|
int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){
|
|
Tcl_Interp *interp;
|
|
+ int i;
|
|
+ const char *zScript = 0;
|
|
+ char zArgc[32];
|
|
+#if defined(TCLSH_INIT_PROC)
|
|
+ extern const char *TCLSH_INIT_PROC(Tcl_Interp*);
|
|
+#endif
|
|
|
|
#if !defined(_WIN32_WCE)
|
|
- if( getenv("BREAK") ){
|
|
- fprintf(stderr,
|
|
- "attach debugger to process %d and press any key to continue.\n",
|
|
- GETPID());
|
|
- fgetc(stdin);
|
|
+ if( getenv("SQLITE_DEBUG_BREAK") ){
|
|
+ if( isatty(0) && isatty(2) ){
|
|
+ fprintf(stderr,
|
|
+ "attach debugger to process %d and press any key to continue.\n",
|
|
+ GETPID());
|
|
+ fgetc(stdin);
|
|
+ }else{
|
|
+#if defined(_WIN32) || defined(WIN32)
|
|
+ DebugBreak();
|
|
+#elif defined(SIGTRAP)
|
|
+ raise(SIGTRAP);
|
|
+#endif
|
|
+ }
|
|
}
|
|
#endif
|
|
|
|
- /* Since the primary use case for this binary is testing of SQLite,
|
|
- ** be sure to generate core files if we crash */
|
|
-#if defined(SQLITE_TEST) && defined(unix)
|
|
- { struct rlimit x;
|
|
- getrlimit(RLIMIT_CORE, &x);
|
|
- x.rlim_cur = x.rlim_max;
|
|
- setrlimit(RLIMIT_CORE, &x);
|
|
- }
|
|
-#endif /* SQLITE_TEST && unix */
|
|
-
|
|
-
|
|
/* Call sqlite3_shutdown() once before doing anything else. This is to
|
|
** test that sqlite3_shutdown() can be safely called by a process before
|
|
** sqlite3_initialize() is. */
|
|
@@ -4284,32 +3766,27 @@
|
|
Tcl_FindExecutable(argv[0]);
|
|
Tcl_SetSystemEncoding(NULL, "utf-8");
|
|
interp = Tcl_CreateInterp();
|
|
+ Sqlite3_Init(interp);
|
|
|
|
-#if TCLSH==2
|
|
- sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
|
+ sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-1);
|
|
+ Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar(interp,"argv0",argv[0],TCL_GLOBAL_ONLY);
|
|
+ Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY);
|
|
+ for(i=1; i<argc; i++){
|
|
+ Tcl_SetVar(interp, "argv", argv[i],
|
|
+ TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
|
|
+ }
|
|
+#if defined(TCLSH_INIT_PROC)
|
|
+ zScript = TCLSH_INIT_PROC(interp);
|
|
#endif
|
|
-
|
|
- init_all(interp);
|
|
- if( argc>=2 ){
|
|
- int i;
|
|
- char zArgc[32];
|
|
- sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH));
|
|
- Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY);
|
|
- Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY);
|
|
- Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY);
|
|
- for(i=3-TCLSH; i<argc; i++){
|
|
- Tcl_SetVar(interp, "argv", argv[i],
|
|
- TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
|
|
- }
|
|
- if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){
|
|
- const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
|
|
- if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp);
|
|
- fprintf(stderr,"%s: %s\n", *argv, zInfo);
|
|
- return 1;
|
|
- }
|
|
+ if( zScript==0 ){
|
|
+ zScript = tclsh_main_loop();
|
|
}
|
|
- if( TCLSH==2 || argc<=1 ){
|
|
- Tcl_GlobalEval(interp, tclsh_main_loop());
|
|
+ if( Tcl_GlobalEval(interp, zScript)!=TCL_OK ){
|
|
+ const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
|
|
+ if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp);
|
|
+ fprintf(stderr,"%s: %s\n", *argv, zInfo);
|
|
+ return 1;
|
|
}
|
|
return 0;
|
|
}
|