diff --git a/Makefile.in b/Makefile.in
index 76c7df32ca..29d7e9e9c0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1268,7 +1268,7 @@ fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuz
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
- valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
+ valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
# The veryquick.test TCL tests.
@@ -1379,6 +1379,9 @@ wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.c
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/test/speedtest1.c sqlite3.c $(TLIBS)
+startup$(TEXE): $(TOP)/test/startup.c sqlite3.c
+ $(CC) -Os -g -DSQLITE_THREADSAFE=0 -o $@ $(TOP)/test/startup.c sqlite3.c $(TLIBS)
+
KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ
kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c
diff --git a/Makefile.msc b/Makefile.msc
index 79dd5ca95c..bcf025f538 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -380,6 +380,9 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
!ENDIF
+# Always enable math functions on Windows
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
+
# Should the rbu extension be enabled? If so, add compilation options
# to enable it.
#
diff --git a/VERSION b/VERSION
index 5c0e053417..b2f5d1d153 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.34.0
+3.35.0
diff --git a/autoconf/configure.ac b/autoconf/configure.ac
index 167626d59e..a86bd2ca39 100644
--- a/autoconf/configure.ac
+++ b/autoconf/configure.ac
@@ -87,7 +87,9 @@ AC_SUBST(READLINE_LIBS)
AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
[--enable-threadsafe], [build a thread-safe library [default=yes]])],
[], [enable_threadsafe=yes])
-if test x"$enable_threadsafe" != "xno"; then
+if test x"$enable_threadsafe" == "xno"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_THREADSAFE=0"
+else
BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
AC_SEARCH_LIBS(pthread_create, pthread)
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
diff --git a/autoconf/tea/win/makefile.vc b/autoconf/tea/win/makefile.vc
index 88b66f173c..d92a8428bf 100644
--- a/autoconf/tea/win/makefile.vc
+++ b/autoconf/tea/win/makefile.vc
@@ -153,7 +153,7 @@ Please `cd` to its location first.
#
#-------------------------------------------------------------------------
-PROJECT = tclsqlite3
+PROJECT = sqlite3
!include "rules.vc"
# nmakehelp -V will search the file for tag, skips until a
diff --git a/configure b/configure
index a03d6fdb3a..1dbef10575 100755
--- a/configure
+++ b/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.34.0.
+# Generated by GNU Autoconf 2.69 for sqlite 3.35.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
-PACKAGE_VERSION='3.34.0'
-PACKAGE_STRING='sqlite 3.34.0'
+PACKAGE_VERSION='3.35.0'
+PACKAGE_STRING='sqlite 3.35.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -903,6 +903,7 @@ with_readline_inc
enable_debug
enable_amalgamation
enable_load_extension
+enable_math
enable_memsys5
enable_memsys3
enable_all
@@ -1466,7 +1467,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures sqlite 3.34.0 to adapt to many kinds of systems.
+\`configure' configures sqlite 3.35.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1531,7 +1532,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sqlite 3.34.0:";;
+ short | recursive ) echo "Configuration of sqlite 3.35.0:";;
esac
cat <<\_ACEOF
@@ -1557,6 +1558,7 @@ Optional Features:
separately
--disable-load-extension
Disable loading of external extensions
+ --disable-math Disable math functions
--enable-memsys5 Enable MEMSYS5
--enable-memsys3 Enable MEMSYS3
--enable-all Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions
@@ -1658,7 +1660,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sqlite configure 3.34.0
+sqlite configure 3.35.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2077,7 +2079,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by sqlite $as_me 3.34.0, which was
+It was created by sqlite $as_me 3.35.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3935,13 +3937,13 @@ if ${lt_cv_nm_interface+:} false; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
- (eval echo "\"\$as_me:3938: $ac_compile\"" >&5)
+ (eval echo "\"\$as_me:3940: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
- (eval echo "\"\$as_me:3941: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval echo "\"\$as_me:3943: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
- (eval echo "\"\$as_me:3944: output\"" >&5)
+ (eval echo "\"\$as_me:3946: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@@ -5147,7 +5149,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5150 "configure"' > conftest.$ac_ext
+ echo '#line 5152 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -6672,11 +6674,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:6675: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6677: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6679: \$? = $ac_status" >&5
+ echo "$as_me:6681: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7011,11 +7013,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7014: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7016: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:7018: \$? = $ac_status" >&5
+ echo "$as_me:7020: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7116,11 +7118,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7119: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7121: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7123: \$? = $ac_status" >&5
+ echo "$as_me:7125: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -7171,11 +7173,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7174: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7176: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7178: \$? = $ac_status" >&5
+ echo "$as_me:7180: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -9551,7 +9553,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9554 "configure"
+#line 9556 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9647,7 +9649,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9650 "configure"
+#line 9652 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -10364,8 +10366,10 @@ fi
if test "x${TCLLIBDIR+set}" != "xset" ; then
TCLLIBDIR='$(libdir)'
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
- TCLLIBDIR=$i
- break
+ if test -d $i ; then
+ TCLLIBDIR=$i
+ break
+ fi
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
@@ -11411,6 +11415,81 @@ else
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
+##########
+# Do we want to support math functions
+#
+# Check whether --enable-math was given.
+if test "${enable_math+set}" = set; then :
+ enableval=$enable_math;
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support math functions" >&5
+$as_echo_n "checking whether to support math functions... " >&6; }
+if test "$enable_math" = "no"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MATH_FUNCTIONS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ceil" >&5
+$as_echo_n "checking for library containing ceil... " >&6; }
+if ${ac_cv_search_ceil+:} 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 ceil ();
+int
+main ()
+{
+return ceil ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' m; 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_ceil=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ceil+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ceil+:} false; then :
+
+else
+ ac_cv_search_ceil=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ceil" >&5
+$as_echo "$ac_cv_search_ceil" >&6; }
+ac_res=$ac_cv_search_ceil
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
##########
# Do we want to support memsys3 and/or memsys5
#
@@ -12236,7 +12315,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by sqlite $as_me 3.34.0, which was
+This file was extended by sqlite $as_me 3.35.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12302,7 +12381,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-sqlite config.status 3.34.0
+sqlite config.status 3.35.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index bb8f4bd563..8a58d3dd33 100644
--- a/configure.ac
+++ b/configure.ac
@@ -134,8 +134,10 @@ AC_ARG_VAR([TCLLIBDIR], [Where to install tcl plugin])
if test "x${TCLLIBDIR+set}" != "xset" ; then
TCLLIBDIR='$(libdir)'
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
- TCLLIBDIR=$i
- break
+ if test -d $i ; then
+ TCLLIBDIR=$i
+ break
+ fi
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
@@ -586,6 +588,20 @@ else
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
+##########
+# Do we want to support math functions
+#
+AC_ARG_ENABLE(math,
+AC_HELP_STRING([--disable-math],[Disable math functions]))
+AC_MSG_CHECKING([whether to support math functions])
+if test "$enable_math" = "no"; then
+ AC_MSG_RESULT([no])
+else
+ AC_MSG_RESULT([yes])
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MATH_FUNCTIONS"
+ AC_SEARCH_LIBS(ceil, m)
+fi
+
##########
# Do we want to support memsys3 and/or memsys5
#
diff --git a/doc/lemon.html b/doc/lemon.html
index bd078c8ec7..457004627e 100644
--- a/doc/lemon.html
+++ b/doc/lemon.html
@@ -1077,7 +1077,7 @@ can choose a different start symbol using the
4.4.19 The %syntax_error directive
-See Error Processing.
+See Error Processing.
4.4.20 The %token_class directive
@@ -1176,7 +1176,7 @@ match any input token.
the wildcard token and some other token, the other token is always used.
The wildcard token is only matched if there are no alternatives.
-
+
5.0 Error Processing
After extensive experimentation over several years, it has been
diff --git a/ext/expert/expert1.test b/ext/expert/expert1.test
index f49f1f5e5c..0e6fc82604 100644
--- a/ext/expert/expert1.test
+++ b/ext/expert/expert1.test
@@ -28,6 +28,7 @@ if {[info commands sqlite3_expert_new]==""} {
return
}
+
set CLI [test_binary_name sqlite3]
set CMD [test_binary_name sqlite3_expert]
@@ -367,6 +368,29 @@ do_setup_rec_test $tn.17.5 {
SEARCH TABLE example USING INDEX example_idx_0000cb3f (B=? AND A>?)
}
+do_setup_rec_test $tn.18.0 {
+ CREATE TABLE SomeObject (
+ a INTEGER PRIMARY KEY,
+ x TEXT GENERATED ALWAYS AS(HEX(a)) VIRTUAL
+ );
+} {
+ SELECT x FROM SomeObject;
+} {
+ (no new indexes)
+ SCAN TABLE SomeObject
+}
+do_setup_rec_test $tn.18.1 {
+ CREATE TABLE SomeObject (
+ a INTEGER PRIMARY KEY,
+ x TEXT GENERATED ALWAYS AS(HEX(a)) VIRTUAL
+ );
+} {
+ SELECT * FROM SomeObject WHERE x=?;
+} {
+ CREATE INDEX SomeObject_idx_00000078 ON SomeObject(x);
+ SEARCH TABLE SomeObject USING COVERING INDEX SomeObject_idx_00000078 (x=?)
+}
+
}
proc do_candidates_test {tn sql res} {
@@ -430,5 +454,5 @@ do_execsql_test 5.3 {
t2 t2_idx_0001295b {100 20 5}
}
-
finish_test
+
diff --git a/ext/expert/sqlite3expert.c b/ext/expert/sqlite3expert.c
index c2a6fe3ba9..863c6a3409 100644
--- a/ext/expert/sqlite3expert.c
+++ b/ext/expert/sqlite3expert.c
@@ -687,7 +687,7 @@ static int idxGetTableInfo(
char *pCsr = 0;
int nPk = 0;
- rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
+ rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%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);
@@ -1721,10 +1721,12 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
idxFinalize(&rc, pIndexXInfo);
idxFinalize(&rc, pWrite);
- for(i=0; inSlot; i++){
- sqlite3_free(pCtx->aSlot[i].z);
+ if( pCtx ){
+ for(i=0; inSlot; i++){
+ sqlite3_free(pCtx->aSlot[i].z);
+ }
+ sqlite3_free(pCtx);
}
- sqlite3_free(pCtx);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c
index 092cad9ac5..bc42fc3d63 100644
--- a/ext/fts3/fts3_write.c
+++ b/ext/fts3/fts3_write.c
@@ -4335,17 +4335,20 @@ static int fts3IncrmergeLoad(
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
if( rc==SQLITE_OK ){
- memcpy(pNode->key.a, reader.term.a, reader.term.n);
+ assert_fts3_nc( reader.term.n>0 || reader.aNode==0 );
+ if( reader.term.n>0 ){
+ memcpy(pNode->key.a, reader.term.a, reader.term.n);
+ }
pNode->key.n = reader.term.n;
if( i>0 ){
char *aBlock = 0;
int nBlock = 0;
pNode = &pWriter->aNodeWriter[i-1];
pNode->iBlock = reader.iChild;
- rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
+ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
- );
+ );
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
diff --git a/ext/fts3/unicode/mkunicode.tcl b/ext/fts3/unicode/mkunicode.tcl
index a4cc540282..58d90c68c7 100644
--- a/ext/fts3/unicode/mkunicode.tcl
+++ b/ext/fts3/unicode/mkunicode.tcl
@@ -742,6 +742,7 @@ proc print_categories {lMap} {
}
iTbl++;
}
+ aAscii[0] = 0; /* 0x00 is never a token character */
}
}]
}
diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c
index 119091574f..77f6d5baba 100644
--- a/ext/fts5/fts5_aux.c
+++ b/ext/fts5/fts5_aux.c
@@ -566,7 +566,7 @@ static int fts5Bm25GetData(
int rc = SQLITE_OK; /* Return code */
Fts5Bm25Data *p; /* Object to return */
- p = pApi->xGetAuxdata(pFts, 0);
+ p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0);
if( p==0 ){
int nPhrase; /* Number of phrases in query */
sqlite3_int64 nRow = 0; /* Number of rows in table */
@@ -640,7 +640,7 @@ static void fts5Bm25Function(
){
const double k1 = 1.2; /* Constant "k1" from BM25 formula */
const double b = 0.75; /* Constant "b" from BM25 formula */
- int rc = SQLITE_OK; /* Error code */
+ int rc; /* Error code */
double score = 0.0; /* SQL function return value */
Fts5Bm25Data *pData; /* Values allocated/calculated once only */
int i; /* Iterator variable */
@@ -672,17 +672,15 @@ static void fts5Bm25Function(
D = (double)nTok;
}
- /* Determine the BM25 score for the current row. */
- for(i=0; rc==SQLITE_OK && inPhrase; i++){
- score += pData->aIDF[i] * (
- ( aFreq[i] * (k1 + 1.0) ) /
- ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
- );
- }
-
- /* If no error has occurred, return the calculated score. Otherwise,
- ** throw an SQL exception. */
+ /* Determine and return the BM25 score for the current row. Or, if an
+ ** error has occurred, throw an exception. */
if( rc==SQLITE_OK ){
+ for(i=0; inPhrase; i++){
+ score += pData->aIDF[i] * (
+ ( aFreq[i] * (k1 + 1.0) ) /
+ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
+ );
+ }
sqlite3_result_double(pCtx, -1.0 * score);
}else{
sqlite3_result_error_code(pCtx, rc);
diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c
index 392dde3ab4..088d8b8cae 100644
--- a/ext/fts5/fts5_expr.c
+++ b/ext/fts5/fts5_expr.c
@@ -1500,8 +1500,8 @@ int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
}
/* If the iterator is not at a real match, skip forward until it is. */
- while( pRoot->bNomatch ){
- assert( pRoot->bEof==0 && rc==SQLITE_OK );
+ while( pRoot->bNomatch && rc==SQLITE_OK ){
+ assert( pRoot->bEof==0 );
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
}
return rc;
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
index d109a4c176..c01d5d9de6 100644
--- a/ext/fts5/fts5_index.c
+++ b/ext/fts5/fts5_index.c
@@ -2070,14 +2070,10 @@ static void fts5SegIterNext(
}else{
/* The following could be done by calling fts5SegIterLoadNPos(). But
** this block is particularly performance critical, so equivalent
- ** code is inlined.
- **
- ** Later: Switched back to fts5SegIterLoadNPos() because it supports
- ** detail=none mode. Not ideal.
- */
+ ** code is inlined. */
int nSz;
assert( p->rc==SQLITE_OK );
- assert( pIter->iLeafOffset<=pIter->pLeaf->nn );
+ assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn );
fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
pIter->bDel = (nSz & 0x0001);
pIter->nPos = nSz>>1;
@@ -3069,7 +3065,7 @@ static void fts5ChunkIterate(
int pgno = pSeg->iLeafPgno;
int pgnoSave = 0;
- /* This function does notmwork with detail=none databases. */
+ /* This function does not work with detail=none databases. */
assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
@@ -3082,6 +3078,9 @@ static void fts5ChunkIterate(
fts5DataRelease(pData);
if( nRem<=0 ){
break;
+ }else if( pSeg->pSeg==0 ){
+ p->rc = FTS5_CORRUPT;
+ return;
}else{
pgno++;
pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
@@ -3133,66 +3132,72 @@ static void fts5SegiterPoslist(
}
/*
-** IN/OUT parameter (*pa) points to a position list n bytes in size. If
-** the position list contains entries for column iCol, then (*pa) is set
-** to point to the sub-position-list for that column and the number of
-** bytes in it returned. Or, if the argument position list does not
-** contain any entries for column iCol, return 0.
+** Parameter pPos points to a buffer containing a position list, size nPos.
+** This function filters it according to pColset (which must be non-NULL)
+** and sets pIter->base.pData/nData to point to the new position list.
+** If memory is required for the new position list, use buffer pIter->poslist.
+** Or, if the new position list is a contiguous subset of the input, set
+** pIter->base.pData/nData to point directly to it.
+**
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM
+** before returning.
*/
-static int fts5IndexExtractCol(
- const u8 **pa, /* IN/OUT: Pointer to poslist */
- int n, /* IN: Size of poslist in bytes */
- int iCol /* Column to extract from poslist */
-){
- int iCurrent = 0; /* Anything before the first 0x01 is col 0 */
- const u8 *p = *pa;
- const u8 *pEnd = &p[n]; /* One byte past end of position list */
-
- while( iCol>iCurrent ){
- /* Advance pointer p until it points to pEnd or an 0x01 byte that is
- ** not part of a varint. Note that it is not possible for a negative
- ** or extremely large varint to occur within an uncorrupted position
- ** list. So the last byte of each varint may be assumed to have a clear
- ** 0x80 bit. */
- while( *p!=0x01 ){
- while( *p++ & 0x80 );
- if( p>=pEnd ) return 0;
- }
- *pa = p++;
- iCurrent = *p++;
- if( iCurrent & 0x80 ){
- p--;
- p += fts5GetVarint32(p, iCurrent);
- }
- }
- if( iCol!=iCurrent ) return 0;
-
- /* Advance pointer p until it points to pEnd or an 0x01 byte that is
- ** not part of a varint */
- while( pnCol; i++){
- const u8 *pSub = pPos;
- int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
- if( nSub ){
- fts5BufferAppendBlob(pRc, pBuf, nSub, pSub);
+ const u8 *p = pPos;
+ const u8 *aCopy = p;
+ const u8 *pEnd = &p[nPos]; /* One byte past end of position list */
+ int i = 0;
+ int iCurrent = 0;
+
+ if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){
+ return;
+ }
+
+ while( 1 ){
+ while( pColset->aiCol[i]nCol ){
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ return;
+ }
+ }
+
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint */
+ while( paiCol[i]==iCurrent ){
+ if( pColset->nCol==1 ){
+ pIter->base.pData = aCopy;
+ pIter->base.nData = p-aCopy;
+ return;
+ }
+ fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy);
+ }
+ if( p==pEnd ){
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ return;
+ }
+ aCopy = p++;
+ iCurrent = *p++;
+ if( iCurrent & 0x80 ){
+ p--;
+ p += fts5GetVarint32(p, iCurrent);
}
}
}
+
}
/*
@@ -3312,16 +3317,9 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
/* All data is stored on the current page. Populate the output
** variables to point into the body of the page object. */
const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
- if( pColset->nCol==1 ){
- pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
- pIter->base.pData = a;
- }else{
- int *pRc = &pIter->pIndex->rc;
- fts5BufferZero(&pIter->poslist);
- fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist);
- pIter->base.pData = pIter->poslist.p;
- pIter->base.nData = pIter->poslist.n;
- }
+ int *pRc = &pIter->pIndex->rc;
+ fts5BufferZero(&pIter->poslist);
+ fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter);
}else{
/* The data is distributed over two or more pages. Copy it into the
** Fts5Iter.poslist buffer and then set the output pointer to point
@@ -4804,7 +4802,7 @@ static void fts5AppendPoslist(
static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;
- assert( pIter->aPoslist );
+ assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) );
if( p>=pIter->aEof ){
pIter->aPoslist = 0;
}else{
@@ -4888,16 +4886,20 @@ static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){
static void fts5MergeRowidLists(
Fts5Index *p, /* FTS5 backend object */
Fts5Buffer *p1, /* First list to merge */
- Fts5Buffer *p2 /* Second list to merge */
+ int nBuf, /* Number of entries in apBuf[] */
+ Fts5Buffer *aBuf /* Array of other lists to merge into p1 */
){
int i1 = 0;
int i2 = 0;
i64 iRowid1 = 0;
i64 iRowid2 = 0;
i64 iOut = 0;
-
+ Fts5Buffer *p2 = &aBuf[0];
Fts5Buffer out;
+
+ (void)nBuf;
memset(&out, 0, sizeof(out));
+ assert( nBuf==1 );
sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
if( p->rc ) return;
@@ -4924,180 +4926,213 @@ static void fts5MergeRowidLists(
fts5BufferFree(&out);
}
+typedef struct PrefixMerger PrefixMerger;
+struct PrefixMerger {
+ Fts5DoclistIter iter; /* Doclist iterator */
+ i64 iPos; /* For iterating through a position list */
+ int iOff;
+ u8 *aPos;
+ PrefixMerger *pNext; /* Next in docid/poslist order */
+};
+
+static void fts5PrefixMergerInsertByRowid(
+ PrefixMerger **ppHead,
+ PrefixMerger *p
+){
+ if( p->iter.aPoslist ){
+ PrefixMerger **pp = ppHead;
+ while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){
+ pp = &(*pp)->pNext;
+ }
+ p->pNext = *pp;
+ *pp = p;
+ }
+}
+
+static void fts5PrefixMergerInsertByPosition(
+ PrefixMerger **ppHead,
+ PrefixMerger *p
+){
+ if( p->iPos>=0 ){
+ PrefixMerger **pp = ppHead;
+ while( *pp && p->iPos>(*pp)->iPos ){
+ pp = &(*pp)->pNext;
+ }
+ p->pNext = *pp;
+ *pp = p;
+ }
+}
+
+
/*
-** Buffers p1 and p2 contain doclists. This function merges the content
-** of the two doclists together and sets buffer p1 to the result before
-** returning.
-**
-** If an error occurs, an error code is left in p->rc. If an error has
-** already occurred, this function is a no-op.
+** Array aBuf[] contains nBuf doclists. These are all merged in with the
+** doclist in buffer p1.
*/
static void fts5MergePrefixLists(
Fts5Index *p, /* FTS5 backend object */
Fts5Buffer *p1, /* First list to merge */
- Fts5Buffer *p2 /* Second list to merge */
+ int nBuf, /* Number of buffers in array aBuf[] */
+ Fts5Buffer *aBuf /* Other lists to merge in */
){
- if( p2->n ){
- i64 iLastRowid = 0;
- Fts5DoclistIter i1;
- Fts5DoclistIter i2;
- Fts5Buffer out = {0, 0, 0};
- Fts5Buffer tmp = {0, 0, 0};
+#define fts5PrefixMergerNextPosition(p) \
+ sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos);
+#define FTS5_MERGE_NLIST 16
+ PrefixMerger aMerger[FTS5_MERGE_NLIST];
+ PrefixMerger *pHead = 0;
+ int i;
+ int nOut = 0;
+ Fts5Buffer out = {0, 0, 0};
+ Fts5Buffer tmp = {0, 0, 0};
+ i64 iLastRowid = 0;
- /* 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.
- **
- ** Or, if the input position-lists are corrupt, then the output might
- ** include up to 2 extra 10-byte positions created by interpreting -1
- ** (the value PoslistNext64() uses for EOF) as a position and appending
- ** it to the output. This can happen at most once for each input
- ** position-list, hence two 10 byte paddings. */
- if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return;
- fts5DoclistIterInit(p1, &i1);
- fts5DoclistIterInit(p2, &i2);
-
- while( 1 ){
- if( i1.iRowidp) + (i2.aPoslist-p2->p)+9+10+10) );
- }
- else if( i2.iRowid!=i1.iRowid ){
- /* Copy entry from i2 */
- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
- fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
- fts5DoclistIterNext(&i2);
- if( i2.aPoslist==0 ) break;
- assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
- }
- else{
- /* Merge the two position lists. */
- i64 iPos1 = 0;
- i64 iPos2 = 0;
- int iOff1 = 0;
- int iOff2 = 0;
- u8 *a1 = &i1.aPoslist[i1.nSize];
- u8 *a2 = &i2.aPoslist[i2.nSize];
- int nCopy;
- u8 *aCopy;
-
- i64 iPrev = 0;
- Fts5PoslistWriter writer;
- memset(&writer, 0, sizeof(writer));
-
- /* See the earlier comment in this function for an explanation of why
- ** corrupt input position lists might cause the output to consume
- ** at most 20 bytes of unexpected space. */
- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
- fts5BufferZero(&tmp);
- sqlite3Fts5BufferSize(&p->rc, &tmp,
- i1.nPoslist + i2.nPoslist + 10 + 10 + FTS5_DATA_ZERO_PADDING
- );
- if( p->rc ) break;
-
- sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
- sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
- assert_nc( iPos1>=0 && iPos2>=0 );
-
- if( iPos1=0 && iPos2>=0 ){
- while( 1 ){
- if( iPos1=0 ){
- if( iPos1!=iPrev ){
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
- }
- aCopy = &a1[iOff1];
- nCopy = i1.nPoslist - iOff1;
- }else{
- assert_nc( iPos2>=0 && iPos2!=iPrev );
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
- aCopy = &a2[iOff2];
- nCopy = i2.nPoslist - iOff2;
- }
- if( nCopy>0 ){
- fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy);
- }
-
- /* WRITEPOSLISTSIZE */
- assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist );
- assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 );
- if( tmp.n>i1.nPoslist+i2.nPoslist ){
- if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
- break;
- }
- fts5BufferSafeAppendVarint(&out, tmp.n * 2);
- fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
- fts5DoclistIterNext(&i1);
- fts5DoclistIterNext(&i2);
- assert_nc( out.n<=(p1->n+p2->n+9) );
- if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
- assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
- }
- }
-
- if( i1.aPoslist ){
- fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
- fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);
- }
- else if( i2.aPoslist ){
- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
- fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
- }
- assert_nc( out.n<=(p1->n+p2->n+9) );
-
- fts5BufferFree(p1);
- fts5BufferFree(&tmp);
- memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
- *p1 = out;
+ /* Initialize a doclist-iterator for each input buffer. Arrange them in
+ ** a linked-list starting at pHead in ascending order of rowid. Avoid
+ ** linking any iterators already at EOF into the linked list at all. */
+ assert( nBuf+1<=sizeof(aMerger)/sizeof(aMerger[0]) );
+ memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1));
+ pHead = &aMerger[nBuf];
+ fts5DoclistIterInit(p1, &pHead->iter);
+ for(i=0; in + 9 + 10*nBuf;
+
+ /* The maximum size of the output is equal to the sum of the
+ ** 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.
+ **
+ ** Or, if the input position-lists are corrupt, then the output might
+ ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1
+ ** (the value PoslistNext64() uses for EOF) as a position and appending
+ ** it to the output. This can happen at most once for each input
+ ** position-list, hence (nBuf+1) 10 byte paddings. */
+ if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return;
+
+ while( pHead ){
+ fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid);
+
+ if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){
+ /* Merge data from two or more poslists */
+ i64 iPrev = 0;
+ int nTmp = FTS5_DATA_ZERO_PADDING;
+ int nMerge = 0;
+ PrefixMerger *pSave = pHead;
+ PrefixMerger *pThis = 0;
+ int nTail = 0;
+
+ pHead = 0;
+ while( pSave && pSave->iter.iRowid==iLastRowid ){
+ PrefixMerger *pNext = pSave->pNext;
+ pSave->iOff = 0;
+ pSave->iPos = 0;
+ pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize];
+ fts5PrefixMergerNextPosition(pSave);
+ nTmp += pSave->iter.nPoslist + 10;
+ nMerge++;
+ fts5PrefixMergerInsertByPosition(&pHead, pSave);
+ pSave = pNext;
+ }
+
+ if( pHead==0 || pHead->pNext==0 ){
+ p->rc = FTS5_CORRUPT;
+ break;
+ }
+
+ /* See the earlier comment in this function for an explanation of why
+ ** corrupt input position lists might cause the output to consume
+ ** at most nMerge*10 bytes of unexpected space. */
+ if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){
+ break;
+ }
+ fts5BufferZero(&tmp);
+
+ pThis = pHead;
+ pHead = pThis->pNext;
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
+ fts5PrefixMergerNextPosition(pThis);
+ fts5PrefixMergerInsertByPosition(&pHead, pThis);
+
+ while( pHead->pNext ){
+ pThis = pHead;
+ if( pThis->iPos!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
+ }
+ fts5PrefixMergerNextPosition(pThis);
+ pHead = pThis->pNext;
+ fts5PrefixMergerInsertByPosition(&pHead, pThis);
+ }
+
+ if( pHead->iPos!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos);
+ }
+ nTail = pHead->iter.nPoslist - pHead->iOff;
+
+ /* WRITEPOSLISTSIZE */
+ assert( tmp.n+nTail<=nTmp );
+ if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
+ break;
+ }
+ fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
+ fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
+ if( nTail>0 ){
+ fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail);
+ }
+
+ pHead = pSave;
+ for(i=0; iiter.aPoslist && pX->iter.iRowid==iLastRowid ){
+ fts5DoclistIterNext(&pX->iter);
+ fts5PrefixMergerInsertByRowid(&pHead, pX);
+ }
+ }
+
+ }else{
+ /* Copy poslist from pHead to output */
+ PrefixMerger *pThis = pHead;
+ Fts5DoclistIter *pI = &pThis->iter;
+ fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize);
+ fts5DoclistIterNext(pI);
+ pHead = pThis->pNext;
+ fts5PrefixMergerInsertByRowid(&pHead, pThis);
+ }
+ }
+
+ fts5BufferFree(p1);
+ fts5BufferFree(&tmp);
+ memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
+ *p1 = out;
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
- const u8 *pToken, /* Buffer containing prefix to match */
+ int iIdx, /* Index to scan for data */
+ u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
- const int nBuf = 32;
+ int nBuf = 32;
+ int nMerge = 1;
- void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
+ void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
xMerge = fts5MergeRowidLists;
xAppend = fts5AppendRowid;
}else{
+ nMerge = FTS5_MERGE_NLIST-1;
+ nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
xMerge = fts5MergePrefixLists;
xAppend = fts5AppendPoslist;
}
@@ -5117,6 +5152,27 @@ static void fts5SetupPrefixIter(
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
+ if( iIdx!=0 ){
+ int dummy = 0;
+ const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
+ pToken[0] = FTS5_MAIN_PREFIX;
+ fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
+ fts5IterSetOutputCb(&p->rc, p1);
+ for(;
+ fts5MultiIterEof(p, p1)==0;
+ fts5MultiIterNext2(p, p1, &dummy)
+ ){
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
+ p1->xSetOutputs(p1, pSeg);
+ if( p1->base.nData ){
+ xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
+ iLastRowid = p1->base.iRowid;
+ }
+ }
+ fts5MultiIterFree(p1);
+ }
+
+ pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for( /* no-op */ ;
@@ -5137,13 +5193,21 @@ static void fts5SetupPrefixIter(
if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
- assert( ibase.iRowid;
}
- for(i=0; irc==SQLITE_OK ){
- xMerge(p, &doclist, &aBuf[i]);
+ xMerge(p, &doclist, nMerge, &aBuf[i]);
+ }
+ for(iFree=i; iFreerc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
+ int iPrefixIdx = 0; /* +1 prefix index */
if( nToken ) memcpy(&buf.p[1], pToken, nToken);
/* Figure out which index to search and set iIdx accordingly. If this
@@ -5433,7 +5502,9 @@ int sqlite3Fts5IndexQuery(
if( flags & FTS5INDEX_QUERY_PREFIX ){
int nChar = fts5IndexCharlen(pToken, nToken);
for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
- if( pConfig->aPrefix[iIdx-1]==nChar ) break;
+ int nIdxChar = pConfig->aPrefix[iIdx-1];
+ if( nIdxChar==nChar ) break;
+ if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
@@ -5450,8 +5521,7 @@ int sqlite3Fts5IndexQuery(
}else{
/* Scan multiple terms in the main index */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
- buf.p[0] = FTS5_MAIN_PREFIX;
- fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
+ fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
fts5IterSetOutputCb(&p->rc, pRet);
if( p->rc==SQLITE_OK ){
@@ -6123,6 +6193,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
}else{
poslist.n = 0;
fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
+ fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0");
while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
int iCol = FTS5_POS2COLUMN(iPos);
int iTokOff = FTS5_POS2OFFSET(iPos);
diff --git a/ext/fts5/fts5_unicode2.c b/ext/fts5/fts5_unicode2.c
index 843133e82d..3e97264fa8 100644
--- a/ext/fts5/fts5_unicode2.c
+++ b/ext/fts5/fts5_unicode2.c
@@ -775,3 +775,4 @@ void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
}
aAscii[0] = 0; /* 0x00 is never a token character */
}
+
diff --git a/ext/fts5/test/fts5corrupt3.test b/ext/fts5/test/fts5corrupt3.test
index a8a7ae9a54..6fae40ecb9 100644
--- a/ext/fts5/test/fts5corrupt3.test
+++ b/ext/fts5/test/fts5corrupt3.test
@@ -8346,6 +8346,7 @@ do_catchsql_test 58.1 {
#-------------------------------------------------------------------------
do_test 59.0 {
sqlite3 db {}
+ sqlite3_fts5_register_matchinfo db
db deserialize [decode_hexdb {
.open --hexdb
| size 32768 pagesize 4096 filename crash-96b136358d01ec.db
@@ -8544,11 +8545,12 @@ do_test 59.0 {
do_catchsql_test 59.1 {
SELECT (matchinfo(591,t1)) FROM t1 WHERE t1 MATCH 'e*e'
-} {1 {database disk image is malformed}}
+} {0 {}}
#-------------------------------------------------------------------------
do_test 60.0 {
sqlite3 db {}
+ sqlite3_fts5_register_matchinfo db
db deserialize [decode_hexdb {
.open --hexdb
| size 32768 pagesize 4096 filename crash-c77b90b929dc92.db
@@ -10427,6 +10429,341 @@ do_catchsql_test 70.2 {
SELECT snippet(ttt, -1, '', '','',13)FROM ttt('e* NOT ee*e* NOT ee*')
} {1 {database disk image is malformed}}
+#-------------------------------------------------------------------------
+reset_db
+do_test 71.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 28672 pagesize 4096 filename sql025294.txt.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 00 01 00 40 20 20 00 00 00 00 00 00 00 07 .....@ ........
+| 96: 00 00 00 00 0d 00 00 00 07 0d d2 00 0f c4 0f 6d ...............m
+| 112: 0f 02 0e ab 0e 4e 0d f6 0d d2 00 00 00 00 00 00 .....N..........
+| 3536: 00 00 22 07 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
+| 3552: 32 74 32 07 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
+| 3568: 20 74 32 28 78 29 56 06 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
+| 3584: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
+| 3600: 6f 6e 66 69 67 06 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
+| 3616: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
+| 3632: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
+| 3648: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 05 WITHOUT ROWID[.
+| 3664: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
+| 3680: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
+| 3696: 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
+| 3712: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
+| 3728: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
+| 3744: 59 2c 20 73 7a 20 42 4c 4f 42 29 55 04 06 17 21 Y, sz BLOB)U...!
+| 3760: 21 01 77 74 61 62 6c 65 74 31 5f 63 6f 6e 74 65 !.wtablet1_conte
+| 3776: 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 04 43 52 45 ntt1_content.CRE
+| 3792: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 6f ATE TABLE 't1_co
+| 3808: 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 45 ntent'(id INTEGE
+| 3824: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 63 R PRIMARY KEY, c
+| 3840: 30 29 69 03 07 17 19 19 01 81 2d 74 61 62 6c 65 0)i.......-table
+| 3856: 74 31 5f 69 64 78 74 31 5f 69 64 78 03 43 52 45 t1_idxt1_idx.CRE
+| 3872: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 69 64 ATE TABLE 't1_id
+| 3888: 78 27 28 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 x'(segid, term,
+| 3904: 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 pgno, PRIMARY KE
+| 3920: 59 28 73 65 67 69 64 2c 20 74 65 72 6d 29 29 20 Y(segid, term))
+| 3936: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 55 02 07 WITHOUT ROWIDU..
+| 3952: 17 1b 1b 01 81 01 74 61 62 6c 65 74 31 5f 64 61 ......tablet1_da
+| 3968: 74 61 74 31 5f 64 61 74 61 02 43 52 45 41 54 45 tat1_data.CREATE
+| 3984: 20 54 41 42 4c 45 20 27 74 31 5f 64 61 74 61 27 TABLE 't1_data'
+| 4000: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM
+| 4016: 41 52 58 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARX KEY, block B
+| 4032: 4c 4f 42 29 3a 01 06 17 11 11 08 63 74 61 62 6c LOB):......ctabl
+| 4048: 65 74 31 74 31 43 52 45 41 54 45 20 56 49 52 54 et1t1CREATE VIRT
+| 4064: 55 41 4c 20 54 41 42 4c 45 20 74 31 20 55 53 49 UAL TABLE t1 USI
+| 4080: 4e 47 20 66 74 73 35 28 63 6f 6e 74 65 6e 74 29 NG fts5(content)
+| page 2 offset 4096
+| 0: 0d 00 00 00 03 0f bd 00 0f e8 0f ef 0f bd 00 00 ................
+| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 24 84 80 .............$..
+| 4032: 80 80 80 01 03 00 4e 00 00 00 1e 06 30 61 62 61 ......N.....0aba
+| 4048: 60 eb 01 02 02 04 01 66 74 02 02 02 04 04 6e 64 `......ft.....nd
+| 4064: 6f 6e 03 01 f2 04 1a 07 05 01 03 00 10 03 03 0f on..............
+| 4080: 0a 03 00 24 00 00 00 00 01 01 4b 00 01 01 01 01 ...$......K.....
+| page 3 offset 8192
+| 0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 03 0f e0 00 0f f6 0f ec 0f e0 00 00 ................
+| 4064: 0a 03 03 00 1b 61 62 61 6e 64 6f 6e 08 02 03 00 .....abandon....
+| 4080: 17 61 62 61 66 74 08 01 03 00 17 61 62 61 63 6b .abaft.....aback
+| page 5 offset 16384
+| 0: 0d 00 00 00 03 0f ee 00 0f fa 0f f4 0f ee 00 00 ................
+| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 03 ................
+| 4080: 03 00 0e 01 04 02 03 00 0e 01 04 01 03 00 0e 01 ................
+| page 6 offset 20480
+| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
+| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
+| page 7 offset 24576
+| 0: 0d 00 00 00 03 0f d6 00 0f f4 0f e1 00 00 00 00 ................
+| 4048: 00 00 00 00 00 00 09 03 02 1b 72 65 62 75 69 6c ..........rebuil
+| 4064: 64 11 02 02 2b 69 6e 74 65 67 72 69 74 79 2d 63 d...+integrity-c
+| 4080: 68 65 63 6b 0a 01 02 1d 6f 70 74 69 6d 69 7a 65 heck....optimize
+| end sql025294.txt.db
+}]} {}
+
+do_catchsql_test 71.2 {
+ INSERT INTO t1(t1) VALUES('integrity-check');
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+do_test 72.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 24576 pagesize 4096 filename crash-77b86d070d0ac6.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
+| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................
+| 96: 00 00 00 00 0d 00 00 00 06 0d e2 00 0f c4 0f 6a ...............j
+| 112: 0e fc 0e 9d 0e 3d 0d e2 00 00 00 00 00 00 00 00 .....=..........
+| 3552: 00 00 59 06 06 17 21 21 01 7f 74 61 62 6c 65 74 ..Y...!!..tablet
+| 3568: 74 74 5f 63 6f 6e 66 69 67 74 74 74 5f 63 6f 6e tt_configttt_con
+| 3584: 66 69 67 06 43 52 45 41 54 45 20 54 41 42 4c 45 fig.CREATE TABLE
+| 3600: 20 27 74 74 74 5f 63 6f 6e 66 69 67 27 28 6b 20 'ttt_config'(k
+| 3616: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 PRIMARY KEY, v)
+| 3632: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5e 05 07 WITHOUT ROWID^..
+| 3648: 17 23 23 01 81 03 74 61 62 6c 65 74 74 74 5f 64 .##...tablettt_d
+| 3664: 6f 63 73 69 7a 65 74 74 74 5f 64 6f 63 73 69 7a ocsizettt_docsiz
+| 3680: 65 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 e.CREATE TABLE '
+| 3696: 74 74 74 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 ttt_docsize'(id
+| 3712: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3728: 4b 45 59 2c 20 73 7a 20 42 4c 4f 42 29 5d 04 07 KEY, sz BLOB)]..
+| 3744: 17 23 23 01 81 01 74 61 62 6c 65 74 74 74 5f 63 .##...tablettt_c
+| 3760: 6f 6e 74 65 6e 74 74 74 74 5f 63 6f 6e 74 65 6e ontentttt_conten
+| 3776: 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 t.CREATE TABLE '
+| 3792: 74 74 74 5f 63 6f 6e 74 65 6e 74 27 28 69 64 20 ttt_content'(id
+| 3808: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3824: 4b 45 59 2c 20 63 30 2c 20 63 31 29 6c 03 07 17 KEY, c0, c1)l...
+| 3840: 1b 1b 01 81 2f 74 61 62 6c 65 74 74 74 5f 69 64 ..../tablettt_id
+| 3856: 78 74 74 74 5f 69 64 78 03 43 52 45 41 54 45 20 xttt_idx.CREATE
+| 3872: 54 41 42 4c 45 20 27 74 74 74 5f 69 64 78 27 28 TABLE 'ttt_idx'(
+| 3888: 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e segid, term, pgn
+| 3904: 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 28 73 o, PRIMARY KEY(s
+| 3920: 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 49 54 egid, term)) WIT
+| 3936: 48 4f 55 54 20 52 4f 57 49 44 58 02 07 17 1d 1d HOUT ROWIDX.....
+| 3952: 01 81 03 74 61 62 6c 65 74 74 74 5f 64 61 74 61 ...tablettt_data
+| 3968: 74 74 74 5f 64 61 74 61 02 43 52 45 41 54 45 20 ttt_data.CREATE
+| 3984: 54 41 42 4c 45 20 27 74 74 74 5f 64 61 74 61 27 TABLE 'ttt_data'
+| 4000: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM
+| 4016: 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARY KEY, block B
+| 4032: 4c 4f 42 29 3a 01 06 17 13 13 08 5f 74 61 62 6c LOB):......_tabl
+| 4048: 65 74 74 74 74 74 74 43 52 45 41 54 45 20 56 49 ettttttCREATE VI
+| 4064: 52 54 55 41 4c 20 54 41 42 4c 45 20 74 74 74 20 RTUAL TABLE ttt
+| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
+| page 2 offset 4096
+| 0: 0d 0f 44 00 05 0e 81 00 0f e7 0e 81 0f af 0f 58 ..D............X
+| 16: 0e 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3712: 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 03 01 .....0..........
+| 3728: 01 01 02 01 01 03 01 01 81 24 8c 80 80 80 80 01 .........$......
+| 3744: 04 00 82 4c 00 00 00 9b 02 30 65 03 1a 02 05 05 ...L.....0e.....
+| 3760: 07 05 01 01 04 03 03 08 03 03 01 2e 02 05 05 07 ................
+| 3776: 05 07 05 07 05 01 01 04 03 03 08 03 03 08 03 03 ................
+| 3792: 08 03 03 02 01 65 03 1e 03 05 05 04 05 05 01 01 .....e..........
+| 3808: 03 06 04 04 06 04 03 01 36 03 05 05 04 05 05 04 ........6.......
+| 3824: 05 05 04 05 05 01 01 03 06 04 04 06 04 04 06 04 ................
+| 3840: 04 06 04 03 03 01 65 03 14 04 05 07 05 05 01 01 ......e.........
+| 3856: 02 08 0a 01 20 04 05 07 05 07 05 07 05 05 01 01 .... ...........
+| 3872: 02 08 0a 09 fa 04 01 65 03 02 0a 01 06 0a 1a 0a .......e........
+| 3888: 05 01 65 03 06 01 01 0a 01 0a 01 01 0a 0a 0a 04 ..e.............
+| 3904: 2b 31 21 0b 0f ef 00 14 2a 00 00 00 00 01 02 02 +1!.....*.......
+| 3920: 00 02 01 01 01 02 01 01 50 88 80 80 80 80 01 04 ........P.......
+| 3936: 00 81 24 00 00 00 47 02 30 65 02 1a 02 05 05 07 ..$...G.0e......
+| 3952: 05 01 01 04 03 03 08 03 03 02 01 65 02 1e 03 05 ...........e....
+| 3968: 05 04 05 05 01 01 03 06 04 04 06 04 03 03 01 65 ...............e
+| 3984: 02 14 04 05 07 05 05 01 01 02 08 0a 04 01 65 02 ..............e.
+| 4000: 02 0a 05 01 65 02 06 01 01 0a 04 12 14 0f 06 31 ....e..........1
+| 4016: 84 80 80 80 80 01 03 00 68 00 00 00 2b 02 30 65 ........h...+.0e
+| 4032: 01 10 02 05 05 01 01 04 03 9f 02 01 65 01 12 03 ............e...
+| 4048: 05 05 01 01 03 06 04 03 03 01 65 01 0e 14 05 05 ..........e.....
+| 4064: 01 01 02 08 04 0d 0e 06 01 03 00 12 04 4c 4c 00 .............LL.
+| 4080: 00 00 11 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
+| page 3 offset 8192
+| 0: 0a 00 00 00 03 0f 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 06 04 01 0c ................
+| 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0c 01 02 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3600: 00 00 00 00 00 00 00 00 00 00 81 52 04 06 00 81 ...........R....
+| 3616: 5d 81 55 65 20 65 65 20 65 65 65 20 65 20 65 65 ].Ue ee eee e ee
+| 3632: 20 65 65 65 20 65 20 65 65 20 65 65 65 65 20 65 eee e ee eeee e
+| 3648: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 3664: 20 65 65 20 65 65 55 65 20 65 65 20 65 65 65 20 ee eeUe ee eee
+| 3680: 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 e ee eee e ee ee
+| 3696: 65 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 ee ee eee e ee e
+| 3712: 65 65 20 65 20 65 65 20 65 65 65 65 65 65 20 65 ee e ee eeeeee e
+| 3728: 65 20 65 20 65 20 65 20 65 65 20 65 85 65 20 65 e e e e ee e.e e
+| 3744: 65 20 65 65 65 65 65 20 65 65 20 65 20 65 20 65 e eeeee ee e e e
+| 3760: 20 65 65 20 65 65 65 20 65 65 20 65 65 65 65 65 ee eee ee eeeee
+| 3776: 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 65 ee e e e ee eee
+| 3792: 20 65 65 20 65 65 65 65 65 20 65 65 20 65 20 65 ee eeeee ee e e
+| 3808: 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 6a e ee eee ee eej
+| 3824: 03 04 00 75 71 65 20 65 65 20 65 65 65 20 65 20 ...uqe ee eee e
+| 3840: 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 65 ee eee e ee eeee
+| 3856: 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 ee eee e ee eee
+| 3872: 20 65 20 65 65 20 65 65 65 65 65 65 20 65 65 20 e ee eeeeee ee
+| 3888: 65 20 65 20 65 20 65 64 20 65 65 65 20 65 65 20 e e e ed eee ee
+| 3904: 65 65 65 65 65 20 65 65 20 65 20 65 20 65 10 65 eeeee ee e e e.e
+| 3920: 65 20 65 65 65 10 65 65 20 65 65 6a 02 04 00 75 e eee.ee eej...u
+| 3936: 71 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 qe ee eee e ee e
+| 3952: 65 65 20 65 20 65 65 20 65 65 65 65 20 65 65 20 ee e ee eeee ee
+| 3968: 65 65 65 20 65 20 65 65 20 65 65 65 20 65 20 65 eee e ee eee e e
+| 3984: 65 20 65 65 65 66 65 65 20 65 65 20 65 20 65 20 e eeefee ee e e
+| 4000: 65 88 65 65 20 65 65 65 30 65 65 20 65 65 65 65 e.ee eee0ee eeee
+| 4016: 65 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 e ee e e e ee ee
+| 4032: 65 20 65 65 20 65 65 37 01 04 00 41 3f 65 20 65 e ee ee7...A?e e
+| 4048: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 4064: 20 65 65 20 65 65 65 65 65 65 20 65 65 20 65 20 ee eeeeee ee e
+| 4080: 65 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 e e ee eee ee ee
+| page 5 offset 16384
+| 0: 0d 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 05 04 03 00 10 21 21 05 03 03 00 10 .........!!.....
+| 4080: 11 11 05 02 03 00 10 11 11 05 01 03 00 10 09 09 ................
+| page 6 offset 20480
+| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
+| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
+| end crash-77b86d070d0ac6.db
+}]} {}
+
+do_catchsql_test 72.1 {
+ INSERT INTO ttt(ttt) VALUES('integrity-check');
+} {1 {database disk image is malformed}}
+
+do_catchsql_test 72.1 {
+ SELECT 1 FROM ttt('e* NOT ee*');
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+do_test 73.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 24576 pagesize 4096 filename crash-b02ca2cc4d7dda.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
+| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................
+| 96: 00 00 00 00 0d 00 00 00 06 0d e2 00 0f c4 0f 6a ...............j
+| 112: 0e fc 0e 9d 0e 3d 0d e2 00 00 00 00 00 00 00 00 .....=..........
+| 3552: 00 00 59 06 06 17 21 21 01 7f 74 61 62 6c 65 74 ..Y...!!..tablet
+| 3568: 74 74 5f 63 6f 6e 66 69 67 74 74 74 5f 63 6f 6e tt_configttt_con
+| 3584: 66 69 67 06 43 52 45 41 54 45 20 54 41 42 4c 45 fig.CREATE TABLE
+| 3600: 20 27 74 74 74 5f 63 6f 6e 66 69 67 27 28 6b 20 'ttt_config'(k
+| 3616: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 PRIMARY KEY, v)
+| 3632: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5e 05 07 WITHOUT ROWID^..
+| 3648: 17 23 23 01 81 03 74 61 62 6c 65 74 74 74 5f 64 .##...tablettt_d
+| 3664: 6f 63 73 69 7a 65 74 74 74 5f 64 6f 63 73 69 7a ocsizettt_docsiz
+| 3680: 65 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 e.CREATE TABLE '
+| 3696: 74 74 74 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 ttt_docsize'(id
+| 3712: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3728: 4b 45 59 2c 20 73 7a 20 42 4c 4f 42 29 5d 04 07 KEY, sz BLOB)]..
+| 3744: 17 23 23 01 81 01 74 61 62 6c 65 74 74 74 5f 63 .##...tablettt_c
+| 3760: 6f 6e 74 65 6e 74 74 74 74 5f 63 6f 6e 74 65 6e ontentttt_conten
+| 3776: 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 t.CREATE TABLE '
+| 3792: 74 74 74 5f 63 6f 6e 74 65 6e 74 27 28 69 64 20 ttt_content'(id
+| 3808: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3824: 4b 45 59 2c 20 63 30 2c 20 63 31 29 6c 03 07 17 KEY, c0, c1)l...
+| 3840: 1b 1b 01 81 2f 74 61 62 6c 65 74 74 74 5f 69 64 ..../tablettt_id
+| 3856: 78 74 74 74 5f 69 64 78 03 43 52 45 41 54 45 20 xttt_idx.CREATE
+| 3872: 54 41 42 4c 45 20 27 74 74 74 5f 69 64 78 27 28 TABLE 'ttt_idx'(
+| 3888: 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e segid, term, pgn
+| 3904: 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 28 73 o, PRIMARY KEY(s
+| 3920: 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 49 54 egid, term)) WIT
+| 3936: 48 4f 55 54 20 52 4f 57 49 44 58 02 07 17 1d 1d HOUT ROWIDX.....
+| 3952: 01 81 03 74 61 62 6c 65 74 74 74 5f 64 61 74 61 ...tablettt_data
+| 3968: 74 74 74 5f 64 61 74 61 02 43 52 45 41 54 45 20 ttt_data.CREATE
+| 3984: 54 41 42 4c 45 20 27 74 74 74 5f 64 61 74 61 27 TABLE 'ttt_data'
+| 4000: 28 69 65 20 49 4e 54 45 47 45 52 20 50 52 49 4d (ie INTEGER PRIM
+| 4016: 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARY KEY, block B
+| 4032: 4c 4f 42 29 3a 01 06 17 13 13 08 5f 74 61 62 6c LOB):......_tabl
+| 4048: 65 74 74 74 74 74 74 43 52 45 41 54 45 20 56 49 ettttttCREATE VI
+| 4064: 52 54 55 41 4c 20 54 41 42 4c 45 20 74 74 74 20 RTUAL TABLE ttt
+| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
+| page 2 offset 4096
+| 0: 0d 0f 44 00 05 0e 81 00 0f e7 0e 81 0f af 0f 58 ..D............X
+| 16: 0e 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3712: 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 03 01 .....0..........
+| 3728: 01 01 02 01 01 03 01 01 81 24 8c 80 80 80 80 01 .........$......
+| 3744: 04 00 82 4c 00 00 00 9b 02 30 65 03 1a 02 05 05 ...L.....0e.....
+| 3760: 07 05 01 01 04 03 03 08 03 03 01 2e 02 05 05 07 ................
+| 3776: 05 07 05 07 05 01 01 04 03 03 08 03 03 08 03 03 ................
+| 3792: 08 03 03 02 01 65 03 1e 03 05 05 04 05 05 01 01 .....e..........
+| 3808: 03 06 04 04 06 04 03 01 36 03 05 05 04 05 05 04 ........6.......
+| 3824: 05 04 f4 04 05 01 01 03 06 04 04 06 04 04 06 04 ................
+| 3840: 04 06 04 db 03 01 65 03 14 04 05 07 05 05 01 01 ......e.........
+| 3856: 02 08 0a 01 20 04 05 07 05 07 05 07 05 05 01 01 .... ...........
+| 3872: 02 08 0a 0a 0a 04 01 65 03 02 0a 01 06 0a 0a 0a .......e........
+| 3888: 05 01 65 03 06 01 01 0a 01 0a 01 01 0a 0a 0a 04 ..e.............
+| 3904: 2b 31 21 0b 0f ef 00 14 2a 00 00 00 00 01 02 02 +1!.....*.......
+| 3920: 00 02 01 01 01 02 01 01 50 88 80 80 80 80 01 04 ........P.......
+| 3936: 00 81 24 00 00 00 47 02 30 65 02 1a 02 05 05 07 ..$...G.0e......
+| 3952: 05 01 01 04 03 03 08 03 03 02 01 65 02 1e 03 05 ...........e....
+| 3968: 05 04 05 05 01 01 03 06 09 14 06 04 03 03 01 65 ...............e
+| 3984: 02 14 04 05 07 05 05 01 01 02 08 ed 04 01 65 02 ..............e.
+| 4000: 02 0a 05 01 65 02 06 01 01 0a 04 12 14 0f 06 31 ....e..........1
+| 4016: 84 80 80 80 80 01 03 00 68 00 00 00 2b 02 30 65 ........h...+.0e
+| 4032: 01 10 02 05 05 01 01 04 03 03 02 01 55 01 12 03 ............U...
+| 4048: 05 05 01 01 03 06 05 03 03 01 65 01 0e 04 05 05 ..........e.....
+| 4064: 01 01 02 08 04 0d 0e 06 01 03 00 12 04 4c 4c 00 .............LL.
+| 4080: 00 00 11 24 00 00 00 00 01 0f c1 00 01 01 01 01 ...$............
+| page 3 offset 8192
+| 0: 0a 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 06 04 01 0c ................
+| 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0c 01 02 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3600: 00 00 00 00 00 00 00 00 00 00 81 52 04 06 00 81 ...........R....
+| 3616: 5d 81 55 65 20 65 65 20 65 65 65 20 65 20 65 65 ].Ue ee eee e ee
+| 3632: 20 65 65 65 20 65 20 65 65 20 65 65 65 65 20 65 eee e ee eeee e
+| 3648: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 3664: 20 65 65 20 65 65 65 65 20 65 65 20 65 65 65 20 ee eeee ee eee
+| 3680: 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 e ee eee e ee ee
+| 3696: 65 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 ee ee eee e ee e
+| 3712: 65 65 20 65 20 65 65 20 65 65 65 65 65 65 20 65 ee e ee eeeeee e
+| 3728: 65 20 65 20 65 20 65 20 65 65 20 65 65 65 20 65 e e e e ee eee e
+| 3744: 65 20 65 65 65 65 65 20 65 65 20 65 20 65 20 65 e eeeee ee e e e
+| 3760: 20 65 65 20 65 62 d5 20 65 65 20 65 65 65 65 65 ee eb. ee eeeee
+| 3776: 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 65 ee e e e ee eee
+| 3792: 20 65 65 20 65 65 65 65 65 20 65 65 20 65 21 65 ee eeeee ee e!e
+| 3808: 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 6a e ee eee ee eej
+| 3824: 03 04 00 75 71 65 20 65 65 10 65 65 65 20 65 20 ...uqe ee.eee e
+| 3840: 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 65 ee eee e ee eeee
+| 3856: 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 ee eee e ee eee
+| 3872: 20 65 20 65 65 20 65 65 65 65 65 65 20 65 65 20 e ee eeeeee ee
+| 3888: 65 20 65 20 65 20 65 65 20 65 65 65 20 62 55 20 e e e ee eee bU
+| 3904: 65 65 65 65 65 20 65 65 20 65 20 65 20 65 20 65 eeeee ee e e e e
+| 3920: 65 20 65 65 65 20 55 65 20 65 65 6a 02 04 00 75 e eee Ue eej...u
+| 3936: 71 65 20 65 65 20 65 65 65 20 65 10 65 65 20 65 qe ee eee e.ee e
+| 3952: 65 65 20 65 20 65 65 20 65 65 65 65 20 65 65 20 ee e ee eeee ee
+| 3968: 65 65 65 20 65 20 65 65 20 65 65 65 20 65 20 65 eee e ee eee e e
+| 3984: 65 20 65 65 65 65 65 65 20 65 65 20 65 20 65 20 e eeeeee ee e e
+| 4000: 65 20 65 65 20 65 65 65 20 65 65 20 65 65 65 65 e ee eee ee eeee
+| 4016: 65 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 e ee e e e ee ee
+| 4032: 65 20 65 65 21 65 65 37 0a 04 00 41 3f 65 20 65 e ee!ee7...A?e e
+| 4048: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 4064: 20 65 65 20 65 65 65 65 65 65 20 65 65 20 65 20 ee eeeeee ee e
+| 4080: 65 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 e e ee eee ee ee
+| page 5 offset 16384
+| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 05 04 03 00 10 21 21 05 13 03 00 10 .........!!.....
+| 4080: 11 11 05 02 03 00 10 11 11 05 01 03 00 10 09 09 ................
+| page 6 offset 20480
+| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
+| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
+| end crash-b02ca2cc4d7dda.db
+}]} {}
+
+do_catchsql_test 73.1 {
+ SELECT snippet(ttt,ttt, NOT 54 ),
+ * FROM ttt('e* NOT ee*e* NOT ee* NOT ee*e* NOT e*') ;
+} {1 {database disk image is malformed}}
+
sqlite3_fts5_may_be_corrupt 0
finish_test
diff --git a/ext/fts5/test/fts5corrupt4.test b/ext/fts5/test/fts5corrupt4.test
index dc9ca661d4..b31f4d96e9 100644
--- a/ext/fts5/test/fts5corrupt4.test
+++ b/ext/fts5/test/fts5corrupt4.test
@@ -29,6 +29,8 @@ do_execsql_test 1.0 {
INSERT INTO ttt SELECT a||a, b||b FROM ttt;
}
+expr srand(1)
+
proc mutate {blob i} {
set o [expr {$i % [string length $blob]}]
set a [string range $blob 0 $o-1]
diff --git a/ext/fts5/test/fts5delete.test b/ext/fts5/test/fts5delete.test
index 467e2e5707..1214fec4f4 100644
--- a/ext/fts5/test/fts5delete.test
+++ b/ext/fts5/test/fts5delete.test
@@ -91,6 +91,27 @@ do_catchsql_test 2.4 {
SELECT rowid FROM test_idx WHERE test_idx MATCH 'two' ORDER BY rank;
} {1 {database disk image is malformed}}
-
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 3.0 {
+ CREATE VIRTUAL TABLE tx USING fts5(a, b, c, d, content=);
+ INSERT INTO tx(rowid, a, c) VALUES(1, 'abc def', 'a b c');
+ INSERT INTO tx(rowid, a, c) VALUES(5, 'a b c', 'a b d def');
+}
+do_execsql_test 3.1 {
+ INSERT INTO tx(tx, rowid, a, b, c, d)
+ VALUES('delete', 5, 'a b c', NULL, 'a b d def', NULL);
+}
+do_execsql_test 3.2 {
+ INSERT INTO tx(tx) VALUES('integrity-check');
+}
+do_execsql_test 3.3 {
+ INSERT INTO tx(tx, rowid, a, b, c, d)
+ VALUES('delete', 1, 'abc def', NULL, 'a b c', NULL);
+}
+do_execsql_test 3.4 {
+ INSERT INTO tx(tx) VALUES('integrity-check');
+}
finish_test
+
diff --git a/ext/fts5/test/fts5prefix2.test b/ext/fts5/test/fts5prefix2.test
new file mode 100644
index 0000000000..bf16e81a73
--- /dev/null
+++ b/ext/fts5/test/fts5prefix2.test
@@ -0,0 +1,57 @@
+# 2020 Dec 3
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# This file contains tests focused on prefix indexes.
+#
+
+source [file join [file dirname [info script]] fts5_common.tcl]
+set testprefix fts5prefix2
+
+# If SQLITE_ENABLE_FTS5 is defined, omit this file.
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+foreach p {3 2 1} {
+ reset_db
+ do_execsql_test 1.$p.0 "
+ CREATE VIRTUAL TABLE t1 USING fts5(xyz, prefix=$p);
+ "
+ do_execsql_test 1.$p.1 {
+ INSERT INTO t1 VALUES
+ ('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 f.');
+ }
+
+ do_execsql_test 1.$p.2 {
+ SELECT highlight(t1, 0, '[', ']') FROM t1('f*');
+ } {
+ {May you [find] [forgiveness] [for] yourself and [forgive] others.}
+ {May you share [freely], never taking more than you give [f].}
+ }
+}
+
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE t2 USING fts5(one, prefix=3);
+ INSERT INTO t2 VALUES('top');
+ INSERT INTO t2 VALUES('to');
+ INSERT INTO t2 VALUES('tommy');
+}
+
+do_execsql_test 2.1 {
+ SELECT * FROM t2('to*');
+} {top to tommy}
+
+
+
+finish_test
diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c
index a0888c0024..0f6b00f436 100644
--- a/ext/misc/cksumvfs.c
+++ b/ext/misc/cksumvfs.c
@@ -356,6 +356,41 @@ static void cksmVerifyFunc(
sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
}
+#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
+/*
+** SQL function: initialize_cksumvfs(SCHEMANAME)
+**
+** This SQL functions (whose name is actually determined at compile-time
+** by the value of the SQLITE_CKSUMVFS_INIT_FUNCNAME macro) invokes:
+**
+** sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n);
+**
+** In order to set the reserve bytes value to 8, so that cksumvfs will
+** operation. This feature is provided (if and only if the
+** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string
+** which is the name of the SQL function) so as to provide the ability
+** to invoke the file-control in programming languages that lack
+** direct access to the sqlite3_file_control() interface (ex: Java).
+**
+** This interface is undocumented, apart from this comment. Usage
+** example:
+**
+** 1. Compile with -DSQLITE_CKSUMVFS_INIT_FUNCNAME="ckvfs_init"
+** 2. Run: "SELECT cksum_init('main'); VACUUM;"
+*/
+static void cksmInitFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int nByte = 8;
+ const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]);
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte);
+ /* Return NULL */
+}
+#endif /* SQLITE_CKSUMBFS_INIT_FUNCNAME */
+
/*
** Close a cksm-file.
*/
@@ -715,7 +750,17 @@ static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
}
static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
- return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
+ sqlite3_vfs *pOrig = ORIGVFS(pVfs);
+ int rc;
+ assert( pOrig->iVersion>=2 );
+ if( pOrig->xCurrentTimeInt64 ){
+ rc = pOrig->xCurrentTimeInt64(pOrig, p);
+ }else{
+ double r;
+ rc = pOrig->xCurrentTime(pOrig, &r);
+ *p = (sqlite3_int64)(r*86400000.0);
+ }
+ return rc;
}
static int cksmSetSystemCall(
sqlite3_vfs *pVfs,
@@ -746,6 +791,11 @@ static int cksmRegisterFunc(
rc = sqlite3_create_function(db, "verify_checksum", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0, cksmVerifyFunc, 0, 0);
+#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
+ (void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1,
+ SQLITE_UTF8|SQLITE_DIRECTONLY,
+ 0, cksmInitFunc, 0, 0);
+#endif
return rc;
}
diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c
index 121eb43d66..6cdd79a4d4 100644
--- a/ext/misc/ieee754.c
+++ b/ext/misc/ieee754.c
@@ -167,6 +167,14 @@ static void ieee754func(
int isNeg = 0;
m = sqlite3_value_int64(argv[0]);
e = sqlite3_value_int64(argv[1]);
+
+ /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
+ if( e>10000 ){
+ e = 10000;
+ }else if( e<-10000 ){
+ e = -10000;
+ }
+
if( m<0 ){
isNeg = 1;
m = -m;
diff --git a/ext/misc/series.c b/ext/misc/series.c
index 092383e57e..a4e92ace88 100644
--- a/ext/misc/series.c
+++ b/ext/misc/series.c
@@ -247,7 +247,8 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
** 4: step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
-** in descending order rather than in ascending order.
+** in descending order rather than in ascending order. If bit 16 is
+** set, then output must appear in ascending order.
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
@@ -273,7 +274,12 @@ static int seriesFilter(
}
if( idxNum & 4 ){
pCur->iStep = sqlite3_value_int64(argv[i++]);
- if( pCur->iStep<1 ) pCur->iStep = 1;
+ if( pCur->iStep==0 ){
+ pCur->iStep = 1;
+ }else if( pCur->iStep<0 ){
+ pCur->iStep = -pCur->iStep;
+ if( (idxNum & 16)==0 ) idxNum |= 8;
+ }
}else{
pCur->iStep = 1;
}
@@ -367,7 +373,11 @@ static int seriesBestIndex(
pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
pIdxInfo->estimatedRows = 1000;
if( pIdxInfo->nOrderBy==1 ){
- if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8;
+ if( pIdxInfo->aOrderBy[0].desc ){
+ idxNum |= 8;
+ }else{
+ idxNum |= 16;
+ }
pIdxInfo->orderByConsumed = 1;
}
}else{
diff --git a/ext/misc/shathree.c b/ext/misc/shathree.c
index 56eba564ce..ef25cb56c6 100644
--- a/ext/misc/shathree.c
+++ b/ext/misc/shathree.c
@@ -31,7 +31,10 @@ SQLITE_EXTENSION_INIT1
#include
#include
#include
+
+#ifndef SQLITE_AMALGAMATION
typedef sqlite3_uint64 u64;
+#endif /* SQLITE_AMALGAMATION */
/******************************************************************************
** The Hash Engine
@@ -621,9 +624,11 @@ static void sha3QueryFunc(
}
nCol = sqlite3_column_count(pStmt);
z = sqlite3_sql(pStmt);
- n = (int)strlen(z);
- hash_step_vformat(&cx,"S%d:",n);
- SHA3Update(&cx,(unsigned char*)z,n);
+ if( z ){
+ n = (int)strlen(z);
+ hash_step_vformat(&cx,"S%d:",n);
+ SHA3Update(&cx,(unsigned char*)z,n);
+ }
/* Compute a hash over the result of the query */
while( SQLITE_ROW==sqlite3_step(pStmt) ){
diff --git a/ext/rbu/rbu.c b/ext/rbu/rbu.c
index 02822c4a42..91773409cf 100644
--- a/ext/rbu/rbu.c
+++ b/ext/rbu/rbu.c
@@ -183,6 +183,13 @@ int main(int argc, char **argv){
break;
}
+ if( nStatStep>0 ){
+ sqlite3_int64 nUsed;
+ sqlite3_int64 nHighwater;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &nUsed, &nHighwater, 0);
+ fprintf(stdout, "memory used=%lld highwater=%lld\n", nUsed, nHighwater);
+ }
+
sqlite3_free(zErrmsg);
return (rc==SQLITE_OK || rc==SQLITE_DONE) ? 0 : 1;
}
diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c
index 12a0a46603..c5f809848d 100644
--- a/ext/rbu/sqlite3rbu.c
+++ b/ext/rbu/sqlite3rbu.c
@@ -4828,22 +4828,24 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
#endif
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
- if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
- /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
- ** taking this lock also prevents any checkpoints from occurring.
- ** todo: really, it's not clear why this might occur, as
- ** wal_autocheckpoint ought to be turned off. */
+ if( pRbu && (
+ pRbu->eStage==RBU_STAGE_OAL
+ || pRbu->eStage==RBU_STAGE_MOVE
+ || pRbu->eStage==RBU_STAGE_DONE
+ )){
+ /* Prevent SQLite from taking a shm-lock on the target file when it
+ ** is supplying heap memory to the upper layer in place of *-shm
+ ** segments. */
if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
}else{
int bCapture = 0;
if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
bCapture = 1;
}
-
if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
if( bCapture && rc==SQLITE_OK ){
- pRbu->mLock |= (1 << ofst);
+ pRbu->mLock |= ((1< $mem1}
+ } $eRes
+}
+
+do_test 1.3 {
+ S delete
+} {}
+
+finish_test
diff --git a/ext/session/sessionnoop.test b/ext/session/sessionnoop.test
new file mode 100644
index 0000000000..16c60b7abf
--- /dev/null
+++ b/ext/session/sessionnoop.test
@@ -0,0 +1,187 @@
+# 2021 Februar 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 regression tests for SQLite library.
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source [file join [file dirname [info script]] session_common.tcl]
+source $testdir/tester.tcl
+ifcapable !session {finish_test; return}
+
+set testprefix sessionnoop
+
+#-------------------------------------------------------------------------
+# Test plan:
+#
+# 1.*: Test that concatenating changesets cannot produce a noop UPDATE.
+# 2.*: Test that rebasing changesets cannot produce a noop UPDATE.
+# 3.*: Test that sqlite3changeset_apply() ignores noop UPDATE changes.
+#
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a PRIMARY KEY, b, c, d);
+ INSERT INTO t1 VALUES(1, 1, 1, 1);
+ INSERT INTO t1 VALUES(2, 2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3, 3);
+}
+
+proc do_concat_test {tn sql1 sql2 res} {
+ uplevel [list do_test $tn [subst -nocommands {
+ set C1 [changeset_from_sql {$sql1}]
+ set C2 [changeset_from_sql {$sql2}]
+ set C3 [sqlite3changeset_concat [set C1] [set C2]]
+ set got [list]
+ sqlite3session_foreach elem [set C3] { lappend got [set elem] }
+ set got
+ }] [list {*}$res]]
+}
+
+do_concat_test 1.1 {
+ UPDATE t1 SET c=c+1;
+} {
+ UPDATE t1 SET c=c-1;
+} {
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(1, 1, 1);
+ INSERT INTO t1 VALUES(2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3);
+}
+
+proc do_rebase_test {tn sql_local sql_remote conflict_res expected} {
+ proc xConflict {args} [list return $conflict_res]
+
+ uplevel [list \
+ do_test $tn [subst -nocommands {
+ execsql BEGIN
+ set c_remote [changeset_from_sql {$sql_remote}]
+ execsql ROLLBACK
+
+ execsql BEGIN
+ set c_local [changeset_from_sql {$sql_local}]
+ set base [sqlite3changeset_apply_v2 db [set c_remote] xConflict]
+ execsql ROLLBACK
+
+ sqlite3rebaser_create R
+ R config [set base]
+ set res [list]
+ sqlite3session_foreach elem [R rebase [set c_local]] {
+ lappend res [set elem]
+ }
+ R delete
+ set res
+ }] [list {*}$expected]
+ ]
+}
+
+do_rebase_test 2.1 {
+ UPDATE t1 SET c=2 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=3 WHERE a=1; -- remote
+} OMIT {
+ {UPDATE t1 0 X.. {i 1 {} {} i 3} {{} {} {} {} i 2}}
+}
+
+do_rebase_test 2.2 {
+ UPDATE t1 SET c=2 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=3 WHERE a=1; -- remote
+} REPLACE {
+}
+
+do_rebase_test 2.3.1 {
+ UPDATE t1 SET c=4 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=4 WHERE a=1 -- remote
+} OMIT {
+ {UPDATE t1 0 X.. {i 1 {} {} i 4} {{} {} {} {} i 4}}
+}
+
+do_rebase_test 2.3.2 {
+ UPDATE t1 SET c=5 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=5 WHERE a=1 -- remote
+} REPLACE {
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 3.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(1, 1, 1);
+ INSERT INTO t1 VALUES(2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3);
+ INSERT INTO t1 VALUES(4, 4, 4);
+}
+
+# Arg $pkstr contains one character for each column in the table. An
+# "X" for PK column, or a "." for a non-PK.
+#
+proc mk_tbl_header {name pkstr} {
+ set ret [binary format H2c 54 [string length $pkstr]]
+ foreach i [split $pkstr {}] {
+ if {$i=="X"} {
+ append ret [binary format H2 01]
+ } else {
+ if {$i!="."} {error "bad pkstr: $pkstr ($i)"}
+ append ret [binary format H2 00]
+ }
+ }
+ append ret $name
+ append ret [binary format H2 00]
+ set ret
+}
+
+proc mk_update_change {args} {
+ set ret [binary format H2H2 17 00]
+ foreach a $args {
+ if {$a==""} {
+ append ret [binary format H2 00]
+ } else {
+ append ret [binary format H2W 01 $a]
+ }
+ }
+ set ret
+}
+
+proc xConflict {args} { return "ABORT" }
+do_test 3.1 {
+ set C [mk_tbl_header t1 X..]
+ append C [mk_update_change 1 {} 1 {} {} 500]
+ append C [mk_update_change 2 {} {} {} {} {}]
+ append C [mk_update_change 3 3 {} {} 600 {}]
+ append C [mk_update_change 4 {} {} {} {} {}]
+
+ sqlite3changeset_apply_v2 db $C xConflict
+} {}
+do_execsql_test 3.2 {
+ SELECT * FROM t1
+} {
+ 1 1 500
+ 2 2 2
+ 3 600 3
+ 4 4 4
+}
+
+
+
+
+
+
+finish_test
+
diff --git a/ext/session/sessionwor.test b/ext/session/sessionwor.test
index 0f0b429d7b..7d9e5c6a89 100644
--- a/ext/session/sessionwor.test
+++ b/ext/session/sessionwor.test
@@ -69,7 +69,9 @@ foreach {tn wo} {
} {
reset_db
- do_execsql_test 2.$tn.0 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b) $wo ;"
+ do_execsql_test 2.$tn.0.1 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b) $wo ;"
+ do_execsql_test 2.$tn.0.2 "CREATE TABLE t2(a INTEGER PRIMARY KEY, b) $wo ;"
+ do_execsql_test 2.$tn.0.3 "CREATE TABLE t3(a INTEGER PRIMARY KEY, b) $wo ;"
do_iterator_test 1.1 t1 {
INSERT INTO t1 VALUES(1, 'two');
@@ -94,6 +96,27 @@ foreach {tn wo} {
} {
{DELETE t1 0 X. {i 1 t four} {}}
}
+
+ do_execsql_test 2.$tn.5 {
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ }
+
+ do_iterator_test 2.$tn.6 t2 {
+ INSERT INTO t2 SELECT a, b FROM t1
+ } {
+ {INSERT t2 0 X. {} {i 1 t one}}
+ {INSERT t2 0 X. {} {i 2 t two}}
+ {INSERT t2 0 X. {} {i 3 t three}}
+ }
+ do_iterator_test 2.$tn.7 t3 {
+ INSERT INTO t3 SELECT * FROM t1
+ } {
+ {INSERT t3 0 X. {} {i 1 t one}}
+ {INSERT t3 0 X. {} {i 2 t two}}
+ {INSERT t3 0 X. {} {i 3 t three}}
+ }
}
finish_test
diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c
index b49bf733ec..f069a42e1f 100644
--- a/ext/session/sqlite3session.c
+++ b/ext/session/sqlite3session.c
@@ -55,6 +55,7 @@ struct sqlite3_session {
int rc; /* Non-zero if an error has occurred */
void *pFilterCtx; /* First argument to pass to xTableFilter */
int (*xTableFilter)(void *pCtx, const char *zTab);
+ i64 nMalloc; /* Number of bytes of data allocated */
sqlite3_value *pZeroBlob; /* Value containing X'' */
sqlite3_session *pNext; /* Next session object on same db. */
SessionTable *pTable; /* List of attached tables */
@@ -97,6 +98,7 @@ struct sqlite3_changeset_iter {
SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
int bPatchset; /* True if this is a patchset */
int bInvert; /* True to invert changeset */
+ int bSkipEmpty; /* Skip noop UPDATE changes */
int rc; /* Iterator error code */
sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
char *zTab; /* Current table */
@@ -438,6 +440,26 @@ static int sessionSerializeValue(
return SQLITE_OK;
}
+/*
+** Allocate and return a pointer to a buffer nByte bytes in size. If
+** pSession is not NULL, increase the sqlite3_session.nMalloc variable
+** by the number of bytes allocated.
+*/
+static void *sessionMalloc64(sqlite3_session *pSession, i64 nByte){
+ void *pRet = sqlite3_malloc64(nByte);
+ if( pSession ) pSession->nMalloc += sqlite3_msize(pRet);
+ return pRet;
+}
+
+/*
+** Free buffer pFree, which must have been allocated by an earlier
+** call to sessionMalloc64(). If pSession is not NULL, decrease the
+** sqlite3_session.nMalloc counter by the number of bytes freed.
+*/
+static void sessionFree(sqlite3_session *pSession, void *pFree){
+ if( pSession ) pSession->nMalloc -= sqlite3_msize(pFree);
+ sqlite3_free(pFree);
+}
/*
** This macro is used to calculate hash key values for data structures. In
@@ -905,13 +927,19 @@ static int sessionPreupdateEqual(
** Growing the hash table in this case is a performance optimization only,
** it is not required for correct operation.
*/
-static int sessionGrowHash(int bPatchset, SessionTable *pTab){
+static int sessionGrowHash(
+ sqlite3_session *pSession, /* For memory accounting. May be NULL */
+ int bPatchset,
+ SessionTable *pTab
+){
if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
int i;
SessionChange **apNew;
sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);
- apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew);
+ apNew = (SessionChange**)sessionMalloc64(
+ pSession, sizeof(SessionChange*) * nNew
+ );
if( apNew==0 ){
if( pTab->nChange==0 ){
return SQLITE_ERROR;
@@ -932,7 +960,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
}
}
- sqlite3_free(pTab->apChange);
+ sessionFree(pSession, pTab->apChange);
pTab->nChange = nNew;
pTab->apChange = apNew;
}
@@ -966,6 +994,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
** be freed using sqlite3_free() by the caller
*/
static int sessionTableInfo(
+ sqlite3_session *pSession, /* For memory accounting. May be NULL */
sqlite3 *db, /* Database connection */
const char *zDb, /* Name of attached database (e.g. "main") */
const char *zThis, /* Table name */
@@ -1020,7 +1049,7 @@ static int sessionTableInfo(
if( rc==SQLITE_OK ){
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
- pAlloc = sqlite3_malloc64(nByte);
+ pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
}
@@ -1063,7 +1092,7 @@ static int sessionTableInfo(
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
- sqlite3_free(azCol);
+ sessionFree(pSession, azCol);
}
sqlite3_finalize(pStmt);
return rc;
@@ -1085,7 +1114,7 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
- pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
+ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
);
if( pSession->rc==SQLITE_OK ){
@@ -1176,7 +1205,7 @@ static void sessionPreupdateOneChange(
}
/* Grow the hash table if required */
- if( sessionGrowHash(0, pTab) ){
+ if( sessionGrowHash(pSession, 0, pTab) ){
pSession->rc = SQLITE_NOMEM;
return;
}
@@ -1243,7 +1272,7 @@ static void sessionPreupdateOneChange(
}
/* Allocate the change object */
- pChange = (SessionChange *)sqlite3_malloc64(nByte);
+ pChange = (SessionChange *)sessionMalloc64(pSession, nByte);
if( !pChange ){
rc = SQLITE_NOMEM;
goto error_out;
@@ -1616,7 +1645,7 @@ int sqlite3session_diff(
int nCol; /* Columns in zFrom.zTbl */
u8 *abPK;
const char **azCol = 0;
- rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
+ rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
if( rc==SQLITE_OK ){
if( pTo->nCol!=nCol ){
bMismatch = 1;
@@ -1714,7 +1743,7 @@ int sqlite3session_create(
** Free the list of table objects passed as the first argument. The contents
** of the changed-rows hash tables are also deleted.
*/
-static void sessionDeleteTable(SessionTable *pList){
+static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){
SessionTable *pNext;
SessionTable *pTab;
@@ -1726,12 +1755,12 @@ static void sessionDeleteTable(SessionTable *pList){
SessionChange *pNextChange;
for(p=pTab->apChange[i]; p; p=pNextChange){
pNextChange = p->pNext;
- sqlite3_free(p);
+ sessionFree(pSession, p);
}
}
- sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */
- sqlite3_free(pTab->apChange);
- sqlite3_free(pTab);
+ sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */
+ sessionFree(pSession, pTab->apChange);
+ sessionFree(pSession, pTab);
}
}
@@ -1759,9 +1788,11 @@ void sqlite3session_delete(sqlite3_session *pSession){
/* Delete all attached table objects. And the contents of their
** associated hash-tables. */
- sessionDeleteTable(pSession->pTable);
+ sessionDeleteTable(pSession, pSession->pTable);
- /* Free the session object itself. */
+ /* Assert that all allocations have been freed and then free the
+ ** session object itself. */
+ assert( pSession->nMalloc==0 );
sqlite3_free(pSession);
}
@@ -1808,7 +1839,8 @@ int sqlite3session_attach(
if( !pTab ){
/* Allocate new SessionTable object. */
- pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1);
+ int nByte = sizeof(SessionTable) + nName + 1;
+ pTab = (SessionTable*)sessionMalloc64(pSession, nByte);
if( !pTab ){
rc = SQLITE_NOMEM;
}else{
@@ -2405,7 +2437,7 @@ static int sessionGenerateChangeset(
int nNoop; /* Size of buffer after writing tbl header */
/* Check the table schema is still Ok. */
- rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
+ rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK);
if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
rc = SQLITE_SCHEMA;
}
@@ -2594,6 +2626,13 @@ int sqlite3session_isempty(sqlite3_session *pSession){
return (ret==0);
}
+/*
+** Return the amount of heap memory in use.
+*/
+sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){
+ return pSession->nMalloc;
+}
+
/*
** Do the work for either sqlite3changeset_start() or start_strm().
*/
@@ -2603,7 +2642,8 @@ static int sessionChangesetStart(
void *pIn,
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset, /* Pointer to buffer containing changeset */
- int bInvert /* True to invert changeset */
+ int bInvert, /* True to invert changeset */
+ int bSkipEmpty /* True to skip empty UPDATE changes */
){
sqlite3_changeset_iter *pRet; /* Iterator to return */
int nByte; /* Number of bytes to allocate for iterator */
@@ -2624,6 +2664,7 @@ static int sessionChangesetStart(
pRet->in.pIn = pIn;
pRet->in.bEof = (xInput ? 0 : 1);
pRet->bInvert = bInvert;
+ pRet->bSkipEmpty = bSkipEmpty;
/* Populate the output variable and return success. */
*pp = pRet;
@@ -2638,7 +2679,7 @@ int sqlite3changeset_start(
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset /* Pointer to buffer containing changeset */
){
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0, 0);
}
int sqlite3changeset_start_v2(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
@@ -2647,7 +2688,7 @@ int sqlite3changeset_start_v2(
int flags
){
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0);
}
/*
@@ -2658,7 +2699,7 @@ int sqlite3changeset_start_strm(
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
){
- return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0, 0);
}
int sqlite3changeset_start_v2_strm(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
@@ -2667,7 +2708,7 @@ int sqlite3changeset_start_v2_strm(
int flags
){
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
- return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert, 0);
}
/*
@@ -2793,11 +2834,14 @@ static int sessionReadRecord(
SessionInput *pIn, /* Input data */
int nCol, /* Number of values in record */
u8 *abPK, /* Array of primary key flags, or NULL */
- sqlite3_value **apOut /* Write values to this array */
+ sqlite3_value **apOut, /* Write values to this array */
+ int *pbEmpty
){
int i; /* Used to iterate through columns */
int rc = SQLITE_OK;
+ assert( pbEmpty==0 || *pbEmpty==0 );
+ if( pbEmpty ) *pbEmpty = 1;
for(i=0; iaData[pIn->iNext++];
assert( apOut[i]==0 );
if( eType ){
+ if( pbEmpty ) *pbEmpty = 0;
apOut[i] = sqlite3ValueNew(0);
if( !apOut[i] ) rc = SQLITE_NOMEM;
}
@@ -2988,31 +3033,27 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
}
/*
-** Advance the changeset iterator to the next change.
+** Advance the changeset iterator to the next change. The differences between
+** this function and sessionChangesetNext() are that
**
-** If both paRec and pnRec are NULL, then this function works like the public
-** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
-** sqlite3changeset_new() and old() APIs may be used to query for values.
+** * If pbEmpty is not NULL and the change is a no-op UPDATE (an UPDATE
+** that modifies no columns), this function sets (*pbEmpty) to 1.
**
-** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
-** record is written to *paRec before returning and the number of bytes in
-** the record to *pnRec.
-**
-** Either way, this function returns SQLITE_ROW if the iterator is
-** successfully advanced to the next change in the changeset, an SQLite
-** error code if an error occurs, or SQLITE_DONE if there are no further
-** changes in the changeset.
+** * If the iterator is configured to skip no-op UPDATEs,
+** sessionChangesetNext() does that. This function does not.
*/
-static int sessionChangesetNext(
+static int sessionChangesetNextOne(
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 *pbNew /* If non-NULL, true if new table */
+ int *pbNew, /* If non-NULL, true if new table */
+ int *pbEmpty
){
int i;
u8 op;
assert( (paRec==0 && pnRec==0) || (paRec && pnRec) );
+ assert( pbEmpty==0 || *pbEmpty==0 );
/* If the iterator is in the error-state, return immediately. */
if( p->rc!=SQLITE_OK ) return p->rc;
@@ -3085,13 +3126,13 @@ static int sessionChangesetNext(
/* 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, apOld);
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld, 0);
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, apNew);
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty);
if( p->rc!=SQLITE_OK ) return p->rc;
}
@@ -3118,6 +3159,37 @@ static int sessionChangesetNext(
return SQLITE_ROW;
}
+/*
+** Advance the changeset iterator to the next change.
+**
+** If both paRec and pnRec are NULL, then this function works like the public
+** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
+** sqlite3changeset_new() and old() APIs may be used to query for values.
+**
+** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
+** record is written to *paRec before returning and the number of bytes in
+** the record to *pnRec.
+**
+** Either way, this function returns SQLITE_ROW if the iterator is
+** successfully advanced to the next change in the changeset, an SQLite
+** error code if an error occurs, or SQLITE_DONE if there are no further
+** changes in the changeset.
+*/
+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 *pbNew /* If non-NULL, true if new table */
+){
+ int bEmpty;
+ int rc;
+ do {
+ bEmpty = 0;
+ rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty);
+ }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty);
+ return rc;
+}
+
/*
** Advance an iterator created by sqlite3changeset_start() to the next
** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
@@ -3390,9 +3462,9 @@ static int sessionChangesetInvert(
/* Read the old.* and new.* records for the update change. */
pInput->iNext += 2;
- rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]);
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0], 0);
if( rc==SQLITE_OK ){
- rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]);
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol], 0);
}
/* Write the new old.* record. Consists of the PK columns from the
@@ -3493,16 +3565,25 @@ int sqlite3changeset_invert_strm(
return rc;
}
+
+typedef struct SessionUpdate SessionUpdate;
+struct SessionUpdate {
+ sqlite3_stmt *pStmt;
+ u32 *aMask;
+ SessionUpdate *pNext;
+};
+
typedef struct SessionApplyCtx SessionApplyCtx;
struct SessionApplyCtx {
sqlite3 *db;
sqlite3_stmt *pDelete; /* DELETE statement */
- sqlite3_stmt *pUpdate; /* UPDATE statement */
sqlite3_stmt *pInsert; /* INSERT statement */
sqlite3_stmt *pSelect; /* SELECT statement */
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 */
+ u32 *aUpdateMask; /* Used by sessionUpdateFind */
+ SessionUpdate *pUp;
int bStat1; /* True if table is sqlite_stat1 */
int bDeferConstraints; /* True to defer constraints */
int bInvertConstraints; /* Invert when iterating constraints buffer */
@@ -3512,6 +3593,167 @@ struct SessionApplyCtx {
u8 bRebase; /* True to collect rebase information */
};
+/* Number of prepared UPDATE statements to cache. */
+#define SESSION_UPDATE_CACHE_SZ 12
+
+/*
+** Find a prepared UPDATE statement suitable for the UPDATE step currently
+** being visited by the iterator. The UPDATE is of the form:
+**
+** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ?
+*/
+static int sessionUpdateFind(
+ sqlite3_changeset_iter *pIter,
+ SessionApplyCtx *p,
+ int bPatchset,
+ sqlite3_stmt **ppStmt
+){
+ int rc = SQLITE_OK;
+ SessionUpdate *pUp = 0;
+ int nCol = pIter->nCol;
+ int nU32 = (pIter->nCol+33)/32;
+ int ii;
+
+ if( p->aUpdateMask==0 ){
+ p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32));
+ if( p->aUpdateMask==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ memset(p->aUpdateMask, 0, nU32*sizeof(u32));
+ rc = SQLITE_CORRUPT;
+ for(ii=0; iinCol; ii++){
+ if( sessionChangesetNew(pIter, ii) ){
+ p->aUpdateMask[ii/32] |= (1<<(ii%32));
+ rc = SQLITE_OK;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32));
+
+ if( p->pUp ){
+ int nUp = 0;
+ SessionUpdate **pp = &p->pUp;
+ while( 1 ){
+ nUp++;
+ if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){
+ pUp = *pp;
+ *pp = pUp->pNext;
+ pUp->pNext = p->pUp;
+ p->pUp = pUp;
+ break;
+ }
+
+ if( (*pp)->pNext ){
+ pp = &(*pp)->pNext;
+ }else{
+ if( nUp>=SESSION_UPDATE_CACHE_SZ ){
+ sqlite3_finalize((*pp)->pStmt);
+ sqlite3_free(*pp);
+ *pp = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ if( pUp==0 ){
+ int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32);
+ int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0);
+ pUp = (SessionUpdate*)sqlite3_malloc(nByte);
+ if( pUp==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ const char *zSep = "";
+ SessionBuffer buf;
+
+ memset(&buf, 0, sizeof(buf));
+ pUp->aMask = (u32*)&pUp[1];
+ memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32));
+
+ sessionAppendStr(&buf, "UPDATE main.", &rc);
+ sessionAppendIdent(&buf, pIter->zTab, &rc);
+ sessionAppendStr(&buf, " SET ", &rc);
+
+ /* Create the assignments part of the UPDATE */
+ for(ii=0; iinCol; ii++){
+ if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[ii], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, ii*2+1, &rc);
+ zSep = ", ";
+ }
+ }
+
+ /* Create the WHERE clause part of the UPDATE */
+ zSep = "";
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(ii=0; iinCol; ii++){
+ if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){
+ sessionAppendStr(&buf, zSep, &rc);
+ if( bStat1 && ii==1 ){
+ assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 );
+ sessionAppendStr(&buf,
+ "idx IS CASE "
+ "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL "
+ "ELSE ?4 END ", &rc
+ );
+ }else{
+ sessionAppendIdent(&buf, p->azCol[ii], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, ii*2+2, &rc);
+ }
+ zSep = " AND ";
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ char *zSql = (char*)buf.aBuf;
+ rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pUp);
+ pUp = 0;
+ }else{
+ pUp->pNext = p->pUp;
+ p->pUp = pUp;
+ }
+ sqlite3_free(buf.aBuf);
+ }
+ }
+ }
+
+ assert( (rc==SQLITE_OK)==(pUp!=0) );
+ if( pUp ){
+ *ppStmt = pUp->pStmt;
+ }else{
+ *ppStmt = 0;
+ }
+ return rc;
+}
+
+/*
+** Free all cached UPDATE statements.
+*/
+static void sessionUpdateFree(SessionApplyCtx *p){
+ SessionUpdate *pUp;
+ SessionUpdate *pNext;
+ for(pUp=p->pUp; pUp; pUp=pNext){
+ pNext = pUp->pNext;
+ sqlite3_finalize(pUp->pStmt);
+ sqlite3_free(pUp);
+ }
+ p->pUp = 0;
+ sqlite3_free(p->aUpdateMask);
+ p->aUpdateMask = 0;
+}
+
/*
** Formulate a statement to DELETE a row from database db. Assuming a table
** structure like this:
@@ -3581,103 +3823,6 @@ static int sessionDeleteRow(
return rc;
}
-/*
-** Formulate and prepare a statement to UPDATE a row from database db.
-** Assuming a table structure like this:
-**
-** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
-**
-** The UPDATE statement looks like this:
-**
-** UPDATE x SET
-** a = CASE WHEN ?2 THEN ?3 ELSE a END,
-** b = CASE WHEN ?5 THEN ?6 ELSE b END,
-** c = CASE WHEN ?8 THEN ?9 ELSE c END,
-** d = CASE WHEN ?11 THEN ?12 ELSE d END
-** WHERE a = ?1 AND c = ?7 AND (?13 OR
-** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND
-** )
-**
-** For each column in the table, there are three variables to bind:
-**
-** ?(i*3+1) The old.* value of the column, if any.
-** ?(i*3+2) A boolean flag indicating that the value is being modified.
-** ?(i*3+3) The new.* value of the column, if any.
-**
-** Also, a boolean flag that, if set to true, causes the statement to update
-** a row even if the non-PK values do not match. This is required if the
-** conflict-handler is invoked with CHANGESET_DATA and returns
-** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
-**
-** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
-** pointing to the prepared version of the SQL statement.
-*/
-static int sessionUpdateRow(
- sqlite3 *db, /* Database handle */
- const char *zTab, /* Table name */
- SessionApplyCtx *p /* Session changeset-apply context */
-){
- int rc = SQLITE_OK;
- int i;
- const char *zSep = "";
- SessionBuffer buf = {0, 0, 0};
-
- /* Append "UPDATE tbl SET " */
- sessionAppendStr(&buf, "UPDATE main.", &rc);
- sessionAppendIdent(&buf, zTab, &rc);
- sessionAppendStr(&buf, " SET ", &rc);
-
- /* Append the assignments */
- for(i=0; inCol; i++){
- sessionAppendStr(&buf, zSep, &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
- sessionAppendInteger(&buf, i*3+2, &rc);
- sessionAppendStr(&buf, " THEN ?", &rc);
- sessionAppendInteger(&buf, i*3+3, &rc);
- sessionAppendStr(&buf, " ELSE ", &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " END", &rc);
- zSep = ", ";
- }
-
- /* Append the PK part of the WHERE clause */
- sessionAppendStr(&buf, " WHERE ", &rc);
- for(i=0; inCol; i++){
- if( p->abPK[i] ){
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " = ?", &rc);
- sessionAppendInteger(&buf, i*3+1, &rc);
- sessionAppendStr(&buf, " AND ", &rc);
- }
- }
-
- /* Append the non-PK part of the WHERE clause */
- sessionAppendStr(&buf, " (?", &rc);
- sessionAppendInteger(&buf, p->nCol*3+1, &rc);
- sessionAppendStr(&buf, " OR 1", &rc);
- for(i=0; inCol; i++){
- if( !p->abPK[i] ){
- sessionAppendStr(&buf, " AND (?", &rc);
- sessionAppendInteger(&buf, i*3+2, &rc);
- sessionAppendStr(&buf, "=0 OR ", &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " IS ?", &rc);
- sessionAppendInteger(&buf, i*3+1, &rc);
- sessionAppendStr(&buf, ")", &rc);
- }
- }
- sessionAppendStr(&buf, ")", &rc);
-
- if( rc==SQLITE_OK ){
- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
- }
- sqlite3_free(buf.aBuf);
-
- return rc;
-}
-
-
/*
** Formulate and prepare an SQL statement to query table zTab by primary
** key. Assuming the following table structure:
@@ -3758,17 +3903,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
"?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 "
@@ -4085,7 +4219,7 @@ static int sessionApplyOneOp(
int nCol;
int rc = SQLITE_OK;
- assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
+ assert( p->pDelete && p->pInsert && p->pSelect );
assert( p->azCol && p->abPK );
assert( !pbReplace || *pbReplace==0 );
@@ -4125,29 +4259,28 @@ static int sessionApplyOneOp(
}else if( op==SQLITE_UPDATE ){
int i;
+ sqlite3_stmt *pUp = 0;
+ int bPatchset = (pbRetry==0 || pIter->bPatchset);
+
+ rc = sessionUpdateFind(pIter, p, bPatchset, &pUp);
/* Bind values to the UPDATE statement. */
for(i=0; rc==SQLITE_OK && ipUpdate, i*3+2, !!pNew);
- if( pOld ){
- rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
+ if( p->abPK[i] || (bPatchset==0 && pOld) ){
+ rc = sessionBindValue(pUp, i*2+2, pOld);
}
if( rc==SQLITE_OK && pNew ){
- rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
+ rc = sessionBindValue(pUp, i*2+1, pNew);
}
}
- if( rc==SQLITE_OK ){
- sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset);
- }
if( rc!=SQLITE_OK ) return rc;
/* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
** the result will be SQLITE_OK with 0 rows modified. */
- sqlite3_step(p->pUpdate);
- rc = sqlite3_reset(p->pUpdate);
+ sqlite3_step(pUp);
+ rc = sqlite3_reset(pUp);
if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
/* A NOTFOUND or DATA error. Search the table to see if it contains
@@ -4279,7 +4412,7 @@ static int sessionRetryConstraints(
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
rc = sessionChangesetStart(
- &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints
+ &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1
);
if( rc==SQLITE_OK ){
size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
@@ -4370,14 +4503,13 @@ static int sessionChangesetApply(
);
if( rc!=SQLITE_OK ) break;
+ sessionUpdateFree(&sApply);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_finalize(sApply.pDelete);
- sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pSelect);
sApply.db = db;
sApply.pDelete = 0;
- sApply.pUpdate = 0;
sApply.pInsert = 0;
sApply.pSelect = 0;
sApply.nCol = 0;
@@ -4405,7 +4537,7 @@ static int sessionChangesetApply(
int i;
sqlite3changeset_pk(pIter, &abPK, 0);
- rc = sessionTableInfo(
+ rc = sessionTableInfo(0,
db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
);
if( rc!=SQLITE_OK ) break;
@@ -4441,11 +4573,10 @@ static int sessionChangesetApply(
}
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))
- ){
+ if( (rc = sessionSelectRow(db, zTab, &sApply))
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
+ || (rc = sessionInsertRow(db, zTab, &sApply))
+ ){
break;
}
sApply.bStat1 = 0;
@@ -4504,9 +4635,9 @@ static int sessionChangesetApply(
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
}
+ sessionUpdateFree(&sApply);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
- sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
@@ -4537,8 +4668,8 @@ int sqlite3changeset_apply_v2(
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);
+ int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -4596,7 +4727,7 @@ int sqlite3changeset_apply_v2_strm(
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse);
+ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -4884,7 +5015,7 @@ static int sessionChangesetToHash(
}
}
- if( sessionGrowHash(pIter->bPatchset, pTab) ){
+ if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
break;
}
@@ -5071,7 +5202,7 @@ int sqlite3changegroup_output_strm(
*/
void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
- sessionDeleteTable(pGrp->pList);
+ sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp);
}
}
@@ -5217,7 +5348,7 @@ static void sessionAppendPartialUpdate(
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( pIter->abPK[i] || a2[0]==0 ){
- if( !pIter->abPK[i] ) bData = 1;
+ if( !pIter->abPK[i] && a1[0] ) bData = 1;
memcpy(pOut, a1, n1);
pOut += n1;
}else if( a2[0]!=0xFF ){
@@ -5472,7 +5603,7 @@ int sqlite3rebaser_rebase_strm(
*/
void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){
- sessionDeleteTable(p->grp.pList);
+ sessionDeleteTable(0, p->grp.pList);
sqlite3_free(p);
}
}
diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h
index 8f255c8645..14d686c66f 100644
--- a/ext/session/sqlite3session.h
+++ b/ext/session/sqlite3session.h
@@ -466,6 +466,14 @@ int sqlite3session_patchset(
*/
int sqlite3session_isempty(sqlite3_session *pSession);
+/*
+** CAPI3REF: Query for the amount of heap memory used by a session object.
+**
+** This API returns the total amount of heap memory in bytes currently
+** used by the session object passed as the only argument.
+*/
+sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);
+
/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
diff --git a/ext/session/test_session.c b/ext/session/test_session.c
index 48ea552e1a..8d6447056a 100644
--- a/ext/session/test_session.c
+++ b/ext/session/test_session.c
@@ -243,6 +243,7 @@ static int SQLITE_TCLAPI test_session_cmd(
{ "patchset", 0, "", }, /* 7 */
{ "diff", 2, "FROMDB TBL" }, /* 8 */
{ "fullchangeset",0, "" }, /* 9 */
+ { "memory_used", 0, "", }, /* 9 */
{ 0 }
};
int iSub;
@@ -352,6 +353,12 @@ static int SQLITE_TCLAPI test_session_cmd(
}
break;
}
+
+ case 9: { /* memory_used */
+ sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
+ break;
+ }
}
return TCL_OK;
diff --git a/main.mk b/main.mk
index 3deae8db06..ad3387f1c5 100644
--- a/main.mk
+++ b/main.mk
@@ -552,13 +552,13 @@ ST_OPT = -DSQLITE_THREADSAFE=0
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
-all: sqlite3.h libsqlite3.a sqlite3$(EXE)
+all: sqlite3.h sqlite3ext.h libsqlite3.a sqlite3$(EXE)
-libsqlite3.a: $(LIBOBJ)
+libsqlite3.a: sqlite3.h $(LIBOBJ)
$(AR) libsqlite3.a $(LIBOBJ)
$(RANLIB) libsqlite3.a
-sqlite3$(EXE): shell.c libsqlite3.a sqlite3.h
+sqlite3$(EXE): sqlite3.h libsqlite3.a shell.c
$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \
shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
@@ -826,13 +826,13 @@ fts3_unicode2.o: $(TOP)/ext/fts3/fts3_unicode2.c $(HDR) $(EXTHDR)
fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c
-fts5.o: fts5.c
+fts5.o: fts5.c sqlite3ext.h sqlite3.h
$(TCCX) -DSQLITE_CORE -c fts5.c
-json1.o: $(TOP)/ext/misc/json1.c
+json1.o: $(TOP)/ext/misc/json1.c sqlite3ext.h sqlite3.h
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c
-stmt.o: $(TOP)/ext/misc/stmt.c
+stmt.o: $(TOP)/ext/misc/stmt.c sqlite3ext.h sqlite3.h
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c
rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
@@ -956,7 +956,7 @@ fuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-
./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db
- valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
+ valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
valgrind ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
# The veryquick.test TCL tests.
diff --git a/manifest b/manifest
index a23dfda342..df61e371c1 100644
--- a/manifest
+++ b/manifest
@@ -1,13 +1,13 @@
-C Merge\slatest\schanges\sfrom\swal2\sbranch.
-D 2020-11-26T18:41:08.207
+C Merge\sin\srecent\senhancements.
+D 2021-03-03T19:41:15.086
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in c16e8a694897f4e6cb26bab4e79306a425f85e30eed63c2a4f1cf36c52a531e2
+F Makefile.in 480ef30ad5ae6914a855df11ef7f9e1c5c6333264aef565d1d9c3b1efa1a4459
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
-F Makefile.msc 305ae9c05ae24bdfcd782a5515a44ad0f6f9855bef69b352bc36be385493c1ce
+F Makefile.msc 43b509eba8b6dd3f6ba60166655aae0066261955f0e197487b251cb9977619d6
F README.md 1514a365ffca3c138e00c5cc839906108a01011a6b082bad19b09781e3aa498a
-F VERSION 4027b9aea92d64385570778ebd14388c0b23e92aafda15e7b89c45886c9b920a
+F VERSION 92f3e4c5cdee6f0779aef1eae857dfc21d0eabb1f2af169dc90e63cd76b15bb2
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
@@ -18,7 +18,7 @@ F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a
F autoconf/Makefile.msc e0f1dafc48d000fd6ddfdb01815271528db55cbc7299ca888df5b93367f0d5a4
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
-F autoconf/configure.ac 3cd933b959fe514eebd1ca1717dfddbf2c9b825b6bc2c5f744deaf5d63af9288
+F autoconf/configure.ac 55be6fc04a5b030b367fe317b72dc8c0eafc4713991fe6182cf647b02fb782cb
F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd
F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
@@ -28,18 +28,18 @@ F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
F autoconf/tea/pkgIndex.tcl.in 3ef61715cf1c7bdcff56947ffadb26bc991ca39d
F autoconf/tea/tclconfig/install-sh bdd5e293591621ae60d9824d86a4b1c5f22c3d00
F autoconf/tea/tclconfig/tcl.m4 66ddf0a5d5e4b1d29bff472c0985fd7fa89d0fb5
-F autoconf/tea/win/makefile.vc 71915591c07cd5137711dc40ba3e127deb3f4531b9aad220b724cf5b451362bd
+F autoconf/tea/win/makefile.vc a5ff708245260c2794c6aaa0151efe5403d5896566eaf096747be0d9075284e4
F autoconf/tea/win/nmakehlp.c 247538ad8e8c508f33c03ec1fbd67d3a07ef6291
F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
-F configure e7df2824b6e60d482d919fb3e7d823faf08467093b096ad47a4fe2eec3f0ba99 x
-F configure.ac 73545c21eebcef9398d85c982c7be260f07708256778221b541f83ae8c6f61eb
+F configure 91893a81f698778dda4d8fb24bfca606ded31ef02bcfbc2ab072d30fb67138d6 x
+F configure.ac 412b65c6107e41c098ad7f5f2e6a3f74ac02ffc6e92b9a6264b9f1060c235a04
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
F doc/begin_concurrent.md 4bee2c3990d1eb800f1ce3726a911292a8e4b889300b2ffd4b08d357370db299
-F doc/lemon.html c5d8ba85ac1daef7be8c2d389899480eb62451ff5c09b0c28ff8157bb8770746
+F doc/lemon.html 1bb72ece6271df0d901d233551dd985f2c6ba30d09382cf2d321ed951ab57491
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56
@@ -52,8 +52,8 @@ F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad
F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
-F ext/expert/expert1.test dba6e752cc701621771f925f3872b183fa688f7b4a9f4822631fc02bdbffc45a
-F ext/expert/sqlite3expert.c 2778d9f06b3a8bfa859cb6b75b82f004477bf5dd78edd17d954319750ca963f3
+F ext/expert/expert1.test 63d778d964e55ef2d1a723043d91c59e7dc6ef1649d91c78c0bef00f9c6f1427
+F ext/expert/sqlite3expert.c de51b187c629a4c4264d5de0b77862641e11426f7a963a92abf2d4077085fc8c
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@@ -104,31 +104,31 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
F ext/fts3/fts3_unicode.c 4b9af6151c29b35ed09574937083cece7c31e911f69615e168a39677569b684d
F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
-F ext/fts3/fts3_write.c 723ed1b11ed46ad1b3a23c0d69fa39e77986783a82d5711bf87a5ce29e0a3b52
+F ext/fts3/fts3_write.c a5159accfd88f85fd3fc2298286d7a9427a02d1ea9a52b7c79730cff7a0bc03f
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
F ext/fts3/tool/fts3view.c 413c346399159df81f86c4928b7c4a455caab73bfbc8cd68f950f632e5751674
F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
-F ext/fts3/unicode/mkunicode.tcl bf7fcaa6d68e6d38223467983785d054f1cff4d9e3905dd51f6ed8801bb590d5
+F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6dbd6348ef0cfc324a7
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
F ext/fts5/fts5.h c132a9323f22a972c4c93a8d5a3d901113a6e612faf30ca8e695788438c5ca2a
F ext/fts5/fts5Int.h 26c74dd5776f798436fbf604a0bf0e8de263b35b5060b05c15f9085845d9fda2
-F ext/fts5/fts5_aux.c dcc627d8b6e3fc773db528ff67b39955dab7b51628f9dba8e15849e5bedfd7fa
+F ext/fts5/fts5_aux.c f558e1fb9f0f86a4f7489e258c162e1f947de5ff2709087fbb465fddb7092f98
F ext/fts5/fts5_buffer.c 5a5fe0159752c0fb0a5a93c722e9db2662822709490769d482b76a6dc8aaca70
F ext/fts5/fts5_config.c be54f44fca491e96c6923a4b9a736f2da2b13811600eb6e38d1bcc91c4ea2e61
-F ext/fts5/fts5_expr.c e527e3a7410393075598cec544e3831798a8c88b3e8878e2cfb7cb147113e925
+F ext/fts5/fts5_expr.c 016bd06030679bd31b0f07ef87d62c42031e5da25cb3174a84e5b0f6ef4b47b0
F ext/fts5/fts5_hash.c 1aa93c9b5f461afba66701ee226297dc78402b3bdde81e90a10de5fe3df14959
-F ext/fts5/fts5_index.c fb8ed13cb8f2ddeb80ea6ade6e35d59b0bc01b9bd741f7e60a1c58a92877d5d7
+F ext/fts5/fts5_index.c acbe3ccd36f5a7373009dfff02a11c4a817af73bc3ef8c340f2105d7d073f8b4
F ext/fts5/fts5_main.c b4e4931c7fcc9acfa0c3b8b5e5e80b5b424b8d9207aae3a22b674bd35ccf149d
F ext/fts5/fts5_storage.c 58ba71e6cd3d43a5735815e7956ee167babb4d2cbfe206905174792af4d09d75
F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
F ext/fts5/fts5_test_tok.c f96c6e193c466711d6d7828d5f190407fe7ab897062d371426dd3036f01258e7
F ext/fts5/fts5_tokenize.c 5e251efb0f1af99a25ed50010ba6b1ad1250aca5921af1988fdcabe5ebc3cb43
-F ext/fts5/fts5_unicode2.c 85f64663cbd8ddd09d3a1e8823759b07085018b4a53158632e264cd785f88763
+F ext/fts5/fts5_unicode2.c eca63dbc797f8ff0572e97caf4631389c0ab900d6364861b915bdd4735973f00
F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d09885d1dadf80
F ext/fts5/fts5_vocab.c 7a071833064dc8bca236c3c323e56aac36f583aa2c46ce916d52e31ce87462c9
F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
@@ -162,9 +162,9 @@ F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c0
F ext/fts5/test/fts5content.test 213506436fb2c87567b8e31f6d43ab30aab99354cec74ed679f22aad0cdbf283
F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519deb43581cb17a57ebe
F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f
-F ext/fts5/test/fts5corrupt3.test b8d4d6f167b8189999dac7162ddec614ab9dca4fe5e9dcfd315e383a5e15444b
-F ext/fts5/test/fts5corrupt4.test ea805c4d7c68b5f185b9db5d2060a7ae5875339738dd48203c92162f41e7ca91
-F ext/fts5/test/fts5delete.test 4a15fb03b6c7eac62ac807a3a32b7f0dc74f0d479c410e3e3568ae96b9469290
+F ext/fts5/test/fts5corrupt3.test 5df97f353102f0078d0bb418d620652d03460ee1cceb2992a3b5ee6fb619b24e
+F ext/fts5/test/fts5corrupt4.test f4c08e2182a48d8b70975fd869ee5391855c06d8a0ff87b6a2529e7c5a88a1d3
+F ext/fts5/test/fts5delete.test 619295b20dbc1d840b403ee07c878f52378849c3c02e44f2ee143b3e978a0aa7
F ext/fts5/test/fts5detail.test 31b240dbf6d44ac3507e2f8b65f29fdc12465ffd531212378c7ce1066766f54e
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
F ext/fts5/test/fts5dlidx.test b90852c55881b29dbac6380b274de27beae623ac4b6d567c6c8fb9cdc315a86e
@@ -206,6 +206,7 @@ F ext/fts5/test/fts5plan.test 79d35b5e83bbdcba48d946a7f008df161f6b0ede1a966892d0
F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15
F ext/fts5/test/fts5porter2.test 0d251a673f02fa13ca7f011654873b3add20745f7402f108600a23e52d8c7457
F ext/fts5/test/fts5prefix.test a0fa67b06650f2deaa7bf27745899d94e0fb547ad9ecbd08bfad98c04912c056
+F ext/fts5/test/fts5prefix2.test 3847ce46f70b82d61c6095103a9d7c53f2952c40a4704157bc079c04d9c8b18b
F ext/fts5/test/fts5query.test ac363b17a442620bb0780e93c24f16a5f963dfe2f23dc85647b869efcfada728
F ext/fts5/test/fts5rank.test c9fd4a1e36b4fa92d572ec13d846469b97da249d1c2f7fd3ee7e017ce46f2416
F ext/fts5/test/fts5rebuild.test 55d6f17715cddbf825680dd6551efbc72ed916d8cf1cde40a46fc5d785b451e7
@@ -293,7 +294,7 @@ F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0e
F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
F ext/misc/carray.c b75a0f207391038bf1540d3372f482a95c3613511c7c474db51ede1196321c7c
F ext/misc/carray.h de74ac70b2338f416723f7d538026e8ec0b7f1d388319f8f140c9a4d7677f02e
-F ext/misc/cksumvfs.c c4e7ebeae5aa578df98f23bddb63ecbcbe913ee3c32b8b769525af100d752061
+F ext/misc/cksumvfs.c 8dc4e1b718e374bed3a9b5c0a08da2922438002e33267311697768e06217b983
F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c85c243
F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9
F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9
@@ -306,7 +307,7 @@ F ext/misc/explain.c 0086fab288d4352ea638cf40ac382aad3b0dc5e845a1ea829a694c015fd
F ext/misc/fileio.c 9b69e25da3b51d4a1d905a464ccb96709792ad627a742ba09215bc0d1447e7bd
F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5
F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d
-F ext/misc/ieee754.c 5c7ca326361c7368f95f5743972eade3b8b24f60359ed7cba4706668a5682896
+F ext/misc/ieee754.c cd6ab89f85fda8a020559b3f4d03001a8a62dd856beda5af3f558621d12be913
F ext/misc/json1.c f31e89171f932d1821c91f10d2cb4979fc0447030030a8bce70420cd43d074c0
F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d
F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b
@@ -321,9 +322,9 @@ F ext/misc/regexp.c 246244c714267f303df76acf73dcf110cf2eaf076896aaaba8db6d6d21a1
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
-F ext/misc/series.c fbb8e6be97b54d10d2f235e163fa2f53a8f4421c66ebd532a233fd1c69c3f522
+F ext/misc/series.c c6bd5d249e5199a1b55aeee4d0e6576ff3a68702fc475dbd64503a32903516c7
F ext/misc/sha1.c c8f2253c8792ffab9517695ea7d88c079f0395a5505eefef5c8198fe184ed5ac
-F ext/misc/shathree.c 135b7c145db4a09b1650c3e7aff9cb538763a9a361e834c015dd1aaf8d5c9a00
+F ext/misc/shathree.c e984f31731de4cf302a0386be5fe664580f63d8204c47b9b41cc4b997745f9ec
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
@@ -341,7 +342,7 @@ F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
F ext/misc/wholenumber.c 520f34c3099e5b7d546f13708607dc2fa173c46b68952eecf0d19cd675fec85e
F ext/misc/zipfile.c e35e035bc2765b1ccdcb15f9815c2112843fcbc8f36aa071f0e5935df7072228
F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64
-F ext/rbu/rbu.c 8681f6157db6adc82c34af24b14ea8a3be0146ad2a3b6c1d5da6cb8a5796c8ce
+F ext/rbu/rbu.c b880ca5cb857d6d6f52e72eb7397813058ef48c78c5402cd04ff2b6b5437f622
F ext/rbu/rbu1.test 221d9c18a5e600ac9ac6b1810d99d9f99163a7909ba61597876ab6e4d4beb3d6
F ext/rbu/rbu10.test 0a201c32202143f23c81c0144503da339786fc20acb7a2fda11601b65659f314
F ext/rbu/rbu11.test 5c834cf491086b45e071eabf71f708febc143e86a384a92de69e0b1a4cace144
@@ -381,7 +382,7 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
F ext/rbu/rbuvacuum2.test b8e5b51dc8b2c0153373d024c0936be3f66f9234acbd6d0baab0869d56b14e6b
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
-F ext/rbu/sqlite3rbu.c 05c457c27e9340c944f34e850871a915a6b5ee1d823f7a0bb2b482ac6b1e1464
+F ext/rbu/sqlite3rbu.c e6531884442b72f9e0ba47036fb5c4641ea817ff659a642b3984c10b8535b0fd
F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
@@ -432,7 +433,7 @@ F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a
F ext/session/session1.test 0b2f88995832ea040ae8e83a1ad4afa99c00b85c779d213da73a95ea4113233e
-F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
+F ext/session/session2.test 7f53d755d921e0baf815c4258348e0ed460dfd8a772351bca5ad3ccbb1dc786e
F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
@@ -454,21 +455,23 @@ F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
F ext/session/sessionfault2.test dd593f80b6b4786f7adfe83c5939620bc505559770cc181332da26f29cddd7bb
F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2c52219819bc105a25
+F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09
+F ext/session/sessionnoop.test a9366a36a95ef85f8a3687856ebef46983df399541174cb1ede2ee53b8011bc7
F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2f05e82363b9142810
F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5
-F ext/session/sessionwor.test 67b5ab91d4f93ce65ff1f58240ac5ddf73f8670facc1ffa49cef56293d52818d
+F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3changebatch.c d5553b79e012ee2cb06c0a96bdf9dfe19e66354390ea0036cc46c4953142d517
F ext/session/sqlite3changebatch.h e72016998c9a22d439ddfd547b69e1ebac810c24
-F ext/session/sqlite3session.c fe5fab3c63b734a647784596e44f4ee778e7c7b12ce7aaa2cf4ed445d6c5763a
-F ext/session/sqlite3session.h 030746cb4c8fb18a3b635e0ff93f609a0ed03da77ce8172dfa61cccead42878c
-F ext/session/test_session.c 1e4790384606226f8e63a6ebebb39962721d0363c8192c230de1e4c31c410c1b
+F ext/session/sqlite3session.c 5eb3a3cb490912376c5720d24eb256753e4a29a2a62f39ac708a084d854b4fea
+F ext/session/sqlite3session.h e06f2bd77d2a2fc00a926c39ac47d639f7f1e36e1eba56862d010aaf37119e68
+F ext/session/test_session.c 602e68c559a8b39e89a76ffd30818594d9691c1cdb1d89df15802c56defde4e7
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk c87970028c63dd8dede77e022304e36cfecbd1faba139bd3a53836ef056dcffd
+F main.mk 69d8954327d27f9e47473e9f93d13d059a3947ab006ad59a116b062ffc96c046
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -480,37 +483,37 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
-F src/alter.c b6de60d59419e34f5b48ff2b21fe0f9bb66fc714e5545a6ac790ac7a0c46548c
+F src/alter.c d5fd529509880eade9ea59ddb24a56e9fe0579ee7f2e9e18bac62b7bd05b3a10
F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c
-F src/attach.c 0f497c15c4cfe3bdcb214f0dbdbbb6c5ed7e8a9308ac445c7959f5e5780437a9
-F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06
+F src/attach.c 9cbe761e464025694df8e6f6ee4d9f41432c3a255ca9443ccbb4130eeb87cf72
+F src/auth.c 08954fdc4cc2da5264ba5b75cfd90b67a6fc7d1710a02ccf917c38eadec77853
F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d
F src/bitvec.c 8433d9e98dd6f2ea3286e0d2fe5d65de1bfc18a706486eb2026b01be066b5806
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
-F src/btree.c f667788093ffab745776e943d3f9db030aea8530c1d14b062ca771e97d7527a1
-F src/btree.h a2eda1ab68939ea3057bc797fd16179dfe2bcd247c022f62ceccd136186611a5
-F src/btreeInt.h 900af7149206694de988d14fbc116e482a732340a8dc5e4bfdd23b7faf7d6f14
-F src/build.c 4c44cee793d16c653986cf00c32c36cf0cbfe4932a319cc7d5c0f10a2801151c
+F src/btree.c 27e89248b523e3799b67b24c8a8757fed45db102a669fc20cff258d2d4f488c0
+F src/btree.h 23ceeca6baee45a9b9d0bff86bdc1924d084f99cdddcd1df8a08fea03f007e60
+F src/btreeInt.h 0ef4c1cba8eafcb54f508c3a6cb3aedbe961ee03e4c147d28252f1e5b5357033
+F src/build.c 3c764575a87097ad799d934c626514a14871acb53f4a388c3d2edfb9bb78143f
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
-F src/ctime.c e98518d2d3d4029a13c805e07313fb60c877be56db76e90dd5f3af73085d0ce6
+F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410
F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
-F src/delete.c 927cf8f900583e79aca8f1a321979e0a8f053babd9a690b44b38f79de2cc09fe
-F src/expr.c 0d196ed5a2ebf96be7e8df88add4fabfad0dce16c0fed81a4b8f6a26e259797f
+F src/delete.c 73f57a9a183532c344a3135cf8f2a5589376e39183e0b5f562d6b61b2af0f4d8
+F src/expr.c 6793c836aff149b14011ad546ae1648a18573779ee78f5a7d375f2a3047e8c8e
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
-F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
-F src/func.c 095f359528417453a4500b5f92dfe146273148f80ad926e53239bfc928a84933
-F src/global.c 943256ac44f333039d35a9830c18d075a81fa6b6bf2af05771494a9acfb9a40b
+F src/fkey.c e9063648396c58778f77583a678342fe4a9bc82436bf23c5f9f444f2df0fdaa4
+F src/func.c e701e9a1cbc2e1814d381ce781925c2e8776cdf994e7c17f53578ec3cc7af40a
+F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 7e081d33aab4a9d761c39dccf3c3872c35501565d2ed9db66301918d23bc7901
+F src/insert.c 8942baede303a54ba3b6d06200d5b74c9bc25ababec8a55823e06309748cd4a3
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
-F src/main.c 975a31135c95ab9b33d275141e4c54171d11b1cc3d6c7060db48a72536304fb5
+F src/main.c f885f4bf754b4d2add47e6e2ad5a2ead591e9718da7170858bd7c2a5c2ea5643
F src/malloc.c c1af4ac5a463648cd2953fd4ac679b3ba9022ce5ec794a60806150ad69dfd33a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -518,7 +521,7 @@ F src/mem2.c b93b8762ab999a29ae7751532dadf0a1ac78040308a5fb1d17fcc365171d67eb
F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memdb.c ab0632d42407e866d2b616bd19d4211ac0ad1b430f04c4e187d60005b8700b98
-F src/memjournal.c 90b2ca7e2f465d57c16b69d15a9f3e3294af61088eb4938f2f7664d5ac50f813
+F src/memjournal.c 431c70a111223a8a6e2e7e9f014afc6c88d818d357d866afc563195f2277d50e
F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8
F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25
F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a
@@ -526,37 +529,37 @@ F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
F src/mutex_unix.c dd2b3f1cc1863079bc1349ac0fec395a500090c4fe4e11ab775310a49f2f956d
F src/mutex_w32.c caa50e1c0258ac4443f52e00fe8aaea73b6d0728bd8856bedfff822cae418541
F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6
-F src/os.c 80e4cf3e5da06be03ca641661e331ce60eeeeabf0d7354dbb1c0e166d0eedbbe
+F src/os.c 2d6e646370b1aa78942c68d7edf124e518963adf4a90bce87f365a5a5495529a
F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c 09f02e450992ae878ab2ab2eea45e92d6bf12d53d0643e5ccd1f3e10103ad7de
F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
-F src/pager.c d24754ab1d9e03349aca479f6357c716183a0db12df930f387071024f557efc1
+F src/pager.c 964ebfc5105bdfe1b85fcdcd8b79fc3b971c994d2eaffa8a9dde288d3091e356
F src/pager.h 034a193e53d6649bdb641aa996e38094dbb7cbe365d8c10eba871a38a0f5ebb3
-F src/parse.y 5ba6522aba301e829917c8dd48290d357b6a9f68276d411739d01f266bafe4be
+F src/parse.y f5376ab515dc1c8cfe6fc884be2eb46726cddaf54eca17346b276757064d250f
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
-F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
+F src/pcache1.c 388304fd2d91c39591080b5e0f3c62cfba87db20370e7e0554062bfb29740e9f
F src/pragma.c 3c948d3ed733ece947757fbe2c1682ce0858f410c23d2347f8e0d8256f00098f
F src/pragma.h b93fd693bd38cefeaf8e2c7e8c929717f7799a9770104cdd2bae8df2a84e2da7
-F src/prepare.c 270170a239c0f66bd3c228f373afe24447c2614a6829ae22080babc64f241931
-F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
+F src/prepare.c f634a9e799a6b1c136d8ee12479cffa22862bfb807d307b1db406aa0cdb042a5
+F src/printf.c 2b03a80d7c11bb422115dca175a18bf430e9c9dbaa0eee63b758f0c022f8f34f
F src/random.c f27af4099afaea7284ade5c206224dcfdb2334cfd119d018b470d46356b3f27d
-F src/resolve.c 1948a92ca9eab776632816b97e57c61d933474a78aad4f4ef835c916a83dbb1c
+F src/resolve.c 889469e6980181ce77ee8ab3fc84bd52ed6c1c3577fd102d52623d66cc65a3d0
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
-F src/select.c fe64564a9437fde31824b85b02d51a2806468e3c70666528087a1cb0730caee8
-F src/shell.c.in 55113760ae91a05c6ce4558714a1c8fc7a44bf266f735de6e71ea40f79e69830
-F src/sqlite.h.in a3452ff4026c01484ed9291388d538c3de7da4118e359c6972de66060317eca8
+F src/select.c 78afdb6f14b492130b3e311285a2ad432c734f492a0c12e633829536f2b0b061
+F src/shell.c.in 844417f84df1f6c4fce1c815629a888cfdcf219e86513e9c332bbcc38832f477
+F src/sqlite.h.in ef0e051b00408e9c8a52c94913515c000793a0ccd81ee39579f1a2305e5f7ff9
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
-F src/sqliteInt.h a40064e6dc3d788bb625e7a34e10b9e4b84c573925540bc07516cb691dd6d980
+F src/sqliteInt.h f43406e959dd3882071603cd3c7b30e0cea3c2cc9e964e65c2d3625533d733be
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 986b6391f02cd9b53c1d688be55899f6ffddeb8e8014cd83c1b73ff912579a71
-F src/test1.c 67aa729f37c2d8066bf0c44536c5ab8646b5dc84d5f84bde6f7397147a4513cc
+F src/test1.c 01e2d375fdbd3fa7519f49160f38d4eda847fca64035b11bbc80cafb06db7d4f
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
@@ -571,7 +574,7 @@ F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0
F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857
F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
-F src/test_config.c 2882bb5dfb6429a79736029766fd12f4fcf25a721dd9e48e71eeca3ccf2c94df
+F src/test_config.c 21aa5fc3e6bb11c60ba7f9a33b73951af8cb7a2042355f07469146b7e5e36765
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c 86142ba864d4297d54c5b2e972e74f3141ae4b30f05b3a95824184ed2d3d7f91
F src/test_devsym.c aff2255ea290d7718da08af30cdf18e470ff7325a5eff63e0057b1496ed66593
@@ -610,34 +613,34 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
-F src/tokenize.c 4dc01b267593537e2a0d0efe9f80dabe24c5b6f7627bc6971c487fa6a1dacbbf
-F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
-F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10
-F src/update.c 602d92660a940600f480e013ce2a23eef1da2eb8e2d1d7ece10dbc9ef464bc8c
-F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
+F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9
+F src/treeview.c c6260e1fa5f41c361b2409edc9b0050bcaef5bc4d6abc467fbc45f0d7ccf3d84
+F src/trigger.c 861c3ec2c5b0fc830bdf82470454a9324fad70cbaa96d2e208fb54577c9e8d28
+F src/update.c 254aa78e23a18767a5d55b9d7efb6d808b1fcfd52124c774fa7868546f2ca2af
+F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
-F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002
+F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048
F src/vacuum.c 9d6ac090ff36d80f0a2c5bfc63994b714c464bfc44efa5056c457a6a4163dc99
-F src/vdbe.c 31c984a6a4ec0445a57b13d76f59323cb364c050648d3acb3421751886e6184e
-F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
-F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
-F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
-F src/vdbeaux.c 0aaec35c90956fb03238e9ce3981e7111a53ff99eb161ef79fa55ae14fedcf7c
+F src/vdbe.c c3daebc1ea5d2e1033c02e7c2df0fc8313f65507d5c7d6847fb91fea9a12bfc3
+F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
+F src/vdbeInt.h 3df118924e1711f1bbc8e30c46260d0ab6c3b029b32dd411f789111f76434f3c
+F src/vdbeapi.c 4a43e303ec3354c785f453e881521969378e85628278ab74ba4a9df790c0d93b
+F src/vdbeaux.c 3ca9ea12fc429b680a1a4748fe7c2dc4eee8cf0ba70c66c04ebca4fb7b652c73
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
-F src/vdbetrace.c fa3bf238002f0bbbdfb66cc8afb0cea284ff9f148d6439bc1f6f2b4c3b7143f0
+F src/vdbetrace.c 666c6fd9f1b62be6999e072a45b913e3c2c3518bc60dfd4d54fe304130acb724
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
-F src/vtab.c 5f5fc793092f53bbdfde296c50f563fb7bda58cf48e9cf6a8bdfbc5abd409845
+F src/vtab.c 032a0165c147fda16927e6a3230e90c068d4af93f887ce94e26f678fe48e5e4c
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 14561b22781dbf67ec97dddce537e31c1419f94e06d548d2d26cb5c44552ecda
F src/wal.h 7a733af13b966ecb81872ce397e862116b3575ea53245b90b139a2873ee87825
-F src/walker.c 3df26a33dc4f54e8771600fb7fdebe1ece0896c2ad68c30ab40b017aa4395049
-F src/where.c ad738741bf45999188a3047f6277063e3f6843304d581e3ad5875419afdeb6b2
-F src/whereInt.h 59077fbd0b3d01bc8715e746c86a99ebf4c85bde8a57077ec04d2a23e59666ec
-F src/wherecode.c a3a1aff30fe99a818d8e7c607980f033f40c68d890e03ed25838b9dbb7908bee
-F src/whereexpr.c 3a463e156ea388083c501502229c2c7f4f5c6b5330ea59bdf40d6eb6e155a25f
-F src/window.c edd6f5e25a1e8f2b6f5305b7f5f7da7bb35f07f0d432b255b1d4c2fcab4205aa
+F src/walker.c d42d6c80ea363ef689a462e65eefcfe87deab924c50de5baa37ecb6af7d7ddaa
+F src/where.c 10d06b16670a1d2a992d52a9f08e49426d38a08fb0a7ae5f7f62fd023d560e1e
+F src/whereInt.h 446e5e8018f83358ef917cf32d8e6a86dc8430113d0b17e720f1839d3faa44c4
+F src/wherecode.c e57a8690311a75d06e723e8d379f9831de04aba300e07174d236e32a7f9c7a13
+F src/whereexpr.c 53452fe2fb07be2f4cb17f55cc721416fae0092c00717f106faf289c990b6494
+F src/window.c fdf01316f6cecf060378aa1713a29e527ab683823ba7d15b8978ec70165e8bdb
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7
@@ -650,14 +653,18 @@ F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807
F test/alter3.test e487958dec7932453e0b83baf21d6b1e71d5e7d9a55bc20eadfa62a51ddffc29
F test/alter4.test dfd6086faf461b27ca2d2999848dcd207edf23352fc1592d0005c0844f3f08cf
F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
-F test/alterauth2.test c0a1ddf5b93d93cb0d15ba7acaf0c5c6fb515bbe861ede75b2d3fabad33b6499
-F test/altercol.test 1d6a6fe698b81e626baea4881f5717f9bc53d7d07f1cd23ee7ad1b931f117ddf
-F test/alterlegacy.test 82022721ce0de29cedc9a7af63bc9fcc078b0ee000f8283b4b6ea9c3eab2f44b
+F test/alterauth2.test 794ac5cef251819fe364b4fe20f12f86e9c5d68070513c7fd26c17cb244c89af
+F test/altercol.test 65eef562f0eea7a1f5ddd4a140c4274c2bfc5712bb2ab2096f738852b0efce86
+F test/altercorrupt.test 584d707a80e106952d6382790c8919bcf9f0db678ed3a1c09fd98b7f9d1d3a10
+F test/alterdropcol.test baad37ff9b07078ea02dcc33dbfb82bde655f3eee5c453e218f69501c36f02ba
+F test/alterdropcol2.test 3948c805ca52f4621051b35968c18c09d107eb117e2b656c78cee3b2870650c0
+F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b
+F test/altermalloc3.test 2c7bbd8cf3e9c4a91e28675bb62bcc2ef70f227967fa74349f03d9f4642f0615
F test/altertab.test 6d7bbac2c4a6ef71b775094a3298fa3a92274d95034ee23157ffba92768e47e6
F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b
-F test/altertab3.test 1db384eb85b4a30b0b332842f5c596b0dc0126f7c61959be3f85ae8b1c271d9a
+F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7
F test/analyze3.test fca2a9de0017becfdcc201647f03b1cfd5ba0e7b5b5c852936e4ec62780cde49
@@ -702,7 +709,7 @@ F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e
F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d
F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989
-F test/backup2.test 1fd1ad8c5b3d2d5b9c0cce4143a4fc610d51ddc6ae16a7a122973d43e6b50bbd
+F test/backup2.test 8facb54df1388419d34b362ab1f7e233310ff3a3af64e8ad5ec47ba3c2bbe5cf
F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32
F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4
F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
@@ -772,6 +779,7 @@ F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 87ad5458bb8709312dac0d6755fd30e8e4ca83298d0a9ef6e5c24277a3c3390e
+F test/columncount.test eff33d402a7b0fde0a52a1920d238af200ca573327021e0ce3b7e5688de41449
F test/concfault.test 500f17c3fcfe7705114422bcc6ddd3c740001a43
F test/concfault2.test 34b3fd258836aa305475d00e804c7450ade92f0de0bf9fa620e701446669bb12
F test/concurrent.test a0248ec6e3e79a5948453649cf86b5b359175cba55ea636b15426d6f0fa6c3da
@@ -808,6 +816,7 @@ F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af
F test/corruptL.test 22589f503602cc5984e80f27f46c4de2134f24f1515ba2440513c377cb692258
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
+F test/corruptN.test 781c5f26a2d8918f03d45ac4968a738031eeb113a4b153c7588756d9b09c7b04
F test/cost.test 1d156ce9858780a966c062694687afe0343a0ed12d081d071fb57027e726bafc
F test/count.test e0699a15712bc2a4679d60e408921c2cce7f6365a30340e790c98e0f334a9c77
F test/countofview.test e17d6e6688cf74f22783c9ec6e788c0790ee4fbbaee713affd00b1ac0bb39b86
@@ -816,7 +825,7 @@ F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418
F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc
-F test/crash5.test f14ff37eddc41991be4eb63568f86caa306fd9962a0ae3750db8836777bb7aae
+F test/crash5.test 4aa55e7ac3c4bc511873e457aa65d2827d52da9b51e061511899dadcfe22b1e8
F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba
F test/crash7.test 1a194c4900a255258cf94b7fcbfd29536db572df
F test/crash8.test 64366e459c28dd62edfb7ad87253a409c7533b92d16fcc479a6a8131bdcc3100
@@ -835,7 +844,7 @@ F test/dbdata.test 042f49acff3438f940eeba5868d3af080ae64ddf26ae78f80c92bec3ca7d8
F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e
F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b321e30
F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee
-F test/dbfuzz2.c 40cc4600947f30600f0ab365a2714ec76a899c9adb2c0ccd63ba583b2f71390e
+F test/dbfuzz2.c db2a1710c0d30d38e1352ee1b52b717fcb224c8caacc6f10909ef540f73cc9e8
F test/dbpage.test 650234ba683b9d82b899c6c51439819787e7609f17a0cc40e0080a7b6443bc38
F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
@@ -890,6 +899,8 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
+F test/exists2.test 3e5726d6a67ebd4bd3466db58be424c09156c1f276c7594abb260cbf6ad494d3
+F test/existsfault.test 74f7edc713f5a335e7ff47adf503067bf05c6f8630f88b2a19c24f0fa5486ab8
F test/expr.test 26cd01e8485bc48c8aa6a1add598e9ce1e706b4eb4f3f554e0b0223022e8c2cf
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
@@ -975,7 +986,7 @@ F test/fts3conf.test c84bbaec81281c1788aa545ac6e78a6bd6cde2bdbbce2da261690e3659f
F test/fts3corrupt.test 79a32ffdcd5254e2f7fa121d9656e61949ad049c3c6554229911b7ceac37c9c6
F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cdb764b491f
-F test/fts3corrupt4.test e4662d37f02248301d8b58778eac380663e09a17a38dd5d6bb5ea4c927b9a575
+F test/fts3corrupt4.test b71512ec391d39da96d60d01959e4e9f20d4237a964a94abcf5f5a2ad28378c1
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
F test/fts3corrupt6.test b6c55218b704b0cef224b284c756f9c55d0afd0b3c3837618bffeaa8c31e0d8e
F test/fts3cov.test 7eacdbefd756cfa4dc2241974e3db2834e9b372ca215880e00032222f32194cf
@@ -1032,9 +1043,9 @@ F test/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309
F test/fts4onepass.test d69ddc4ee3415e40b0c5d1d0408488a87614d4f63ba9c44f3e52db541d6b7cc7
F test/fts4opt.test 0fd0cc84000743ff2a883b9b84b4a5be07249f0ba790c8848a757164cdd46b2a
F test/fts4record.test a48508f69a84c9287c8019d3a1ae712f5730d8335ffaf8e2101e691d078950bb
-F test/fts4rename.test 15fd9985c2bce6dea20da2245b22029ec89bd4710ed317c4c53abbe3cfd0c880
+F test/fts4rename.test 2e0565ffd92b2c51f1a757df0b8f2ca30537197106fec09e943919801d173692
F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f757380429
-F test/fts4unicode.test ceca76422abc251818cb25dabe33d3c3970da5f7c90e1540f190824e6b3a7c95
+F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9
F test/fts4upfrom.test 8df5acb6e10ad73f393d1add082b042ab1db72567888847d098152121e507b34
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test f673822636fb8ed618dd2b80230d16e495d19c8f2e2e7d6c22e93e2b3de097ad
@@ -1043,6 +1054,7 @@ F test/func3.test 2bb0f31ab7baaed690b962a88544d7be6b34fa389364bc36a44e441ed3e3f1
F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31
F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a
F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c
+F test/func7.test b9e2a1a30a8562b00841b4a21a5d2d81754fa3ab99275fd71fd5279287b44b1c
F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa
F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
@@ -1050,7 +1062,7 @@ F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c
F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
-F test/fuzzcheck.c 5f81f2cc65f13068620245f2e2c6059657d3b26be476df379ae2da539f17676d
+F test/fuzzcheck.c 95427205d6cedb8fdb95bf7738e95785c790febf782d9c1e6067baaa1adc871d
F test/fuzzdata1.db d36e88741b4f23bcbaaf55b006290669d03c6c891cf13c7b3a53bc1b097b693f
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
@@ -1058,17 +1070,17 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4
F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5
F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7
F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2
-F test/fuzzdata8.db f8451a1fd38efbea8c1a7cdf5d02259c4702446a9fabf566becd306b64a50236
+F test/fuzzdata8.db c8325de6fbdd24d030cd3a01384a2ff325dda5d5e3ff5d531a26ada3d9d7e010
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
-F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
+F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc
F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c
F test/gencol1.test b05e6c5edb9b10d48efb634ed07342441bddc89d225043e17095c36e567521a0
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
-F test/having.test e4098a4b8962f9596035c3b87a8928a10648acc509f1bb8d6f96413bbf79a1b3
+F test/having.test a89236dd8d55aa50c4805f82ac9daf64d477a44d712d8209c118978d0ca21ec9
F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
-F test/hook.test e97382e68e4379838e888756d653afd159f5f14780315ff97b70360d3d8485bc
+F test/hook.test fa54fa8afc842ae375f10c1f9fc0014fa59789052fc30c9eae19811fa3afa009
F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
F test/icu.test 716a6b89fbabe5cc63e0cd4c260befb08fd7b9d761f04d43669233292f0753b1
F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
@@ -1076,7 +1088,7 @@ F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
F test/in.test 688ed2011d922d83141a45af431601738674a4c0bdde34b6351f688b82a169b3
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
-F test/in4.test 65460600d48933adba4283c6ebd089aae173d16136ab9d01f74c89089090c5a5
+F test/in4.test 64ac9c767ac5af562f066a40163d4202f8fa3be05d264ec65d6258e74606b30c
F test/in5.test b32ce7f4a93f44c5dee94af16886d922cc16ebe33c8e1765c73d4049d0f4b40f
F test/in6.test 8562d0945195cab3cc4ab3794e9118e72cb44c43f785c2b04d48a9d06ca6b4ec
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
@@ -1147,7 +1159,7 @@ F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/lemon-test01.y 58b764610fd934e189ffbb0bbfa33d171b9cb06019b55bdc04d090d6767e11d7
-F test/like.test 47b81d5de2ff19d996d49a65d50ec9754246aacbe0e950b48d186d9d8171eaf0
+F test/like.test 0b7b4765ca59d95a1f92dfab9e4d810c9fb8280b5edd6332a01340a20db9e0ed
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/like3.test 03d1bdf848483b78d2cfd1db283d75c4ec2e37c8b8eccc006813f3978d78fbbd
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
@@ -1230,6 +1242,7 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
+F test/notnull2.test 965a893619751255e59c911a8c58504b3174a3788b1458b7c7365b232209711b
F test/null.test b7ff206a1c60fe01aa2abd33ef9ea83c93727d993ca8a613de86e925c9f2bc6f
F test/nulls1.test 82c5bc33148405f21205865abf13c786084438d573a4ac4e87e11b6091cde526
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
@@ -1265,7 +1278,7 @@ F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 5ec98f5aa6840c42800fa138bc19adbd33ea455be815dbf52b7d100b515f8ecc
+F test/permutations.test b90dad2da1a6e7884a02fba1146beaa5f21c69e3a523697595751afa8164cbb9
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 50b91bedea9324d3ab48e793f908ee7d2c7dcf84bfa2281e792838be59641ec8
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@@ -1294,9 +1307,10 @@ F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
F test/releasetest.tcl fb76d8fcc95ac29d6356cd9e52b726ab9e43a24082897618dfbcb7c2b0049153 x
-F test/releasetest_data.tcl b9cb30360759b80d92d4ea86b84ebfd8035b97f9078a482deb3cf9d0b2442655
+F test/releasetest_data.tcl d60628b6a04891e97cc4f5fbce08abe13c5b74cb23dcca5ac24800471a3bc6db
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
+F test/returning1.test 684e1c73d961422a7376c932fcdd6dacf02bad21d12f749cfe8c19991ef379f6
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
@@ -1304,7 +1318,7 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test bfbd7b97d9267660be3c8f28507c4ed7f205196b8877c0db42df347c2e8845e3
-F test/rowvalue.test 8964f95b253d3b5cc8dc1cfd0cdb7529bce3ecc6b6259e23c5f829f80f4d51cd
+F test/rowvalue.test b5436c767394a3f8fa5e7e474b2114ba430fdab34a5c8573c1b6256756534565
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
F test/rowvalue4.test 02e35f7762371c2f57ebd856aa056eac56cb27ef7715a0bb31eac1895a745356
@@ -1334,7 +1348,7 @@ F test/schema6.test e4bd1f23d368695eb9e7b51ef6e02ca0642ea2ab4a52579959826b5e7dce
F test/schemafault.test 1936bceca55ac82c5efbcc9fc91a1933e45c8d1e1d106b9a7e56c972a5a2a51e
F test/securedel.test 2f70b2449186a1921bd01ec9da407fbfa98c3a7a5521854c300c194b2ff09384
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
-F test/select1.test 0ed936740777f52858b6607f39ffac4b2b63b8fc7edf3ab2ebad3c3553ceecee
+F test/select1.test 3d23f66bf9ba77570acfe2ca5f1540ece17037cc64ab1a00efec9758ac29c268
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test c49fbb758903f3718e2de5aa4655eda4838131cbea24a86db908f8b6889aa68c
F test/select4.test e8a2502e3623f3058871030599a48abb35789d2244d5b380ecf3696873fdd4a4
@@ -1345,7 +1359,7 @@ F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
F test/selectA.test 68de52409e45a3313d00b8461b48bef4fb729faf36ade9067a994eae55cc86f4
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
-F test/selectC.test e25243f8ca503e06f252eb0218976d07cfeceac3
+F test/selectC.test fec14c9015ed4ec941508bbc144f30b42e40ac34a4bb33001450369865dd0b75
F test/selectD.test fc20452847a01775710090383cfb4423275d2f745fed61f34fbf37573ac0d214
F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
@@ -1366,10 +1380,10 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
-F test/shell1.test 0ba53c72545de142e1b76fa793ee33293134aa02abb9b50b35398670481ea661
+F test/shell1.test 56a7358a2a05e850e9e4aa24629db9c8975e8038dbe8debd2d95be22a5f03612
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
-F test/shell4.test 1c6aef11daaa2d6830acaba3ac9cbec93fbc1c3d5530743a637f39b3987d08ce
+F test/shell4.test 3ed6c4b42fd695efcbc25d69ef759dbb15855ca8e52ba6c5ee076f8b435f48be
F test/shell5.test 84a30b55722a95a5b72989e691c469a999ca7591e7aa00b7fabc783ea5c9a6fe
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
@@ -1407,7 +1421,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
-F test/speedtest1.c 849dbcb0bded9f966e43f28e8ce824b2915cb5dd0031a2e85996f7e3de36c2b1
+F test/speedtest1.c 5e5b805f24cc939656058f6a498f5a2160f9142e4815c54faf758ec798d4cdad
F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
@@ -1415,6 +1429,7 @@ F test/spellfix4.test 51c7c26514ade169855c66bcf130bd5acfb4d7fd090cc624645ab275ae
F test/sqldiff1.test 28cd737cf1b0078b1ec1bbf425e674c47785835e
F test/sqllimits1.test 264f4b0f941800ba139d25e33ee919c5d95fea06dfbe8ac291d6811a30984ca5
F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a
+F test/startup.c 1beb5ca66fcc0fce95c3444db9d1674f90fc605499a574ae2434dcfc10d22805
F test/stat.test 15a3106eddedfc882f64bc09f237b4169be4b92dd57c93031b8ff8b13af3e7c5
F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75
@@ -1448,7 +1463,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
-F test/tester.tcl 658fbf51736392fad7e98ba309929d61c22a8ff06c4f1a7ca9f079ed99e5eff7
+F test/tester.tcl f817f08ca4f65c5725d13340061f6f78a12162da9d19d503e098c55435778a56
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1609,7 +1624,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/tpch01.test 7c4eb8cdd79c568f46d344b3e789c9fdb8a766d112871352704861f3fca32a2a
F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
-F test/trace3.test 1dff966888773ff1bfea01c080caf15417892b3f998408fe920c4791f7337144
+F test/trace3.test ae2004df24b585fed9046cc0bae4601762bc6fc4aa321d475f1350bba5047f31
F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f439
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
@@ -1627,7 +1642,7 @@ F test/triggerA.test 837be862d8721f903dba3f3ceff05b32e0bee5214cf6ea3da5fadf12d36
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 29f5a28d0fe39e6e2c01f6e1f53f08c0955170ae10a63ad023e33cb0a1682a51
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
-F test/triggerE.test ede2e4bce4ba802337bd69d39447fa04a938e06d84a8bfc53c76850fc36ed86d
+F test/triggerE.test 612969cb57a4ef792059ad6d01af0117e1ae862c283753ffcc9a6428642b22ee
F test/triggerF.test 5d76f0a8c428ff87a4d5ed52da06f6096a2c787a1e21b846111dfac4123de3ad
F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505233e9
F test/triggerupfrom.test d25961fa70a99b6736193da7b49a36d8c1d28d56188f0be6406d4366315cd6e4
@@ -1642,6 +1657,9 @@ F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
+F test/unionall.test 369dac51f4e7b94442b054d3d7f2e6755cd6994274718228878e3bd47c425f6d
+F test/unionall2.test c9a62db63350bcbce3a7bec50dd8c5410f08be33f8af435473756286d4657215
+F test/unionallfault.test 652bfbb630e6c43135965dc1e8f0a9a791da83aec885d626a632fe1909c56f73
F test/unionvtab.test e1704ab1b4c1bb3ffc9da4681f8e85a0b909fd80b937984fc94b27415ac8e5a4
F test/unionvtabfault.test e8759f3d14fb938ce9657e2342db34aeac0fb9bc1692b0d1ebb0069630151d06
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
@@ -1659,6 +1677,7 @@ F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
+F test/upsert5.test f49faf5f15b5c3641c6f5d7c7cc531ef5ac997567b2b6bb7bc96f7c88753ca0b
F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35
F test/uri.test a2becabcb9fe25d08d1ae49c0788f4a75dda97bfe4c8641c2d04e224faf7a6e2
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
@@ -1666,10 +1685,11 @@ F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum-into.test 48f4cec354fb6f27c98ef58d2fe49a11b71ff131af0cd9140efacc9858b9f670
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
-F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b
+F test/vacuum2.test 9fd45ce6ce29f5614c249e03938d3567c06a9e772d4f155949f8eafe2d8af520
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
F test/vacuum4.test 7ea76b769fffeb41f925303b04cbcf5a5bbeabe55e4c60ae754ff24eeeb7c010
F test/vacuum5.test 263b144d537e92ad8e9ca8a73cc6e1583f41cfd0dda9432b87f7806174a2f48c
+F test/vacuum6.test d3173a54edc81d13d99e4cf4972232b3cbb52f1d56ed48c3a939ef4e751c1ee8
F test/vacuummem.test 7b42abb3208bd82dd23a7536588396f295a314f2
F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
@@ -1742,7 +1762,7 @@ F test/walsetlk.test 11f7fe792fdce54cf09874dab824e0627f2eedecfb9f7983e325606ec51
F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
-F test/walvfs.test a2913001a83b19c1d20220e556cee14d87d47ecb6949b5e0a2e9e2590abecf1e
+F test/walvfs.test bccb3e0d235ef85e276f491d34db32c9ada1ea67be8d9f10aabe7b30319ec656
F test/wapp.tcl b440cd8cf57953d3a49e7ee81e6a18f18efdaf113b69f7d8482b0710a64566ec
F test/wapptest.tcl 899594e25684861d5b0c0880fb012364def50ef8097041b8ddf74be5ba7fa270 x
F test/where.test e713c0c64e3e6b062235e39a2f7e5508c517df16b63d69fd786e26bc7330b1c6
@@ -1765,10 +1785,10 @@ F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
F test/whereI.test a2874062140ed4aba9ffae76e6190a3df6fc73d1373fdfa8fd632945082a5364
F test/whereJ.test 88287550f6ee604422403b053455b1ad894eeaa5c35d348532dfa1439286cb9a
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
-F test/whereL.test e05cedc9389c6f09ad55bd5999a3fddccebec90672fb989433c145dcdaf26996
+F test/whereL.test 1afe47227f093dc0547236491fb37529b7be9724b8575925a321001b80e6a23a
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
-F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
+F test/wherelimit.test daa0fd9122c5745cc459ec40b8d3c16ce13ce8382b5b847e7cfff4b871260cbf
F test/wherelimit2.test 657a3f24aadee62d058c5091ea682dc4af4b95ffe32f137155be49799a58e721
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
@@ -1792,12 +1812,14 @@ F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af
F test/windowB.test 7a983ea1cc1cf72be7f378e4b32f6cb2d73014c5cd8b25aaee825164cd4269e5
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
-F test/windowfault.test 72375ae71031eabf96bc88d0af128c8628a091ddc99b5a394e848b3df5fc17ad
+F test/windowfault.test d543d46571b32d19f198cb04b6505747fabf3cc369970daae47074ee793612be
+F test/windowpushd.test 5b9c114e8173c3addacf58a0fcd941437b14649f2033700184479a13f188ad00
F test/with1.test 780be387f01e290e768bdfd1827280f9e37ba37223eb4736aba386864fac5a94
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
-F test/with3.test a261f0ea225c4af0ce6447f1157bb603959b2a665f14a03951c2883d2ef1c0f0
+F test/with3.test 85e059bf4c2ef5626411ee59f399b4bb4b4a0f009bcb7db86f254e570ed11831
F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205
F test/with5.test 6248213c41fab36290b5b73aa3f937309dfba337004d9d8434c3fabc8c7d4be8
+F test/with6.test 3001b59179cbdc26a8c67ff8f46944e3141fdece9ab064c49bbf08459b67b207
F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
F test/without_rowid1.test e4034c0849ccc2e8bb749c69f15bd69bb9fcf8fe77e8d17ce02369604242fe83
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
@@ -1834,8 +1856,8 @@ F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
-F tool/lemon.c 70eedc31614a58fe31a71025c17ebd1502a6ce9cfef0ed5e33acb0b5b737b291
-F tool/lempar.c 0e1d5eeb9736108d3dba782a9dd56f4e7cb69006b6ee181308b7ebfb15313a12
+F tool/lemon.c d44ba4f03427c9bd34b601f315fe77c2b6d4bd215801a0259aeedbcc4c94a95c
+F tool/lempar.c 1d3d075da18681c67ecc66c1f171e7094e18cd2cfba6a8a1bd4f3f639d6656e1
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
@@ -1843,7 +1865,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
-F tool/mkkeywordhash.c 11a3f3af8e787d0c5ca459ed66fe80fd09e661876506e7b978ec08c19477bdc2
+F tool/mkkeywordhash.c 08b6e4d7a482a7f37a9a0032e7ba968e26624a027b6b2e9ba589be6f5e3d8c2c
F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
@@ -1917,7 +1939,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P ebefcb8607c91d19ee68935b01fc9c237af41df8b866a9372f7940d89ba44c9c cb1f6f18e74aeff522c332cb6c8f4c202bba8fba4ff7869198e4501ffa4a0387
-R 59ebeda1c8fa888639c3b8895a8adf30
-U dan
-Z 7cc91863c865316e090ee96a83f0661e
+P b451bb724ea73367a27c51fcf6e48d02d774c7c5b5b8ec0bd3726a6e4689e591 0ae81039083dbeb67cd4da9f56cdcb31fd747ba258179a46781a12b2872e9f49
+R bfe949c18345fcf6b47115103fc06a97
+U drh
+Z e373e1ae3ae89d41efdb6f027cead9d9
diff --git a/manifest.uuid b/manifest.uuid
index fe0ffbc1ce..2c4723f8e8 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-b451bb724ea73367a27c51fcf6e48d02d774c7c5b5b8ec0bd3726a6e4689e591
\ No newline at end of file
+b0fc8ca14ec8ed3a42bf647b112d5fba640f2adc948485da795fc802b74b3106
\ No newline at end of file
diff --git a/src/alter.c b/src/alter.c
index f4098863dd..5409de594f 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -49,15 +49,21 @@ static int isAlterableTable(Parse *pParse, Table *pTab){
** statement to ensure that the operation has not rendered any schema
** objects unusable.
*/
-static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
+static void renameTestSchema(
+ Parse *pParse, /* Parse context */
+ const char *zDb, /* Name of db to verify schema of */
+ int bTemp, /* True if this is the temp db */
+ const char *zWhen /* "when" part of error message */
+){
+ pParse->colNamesSet = 1;
sqlite3NestedParse(pParse,
"SELECT 1 "
"FROM \"%w\"." DFLT_SCHEMA_TABLE " "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
+ " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ",
zDb,
- zDb, bTemp
+ zDb, bTemp, zWhen
);
if( bTemp==0 ){
@@ -66,8 +72,8 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
"FROM temp." DFLT_SCHEMA_TABLE " "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
- zDb
+ " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ",
+ zDb, zWhen
);
}
}
@@ -76,12 +82,12 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
** Generate code to reload the schema for database iDb. And, if iDb!=1, for
** the temp database as well.
*/
-static void renameReloadSchema(Parse *pParse, int iDb){
+static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){
Vdbe *v = pParse->pVdbe;
if( v ){
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
- if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
+ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5);
+ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5);
}
}
@@ -230,7 +236,7 @@ void sqlite3AlterRenameTable(
"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) "
+ " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') "
"THEN %Q ELSE tbl_name END "
"WHERE type IN ('view', 'trigger')"
, zDb, zTabName, zName, zTabName, zDb, zName);
@@ -249,8 +255,8 @@ void sqlite3AlterRenameTable(
}
#endif
- renameReloadSchema(pParse, iDb);
- renameTestSchema(pParse, zDb, iDb==1);
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
+ renameTestSchema(pParse, zDb, iDb==1, "after rename");
exit_rename_table:
sqlite3SrcListDelete(db, pSrc);
@@ -381,11 +387,14 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
*zEnd-- = '\0';
}
db->mDbFlags |= DBFLAG_PreferBuiltin;
+ /* substr() operations on characters, but addColOffset is in bytes. So we
+ ** have to use printf() to translate between these units: */
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
- "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
+ "sql = printf('%%.%ds, ',sql) || %Q"
+ " || substr(sql,1+length(printf('%%.%ds',sql))) "
"WHERE type = 'table' AND name = %Q",
- zDb, pNew->addColOffset, zCol, pNew->addColOffset+1,
+ zDb, pNew->addColOffset, zCol, pNew->addColOffset,
zTab
);
sqlite3DbFree(db, zCol);
@@ -409,7 +418,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
}
/* Reload the table definition */
- renameReloadSchema(pParse, iDb);
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
}
/*
@@ -509,7 +518,7 @@ exit_begin_add_column:
** 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){
+static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
const char *zType = 0;
#ifndef SQLITE_OMIT_VIEW
if( pTab->pSelect ){
@@ -522,15 +531,16 @@ static int isRealTable(Parse *pParse, Table *pTab){
}
#endif
if( zType ){
- sqlite3ErrorMsg(
- pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName
+ sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
+ (bDrop ? "drop column from" : "rename columns of"),
+ zType, pTab->zName
);
return 1;
}
return 0;
}
#else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
-# define isRealTable(x,y) (0)
+# define isRealTable(x,y,z) (0)
#endif
/*
@@ -559,7 +569,7 @@ void sqlite3AlterRenameColumn(
/* Cannot alter a system table */
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column;
- if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column;
+ if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column;
/* Which schema holds the table to be altered */
iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -613,8 +623,8 @@ void sqlite3AlterRenameColumn(
);
/* Drop and reload the database schema. */
- renameReloadSchema(pParse, iSchema);
- renameTestSchema(pParse, zDb, iSchema==1);
+ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
+ renameTestSchema(pParse, zDb, iSchema==1, "after rename");
exit_rename_column:
sqlite3SrcListDelete(db, pSrc);
@@ -866,23 +876,33 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
/*
** 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.
+** object associated with parse tree element pPtr. If found, return a pointer
+** to it. Otherwise, return NULL.
+**
+** If the second argument passed to this function is not NULL and a matching
+** RenameToken object is found, remove it from the Parse object and add it to
+** the list maintained by the RenameCtx object.
*/
-static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
+static RenameToken *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;
+ if( pCtx ){
+ *pp = pToken->pNext;
+ pToken->pNext = pCtx->pList;
+ pCtx->pList = pToken;
+ pCtx->nList++;
+ }
+ return pToken;
}
}
+ return 0;
}
/*
@@ -953,7 +973,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
*/
static void renameColumnParseError(
sqlite3_context *pCtx,
- int bPost,
+ const char *zWhen,
sqlite3_value *pType,
sqlite3_value *pObject,
Parse *pParse
@@ -962,8 +982,8 @@ static void renameColumnParseError(
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" : ""),
+ zErr = sqlite3_mprintf("error in %s %s%s%s: %s",
+ zT, zN, (zWhen[0] ? " " : ""), zWhen,
pParse->zErrMsg
);
sqlite3_result_error(pCtx, zErr, -1);
@@ -1042,7 +1062,7 @@ static int renameParseSql(
p->eParseMode = PARSE_MODE_RENAME;
p->db = db;
p->nQueryLoop = 1;
- rc = sqlite3RunParser(p, zSql, &zErr);
+ rc = zSql ? sqlite3RunParser(p, zSql, &zErr) : SQLITE_NOMEM;
assert( p->zErrMsg==0 );
assert( rc!=SQLITE_OK || zErr==0 );
p->zErrMsg = zErr;
@@ -1195,7 +1215,7 @@ static int renameResolveTrigger(Parse *pParse){
if( pSrc ){
int i;
for(i=0; inSrc && rc==SQLITE_OK; i++){
- struct SrcList_item *p = &pSrc->a[i];
+ SrcItem *p = &pSrc->a[i];
p->iCursor = pParse->nTab++;
if( p->pSelect ){
sqlite3SelectPrep(pParse, p->pSelect, 0);
@@ -1469,7 +1489,7 @@ static void renameColumnFunc(
renameColumnFunc_done:
if( rc!=SQLITE_OK ){
if( sParse.zErrMsg ){
- renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
+ renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
@@ -1507,7 +1527,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
return WRC_Abort;
}
for(i=0; inSrc; i++){
- struct SrcList_item *pItem = &pSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i];
if( pItem->pTab==p->pTab ){
renameTokenFind(pWalker->pParse, p, pItem->zName);
}
@@ -1658,7 +1678,7 @@ static void renameTableFunc(
}
if( rc!=SQLITE_OK ){
if( sParse.zErrMsg ){
- renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
+ renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
@@ -1687,6 +1707,7 @@ static void renameTableFunc(
** 2: Object type ("view", "table", "trigger" or "index").
** 3: Object name.
** 4: True if object is from temp schema.
+** 5: "when" part of error message.
**
** Unless it finds an error, this function normally returns NULL. However, it
** returns integer value 1 if:
@@ -1704,6 +1725,7 @@ static void renameTableTest(
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
int bTemp = sqlite3_value_int(argv[4]);
int isLegacy = (db->flags & SQLITE_LegacyAlter);
+ char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
@@ -1736,8 +1758,8 @@ static void renameTableTest(
}
}
- if( rc!=SQLITE_OK ){
- renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
+ if( rc!=SQLITE_OK && zWhen ){
+ renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse);
}
renameParseCleanup(&sParse);
}
@@ -1747,14 +1769,205 @@ static void renameTableTest(
#endif
}
+/*
+** The implementation of internal UDF sqlite_drop_column().
+**
+** Arguments:
+**
+** argv[0]: An integer - the index of the schema containing the table
+** argv[1]: CREATE TABLE statement to modify.
+** argv[2]: An integer - the index of the column to remove.
+**
+** The value returned is a string containing the CREATE TABLE statement
+** with column argv[2] removed.
+*/
+static void dropColumnFunc(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ int iSchema = sqlite3_value_int(argv[0]);
+ const char *zSql = (const char*)sqlite3_value_text(argv[1]);
+ int iCol = sqlite3_value_int(argv[2]);
+ const char *zDb = db->aDb[iSchema].zDbSName;
+ int rc;
+ Parse sParse;
+ RenameToken *pCol;
+ Table *pTab;
+ const char *zEnd;
+ char *zNew = 0;
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+
+ rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
+ if( rc!=SQLITE_OK ) goto drop_column_done;
+ pTab = sParse.pNewTable;
+ if( pTab->nCol==1 || iCol>=pTab->nCol ){
+ /* This can happen if the sqlite_schema table is corrupt */
+ rc = SQLITE_CORRUPT_BKPT;
+ goto drop_column_done;
+ }
+
+ pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zName);
+ if( iColnCol-1 ){
+ RenameToken *pEnd;
+ pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zName);
+ zEnd = (const char*)pEnd->t.z;
+ }else{
+ zEnd = (const char*)&zSql[pTab->addColOffset];
+ while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
+ }
+
+ zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd);
+ sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT);
+ sqlite3_free(zNew);
+
+drop_column_done:
+ renameParseCleanup(&sParse);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(context, rc);
+ }
+}
+
+/*
+** This function is called by the parser upon parsing an
+**
+** ALTER TABLE pSrc DROP COLUMN pName
+**
+** statement. Argument pSrc contains the possibly qualified name of the
+** table being edited, and token pName the name of the column to drop.
+*/
+void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){
+ sqlite3 *db = pParse->db; /* Database handle */
+ Table *pTab; /* Table to modify */
+ int iDb; /* Index of db containing pTab in aDb[] */
+ const char *zDb; /* Database containing pTab ("main" etc.) */
+ char *zCol = 0; /* Name of column to drop */
+ int iCol; /* Index of column zCol in pTab->aCol[] */
+
+ /* Look up the table being altered. */
+ assert( pParse->pNewTable==0 );
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ if( NEVER(db->mallocFailed) ) goto exit_drop_column;
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
+ if( !pTab ) goto exit_drop_column;
+
+ /* Make sure this is not an attempt to ALTER a view, virtual table or
+ ** system table. */
+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column;
+ if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column;
+
+ /* Find the index of the column being dropped. */
+ zCol = sqlite3NameFromToken(db, pName);
+ if( zCol==0 ){
+ assert( db->mallocFailed );
+ goto exit_drop_column;
+ }
+ iCol = sqlite3ColumnIndex(pTab, zCol);
+ if( iCol<0 ){
+ sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zCol);
+ goto exit_drop_column;
+ }
+
+ /* Do not allow the user to drop a PRIMARY KEY column or a column
+ ** constrained by a UNIQUE constraint. */
+ if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){
+ sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"",
+ (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE",
+ zCol
+ );
+ goto exit_drop_column;
+ }
+
+ /* Do not allow the number of columns to go to zero */
+ if( pTab->nCol<=1 ){
+ sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol);
+ goto exit_drop_column;
+ }
+
+ /* Edit the sqlite_schema table */
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( iDb>=0 );
+ zDb = db->aDb[iDb].zDbSName;
+ renameTestSchema(pParse, zDb, iDb==1, "");
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
+ "sql = sqlite_drop_column(%d, sql, %d) "
+ "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
+ , zDb, iDb, iCol, pTab->zName
+ );
+
+ /* Drop and reload the database schema. */
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
+ renameTestSchema(pParse, zDb, iDb==1, "after drop column");
+
+ /* Edit rows of table on disk */
+ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
+ int i;
+ int addr;
+ int reg;
+ int regRec;
+ Index *pPk = 0;
+ int nField = 0; /* Number of non-virtual columns after drop */
+ int iCur;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ iCur = pParse->nTab++;
+ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ reg = ++pParse->nMem;
+ pParse->nMem += pTab->nCol;
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg);
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ }
+ for(i=0; inCol; i++){
+ if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
+ int regOut;
+ if( pPk ){
+ int iPos = sqlite3TableColumnToIndex(pPk, i);
+ int iColPos = sqlite3TableColumnToIndex(pPk, iCol);
+ regOut = reg+1+iPos-(iPos>iColPos);
+ }else{
+ regOut = reg+1+nField;
+ }
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
+ nField++;
+ }
+ }
+ regRec = reg + pTab->nCol;
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec);
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg);
+ }
+
+ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr);
+ }
+
+exit_drop_column:
+ sqlite3DbFree(db, zCol);
+ sqlite3SrcListDelete(db, pSrc);
+}
+
/*
** Register built-in functions used to help implement ALTER TABLE
*/
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),
+ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
+ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
+ INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest),
+ INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
diff --git a/src/attach.c b/src/attach.c
index 3b5c57f0cc..8eb4486e58 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -433,6 +433,62 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
}
#endif /* SQLITE_OMIT_ATTACH */
+/*
+** Expression callback used by sqlite3FixAAAA() routines.
+*/
+static int fixExprCb(Walker *p, Expr *pExpr){
+ DbFixer *pFix = p->u.pFix;
+ if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
+ if( pExpr->op==TK_VARIABLE ){
+ if( pFix->pParse->db->init.busy ){
+ pExpr->op = TK_NULL;
+ }else{
+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
+ return WRC_Abort;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Select callback used by sqlite3FixAAAA() routines.
+*/
+static int fixSelectCb(Walker *p, Select *pSelect){
+ DbFixer *pFix = p->u.pFix;
+ int i;
+ SrcItem *pItem;
+ sqlite3 *db = pFix->pParse->db;
+ int iDb = sqlite3FindDbName(db, pFix->zDb);
+ SrcList *pList = pSelect->pSrc;
+
+ if( NEVER(pList==0) ) return WRC_Continue;
+ for(i=0, pItem=pList->a; inSrc; i++, pItem++){
+ if( pFix->bTemp==0 ){
+ if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return WRC_Abort;
+ }
+ sqlite3DbFree(db, pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->pSchema = pFix->pSchema;
+ pItem->fg.fromDDL = 1;
+ }
+#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
+ if( sqlite3WalkExpr(&pFix->w, pList->a[i].pOn) ) return WRC_Abort;
+#endif
+ }
+ if( pSelect->pWith ){
+ for(i=0; ipWith->nCte; i++){
+ if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){
+ return WRC_Abort;
+ }
+ }
+ }
+ return WRC_Continue;
+}
+
/*
** Initialize a DbFixer structure. This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
@@ -444,9 +500,7 @@ void sqlite3FixInit(
const char *zType, /* "view", "trigger", or "index" */
const Token *pName /* Name of the view, trigger, or index */
){
- sqlite3 *db;
-
- db = pParse->db;
+ sqlite3 *db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
pFix->zDb = db->aDb[iDb].zDbSName;
@@ -454,6 +508,13 @@ void sqlite3FixInit(
pFix->zType = zType;
pFix->pName = pName;
pFix->bTemp = (iDb==1);
+ pFix->w.pParse = pParse;
+ pFix->w.xExprCallback = fixExprCb;
+ pFix->w.xSelectCallback = fixSelectCb;
+ pFix->w.xSelectCallback2 = 0;
+ pFix->w.walkerDepth = 0;
+ pFix->w.eCode = 0;
+ pFix->w.u.pFix = pFix;
}
/*
@@ -474,115 +535,27 @@ int sqlite3FixSrcList(
DbFixer *pFix, /* Context of the fixation */
SrcList *pList /* The Source list to check and modify */
){
- int i;
- struct SrcList_item *pItem;
- sqlite3 *db = pFix->pParse->db;
- int iDb = sqlite3FindDbName(db, pFix->zDb);
-
- if( NEVER(pList==0) ) return 0;
-
- for(i=0, pItem=pList->a; inSrc; i++, pItem++){
- if( pFix->bTemp==0 ){
- if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
- sqlite3ErrorMsg(pFix->pParse,
- "%s %T cannot reference objects in database %s",
- pFix->zType, pFix->pName, pItem->zDatabase);
- return 1;
- }
- sqlite3DbFree(db, pItem->zDatabase);
- pItem->zDatabase = 0;
- pItem->pSchema = pFix->pSchema;
- pItem->fg.fromDDL = 1;
- }
-#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
- if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
- if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
-#endif
- if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
- return 1;
- }
+ int res = 0;
+ if( pList ){
+ Select s;
+ memset(&s, 0, sizeof(s));
+ s.pSrc = pList;
+ res = sqlite3WalkSelect(&pFix->w, &s);
}
- return 0;
+ return res;
}
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
int sqlite3FixSelect(
DbFixer *pFix, /* Context of the fixation */
Select *pSelect /* The SELECT statement to be fixed to one database */
){
- while( pSelect ){
- if( sqlite3FixExprList(pFix, pSelect->pEList) ){
- return 1;
- }
- if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
- return 1;
- }
- if( pSelect->pWith ){
- int i;
- for(i=0; ipWith->nCte; i++){
- if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){
- return 1;
- }
- }
- }
- pSelect = pSelect->pPrior;
- }
- return 0;
+ return sqlite3WalkSelect(&pFix->w, pSelect);
}
int sqlite3FixExpr(
DbFixer *pFix, /* Context of the fixation */
Expr *pExpr /* The expression to be fixed to one database */
){
- while( pExpr ){
- if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
- if( pExpr->op==TK_VARIABLE ){
- if( pFix->pParse->db->init.busy ){
- pExpr->op = TK_NULL;
- }else{
- sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
- return 1;
- }
- }
- if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
- }else{
- if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
- }
- if( sqlite3FixExpr(pFix, pExpr->pRight) ){
- return 1;
- }
- pExpr = pExpr->pLeft;
- }
- return 0;
-}
-int sqlite3FixExprList(
- DbFixer *pFix, /* Context of the fixation */
- ExprList *pList /* The expression to be fixed to one database */
-){
- int i;
- struct ExprList_item *pItem;
- if( pList==0 ) return 0;
- for(i=0, pItem=pList->a; inExpr; i++, pItem++){
- if( sqlite3FixExpr(pFix, pItem->pExpr) ){
- return 1;
- }
- }
- return 0;
+ return sqlite3WalkExpr(&pFix->w, pExpr);
}
#endif
@@ -592,25 +565,20 @@ int sqlite3FixTriggerStep(
TriggerStep *pStep /* The trigger step be fixed to one database */
){
while( pStep ){
- if( sqlite3FixSelect(pFix, pStep->pSelect) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pStep->pWhere) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pStep->pExprList) ){
- return 1;
- }
- if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){
+ if( sqlite3WalkSelect(&pFix->w, pStep->pSelect)
+ || sqlite3WalkExpr(&pFix->w, pStep->pWhere)
+ || sqlite3WalkExprList(&pFix->w, pStep->pExprList)
+ || sqlite3FixSrcList(pFix, pStep->pFrom)
+ ){
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)
+ if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget)
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere)
+ || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet)
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere)
){
return 1;
}
@@ -618,6 +586,7 @@ int sqlite3FixTriggerStep(
#endif
pStep = pStep->pNext;
}
+
return 0;
}
#endif
diff --git a/src/auth.c b/src/auth.c
index 40673d5ea4..33420f5839 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -143,7 +143,6 @@ void sqlite3AuthRead(
Schema *pSchema, /* The schema of the expression */
SrcList *pTabList /* All table that pExpr might refer to */
){
- sqlite3 *db = pParse->db;
Table *pTab = 0; /* The table being read */
const char *zCol; /* Name of the column of the table */
int iSrc; /* Index in pTabList->a[] of table being read */
@@ -151,8 +150,8 @@ void sqlite3AuthRead(
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;
+ assert( !IN_RENAME_OBJECT );
+ assert( pParse->db->xAuth!=0 );
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
if( iDb<0 ){
/* An attempt to read a column out of a subquery or other
@@ -164,7 +163,7 @@ void sqlite3AuthRead(
pTab = pParse->pTriggerTab;
}else{
assert( pTabList );
- for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){
+ for(iSrc=0; iSrcnSrc; iSrc++){
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
pTab = pTabList->a[iSrc].pTab;
break;
@@ -172,7 +171,7 @@ void sqlite3AuthRead(
}
}
iCol = pExpr->iColumn;
- if( NEVER(pTab==0) ) return;
+ if( pTab==0 ) return;
if( iCol>=0 ){
assert( iColnCol );
@@ -183,7 +182,7 @@ void sqlite3AuthRead(
}else{
zCol = "ROWID";
}
- assert( iDb>=0 && iDbnDb );
+ assert( iDb>=0 && iDbdb->nDb );
if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){
pExpr->op = TK_NULL;
}
@@ -209,11 +208,7 @@ int sqlite3AuthCheck(
** or if the parser is being invoked from within sqlite3_declare_vtab.
*/
assert( !IN_RENAME_OBJECT || db->xAuth==0 );
- if( db->init.busy || IN_SPECIAL_PARSE ){
- return SQLITE_OK;
- }
-
- if( db->xAuth==0 ){
+ if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){
return SQLITE_OK;
}
diff --git a/src/btree.c b/src/btree.c
index 4c8ef12ef0..6e9dbcfc48 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -1383,6 +1383,24 @@ static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow(
pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4;
}
+/*
+** Given a record with nPayload bytes of payload stored within btree
+** page pPage, return the number of bytes of payload stored locally.
+*/
+static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
+ int maxLocal; /* Maximum amount of payload held locally */
+ maxLocal = pPage->maxLocal;
+ if( nPayload<=maxLocal ){
+ return nPayload;
+ }else{
+ int minLocal; /* Minimum amount of payload held locally */
+ int surplus; /* Overflow payload available for local storage */
+ minLocal = pPage->minLocal;
+ surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4);
+ return ( surplus <= maxLocal ) ? surplus : minLocal;
+ }
+}
+
/*
** The following routines are implementations of the MemPage.xParseCell()
** method.
@@ -3134,6 +3152,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pCursor );
+ if( nReserve>32 && pageSize==512 ) pageSize = 1024;
pBt->pageSize = (u32)pageSize;
freeTempSpace(pBt);
}
@@ -8085,7 +8104,9 @@ static int balance_nonroot(
}
pgno = get4byte(pRight);
while( 1 ){
- rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
+ }
if( rc ){
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
@@ -8125,12 +8146,10 @@ static int balance_nonroot(
if( pBt->btsFlags & BTS_FAST_SECURE ){
int iOff;
+ /* If the following if() condition is not true, the db is corrupted.
+ ** The call to dropCell() below will detect this. */
iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
- if( (iOff+szNew[i])>(int)pBt->usableSize ){
- rc = SQLITE_CORRUPT_BKPT;
- memset(apOld, 0, (i+1)*sizeof(MemPage*));
- goto balance_cleanup;
- }else{
+ if( (iOff+szNew[i])<=(int)pBt->usableSize ){
memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
}
@@ -8424,6 +8443,9 @@ static int balance_nonroot(
apOld[i] = 0;
rc = sqlite3PagerWrite(pNew->pDbPage);
nNew++;
+ if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
@@ -8460,7 +8482,7 @@ static int balance_nonroot(
aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
aPgFlags[i] = apNew[i]->pDbPage->flags;
for(j=0; jpKeyInfo==0 );
if( pCur->eState==CURSOR_FAULT ){
assert( pCur->skipNext!=SQLITE_OK );
@@ -9146,7 +9169,7 @@ int sqlite3BtreeInsert(
** keys with no associated data. If the cursor was opened expecting an
** intkey table, the caller should be inserting integer keys with a
** blob of associated data. */
- assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );
+ assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) );
/* Save the positions of any other cursors open on this table.
**
@@ -9256,7 +9279,7 @@ int sqlite3BtreeInsert(
|| CORRUPT_DB );
pPage = pCur->pPage;
- assert( pPage->intKey || pX->nKey>=0 );
+ assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) );
assert( pPage->leaf || !pPage->intKey );
if( pPage->nFree<0 ){
if( pCur->eState>CURSOR_INVALID ){
@@ -9273,7 +9296,21 @@ int sqlite3BtreeInsert(
assert( pPage->isInit );
newCell = pBt->pTmpSpace;
assert( newCell!=0 );
- rc = fillInCell(pPage, newCell, pX, &szNew);
+ if( flags & BTREE_PREFORMAT ){
+ rc = SQLITE_OK;
+ szNew = pBt->nPreformatSize;
+ if( szNew<4 ) szNew = 4;
+ if( ISAUTOVACUUM && szNew>pPage->maxLocal ){
+ CellInfo info;
+ pPage->xParseCell(pPage, newCell, &info);
+ if( info.nPayload!=info.nLocal ){
+ Pgno ovfl = get4byte(&newCell[szNew-4]);
+ ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
+ }
+ }
+ }else{
+ rc = fillInCell(pPage, newCell, pX, &szNew);
+ }
if( rc ) goto end_insert;
assert( szNew==pPage->xCellSize(pPage, newCell) );
assert( szNew <= MX_CELL_SIZE(pBt) );
@@ -9380,6 +9417,114 @@ end_insert:
return rc;
}
+/*
+** This function is used as part of copying the current row from cursor
+** pSrc into cursor pDest. If the cursors are open on intkey tables, then
+** parameter iKey is used as the rowid value when the record is copied
+** into pDest. Otherwise, the record is copied verbatim.
+**
+** This function does not actually write the new value to cursor pDest.
+** Instead, it creates and populates any required overflow pages and
+** writes the data for the new cell into the BtShared.pTmpSpace buffer
+** for the destination database. The size of the cell, in bytes, is left
+** in BtShared.nPreformatSize. The caller completes the insertion by
+** calling sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
+ int rc = SQLITE_OK;
+ BtShared *pBt = pDest->pBt;
+ u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
+ const u8 *aIn; /* Pointer to next input buffer */
+ u32 nIn; /* Size of input buffer aIn[] */
+ u32 nRem; /* Bytes of data still to copy */
+
+ getCellInfo(pSrc);
+ aOut += putVarint32(aOut, pSrc->info.nPayload);
+ if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
+ nIn = pSrc->info.nLocal;
+ aIn = pSrc->info.pPayload;
+ if( aIn+nIn>pSrc->pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ nRem = pSrc->info.nPayload;
+ if( nIn==nRem && nInpPage->maxLocal ){
+ memcpy(aOut, aIn, nIn);
+ pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
+ }else{
+ Pager *pSrcPager = pSrc->pBt->pPager;
+ u8 *pPgnoOut = 0;
+ Pgno ovflIn = 0;
+ DbPage *pPageIn = 0;
+ MemPage *pPageOut = 0;
+ u32 nOut; /* Size of output buffer aOut[] */
+
+ nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
+ pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
+ if( nOutinfo.nPayload ){
+ pPgnoOut = &aOut[nOut];
+ pBt->nPreformatSize += 4;
+ }
+
+ if( nRem>nIn ){
+ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
+ }
+
+ do {
+ nRem -= nOut;
+ do{
+ assert( nOut>0 );
+ if( nIn>0 ){
+ int nCopy = MIN(nOut, nIn);
+ memcpy(aOut, aIn, nCopy);
+ nOut -= nCopy;
+ nIn -= nCopy;
+ aOut += nCopy;
+ aIn += nCopy;
+ }
+ if( nOut>0 ){
+ sqlite3PagerUnref(pPageIn);
+ pPageIn = 0;
+ rc = sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY);
+ if( rc==SQLITE_OK ){
+ aIn = (const u8*)sqlite3PagerGetData(pPageIn);
+ ovflIn = get4byte(aIn);
+ aIn += 4;
+ nIn = pSrc->pBt->usableSize - 4;
+ }
+ }
+ }while( rc==SQLITE_OK && nOut>0 );
+
+ if( rc==SQLITE_OK && nRem>0 ){
+ Pgno pgnoNew;
+ MemPage *pNew = 0;
+ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
+ put4byte(pPgnoOut, pgnoNew);
+ if( ISAUTOVACUUM && pPageOut ){
+ ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
+ }
+ releasePage(pPageOut);
+ pPageOut = pNew;
+ if( pPageOut ){
+ pPgnoOut = pPageOut->aData;
+ put4byte(pPgnoOut, 0);
+ aOut = &pPgnoOut[4];
+ nOut = MIN(pBt->usableSize - 4, nRem);
+ }
+ }
+ }while( nRem>0 && rc==SQLITE_OK );
+
+ releasePage(pPageOut);
+ sqlite3PagerUnref(pPageIn);
+ }
+
+ return rc;
+}
+
/*
** Delete the entry that the cursor is pointing to.
**
diff --git a/src/btree.h b/src/btree.h
index 0cd8a27473..bbed6364d8 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -262,6 +262,7 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
#define BTREE_APPEND 0x08 /* Insert is likely an append */
+#define BTREE_PREFORMAT 0x80 /* Insert is likely an append */
/* An instance of the BtreePayload object describes the content of a single
** entry in either an index or table btree.
@@ -363,6 +364,8 @@ void sqlite3BtreeCursorList(Btree*);
int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#endif
+int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64);
+
/*
** If we are not using shared cache, then there is no need to
** use mutexes to access the BtShared structures. So make the
diff --git a/src/btreeInt.h b/src/btreeInt.h
index 3309d32b3b..f7d7dfcbf1 100644
--- a/src/btreeInt.h
+++ b/src/btreeInt.h
@@ -462,6 +462,7 @@ struct BtShared {
#ifndef SQLITE_OMIT_CONCURRENT
BtreePtrmap *pMap;
#endif
+ int nPreformatSize; /* Size of last cell written by TransferRow() */
};
/*
diff --git a/src/build.c b/src/build.c
index d09bdb6939..91ed43a227 100644
--- a/src/build.c
+++ b/src/build.c
@@ -145,10 +145,36 @@ void sqlite3FinishCoding(Parse *pParse){
/* Begin by generating some termination code at the end of the
** vdbe program
*/
- v = sqlite3GetVdbe(pParse);
+ v = pParse->pVdbe;
+ if( v==0 ){
+ if( db->init.busy ){
+ pParse->rc = SQLITE_DONE;
+ return;
+ }
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) pParse->rc = SQLITE_ERROR;
+ }
assert( !pParse->isMultiWrite
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
if( v ){
+ if( pParse->bReturning ){
+ Returning *pReturning = pParse->u1.pReturning;
+ int addrRewind;
+ int i;
+ int reg;
+
+ addrRewind =
+ sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur);
+ VdbeCoverage(v);
+ reg = pReturning->iRetReg;
+ for(i=0; inRetCol; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i);
+ }
+ sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i);
+ sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrRewind);
+ }
sqlite3VdbeAddOp0(v, OP_Halt);
#if SQLITE_USER_AUTHENTICATION
@@ -226,12 +252,16 @@ void sqlite3FinishCoding(Parse *pParse){
}
}
+ if( pParse->bReturning ){
+ Returning *pRet = pParse->u1.pReturning;
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
+ }
+
/* Finally, jump back to the beginning of the executable code. */
sqlite3VdbeGoto(v, 1);
}
}
-
/* Get the VDBE program ready for execution
*/
if( v && pParse->nErr==0 && !db->mallocFailed ){
@@ -450,7 +480,7 @@ Table *sqlite3LocateTable(
Table *sqlite3LocateTableItem(
Parse *pParse,
u32 flags,
- struct SrcList_item *p
+ SrcItem *p
){
const char *zDb;
assert( p->pSchema==0 || p->zDatabase==0 );
@@ -1208,7 +1238,8 @@ void sqlite3StartTable(
}else
#endif
{
- pParse->addrCrTab =
+ assert( !pParse->bReturning );
+ pParse->u1.addrCrTab =
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
}
sqlite3OpenSchemaTable(pParse, iDb);
@@ -1235,12 +1266,84 @@ begin_table_error:
void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){
pCol->colFlags |= COLFLAG_HIDDEN;
+ if( pTab ) pTab->tabFlags |= TF_HasHidden;
}else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){
pTab->tabFlags |= TF_OOOHidden;
}
}
#endif
+/*
+** Name of the special TEMP trigger used to implement RETURNING. The
+** name begins with "sqlite_" so that it is guaranteed not to collide
+** with any application-generated triggers.
+*/
+#define RETURNING_TRIGGER_NAME "sqlite_returning"
+
+/*
+** Clean up the data structures associated with the RETURNING clause.
+*/
+static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
+ Hash *pHash;
+ pHash = &(db->aDb[1].pSchema->trigHash);
+ sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
+ sqlite3ExprListDelete(db, pRet->pReturnEL);
+ sqlite3DbFree(db, pRet);
+}
+
+/*
+** Add the RETURNING clause to the parse currently underway.
+**
+** This routine creates a special TEMP trigger that will fire for each row
+** of the DML statement. That TEMP trigger contains a single SELECT
+** statement with a result set that is the argument of the RETURNING clause.
+** The trigger has the Trigger.bReturning flag and an opcode of
+** TK_RETURNING instead of TK_SELECT, so that the trigger code generator
+** knows to handle it specially. The TEMP trigger is automatically
+** removed at the end of the parse.
+**
+** When this routine is called, we do not yet know if the RETURNING clause
+** is attached to a DELETE, INSERT, or UPDATE, so construct it as a
+** RETURNING trigger instead. It will then be converted into the appropriate
+** type on the first call to sqlite3TriggersExist().
+*/
+void sqlite3AddReturning(Parse *pParse, ExprList *pList){
+ Returning *pRet;
+ Hash *pHash;
+ sqlite3 *db = pParse->db;
+ if( pParse->pNewTrigger ){
+ sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger");
+ }else{
+ assert( pParse->bReturning==0 );
+ }
+ pParse->bReturning = 1;
+ pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
+ if( pRet==0 ){
+ sqlite3ExprListDelete(db, pList);
+ return;
+ }
+ pParse->u1.pReturning = pRet;
+ pRet->pParse = pParse;
+ pRet->pReturnEL = pList;
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
+ if( db->mallocFailed ) return;
+ pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
+ pRet->retTrig.op = TK_RETURNING;
+ pRet->retTrig.tr_tm = TRIGGER_AFTER;
+ pRet->retTrig.bReturning = 1;
+ pRet->retTrig.pSchema = db->aDb[1].pSchema;
+ pRet->retTrig.step_list = &pRet->retTStep;
+ pRet->retTStep.op = TK_RETURNING;
+ pRet->retTStep.pTrig = &pRet->retTrig;
+ pRet->retTStep.pExprList = pList;
+ pHash = &(db->aDb[1].pSchema->trigHash);
+ assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 || pParse->nErr );
+ if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
+ ==&pRet->retTrig ){
+ sqlite3OomFault(db);
+ }
+}
/*
** Add a new column to the table currently being constructed.
@@ -1257,6 +1360,8 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
+ u8 hName;
+
if( (p = pParse->pNewTable)==0 ) return;
if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
@@ -1268,8 +1373,9 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
memcpy(z, pName->z, pName->n);
z[pName->n] = 0;
sqlite3Dequote(z);
+ hName = sqlite3StrIHash(z);
for(i=0; inCol; i++){
- if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
+ if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zName)==0 ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
sqlite3DbFree(db, z);
return;
@@ -1287,7 +1393,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
pCol = &p->aCol[p->nCol];
memset(pCol, 0, sizeof(p->aCol[0]));
pCol->zName = z;
- pCol->hName = sqlite3StrIHash(z);
+ pCol->hName = hName;
sqlite3ColumnPropertiesFromName(p, pCol);
if( pType->n==0 ){
@@ -2070,9 +2176,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
/* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
** into BTREE_BLOBKEY.
*/
- if( pParse->addrCrTab ){
+ assert( !pParse->bReturning );
+ if( pParse->u1.addrCrTab ){
assert( v );
- sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY);
+ sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY);
}
/* Locate the PRIMARY KEY index. Or, if this table was originally
@@ -2536,7 +2643,7 @@ void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
+ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
}
/* Add the table to the in-memory representation of the database.
@@ -2553,20 +2660,17 @@ void sqlite3EndTable(
}
pParse->pNewTable = 0;
db->mDbFlags |= DBFLAG_SchemaChange;
+ }
#ifndef SQLITE_OMIT_ALTERTABLE
- if( !p->pSelect ){
- const char *zName = (const char *)pParse->sNameToken.z;
- int nName;
- assert( !pSelect && pCons && pEnd );
- if( pCons->z==0 ){
- pCons = pEnd;
- }
- nName = (int)((const char *)pCons->z - zName);
- p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName);
+ if( !pSelect && !p->pSelect ){
+ assert( pCons && pEnd );
+ if( pCons->z==0 ){
+ pCons = pEnd;
}
-#endif
+ p->addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z);
}
+#endif
}
#ifndef SQLITE_OMIT_VIEW
@@ -2757,6 +2861,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
assert( pTable->aCol==0 );
pTable->nCol = pSelTab->nCol;
pTable->aCol = pSelTab->aCol;
+ pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT);
pSelTab->nCol = 0;
pSelTab->aCol = 0;
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
@@ -4024,7 +4129,7 @@ void sqlite3CreateIndex(
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
+ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0);
sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
}
@@ -4417,7 +4522,7 @@ SrcList *sqlite3SrcListAppend(
Token *pTable, /* Table to append */
Token *pDatabase /* Database of the table */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
sqlite3 *db;
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
assert( pParse!=0 );
@@ -4458,7 +4563,7 @@ SrcList *sqlite3SrcListAppend(
*/
void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert(pList || pParse->db->mallocFailed );
if( pList ){
for(i=0, pItem=pList->a; inSrc; i++, pItem++){
@@ -4476,7 +4581,7 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
*/
void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
if( pList==0 ) return;
for(pItem=pList->a, i=0; inSrc; i++, pItem++){
if( pItem->zDatabase ) sqlite3DbFreeNN(db, pItem->zDatabase);
@@ -4518,7 +4623,7 @@ SrcList *sqlite3SrcListAppendFromTerm(
Expr *pOn, /* The ON clause of a join */
IdList *pUsing /* The USING clause of a join */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
sqlite3 *db = pParse->db;
if( !p && (pOn || pUsing) ){
sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
@@ -4562,7 +4667,7 @@ SrcList *sqlite3SrcListAppendFromTerm(
void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
assert( pIndexedBy!=0 );
if( p && pIndexedBy->n>0 ){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert( p->nSrc>0 );
pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
@@ -4592,7 +4697,7 @@ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
sqlite3SrcListDelete(pParse->db, p2);
}else{
p1 = pNew;
- memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item));
+ memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
sqlite3DbFree(pParse->db, p2);
}
}
@@ -4605,7 +4710,7 @@ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
*/
void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
if( p ){
- struct SrcList_item *pItem = &p->a[p->nSrc-1];
+ SrcItem *pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
assert( pItem->fg.isIndexedBy==0 );
assert( pItem->fg.isTabFunc==0 );
@@ -5102,24 +5207,76 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
}
#ifndef SQLITE_OMIT_CTE
+/*
+** Create a new CTE object
+*/
+Cte *sqlite3CteNew(
+ Parse *pParse, /* Parsing context */
+ Token *pName, /* Name of the common-table */
+ ExprList *pArglist, /* Optional column name list for the table */
+ Select *pQuery, /* Query used to initialize the table */
+ u8 eM10d /* The MATERIALIZED flag */
+){
+ Cte *pNew;
+ sqlite3 *db = pParse->db;
+
+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew));
+ assert( pNew!=0 || db->mallocFailed );
+
+ if( db->mallocFailed ){
+ sqlite3ExprListDelete(db, pArglist);
+ sqlite3SelectDelete(db, pQuery);
+ }else{
+ pNew->pSelect = pQuery;
+ pNew->pCols = pArglist;
+ pNew->zName = sqlite3NameFromToken(pParse->db, pName);
+ pNew->eM10d = eM10d;
+ }
+ return pNew;
+}
+
+/*
+** Clear information from a Cte object, but do not deallocate storage
+** for the object itself.
+*/
+static void cteClear(sqlite3 *db, Cte *pCte){
+ assert( pCte!=0 );
+ sqlite3ExprListDelete(db, pCte->pCols);
+ sqlite3SelectDelete(db, pCte->pSelect);
+ sqlite3DbFree(db, pCte->zName);
+}
+
+/*
+** Free the contents of the CTE object passed as the second argument.
+*/
+void sqlite3CteDelete(sqlite3 *db, Cte *pCte){
+ assert( pCte!=0 );
+ cteClear(db, pCte);
+ sqlite3DbFree(db, pCte);
+}
+
/*
** This routine is invoked once per CTE by the parser while parsing a
-** WITH clause.
+** WITH clause. The CTE described by teh third argument is added to
+** the WITH clause of the second argument. If the second argument is
+** NULL, then a new WITH argument is created.
*/
With *sqlite3WithAdd(
Parse *pParse, /* Parsing context */
With *pWith, /* Existing WITH clause, or NULL */
- Token *pName, /* Name of the common-table */
- ExprList *pArglist, /* Optional column name list for the table */
- Select *pQuery /* Query used to initialize the table */
+ Cte *pCte /* CTE to add to the WITH clause */
){
sqlite3 *db = pParse->db;
With *pNew;
char *zName;
+ if( pCte==0 ){
+ return pWith;
+ }
+
/* Check that the CTE name is unique within this WITH clause. If
** not, store an error in the Parse structure. */
- zName = sqlite3NameFromToken(pParse->db, pName);
+ zName = pCte->zName;
if( zName && pWith ){
int i;
for(i=0; inCte; i++){
@@ -5138,16 +5295,11 @@ With *sqlite3WithAdd(
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
if( db->mallocFailed ){
- sqlite3ExprListDelete(db, pArglist);
- sqlite3SelectDelete(db, pQuery);
- sqlite3DbFree(db, zName);
+ sqlite3CteDelete(db, pCte);
pNew = pWith;
}else{
- pNew->a[pNew->nCte].pSelect = pQuery;
- pNew->a[pNew->nCte].pCols = pArglist;
- pNew->a[pNew->nCte].zName = zName;
- pNew->a[pNew->nCte].zCteErr = 0;
- pNew->nCte++;
+ pNew->a[pNew->nCte++] = *pCte;
+ sqlite3DbFree(db, pCte);
}
return pNew;
@@ -5160,10 +5312,7 @@ void sqlite3WithDelete(sqlite3 *db, With *pWith){
if( pWith ){
int i;
for(i=0; inCte; i++){
- struct Cte *pCte = &pWith->a[i];
- sqlite3ExprListDelete(db, pCte->pCols);
- sqlite3SelectDelete(db, pCte->pSelect);
- sqlite3DbFree(db, pCte->zName);
+ cteClear(db, &pWith->a[i]);
}
sqlite3DbFree(db, pWith);
}
diff --git a/src/ctime.c b/src/ctime.c
index e3e1e79a95..7982f23aeb 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -259,6 +259,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_ENABLE_LOCKING_STYLE
"ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
#endif
+#if SQLITE_ENABLE_MATH_FUNCTIONS
+ "ENABLE_MATH_FUNCTIONS",
+#endif
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
"ENABLE_MEMORY_MANAGEMENT",
#endif
diff --git a/src/delete.c b/src/delete.c
index 064ae7325a..0c9c7bc8d0 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -29,7 +29,7 @@
**
*/
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
- struct SrcList_item *pItem = pSrc->a;
+ SrcItem *pItem = pSrc->a;
Table *pTab;
assert( pItem && pSrc->nSrc>=1 );
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
@@ -37,9 +37,9 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
pItem->pTab = pTab;
if( pTab ){
pTab->nTabRef++;
- }
- if( sqlite3IndexedByLookup(pParse, pItem) ){
- pTab = 0;
+ if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){
+ pTab = 0;
+ }
}
return pTab;
}
@@ -207,9 +207,15 @@ Expr *sqlite3LimitWhere(
/* 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);
+ pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);
pSrc->a[0].pTab = pTab;
- pSrc->a[0].pIBIndex = 0;
+ if( pSrc->a[0].fg.isIndexedBy ){
+ pSrc->a[0].u2.pIBIndex = 0;
+ pSrc->a[0].fg.isIndexedBy = 0;
+ sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy);
+ }else if( pSrc->a[0].fg.isCte ){
+ pSrc->a[0].u2.pCteUse->nUse++;
+ }
/* generate the SELECT expression tree. */
pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
@@ -387,6 +393,7 @@ void sqlite3DeleteFrom(
if( (db->flags & SQLITE_CountRows)!=0
&& !pParse->nested
&& !pParse->pTriggerTab
+ && !pParse->bReturning
){
memCnt = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
@@ -608,7 +615,7 @@ void sqlite3DeleteFrom(
** invoke the callback function.
*/
if( memCnt ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
+ sqlite3VdbeAddOp2(v, OP_ChngCntRow, memCnt, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
}
diff --git a/src/expr.c b/src/expr.c
index 685f041752..b66556b039 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -95,7 +95,18 @@ Expr *sqlite3ExprAddCollateToken(
const Token *pCollName, /* Name of collating sequence */
int dequote /* True to dequote pCollName */
){
- if( pCollName->n>0 ){
+ assert( pExpr!=0 || pParse->db->mallocFailed );
+ if( pExpr==0 ) return 0;
+ if( pExpr->op==TK_VECTOR ){
+ ExprList *pList = pExpr->x.pList;
+ if( ALWAYS(pList!=0) ){
+ int i;
+ for(i=0; inExpr; i++){
+ pList->a[i].pExpr = sqlite3ExprAddCollateToken(pParse,pList->a[i].pExpr,
+ pCollName, dequote);
+ }
+ }
+ }else if( pCollName->n>0 ){
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
if( pNew ){
pNew->pLeft = pExpr;
@@ -1519,8 +1530,8 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; inSrc; i++){
- struct SrcList_item *pNewItem = &pNew->a[i];
- struct SrcList_item *pOldItem = &p->a[i];
+ SrcItem *pNewItem = &pNew->a[i];
+ SrcItem *pOldItem = &p->a[i];
Table *pTab;
pNewItem->pSchema = pOldItem->pSchema;
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
@@ -1533,7 +1544,10 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}
- pNewItem->pIBIndex = pOldItem->pIBIndex;
+ pNewItem->u2 = pOldItem->u2;
+ if( pNewItem->fg.isCte ){
+ pNewItem->u2.pCteUse->nUse++;
+ }
if( pNewItem->fg.isTabFunc ){
pNewItem->u1.pFuncArg =
sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
@@ -5850,7 +5864,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
/* Check to see if the column is in one of the tables in the FROM
** clause of the aggregate query */
if( ALWAYS(pSrcList!=0) ){
- struct SrcList_item *pItem = pSrcList->a;
+ SrcItem *pItem = pSrcList->a;
for(i=0; inSrc; i++, pItem++){
struct AggInfo_col *pCol;
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
diff --git a/src/fkey.c b/src/fkey.c
index 959e994d17..9f622f40c6 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -1024,7 +1024,7 @@ void sqlite3FkCheck(
** child table as a SrcList for sqlite3WhereBegin() */
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( pSrc ){
- struct SrcList_item *pItem = pSrc->a;
+ SrcItem *pItem = pSrc->a;
pItem->pTab = pFKey->pFrom;
pItem->zName = pFKey->pFrom->zName;
pItem->pTab->nTabRef++;
@@ -1112,7 +1112,9 @@ u32 sqlite3FkOldmask(
**
** For an UPDATE, this function returns 2 if:
**
-** * There are any FKs for which pTab is the child and the parent table, or
+** * There are any FKs for which pTab is the child and the parent table
+** and any FK processing at all is required (even of a different FK), or
+**
** * the UPDATE modifies one or more parent keys for which the action is
** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL).
**
@@ -1124,13 +1126,14 @@ int sqlite3FkRequired(
int *aChange, /* Non-NULL for UPDATE operations */
int chngRowid /* True for UPDATE that affects rowid */
){
- int eRet = 0;
+ int eRet = 1; /* Value to return if bHaveFK is true */
+ int bHaveFK = 0; /* If FK processing is required */
if( pParse->db->flags&SQLITE_ForeignKeys ){
if( !aChange ){
/* A DELETE operation. Foreign key processing is required if the
** table in question is either the child or parent table for any
** foreign key constraint. */
- eRet = (sqlite3FkReferences(pTab) || pTab->pFKey);
+ bHaveFK = (sqlite3FkReferences(pTab) || pTab->pFKey);
}else{
/* This is an UPDATE. Foreign key processing is only required if the
** operation modifies one or more child or parent key columns. */
@@ -1138,9 +1141,9 @@ int sqlite3FkRequired(
/* Check if any child key columns are being modified. */
for(p=pTab->pFKey; p; p=p->pNextFrom){
- if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2;
if( fkChildIsModified(pTab, p, aChange, chngRowid) ){
- eRet = 1;
+ if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2;
+ bHaveFK = 1;
}
}
@@ -1148,12 +1151,12 @@ int sqlite3FkRequired(
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
if( p->aAction[1]!=OE_None ) return 2;
- eRet = 1;
+ bHaveFK = 1;
}
}
}
}
- return eRet;
+ return bHaveFK ? eRet : 0;
}
/*
@@ -1352,7 +1355,7 @@ static Trigger *fkActionTrigger(
switch( action ){
case OE_Restrict:
- pStep->op = TK_SELECT;
+ pStep->op = TK_SELECT;
break;
case OE_Cascade:
if( !pChanges ){
diff --git a/src/func.c b/src/func.c
index ad948d658d..5e7eae873c 100644
--- a/src/func.c
+++ b/src/func.c
@@ -696,7 +696,8 @@ static int patternCompare(
/* Skip over multiple "*" characters in the pattern. If there
** are also "?" characters, skip those as well, but consume a
** single character of the input string for each "?" skipped */
- while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
+ while( (c=Utf8Read(zPattern)) == matchAll
+ || (c == matchOne && matchOne!=0) ){
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
return SQLITE_NOWILDCARDMATCH;
}
@@ -1867,7 +1868,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef;
int nExpr;
- if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
+ assert( pExpr!=0 );
+ assert( pExpr->op==TK_FUNCTION );
+ if( !pExpr->x.pList ){
return 0;
}
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
@@ -1906,6 +1909,203 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
return 1;
}
+/* Mathematical Constants */
+#ifndef M_PI
+# define M_PI 3.141592653589793238462643383279502884
+#endif
+#ifndef M_LN10
+# define M_LN10 2.302585092994045684017991454684364208
+#endif
+#ifndef M_LN2
+# define M_LN2 0.693147180559945309417232121458176568
+#endif
+
+
+/* Extra math functions that require linking with -lm
+*/
+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+/*
+** Implementation SQL functions:
+**
+** ceil(X)
+** ceiling(X)
+** floor(X)
+**
+** The sqlite3_user_data() pointer is a pointer to the libm implementation
+** of the underlying C function.
+*/
+static void ceilingFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ switch( sqlite3_value_numeric_type(argv[0]) ){
+ case SQLITE_INTEGER: {
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double (*x)(double) = (double(*)(double))sqlite3_user_data(context);
+ sqlite3_result_double(context, x(sqlite3_value_double(argv[0])));
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/*
+** On some systems, ceil() and floor() are intrinsic function. You are
+** unable to take a pointer to these functions. Hence, we here wrap them
+** in our own actual functions.
+*/
+static double xCeil(double x){ return ceil(x); }
+static double xFloor(double x){ return floor(x); }
+
+/*
+** Implementation of SQL functions:
+**
+** ln(X) - natural logarithm
+** log(X) - log X base 10
+** log10(X) - log X base 10
+** log(B,X) - log X base B
+*/
+static void logFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ double x, b, ans;
+ assert( argc==1 || argc==2 );
+ switch( sqlite3_value_numeric_type(argv[0]) ){
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ x = sqlite3_value_double(argv[0]);
+ if( x<=0.0 ) return;
+ break;
+ default:
+ return;
+ }
+ if( argc==2 ){
+ switch( sqlite3_value_numeric_type(argv[0]) ){
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ b = log(x);
+ if( b<=0.0 ) return;
+ x = sqlite3_value_double(argv[1]);
+ if( x<=0.0 ) return;
+ break;
+ default:
+ return;
+ }
+ ans = log(x)/b;
+ }else{
+ ans = log(x);
+ switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
+ case 1:
+ /* Convert from natural logarithm to log base 10 */
+ ans *= 1.0/M_LN10;
+ break;
+ case 2:
+ /* Convert from natural logarithm to log base 2 */
+ ans *= 1.0/M_LN2;
+ break;
+ default:
+ break;
+ }
+ }
+ sqlite3_result_double(context, ans);
+}
+
+/*
+** Functions to converts degrees to radians and radians to degrees.
+*/
+static double degToRad(double x){ return x*(M_PI/180.0); }
+static double radToDeg(double x){ return x*(180.0/M_PI); }
+
+/*
+** Implementation of 1-argument SQL math functions:
+**
+** exp(X) - Compute e to the X-th power
+*/
+static void math1Func(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int type0;
+ double v0, ans;
+ double (*x)(double);
+ assert( argc==1 );
+ type0 = sqlite3_value_numeric_type(argv[0]);
+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+ v0 = sqlite3_value_double(argv[0]);
+ x = (double(*)(double))sqlite3_user_data(context);
+ ans = x(v0);
+ sqlite3_result_double(context, ans);
+}
+
+/*
+** Implementation of 2-argument SQL math functions:
+**
+** power(X,Y) - Compute X to the Y-th power
+*/
+static void math2Func(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int type0, type1;
+ double v0, v1, ans;
+ double (*x)(double,double);
+ assert( argc==2 );
+ type0 = sqlite3_value_numeric_type(argv[0]);
+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+ type1 = sqlite3_value_numeric_type(argv[1]);
+ if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return;
+ v0 = sqlite3_value_double(argv[0]);
+ v1 = sqlite3_value_double(argv[1]);
+ x = (double(*)(double,double))sqlite3_user_data(context);
+ ans = x(v0, v1);
+ sqlite3_result_double(context, ans);
+}
+
+/*
+** Implementation of 2-argument SQL math functions:
+**
+** power(X,Y) - Compute X to the Y-th power
+*/
+static void piFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==0 );
+ sqlite3_result_double(context, M_PI);
+}
+
+#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
+
+/*
+** Implementation of sign(X) function.
+*/
+static void signFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int type0;
+ double x;
+ UNUSED_PARAMETER(argc);
+ assert( argc==1 );
+ type0 = sqlite3_value_numeric_type(argv[0]);
+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+ x = sqlite3_value_double(argv[0]);
+ sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
+}
+
/*
** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table. This occurs at start-time (as
@@ -2024,6 +2224,43 @@ void sqlite3RegisterBuiltinFunctions(void){
#endif
FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ),
+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+ MFUNCTION(ceil, 1, xCeil, ceilingFunc ),
+ MFUNCTION(ceiling, 1, xCeil, ceilingFunc ),
+ MFUNCTION(floor, 1, xFloor, ceilingFunc ),
+#if SQLITE_HAVE_C99_MATH_FUNCS
+ MFUNCTION(trunc, 1, trunc, ceilingFunc ),
+#endif
+ FUNCTION(ln, 1, 0, 0, logFunc ),
+ FUNCTION(log, 1, 1, 0, logFunc ),
+ FUNCTION(log10, 1, 1, 0, logFunc ),
+ FUNCTION(log2, 1, 2, 0, logFunc ),
+ FUNCTION(log, 2, 0, 0, logFunc ),
+ MFUNCTION(exp, 1, exp, math1Func ),
+ MFUNCTION(pow, 2, pow, math2Func ),
+ MFUNCTION(power, 2, pow, math2Func ),
+ MFUNCTION(mod, 2, fmod, math2Func ),
+ MFUNCTION(acos, 1, acos, math1Func ),
+ MFUNCTION(asin, 1, asin, math1Func ),
+ MFUNCTION(atan, 1, atan, math1Func ),
+ MFUNCTION(atan2, 2, atan2, math2Func ),
+ MFUNCTION(cos, 1, cos, math1Func ),
+ MFUNCTION(sin, 1, sin, math1Func ),
+ MFUNCTION(tan, 1, tan, math1Func ),
+ MFUNCTION(cosh, 1, cosh, math1Func ),
+ MFUNCTION(sinh, 1, sinh, math1Func ),
+ MFUNCTION(tanh, 1, tanh, math1Func ),
+#if SQLITE_HAVE_C99_MATH_FUNCS
+ MFUNCTION(acosh, 1, acosh, math1Func ),
+ MFUNCTION(asinh, 1, asinh, math1Func ),
+ MFUNCTION(atanh, 1, atanh, math1Func ),
+#endif
+ MFUNCTION(sqrt, 1, sqrt, math1Func ),
+ MFUNCTION(radians, 1, degToRad, math1Func ),
+ MFUNCTION(degrees, 1, radToDeg, math1Func ),
+ FUNCTION(pi, 0, 0, 0, piFunc ),
+#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
+ FUNCTION(sign, 1, 0, 0, signFunc ),
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
};
diff --git a/src/global.c b/src/global.c
index 8ef5a8b2d0..b5239ad81a 100644
--- a/src/global.c
+++ b/src/global.c
@@ -301,9 +301,10 @@ int sqlite3PendingByte = 0x40000000;
#endif
/*
-** Flags for select tracing and the ".selecttrace" macro of the CLI
+** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS.
*/
-u32 sqlite3_unsupported_selecttrace = 0;
+u32 sqlite3SelectTrace = 0;
+u32 sqlite3WhereTrace = 0;
#include "opcodes.h"
/*
diff --git a/src/insert.c b/src/insert.c
index 393cd528f1..f0b93ae376 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -370,7 +370,9 @@ static int autoIncBegin(
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
- if( pInfo==0 ) return 0;
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DbFree, pInfo);
+ testcase( pParse->earlyCleanup );
+ if( pParse->db->mallocFailed ) return 0;
pInfo->pNext = pToplevel->pAinc;
pToplevel->pAinc = pInfo;
pInfo->pTab = pTab;
@@ -928,19 +930,24 @@ void sqlite3Insert(
}
}
#endif
- }
- /* Make sure the number of columns in the source data matches the number
- ** of columns to be inserted into the table.
- */
- for(i=0; inCol; i++){
- if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
- }
- if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
- sqlite3ErrorMsg(pParse,
- "table %S has %d columns but %d values were supplied",
- pTabList, 0, pTab->nCol-nHidden, nColumn);
- goto insert_cleanup;
+ /* Make sure the number of columns in the source data matches the number
+ ** of columns to be inserted into the table.
+ */
+ assert( TF_HasHidden==COLFLAG_HIDDEN );
+ assert( TF_HasGenerated==COLFLAG_GENERATED );
+ assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) );
+ if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){
+ for(i=0; inCol; i++){
+ if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
+ }
+ }
+ if( nColumn!=(pTab->nCol-nHidden) ){
+ sqlite3ErrorMsg(pParse,
+ "table %S has %d columns but %d values were supplied",
+ pTabList, 0, pTab->nCol-nHidden, nColumn);
+ goto insert_cleanup;
+ }
}
if( pColumn!=0 && nColumn!=pColumn->nId ){
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
@@ -952,6 +959,7 @@ void sqlite3Insert(
if( (db->flags & SQLITE_CountRows)!=0
&& !pParse->nested
&& !pParse->pTriggerTab
+ && !pParse->bReturning
){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
@@ -975,6 +983,7 @@ void sqlite3Insert(
}
#ifndef SQLITE_OMIT_UPSERT
if( pUpsert ){
+ Upsert *pNx;
if( IsVirtual(pTab) ){
sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"",
pTab->zName);
@@ -988,13 +997,17 @@ void sqlite3Insert(
goto insert_cleanup;
}
pTabList->a[0].iCursor = iDataCur;
- pUpsert->pUpsertSrc = pTabList;
- pUpsert->regData = regData;
- pUpsert->iDataCur = iDataCur;
- pUpsert->iIdxCur = iIdxCur;
- if( pUpsert->pUpsertTarget ){
- sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert);
- }
+ pNx = pUpsert;
+ do{
+ pNx->pUpsertSrc = pTabList;
+ pNx->regData = regData;
+ pNx->iDataCur = iDataCur;
+ pNx->iIdxCur = iIdxCur;
+ if( pNx->pUpsertTarget ){
+ sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx);
+ }
+ pNx = pNx->pNextUpsert;
+ }while( pNx!=0 );
}
#endif
@@ -1135,11 +1148,6 @@ void sqlite3Insert(
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
- /* Cannot have triggers on a virtual table. If it were possible,
- ** this block would have to account for hidden column.
- */
- assert( !IsVirtual(pTab) );
-
/* Copy the new data already generated. */
assert( pTab->nNVCol>0 );
sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1);
@@ -1294,7 +1302,9 @@ void sqlite3Insert(
sqlite3VdbeJumpHere(v, addrInsTop);
}
+#ifndef SQLITE_OMIT_XFER_OPT
insert_end:
+#endif /* SQLITE_OMIT_XFER_OPT */
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
@@ -1309,7 +1319,7 @@ insert_end:
** invoke the callback function.
*/
if( regRowCount ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
+ sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
}
@@ -1399,6 +1409,70 @@ int sqlite3ExprReferencesUpdatedColumn(
return w.eCode!=0;
}
+/*
+** The sqlite3GenerateConstraintChecks() routine usually wants to visit
+** the indexes of a table in the order provided in the Table->pIndex list.
+** However, sometimes (rarely - when there is an upsert) it wants to visit
+** the indexes in a different order. The following data structures accomplish
+** this.
+**
+** The IndexIterator object is used to walk through all of the indexes
+** of a table in either Index.pNext order, or in some other order established
+** by an array of IndexListTerm objects.
+*/
+typedef struct IndexListTerm IndexListTerm;
+typedef struct IndexIterator IndexIterator;
+struct IndexIterator {
+ int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */
+ int i; /* Index of the current item from the list */
+ union {
+ struct { /* Use this object for eType==0: A Index.pNext list */
+ Index *pIdx; /* The current Index */
+ } lx;
+ struct { /* Use this object for eType==1; Array of IndexListTerm */
+ int nIdx; /* Size of the array */
+ IndexListTerm *aIdx; /* Array of IndexListTerms */
+ } ax;
+ } u;
+};
+
+/* When IndexIterator.eType==1, then each index is an array of instances
+** of the following object
+*/
+struct IndexListTerm {
+ Index *p; /* The index */
+ int ix; /* Which entry in the original Table.pIndex list is this index*/
+};
+
+/* Return the first index on the list */
+static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){
+ assert( pIter->i==0 );
+ if( pIter->eType ){
+ *pIx = pIter->u.ax.aIdx[0].ix;
+ return pIter->u.ax.aIdx[0].p;
+ }else{
+ *pIx = 0;
+ return pIter->u.lx.pIdx;
+ }
+}
+
+/* Return the next index from the list. Return NULL when out of indexes */
+static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){
+ if( pIter->eType ){
+ int i = ++pIter->i;
+ if( i>=pIter->u.ax.nIdx ){
+ *pIx = i;
+ return 0;
+ }
+ *pIx = pIter->u.ax.aIdx[i].ix;
+ return pIter->u.ax.aIdx[i].p;
+ }else{
+ ++(*pIx);
+ pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext;
+ return pIter->u.lx.pIdx;
+ }
+}
+
/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
@@ -1507,7 +1581,7 @@ void sqlite3GenerateConstraintChecks(
){
Vdbe *v; /* VDBE under constrution */
Index *pIdx; /* Pointer to one of the indices */
- Index *pPk = 0; /* The PRIMARY KEY index */
+ Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */
sqlite3 *db; /* Database connection */
int i; /* loop counter */
int ix; /* Index loop counter */
@@ -1515,11 +1589,11 @@ void sqlite3GenerateConstraintChecks(
int onError; /* Conflict resolution strategy */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
- Index *pUpIdx = 0; /* Index to which to apply the upsert */
- u8 isUpdate; /* True if this is an UPDATE operation */
+ Upsert *pUpsertClause = 0; /* The specific ON CONFLICT clause for pIdx */
+ 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 upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */
+ int upsertIpkDelay = 0; /* Address of Goto to bypass initial IPK check */
int ipkTop = 0; /* Top of the IPK uniqueness check */
int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */
/* Variables associated with retesting uniqueness constraints after
@@ -1529,6 +1603,7 @@ void sqlite3GenerateConstraintChecks(
int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
Trigger *pTrigger; /* List of DELETE triggers on the table pTab */
int nReplaceTrig = 0; /* Number of replace triggers coded */
+ IndexIterator sIdxIter; /* Index iterator */
isUpdate = regOldData!=0;
db = pParse->db;
@@ -1726,19 +1801,63 @@ void sqlite3GenerateConstraintChecks(
** list of indexes attached to a table puts all OE_Replace indexes last
** in the list. See sqlite3CreateIndex() for where that happens.
*/
-
+ sIdxIter.eType = 0;
+ sIdxIter.i = 0;
+ sIdxIter.u.ax.aIdx = 0; /* Silence harmless compiler warning */
+ sIdxIter.u.lx.pIdx = pTab->pIndex;
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"));
+ /* There is just on ON CONFLICT clause and it has no constraint-target */
+ assert( pUpsert->pNextUpsert==0 );
+ if( pUpsert->isDoUpdate==0 ){
+ /* A single ON CONFLICT DO NOTHING clause, without a constraint-target.
+ ** Make all unique constraint resolution be OE_Ignore */
+ overrideError = OE_Ignore;
+ pUpsert = 0;
+ }else{
+ /* A single ON CONFLICT DO UPDATE. Make all resolutions OE_Update */
+ overrideError = OE_Update;
+ }
+ }else if( pTab->pIndex!=0 ){
+ /* Otherwise, we'll need to run the IndexListTerm array version of the
+ ** iterator to ensure that all of the ON CONFLICT conditions are
+ ** checked first and in order. */
+ int nIdx, jj;
+ u64 nByte;
+ Upsert *pTerm;
+ u8 *bUsed;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
+ assert( aRegIdx[nIdx]>0 );
+ }
+ sIdxIter.eType = 1;
+ sIdxIter.u.ax.nIdx = nIdx;
+ nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx;
+ sIdxIter.u.ax.aIdx = sqlite3DbMallocZero(db, nByte);
+ if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */
+ bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx];
+ pUpsert->pToFree = sIdxIter.u.ax.aIdx;
+ for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){
+ if( pTerm->pUpsertTarget==0 ) break;
+ if( pTerm->pUpsertIdx==0 ) continue; /* Skip ON CONFLICT for the IPK */
+ jj = 0;
+ pIdx = pTab->pIndex;
+ while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){
+ pIdx = pIdx->pNext;
+ jj++;
+ }
+ if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */
+ bUsed[jj] = 1;
+ sIdxIter.u.ax.aIdx[i].p = pIdx;
+ sIdxIter.u.ax.aIdx[i].ix = jj;
+ i++;
+ }
+ for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){
+ if( bUsed[jj] ) continue;
+ sIdxIter.u.ax.aIdx[i].p = pIdx;
+ sIdxIter.u.ax.aIdx[i].ix = jj;
+ i++;
+ }
+ assert( i==nIdx );
}
}
@@ -1801,11 +1920,20 @@ void sqlite3GenerateConstraintChecks(
}
/* 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( pUpsert ){
+ pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0);
+ if( pUpsertClause!=0 ){
+ if( pUpsertClause->isDoUpdate==0 ){
+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
+ }else{
+ onError = OE_Update; /* DO UPDATE */
+ }
+ }
+ if( pUpsertClause!=pUpsert ){
+ /* The first ON CONFLICT clause has a conflict target other than
+ ** the IPK. We have to jump ahead to that first ON CONFLICT clause
+ ** and then come back here and deal with the IPK afterwards */
+ upsertIpkDelay = sqlite3VdbeAddOp0(v, OP_Goto);
}
}
@@ -1815,7 +1943,7 @@ void sqlite3GenerateConstraintChecks(
** the UNIQUE constraints have run.
*/
if( onError==OE_Replace /* IPK rule is REPLACE */
- && onError!=overrideError /* Rules for other contraints are different */
+ && onError!=overrideError /* Rules for other constraints are different */
&& pTab->pIndex /* There exist other constraints */
){
ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
@@ -1912,7 +2040,9 @@ void sqlite3GenerateConstraintChecks(
}
}
sqlite3VdbeResolveLabel(v, addrRowidOk);
- if( ipkTop ){
+ if( pUpsert && pUpsertClause!=pUpsert ){
+ upsertIpkReturn = sqlite3VdbeAddOp0(v, OP_Goto);
+ }else if( ipkTop ){
ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, ipkTop-1);
}
@@ -1925,7 +2055,10 @@ void sqlite3GenerateConstraintChecks(
** This loop also handles the case of the PRIMARY KEY index for a
** WITHOUT ROWID table.
*/
- for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
+ for(pIdx = indexIteratorFirst(&sIdxIter, &ix);
+ pIdx;
+ pIdx = indexIteratorNext(&sIdxIter, &ix)
+ ){
int regIdx; /* Range of registers hold conent for pIdx */
int regR; /* Range of registers holding conflicting PK */
int iThisCur; /* Cursor for this UNIQUE index */
@@ -1933,15 +2066,14 @@ void sqlite3GenerateConstraintChecks(
int addrConflictCk; /* First opcode in the conflict check logic */
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
- if( pUpIdx==pIdx ){
- addrUniqueOk = upsertJump+1;
- upsertBypass = sqlite3VdbeGoto(v, 0);
- VdbeComment((v, "Skip upsert subroutine"));
- sqlite3VdbeJumpHere(v, upsertJump);
- }else{
- addrUniqueOk = sqlite3VdbeMakeLabel(pParse);
+ if( pUpsert ){
+ pUpsertClause = sqlite3UpsertOfIndex(pUpsert, pIdx);
+ if( upsertIpkDelay && pUpsertClause==pUpsert ){
+ sqlite3VdbeJumpHere(v, upsertIpkDelay);
+ }
}
- if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
+ addrUniqueOk = sqlite3VdbeMakeLabel(pParse);
+ if( bAffinityDone==0 ){
sqlite3TableAffinity(v, pTab, regNewData+1);
bAffinityDone = 1;
}
@@ -2012,8 +2144,8 @@ void sqlite3GenerateConstraintChecks(
}
/* Figure out if the upsert clause applies to this index */
- if( pUpIdx==pIdx ){
- if( pUpsert->pUpsertSet==0 ){
+ if( pUpsertClause ){
+ if( pUpsertClause->isDoUpdate==0 ){
onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
}else{
onError = OE_Update; /* DO UPDATE */
@@ -2051,7 +2183,7 @@ void sqlite3GenerateConstraintChecks(
regIdx, pIdx->nKeyCol); VdbeCoverage(v);
/* Generate code to handle collisions */
- regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField);
+ regR = pIdx==pPk ? regIdx : sqlite3GetTempRange(pParse, nPkField);
if( isUpdate || onError==OE_Replace ){
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
@@ -2203,13 +2335,16 @@ void sqlite3GenerateConstraintChecks(
break;
}
}
- if( pUpIdx==pIdx ){
- sqlite3VdbeGoto(v, upsertJump+1);
- sqlite3VdbeJumpHere(v, upsertBypass);
- }else{
- sqlite3VdbeResolveLabel(v, addrUniqueOk);
- }
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
+ if( pUpsertClause
+ && upsertIpkReturn
+ && sqlite3UpsertNextIsIPK(pUpsertClause)
+ ){
+ sqlite3VdbeGoto(v, upsertIpkDelay+1);
+ sqlite3VdbeJumpHere(v, upsertIpkReturn);
+ upsertIpkReturn = 0;
+ }
}
/* If the IPK constraint is a REPLACE, run it last */
@@ -2275,6 +2410,32 @@ void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){
}
#endif
+/*
+** Table pTab is a WITHOUT ROWID table that is being written to. The cursor
+** number is iCur, and register regData contains the new record for the
+** PK index. This function adds code to invoke the pre-update hook,
+** if one is registered.
+*/
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+static void codeWithoutRowidPreupdate(
+ Parse *pParse, /* Parse context */
+ Table *pTab, /* Table being updated */
+ int iCur, /* Cursor number for table */
+ int regData /* Data containing new record */
+){
+ Vdbe *v = pParse->pVdbe;
+ int r = sqlite3GetTempReg(pParse);
+ assert( !HasRowid(pTab) );
+ assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) );
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
+ sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
+ sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+ sqlite3ReleaseTempReg(pParse, r);
+}
+#else
+# define codeWithoutRowidPreupdate(a,b,c,d)
+#endif
+
/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
@@ -2323,17 +2484,9 @@ void sqlite3CompleteInsertion(
assert( pParse->nested==0 );
pik_flags |= OPFLAG_NCHANGE;
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
-#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( update_flags==0 ){
- int r = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
- sqlite3VdbeAddOp4(v, OP_Insert,
- iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE
- );
- sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
- sqlite3ReleaseTempReg(pParse, r);
+ codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]);
}
-#endif
}
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
aRegIdx[i]+1,
@@ -2531,7 +2684,7 @@ static int xferOptimization(
ExprList *pEList; /* The result set of the SELECT */
Table *pSrc; /* The table in the FROM clause of SELECT */
Index *pSrcIdx, *pDestIdx; /* Source and destination indices */
- struct SrcList_item *pItem; /* An element of pSelect->pSrc */
+ SrcItem *pItem; /* An element of pSelect->pSrc */
int i; /* Loop counter */
int iDbSrc; /* The database of pSrc */
int iSrc, iDest; /* Cursors from source and destination */
@@ -2748,6 +2901,7 @@ static int xferOptimization(
iDest = pParse->nTab++;
regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
regData = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regData);
regRowid = sqlite3GetTempReg(pParse);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
assert( HasRowid(pDest) || destHasUniqueIdx );
@@ -2783,11 +2937,13 @@ static int xferOptimization(
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);
- sqlite3VdbeJumpHere(v, addr2);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeVerifyAbortable(v, onError);
+ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
+ VdbeCoverage(v);
+ sqlite3RowidConstraint(pParse, onError, pDest);
+ sqlite3VdbeJumpHere(v, addr2);
+ }
autoIncStep(pParse, regAutoinc, regRowid);
}else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){
addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
@@ -2795,16 +2951,28 @@ static int xferOptimization(
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
}
+
if( db->mDbFlags & DBFLAG_Vacuum ){
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
- insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
+ insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
}else{
- insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND;
+ insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT;
+ }
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+ insFlags &= ~OPFLAG_PREFORMAT;
+ }else
+#endif
+ {
+ sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid);
+ }
+ sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE);
}
- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
- sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
- (char*)pDest, P4_TABLE);
sqlite3VdbeChangeP5(v, insFlags);
+
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
@@ -2846,13 +3014,22 @@ static int xferOptimization(
if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
}
if( i==pSrcIdx->nColumn ){
- idxInsFlags = OPFLAG_USESEEKRESULT;
+ idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
+ sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc);
}
}else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
idxInsFlags |= OPFLAG_NCHANGE;
}
- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+ if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){
+ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0
+ && !HasRowid(pDest)
+ && IsPrimaryKeyIndex(pDestIdx)
+ ){
+ codeWithoutRowidPreupdate(pParse, pDest, iDest, regData);
+ }
+ }
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
diff --git a/src/main.c b/src/main.c
index 2fee2934ba..fbc0e156c6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -4080,7 +4080,7 @@ int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
+ db->dbOptFlags = va_arg(ap, u32);
break;
}
@@ -4255,7 +4255,26 @@ int sqlite3_test_control(int op, ...){
break;
}
-
+ /* sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, op, ptr)
+ **
+ ** "ptr" is a pointer to a u32.
+ **
+ ** op==0 Store the current sqlite3SelectTrace in *ptr
+ ** op==1 Set sqlite3SelectTrace to the value *ptr
+ ** op==3 Store the current sqlite3WhereTrace in *ptr
+ ** op==3 Set sqlite3WhereTrace to the value *ptr
+ */
+ case SQLITE_TESTCTRL_TRACEFLAGS: {
+ int opTrace = va_arg(ap, int);
+ u32 *ptr = va_arg(ap, u32*);
+ switch( opTrace ){
+ case 0: *ptr = sqlite3SelectTrace; break;
+ case 1: sqlite3SelectTrace = *ptr; break;
+ case 2: *ptr = sqlite3WhereTrace; break;
+ case 3: sqlite3WhereTrace = *ptr; break;
+ }
+ break;
+ }
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
diff --git a/src/memjournal.c b/src/memjournal.c
index 4811f2d8d4..660a842676 100644
--- a/src/memjournal.c
+++ b/src/memjournal.c
@@ -70,7 +70,6 @@ struct MemJournal {
int nChunkSize; /* In-memory chunk-size */
int nSpill; /* Bytes of data before flushing */
- int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
FilePoint readpoint; /* Pointer to the end of the last xRead() */
@@ -131,14 +130,13 @@ static int memjrnlRead(
/*
** Free the list of FileChunk structures headed at MemJournal.pFirst.
*/
-static void memjrnlFreeChunks(MemJournal *p){
+static void memjrnlFreeChunks(FileChunk *pFirst){
FileChunk *pIter;
FileChunk *pNext;
- for(pIter=p->pFirst; pIter; pIter=pNext){
+ for(pIter=pFirst; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
- p->pFirst = 0;
}
/*
@@ -165,7 +163,7 @@ static int memjrnlCreateFile(MemJournal *p){
}
if( rc==SQLITE_OK ){
/* No error has occurred. Free the in-memory buffers. */
- memjrnlFreeChunks(©);
+ memjrnlFreeChunks(copy.pFirst);
}
}
if( rc!=SQLITE_OK ){
@@ -248,7 +246,6 @@ static int memjrnlWrite(
nWrite -= iSpace;
p->endpoint.iOffset += iSpace;
}
- p->nSize = iAmt + iOfst;
}
}
@@ -256,22 +253,30 @@ static int memjrnlWrite(
}
/*
-** Truncate the file.
-**
-** If the journal file is already on disk, truncate it there. Or, if it
-** is still in main memory but is being truncated to zero bytes in size,
-** ignore
+** Truncate the in-memory file.
*/
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
- if( ALWAYS(size==0) ){
- memjrnlFreeChunks(p);
- p->nSize = 0;
- p->endpoint.pChunk = 0;
- p->endpoint.iOffset = 0;
- p->readpoint.pChunk = 0;
- p->readpoint.iOffset = 0;
+ FileChunk *pIter = 0;
+
+ if( size==0 ){
+ memjrnlFreeChunks(p->pFirst);
+ p->pFirst = 0;
+ }else{
+ i64 iOff = p->nChunkSize;
+ for(pIter=p->pFirst; ALWAYS(pIter) && iOff<=size; pIter=pIter->pNext){
+ iOff += p->nChunkSize;
+ }
+ if( ALWAYS(pIter) ){
+ memjrnlFreeChunks(pIter->pNext);
+ pIter->pNext = 0;
+ }
}
+
+ p->endpoint.pChunk = pIter;
+ p->endpoint.iOffset = size;
+ p->readpoint.pChunk = 0;
+ p->readpoint.iOffset = 0;
return SQLITE_OK;
}
@@ -280,7 +285,7 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
*/
static int memjrnlClose(sqlite3_file *pJfd){
MemJournal *p = (MemJournal *)pJfd;
- memjrnlFreeChunks(p);
+ memjrnlFreeChunks(p->pFirst);
return SQLITE_OK;
}
diff --git a/src/os.c b/src/os.c
index a1a276f433..693d78dc91 100644
--- a/src/os.c
+++ b/src/os.c
@@ -129,6 +129,8 @@ int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_TEST
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
&& op!=SQLITE_FCNTL_LOCK_TIMEOUT
+ && op!=SQLITE_FCNTL_CKPT_DONE
+ && op!=SQLITE_FCNTL_CKPT_START
){
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
** is using a regular VFS, it is called after the corresponding
@@ -139,7 +141,12 @@ int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
** The core must call OsFileControl() though, not OsFileControlHint(),
** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
** means the commit really has failed and an error should be returned
- ** to the user. */
+ ** to the user.
+ **
+ ** The CKPT_DONE and CKPT_START file-controls are write-only signals
+ ** to the cksumvfs. Their return code is meaningless and is ignored
+ ** by the SQLite core, so there is no point in simulating OOMs for them.
+ */
DO_OS_MALLOC_TEST(id);
}
#endif
diff --git a/src/pager.c b/src/pager.c
index 45e69f29e4..698ff695f6 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -435,6 +435,7 @@ struct PagerSavepoint {
Bitvec *pInSavepoint; /* Set of pages in this savepoint */
Pgno nOrig; /* Original number of pages in file */
Pgno iSubRec; /* Index of first record in sub-journal */
+ int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */
#ifndef SQLITE_OMIT_WAL
u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
#endif
@@ -1064,6 +1065,9 @@ static int subjRequiresPage(PgHdr *pPg){
for(i=0; inSavepoint; i++){
p = &pPager->aSavepoint[i];
if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
+ for(i=i+1; inSavepoint; i++){
+ pPager->aSavepoint[i].bTruncateOnRelease = 0;
+ }
return 1;
}
}
@@ -7006,6 +7010,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
}
aNew[ii].iSubRec = pPager->nSubRec;
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
+ aNew[ii].bTruncateOnRelease = 1;
if( !aNew[ii].pInSavepoint ){
return SQLITE_NOMEM_BKPT;
}
@@ -7087,13 +7092,15 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
/* If this is a release of the outermost savepoint, truncate
** the sub-journal to zero bytes in size. */
if( op==SAVEPOINT_RELEASE ){
- if( nNew==0 && isOpen(pPager->sjfd) ){
+ PagerSavepoint *pRel = &pPager->aSavepoint[nNew];
+ if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
if( sqlite3JournalIsInMemory(pPager->sjfd) ){
- rc = sqlite3OsTruncate(pPager->sjfd, 0);
+ i64 sz = (pPager->pageSize+4)*pRel->iSubRec;
+ rc = sqlite3OsTruncate(pPager->sjfd, sz);
assert( rc==SQLITE_OK );
}
- pPager->nSubRec = 0;
+ pPager->nSubRec = pRel->iSubRec;
}
}
/* Else this is a rollback operation, playback the specified savepoint.
diff --git a/src/parse.y b/src/parse.y
index 8f2e97fc7d..821e8ebe46 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -266,6 +266,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
%ifndef SQLITE_OMIT_GENERATED_COLUMNS
GENERATED ALWAYS
%endif
+ MATERIALIZED
REINDEX RENAME CTIME_KW IF
.
%wildcard ANY.
@@ -525,29 +526,25 @@ cmd ::= select(X). {
}
}
}
+
+ /* Attach a With object describing the WITH clause to a Select
+ ** object describing the query for which the WITH clause is a prefix.
+ */
+ static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){
+ if( pSelect ){
+ pSelect->pWith = pWith;
+ parserDoubleLinkSelect(pParse, pSelect);
+ }else{
+ sqlite3WithDelete(pParse->db, pWith);
+ }
+ return pSelect;
+ }
}
%ifndef SQLITE_OMIT_CTE
-select(A) ::= WITH wqlist(W) selectnowith(X). {
- Select *p = X;
- if( p ){
- p->pWith = W;
- parserDoubleLinkSelect(pParse, p);
- }else{
- sqlite3WithDelete(pParse->db, W);
- }
- A = p;
-}
-select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {
- Select *p = X;
- if( p ){
- p->pWith = W;
- parserDoubleLinkSelect(pParse, p);
- }else{
- sqlite3WithDelete(pParse->db, W);
- }
- A = p;
-}
+select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);}
+select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X).
+ {A = attachWithToSelect(pParse,X,W);}
%endif /* SQLITE_OMIT_CTE */
select(A) ::= selectnowith(X). {
Select *p = X;
@@ -714,8 +711,8 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z)
}else if( F->nSrc==1 ){
A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,N,U);
if( A ){
- struct SrcList_item *pNew = &A->a[A->nSrc-1];
- struct SrcList_item *pOld = F->a;
+ SrcItem *pNew = &A->a[A->nSrc-1];
+ SrcItem *pOld = F->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -883,7 +880,7 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
/////////////////////////// The DELETE statement /////////////////////////////
//
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
-cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
+cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W)
orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
@@ -896,7 +893,7 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
sqlite3DeleteFrom(pParse,X,W,O,L);
}
%else
-cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
+cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W,0,0);
}
@@ -904,15 +901,23 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
%type where_opt {Expr*}
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
+%type where_opt_ret {Expr*}
+%destructor where_opt_ret {sqlite3ExprDelete(pParse->db, $$);}
where_opt(A) ::= . {A = 0;}
where_opt(A) ::= WHERE expr(X). {A = X;}
+where_opt_ret(A) ::= . {A = 0;}
+where_opt_ret(A) ::= WHERE expr(X). {A = X;}
+where_opt_ret(A) ::= RETURNING selcollist(X).
+ {sqlite3AddReturning(pParse,X); A = 0;}
+where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y).
+ {sqlite3AddReturning(pParse,Y); A = X;}
////////////////////////// The UPDATE command ////////////////////////////////
//
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
- where_opt(W) orderby_opt(O) limit_opt(L). {
+ where_opt_ret(W) orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
X = sqlite3SrcListAppendList(pParse, X, F);
sqlite3ExprListCheckLength(pParse,Y,"set list");
@@ -927,7 +932,7 @@ cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
}
%else
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
- where_opt(W). {
+ where_opt_ret(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
X = sqlite3SrcListAppendList(pParse, X, F);
@@ -961,7 +966,7 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S)
upsert(U). {
sqlite3Insert(pParse, X, S, F, R, U);
}
-cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
+cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES returning.
{
sqlite3Insert(pParse, X, 0, F, R, 0);
}
@@ -974,13 +979,19 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
// avoid unreachable code.
//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
upsert(A) ::= . { A = 0; }
+upsert(A) ::= RETURNING selcollist(X). { A = 0; sqlite3AddReturning(pParse,X); }
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
- DO UPDATE SET setlist(Z) where_opt(W).
- { A = sqlite3UpsertNew(pParse->db,T,TW,Z,W);}
-upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING.
- { A = sqlite3UpsertNew(pParse->db,T,TW,0,0); }
-upsert(A) ::= ON CONFLICT DO NOTHING.
- { A = sqlite3UpsertNew(pParse->db,0,0,0,0); }
+ DO UPDATE SET setlist(Z) where_opt(W) upsert(N).
+ { A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);}
+upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N).
+ { A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); }
+upsert(A) ::= ON CONFLICT DO NOTHING returning.
+ { A = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
+upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W) returning.
+ { A = sqlite3UpsertNew(pParse->db,0,0,Z,W,0);}
+
+returning ::= RETURNING selcollist(X). {sqlite3AddReturning(pParse,X);}
+returning ::= .
%type insert_cmd {int}
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
@@ -1622,6 +1633,10 @@ cmd ::= ALTER TABLE add_column_fullname
Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &Y);
}
+cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). {
+ sqlite3AlterDropColumn(pParse, X, &Y);
+}
+
add_column_fullname ::= fullname(X). {
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, X);
@@ -1659,17 +1674,26 @@ anylist ::= anylist ANY.
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
%type wqlist {With*}
%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
+%type wqitem {Cte*}
+// %destructor wqitem {sqlite3CteDelete(pParse->db, $$);} // not reachable
with ::= .
%ifndef SQLITE_OMIT_CTE
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
-wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
- A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/
+%type wqas {u8}
+wqas(A) ::= AS. {A = M10d_Any;}
+wqas(A) ::= AS MATERIALIZED. {A = M10d_Yes;}
+wqas(A) ::= AS NOT MATERIALIZED. {A = M10d_No;}
+wqitem(A) ::= nm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. {
+ A = sqlite3CteNew(pParse, &X, Y, Z, M); /*A-overwrites-X*/
}
-wqlist(A) ::= wqlist(A) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
- A = sqlite3WithAdd(pParse, A, &X, Y, Z);
+wqlist(A) ::= wqitem(X). {
+ A = sqlite3WithAdd(pParse, 0, X); /*A-overwrites-X*/
+}
+wqlist(A) ::= wqlist(A) COMMA wqitem(X). {
+ A = sqlite3WithAdd(pParse, A, X);
}
%endif SQLITE_OMIT_CTE
diff --git a/src/pcache1.c b/src/pcache1.c
index ed762ebf70..3eae6b63cd 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -461,6 +461,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
p->page.pExtra = &p[1];
p->isBulkLocal = 0;
p->isAnchor = 0;
+ p->pLruPrev = 0; /* Initializing this saves a valgrind error */
}
(*pCache->pnPurgeable)++;
return p;
diff --git a/src/prepare.c b/src/prepare.c
index 13fd1d33b2..d79495d2c6 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -21,7 +21,7 @@
*/
static void corruptSchema(
InitData *pData, /* Initialization context */
- const char *zObj, /* Object being parsed at the point of error */
+ char **azObj, /* Type and name of object being parsed */
const char *zExtra /* Error information */
){
sqlite3 *db = pData->db;
@@ -29,14 +29,18 @@ static void corruptSchema(
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);
+ }else if( pData->mInitFlags & (INITFLAG_AlterRename|INITFLAG_AlterDrop) ){
+ *pData->pzErrMsg = sqlite3MPrintf(db,
+ "error in %s %s after %s: %s", azObj[0], azObj[1],
+ (pData->mInitFlags & INITFLAG_AlterRename) ? "rename" : "drop column",
+ zExtra
+ );
pData->rc = SQLITE_ERROR;
}else if( db->flags & SQLITE_WriteSchema ){
pData->rc = SQLITE_CORRUPT_BKPT;
}else{
char *z;
- if( zObj==0 ) zObj = "?";
+ const char *zObj = azObj[1] ? azObj[1] : "?";
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
*pData->pzErrMsg = z;
@@ -94,19 +98,26 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
db->mDbFlags |= DBFLAG_EncodingFixed;
pData->nInitRow++;
if( db->mallocFailed ){
- corruptSchema(pData, argv[1], 0);
+ corruptSchema(pData, argv, 0);
return 1;
}
assert( iDb>=0 && iDbnDb );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[3]==0 ){
- corruptSchema(pData, argv[1], 0);
- }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){
+ corruptSchema(pData, argv, 0);
+ }else if( argv[4]
+ && 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]]
+ && 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
** structures that describe the table, index, or view.
+ **
+ ** No other valid SQL statement, other than the variable CREATE statements,
+ ** can begin with the letters "C" and "R". Thus, it is not possible run
+ ** any other kind of statement while parsing the schema, even a corrupt
+ ** schema.
*/
int rc;
u8 saved_iDb = db->init.iDb;
@@ -119,7 +130,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|| (db->init.newTnum>pData->mxPage && pData->mxPage>0)
){
if( sqlite3Config.bExtraSchemaChecks ){
- corruptSchema(pData, argv[1], "invalid rootpage");
+ corruptSchema(pData, argv, "invalid rootpage");
}
}
db->init.orphanTrigger = 0;
@@ -138,13 +149,13 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
- corruptSchema(pData, argv[1], sqlite3_errmsg(db));
+ corruptSchema(pData, argv, sqlite3_errmsg(db));
}
}
}
sqlite3_finalize(pStmt);
}else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
- corruptSchema(pData, argv[1], 0);
+ corruptSchema(pData, argv, 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -155,7 +166,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
Index *pIndex;
pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
if( pIndex==0 ){
- corruptSchema(pData, argv[1], "orphan index");
+ corruptSchema(pData, argv, "orphan index");
}else
if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0
|| pIndex->tnum<2
@@ -163,7 +174,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|| sqlite3IndexHasDuplicateRootPage(pIndex)
){
if( sqlite3Config.bExtraSchemaChecks ){
- corruptSchema(pData, argv[1], "invalid rootpage");
+ corruptSchema(pData, argv, "invalid rootpage");
}
}
}
@@ -543,28 +554,21 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
return i;
}
-/*
-** Deallocate a single AggInfo object
-*/
-static void agginfoFree(sqlite3 *db, AggInfo *p){
- sqlite3DbFree(db, p->aCol);
- sqlite3DbFree(db, p->aFunc);
- sqlite3DbFree(db, p);
-}
-
/*
** Free all memory allocations in the pParse object
*/
void sqlite3ParserReset(Parse *pParse){
sqlite3 *db = pParse->db;
- AggInfo *pThis = pParse->pAggList;
- while( pThis ){
- AggInfo *pNext = pThis->pNext;
- agginfoFree(db, pThis);
- pThis = pNext;
+ while( pParse->pCleanup ){
+ ParseCleanup *pCleanup = pParse->pCleanup;
+ pParse->pCleanup = pCleanup->pNext;
+ pCleanup->xCleanup(db, pCleanup->pPtr);
+ sqlite3DbFreeNN(db, pCleanup);
}
sqlite3DbFree(db, pParse->aLabel);
- sqlite3ExprListDelete(db, pParse->pConstExpr);
+ if( pParse->pConstExpr ){
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
+ }
if( db ){
assert( db->lookaside.bDisable >= pParse->disableLookaside );
db->lookaside.bDisable -= pParse->disableLookaside;
@@ -573,6 +577,47 @@ void sqlite3ParserReset(Parse *pParse){
pParse->disableLookaside = 0;
}
+/*
+** Add a new cleanup operation to a Parser. The cleanup should happen when
+** the parser object is destroyed. But, beware: the cleanup might happen
+** immediately.
+**
+** Use this mechanism for uncommon cleanups. There is a higher setup
+** cost for this mechansim (an extra malloc), so it should not be used
+** for common cleanups that happen on most calls. But for less
+** common cleanups, we save a single NULL-pointer comparison in
+** sqlite3ParserReset(), which reduces the total CPU cycle count.
+**
+** If a memory allocation error occurs, then the cleanup happens immediately.
+** When eithr SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
+** pParse->earlyCleanup flag is set in that case. Calling code show verify
+** that test cases exist for which this happens, to guard against possible
+** use-after-free errors following an OOM. The preferred way to do this is
+** to immediately follow the call to this routine with:
+**
+** testcase( pParse->earlyCleanup );
+*/
+void *sqlite3ParserAddCleanup(
+ Parse *pParse, /* Destroy when this Parser finishes */
+ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
+ void *pPtr /* Pointer to object to be cleaned up */
+){
+ ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ if( pCleanup ){
+ pCleanup->pNext = pParse->pCleanup;
+ pParse->pCleanup = pCleanup;
+ pCleanup->pPtr = pPtr;
+ pCleanup->xCleanup = xCleanup;
+ }else{
+ xCleanup(pParse->db, pPtr);
+ pPtr = 0;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+ pParse->earlyCleanup = 1;
+#endif
+ }
+ return pPtr;
+}
+
/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
@@ -671,12 +716,6 @@ static int sqlite3Prepare(
}
assert( 0==sParse.nQueryLoop );
- if( sParse.rc==SQLITE_DONE ){
- sParse.rc = SQLITE_OK;
- }
- if( sParse.checkSchema ){
- schemaIsValid(&sParse);
- }
if( pzTail ){
*pzTail = sParse.zTail;
}
@@ -687,20 +726,28 @@ static int sqlite3Prepare(
if( db->mallocFailed ){
sParse.rc = SQLITE_NOMEM_BKPT;
}
- rc = sParse.rc;
- if( rc!=SQLITE_OK ){
- if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
- assert(!(*ppStmt));
+ if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){
+ if( sParse.checkSchema ){
+ schemaIsValid(&sParse);
+ }
+ if( sParse.pVdbe ){
+ sqlite3VdbeFinalize(sParse.pVdbe);
+ }
+ assert( 0==(*ppStmt) );
+ rc = sParse.rc;
+ if( zErrMsg ){
+ sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
+ sqlite3DbFree(db, zErrMsg);
+ }else{
+ sqlite3Error(db, rc);
+ }
}else{
+ assert( zErrMsg==0 );
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
+ rc = SQLITE_OK;
+ sqlite3ErrorClear(db);
}
- if( zErrMsg ){
- sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
- sqlite3DbFree(db, zErrMsg);
- }else{
- sqlite3Error(db, rc);
- }
/* Delete any TriggerPrg structures allocated while parsing this statement. */
while( sParse.pTriggerPrg ){
diff --git a/src/printf.c b/src/printf.c
index f78d3bbb17..9aab863ed2 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -856,7 +856,7 @@ void sqlite3_str_vappendf(
case etSRCLIST: {
SrcList *pSrc;
int k;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
pSrc = va_arg(ap, SrcList*);
k = va_arg(ap, int);
@@ -921,7 +921,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
i64 szNew = p->nChar;
- szNew += N + 1;
+ szNew += (sqlite3_int64)N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
** to avoid having to call this routine too often */
diff --git a/src/resolve.c b/src/resolve.c
index b55bdc4187..6d3ed62f93 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -70,7 +70,6 @@ static void resolveAlias(
ExprList *pEList, /* A result set */
int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
Expr *pExpr, /* Transform this into an alias to the result set */
- const char *zType, /* "GROUP" or "ORDER" or "" */
int nSubquery /* Number of subqueries that the label is moving */
){
Expr *pOrig; /* The iCol-th column of the result set */
@@ -83,7 +82,7 @@ static void resolveAlias(
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( pDup!=0 ){
- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
+ incrAggFunctionDepth(pDup, nSubquery);
if( pExpr->op==TK_COLLATE ){
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
}
@@ -112,7 +111,6 @@ static void resolveAlias(
}
sqlite3DbFree(db, pDup);
}
- ExprSetProperty(pExpr, EP_Alias);
}
@@ -247,8 +245,8 @@ static int lookupName(
int cntTab = 0; /* Number of matching table names */
int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
- struct SrcList_item *pItem; /* Use for looping over pSrcList items */
- struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
+ SrcItem *pItem; /* Use for looping over pSrcList items */
+ SrcItem *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
@@ -369,25 +367,29 @@ static int lookupName(
#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. Or
- ** maybe it is an excluded.* from an upsert.
+ ** maybe it is an excluded.* from an upsert. Or maybe it is
+ ** a reference in the RETURNING clause to a table being modified.
*/
- if( zDb==0 && zTab!=0 && cntTab==0 ){
+ if( cnt==0 && zDb==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 ){
+ if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1;
pTab = pParse->pTriggerTab;
- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
+ }else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){
pExpr->iTable = 0;
pTab = pParse->pTriggerTab;
+ }else if( pParse->bReturning && (pNC->ncFlags & NC_UBaseReg)!=0 ){
+ pExpr->iTable = op!=TK_DELETE;
+ pTab = pParse->pTriggerTab;
}
}
#endif /* SQLITE_OMIT_TRIGGER */
#ifndef SQLITE_OMIT_UPSERT
- if( (pNC->ncFlags & NC_UUpsert)!=0 ){
+ if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){
Upsert *pUpsert = pNC->uNC.pUpsert;
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
pTab = pUpsert->pUpsertSrc->a[0].pTab;
@@ -426,27 +428,32 @@ static int lookupName(
pExpr->iTable = pNC->uNC.pUpsert->regData +
sqlite3TableColumnToStorage(pTab, iCol);
eNewExprOp = TK_REGISTER;
- ExprSetProperty(pExpr, EP_Alias);
}
}else
#endif /* SQLITE_OMIT_UPSERT */
{
-#ifndef SQLITE_OMIT_TRIGGER
- if( iCol<0 ){
- pExpr->affExpr = SQLITE_AFF_INTEGER;
- }else if( pExpr->iTable==0 ){
- testcase( iCol==31 );
- testcase( iCol==32 );
- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<y.pTab = pTab;
- pExpr->iColumn = (i16)iCol;
- eNewExprOp = TK_TRIGGER;
+ if( pParse->bReturning ){
+ eNewExprOp = TK_REGISTER;
+ pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable
+ + iCol + 1;
+ }else{
+ pExpr->iColumn = (i16)iCol;
+ eNewExprOp = TK_TRIGGER;
+#ifndef SQLITE_OMIT_TRIGGER
+ if( iCol<0 ){
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
+ }else if( pExpr->iTable==0 ){
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<pLeft);
- pExpr->pLeft = 0;
- sqlite3ExprDelete(db, pExpr->pRight);
- pExpr->pRight = 0;
+ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
+ sqlite3ExprDelete(db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pExpr->pRight);
+ pExpr->pRight = 0;
+ }
pExpr->op = eNewExprOp;
ExprSetProperty(pExpr, EP_Leaf);
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );
- if( !ExprHasProperty(pExpr, EP_Alias) ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ if( pParse->db->xAuth
+ && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
+ ){
sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
}
+#endif
/* Increment the nRef value on all name contexts from TopNC up to
** the point where the name matched. */
for(;;){
@@ -651,7 +664,7 @@ lookupname_end:
Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
if( p ){
- struct SrcList_item *pItem = &pSrc->a[iSrc];
+ SrcItem *pItem = &pSrc->a[iSrc];
Table *pTab = p->y.pTab = pItem->pTab;
p->iTable = pItem->iCursor;
if( p->y.pTab->iPKey==iCol ){
@@ -763,7 +776,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
*/
case TK_ROW: {
SrcList *pSrcList = pNC->pSrcList;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert( pSrcList && pSrcList->nSrc>=1 );
pItem = pSrcList->a;
pExpr->op = TK_COLUMN;
@@ -774,6 +787,44 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
break;
}
+ /* An " IS NOT NULL" or " IS NULL". After resolving the
+ ** LHS, check if there is a NOT NULL constraint in the schema that
+ ** means the value of the expression can be determined immediately.
+ ** If that is the case, replace the current expression node with
+ ** a TK_TRUEFALSE node.
+ **
+ ** If the node is replaced with a TK_TRUEFALSE node, then also restore
+ ** the NameContext ref-counts to the state they where in before the
+ ** LHS expression was resolved. This prevents the current select
+ ** from being erroneously marked as correlated in some cases.
+ */
+ case TK_NOTNULL:
+ case TK_ISNULL: {
+ int anRef[8];
+ NameContext *p;
+ int i;
+ for(i=0, p=pNC; p && ipNext, i++){
+ anRef[i] = p->nRef;
+ }
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
+ if( pExpr->op==TK_NOTNULL ){
+ pExpr->u.zToken = "true";
+ ExprSetProperty(pExpr, EP_IsTrue);
+ }else{
+ pExpr->u.zToken = "false";
+ ExprSetProperty(pExpr, EP_IsFalse);
+ }
+ pExpr->op = TK_TRUEFALSE;
+ for(i=0, p=pNC; p && ipNext, i++){
+ p->nRef = anRef[i];
+ }
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ }
+ return WRC_Prune;
+ }
+
/* A column name: ID
** Or table name and column name: ID.ID
** Or a database, table and column: ID.ID.ID
@@ -1391,8 +1442,7 @@ int sqlite3ResolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
- zType,0);
+ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0);
}
}
return 0;
@@ -1577,27 +1627,26 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Recursively resolve names in all subqueries
*/
for(i=0; ipSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
+ SrcItem *pItem = &p->pSrc->a[i];
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
- NameContext *pNC; /* Used to iterate name contexts */
- int nRef = 0; /* Refcount for pOuterNC and outer contexts */
+ int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
- /* Count the total number of references to pOuterNC and all of its
- ** parent contexts. After resolving references to expressions in
- ** pItem->pSelect, check if this value has changed. If so, then
- ** SELECT statement pItem->pSelect must be correlated. Set the
- ** pItem->fg.isCorrelated flag if this is the case. */
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
-
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
pParse->zAuthContext = zSavedContext;
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
- assert( pItem->fg.isCorrelated==0 && nRef<=0 );
- pItem->fg.isCorrelated = (nRef!=0);
+ /* If the number of references to the outer context changed when
+ ** expressions in the sub-select were resolved, the sub-select
+ ** is correlated. It is not required to check the refcount on any
+ ** but the innermost outer context object, as lookupName() increments
+ ** the refcount on all contexts between the current one and the
+ ** context containing the column when it resolves a name. */
+ if( pOuterNC ){
+ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
+ pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
+ }
}
}
@@ -1639,7 +1688,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** Minor point: If this is the case, then the expression will be
** re-evaluated for each reference to it.
*/
- assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
+ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 );
sNC.uNC.pEList = p->pEList;
sNC.ncFlags |= NC_UEList;
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
@@ -1647,7 +1696,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Resolve names in table-valued-function arguments */
for(i=0; ipSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
+ SrcItem *pItem = &p->pSrc->a[i];
if( pItem->fg.isTabFunc
&& sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
){
diff --git a/src/select.c b/src/select.c
index 9711257edf..75050d5740 100644
--- a/src/select.c
+++ b/src/select.c
@@ -262,7 +262,7 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
** Return the index of a column in a table. Return -1 if the column
** is not contained in the table.
*/
-static int columnIndex(Table *pTab, const char *zCol){
+int sqlite3ColumnIndex(Table *pTab, const char *zCol){
int i;
u8 h = sqlite3StrIHash(zCol);
Column *pCol;
@@ -294,7 +294,7 @@ static int tableAndColumnIndex(
assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */
for(i=0; ia[i].pTab, zCol);
+ iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol);
if( iCol>=0
&& (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0)
){
@@ -347,7 +347,7 @@ static void addWhereTerm(
ExprSetProperty(pEq, EP_FromJoin);
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(pEq, EP_NoReduce);
- pEq->iRightJoinTable = (i16)pE2->iTable;
+ pEq->iRightJoinTable = pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
}
@@ -383,7 +383,7 @@ void sqlite3SetJoinExpr(Expr *p, int iTable){
ExprSetProperty(p, EP_FromJoin);
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
- p->iRightJoinTable = (i16)iTable;
+ p->iRightJoinTable = iTable;
if( p->op==TK_FUNCTION && p->x.pList ){
int i;
for(i=0; ix.pList->nExpr; i++){
@@ -407,6 +407,9 @@ static void unsetJoinExpr(Expr *p, int iTable){
&& (iTable<0 || p->iRightJoinTable==iTable) ){
ExprClearProperty(p, EP_FromJoin);
}
+ if( p->op==TK_COLUMN && p->iTable==iTable ){
+ ExprClearProperty(p, EP_CanBeNull);
+ }
if( p->op==TK_FUNCTION && p->x.pList ){
int i;
for(i=0; ix.pList->nExpr; i++){
@@ -435,8 +438,8 @@ static void unsetJoinExpr(Expr *p, int iTable){
static int sqliteProcessJoin(Parse *pParse, Select *p){
SrcList *pSrc; /* All tables in the FROM clause */
int i, j; /* Loop counters */
- struct SrcList_item *pLeft; /* Left table being joined */
- struct SrcList_item *pRight; /* Right table being joined */
+ SrcItem *pLeft; /* Left table being joined */
+ SrcItem *pRight; /* Right table being joined */
pSrc = p->pSrc;
pLeft = &pSrc->a[0];
@@ -504,7 +507,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
int iRightCol; /* Column number of matching column on the right */
zName = pList->a[j].zName;
- iRightCol = columnIndex(pRightTab, zName);
+ iRightCol = sqlite3ColumnIndex(pRightTab, zName);
if( iRightCol<0
|| !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0)
){
@@ -2086,6 +2089,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){
const char *zType;
int n, m;
+ pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
zType = columnType(&sNC, p, 0, 0, 0);
/* pCol->szEst = ... // Column size est for SELECT tables never used */
@@ -3596,7 +3600,7 @@ static void substSelect(
int doPrior /* Do substitutes on p->pPrior too */
){
SrcList *pSrc;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
int i;
if( !p ) return;
do{
@@ -3626,7 +3630,7 @@ static void substSelect(
** pSrcItem->colUsed mask.
*/
static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
pItem = pWalker->u.pSrcItem;
if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue;
@@ -3636,7 +3640,7 @@ static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){
}
static void recomputeColumnsUsed(
Select *pSelect, /* The complete SELECT statement */
- struct SrcList_item *pSrcItem /* Which FROM clause item to recompute */
+ SrcItem *pSrcItem /* Which FROM clause item to recompute */
){
Walker w;
if( NEVER(pSrcItem->pTab==0) ) return;
@@ -3649,6 +3653,89 @@ static void recomputeColumnsUsed(
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+/*
+** Assign new cursor numbers to each of the items in pSrc. For each
+** new cursor number assigned, set an entry in the aCsrMap[] array
+** to map the old cursor number to the new:
+**
+** aCsrMap[iOld] = iNew;
+**
+** The array is guaranteed by the caller to be large enough for all
+** existing cursor numbers in pSrc.
+**
+** If pSrc contains any sub-selects, call this routine recursively
+** on the FROM clause of each such sub-select, with iExcept set to -1.
+*/
+static void srclistRenumberCursors(
+ Parse *pParse, /* Parse context */
+ int *aCsrMap, /* Array to store cursor mappings in */
+ SrcList *pSrc, /* FROM clause to renumber */
+ int iExcept /* FROM clause item to skip */
+){
+ int i;
+ SrcItem *pItem;
+ for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){
+ if( i!=iExcept ){
+ Select *p;
+ pItem->iCursor = aCsrMap[pItem->iCursor] = pParse->nTab++;
+ for(p=pItem->pSelect; p; p=p->pPrior){
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1);
+ }
+ }
+ }
+}
+
+/*
+** Expression walker callback used by renumberCursors() to update
+** Expr objects to match newly assigned cursor numbers.
+*/
+static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){
+ int *aCsrMap = pWalker->u.aiCol;
+ int op = pExpr->op;
+ if( (op==TK_COLUMN || op==TK_IF_NULL_ROW) && aCsrMap[pExpr->iTable] ){
+ pExpr->iTable = aCsrMap[pExpr->iTable];
+ }
+ if( ExprHasProperty(pExpr, EP_FromJoin) && aCsrMap[pExpr->iRightJoinTable] ){
+ pExpr->iRightJoinTable = aCsrMap[pExpr->iRightJoinTable];
+ }
+ return WRC_Continue;
+}
+
+/*
+** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc)
+** of the SELECT statement passed as the second argument, and to each
+** cursor in the FROM clause of any FROM clause sub-selects, recursively.
+** Except, do not assign a new cursor number to the iExcept'th element in
+** the FROM clause of (*p). Update all expressions and other references
+** to refer to the new cursor numbers.
+**
+** Argument aCsrMap is an array that may be used for temporary working
+** space. Two guarantees are made by the caller:
+**
+** * the array is larger than the largest cursor number used within the
+** select statement passed as an argument, and
+**
+** * the array entries for all cursor numbers that do *not* appear in
+** FROM clauses of the select statement as described above are
+** initialized to zero.
+*/
+static void renumberCursors(
+ Parse *pParse, /* Parse context */
+ Select *p, /* Select to renumber cursors within */
+ int iExcept, /* FROM clause item to skip */
+ int *aCsrMap /* Working space */
+){
+ Walker w;
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept);
+ memset(&w, 0, sizeof(w));
+ w.u.aiCol = aCsrMap;
+ w.xExprCallback = renumberCursorsCb;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ sqlite3WalkSelect(&w, p);
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
@@ -3743,9 +3830,9 @@ static void recomputeColumnsUsed(
** (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.
-** (17e) the subquery may not contain window functions
+** (17d2) DISTINCT
+** (17e) the subquery may not contain window functions, and
+** (17f) the subquery must not be the RHS of a LEFT JOIN.
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
@@ -3761,8 +3848,8 @@ static void recomputeColumnsUsed(
** 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
-** columns of the sub-query.
+** ORDER BY clause of the parent must be copies of a term returned
+** by the parent query.
**
** (19) If the subquery uses LIMIT then the outer query may not
** have a WHERE clause.
@@ -3778,9 +3865,8 @@ static void recomputeColumnsUsed(
**
** (22) The subquery may not be a recursive CTE.
**
-** (**) 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
+** (23) 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().
**
@@ -3822,9 +3908,10 @@ static int flattenSubquery(
int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
int i; /* Loop counter */
Expr *pWhere; /* The WHERE clause */
- struct SrcList_item *pSubitem; /* The subquery */
+ SrcItem *pSubitem; /* The subquery */
sqlite3 *db = pParse->db;
Walker w; /* Walker to persist agginfo data */
+ int *aCsrMap = 0;
/* Check to see if flattening is permitted. Return 0 if not.
*/
@@ -3920,13 +4007,14 @@ static int flattenSubquery(
if( pSub->pOrderBy ){
return 0; /* Restriction (20) */
}
- if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
- return 0; /* (17d1), (17d2), or (17d3) */
+ if( isAgg || (p->selFlags & SF_Distinct)!=0 || isLeftJoin>0 ){
+ return 0; /* (17d1), (17d2), or (17f) */
}
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
assert( pSub->pSrc!=0 );
+ assert( (pSub->selFlags & SF_Recursive)==0 );
assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */
|| (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */
@@ -3947,15 +4035,14 @@ static int flattenSubquery(
if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
}
}
- }
- /* 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 );
+ /* Restriction (23) */
+ if( (p->selFlags & SF_Recursive) ) return 0;
+
+ if( pSrc->nSrc>1 ){
+ aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int));
+ }
+ }
/***** If we reach this point, flattening is permitted. *****/
SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
@@ -3967,6 +4054,17 @@ static int flattenSubquery(
testcase( i==SQLITE_DENY );
pParse->zAuthContext = zSavedAuthContext;
+ /* Delete the transient structures associated with thesubquery */
+ pSub1 = pSubitem->pSelect;
+ sqlite3DbFree(db, pSubitem->zDatabase);
+ sqlite3DbFree(db, pSubitem->zName);
+ sqlite3DbFree(db, pSubitem->zAlias);
+ pSubitem->zDatabase = 0;
+ pSubitem->zName = 0;
+ pSubitem->zAlias = 0;
+ pSubitem->pSelect = 0;
+ assert( pSubitem->pOn==0 );
+
/* If the sub-query is a compound SELECT statement, then (by restrictions
** 17 and 18 above) it must be a UNION ALL and the parent query must
** be of the form:
@@ -4005,18 +4103,22 @@ static int flattenSubquery(
ExprList *pOrderBy = p->pOrderBy;
Expr *pLimit = p->pLimit;
Select *pPrior = p->pPrior;
+ Table *pItemTab = pSubitem->pTab;
+ pSubitem->pTab = 0;
p->pOrderBy = 0;
- p->pSrc = 0;
p->pPrior = 0;
p->pLimit = 0;
pNew = sqlite3SelectDup(db, p, 0);
p->pLimit = pLimit;
p->pOrderBy = pOrderBy;
- p->pSrc = pSrc;
p->op = TK_ALL;
+ pSubitem->pTab = pItemTab;
if( pNew==0 ){
p->pPrior = pPrior;
}else{
+ if( aCsrMap && db->mallocFailed==0 ){
+ renumberCursors(pParse, pNew, iFrom, aCsrMap);
+ }
pNew->pPrior = pPrior;
if( pPrior ) pPrior->pNext = pNew;
pNew->pNext = p;
@@ -4024,24 +4126,13 @@ static int flattenSubquery(
SELECTTRACE(2,pParse,p,("compound-subquery flattener"
" creates %u as peer\n",pNew->selId));
}
- if( db->mallocFailed ) return 1;
+ assert( pSubitem->pSelect==0 );
+ }
+ sqlite3DbFree(db, aCsrMap);
+ if( db->mallocFailed ){
+ pSubitem->pSelect = pSub1;
+ return 1;
}
-
- /* Begin flattening the iFrom-th entry of the FROM clause
- ** in the outer query.
- */
- pSub = pSub1 = pSubitem->pSelect;
-
- /* Delete the transient table structure associated with the
- ** subquery
- */
- sqlite3DbFree(db, pSubitem->zDatabase);
- sqlite3DbFree(db, pSubitem->zName);
- sqlite3DbFree(db, pSubitem->zAlias);
- pSubitem->zDatabase = 0;
- pSubitem->zName = 0;
- pSubitem->zAlias = 0;
- pSubitem->pSelect = 0;
/* Defer deleting the Table object associated with the
** subquery until code generation is
@@ -4054,8 +4145,10 @@ static int flattenSubquery(
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- pTabToDel->pNextZombie = pToplevel->pZombieTab;
- pToplevel->pZombieTab = pTabToDel;
+ sqlite3ParserAddCleanup(pToplevel,
+ (void(*)(sqlite3*,void*))sqlite3DeleteTable,
+ pTabToDel);
+ testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
}
@@ -4075,6 +4168,7 @@ static int flattenSubquery(
** those references with expressions that resolve to the subquery FROM
** elements we are now copying in.
*/
+ pSub = pSub1;
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = 0;
@@ -4083,16 +4177,10 @@ static int flattenSubquery(
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
pSrc = pParent->pSrc; /* FROM clause of the outer query */
- if( pSrc ){
- assert( pParent==p ); /* First time through the loop */
- jointype = pSubitem->fg.jointype;
- }else{
- assert( pParent!=p ); /* 2nd and subsequent times through the loop */
- pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
- if( pSrc==0 ) break;
- pParent->pSrc = pSrc;
+ if( pParent==p ){
+ jointype = pSubitem->fg.jointype; /* First time through the loop */
}
-
+
/* The subquery uses a single slot of the FROM clause of the outer
** query. If the subquery has more than one element in its FROM clause,
** then expand the outer query to make space for it to hold all elements
@@ -4210,7 +4298,7 @@ static int flattenSubquery(
sqlite3SelectDelete(db, pSub1);
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x100 ){
+ if( sqlite3SelectTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -4404,6 +4492,35 @@ static int propagateConstants(
return nChng;
}
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+# if !defined(SQLITE_OMIT_WINDOWFUNC)
+/*
+** This function is called to determine whether or not it is safe to
+** push WHERE clause expression pExpr down to FROM clause sub-query
+** pSubq, which contains at least one window function. Return 1
+** if it is safe and the expression should be pushed down, or 0
+** otherwise.
+**
+** It is only safe to push the expression down if it consists only
+** of constants and copies of expressions that appear in the PARTITION
+** BY clause of all window function used by the sub-query. It is safe
+** to filter out entire partitions, but not rows within partitions, as
+** this may change the results of the window functions.
+**
+** At the time this function is called it is guaranteed that
+**
+** * the sub-query uses only one distinct window frame, and
+** * that the window frame has a PARTITION BY clase.
+*/
+static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
+ assert( pSubq->pWin->pPartition );
+ assert( (pSubq->selFlags & SF_MultiPart)==0 );
+ assert( pSubq->pPrior==0 );
+ return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition);
+}
+# endif /* SQLITE_OMIT_WINDOWFUNC */
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** Make copies of relevant WHERE clause terms of the outer query into
@@ -4451,9 +4568,24 @@ static int propagateConstants(
** 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).
+** (6) Window functions make things tricky as changes to the WHERE clause
+** of the inner query could change the window over which window
+** functions are calculated. Therefore, do not attempt the optimization
+** if:
+**
+** (6a) The inner query uses multiple incompatible window partitions.
+**
+** (6b) The inner query is a compound and uses window-functions.
+**
+** (6c) The WHERE clause does not consist entirely of constants and
+** copies of expressions found in the PARTITION BY clause of
+** all window-functions used by the sub-query. It is safe to
+** filter out entire partitions, as this does not change the
+** window over which any window-function is calculated.
+**
+** (7) The inner query is a Common Table Expression (CTE) that should
+** be materialized. (This restriction is implemented in the calling
+** routine.)
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
@@ -4467,13 +4599,17 @@ static int pushDownWhereTerms(
){
Expr *pNew;
int nChng = 0;
- Select *pSel;
if( pWhere==0 ) return 0;
- if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */
+ if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
- for(pSel=pSubq; pSel; pSel=pSel->pPrior){
- if( pSel->pWin ) return 0; /* restriction (6) */
+ if( pSubq->pPrior ){
+ Select *pSel;
+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){
+ if( pSel->pWin ) return 0; /* restriction (6b) */
+ }
+ }else{
+ if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
}
#endif
@@ -4509,6 +4645,7 @@ static int pushDownWhereTerms(
}
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
nChng++;
+ pSubq->selFlags |= SF_PushDown;
while( pSubq ){
SubstContext x;
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
@@ -4519,6 +4656,14 @@ static int pushDownWhereTerms(
x.isLeftJoin = 0;
x.pEList = pSubq->pEList;
pNew = substExpr(&x, pNew);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
+ /* Restriction 6c has prevented push-down in this case */
+ sqlite3ExprDelete(pParse->db, pNew);
+ nChng--;
+ break;
+ }
+#endif
if( pSubq->selFlags & SF_Aggregate ){
pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
}else{
@@ -4557,7 +4702,11 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
assert( !IsWindowFunc(pFunc) );
- if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){
+ if( pEList==0
+ || pEList->nExpr!=1
+ || ExprHasProperty(pFunc, EP_WinFunc)
+ || OptimizationDisabled(db, SQLITE_MinMaxOpt)
+ ){
return eRet;
}
zFunc = pFunc->u.zToken;
@@ -4620,24 +4769,26 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
** SQLITE_ERROR and leave an error in pParse. Otherwise, populate
** pFrom->pIndex and return SQLITE_OK.
*/
-int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
- if( pFrom->pTab && pFrom->fg.isIndexedBy ){
- Table *pTab = pFrom->pTab;
- char *zIndexedBy = pFrom->u1.zIndexedBy;
- Index *pIdx;
- for(pIdx=pTab->pIndex;
- pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
- pIdx=pIdx->pNext
- );
- if( !pIdx ){
- sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
- pParse->checkSchema = 1;
- return SQLITE_ERROR;
- }
- pFrom->pIBIndex = pIdx;
+int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){
+ Table *pTab = pFrom->pTab;
+ char *zIndexedBy = pFrom->u1.zIndexedBy;
+ Index *pIdx;
+ assert( pTab!=0 );
+ assert( pFrom->fg.isIndexedBy!=0 );
+
+ for(pIdx=pTab->pIndex;
+ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
+ pIdx=pIdx->pNext
+ );
+ if( !pIdx ){
+ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
+ pParse->checkSchema = 1;
+ return SQLITE_ERROR;
}
+ pFrom->u2.pIBIndex = pIdx;
return SQLITE_OK;
}
+
/*
** Detect compound SELECT statements that use an ORDER BY clause with
** an alternative collating sequence.
@@ -4724,7 +4875,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
** arguments. If it does, leave an error message in pParse and return
** non-zero, since pFrom is not allowed to be a table-valued function.
*/
-static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
+static int cannotBeFunction(Parse *pParse, SrcItem *pFrom){
if( pFrom->fg.isTabFunc ){
sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
return 1;
@@ -4745,19 +4896,19 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
*/
static struct Cte *searchWith(
With *pWith, /* Current innermost WITH clause */
- struct SrcList_item *pItem, /* FROM clause element to resolve */
+ SrcItem *pItem, /* FROM clause element to resolve */
With **ppContext /* OUT: WITH clause return value belongs to */
){
- const char *zName;
- if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){
- With *p;
- for(p=pWith; p; p=p->pOuter){
- int i;
- for(i=0; inCte; i++){
- if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
- *ppContext = p;
- return &p->a[i];
- }
+ const char *zName = pItem->zName;
+ With *p;
+ assert( pItem->zDatabase==0 );
+ assert( zName!=0 );
+ for(p=pWith; p; p=p->pOuter){
+ int i;
+ for(i=0; inCte; i++){
+ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
+ *ppContext = p;
+ return &p->a[i];
}
}
}
@@ -4775,46 +4926,54 @@ static struct Cte *searchWith(
** statement with which it is associated.
*/
void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
- assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
if( pWith ){
assert( pParse->pWith!=pWith );
pWith->pOuter = pParse->pWith;
pParse->pWith = pWith;
- if( bFree ) pParse->pWithToFree = pWith;
+ if( bFree ){
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3WithDelete,
+ pWith);
+ testcase( pParse->earlyCleanup );
+ }
}
}
/*
** This function checks if argument pFrom refers to a CTE declared by
-** a WITH clause on the stack currently maintained by the parser. And,
-** if currently processing a CTE expression, if it is a recursive
-** reference to the current CTE.
+** a WITH clause on the stack currently maintained by the parser (on the
+** pParse->pWith linked list). And if currently processing a CTE
+** CTE expression, through routine checks to see if the reference is
+** a recursive reference to the CTE.
**
-** If pFrom falls into either of the two categories above, pFrom->pTab
-** and other fields are populated accordingly. The caller should check
-** (pFrom->pTab!=0) to determine whether or not a successful match
-** was found.
+** If pFrom matches a CTE according to either of these two above, pFrom->pTab
+** and other fields are populated accordingly.
**
-** Whether or not a match is found, SQLITE_OK is returned if no error
-** occurs. If an error does occur, an error message is stored in the
-** parser and some error code other than SQLITE_OK returned.
+** Return 0 if no match is found.
+** Return 1 if a match is found.
+** Return 2 if an error condition is detected.
*/
-static int withExpand(
- Walker *pWalker,
- struct SrcList_item *pFrom
+static int resolveFromTermToCte(
+ Parse *pParse, /* The parsing context */
+ Walker *pWalker, /* Current tree walker */
+ SrcItem *pFrom /* The FROM clause term to check */
){
- Parse *pParse = pWalker->pParse;
- sqlite3 *db = pParse->db;
- struct Cte *pCte; /* Matched CTE (or NULL if no match) */
- With *pWith; /* WITH clause that pCte belongs to */
+ Cte *pCte; /* Matched CTE (or NULL if no match) */
+ With *pWith; /* The matching WITH */
assert( pFrom->pTab==0 );
- if( pParse->nErr ){
- return SQLITE_ERROR;
+ if( pParse->pWith==0 ){
+ /* There are no WITH clauses in the stack. No match is possible */
+ return 0;
+ }
+ if( pFrom->zDatabase!=0 ){
+ /* The FROM term contains a schema qualifier (ex: main.t1) and so
+ ** it cannot possibly be a CTE reference. */
+ return 0;
}
-
pCte = searchWith(pParse->pWith, pFrom, &pWith);
if( pCte ){
+ sqlite3 *db = pParse->db;
Table *pTab;
ExprList *pEList;
Select *pSel;
@@ -4823,6 +4982,7 @@ static int withExpand(
int bMayRecursive; /* True if compound joined by UNION [ALL] */
With *pSavedWith; /* Initial value of pParse->pWith */
int iRecTab = -1; /* Cursor for recursive table */
+ CteUse *pCteUse;
/* If pCte->zCteErr is non-NULL at this point, then this is an illegal
** recursive reference to CTE pCte. Leave an error in pParse and return
@@ -4830,21 +4990,39 @@ static int withExpand(
** In this case, proceed. */
if( pCte->zCteErr ){
sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName);
- return SQLITE_ERROR;
+ return 2;
}
- if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR;
+ if( cannotBeFunction(pParse, pFrom) ) return 2;
assert( pFrom->pTab==0 );
- pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
- if( pTab==0 ) return WRC_Abort;
+ pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return 2;
+ pCteUse = pCte->pUse;
+ if( pCteUse==0 ){
+ pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0]));
+ if( pCteUse==0
+ || sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0
+ ){
+ sqlite3DbFree(db, pTab);
+ return 2;
+ }
+ pCteUse->eM10d = pCte->eM10d;
+ }
+ pFrom->pTab = pTab;
pTab->nTabRef = 1;
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ if( db->mallocFailed ) return 2;
assert( pFrom->pSelect );
+ pFrom->fg.isCte = 1;
+ pFrom->u2.pCteUse = pCteUse;
+ pCteUse->nUse++;
+ if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){
+ pCteUse->eM10d = M10d_Yes;
+ }
/* Check if this is a recursive CTE. */
pRecTerm = pSel = pFrom->pSelect;
@@ -4854,7 +5032,7 @@ static int withExpand(
SrcList *pSrc = pRecTerm->pSrc;
assert( pRecTerm->pPrior!=0 );
for(i=0; inSrc; i++){
- struct SrcList_item *pItem = &pSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i];
if( pItem->zDatabase==0
&& pItem->zName!=0
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
@@ -4866,7 +5044,7 @@ static int withExpand(
sqlite3ErrorMsg(pParse,
"multiple references to recursive table: %s", pCte->zName
);
- return SQLITE_ERROR;
+ return 2;
}
pRecTerm->selFlags |= SF_Recursive;
if( iRecTab<0 ) iRecTab = pParse->nTab++;
@@ -4902,7 +5080,7 @@ static int withExpand(
pCte->zName, pEList->nExpr, pCte->pCols->nExpr
);
pParse->pWith = pSavedWith;
- return SQLITE_ERROR;
+ return 2;
}
pEList = pCte->pCols;
}
@@ -4918,9 +5096,9 @@ static int withExpand(
}
pCte->zCteErr = 0;
pParse->pWith = pSavedWith;
+ return 1; /* Success */
}
-
- return SQLITE_OK;
+ return 0; /* No match */
}
#endif
@@ -4954,7 +5132,7 @@ static void selectPopWith(Walker *pWalker, Select *p){
** SQLITE_OK is returned. Otherwise, if an OOM error is encountered,
** SQLITE_NOMEM.
*/
-int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
+int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
Select *pSel = pFrom->pSelect;
Table *pTab;
@@ -5002,10 +5180,10 @@ int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
*/
static int selectExpander(Walker *pWalker, Select *p){
Parse *pParse = pWalker->pParse;
- int i, j, k;
+ int i, j, k, rc;
SrcList *pTabList;
ExprList *pEList;
- struct SrcList_item *pFrom;
+ SrcItem *pFrom;
sqlite3 *db = pParse->db;
Expr *pE, *pRight, *pExpr;
u16 selFlags = p->selFlags;
@@ -5041,10 +5219,6 @@ static int selectExpander(Walker *pWalker, Select *p){
assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
if( pFrom->pTab ) continue;
assert( pFrom->fg.isRecursive==0 );
-#ifndef SQLITE_OMIT_CTE
- if( withExpand(pWalker, pFrom) ) return WRC_Abort;
- if( pFrom->pTab ) {} else
-#endif
if( pFrom->zName==0 ){
#ifndef SQLITE_OMIT_SUBQUERY
Select *pSel = pFrom->pSelect;
@@ -5053,6 +5227,12 @@ static int selectExpander(Walker *pWalker, Select *p){
assert( pFrom->pTab==0 );
if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort;
+#endif
+#ifndef SQLITE_OMIT_CTE
+ }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){
+ if( rc>1 ) return WRC_Abort;
+ pTab = pFrom->pTab;
+ assert( pTab!=0 );
#endif
}else{
/* An ordinary table or view name in the FROM clause */
@@ -5101,7 +5281,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
/* Locate the index named by the INDEXED BY clause, if any. */
- if( sqlite3IndexedByLookup(pParse, pFrom) ){
+ if( pFrom->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pFrom) ){
return WRC_Abort;
}
}
@@ -5344,7 +5524,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Parse *pParse;
int i;
SrcList *pTabList;
- struct SrcList_item *pFrom;
+ SrcItem *pFrom;
assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
@@ -5615,7 +5795,9 @@ static void explainSimpleCount(
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op!=TK_AND ){
Select *pS = pWalker->u.pSelect;
- if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
+ if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy)
+ && ExprAlwaysFalse(pExpr)==0
+ ){
sqlite3 *db = pWalker->pParse->db;
Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
if( pNew ){
@@ -5654,7 +5836,7 @@ static void havingToWhere(Parse *pParse, Select *p){
sWalker.u.pSelect = p;
sqlite3WalkExpr(&sWalker, p->pHaving);
#if SELECTTRACE_ENABLED
- if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){
+ if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){
SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -5666,11 +5848,13 @@ static void havingToWhere(Parse *pParse, Select *p){
** If it is, then return the SrcList_item for the prior view. If it is not,
** then return 0.
*/
-static struct SrcList_item *isSelfJoinView(
+static SrcItem *isSelfJoinView(
SrcList *pTabList, /* Search for self-joins in this FROM clause */
- struct SrcList_item *pThis /* Search for prior reference to this subquery */
+ SrcItem *pThis /* Search for prior reference to this subquery */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
+ assert( pThis->pSelect!=0 );
+ if( pThis->pSelect->selFlags & SF_PushDown ) return 0;
for(pItem = pTabList->a; pItempSelect==0 ) continue;
@@ -5686,9 +5870,7 @@ static struct SrcList_item *isSelfJoinView(
** names in the same FROM clause. */
continue;
}
- if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1)
- || sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1)
- ){
+ if( pItem->pSelect->selFlags & SF_PushDown ){
/* The view was modified by some other optimization such as
** pushDownWhereTerms() */
continue;
@@ -5698,6 +5880,15 @@ static struct SrcList_item *isSelfJoinView(
return 0;
}
+/*
+** Deallocate a single AggInfo object
+*/
+static void agginfoFree(sqlite3 *db, AggInfo *p){
+ sqlite3DbFree(db, p->aCol);
+ sqlite3DbFree(db, p->aFunc);
+ sqlite3DbFreeNN(db, p);
+}
+
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
/*
** Attempt to transform a query of the form
@@ -5776,7 +5967,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
p->selFlags &= ~SF_Aggregate;
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x400 ){
+ if( sqlite3SelectTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -5829,7 +6020,7 @@ int sqlite3Select(
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
#if SELECTTRACE_ENABLED
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
- if( sqlite3_unsupported_selecttrace & 0x100 ){
+ if( sqlite3SelectTrace & 0x100 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -5843,8 +6034,18 @@ int sqlite3Select(
pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo );
/* All of these destinations are also able to ignore the ORDER BY clause */
- sqlite3ExprListDelete(db, p->pOrderBy);
- p->pOrderBy = 0;
+ if( p->pOrderBy ){
+#if SELECTTRACE_ENABLED
+ SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
+ if( sqlite3SelectTrace & 0x100 ){
+ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
+ }
+#endif
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
+ p->pOrderBy);
+ p->pOrderBy = 0;
+ }
p->selFlags &= ~SF_Distinct;
p->selFlags |= SF_NoopOrderBy;
}
@@ -5854,7 +6055,7 @@ int sqlite3Select(
}
assert( p->pEList!=0 );
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x104 ){
+ if( sqlite3SelectTrace & 0x104 ){
SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -5865,9 +6066,9 @@ int sqlite3Select(
** In this case, it is an error if the target object (pSrc->a[0]) name
** or alias is duplicated within FROM clause (pSrc->a[1..n]). */
if( p->selFlags & SF_UpdateFrom ){
- struct SrcList_item *p0 = &p->pSrc->a[0];
+ SrcItem *p0 = &p->pSrc->a[0];
for(i=1; ipSrc->nSrc; i++){
- struct SrcList_item *p1 = &p->pSrc->a[i];
+ SrcItem *p1 = &p->pSrc->a[i];
if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
sqlite3ErrorMsg(pParse,
"target object/alias may not appear in FROM clause: %s",
@@ -5889,7 +6090,7 @@ int sqlite3Select(
goto select_end;
}
#if SELECTTRACE_ENABLED
- if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){
+ if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){
SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -5905,7 +6106,7 @@ int sqlite3Select(
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
for(i=0; !p->pPrior && inSrc; i++){
- struct SrcList_item *pItem = &pTabList->a[i];
+ SrcItem *pItem = &pTabList->a[i];
Select *pSub = pItem->pSelect;
Table *pTab = pItem->pTab;
@@ -5996,7 +6197,7 @@ int sqlite3Select(
rc = multiSelect(pParse, p, pDest);
#if SELECTTRACE_ENABLED
SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
- if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+ if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6015,7 +6216,7 @@ int sqlite3Select(
&& propagateConstants(pParse, p)
){
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x100 ){
+ if( sqlite3SelectTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -6039,7 +6240,8 @@ int sqlite3Select(
** (2) Generate code for all sub-queries
*/
for(i=0; inSrc; i++){
- struct SrcList_item *pItem = &pTabList->a[i];
+ SrcItem *pItem = &pTabList->a[i];
+ SrcItem *pPrior;
SelectDest dest;
Select *pSub;
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
@@ -6099,16 +6301,18 @@ int sqlite3Select(
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( OptimizationEnabled(db, SQLITE_PushDown)
+ && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes)
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
(pItem->fg.jointype & JT_OUTER)!=0)
){
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x100 ){
+ if( sqlite3SelectTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
+ assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
}
@@ -6118,16 +6322,18 @@ int sqlite3Select(
/* Generate code to implement the subquery
**
- ** 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)
+ ** The subquery is implemented as a co-routine if:
+ ** (1) the subquery is guaranteed to be the outer loop (so that
+ ** it does not need to be computed more than once), and
+ ** (2) the subquery is not a CTE that should be materialized
**
- ** TODO: Are there other reasons beside (1) to use a co-routine
+ ** TODO: Are there other reasons beside (1) and (2) to use a co-routine
** implementation?
*/
if( i==0
&& (pTabList->nSrc==1
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
+ && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
@@ -6147,16 +6353,30 @@ int sqlite3Select(
sqlite3VdbeEndCoroutine(v, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
+ }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){
+ /* This is a CTE for which materialization code has already been
+ ** generated. Invoke the subroutine to compute the materialization,
+ ** the make the pItem->iCursor be a copy of the ephemerial table that
+ ** holds the result of the materialization. */
+ CteUse *pCteUse = pItem->u2.pCteUse;
+ sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e);
+ if( pItem->iCursor!=pCteUse->iCur ){
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur);
+ }
+ pSub->nSelectRow = pCteUse->nRowEst;
+ }else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){
+ /* This view has already been materialized by a prior entry in
+ ** this same FROM clause. Reuse it. */
+ if( pPrior->addrFillSub ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
+ }
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
+ pSub->nSelectRow = pPrior->pSelect->nSelectRow;
}else{
- /* Generate a subroutine that will fill an ephemeral table with
- ** the content of this subquery. pItem->addrFillSub will point
- ** to the address of the generated subroutine. pItem->regReturn
- ** is a register allocated to hold the subroutine return address
- */
+ /* Generate a subroutine that will materialize the view. */
int topAddr;
int onceAddr = 0;
int retAddr;
- struct SrcList_item *pPrior;
testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
pItem->regReturn = ++pParse->nMem;
@@ -6171,22 +6391,22 @@ int sqlite3Select(
}else{
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
}
- pPrior = isSelfJoinView(pTabList, pItem);
- if( pPrior ){
- sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
- assert( pPrior->pSelect!=0 );
- pSub->nSelectRow = pPrior->pSelect->nSelectRow;
- }else{
- sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
- sqlite3Select(pParse, pSub, &dest);
- }
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
+ ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
+ sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
sqlite3ClearTempRegCache(pParse);
+ if( pItem->fg.isCte ){
+ CteUse *pCteUse = pItem->u2.pCteUse;
+ pCteUse->addrM9e = pItem->addrFillSub;
+ pCteUse->regRtn = pItem->regReturn;
+ pCteUse->iCur = pItem->iCursor;
+ pCteUse->nRowEst = pSub->nSelectRow;
+ }
}
if( db->mallocFailed ) goto select_end;
pParse->nHeight -= sqlite3SelectExprHeight(p);
@@ -6203,7 +6423,7 @@ int sqlite3Select(
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x400 ){
+ if( sqlite3SelectTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -6239,7 +6459,7 @@ int sqlite3Select(
assert( sDistinct.isTnct );
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x400 ){
+ if( sqlite3SelectTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
@@ -6331,6 +6551,7 @@ int sqlite3Select(
sSort.pOrderBy = 0;
}
}
+ SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
@@ -6369,6 +6590,7 @@ int sqlite3Select(
/* End the database scan loop.
*/
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
}
}else{
@@ -6439,11 +6661,13 @@ int sqlite3Select(
** SELECT statement.
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
- if( pAggInfo==0 ){
+ if( pAggInfo ){
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
+ }
+ if( db->mallocFailed ){
goto select_end;
}
- pAggInfo->pNext = pParse->pAggList;
- pParse->pAggList = pAggInfo;
pAggInfo->selId = p->selId;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
@@ -6487,10 +6711,14 @@ int sqlite3Select(
pAggInfo->mxReg = pParse->nMem;
if( db->mallocFailed ) goto select_end;
#if SELECTTRACE_ENABLED
- if( sqlite3_unsupported_selecttrace & 0x400 ){
+ if( sqlite3SelectTrace & 0x400 ){
int ii;
SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
sqlite3TreeViewSelect(0, p, 0);
+ if( minMaxFlag ){
+ sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
+ sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
+ }
for(ii=0; iinColumn; ii++){
sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
ii, pAggInfo->aCol[ii].iMem);
@@ -6558,6 +6786,7 @@ int sqlite3Select(
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
);
if( pWInfo==0 ) goto select_end;
+ SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
@@ -6606,6 +6835,7 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
sortOut = sqlite3GetTempReg(pParse);
@@ -6680,9 +6910,10 @@ int sqlite3Select(
/* End of the loop
*/
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
VdbeCoverage(v);
}else{
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
}
@@ -6792,7 +7023,6 @@ int sqlite3Select(
explainSimpleCount(pParse, pTab, pBest);
}else{
int regAcc = 0; /* "populate accumulators" flag */
- int addrSkip;
/* If there are accumulator registers but no min() or max() functions
** without FILTER clauses, allocate register regAcc. Register regAcc
@@ -6839,12 +7069,13 @@ int sqlite3Select(
if( pWInfo==0 ){
goto select_end;
}
+ SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
updateAccumulator(pParse, regAcc, pAggInfo);
if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
- addrSkip = sqlite3WhereOrderByLimitOptLabel(pWInfo);
- if( addrSkip!=sqlite3WhereContinueLabel(pWInfo) ){
- sqlite3VdbeGoto(v, addrSkip);
+ if( minMaxFlag ){
+ sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
}
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, pAggInfo);
}
@@ -6889,15 +7120,13 @@ select_end:
if( pAggInfo && !db->mallocFailed ){
for(i=0; inColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
- assert( pExpr!=0 || db->mallocFailed );
- if( pExpr==0 ) continue;
+ assert( pExpr!=0 );
assert( pExpr->pAggInfo==pAggInfo );
assert( pExpr->iAgg==i );
}
for(i=0; inFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
- assert( pExpr!=0 || db->mallocFailed );
- if( pExpr==0 ) continue;
+ assert( pExpr!=0 );
assert( pExpr->pAggInfo==pAggInfo );
assert( pExpr->iAgg==i );
}
@@ -6906,7 +7135,7 @@ select_end:
#if SELECTTRACE_ENABLED
SELECTTRACE(0x1,pParse,p,("end processing\n"));
- if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+ if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
diff --git a/src/shell.c.in b/src/shell.c.in
index 2d98d23c2d..03c4fd319e 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -1085,12 +1085,12 @@ struct ShellState {
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
u8 autoEQPtest; /* autoEQP is in test mode */
u8 autoEQPtrace; /* autoEQP is in trace 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 */
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
+ unsigned statsOn; /* True to display memory stats before each finalize */
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 */
@@ -2027,6 +2027,7 @@ static int shell_callback(
if( azArg==0 ) break;
for(i=0; iw ){
w = strlenChar(azArg[i]);
}
@@ -2591,7 +2592,7 @@ static int display_stats(
if( pArg==0 || pArg->out==0 ) return 0;
out = pArg->out;
- if( pArg->pStmt && (pArg->statsOn & 2) ){
+ if( pArg->pStmt && pArg->statsOn==2 ){
int nCol, i, x;
sqlite3_stmt *pStmt = pArg->pStmt;
char z[100];
@@ -2615,6 +2616,14 @@ static int display_stats(
}
}
+ if( pArg->statsOn==3 ){
+ if( pArg->pStmt ){
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
+ raw_printf(pArg->out, "VM-steps: %d\n", iCur);
+ }
+ return 0;
+ }
+
displayStatLine(pArg, "Memory Used:",
"%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
displayStatLine(pArg, "Number of Outstanding Allocations:",
@@ -2881,31 +2890,18 @@ static void explain_data_delete(ShellState *p){
/*
** Disable and restore .wheretrace and .selecttrace settings.
*/
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
-extern unsigned int sqlite3_unsupported_selecttrace;
-static int savedSelectTrace;
-#endif
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
-extern int sqlite3WhereTrace;
-static int savedWhereTrace;
-#endif
+static unsigned int savedSelectTrace;
+static unsigned int savedWhereTrace;
static void disable_debug_trace_modes(void){
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
- savedSelectTrace = sqlite3_unsupported_selecttrace;
- sqlite3_unsupported_selecttrace = 0;
-#endif
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
- savedWhereTrace = sqlite3WhereTrace;
- sqlite3WhereTrace = 0;
-#endif
+ unsigned int zero = 0;
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 0, &savedSelectTrace);
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &zero);
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 2, &savedWhereTrace);
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &zero);
}
static void restore_debug_trace_modes(void){
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
- sqlite3_unsupported_selecttrace = savedSelectTrace;
-#endif
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
- sqlite3WhereTrace = savedWhereTrace;
-#endif
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &savedSelectTrace);
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &savedWhereTrace);
}
/* Create the TEMP table used to store parameter bindings */
@@ -4060,7 +4056,11 @@ static const char *(azHelp[]) = {
".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",
+ ".stats ?ARG? Show stats or turn stats on or off",
+ " off Turn off automatic stat display",
+ " on Turn on automatic stat display",
+ " stmt Show statement stats",
+ " vmstep Show the virtual machine step count only",
#ifndef SQLITE_NOHAVE_SYSTEM
".system CMD ARGS... Run CMD ARGS... in a system shell",
#endif
@@ -7876,16 +7876,17 @@ static int do_meta_command(char *zLine, ShellState *p){
int ctrlCode; /* Integer code for that option */
const char *zUsage; /* Usage notes */
} aCtrl[] = {
- { "size_limit", SQLITE_FCNTL_SIZE_LIMIT, "[LIMIT]" },
{ "chunk_size", SQLITE_FCNTL_CHUNK_SIZE, "SIZE" },
- /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY, "COUNT DELAY" },*/
- { "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" },
- { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
- /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
- { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
+ { "data_version", SQLITE_FCNTL_DATA_VERSION, "" },
{ "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
{ "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" },
+ { "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" },
+ /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
+ { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
{ "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" },
+ { "size_limit", SQLITE_FCNTL_SIZE_LIMIT, "[LIMIT]" },
+ { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
+ /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY, "COUNT DELAY" },*/
};
int filectrl = -1;
int iCtrl = -1;
@@ -7972,6 +7973,7 @@ static int do_meta_command(char *zLine, ShellState *p){
isOk = 1;
break;
}
+ case SQLITE_FCNTL_DATA_VERSION:
case SQLITE_FCNTL_HAS_MOVED: {
int x;
if( nArg!=2 ) break;
@@ -8680,9 +8682,9 @@ static int do_meta_command(char *zLine, ShellState *p){
#endif /* SQLITE_DEBUG */
if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
- char *zNewFilename; /* Name of the database file to open */
- int iName = 1; /* Index in azArg[] of the filename */
- int newFlag = 0; /* True to delete file before opening */
+ char *zNewFilename = 0; /* Name of the database file to open */
+ int iName = 1; /* Index in azArg[] of the filename */
+ int newFlag = 0; /* True to delete file before opening */
/* Close the existing database */
session_close_all(p);
close_db(p->db);
@@ -8694,7 +8696,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->openFlags = 0;
p->szMax = 0;
/* Check for command-line arguments */
- for(iName=1; iNameiName ? sqlite3_mprintf("%s", azArg[iName]) : 0;
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag ) shellDeleteFile(zNewFilename);
p->zDbFilename = zNewFilename;
@@ -8746,7 +8753,7 @@ static int do_meta_command(char *zLine, ShellState *p){
&& (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
){
- const char *zFile = 0;
+ char *zFile = 0;
int bTxtMode = 0;
int i;
int eMode = 0;
@@ -8776,17 +8783,22 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1;
goto meta_command_exit;
}
- }else if( zFile==0 ){
- zFile = z;
+ }else if( zFile==0 && eMode!='e' && eMode!='x' ){
+ zFile = sqlite3_mprintf("%s", z);
+ if( zFile[0]=='|' ){
+ while( i+1out,"ERROR: extra parameter: \"%s\". Usage:\n",
azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
+ sqlite3_free(zFile);
goto meta_command_exit;
}
}
- if( zFile==0 ) zFile = "stdout";
+ if( zFile==0 ) zFile = sqlite3_mprintf("stdout");
if( bOnce ){
p->outCount = 2;
}else{
@@ -8809,7 +8821,8 @@ static int do_meta_command(char *zLine, ShellState *p){
newTempFile(p, "txt");
bTxtMode = 1;
}
- zFile = p->zTempFile;
+ sqlite3_free(zFile);
+ zFile = sqlite3_mprintf("%s", p->zTempFile);
}
#endif /* SQLITE_NOHAVE_SYSTEM */
if( zFile[0]=='|' ){
@@ -8841,6 +8854,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
+ sqlite3_free(zFile);
}else
if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){
@@ -9246,11 +9260,10 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
- sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff;
+ unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x);
}else
-#endif
#if defined(SQLITE_ENABLE_SESSION)
if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
@@ -9731,6 +9744,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
static const char *azBool[] = { "off", "on", "trigger", "full"};
+ const char *zOut;
int i;
if( nArg!=1 ){
raw_printf(stderr, "Usage: .show\n");
@@ -9755,7 +9769,13 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(p->out,"%12.12s: ", "rowseparator");
output_c_string(p->out, p->rowSeparator);
raw_printf(p->out, "\n");
- utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]);
+ switch( p->statsOn ){
+ case 0: zOut = "off"; break;
+ default: zOut = "on"; break;
+ case 2: zOut = "stmt"; break;
+ case 3: zOut = "vmstep"; break;
+ }
+ utf8_printf(p->out, "%12.12s: %s\n","stats", zOut);
utf8_printf(p->out, "%12.12s: ", "width");
for (i=0;inWidth;i++) {
raw_printf(p->out, "%d ", p->colWidth[i]);
@@ -9767,11 +9787,17 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
if( nArg==2 ){
- p->statsOn = (u8)booleanValue(azArg[1]);
+ if( strcmp(azArg[1],"stmt")==0 ){
+ p->statsOn = 2;
+ }else if( strcmp(azArg[1],"vmstep")==0 ){
+ p->statsOn = 3;
+ }else{
+ p->statsOn = (u8)booleanValue(azArg[1]);
+ }
}else if( nArg==1 ){
display_stats(p->db, p, 0);
}else{
- raw_printf(stderr, "Usage: .stats ?on|off?\n");
+ raw_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n");
rc = 1;
}
}else
@@ -9976,7 +10002,7 @@ static int do_meta_command(char *zLine, ShellState *p){
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_OPTIMIZATIONS:
if( nArg==3 ){
- int opt = (int)strtol(azArg[2], 0, 0);
+ unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0);
rc2 = sqlite3_test_control(testctrl, p->db, opt);
isOk = 3;
}
@@ -10305,11 +10331,10 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
-#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
- sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
+ unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
+ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x);
}else
-#endif
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 71b4325878..6ebde28174 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -2115,7 +2115,13 @@ struct sqlite3_mem_methods {
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
-** which case the trigger setting is not reported back.
+** which case the trigger setting is not reported back.
+**
+** Originally this option disabled all triggers. ^(However, since
+** SQLite version 3.35.0, TEMP triggers are still allowed even if
+** this option is off. So, in other words, this option now only disables
+** triggers in the main database schema or in the schemas of ATTACH-ed
+** databases.)^
**
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
**
SQLITE_DBCONFIG_ENABLE_VIEW
@@ -3499,6 +3505,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** that uses dot-files in place of posix advisory locking.
** file:data.db?mode=readonly |
** An error. "readonly" is not a valid option for the "mode" parameter.
+** Use "ro" instead: "file:data.db?mode=ro".
**
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
@@ -3697,7 +3704,7 @@ sqlite3_file *sqlite3_database_file_object(const char*);
** If the Y parameter to sqlite3_free_filename(Y) is anything other
** than a NULL pointer or a pointer previously acquired from
** sqlite3_create_filename(), then bad things such as heap
-** corruption or segfaults may occur. The value Y should be
+** corruption or segfaults may occur. The value Y should not be
** used again after sqlite3_free_filename(Y) has been called. This means
** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
** then the corresponding [sqlite3_module.xClose() method should also be
@@ -7740,7 +7747,6 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
-#define SQLITE_TESTCTRL_SEEK_COUNT 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -7765,7 +7771,9 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
#define SQLITE_TESTCTRL_PRNG_SEED 28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
-#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_SEEK_COUNT 30
+#define SQLITE_TESTCTRL_TRACEFLAGS 31
+#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 30e2afcac0..a42ade2df0 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -119,6 +119,18 @@
# define MSVC_VERSION 0
#endif
+/*
+** Some C99 functions in "math.h" are only present for MSVC when its version
+** is associated with Visual Studio 2013 or higher.
+*/
+#ifndef SQLITE_HAVE_C99_MATH_FUNCS
+# if MSVC_VERSION==0 || MSVC_VERSION>=1800
+# define SQLITE_HAVE_C99_MATH_FUNCS (1)
+# else
+# define SQLITE_HAVE_C99_MATH_FUNCS (0)
+# endif
+#endif
+
/* Needed for various definitions... */
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
@@ -202,7 +214,8 @@
#ifndef __has_extension
# define __has_extension(x) 0 /* compatibility with non-clang compilers */
#endif
-#if GCC_VERSION>=4007000 || __has_extension(c_atomic)
+#if GCC_VERSION>=4007000 || \
+ (__has_extension(c_atomic) && __has_extension(c_atomic_store_n))
# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
#else
@@ -984,15 +997,14 @@ typedef INT16_TYPE LogEst;
** 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_ENABLE_SELECTTRACE)
-# define SELECTTRACE_ENABLED 1
-#else
-# define SELECTTRACE_ENABLED 0
+#if !defined(SQLITE_AMALGAMATION)
+extern u32 sqlite3SelectTrace;
#endif
-#if defined(SQLITE_ENABLE_SELECTTRACE)
+#if defined(SQLITE_DEBUG) \
+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE))
# define SELECTTRACE_ENABLED 1
# define SELECTTRACE(K,P,S,X) \
- if(sqlite3_unsupported_selecttrace&(K)) \
+ if(sqlite3SelectTrace&(K)) \
sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
sqlite3DebugPrintf X
#else
@@ -1000,6 +1012,19 @@ typedef INT16_TYPE LogEst;
# define SELECTTRACE_ENABLED 0
#endif
+/*
+** Macros for "wheretrace"
+*/
+extern u32 sqlite3WhereTrace;
+#if defined(SQLITE_DEBUG) \
+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
+# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
+# define WHERETRACE_ENABLED 1
+#else
+# define WHERETRACE(K,X)
+#endif
+
+
/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
@@ -1111,10 +1136,14 @@ typedef struct AutoincInfo AutoincInfo;
typedef struct Bitvec Bitvec;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
+typedef struct Cte Cte;
+typedef struct CteUse CteUse;
typedef struct Db Db;
+typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
+typedef struct FastPrng FastPrng;
typedef struct FKey FKey;
typedef struct FuncDestructor FuncDestructor;
typedef struct FuncDef FuncDef;
@@ -1129,15 +1158,17 @@ typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
+typedef struct ParseCleanup ParseCleanup;
typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
typedef struct RenameToken RenameToken;
-typedef struct FastPrng FastPrng;
+typedef struct Returning Returning;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;
typedef struct SelectDest SelectDest;
+typedef struct SrcItem SrcItem;
typedef struct SrcList SrcList;
typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
typedef struct Table Table;
@@ -1479,7 +1510,7 @@ struct sqlite3 {
int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */
int iSysErrno; /* Errno value from last system error */
- u16 dbOptFlags; /* Flags to enable/disable optimizations */
+ u32 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 eConcurrent; /* CONCURRENT_* value */
@@ -1696,24 +1727,26 @@ struct sqlite3 {
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
-#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
-#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */
-#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
-#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
-#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_Stat4 0x0800 /* Use STAT4 data */
- /* TH3 expects the Stat4 ^^^^^^ 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 */
+#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */
+#define SQLITE_WindowFunc 0x00000002 /* Use xInverse for window functions */
+#define SQLITE_GroupByOrder 0x00000004 /* GROUPBY cover of ORDERBY */
+#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */
+#define SQLITE_DistinctOpt 0x00000010 /* DISTINCT using indexes */
+#define SQLITE_CoverIdxScan 0x00000020 /* Covering index scans */
+#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */
+#define SQLITE_Transitive 0x00000080 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */
+#define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */
+#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */
+#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */
+ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */
+#define SQLITE_PushDown 0x00001000 /* The push-down optimization */
+#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */
+#define SQLITE_SkipScan 0x00004000 /* Skip-scans */
+#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
+#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */
+#define SQLITE_ExistsToIN 0x00020000 /* The EXISTS-to-IN optimization */
+#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
** Macros for testing whether or not optimizations are enabled or disabled.
@@ -1869,6 +1902,9 @@ struct FuncDestructor {
** a single query. The iArg is ignored. The user-data is always set
** to a NULL pointer. The bNC parameter is not used.
**
+** MFUNCTION(zName, nArg, xPtr, xFunc)
+** For math-library functions. xPtr is an arbitrary pointer.
+**
** PURE_DATE(zName, nArg, iArg, bNC, xFunc)
** Used for "pure" date/time functions, this macro is like DFUNCTION
** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is
@@ -1904,6 +1940,9 @@ struct FuncDestructor {
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
+#define MFUNCTION(zName, nArg, xPtr, xFunc) \
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
+ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
@@ -1998,7 +2037,12 @@ struct Column {
u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
-/* Allowed values for Column.colFlags:
+/* Allowed values for Column.colFlags.
+**
+** Constraints:
+** TF_HasVirtual == COLFLAG_VIRTUAL
+** TF_HasStored == COLFLAG_STORED
+** TF_HasHidden == COLFLAG_HIDDEN
*/
#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
@@ -2174,7 +2218,6 @@ struct Table {
#endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
Schema *pSchema; /* Schema that contains this table */
- Table *pNextZombie; /* Next on the Parse.pZombieTab list */
};
/*
@@ -2188,11 +2231,12 @@ struct Table {
**
** Constraints:
**
-** TF_HasVirtual == COLFLAG_Virtual
-** TF_HasStored == COLFLAG_Stored
+** TF_HasVirtual == COLFLAG_VIRTUAL
+** TF_HasStored == COLFLAG_STORED
+** TF_HasHidden == COLFLAG_HIDDEN
*/
#define TF_Readonly 0x0001 /* Read-only system table */
-#define TF_Ephemeral 0x0002 /* An ephemeral table */
+#define TF_HasHidden 0x0002 /* Has one or more hidden columns */
#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */
#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */
#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */
@@ -2207,6 +2251,7 @@ struct Table {
#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */
#define TF_Shadow 0x1000 /* True for a shadow table */
#define TF_HasStat4 0x2000 /* STAT4 info available for this table */
+#define TF_Ephemeral 0x4000 /* An ephemeral table */
/*
** Test to see whether or not a table is a virtual table. This is
@@ -2303,16 +2348,22 @@ struct FKey {
** is returned. REPLACE means that preexisting database rows that caused
** a UNIQUE constraint violation are removed so that the new insert or
** update can proceed. Processing continues and no error is reported.
+** UPDATE applies to insert operations only and means that the insert
+** is omitted and the DO UPDATE clause of an upsert is run instead.
**
-** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys.
+** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys.
** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the
** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign
-** key is set to NULL. CASCADE means that a DELETE or UPDATE of the
+** key is set to NULL. SETDFLT means that the foreign key is set
+** to its default value. CASCADE means that a DELETE or UPDATE of the
** referenced table row is propagated into the row that holds the
** foreign key.
**
+** The OE_Default value is a place holder that means to use whatever
+** conflict resolution algorthm is required from context.
+**
** The following symbolic values are used to record which type
-** of action to take.
+** of conflict resolution action to take.
*/
#define OE_None 0 /* There is no constraint to check */
#define OE_Rollback 1 /* Fail the operation and rollback the transaction */
@@ -2569,7 +2620,6 @@ struct AggInfo {
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
- AggInfo *pNext; /* Next in list of them all */
};
/*
@@ -2698,7 +2748,7 @@ struct Expr {
** TK_VARIABLE: variable number (always >= 1).
** TK_SELECT_COLUMN: column of the result vector */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
- i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
+ int iRightJoinTable; /* If EP_FromJoin, the right table of the join */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
@@ -2740,7 +2790,7 @@ struct Expr {
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
-#define EP_Alias 0x400000 /* Is an alias for a result set column */
+ /* 0x400000 // Available */
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
@@ -2888,6 +2938,45 @@ struct IdList {
int nId; /* Number of identifiers on the list */
};
+/*
+** The SrcItem object represents a single term in the FROM clause of a query.
+** The SrcList object is mostly an array of SrcItems.
+*/
+struct SrcItem {
+ Schema *pSchema; /* Schema to which this item is fixed */
+ char *zDatabase; /* Name of database holding this table */
+ char *zName; /* Name of the table */
+ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
+ Table *pTab; /* An SQL table corresponding to zName */
+ Select *pSelect; /* A SELECT statement used in place of a table name */
+ int addrFillSub; /* Address of subroutine to manifest a subquery */
+ int regReturn; /* Register holding return address of addrFillSub */
+ int regResult; /* Registers holding results of a co-routine */
+ struct {
+ u8 jointype; /* Type of join between this table and the previous */
+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
+ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
+ unsigned isTabFunc :1; /* True if table-valued-function syntax */
+ unsigned isCorrelated :1; /* True if sub-query is correlated */
+ unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
+ unsigned fromDDL :1; /* Comes from sqlite_schema */
+ unsigned isCte :1; /* This is a CTE */
+ } fg;
+ 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 */
+ Bitmask colUsed; /* Bit N (1<" clause */
+ ExprList *pFuncArg; /* Arguments to table-valued-function */
+ } u1;
+ union {
+ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
+ CteUse *pCteUse; /* CTE Usage info info fg.isCte is true */
+ } u2;
+};
+
/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
@@ -2910,36 +2999,7 @@ struct IdList {
struct SrcList {
int nSrc; /* Number of tables or subqueries in the FROM clause */
u32 nAlloc; /* Number of entries allocated in a[] below */
- struct SrcList_item {
- Schema *pSchema; /* Schema to which this item is fixed */
- char *zDatabase; /* Name of database holding this table */
- char *zName; /* Name of the table */
- char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
- Table *pTab; /* An SQL table corresponding to zName */
- Select *pSelect; /* A SELECT statement used in place of a table name */
- int addrFillSub; /* Address of subroutine to manifest a subquery */
- int regReturn; /* Register holding return address of addrFillSub */
- int regResult; /* Registers holding results of a co-routine */
- struct {
- u8 jointype; /* Type of join between this table and the previous */
- unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
- unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
- unsigned isTabFunc :1; /* True if table-valued-function syntax */
- unsigned isCorrelated :1; /* True if sub-query is correlated */
- unsigned viaCoroutine :1; /* Implemented as a co-routine */
- unsigned isRecursive :1; /* True for recursive reference in WITH */
- unsigned fromDDL :1; /* Comes from sqlite_schema */
- } fg;
- 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 */
- Bitmask colUsed; /* Bit N (1<" clause */
- ExprList *pFuncArg; /* Arguments to table-valued-function */
- } u1;
- Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
- } a[1]; /* One entry for each identifier on the list */
+ SrcItem a[1]; /* One entry for each identifier on the list */
};
/*
@@ -3015,6 +3075,7 @@ struct NameContext {
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 */
+ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */
} uNC;
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
@@ -3043,6 +3104,7 @@ struct NameContext {
#define NC_UEList 0x00080 /* True if uNC.pEList is used */
#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */
#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */
+#define NC_UBaseReg 0x00400 /* True if uNC.iBaseReg is used */
#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x02000 /* True if a function or subquery seen */
#define NC_AllowWin 0x04000 /* Window functions are allowed here */
@@ -3066,15 +3128,21 @@ struct NameContext {
** WHERE clause is omitted.
*/
struct Upsert {
- ExprList *pUpsertTarget; /* Optional description of conflicting index */
+ ExprList *pUpsertTarget; /* Optional description of conflict target */
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 */
+ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
+ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */
+ /* Above this point is the parse tree for the ON CONFLICT clauses.
+ ** The next group of fields stores intermediate data. */
+ void *pToFree; /* Free memory when deleting the Upsert object */
+ /* All fields above are owned by the Upsert object and must be freed
+ ** when the Upsert is destroyed. The fields below are used to transfer
+ ** information from the INSERT processing down into the UPDATE processing
+ ** while generating code. The fields below are owned by the INSERT
+ ** statement and will be freed by INSERT processing. */
+ Index *pUpsertIdx; /* UNIQUE constraint specified by pUpsertTarget */
SrcList *pUpsertSrc; /* Table to be updated */
int regData; /* First register holding array of VALUES */
int iDataCur; /* Index of the data cursor */
@@ -3154,6 +3222,8 @@ struct Select {
#define SF_View 0x0200000 /* SELECT statement is a view */
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
+#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
+#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -3324,6 +3394,17 @@ struct TriggerPrg {
# define DbMaskNonZero(M) (M)!=0
#endif
+/*
+** An instance of the ParseCleanup object specifies an operation that
+** should be performed after parsing to deallocation resources obtained
+** during the parse and which are no longer needed.
+*/
+struct ParseCleanup {
+ ParseCleanup *pNext; /* Next cleanup task */
+ void *pPtr; /* Pointer to object to deallocate */
+ void (*xCleanup)(sqlite3*,void*); /* Deallocation routine */
+};
+
/*
** An SQL parser context. A copy of this structure is passed through
** the parser and down into all the parser action routine in order to
@@ -3355,6 +3436,9 @@ struct Parse {
u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 disableVtab; /* Disable all virtual tables for this parse */
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+ u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
+#endif
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
@@ -3382,12 +3466,15 @@ struct Parse {
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
Parse *pParentParse; /* Parent parser if this parser is nested */
- AggInfo *pAggList; /* List of all AggInfo objects */
- int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
+ union {
+ int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
+ Returning *pReturning; /* The RETURNING clause */
+ } u1;
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 */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
+ u8 bReturning; /* Coding a RETURNING trigger */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
@@ -3433,10 +3520,9 @@ struct Parse {
Token sArg; /* Complete text of a module argument */
Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
- Table *pZombieTab; /* List of Table objects to delete after code gen */
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 */
+ ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */
#ifndef SQLITE_OMIT_ALTERTABLE
RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */
#endif
@@ -3516,6 +3602,7 @@ struct AuthContext {
#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 */
+#define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */
/*
* Each trigger present in the database schema is stored as an instance of
@@ -3537,6 +3624,7 @@ struct Trigger {
char *table; /* The table or view to which the trigger applies */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
+ u8 bReturning; /* This trigger implements a RETURNING clause */
Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */
IdList *pColumns; /* If this is an UPDATE OF trigger,
the is stored here */
@@ -3595,14 +3683,15 @@ struct Trigger {
*
*/
struct TriggerStep {
- u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
+ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT,
+ ** or TK_RETURNING */
u8 orconf; /* OE_Rollback etc. */
Trigger *pTrig; /* The trigger that this step is a part of */
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE */
+ ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */
IdList *pIdList; /* Column names for INSERT */
Upsert *pUpsert; /* Upsert clauses on an INSERT */
char *zSpan; /* Original SQL text of this command */
@@ -3611,18 +3700,16 @@ struct TriggerStep {
};
/*
-** The following structure contains information used by the sqliteFix...
-** routines as they walk the parse tree to make database references
-** explicit.
+** Information about a RETURNING clause
*/
-typedef struct DbFixer DbFixer;
-struct DbFixer {
- Parse *pParse; /* The parsing context. Error messages written here */
- Schema *pSchema; /* Fix items to this schema */
- u8 bTemp; /* True for TEMP schema entries */
- const char *zDb; /* Make sure all objects are contained in this database */
- const char *zType; /* Type of the container - used for error messages */
- const Token *pName; /* Name of the container - used for error messages */
+struct Returning {
+ Parse *pParse; /* The parse that includes the RETURNING clause */
+ ExprList *pReturnEL; /* List of expressions to return */
+ Trigger retTrig; /* The transient trigger that implements RETURNING */
+ TriggerStep retTStep; /* The trigger step */
+ int iRetCur; /* Transient table holding RETURNING results */
+ int nRetCol; /* Number of in pReturnEL after expansion */
+ int iRetReg; /* Register array for holding a row of RETURNING */
};
/*
@@ -3662,7 +3749,8 @@ typedef struct {
/*
** Allowed values for mInitFlags
*/
-#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
+#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */
+#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */
/*
** Structure containing global configuration data for the SQLite library.
@@ -3774,10 +3862,26 @@ struct Walker {
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
- struct SrcList_item *pSrcItem; /* A single FROM clause item */
+ SrcItem *pSrcItem; /* A single FROM clause item */
+ DbFixer *pFix;
} u;
};
+/*
+** The following structure contains information used by the sqliteFix...
+** routines as they walk the parse tree to make database references
+** explicit.
+*/
+struct DbFixer {
+ Parse *pParse; /* The parsing context. Error messages written here */
+ Walker w; /* Walker object */
+ Schema *pSchema; /* Fix items to this schema */
+ u8 bTemp; /* True for TEMP schema entries */
+ const char *zDb; /* Make sure all objects are contained in this database */
+ const char *zType; /* Type of the container - used for error messages */
+ const Token *pName; /* Name of the container - used for error messages */
+};
+
/* Forward declarations */
int sqlite3WalkExpr(Walker*, Expr*);
int sqlite3WalkExprList(Walker*, ExprList*);
@@ -3803,20 +3907,55 @@ void sqlite3SelectWalkAssert2(Walker*, Select*);
#define WRC_Abort 2 /* Abandon the tree walk */
/*
-** An instance of this structure represents a set of one or more CTEs
-** (common table expressions) created by a single WITH clause.
+** A single common table expression
+*/
+struct Cte {
+ char *zName; /* Name of this CTE */
+ ExprList *pCols; /* List of explicit column names, or NULL */
+ Select *pSelect; /* The definition of this CTE */
+ const char *zCteErr; /* Error message for circular references */
+ CteUse *pUse; /* Usage information for this CTE */
+ u8 eM10d; /* The MATERIALIZED flag */
+};
+
+/*
+** Allowed values for the materialized flag (eM10d):
+*/
+#define M10d_Yes 0 /* AS MATERIALIZED */
+#define M10d_Any 1 /* Not specified. Query planner's choice */
+#define M10d_No 2 /* AS NOT MATERIALIZED */
+
+/*
+** An instance of the With object represents a WITH clause containing
+** one or more CTEs (common table expressions).
*/
struct With {
- int nCte; /* Number of CTEs in the WITH clause */
- With *pOuter; /* Containing WITH clause, or NULL */
- struct Cte { /* For each CTE in the WITH clause.... */
- char *zName; /* Name of this CTE */
- ExprList *pCols; /* List of explicit column names, or NULL */
- Select *pSelect; /* The definition of this CTE */
- const char *zCteErr; /* Error message for circular references */
- } a[1];
+ int nCte; /* Number of CTEs in the WITH clause */
+ With *pOuter; /* Containing WITH clause, or NULL */
+ Cte a[1]; /* For each CTE in the WITH clause.... */
};
+/*
+** The Cte object is not guaranteed to persist for the entire duration
+** of code generation. (The query flattener or other parser tree
+** edits might delete it.) The following object records information
+** about each Common Table Expression that must be preserved for the
+** duration of the parse.
+**
+** The CteUse objects are freed using sqlite3ParserAddCleanup() rather
+** than sqlite3SelectDelete(), which is what enables them to persist
+** until the end of code generation.
+*/
+struct CteUse {
+ int nUse; /* Number of users of this CTE */
+ int addrM9e; /* Start of subroutine to compute materialization */
+ int regRtn; /* Return address register for addrM9e subroutine */
+ int iCur; /* Ephemeral table holding the materialization */
+ LogEst nRowEst; /* Estimated number of rows in the table */
+ u8 eM10d; /* The MATERIALIZED flag */
+};
+
+
#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
@@ -3894,7 +4033,6 @@ int sqlite3WindowCompare(Parse*, Window*, Window*, int);
void sqlite3WindowCodeInit(Parse*, Select*);
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
int sqlite3WindowRewrite(Parse*, Select*);
-int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
@@ -4211,6 +4349,7 @@ void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
void sqlite3AddCollateType(Parse*, Token*);
void sqlite3AddGenerated(Parse*,Expr*,Token*);
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
+void sqlite3AddReturning(Parse*,ExprList*);
int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
#define sqlite3CodecQueryParameters(A,B,C) 0
@@ -4276,7 +4415,7 @@ SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, Expr*, IdList*);
void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
-int sqlite3IndexedByLookup(Parse *, struct SrcList_item *);
+int sqlite3IndexedByLookup(Parse *, SrcItem *);
void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(sqlite3*, IdList*);
@@ -4304,6 +4443,7 @@ LogEst sqlite3WhereOutputRowCount(WhereInfo*);
int sqlite3WhereIsDistinct(WhereInfo*);
int sqlite3WhereIsOrdered(WhereInfo*);
int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);
+void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*);
int sqlite3WhereIsSorted(WhereInfo*);
int sqlite3WhereContinueLabel(WhereInfo*);
int sqlite3WhereBreakLabel(WhereInfo*);
@@ -4337,7 +4477,7 @@ Table *sqlite3FindTable(sqlite3*,const char*, const char*);
#define LOCATE_VIEW 0x01
#define LOCATE_NOERR 0x02
Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*);
-Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
+Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *);
Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
@@ -4467,6 +4607,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
#endif
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
+int sqlite3ColumnIndex(Table *pTab, const char *zCol);
void sqlite3SetJoinExpr(Expr*,int);
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
void sqlite3DeferForeignKey(Parse*, int);
@@ -4489,7 +4630,6 @@ void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
int sqlite3FixSrcList(DbFixer*, SrcList*);
int sqlite3FixSelect(DbFixer*, Select*);
int sqlite3FixExpr(DbFixer*, Expr*);
-int sqlite3FixExprList(DbFixer*, ExprList*);
int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
int sqlite3RealSameAsInt(double,sqlite3_int64);
void sqlite3Int64ToText(i64,char*);
@@ -4552,6 +4692,7 @@ int sqlite3Atoi64(const char*, i64*, int, u8);
int sqlite3DecOrHexToI64(const char*, i64*);
void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
void sqlite3Error(sqlite3*,int);
+void sqlite3ErrorClear(sqlite3*);
void sqlite3SystemError(sqlite3*,int);
void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
u8 sqlite3HexToInt(int h);
@@ -4615,7 +4756,6 @@ extern const unsigned char sqlite3UpperToLower[];
extern const unsigned char sqlite3CtypeMap[];
extern SQLITE_WSD struct Sqlite3Config sqlite3Config;
extern FuncDefHash sqlite3BuiltinFunctions;
-extern u32 sqlite3_unsupported_selecttrace;
#ifndef SQLITE_OMIT_WSD
extern int sqlite3PendingByte;
#endif
@@ -4634,6 +4774,7 @@ void sqlite3ExpirePreparedStatements(sqlite3*, int);
void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
int sqlite3CodeSubselect(Parse*, Expr*);
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+int sqlite3ExpandSubquery(Parse*, SrcItem*);
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
int sqlite3MatchEName(
const struct ExprList_item*,
@@ -4651,6 +4792,7 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
void sqlite3AlterFinishAddColumn(Parse *, Token *);
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
+void sqlite3AlterDropColumn(Parse*, SrcList*, Token*);
void *sqlite3RenameTokenMap(Parse*, void*, Token*);
void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
void sqlite3RenameExprUnmap(Parse*, Expr*);
@@ -4804,6 +4946,7 @@ sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
void sqlite3ParserReset(Parse*);
+void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*);
#ifdef SQLITE_ENABLE_NORMALIZE
char *sqlite3Normalize(Vdbe*, const char*);
#endif
@@ -4818,23 +4961,32 @@ const char *sqlite3JournalModename(int);
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
#endif
#ifndef SQLITE_OMIT_CTE
- With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
+ Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
+ void sqlite3CteDelete(sqlite3*,Cte*);
+ With *sqlite3WithAdd(Parse*,With*,Cte*);
void sqlite3WithDelete(sqlite3*,With*);
void sqlite3WithPush(Parse*, With*, u8);
#else
-#define sqlite3WithPush(x,y,z)
-#define sqlite3WithDelete(x,y)
+# define sqlite3CteNew(P,T,E,S) ((void*)0)
+# define sqlite3CteDelete(D,C)
+# define sqlite3CteWithAdd(P,W,C) ((void*)0)
+# define sqlite3WithDelete(x,y)
+# define sqlite3WithPush(x,y,z)
#endif
#ifndef SQLITE_OMIT_UPSERT
- Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*);
+ Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
void sqlite3UpsertDelete(sqlite3*,Upsert*);
Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
+ Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
+ int sqlite3UpsertNextIsIPK(Upsert*);
#else
-#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0)
+#define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0)
#define sqlite3UpsertDelete(x,y)
-#define sqlite3UpsertDup(x,y) ((Upsert*)0)
+#define sqlite3UpsertDup(x,y) ((Upsert*)0)
+#define sqlite3UpsertOfIndex(x,y) ((Upsert*)0)
+#define sqlite3UpsertNextIsIPK(x) 0
#endif
diff --git a/src/test1.c b/src/test1.c
index 1a95120685..9c79b4ccb5 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -8548,7 +8548,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
extern LONG volatile sqlite3_os_type;
#endif
#ifdef SQLITE_DEBUG
- extern int sqlite3WhereTrace;
+ extern u32 sqlite3WhereTrace;
extern int sqlite3OSTrace;
extern int sqlite3WalTrace;
#endif
@@ -8556,9 +8556,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#ifdef SQLITE_ENABLE_FTS3
extern int sqlite3_fts3_enable_parentheses;
#endif
-#endif
-#if defined(SQLITE_ENABLE_SELECTTRACE)
- extern u32 sqlite3_unsupported_selecttrace;
#endif
for(i=0; ipNewTrigger);
}
-
- if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DbFree(db, pParse->pVList);
- while( pParse->pAinc ){
- AutoincInfo *p = pParse->pAinc;
- pParse->pAinc = p->pNext;
- sqlite3DbFreeNN(db, p);
- }
- while( pParse->pZombieTab ){
- Table *p = pParse->pZombieTab;
- pParse->pZombieTab = p->pNextZombie;
- sqlite3DeleteTable(db, p);
- }
db->pParse = pParse->pParentParse;
pParse->pParentParse = 0;
assert( nErr==0 || pParse->rc!=SQLITE_OK );
diff --git a/src/treeview.c b/src/treeview.c
index 187f1a07d1..b696d764e0 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -111,7 +111,10 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
}
sqlite3_str_appendf(&x, ")");
}
- sqlite3_str_appendf(&x, " AS");
+ if( pCte->pUse ){
+ sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse,
+ pCte->pUse->nUse);
+ }
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, inCte-1);
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
@@ -127,7 +130,7 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
int i;
for(i=0; inSrc; i++){
- const struct SrcList_item *pItem = &pSrc->a[i];
+ const SrcItem *pItem = &pSrc->a[i];
StrAccum x;
char zLine[100];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
@@ -150,6 +153,9 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
+ if( pItem->fg.isCte ){
+ sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
+ }
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, inSrc-1);
if( pItem->pSelect ){
diff --git a/src/trigger.c b/src/trigger.c
index dd4ed8c2e8..689c7c741e 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -48,28 +48,39 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
** pTab as well as the triggers lised in pTab->pTrigger.
*/
Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
- Schema * const pTmpSchema = pParse->db->aDb[1].pSchema;
- Trigger *pList = 0; /* List of triggers to return */
+ Schema *pTmpSchema; /* Schema of the pTab table */
+ Trigger *pList; /* List of triggers to return */
+ HashElem *p; /* Loop variable for TEMP triggers */
if( pParse->disableTriggers ){
return 0;
}
-
+ pTmpSchema = pParse->db->aDb[1].pSchema;
+ p = sqliteHashFirst(&pTmpSchema->trigHash);
+ if( p==0 ){
+ return pTab->pTrigger;
+ }
+ pList = pTab->pTrigger;
if( pTmpSchema!=pTab->pSchema ){
- HashElem *p;
- assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
- for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
+ while( p ){
Trigger *pTrig = (Trigger *)sqliteHashData(p);
if( pTrig->pTabSchema==pTab->pSchema
- && 0==sqlite3StrICmp(pTrig->table, pTab->zName)
+ && 0==sqlite3StrICmp(pTrig->table, pTab->zName)
){
- pTrig->pNext = (pList ? pList : pTab->pTrigger);
+ pTrig->pNext = pList;
pList = pTrig;
- }
+ }else if( pTrig->op==TK_RETURNING ){
+ assert( pParse->bReturning );
+ assert( &(pParse->u1.pReturning->retTrig) == pTrig );
+ pTrig->table = pTab->zName;
+ pTrig->pTabSchema = pTab->pSchema;
+ pTrig->pNext = pList;
+ pList = pTrig;
+ }
+ p = sqliteHashNext(p);
}
}
-
- return (pList ? pList : pTab->pTrigger);
+ return pList;
}
/*
@@ -345,7 +356,7 @@ void sqlite3FinishTrigger(
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
+ sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0);
}
if( db->init.busy ){
@@ -558,7 +569,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
** Recursively delete a Trigger structure
*/
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
- if( pTrigger==0 ) return;
+ if( pTrigger==0 || pTrigger->bReturning ) return;
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
sqlite3DbFree(db, pTrigger->zName);
sqlite3DbFree(db, pTrigger->table);
@@ -723,15 +734,53 @@ Trigger *sqlite3TriggersExist(
Trigger *pList = 0;
Trigger *p;
- if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
- pList = sqlite3TriggerList(pParse, pTab);
- }
- assert( pList==0 || IsVirtual(pTab)==0 );
- for(p=pList; p; p=p->pNext){
- if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
- mask |= p->tr_tm;
+ pList = sqlite3TriggerList(pParse, pTab);
+ assert( pList==0 || IsVirtual(pTab)==0
+ || (pList->bReturning && pList->pNext==0) );
+ if( pList!=0 ){
+ p = pList;
+ if( (pParse->db->flags & SQLITE_EnableTrigger)==0
+ && pTab->pTrigger!=0
+ ){
+ /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that
+ ** only TEMP triggers are allowed. Truncate the pList so that it
+ ** includes only TEMP triggers */
+ if( pList==pTab->pTrigger ){
+ pList = 0;
+ goto exit_triggers_exist;
+ }
+ while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext;
+ p->pNext = 0;
+ p = pList;
}
+ do{
+ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
+ mask |= p->tr_tm;
+ }else if( p->op==TK_RETURNING ){
+ /* The first time a RETURNING trigger is seen, the "op" value tells
+ ** us what time of trigger it should be. */
+ assert( sqlite3IsToplevel(pParse) );
+ p->op = op;
+ if( IsVirtual(pTab) ){
+ if( op!=TK_INSERT ){
+ sqlite3ErrorMsg(pParse,
+ "%s RETURNING is not available on virtual tables",
+ op==TK_DELETE ? "DELETE" : "UPDATE");
+ }
+ p->tr_tm = TRIGGER_BEFORE;
+ }else{
+ p->tr_tm = TRIGGER_AFTER;
+ }
+ mask |= p->tr_tm;
+ }else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE
+ && sqlite3IsToplevel(pParse) ){
+ /* Also fire a RETURNING trigger for an UPSERT */
+ mask |= p->tr_tm;
+ }
+ p = p->pNext;
+ }while( p );
}
+exit_triggers_exist:
if( pMask ){
*pMask = mask;
}
@@ -774,6 +823,111 @@ SrcList *sqlite3TriggerStepSrc(
return pSrc;
}
+/* The input list pList is the list of result set terms from a RETURNING
+** clause. The table that we are returning from is pTab.
+**
+** This routine makes a copy of the pList, and at the same time expands
+** any "*" wildcards to be the complete set of columns from pTab.
+*/
+static ExprList *sqlite3ExpandReturning(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* The arguments to RETURNING */
+ Table *pTab /* The table being updated */
+){
+ ExprList *pNew = 0;
+ sqlite3 *db = pParse->db;
+ int i;
+
+ for(i=0; inExpr; i++){
+ Expr *pOldExpr = pList->a[i].pExpr;
+ if( ALWAYS(pOldExpr!=0) && pOldExpr->op==TK_ASTERISK ){
+ int jj;
+ for(jj=0; jjnCol; jj++){
+ Expr *pNewExpr;
+ if( IsHiddenColumn(pTab->aCol+jj) ) continue;
+ pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zName);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
+ if( !db->mallocFailed ){
+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
+ pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zName);
+ pItem->eEName = ENAME_NAME;
+ }
+ }
+ }else{
+ Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
+ if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){
+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
+ pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName);
+ pItem->eEName = pList->a[i].eEName;
+ }
+ }
+ }
+ if( !db->mallocFailed ){
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ sqlite3VdbeSetNumCols(v, pNew->nExpr);
+ for(i=0; inExpr; i++){
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, pNew->a[i].zEName,
+ SQLITE_TRANSIENT);
+ }
+ }
+ return pNew;
+}
+
+/*
+** Generate code for the RETURNING trigger. Unlike other triggers
+** that invoke a subprogram in the bytecode, the code for RETURNING
+** is generated in-line.
+*/
+static void codeReturningTrigger(
+ Parse *pParse, /* Parse context */
+ Trigger *pTrigger, /* The trigger step that defines the RETURNING */
+ Table *pTab, /* The table to code triggers from */
+ int regIn /* The first in an array of registers */
+){
+ Vdbe *v = pParse->pVdbe;
+ ExprList *pNew;
+ Returning *pReturning;
+
+ assert( v!=0 );
+ assert( pParse->bReturning );
+ pReturning = pParse->u1.pReturning;
+ assert( pTrigger == &(pReturning->retTrig) );
+ pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
+ if( pNew ){
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ if( pReturning->nRetCol==0 ){
+ pReturning->nRetCol = pNew->nExpr;
+ pReturning->iRetCur = pParse->nTab++;
+ }
+ sNC.pParse = pParse;
+ sNC.uNC.iBaseReg = regIn;
+ sNC.ncFlags = NC_UBaseReg;
+ pParse->eTriggerOp = pTrigger->op;
+ pParse->pTriggerTab = pTab;
+ if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK ){
+ int i;
+ int nCol = pNew->nExpr;
+ int reg = pParse->nMem+1;
+ pParse->nMem += nCol+2;
+ pReturning->iRetReg = reg;
+ for(i=0; ia[i].pExpr, reg+i);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1);
+ sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1);
+ }
+ sqlite3ExprListDelete(pParse->db, pNew);
+ pParse->eTriggerOp = 0;
+ pParse->pTriggerTab = 0;
+ }
+}
+
+
+
/*
** Generate VDBE code for the statements inside the body of a single
** trigger.
@@ -823,6 +977,7 @@ static int codeTriggerProgram(
sqlite3ExprDup(db, pStep->pWhere, 0),
pParse->eOrconf, 0, 0, 0
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
case TK_INSERT: {
@@ -833,6 +988,7 @@ static int codeTriggerProgram(
pParse->eOrconf,
sqlite3UpsertDup(db, pStep->pUpsert)
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
case TK_DELETE: {
@@ -840,6 +996,7 @@ static int codeTriggerProgram(
sqlite3TriggerStepSrc(pParse, pStep),
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
default: assert( pStep->op==TK_SELECT ); {
@@ -851,9 +1008,6 @@ static int codeTriggerProgram(
break;
}
}
- if( pStep->op!=TK_SELECT ){
- sqlite3VdbeAddOp0(v, OP_ResetCount);
- }
}
return 0;
@@ -1000,7 +1154,6 @@ static TriggerPrg *codeRowTrigger(
sqlite3VdbeDelete(v);
}
- assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
sqlite3ParserReset(pSubParse);
sqlite3StackFree(db, pSubParse);
@@ -1102,7 +1255,7 @@ void sqlite3CodeRowTriggerDirect(
** ... ...
** reg+N OLD.* value of right-most column of pTab
** reg+N+1 NEW.rowid
-** reg+N+2 OLD.* value of left-most column of pTab
+** reg+N+2 NEW.* value of left-most column of pTab
** ... ...
** reg+N+N+1 NEW.* value of right-most column of pTab
**
@@ -1147,12 +1300,20 @@ void sqlite3CodeRowTrigger(
assert( p->pSchema==p->pTabSchema
|| p->pSchema==pParse->db->aDb[1].pSchema );
- /* Determine whether we should code this trigger */
- if( p->op==op
+ /* Determine whether we should code this trigger. One of two choices:
+ ** 1. The trigger is an exact match to the current DML statement
+ ** 2. This is a RETURNING trigger for INSERT but we are currently
+ ** doing the UPDATE part of an UPSERT.
+ */
+ if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE))
&& p->tr_tm==tr_tm
&& checkColumnOverlap(p->pColumns, pChanges)
){
- sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
+ if( !p->bReturning ){
+ sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
+ }else if( sqlite3IsToplevel(pParse) ){
+ codeReturningTrigger(pParse, p, pTab, reg);
+ }
}
}
}
@@ -1197,13 +1358,18 @@ u32 sqlite3TriggerColmask(
assert( isNew==1 || isNew==0 );
for(p=pTrigger; p; p=p->pNext){
- if( p->op==op && (tr_tm&p->tr_tm)
+ if( p->op==op
+ && (tr_tm&p->tr_tm)
&& checkColumnOverlap(p->pColumns,pChanges)
){
- TriggerPrg *pPrg;
- pPrg = getRowTrigger(pParse, p, pTab, orconf);
- if( pPrg ){
- mask |= pPrg->aColmask[isNew];
+ if( p->bReturning ){
+ mask = 0xffffffff;
+ }else{
+ TriggerPrg *pPrg;
+ pPrg = getRowTrigger(pParse, p, pTab, orconf);
+ if( pPrg ){
+ mask |= pPrg->aColmask[isNew];
+ }
}
}
}
diff --git a/src/update.c b/src/update.c
index 4d3b6cc724..8c37456948 100644
--- a/src/update.c
+++ b/src/update.c
@@ -654,6 +654,7 @@ void sqlite3Update(
if( (db->flags&SQLITE_CountRows)!=0
&& !pParse->pTriggerTab
&& !pParse->nested
+ && !pParse->bReturning
&& pUpsert==0
){
regRowCount = ++pParse->nMem;
@@ -1117,7 +1118,7 @@ void sqlite3Update(
** that information.
*/
if( regRowCount ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
+ sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
}
diff --git a/src/upsert.c b/src/upsert.c
index 9a33f75d0a..982dc7dbc1 100644
--- a/src/upsert.c
+++ b/src/upsert.c
@@ -18,15 +18,22 @@
/*
** Free a list of Upsert objects
*/
-void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
- if( p ){
+static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
+ do{
+ Upsert *pNext = p->pNextUpsert;
sqlite3ExprListDelete(db, p->pUpsertTarget);
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
sqlite3ExprListDelete(db, p->pUpsertSet);
sqlite3ExprDelete(db, p->pUpsertWhere);
+ sqlite3DbFree(db, p->pToFree);
sqlite3DbFree(db, p);
- }
+ p = pNext;
+ }while( p );
}
+void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
+ if( p ) upsertDelete(db, p);
+}
+
/*
** Duplicate an Upsert object.
@@ -37,7 +44,8 @@ Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
sqlite3ExprListDup(db, p->pUpsertTarget, 0),
sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
sqlite3ExprListDup(db, p->pUpsertSet, 0),
- sqlite3ExprDup(db, p->pUpsertWhere, 0)
+ sqlite3ExprDup(db, p->pUpsertWhere, 0),
+ sqlite3UpsertDup(db, p->pNextUpsert)
);
}
@@ -49,22 +57,25 @@ Upsert *sqlite3UpsertNew(
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 */
+ Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */
+ Upsert *pNext /* Next ON CONFLICT clause in the list */
){
Upsert *pNew;
- pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
+ pNew = sqlite3DbMallocZero(db, sizeof(Upsert));
if( pNew==0 ){
sqlite3ExprListDelete(db, pTarget);
sqlite3ExprDelete(db, pTargetWhere);
sqlite3ExprListDelete(db, pSet);
sqlite3ExprDelete(db, pWhere);
+ sqlite3UpsertDelete(db, pNext);
return 0;
}else{
pNew->pUpsertTarget = pTarget;
pNew->pUpsertTargetWhere = pTargetWhere;
pNew->pUpsertSet = pSet;
pNew->pUpsertWhere = pWhere;
- pNew->pUpsertIdx = 0;
+ pNew->isDoUpdate = pSet!=0;
+ pNew->pNextUpsert = pNext;
}
return pNew;
}
@@ -89,6 +100,7 @@ int sqlite3UpsertAnalyzeTarget(
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 */
+ int nClause = 0; /* Counter of ON CONFLICT clauses */
assert( pTabList->nSrc==1 );
assert( pTabList->a[0].pTab!=0 );
@@ -102,87 +114,131 @@ int sqlite3UpsertAnalyzeTarget(
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; iiazColl[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; jja[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( iipUpsertTarget;
+ pUpsert=pUpsert->pNextUpsert, nClause++){
+ 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 );
continue;
}
- pUpsert->pUpsertIdx = pIdx;
- 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; iiazColl[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; jja[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( iipUpsertIdx = pIdx;
+ break;
+ }
+ if( pUpsert->pUpsertIdx==0 ){
+ char zWhich[16];
+ if( nClause==0 && pUpsert->pNextUpsert==0 ){
+ zWhich[0] = 0;
+ }else{
+ sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1);
+ }
+ sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any "
+ "PRIMARY KEY or UNIQUE constraint", zWhich);
+ return SQLITE_ERROR;
+ }
}
- sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
- "PRIMARY KEY or UNIQUE constraint");
- return SQLITE_ERROR;
+ return SQLITE_OK;
+}
+
+/*
+** Return true if pUpsert is the last ON CONFLICT clause with a
+** conflict target, or if pUpsert is followed by another ON CONFLICT
+** clause that targets the INTEGER PRIMARY KEY.
+*/
+int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
+ Upsert *pNext;
+ if( NEVER(pUpsert==0) ) return 0;
+ pNext = pUpsert->pNextUpsert;
+ if( pNext==0 ) return 1;
+ if( pNext->pUpsertTarget==0 ) return 1;
+ if( pNext->pUpsertIdx==0 ) return 1;
+ return 0;
+}
+
+/*
+** Given the list of ON CONFLICT clauses described by pUpsert, and
+** a particular index pIdx, return a pointer to the particular ON CONFLICT
+** clause that applies to the index. Or, if the index is not subject to
+** any ON CONFLICT clause, return NULL.
+*/
+Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){
+ while(
+ pUpsert
+ && pUpsert->pUpsertTarget!=0
+ && pUpsert->pUpsertIdx!=pIdx
+ ){
+ pUpsert = pUpsert->pNextUpsert;
+ }
+ return pUpsert;
}
/*
@@ -206,11 +262,13 @@ void sqlite3UpsertDoUpdate(
SrcList *pSrc; /* FROM clause for the UPDATE */
int iDataCur;
int i;
+ Upsert *pTop = pUpsert;
assert( v!=0 );
assert( pUpsert!=0 );
- VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
iDataCur = pUpsert->iDataCur;
+ pUpsert = sqlite3UpsertOfIndex(pTop, pIdx);
+ VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
if( pIdx && iCur!=iDataCur ){
if( HasRowid(pTab) ){
int regRowid = sqlite3GetTempReg(pParse);
@@ -240,19 +298,17 @@ void sqlite3UpsertDoUpdate(
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);
+ /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does.
+ ** So we have to make a copy before passing it down into sqlite3Update() */
+ pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0);
/* excluded.* columns of type REAL need to be converted to a hard real */
for(i=0; inCol; i++){
if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
- sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i);
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i);
}
}
- 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() */
+ sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0),
+ sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert);
VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
}
diff --git a/src/util.c b/src/util.c
index fb86d7d118..fc0c2042bd 100644
--- a/src/util.c
+++ b/src/util.c
@@ -114,6 +114,16 @@ void sqlite3Error(sqlite3 *db, int err_code){
if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
}
+/*
+** The equivalent of sqlite3Error(db, SQLITE_OK). Clear the error state
+** and error message.
+*/
+void sqlite3ErrorClear(sqlite3 *db){
+ assert( db!=0 );
+ db->errCode = SQLITE_OK;
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr);
+}
+
/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
diff --git a/src/vdbe.c b/src/vdbe.c
index eaf1c035a0..3d40a9ebc8 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -685,7 +685,7 @@ int sqlite3VdbeExec(
#endif
/*** INSERT STACK UNION HERE ***/
- assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
+ assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
sqlite3VdbeEnter(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( db->xProgress ){
@@ -1445,6 +1445,26 @@ case OP_IntCopy: { /* out2 */
break;
}
+/* Opcode: ChngCntRow P1 P2 * * *
+** Synopsis: output=r[P1]
+**
+** Output value in register P1 as the chance count for a DML statement,
+** due to the "PRAGMA count_changes=ON" setting. Or, if there was a
+** foreign key error in the statement, trigger the error now.
+**
+** This opcode is a variant of OP_ResultRow that checks the foreign key
+** immediate constraint count and throws an error if the count is
+** non-zero. The P2 opcode must be 1.
+*/
+case OP_ChngCntRow: {
+ assert( pOp->p2==1 );
+ if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ /* Fall through to the next case, OP_String */
+ /* no break */ deliberate_fall_through
+}
+
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
**
@@ -1461,34 +1481,6 @@ case OP_ResultRow: {
assert( pOp->p1>0 );
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
- /* If this statement has violated immediate foreign key constraints, do
- ** not return the number of rows modified. And do not RELEASE the statement
- ** transaction. It needs to be rolled back. */
- if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
- assert( db->flags&SQLITE_CountRows );
- assert( p->usesStmtJournal );
- goto abort_due_to_error;
- }
-
- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
- ** DML statements invoke this opcode to return the number of rows
- ** modified to the user. This is the only way that a VM that
- ** opens a statement transaction may invoke this opcode.
- **
- ** In case this is such a statement, close any statement transaction
- ** opened by this VM before returning control to the user. This is to
- ** ensure that statement-transactions are always nested, not overlapping.
- ** If the open statement-transaction is not closed here, then the user
- ** may step another VM that opens its own statement transaction. This
- ** may lead to overlapping statement transactions.
- **
- ** The statement transaction is never a top-level transaction. Hence
- ** the RELEASE call below can never fail.
- */
- assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
- rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
- assert( rc==SQLITE_OK );
-
/* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
@@ -3993,7 +3985,7 @@ case OP_OpenEphemeral: {
aMem[pOp->p3].z = "";
}
pCx = p->apCsr[pOp->p1];
- if( pCx && pCx->pBtx ){
+ if( pCx && ALWAYS(pCx->pBtx) ){
/* If the ephermeral table is already open, erase all existing content
** so that the table is empty again, rather than creating a new table. */
assert( pCx->isEphemeral );
@@ -4945,8 +4937,10 @@ case OP_NewRowid: { /* out2 */
VdbeCursor *pC; /* Cursor of table to get the new rowid */
int res; /* Result of an sqlite3BtreeLast() */
int cnt; /* Counter to limit the number of searches */
+#ifndef SQLITE_OMIT_AUTOINCREMENT
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
VdbeFrame *pFrame; /* Root frame of VDBE */
+#endif
v = 0;
res = 0;
@@ -5162,7 +5156,8 @@ case OP_Insert: {
}
x.pKey = 0;
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult
+ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
+ seekResult
);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@@ -5179,6 +5174,33 @@ case OP_Insert: {
break;
}
+/* Opcode: RowCell P1 P2 P3 * *
+**
+** P1 and P2 are both open cursors. Both must be opened on the same type
+** of table - intkey or index. This opcode is used as part of copying
+** the current row from P2 into P1. If the cursors are opened on intkey
+** tables, register P3 contains the rowid to use with the new record in
+** P1. If they are opened on index tables, P3 is not used.
+**
+** This opcode must be followed by either an Insert or InsertIdx opcode
+** with the OPFLAG_PREFORMAT flag set to complete the insert operation.
+*/
+case OP_RowCell: {
+ VdbeCursor *pDest; /* Cursor to write to */
+ VdbeCursor *pSrc; /* Cursor to read from */
+ i64 iKey; /* Rowid value to insert with */
+ assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert );
+ assert( pOp[1].opcode==OP_Insert || pOp->p3==0 );
+ assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 );
+ assert( pOp[1].p5 & OPFLAG_PREFORMAT );
+ pDest = p->apCsr[pOp->p1];
+ pSrc = p->apCsr[pOp->p2];
+ iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0;
+ rc = sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ break;
+};
+
/* Opcode: Delete P1 P2 P3 P4 P5
**
** Delete the record at which the P1 cursor is currently pointing.
@@ -5834,7 +5856,7 @@ case OP_IdxInsert: { /* in2 */
assert( pC!=0 );
assert( !isSorter(pC) );
pIn2 = &aMem[pOp->p2];
- assert( pIn2->flags & MEM_Blob );
+ assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) );
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->isTable==0 );
@@ -5845,7 +5867,7 @@ case OP_IdxInsert: { /* in2 */
x.aMem = aMem + pOp->p3;
x.nMem = (u16)pOp->p4.i;
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)),
+ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
);
assert( pC->deferredMoveto==0 );
@@ -5918,7 +5940,7 @@ case OP_IdxDelete: {
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
if( rc ) goto abort_due_to_error;
}else if( pOp->p5 ){
- rc = SQLITE_CORRUPT_INDEX;
+ rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
goto abort_due_to_error;
}
assert( pC->deferredMoveto==0 );
@@ -6344,7 +6366,7 @@ case OP_ParseSchema: {
if( pOp->p4.z==0 ){
sqlite3SchemaClear(db->aDb[iDb].pSchema);
db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
- rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
+ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5);
db->mDbFlags |= DBFLAG_SchemaChange;
p->expired = 0;
}else
diff --git a/src/vdbe.h b/src/vdbe.h
index 17f11fdd77..3257ff68a1 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -223,7 +223,7 @@ VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
#else
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
#endif
-void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
+void sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16);
void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 7d22cb8093..4f8a2edf37 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -381,7 +381,7 @@ struct Vdbe {
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
Parse *pParse; /* Parsing context used to create this Vdbe */
ynVar nVar; /* Number of entries in aVar[] */
- u32 magic; /* Magic number for sanity checking */
+ u32 iVdbeMagic; /* Magic number defining state of the SQL statement */
int nMem; /* Number of memory locations currently allocated */
int nCursor; /* Number of slots in apCsr[] */
u32 cacheCtr; /* VdbeCursor row cache generation counter */
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index a9cbf92fc3..ba3bdf6a5f 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -617,7 +617,7 @@ static int sqlite3Step(Vdbe *p){
int rc;
assert(p);
- if( p->magic!=VDBE_MAGIC_RUN ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){
/* We used to require that sqlite3_reset() be called before retrying
** sqlite3_step() after any error or after SQLITE_DONE. But beginning
** with version 3.7.0, we changed this so that sqlite3_reset() would
@@ -1333,7 +1333,7 @@ static int vdbeUnbind(Vdbe *p, int i){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(p->db->mutex);
- if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_RUN || p->pc>=0 ){
sqlite3Error(p->db, SQLITE_MISUSE);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
@@ -1687,7 +1687,7 @@ int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
*/
int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
- return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0;
+ return v!=0 && v->iVdbeMagic==VDBE_MAGIC_RUN && v->pc>=0;
}
/*
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 63495f35d5..d790bb019b 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -35,7 +35,7 @@ Vdbe *sqlite3VdbeCreate(Parse *pParse){
p->pNext = db->pVdbe;
p->pPrev = 0;
db->pVdbe = p;
- p->magic = VDBE_MAGIC_INIT;
+ p->iVdbeMagic = VDBE_MAGIC_INIT;
p->pParse = pParse;
pParse->pVdbe = p;
assert( pParse->aLabel==0 );
@@ -236,7 +236,7 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
VdbeOp *pOp;
i = p->nOp;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( op>=0 && op<0xff );
if( p->nOpAlloc<=i ){
return growOp3(p, op, p1, p2, p3);
@@ -471,9 +471,10 @@ void sqlite3VdbeExplainPop(Parse *pParse){
** The zWhere string must have been obtained from sqlite3_malloc().
** This routine will take ownership of the allocated memory.
*/
-void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
+void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){
int j;
sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(p, p5);
for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j);
sqlite3MayAbort(p->pParse);
}
@@ -565,7 +566,7 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){
void sqlite3VdbeResolveLabel(Vdbe *v, int x){
Parse *p = v->pParse;
int j = ADDR(x);
- assert( v->magic==VDBE_MAGIC_INIT );
+ assert( v->iVdbeMagic==VDBE_MAGIC_INIT );
assert( j<-p->nLabel );
assert( j>=0 );
#ifdef SQLITE_DEBUG
@@ -890,7 +891,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
** Return the address of the next instruction to be inserted.
*/
int sqlite3VdbeCurrentAddr(Vdbe *p){
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
return p->nOp;
}
@@ -975,7 +976,7 @@ VdbeOp *sqlite3VdbeAddOpList(
int i;
VdbeOp *pOut, *pFirst;
assert( nOp>0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
@@ -1299,7 +1300,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
sqlite3 *db;
assert( p!=0 );
db = p->db;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( p->aOp!=0 || db->mallocFailed );
if( db->mallocFailed ){
if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4);
@@ -1428,7 +1429,7 @@ VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
** zeros, which is correct. MSVC generates a warning, nevertheless. */
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
if( addr<0 ){
addr = p->nOp - 1;
}
@@ -2113,7 +2114,7 @@ int sqlite3VdbeList(
Op *pOp; /* Current opcode */
assert( p->explain );
- assert( p->magic==VDBE_MAGIC_RUN );
+ assert( p->iVdbeMagic==VDBE_MAGIC_RUN );
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
/* Even though this opcode does not use dynamic strings for
@@ -2293,14 +2294,14 @@ void sqlite3VdbeRewind(Vdbe *p){
int i;
#endif
assert( p!=0 );
- assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT || p->iVdbeMagic==VDBE_MAGIC_RESET );
/* There should be at least one opcode.
*/
assert( p->nOp>0 );
/* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
- p->magic = VDBE_MAGIC_RUN;
+ p->iVdbeMagic = VDBE_MAGIC_RUN;
#ifdef SQLITE_DEBUG
for(i=0; inMem; i++){
@@ -2356,8 +2357,10 @@ void sqlite3VdbeMakeReady(
assert( p!=0 );
assert( p->nOp>0 );
assert( pParse!=0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( pParse==p->pParse );
+ p->pVList = pParse->pVList;
+ pParse->pVList = 0;
db = p->db;
assert( db->mallocFailed==0 );
nVar = pParse->nVar;
@@ -2442,8 +2445,6 @@ void sqlite3VdbeMakeReady(
}
}
- p->pVList = pParse->pVList;
- pParse->pVList = 0;
if( db->mallocFailed ){
p->nVar = 0;
p->nCursor = 0;
@@ -3058,7 +3059,7 @@ int sqlite3VdbeHalt(Vdbe *p){
** one, or the complete transaction if there is no statement transaction.
*/
- if( p->magic!=VDBE_MAGIC_RUN ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){
return SQLITE_OK;
}
if( db->mallocFailed ){
@@ -3219,7 +3220,7 @@ int sqlite3VdbeHalt(Vdbe *p){
assert( db->nVdbeRead>=db->nVdbeWrite );
assert( db->nVdbeWrite>=0 );
}
- p->magic = VDBE_MAGIC_HALT;
+ p->iVdbeMagic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM_BKPT;
@@ -3392,7 +3393,7 @@ int sqlite3VdbeReset(Vdbe *p){
}
}
#endif
- p->magic = VDBE_MAGIC_RESET;
+ p->iVdbeMagic = VDBE_MAGIC_RESET;
return p->rc & db->errMask;
}
@@ -3402,7 +3403,7 @@ int sqlite3VdbeReset(Vdbe *p){
*/
int sqlite3VdbeFinalize(Vdbe *p){
int rc = SQLITE_OK;
- if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){
+ if( p->iVdbeMagic==VDBE_MAGIC_RUN || p->iVdbeMagic==VDBE_MAGIC_HALT ){
rc = sqlite3VdbeReset(p);
assert( (rc & p->db->errMask)==rc );
}
@@ -3463,7 +3464,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
sqlite3DbFree(db, pSub);
}
- if( p->magic!=VDBE_MAGIC_INIT ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_INIT ){
releaseMemArray(p->aVar, p->nVar);
sqlite3DbFree(db, p->pVList);
sqlite3DbFree(db, p->pFree);
@@ -3511,7 +3512,7 @@ void sqlite3VdbeDelete(Vdbe *p){
if( p->pNext ){
p->pNext->pPrev = p->pPrev;
}
- p->magic = VDBE_MAGIC_DEAD;
+ p->iVdbeMagic = VDBE_MAGIC_DEAD;
p->db = 0;
sqlite3DbFreeNN(db, p);
}
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index 32c66af228..1095e7f589 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -125,7 +125,7 @@ char *sqlite3VdbeExpandSql(
assert( idx>0 );
}
zRawSql += nToken;
- nextIndex = idx + 1;
+ nextIndex = MAX(idx + 1, nextIndex);
assert( idx>0 && idx<=p->nVar );
pVar = &p->aVar[idx-1];
if( pVar->flags & MEM_Null ){
diff --git a/src/vtab.c b/src/vtab.c
index b2c01f2fad..ded12c13bb 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -489,7 +489,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3VdbeAddOp0(v, OP_Expire);
zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt);
- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0);
sqlite3DbFree(db, zStmt);
iReg = ++pParse->nMem;
@@ -660,6 +660,7 @@ static int vtabCallConstructor(
zType[i-1] = '\0';
}
pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN;
+ pTab->tabFlags |= TF_HasHidden;
oooHidden = TF_OOOHidden;
}else{
pTab->tabFlags |= oooHidden;
@@ -828,7 +829,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
- pTab->nCol = pNew->nCol;
+ pTab->nNVCol = pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
diff --git a/src/walker.c b/src/walker.c
index 7649036f56..927f7e52d7 100644
--- a/src/walker.c
+++ b/src/walker.c
@@ -22,7 +22,7 @@
** Walk all expressions linked into the list of Window objects passed
** as the second argument.
*/
-static int walkWindowList(Walker *pWalker, Window *pList){
+static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){
Window *pWin;
for(pWin=pList; pWin; pWin=pWin->pNextWin){
int rc;
@@ -41,6 +41,7 @@ static int walkWindowList(Walker *pWalker, Window *pList){
if( NEVER(rc) ) return WRC_Abort;
rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
if( NEVER(rc) ) return WRC_Abort;
+ if( bOneOnly ) break;
}
return WRC_Continue;
}
@@ -88,7 +89,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(pExpr, EP_WinFunc) ){
- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
+ if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort;
}
#endif
}
@@ -135,7 +136,7 @@ int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
if( pParse && IN_RENAME_OBJECT ){
/* The following may return WRC_Abort if there are unresolvable
** symbols (e.g. a table that does not exist) in a window definition. */
- int rc = walkWindowList(pWalker, p->pWinDefn);
+ int rc = walkWindowList(pWalker, p->pWinDefn, 0);
return rc;
}
}
@@ -153,7 +154,7 @@ int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
SrcList *pSrc;
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pSrc = p->pSrc;
if( pSrc ){
diff --git a/src/where.c b/src/where.c
index 2b97114e6b..75b3ceff87 100644
--- a/src/where.c
+++ b/src/where.c
@@ -37,12 +37,6 @@ struct HiddenIndexInfo {
/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);
-/* Test variable that can be set to enable WHERE tracing */
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-/***/ int sqlite3WhereTrace = 0;
-#endif
-
-
/*
** Return the estimated number of output rows from a WHERE clause
*/
@@ -105,6 +99,32 @@ int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){
return pInner->addrNxt;
}
+/*
+** While generating code for the min/max optimization, after handling
+** the aggregate-step call to min() or max(), check to see if any
+** additional looping is required. If the output order is such that
+** we are certain that the correct answer has already been found, then
+** code an OP_Goto to by pass subsequent processing.
+**
+** Any extra OP_Goto that is coded here is an optimization. The
+** correct answer should be obtained regardless. This OP_Goto just
+** makes the answer appear faster.
+*/
+void sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){
+ WhereLevel *pInner;
+ int i;
+ if( !pWInfo->bOrderedInnerLoop ) return;
+ if( pWInfo->nOBSat==0 ) return;
+ for(i=pWInfo->nLevel-1; i>=0; i--){
+ pInner = &pWInfo->a[i];
+ if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){
+ sqlite3VdbeGoto(v, pInner->addrNxt);
+ return;
+ }
+ }
+ sqlite3VdbeGoto(v, pWInfo->iBreak);
+}
+
/*
** Return the VDBE address or label to jump to in order to continue
** immediately with the next row of a WHERE clause.
@@ -674,7 +694,7 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
*/
static int termCanDriveIndex(
WhereTerm *pTerm, /* WHERE clause term to check */
- struct SrcList_item *pSrc, /* Table we are trying to access */
+ SrcItem *pSrc, /* Table we are trying to access */
Bitmask notReady /* Tables in outer loops of the join */
){
char aff;
@@ -708,7 +728,7 @@ static int termCanDriveIndex(
static void constructAutomaticIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to get the next index */
+ SrcItem *pSrc, /* The FROM clause term to get the next index */
Bitmask notReady, /* Mask of cursors that are not available */
WhereLevel *pLevel /* Write new index here */
){
@@ -732,7 +752,7 @@ static void constructAutomaticIndex(
u8 sentWarning = 0; /* True if a warnning has been issued */
Expr *pPartial = 0; /* Partial Index Expression */
int iContinue = 0; /* Jump here to skip excluded rows */
- struct SrcList_item *pTabItem; /* FROM clause term being indexed */
+ SrcItem *pTabItem; /* FROM clause term being indexed */
int addrCounter = 0; /* Address where integer counter is initialized */
int regBase; /* Array of registers where record is assembled */
@@ -916,7 +936,7 @@ static sqlite3_index_info *allocateIndexInfo(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause being analyzed */
Bitmask mUnusable, /* Ignore terms with these prereqs */
- struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */
+ SrcItem *pSrc, /* The FROM clause term that is the vtab */
ExprList *pOrderBy, /* The ORDER BY clause */
u16 *pmNoOmit /* Mask of terms not to omit */
){
@@ -1814,7 +1834,7 @@ void sqlite3WhereClausePrint(WhereClause *pWC){
void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
WhereInfo *pWInfo = pWC->pWInfo;
int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
- struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
Table *pTab = pItem->pTab;
Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
@@ -2425,7 +2445,7 @@ static int whereRangeVectorLen(
*/
static int whereLoopAddBtreeIndex(
WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
- struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ SrcItem *pSrc, /* FROM clause term being analyzed */
Index *pProbe, /* An index on pSrc */
LogEst nInMul /* log(Number of iterations due to IN) */
){
@@ -2611,7 +2631,7 @@ static int whereLoopAddBtreeIndex(
pBtm = pTerm;
pTop = 0;
if( pTerm->wtFlags & TERM_LIKEOPT ){
- /* Range contraints that come from the LIKE optimization are
+ /* Range constraints that come from the LIKE optimization are
** always used in pairs. */
pTop = &pTerm[1];
assert( (pTop-(pTerm->pWC->a))pWC->nTerm );
@@ -2916,7 +2936,7 @@ static int whereLoopAddBtree(
LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
SrcList *pTabList; /* The FROM clause */
- struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ SrcItem *pSrc; /* The FROM clause btree term to add */
WhereLoop *pNew; /* Template WhereLoop object */
int rc = SQLITE_OK; /* Return code */
int iSortIdx = 1; /* Index number */
@@ -2934,9 +2954,9 @@ static int whereLoopAddBtree(
pWC = pBuilder->pWC;
assert( !IsVirtual(pSrc->pTab) );
- if( pSrc->pIBIndex ){
+ if( pSrc->fg.isIndexedBy ){
/* An INDEXED BY clause specifies a particular index to use */
- pProbe = pSrc->pIBIndex;
+ pProbe = pSrc->u2.pIBIndex;
}else if( !HasRowid(pTab) ){
pProbe = pTab->pIndex;
}else{
@@ -2972,7 +2992,7 @@ static int whereLoopAddBtree(
if( !pBuilder->pOrSet /* Not part of an OR optimization */
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
- && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
+ && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
@@ -3022,7 +3042,7 @@ static int whereLoopAddBtree(
/* 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++
+ pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++
){
int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0;
if( pProbe->pPartIdxWhere!=0
@@ -3197,7 +3217,7 @@ static int whereLoopAddVirtualOne(
int rc = SQLITE_OK;
WhereLoop *pNew = pBuilder->pNew;
Parse *pParse = pBuilder->pWInfo->pParse;
- struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
+ SrcItem *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
int nConstraint = pIdxInfo->nConstraint;
assert( (mUsable & mPrereq)==mPrereq );
@@ -3389,7 +3409,7 @@ static int whereLoopAddVirtual(
WhereInfo *pWInfo; /* WHERE analysis context */
Parse *pParse; /* The parsing context */
WhereClause *pWC; /* The WHERE clause */
- struct SrcList_item *pSrc; /* The FROM clause term to search */
+ SrcItem *pSrc; /* The FROM clause term to search */
sqlite3_index_info *p; /* Object to pass to xBestIndex() */
int nConstraint; /* Number of constraints in p */
int bIn; /* True if plan uses IN(...) operator */
@@ -3517,7 +3537,7 @@ static int whereLoopAddOr(
WhereClause tempWC;
WhereLoopBuilder sSubBuild;
WhereOrSet sSum, sCur;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pWC = pBuilder->pWC;
pWCEnd = pWC->a + pWC->nTerm;
@@ -3633,8 +3653,8 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
Bitmask mPrior = 0;
int iTab;
SrcList *pTabList = pWInfo->pTabList;
- struct SrcList_item *pItem;
- struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
+ SrcItem *pItem;
+ SrcItem *pEnd = &pTabList->a[pWInfo->nLevel];
sqlite3 *db = pWInfo->pParse->db;
int rc = SQLITE_OK;
WhereLoop *pNew;
@@ -3657,7 +3677,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pItem->pTab) ){
- struct SrcList_item *p;
+ SrcItem *p;
for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
@@ -4512,7 +4532,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
*/
static int whereShortCut(WhereLoopBuilder *pBuilder){
WhereInfo *pWInfo;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
WhereClause *pWC;
WhereTerm *pTerm;
WhereLoop *pLoop;
@@ -4971,7 +4991,7 @@ WhereInfo *sqlite3WhereBegin(
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
pWInfo->revMask = ALLBITS;
}
- if( pParse->nErr || NEVER(db->mallocFailed) ){
+ if( pParse->nErr || db->mallocFailed ){
goto whereBeginError;
}
#ifdef WHERETRACE_ENABLED
@@ -5042,7 +5062,7 @@ WhereInfo *sqlite3WhereBegin(
}
for(i=pWInfo->nLevel-1; i>=1; i--){
WhereTerm *pTerm, *pEnd;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pLoop = pWInfo->a[i].pWLoop;
pItem = &pWInfo->pTabList->a[pLoop->iTab];
if( (pItem->fg.jointype & JT_LEFT)==0 ) continue;
@@ -5132,7 +5152,7 @@ WhereInfo *sqlite3WhereBegin(
for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom];
pTab = pTabItem->pTab;
@@ -5469,7 +5489,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
int k, last;
VdbeOp *pOp, *pLastOp;
Index *pIdx = 0;
- struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
+ SrcItem *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
pLoop = pLevel->pWLoop;
@@ -5545,7 +5565,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
#endif
pOp = sqlite3VdbeGetOp(v, k);
pLastOp = pOp + (last - k);
- assert( pOpnErr>0 && pOp==pLastOp) );
do{
if( pOp->p1!=pLevel->iTabCur ){
/* no-op */
diff --git a/src/whereInt.h b/src/whereInt.h
index 6c969af9c2..8896da0271 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -17,19 +17,6 @@
#ifndef SQLITE_WHEREINT_H
#define SQLITE_WHEREINT_H
-/*
-** Trace output macros
-*/
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-/***/ extern int sqlite3WhereTrace;
-#endif
-#if defined(SQLITE_DEBUG) \
- && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
-# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
-# define WHERETRACE_ENABLED 1
-#else
-# define WHERETRACE(K,X)
-#endif
/* Forward references
*/
@@ -283,11 +270,7 @@ struct WhereTerm {
#define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT4
-# define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
-#else
-# define TERM_VNULL 0x0000 /* Disabled if not using stat4 */
-#endif
+#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
#define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */
#define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */
#define TERM_LIKE 0x0400 /* The original LIKE operator */
@@ -557,7 +540,7 @@ Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*);
Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
-void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
+void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
diff --git a/src/wherecode.c b/src/wherecode.c
index 4afe0ac9c9..a7a76fb704 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -129,7 +129,7 @@ int sqlite3WhereExplainOneScan(
if( sqlite3ParseToplevel(pParse)->explain==2 )
#endif
{
- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
+ SrcItem *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
@@ -922,7 +922,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
** Insert an OP_CursorHint instruction if it is appropriate to do so.
*/
static void codeCursorHint(
- struct SrcList_item *pTabItem, /* FROM clause item */
+ SrcItem *pTabItem, /* FROM clause item */
WhereInfo *pWInfo, /* The where clause */
WhereLevel *pLevel, /* Which loop to provide hints for */
WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
@@ -1297,7 +1297,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
WhereClause *pWC; /* Decomposition of the entire WHERE clause */
WhereTerm *pTerm; /* A WHERE clause term */
sqlite3 *db; /* Database connection */
- struct SrcList_item *pTabItem; /* FROM clause term being coded */
+ SrcItem *pTabItem; /* FROM clause term being coded */
int addrBrk; /* Jump here to break out of the loop */
int addrHalt; /* addrBrk for the outermost loop */
int addrCont; /* Jump here to continue with next cycle */
@@ -1743,6 +1743,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
SWAP(u8, nBtm, nTop);
}
+ if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){
+ /* In case OP_SeekScan is used, ensure that the index cursor does not
+ ** point to a valid row for the first iteration of this loop. */
+ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
+ }
+
/* Generate code to evaluate all constraint terms using == or IN
** and store the values of those terms in an array of registers
** starting at regBase.
@@ -2079,7 +2085,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
*/
if( pWInfo->nLevel>1 ){
int nNotReady; /* The number of notReady tables */
- struct SrcList_item *origSrc; /* Original list of tables */
+ SrcItem *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
pOrTab = sqlite3StackAllocRaw(db,
sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
diff --git a/src/whereexpr.c b/src/whereexpr.c
index a77eb36106..7b80c41393 100644
--- a/src/whereexpr.c
+++ b/src/whereexpr.c
@@ -1007,6 +1007,276 @@ static int exprMightBeIndexed(
return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
}
+/*
+** Expression callback for exprUsesSrclist().
+*/
+static int exprUsesSrclistCb(Walker *p, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN ){
+ SrcList *pSrc = p->u.pSrcList;
+ int iCsr = pExpr->iTable;
+ int ii;
+ for(ii=0; iinSrc; ii++){
+ if( pSrc->a[ii].iCursor==iCsr ){
+ return p->eCode ? WRC_Abort : WRC_Continue;
+ }
+ }
+ return p->eCode ? WRC_Continue : WRC_Abort;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Select callback for exprUsesSrclist().
+*/
+static int exprUsesSrclistSelectCb(Walker *NotUsed1, Select *NotUsed2){
+ UNUSED_PARAMETER(NotUsed1);
+ UNUSED_PARAMETER(NotUsed2);
+ return WRC_Abort;
+}
+
+/*
+** This function always returns true if expression pExpr contains
+** a sub-select.
+**
+** If there is no sub-select in pExpr, then return true if pExpr
+** contains a TK_COLUMN node for a table that is (bUses==1)
+** or is not (bUses==0) in pSrc.
+**
+** Said another way:
+**
+** bUses Return Meaning
+** -------- ------ ------------------------------------------------
+**
+** bUses==1 true pExpr contains either a sub-select or a
+** TK_COLUMN referencing pSrc.
+**
+** bUses==1 false pExpr contains no sub-selects and all TK_COLUMN
+** nodes reference tables not found in pSrc
+**
+** bUses==0 true pExpr contains either a sub-select or a TK_COLUMN
+** that references a table not in pSrc.
+**
+** bUses==0 false pExpr contains no sub-selects and all TK_COLUMN
+** nodes reference pSrc
+*/
+static int exprUsesSrclist(SrcList *pSrc, Expr *pExpr, int bUses){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.eCode = bUses;
+ sWalker.u.pSrcList = pSrc;
+ sWalker.xExprCallback = exprUsesSrclistCb;
+ sWalker.xSelectCallback = exprUsesSrclistSelectCb;
+ return (sqlite3WalkExpr(&sWalker, pExpr)==WRC_Abort);
+}
+
+/*
+** Context object used by exprExistsToInIter() as it iterates through an
+** expression tree.
+*/
+struct ExistsToInCtx {
+ SrcList *pSrc; /* The tables in an EXISTS(SELECT ... FROM ...) */
+ Expr *pInLhs; /* OUT: Use this as the LHS of the IN operator */
+ Expr *pEq; /* OUT: The == term that include pInLhs */
+ Expr **ppAnd; /* OUT: The AND operator that includes pEq as a child */
+ Expr **ppParent; /* The AND operator currently being examined */
+};
+
+/*
+** Iterate through all AND connected nodes in the expression tree
+** headed by (*ppExpr), populating the structure passed as the first
+** argument with the values required by exprAnalyzeExistsFindEq().
+**
+** This function returns non-zero if the expression tree does not meet
+** the two conditions described by the header comment for
+** exprAnalyzeExistsFindEq(), or zero if it does.
+*/
+static int exprExistsToInIter(struct ExistsToInCtx *p, Expr **ppExpr){
+ Expr *pExpr = *ppExpr;
+ switch( pExpr->op ){
+ case TK_AND:
+ p->ppParent = ppExpr;
+ if( exprExistsToInIter(p, &pExpr->pLeft) ) return 1;
+ p->ppParent = ppExpr;
+ if( exprExistsToInIter(p, &pExpr->pRight) ) return 1;
+ break;
+ case TK_EQ: {
+ int bLeft = exprUsesSrclist(p->pSrc, pExpr->pLeft, 0);
+ int bRight = exprUsesSrclist(p->pSrc, pExpr->pRight, 0);
+ if( bLeft || bRight ){
+ if( (bLeft && bRight) || p->pInLhs ) return 1;
+ p->pInLhs = bLeft ? pExpr->pLeft : pExpr->pRight;
+ if( exprUsesSrclist(p->pSrc, p->pInLhs, 1) ) return 1;
+ p->pEq = pExpr;
+ p->ppAnd = p->ppParent;
+ }
+ break;
+ }
+ default:
+ if( exprUsesSrclist(p->pSrc, pExpr, 0) ){
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/*
+** This function is used by exprAnalyzeExists() when creating virtual IN(...)
+** terms equivalent to user-supplied EXIST(...) clauses. It splits the WHERE
+** clause of the Select object passed as the first argument into one or more
+** expressions joined by AND operators, and then tests if the following are
+** true:
+**
+** 1. Exactly one of the AND separated terms refers to the outer
+** query, and it is an == (TK_EQ) expression.
+**
+** 2. Only one side of the == expression refers to the outer query, and
+** it does not refer to any columns from the inner query.
+**
+** If both these conditions are true, then a pointer to the side of the ==
+** expression that refers to the outer query is returned. The caller will
+** use this expression as the LHS of the IN(...) virtual term. Or, if one
+** or both of the above conditions are not true, NULL is returned.
+**
+** If non-NULL is returned and ppEq is non-NULL, *ppEq is set to point
+** to the == expression node before returning. If pppAnd is non-NULL and
+** the == node is not the root of the WHERE clause, then *pppAnd is set
+** to point to the pointer to the AND node that is the parent of the ==
+** node within the WHERE expression tree.
+*/
+static Expr *exprAnalyzeExistsFindEq(
+ Select *pSel, /* The SELECT of the EXISTS */
+ Expr **ppEq, /* OUT: == node from WHERE clause */
+ Expr ***pppAnd /* OUT: Pointer to parent of ==, if any */
+){
+ struct ExistsToInCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pSrc = pSel->pSrc;
+ if( exprExistsToInIter(&ctx, &pSel->pWhere) ){
+ return 0;
+ }
+ if( ppEq ) *ppEq = ctx.pEq;
+ if( pppAnd ) *pppAnd = ctx.ppAnd;
+ return ctx.pInLhs;
+}
+
+/*
+** Term idxTerm of the WHERE clause passed as the second argument is an
+** EXISTS expression with a correlated SELECT statement on the RHS.
+** This function analyzes the SELECT statement, and if possible adds an
+** equivalent "? IN(SELECT...)" virtual term to the WHERE clause.
+**
+** For an EXISTS term such as the following:
+**
+** EXISTS (SELECT ... FROM WHERE = AND )
+**
+** The virtual IN() term added is:
+**
+** IN (SELECT FROM WHERE )
+**
+** The virtual term is only added if the following conditions are met:
+**
+** 1. The sub-select must not be an aggregate or use window functions,
+**
+** 2. The sub-select must not be a compound SELECT,
+**
+** 3. Expression must refer to at least one column from the outer
+** query, and must not refer to any column from the inner query
+** (i.e. from ).
+**
+** 4. and must not refer to any values from the outer query.
+** In other words, once has been removed, the inner query
+** must not be correlated.
+**
+*/
+static void exprAnalyzeExists(
+ SrcList *pSrc, /* the FROM clause */
+ WhereClause *pWC, /* the WHERE clause */
+ int idxTerm /* Index of the term to be analyzed */
+){
+ Parse *pParse = pWC->pWInfo->pParse;
+ WhereTerm *pTerm = &pWC->a[idxTerm];
+ Expr *pExpr = pTerm->pExpr;
+ Select *pSel = pExpr->x.pSelect;
+ Expr *pDup = 0;
+ Expr *pEq = 0;
+ Expr *pRet = 0;
+ Expr *pInLhs = 0;
+ Expr **ppAnd = 0;
+ int idxNew;
+ sqlite3 *db = pParse->db;
+
+ assert( pExpr->op==TK_EXISTS );
+ assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
+
+ if( pSel->selFlags & SF_Aggregate ) return;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSel->pWin ) return;
+#endif
+ if( pSel->pPrior ) return;
+ if( pSel->pWhere==0 ) return;
+ if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;
+
+ pDup = sqlite3ExprDup(db, pExpr, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDup);
+ return;
+ }
+ pSel = pDup->x.pSelect;
+ sqlite3ExprListDelete(db, pSel->pEList);
+ pSel->pEList = 0;
+
+ pInLhs = exprAnalyzeExistsFindEq(pSel, &pEq, &ppAnd);
+ assert( pInLhs && pEq );
+ assert( pEq==pSel->pWhere || ppAnd );
+ if( pInLhs==pEq->pLeft ){
+ pRet = pEq->pRight;
+ }else{
+ CollSeq *p = sqlite3ExprCompareCollSeq(pParse, pEq);
+ pInLhs = sqlite3ExprAddCollateString(pParse, pInLhs, p?p->zName:"BINARY");
+ pRet = pEq->pLeft;
+ }
+
+ assert( pDup->pLeft==0 );
+ pDup->op = TK_IN;
+ pDup->pLeft = pInLhs;
+ pDup->flags &= ~EP_VarSelect;
+ if( pRet->op==TK_VECTOR ){
+ pSel->pEList = pRet->x.pList;
+ pRet->x.pList = 0;
+ sqlite3ExprDelete(db, pRet);
+ }else{
+ pSel->pEList = sqlite3ExprListAppend(pParse, 0, pRet);
+ }
+ pEq->pLeft = 0;
+ pEq->pRight = 0;
+ if( ppAnd ){
+ Expr *pAnd = *ppAnd;
+ Expr *pOther = (pAnd->pLeft==pEq) ? pAnd->pRight : pAnd->pLeft;
+ pAnd->pLeft = pAnd->pRight = 0;
+ sqlite3ExprDelete(db, pAnd);
+ *ppAnd = pOther;
+ }else{
+ assert( pSel->pWhere==pEq );
+ pSel->pWhere = 0;
+ }
+ sqlite3ExprDelete(db, pEq);
+
+#ifdef WHERETRACE_ENABLED /* 0x20 */
+ if( sqlite3WhereTrace & 0x20 ){
+ sqlite3DebugPrintf("Convert EXISTS:\n");
+ sqlite3TreeViewExpr(0, pExpr, 0);
+ sqlite3DebugPrintf("into IN:\n");
+ sqlite3TreeViewExpr(0, pDup, 0);
+ }
+#endif
+ idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ pWC->a[idxTerm].wtFlags |= TERM_COPIED;
+}
+
/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
@@ -1140,6 +1410,12 @@ static void exprAnalyze(
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
+ }else if( op==TK_ISNULL && 0==sqlite3ExprCanBeNull(pLeft) ){
+ pExpr->op = TK_TRUEFALSE;
+ pExpr->u.zToken = "false";
+ ExprSetProperty(pExpr, EP_IsFalse);
+ pTerm->prereqAll = 0;
+ pTerm->eOperator = 0;
}
}
@@ -1192,6 +1468,52 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
+ else if( pExpr->op==TK_EXISTS ){
+ /* Perhaps treat an EXISTS operator as an IN operator */
+ if( (pExpr->flags & EP_VarSelect)!=0
+ && OptimizationEnabled(db, SQLITE_ExistsToIN)
+ ){
+ exprAnalyzeExists(pSrc, pWC, idxTerm);
+ }
+ }
+
+ /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently
+ ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
+ ** virtual term of that form.
+ **
+ ** The virtual term must be tagged with TERM_VNULL.
+ */
+ else if( pExpr->op==TK_NOTNULL ){
+ if( pExpr->pLeft->op==TK_COLUMN
+ && pExpr->pLeft->iColumn>=0
+ && !ExprHasProperty(pExpr, EP_FromJoin)
+ ){
+ Expr *pNewExpr;
+ Expr *pLeft = pExpr->pLeft;
+ int idxNew;
+ WhereTerm *pNewTerm;
+
+ pNewExpr = sqlite3PExpr(pParse, TK_GT,
+ sqlite3ExprDup(db, pLeft, 0),
+ sqlite3ExprAlloc(db, TK_NULL, 0, 0));
+
+ idxNew = whereClauseInsert(pWC, pNewExpr,
+ TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
+ if( idxNew ){
+ pNewTerm = &pWC->a[idxNew];
+ pNewTerm->prereqRight = 0;
+ pNewTerm->leftCursor = pLeft->iTable;
+ pNewTerm->u.x.leftColumn = pLeft->iColumn;
+ pNewTerm->eOperator = WO_GT;
+ markTermAsChild(pWC, idxNew, idxTerm);
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags |= TERM_COPIED;
+ pNewTerm->prereqAll = pTerm->prereqAll;
+ }
+ }
+ }
+
+
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
/* Add constraints to reduce the search space on a LIKE or GLOB
** operator.
@@ -1206,7 +1528,8 @@ static void exprAnalyze(
** bound is made all lowercase so that the bounds also work when comparing
** BLOBs.
*/
- if( pWC->op==TK_AND
+ else if( pExpr->op==TK_FUNCTION
+ && pWC->op==TK_AND
&& isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase)
){
Expr *pLeft; /* LHS of LIKE/GLOB operator */
@@ -1276,6 +1599,65 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
+ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
+ ** new terms for each component comparison - "a = ?" and "b = ?". The
+ ** new terms completely replace the original vector comparison, which is
+ ** no longer used.
+ **
+ ** This is only required if at least one side of the comparison operation
+ ** is not a sub-select. */
+ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS)
+ && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
+ && sqlite3ExprVectorSize(pExpr->pRight)==nLeft
+ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
+ || (pExpr->pRight->flags & EP_xIsSelect)==0)
+ && pWC->op==TK_AND
+ ){
+ int i;
+ for(i=0; ipLeft, i);
+ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
+
+ pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
+ transferJoinMarkings(pNew, pExpr);
+ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
+ }
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
+ pTerm->eOperator = 0;
+ }
+
+ /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
+ ** a virtual term for each vector component. The expression object
+ ** used by each such virtual term is pExpr (the full vector IN(...)
+ ** expression). The WhereTerm.u.x.iField variable identifies the index within
+ ** the vector on the LHS that the virtual term represents.
+ **
+ ** This only works if the RHS is a simple SELECT (not a compound) that does
+ ** not use window functions.
+ */
+ else if( pExpr->op==TK_IN
+ && pTerm->u.x.iField==0
+ && pExpr->pLeft->op==TK_VECTOR
+ && pExpr->x.pSelect->pPrior==0
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ && pExpr->x.pSelect->pWin==0
+#endif
+ && pWC->op==TK_AND
+ ){
+ int i;
+ for(i=0; ipLeft); i++){
+ int idxNew;
+ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
+ pWC->a[idxNew].u.x.iField = i+1;
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ }
+ }
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Add a WO_AUX auxiliary term to the constraint set if the
** current expression is of the form "column OP expr" where OP
@@ -1286,7 +1668,7 @@ static void exprAnalyze(
** virtual tables. The native query optimizer does not attempt
** to do anything with MATCH functions.
*/
- if( pWC->op==TK_AND ){
+ else if( pWC->op==TK_AND ){
Expr *pRight = 0, *pLeft = 0;
int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
while( res-- > 0 ){
@@ -1322,102 +1704,6 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
- /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
- ** new terms for each component comparison - "a = ?" and "b = ?". The
- ** new terms completely replace the original vector comparison, which is
- ** no longer used.
- **
- ** This is only required if at least one side of the comparison operation
- ** is not a sub-select. */
- if( pWC->op==TK_AND
- && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
- && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
- && sqlite3ExprVectorSize(pExpr->pRight)==nLeft
- && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
- || (pExpr->pRight->flags & EP_xIsSelect)==0)
- ){
- int i;
- for(i=0; ipLeft, i);
- Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
-
- pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
- transferJoinMarkings(pNew, pExpr);
- idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
- exprAnalyze(pSrc, pWC, idxNew);
- }
- pTerm = &pWC->a[idxTerm];
- pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
- pTerm->eOperator = 0;
- }
-
- /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
- ** a virtual term for each vector component. The expression object
- ** used by each such virtual term is pExpr (the full vector IN(...)
- ** expression). The WhereTerm.u.x.iField variable identifies the index within
- ** the vector on the LHS that the virtual term represents.
- **
- ** This only works if the RHS is a simple SELECT (not a compound) that does
- ** not use window functions.
- */
- if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->u.x.iField==0
- && pExpr->pLeft->op==TK_VECTOR
- && pExpr->x.pSelect->pPrior==0
-#ifndef SQLITE_OMIT_WINDOWFUNC
- && pExpr->x.pSelect->pWin==0
-#endif
- ){
- int i;
- for(i=0; ipLeft); i++){
- int idxNew;
- idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
- pWC->a[idxNew].u.x.iField = i+1;
- exprAnalyze(pSrc, pWC, idxNew);
- markTermAsChild(pWC, idxNew, idxTerm);
- }
- }
-
-#ifdef SQLITE_ENABLE_STAT4
- /* When sqlite_stat4 histogram data is available an operator of the
- ** form "x IS NOT NULL" can sometimes be evaluated more efficiently
- ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
- ** virtual term of that form.
- **
- ** Note that the virtual term must be tagged with TERM_VNULL.
- */
- if( pExpr->op==TK_NOTNULL
- && pExpr->pLeft->op==TK_COLUMN
- && pExpr->pLeft->iColumn>=0
- && !ExprHasProperty(pExpr, EP_FromJoin)
- && OptimizationEnabled(db, SQLITE_Stat4)
- ){
- Expr *pNewExpr;
- Expr *pLeft = pExpr->pLeft;
- int idxNew;
- WhereTerm *pNewTerm;
-
- pNewExpr = sqlite3PExpr(pParse, TK_GT,
- sqlite3ExprDup(db, pLeft, 0),
- sqlite3ExprAlloc(db, TK_NULL, 0, 0));
-
- idxNew = whereClauseInsert(pWC, pNewExpr,
- TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
- if( idxNew ){
- pNewTerm = &pWC->a[idxNew];
- pNewTerm->prereqRight = 0;
- pNewTerm->leftCursor = pLeft->iTable;
- pNewTerm->u.x.leftColumn = pLeft->iColumn;
- pNewTerm->eOperator = WO_GT;
- markTermAsChild(pWC, idxNew, idxTerm);
- pTerm = &pWC->a[idxTerm];
- pTerm->wtFlags |= TERM_COPIED;
- pNewTerm->prereqAll = pTerm->prereqAll;
- }
- }
-#endif /* SQLITE_ENABLE_STAT4 */
-
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
*/
@@ -1576,7 +1862,7 @@ void sqlite3WhereExprAnalyze(
*/
void sqlite3WhereTabFuncArgs(
Parse *pParse, /* Parsing context */
- struct SrcList_item *pItem, /* The FROM clause term to process */
+ SrcItem *pItem, /* The FROM clause term to process */
WhereClause *pWC /* Xfer function arguments to here */
){
Table *pTab;
diff --git a/src/window.c b/src/window.c
index 88ff7d314d..09572ec033 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1304,15 +1304,19 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
** SELECT, or (b) the windows already linked use a compatible window frame.
*/
void sqlite3WindowLink(Select *pSel, Window *pWin){
- if( pSel!=0
- && (0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0))
- ){
- pWin->pNextWin = pSel->pWin;
- if( pSel->pWin ){
- pSel->pWin->ppThis = &pWin->pNextWin;
+ if( pSel ){
+ if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){
+ pWin->pNextWin = pSel->pWin;
+ if( pSel->pWin ){
+ pSel->pWin->ppThis = &pWin->pNextWin;
+ }
+ pSel->pWin = pWin;
+ pWin->ppThis = &pSel->pWin;
+ }else{
+ if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){
+ pSel->selFlags |= SF_MultiPart;
+ }
}
- pSel->pWin = pWin;
- pWin->ppThis = &pSel->pWin;
}
}
diff --git a/test/alterauth2.test b/test/alterauth2.test
index bd589cda1d..a411408b58 100644
--- a/test/alterauth2.test
+++ b/test/alterauth2.test
@@ -95,4 +95,21 @@ do_auth_test 1.2 {
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
}
+do_auth_test 1.3 {
+ ALTER TABLE t2 DROP COLUMN c;
+} {
+ {SQLITE_FUNCTION {} like {} {}}
+ {SQLITE_FUNCTION {} sqlite_drop_column {} {}}
+ {SQLITE_FUNCTION {} sqlite_rename_test {} {}}
+ {SQLITE_READ sqlite_master name main {}}
+ {SQLITE_READ sqlite_master sql main {}}
+ {SQLITE_READ sqlite_master tbl_name main {}}
+ {SQLITE_READ sqlite_master type main {}}
+ {SQLITE_READ sqlite_temp_master name temp {}}
+ {SQLITE_READ sqlite_temp_master sql temp {}}
+ {SQLITE_READ sqlite_temp_master type temp {}}
+ {SQLITE_SELECT {} {} {} {}}
+ {SQLITE_UPDATE sqlite_master sql main {}}
+}
+
finish_test
diff --git a/test/altercol.test b/test/altercol.test
index 1479b3a7d3..d2406e4c96 100644
--- a/test/altercol.test
+++ b/test/altercol.test
@@ -13,8 +13,6 @@
# file format change that may be used in the future to implement
# "ALTER TABLE ... RENAME COLUMN ... TO".
#
-# $Id: alter4.test,v 1.1 2009/02/02 18:03:22 drh Exp $
-#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -804,6 +802,25 @@ do_execsql_test 20.110 {
SELECT sql FROM sqlite_master WHERE name='t1';
} {{CREATE TABLE t1(xx UNIQUE,yy UNIQUE,zz UNIQUE,UNIQUE(xx),PRIMARY KEY(yy),UNIQUE(zz))}}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 21.0 {
+ CREATE TABLE t1(a, b, c NOT NULL);
+ CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.c IS NOT NULL BEGIN
+ SELECT c NOT NULL FROM t1;
+ END;
+}
+
+do_execsql_test 21.1 {
+ ALTER TABLE t1 RENAME c TO d;
+}
+
+do_execsql_test 21.2 {
+ SELECT sql FROM sqlite_schema WHERE name IS 'tr1'
+} {{CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.d IS NOT NULL BEGIN
+ SELECT d NOT NULL FROM t1;
+ END}
+}
finish_test
diff --git a/test/altercorrupt.test b/test/altercorrupt.test
new file mode 100644
index 0000000000..5c50fa4a47
--- /dev/null
+++ b/test/altercorrupt.test
@@ -0,0 +1,175 @@
+# 2019-01-11
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix altercorrupt
+
+database_may_be_corrupt
+
+#--------------------------------------------------------------------------
+reset_db
+do_test 1.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 24576 pagesize 4096 filename crash-685346d89b5e5f.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
+| 32: 00 00 63 00 00 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
+| 48: 00 00 00 00 00 00 0f f0 00 00 00 01 00 00 00 00 ................
+| 64: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
+| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
+| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
+| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
+| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
+| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
+| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
+| 3856: 45 20 74 33 28 78 20 49 4e 54 45 47 45 52 20 50 E t3(x INTEGER P
+| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
+| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
+| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
+| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
+| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
+| 3952: 4f 55 54 20 52 4f 57 49 44 58 03 07 17 11 11 01 OUT ROWIDX......
+| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
+| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
+| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
+| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
+| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
+| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
+| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
+| 4080: 5f 74 31 5f 31 74 31 03 00 00 00 08 00 00 00 00 _t1_1t1.........
+| page 2 offset 4096
+| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
+| 16: 0f ca 0f 8f 0f b4 0f a9 0f 9e 0f 93 00 00 00 00 ................
+| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
+| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 01 01 01 ......Zm........
+| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
+| 4032: 06 05 01 01 01 01 06 3c 6a 0c 09 05 05 01 01 01 .......5),e
+| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
+| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
+| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
+| 4080: 5f 74 31 5f 31 84 31 03 01 00 00 08 00 00 00 00 _t1_1.1.........
+| page 2 offset 4096
+| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
+| 16: 0f ca 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
+| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 00 f1 01 ......Zm........
+| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
+| 4032: 06 05 01 00 f1 01 06 3c 6a 0c 09 05 05 01 01 01 .......10));
+ CREATE TABLE t13(a, b, c CHECK(c>10));
+}
+do_catchsql_test 3.1 {
+ ALTER TABLE t12 DROP COLUMN c;
+} {1 {error in table t12 after drop column: no such column: c}}
+
+do_catchsql_test 3.2 {
+ ALTER TABLE t13 DROP COLUMN c;
+} {0 {}}
+
+#-------------------------------------------------------------------------
+# Test that generated columns can be dropped. And that other columns from
+# tables that contain generated columns can be dropped.
+#
+foreach {tn wo vs} {
+ 1 "" ""
+ 2 "" VIRTUAL
+ 3 "" STORED
+ 4 "WITHOUT ROWID" STORED
+ 5 "WITHOUT ROWID" VIRTUAL
+} {
+ reset_db
+
+ do_execsql_test 4.$tn.0 "
+ CREATE TABLE 'my table'(a, b PRIMARY KEY, c AS (a+b) $vs, d) $wo
+ "
+ do_execsql_test 4.$tn.1 {
+ INSERT INTO "my table"(a, b, d) VALUES(1, 2, 'hello');
+ INSERT INTO "my table"(a, b, d) VALUES(3, 4, 'world');
+
+ SELECT * FROM "my table"
+ } {
+ 1 2 3 hello
+ 3 4 7 world
+ }
+
+ do_execsql_test 4.$tn.2 {
+ ALTER TABLE "my table" DROP COLUMN c;
+ }
+ do_execsql_test 4.$tn.3 {
+ SELECT * FROM "my table"
+ } {
+ 1 2 hello
+ 3 4 world
+ }
+
+ do_execsql_test 4.$tn.4 "
+ CREATE TABLE x1(a, b, c PRIMARY KEY, d AS (b+c) $vs, e) $wo
+ "
+ do_execsql_test 4.$tn.5 {
+ INSERT INTO x1(a, b, c, e) VALUES(1, 2, 3, 4);
+ INSERT INTO x1(a, b, c, e) VALUES(5, 6, 7, 8);
+ INSERT INTO x1(a, b, c, e) VALUES(9, 10, 11, 12);
+ SELECT * FROM x1;
+ } {
+ 1 2 3 5 4
+ 5 6 7 13 8
+ 9 10 11 21 12
+ }
+
+ do_execsql_test 4.$tn.6 {
+ ALTER TABLE x1 DROP COLUMN a
+ }
+ do_execsql_test 4.$tn.7 {
+ SELECT * FROM x1
+ } {
+ 2 3 5 4
+ 6 7 13 8
+ 10 11 21 12
+ }
+ do_execsql_test 4.$tn.8 {
+ ALTER TABLE x1 DROP COLUMN e
+ }
+ do_execsql_test 4.$tn.9 {
+ SELECT * FROM x1
+ } {
+ 2 3 5
+ 6 7 13
+ 10 11 21
+ }
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE p1(a PRIMARY KEY, b UNIQUE);
+ CREATE TABLE c1(x, y, z REFERENCES p1(c));
+ CREATE TABLE c2(x, y, z, w REFERENCES p1(b));
+}
+do_execsql_test 5.1 {
+ ALTER TABLE c1 DROP COLUMN z;
+ ALTER TABLE c2 DROP COLUMN z;
+ SELECT sql FROM sqlite_schema WHERE name IN ('c1', 'c2');
+} {
+ {CREATE TABLE c1(x, y)}
+ {CREATE TABLE c2(x, y, w REFERENCES p1(b))}
+}
+
+do_execsql_test 5.2.1 {
+ CREATE VIEW v1 AS SELECT d, e FROM p1
+}
+do_catchsql_test 5.2.2 {
+ ALTER TABLE c1 DROP COLUMN x
+} {1 {error in view v1: no such column: d}}
+do_execsql_test 5.3.1 {
+ DROP VIEW v1;
+ CREATE VIEW v1 AS SELECT x, y FROM c1;
+}
+do_catchsql_test 5.3.2 {
+ ALTER TABLE c1 DROP COLUMN x
+} {1 {error in view v1 after drop column: no such column: x}}
+
+do_execsql_test 5.4.1 {
+ CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
+ INSERT INTO p1 VALUES(new.y, new.xyz);
+ END;
+}
+do_catchsql_test 5.4.2 {
+ ALTER TABLE c1 DROP COLUMN y
+} {1 {error in trigger tr: no such column: new.xyz}}
+do_execsql_test 5.5.1 {
+ DROP TRIGGER tr;
+ CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
+ INSERT INTO p1 VALUES(new.y, new.z);
+ END;
+}
+do_catchsql_test 5.5.2 {
+ ALTER TABLE c1 DROP COLUMN y
+} {1 {error in trigger tr: no such column: new.z}}
+
+finish_test
diff --git a/test/alterdropcol2.test b/test/alterdropcol2.test
new file mode 100644
index 0000000000..8af3677d32
--- /dev/null
+++ b/test/alterdropcol2.test
@@ -0,0 +1,214 @@
+# 2021 February 19
+#
+# 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.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix alterdropcol2
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+# EVIDENCE-OF: R-58318-35349 The DROP COLUMN syntax is used to remove an
+# existing column from a table.
+do_execsql_test 1.0 {
+ CREATE TABLE t1(c, b, a, PRIMARY KEY(b, a)) WITHOUT ROWID;
+ INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
+}
+do_execsql_test 1.1 {
+ ALTER TABLE t1 DROP c;
+}
+
+# EVIDENCE-OF: The DROP COLUMN command removes the named column from the table,
+# and also rewrites the entire table to purge the data associated with that
+# column.
+do_execsql_test 1.2.1 {
+ SELECT * FROM t1;
+} {2 3 5 6}
+
+do_execsql_test 1.2.2 {
+ SELECT sql FROM sqlite_schema;
+} {
+ {CREATE TABLE t1(b, a, PRIMARY KEY(b, a)) WITHOUT ROWID}
+}
+
+proc do_atdc_error_test {tn schema atdc error} {
+ reset_db
+ execsql $schema
+ uplevel [list do_catchsql_test $tn $atdc [list 1 [string trim $error]]]
+}
+
+#-------------------------------------------------------------------------
+# Test cases 2.* attempt to verify the following:
+#
+# EVIDENCE-OF: R-24098-10282 The DROP COLUMN command only works if the column
+# is not referenced by any other parts of the schema and is not a PRIMARY KEY
+# and does not have a UNIQUE constraint.
+#
+
+# EVIDENCE-OF: R-05184-13006 The column is a PRIMARY KEY.
+#
+do_atdc_error_test 2.1.1 {
+ CREATE TABLE x1(a PRIMARY KEY, b, c);
+} {
+ ALTER TABLE x1 DROP COLUMN a
+} {
+ cannot drop PRIMARY KEY column: "a"
+}
+
+# EVIDENCE-OF: R-43412-16016 The column has a UNIQUE constraint.
+#
+do_atdc_error_test 2.2.1 {
+ CREATE TABLE x1(a PRIMARY KEY, b, c UNIQUE);
+} {
+ ALTER TABLE x1 DROP COLUMN c
+} {
+ cannot drop UNIQUE column: "c"
+}
+do_atdc_error_test 2.2.2 {
+ CREATE TABLE x1(a PRIMARY KEY, b, c, UNIQUE(b, c));
+} {
+ ALTER TABLE x1 DROP COLUMN c
+} {
+ error in table x1 after drop column: no such column: c
+}
+
+# EVIDENCE-OF: R-46731-08965: The column is indexed.
+#
+do_atdc_error_test 2.3.1 {
+ CREATE TABLE 'one two'('x y', 'z 1', 'a b');
+ CREATE INDEX idx ON 'one two'('z 1');
+} {
+ ALTER TABLE 'one two' DROP COLUMN 'z 1'
+} {
+ error in index idx after drop column: no such column: z 1
+}
+do_atdc_error_test 2.3.2 {
+ CREATE TABLE x1(a, b, c);
+ CREATE INDEX idx ON x1(a);
+} {
+ ALTER TABLE x1 DROP COLUMN a;
+} {
+ error in index idx after drop column: no such column: a
+}
+
+# EVIDENCE-OF: R-46731-08965: The column is indexed.
+#
+do_atdc_error_test 2.4.1 {
+ CREATE TABLE x1234(a, b, c PRIMARY KEY) WITHOUT ROWID;
+ CREATE INDEX i1 ON x1234(b) WHERE ((a+5) % 10)==0;
+} {
+ ALTER TABLE x1234 DROP a
+} {
+ error in index i1 after drop column: no such column: a
+}
+
+# EVIDENCE-OF: R-18825-17786 The column appears in a table CHECK constraint.
+#
+do_atdc_error_test 2.5.1 {
+ CREATE TABLE x1234(a, b, c PRIMARY KEY, CHECK(((a+5)%10)!=0)) WITHOUT ROWID;
+} {
+ ALTER TABLE x1234 DROP a
+} {
+ error in table x1234 after drop column: no such column: a
+}
+
+# EVIDENCE-OF: R-55640-01652 The column is used in a foreign key constraint.
+#
+do_atdc_error_test 2.6.1 {
+ CREATE TABLE p1(x, y UNIQUE);
+ CREATE TABLE c1(u, v, FOREIGN KEY (v) REFERENCES p1(y))
+} {
+ ALTER TABLE c1 DROP v
+} {
+ error in table c1 after drop column: unknown column "v" in foreign key definition
+}
+
+# EVIDENCE-OF: R-20795-39479 The column is used in the expression of a
+# generated column.
+do_atdc_error_test 2.7.1 {
+ CREATE TABLE c1(u, v, w AS (u+v));
+} {
+ ALTER TABLE c1 DROP v
+} {
+ error in table c1 after drop column: no such column: v
+}
+do_atdc_error_test 2.7.2 {
+ CREATE TABLE c1(u, v, w AS (u+v) STORED);
+} {
+ ALTER TABLE c1 DROP u
+} {
+ error in table c1 after drop column: no such column: u
+}
+
+# EVIDENCE-OF: R-01515-49025 The column appears in a trigger or view.
+#
+do_atdc_error_test 2.8.1 {
+ CREATE TABLE log(l);
+ CREATE TABLE c1(u, v, w);
+ CREATE TRIGGER tr1 AFTER INSERT ON c1 BEGIN
+ INSERT INTO log VALUES(new.w);
+ END;
+} {
+ ALTER TABLE c1 DROP w
+} {
+ error in trigger tr1 after drop column: no such column: new.w
+}
+do_atdc_error_test 2.8.2 {
+ CREATE TABLE c1(u, v, w);
+ CREATE VIEW v1 AS SELECT u, v, w FROM c1;
+} {
+ ALTER TABLE c1 DROP w
+} {
+ error in view v1 after drop column: no such column: w
+}
+do_atdc_error_test 2.8.3 {
+ CREATE TABLE c1(u, v, w);
+ CREATE VIEW v1 AS SELECT * FROM c1 WHERE w IS NOT NULL;
+} {
+ ALTER TABLE c1 DROP w
+} {
+ error in view v1 after drop column: no such column: w
+}
+
+#-------------------------------------------------------------------------
+# Verify that a column that is part of a CHECK constraint may be dropped
+# if the CHECK constraint was specified as part of the column definition.
+#
+
+# EVIDENCE-OF: R-60924-11170 However, the column being deleted can be used in a
+# column CHECK constraint because the column CHECK constraint is dropped
+# together with the column itself.
+do_execsql_test 3.0 {
+ CREATE TABLE yyy(q, w, e CHECK (e > 0), r);
+ INSERT INTO yyy VALUES(1,1,1,1), (2,2,2,2);
+
+ CREATE TABLE zzz(q, w, e, r, CHECK (e > 0));
+ INSERT INTO zzz VALUES(1,1,1,1), (2,2,2,2);
+}
+do_catchsql_test 3.1.1 {
+ INSERT INTO yyy VALUES(0,0,0,0);
+} {1 {CHECK constraint failed: e > 0}}
+do_catchsql_test 3.1.2 {
+ INSERT INTO yyy VALUES(0,0,0,0);
+} {1 {CHECK constraint failed: e > 0}}
+
+do_execsql_test 3.2.1 {
+ ALTER TABLE yyy DROP e;
+}
+do_catchsql_test 3.2.2 {
+ ALTER TABLE zzz DROP e;
+} {1 {error in table zzz after drop column: no such column: e}}
+
+finish_test
diff --git a/test/alterlegacy.test b/test/alterlegacy.test
index 47d8bcfba9..dfa1716467 100644
--- a/test/alterlegacy.test
+++ b/test/alterlegacy.test
@@ -40,7 +40,7 @@ do_execsql_test 1.1 {
# slightly different - it rejects the change and rolls back the transaction.
do_catchsql_test 1.2 {
ALTER TABLE t1 RENAME TO t1new;
-} {1 {no such column: t1.a}}
+} {1 {error in table t1new after rename: no such column: t1.a}}
do_execsql_test 1.3 {
CREATE TABLE t3(c, d);
@@ -59,7 +59,7 @@ do_execsql_test 1.4 {
do_catchsql_test 1.3 {
ALTER TABLE t2 RENAME TO t2new;
-} {1 {no such column: t2.b}}
+} {1 {error in index t2expr after rename: no such column: t2.b}}
do_execsql_test 1.4 {
SELECT sql FROM sqlite_master
} {
diff --git a/test/altermalloc3.test b/test/altermalloc3.test
new file mode 100644
index 0000000000..d84f6bbc94
--- /dev/null
+++ b/test/altermalloc3.test
@@ -0,0 +1,40 @@
+# 2021 February 18
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+set testprefix altermalloc3
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, b)) WITHOUT ROWID;
+ INSERT INTO t1 VALUES(1, 2, 3, 4);
+}
+faultsim_save_and_close
+
+do_faultsim_test 1 -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql { ALTER TABLE t1 DROP COLUMN c }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+
+finish_test
+
diff --git a/test/altertab3.test b/test/altertab3.test
index 10d9ef32e3..1823b21edf 100644
--- a/test/altertab3.test
+++ b/test/altertab3.test
@@ -253,7 +253,7 @@ do_execsql_test 11.1 {
do_catchsql_test 11.2 {
ALTER TABLE t1 RENAME TO t1x;
-} {1 {error in trigger b: no such table: abc}}
+} {1 {error in trigger b: no such table: main.abc}}
do_execsql_test 11.3 {
DROP TRIGGER b;
diff --git a/test/backup2.test b/test/backup2.test
index b58c6a3cd3..1822e2dbf0 100644
--- a/test/backup2.test
+++ b/test/backup2.test
@@ -144,7 +144,7 @@ do_test backup2-9 {
if {$tcl_platform(platform)=="windows"} {
set msg {cannot open source database: unable to open database file}
} elseif {[string match *BSD $tcl_platform(os)]} {
- set msg {restore failed: file is not a database}
+ set msg {}
} else {
set msg {cannot open source database: disk I/O error}
}
@@ -152,7 +152,8 @@ do_test backup2-10 {
forcedelete bu3.db
file mkdir bu3.db
set rc [catch {db restore temp bu3.db} res]
- lappend rc $res
+ if {[string match *BSD $tcl_platform(os)]} { set res "" }
+ list $rc $res
} [list 1 $msg]
# Try to restore from something that is not a database file.
diff --git a/test/columncount.test b/test/columncount.test
new file mode 100644
index 0000000000..3ecc1f8d8a
--- /dev/null
+++ b/test/columncount.test
@@ -0,0 +1,57 @@
+# 2021 February 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing the sqlite3_column_count() API.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix columncount
+
+proc do_ccsql_test {tn sql res} {
+
+ uplevel [list do_test $tn [subst -nocommands {
+ set stmt [sqlite3_prepare_v2 db {$sql} -1 dummy]
+ set res [sqlite3_column_count [set stmt]]
+ while {[sqlite3_step [set stmt]]=="SQLITE_ROW"} {
+ for {set i 0} {[set i] < [sqlite3_data_count [set stmt]]} {incr i} {
+ lappend res [sqlite3_column_text [set stmt] [set i]]
+ }
+ }
+
+ set rc [sqlite3_finalize [set stmt]]
+ if {[set rc]!="SQLITE_OK"} {
+ error [sqlite3_errmsg db]
+ }
+
+ set res
+ }] [list {*}$res]]
+
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x, y, z);
+ INSERT INTO t1 VALUES('a', 'b', 'c');
+}
+
+do_ccsql_test 1.1 { SELECT * FROM t1 } {3 a b c}
+do_ccsql_test 1.2 { CREATE TABLE t2(a, b) } {0}
+
+do_ccsql_test 1.3 { ALTER TABLE t2 RENAME TO t3 } {0}
+do_ccsql_test 1.4 { ALTER TABLE t3 RENAME b TO ccc } {0}
+do_ccsql_test 1.5 { ALTER TABLE t3 ADD COLUMN d } {0}
+
+do_ccsql_test 1.6 { DROP TABLE t3 } {0}
+
+
+
+finish_test
+
diff --git a/test/corruptN.test b/test/corruptN.test
new file mode 100644
index 0000000000..4f7667ce1e
--- /dev/null
+++ b/test/corruptN.test
@@ -0,0 +1,106 @@
+# 2020-12-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix corruptN
+
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
+reset_db
+do_test 1.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 4096 pagesize 512 filename sql024239.txt.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 02 00 01 01 00 40 20 20 00 00 00 0c 00 00 00 07 .....@ ........
+| 32: 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 04 ................
+| 48: 00 00 00 00 89 00 00 04 00 10 00 01 0a 00 00 01 ................
+| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c ................
+| 96: 00 2e 2c 50 0d 00 00 00 06 01 06 00 01 da 01 b0 ..,P............
+| 112: 01 56 01 86 01 2a 01 06 00 00 00 00 00 00 00 00 .V...*..........
+| 256: 00 00 00 00 00 00 22 07 06 17 11 11 01 31 74 61 .............1ta
+| 272: 62 6c 65 74 34 74 34 07 43 52 45 41 54 45 20 54 blet4t4.CREATE T
+| 288: 41 42 4c 45 20 74 34 28 78 29 2a 06 06 17 13 11 ABLE t4(x)*.....
+| 304: 01 3f 69 6e 64 65 78 74 33 78 74 33 05 43 52 45 .?indext3xt3.CRE
+| 320: 41 54 45 20 49 4e 44 45 58 20 74 33 78 20 4f 4e ATE INDEX t3x ON
+| 336: 20 74 33 28 78 29 2e 04 06 17 15 11 01 45 69 6e t3(x).......Ein
+| 352: 64 65 78 74 32 63 64 74 32 05 43 52 45 41 54 45 dext2cdt2.CREATE
+| 368: 20 49 4e 44 45 58 20 74 32 63 64 20 4f 4e 20 74 INDEX t2cd ON t
+| 384: 32 28 63 2c 64 29 28 05 06 17 11 11 01 3d 74 61 2(c,d)(......=ta
+| 400: 62 6c 65 74 33 74 33 07 43 52 45 41 54 45 20 54 blet3t3.CREATE T
+| 416: 41 42 4c 45 20 74 33 28 63 2c 78 2c 65 2c 66 29 ABLE t3(c,x,e,f)
+| 432: 28 02 06 17 11 11 01 3d 74 61 62 6c 65 74 32 74 (......=tablet2t
+| 448: 32 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 2.CREATE TABLE t
+| 464: 32 28 63 2c 64 2c 65 2c 66 29 24 01 06 17 11 11 2(c,d,e,f)$.....
+| 480: 01 35 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 .5tablet1t1.CREA
+| 496: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 29 TE TABLE t1(a,b)
+| page 2 offset 512
+| 0: 0d 00 00 00 04 01 41 00 01 fa 01 f3 01 de 01 cf ......A.........
+| 160: 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 .. .............
+| 448: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0d ................
+| 464: 04 03 17 17 73 65 76 65 6e 65 69 67 68 74 13 03 ....seveneight..
+| 480: 03 07 07 40 14 00 00 00 00 00 00 40 18 00 00 00 ...@.......@....
+| 496: 00 00 00 05 02 03 01 01 03 04 04 01 03 09 01 02 ................
+| page 3 offset 1024
+| 0: 0d 00 00 00 08 01 54 00 01 f7 01 ec 01 c5 01 aa ......T.........
+| 16: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 112: 00 00 dd 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 336: 00 00 00 00 19 08 05 17 17 17 17 65 69 67 68 74 ...........eight
+| 352: 65 69 67 68 74 73 65 76 65 6e 73 65 76 65 6e 25 eightsevenseven%
+| 368: 07 05 07 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 432: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 ................
+| 480: 00 00 0f 04 17 17 01 65 69 67 68 74 65 69 67 68 .......eighteigh
+| 496: 74 08 15 04 07 07 01 40 18 00 00 00 00 00 00 40 t......@.......@
+| page 4 offset 1536
+| 0: 18 00 00 00 00 00 00 07 07 04 01 01 01 04 04 06 ................
+| 16: 07 04 01 01 01 02 02 05 0f 04 17 17 01 73 6d 76 .............smv
+| 32: 65 6e 65 69 67 68 74 04 15 04 07 07 01 40 14 00 eneight......@..
+| page 5 offset 2048
+| 0: 0a 00 00 00 08 01 96 00 01 fa 01 c4 01 f2 01 bc ................
+| 16: 01 dc 01 e1 01 96 01 cc 00 00 00 00 00 00 00 00 ................
+| 160: 00 00 00 00 00 00 32 00 00 00 00 00 00 00 00 00 ......2.........
+| 368: 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 400: 00 00 00 00 00 00 0f 04 17 17 01 85 69 67 68 74 ............ight
+| 416: 65 69 67 68 74 08 15 04 07 07 01 40 18 00 00 00 eight......@....
+| 432: 00 00 00 40 18 00 00 00 00 00 00 07 07 04 01 01 ...@............
+| 448: 01 04 04 06 07 04 01 01 01 02 02 05 0f 04 17 17 ................
+| 464: 01 73 6d 76 65 6e 65 69 67 68 74 04 15 04 07 07 .smveneight.....
+| 480: 01 40 14 00 00 00 00 00 00 40 18 00 00 00 00 00 .@.......@......
+| 496: 00 03 07 04 01 01 01 03 04 02 05 04 03 01 09 02 ................
+| page 6 offset 2560
+| 0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 ................
+| 304: 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 00 ...&............
+| page 7 offset 3072
+| 0: 0d 00 00 00 08 01 c2 00 01 fb 01 f6 01 f1 01 ec ................
+| 16: 01 e0 01 d4 01 cb 01 c2 00 00 00 00 00 00 00 00 ................
+| 128: 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 04 ............. ..
+| 384: 00 00 00 00 00 00 00 00 00 07 08 02 17 65 69 fc .............ei.
+| 400: 68 74 07 07 02 17 65 69 67 68 74 0a fb fd f8 bf ht....eight.....
+| 416: e7 ff ff ff 00 00 00 0a 05 02 07 40 18 00 00 00 ...........@....
+| 432: 00 00 00 03 04 02 01 04 03 03 02 01 04 03 02 01 ................
+| 448: ff ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00 ................
+| end sql024239.txt.db
+}]} {}
+
+do_catchsql_test 1.1 {
+ VACUUM;
+} {1 {database disk image is malformed}}
+
+
+finish_test
diff --git a/test/crash5.test b/test/crash5.test
index b80b15137f..fc078b3504 100644
--- a/test/crash5.test
+++ b/test/crash5.test
@@ -21,7 +21,7 @@ source $testdir/tester.tcl
# Only run these tests if memory debugging is turned on.
#
ifcapable !crashtest||!memorymanage {
- puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ puts "Skipping crash5 tests: not compiled with -DSQLITE_ENABLE_MEMORY_MANAGEMENT..."
finish_test
return
}
@@ -29,7 +29,7 @@ ifcapable !crashtest||!memorymanage {
db close
for {set ii 0} {$ii < 10} {incr ii} {
- for {set jj 50} {$jj < 100} {incr jj} {
+ for {set jj 1} {$jj < 100} {incr jj} {
# Set up the database so that it is an auto-vacuum database
# containing a single table (root page 3) with a single row.
@@ -47,6 +47,20 @@ for {set ii 0} {$ii < 10} {incr ii} {
do_test crash5-$ii.$jj.1 {
crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \
[list set iFail $jj] {
+ proc get_pwd {} {
+ if {$::tcl_platform(platform) eq "windows"} {
+ if {[info exists ::env(ComSpec)]} {
+ set comSpec $::env(ComSpec)
+ } else {
+ # NOTE: Hard-code the typical default value.
+ set comSpec {C:\Windows\system32\cmd.exe}
+ }
+ return [string map [list \\ /] \
+ [string trim [exec -- $comSpec /c echo %CD%]]]
+ } else {
+ return [pwd]
+ }
+ }
sqlite3_crashparams 0 [file join [get_pwd] test.db-journal]
# Begin a transaction and evaluate a "CREATE INDEX" statement
@@ -61,36 +75,39 @@ for {set ii 0} {$ii < 10} {incr ii} {
#
db eval BEGIN
sqlite3_memdebug_fail $iFail -repeat 0
- catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg
- # puts "$n $msg ac=[sqlite3_get_autocommit db]"
+ set rc [catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg]
+# puts "$msg ac=[sqlite3_get_autocommit db] iFail=$iFail"
+# puts "fail=[sqlite3_memdebug_fail -1]"
- # If the transaction is still active (it may not be if the malloc()
- # failure occurred in the OS layer), write to the database. Make sure
- # page 4 is among those written.
- #
- if {![sqlite3_get_autocommit db]} {
- db eval {
- DELETE FROM t1; -- This will put page 4 on the free list.
- INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
- INSERT INTO t1 SELECT * FROM t1; -- 2
- INSERT INTO t1 SELECT * FROM t1; -- 4
- INSERT INTO t1 SELECT * FROM t1; -- 8
- INSERT INTO t1 SELECT * FROM t1; -- 16
- INSERT INTO t1 SELECT * FROM t1; -- 32
- INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48
+ if {$rc} {
+ # If the transaction is still active (it may not be if the malloc()
+ # failure occurred in the OS layer), write to the database. Make sure
+ # page 4 is among those written.
+ #
+ if {![sqlite3_get_autocommit db]} {
+ db eval {
+ DELETE FROM t1; -- This will put page 4 on the free list.
+ INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
+ INSERT INTO t1 SELECT * FROM t1; -- 2
+ INSERT INTO t1 SELECT * FROM t1; -- 4
+ INSERT INTO t1 SELECT * FROM t1; -- 8
+ INSERT INTO t1 SELECT * FROM t1; -- 16
+ INSERT INTO t1 SELECT * FROM t1; -- 32
+ INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48
+ }
}
+
+ # If the right malloc() failed during the 'CREATE INDEX' above and
+ # the transaction was not rolled back, then the sqlite cache now
+ # has a dirty page 4 that it incorrectly believes is already safely
+ # in the synced part of the journal file. When
+ # sqlite3_release_memory() is called sqlite tries to free memory
+ # by writing page 4 out to the db file. If it crashes later on,
+ # before syncing the journal... Corruption!
+ #
+ sqlite3_crashparams 1 [file join [get_pwd] test.db-journal]
+ sqlite3_release_memory 8092
}
-
- # If the right malloc() failed during the 'CREATE INDEX' above and
- # the transaction was not rolled back, then the sqlite cache now
- # has a dirty page 4 that it incorrectly believes is already safely
- # in the synced part of the journal file. When
- # sqlite3_release_memory() is called sqlite tries to free memory
- # by writing page 4 out to the db file. If it crashes later on,
- # before syncing the journal... Corruption!
- #
- sqlite3_crashparams 1 [file join [get_pwd] test.db-journal]
- sqlite3_release_memory 8092
}]] {}
expr 1
} {1}
diff --git a/test/dbfuzz2.c b/test/dbfuzz2.c
index e35162937f..6b30490dde 100644
--- a/test/dbfuzz2.c
+++ b/test/dbfuzz2.c
@@ -287,10 +287,6 @@ int LLVMFuzzerInitialize(int *pArgc, char ***pArgv){
sqlite3MemTraceActivate(stdout);
continue;
}
- if( strcmp(z,"mem")==0 ){
- bVdbeDebug = 1;
- continue;
- }
if( strcmp(z,"max-db-size")==0 ){
if( i+1==argc ){
fprintf(stderr, "missing argument to %s\n", argv[i]);
@@ -299,6 +295,18 @@ int LLVMFuzzerInitialize(int *pArgc, char ***pArgv){
szMax = strtol(argv[++i], 0, 0);
continue;
}
+ if( strcmp(z, "lookaside")==0 ){
+ int sz, nSlot;
+ if( i+2>=argc ){
+ fprintf(stderr,
+ "--lookaside requires two arguments: slot-size num-slots\n");
+ exit(1);
+ }
+ sz = atoi(argv[++i]);
+ nSlot = atoi(argv[++i]);
+ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, nSlot);
+ continue;
+ }
#ifndef _WIN32
if( strcmp(z,"max-stack")==0
|| strcmp(z,"max-data")==0
diff --git a/test/exists2.test b/test/exists2.test
new file mode 100644
index 0000000000..c4fc9dc373
--- /dev/null
+++ b/test/exists2.test
@@ -0,0 +1,188 @@
+# 2021 January 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing cases where EXISTS expressions are
+# transformed to IN() expressions by where.c
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix exists2
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(4, 'four');
+ INSERT INTO t1 VALUES(5, 'five');
+ INSERT INTO t1 VALUES(6, 'six');
+ INSERT INTO t1 VALUES(7, 'seven');
+
+ CREATE TABLE t2(c INTEGER, d INTEGER);
+ INSERT INTO t2 VALUES(1, 1);
+ INSERT INTO t2 VALUES(3, 2);
+ INSERT INTO t2 VALUES(5, 3);
+ INSERT INTO t2 VALUES(7, 4);
+}
+
+proc do_execsql_eqp_test {tn sql eqp res} {
+ uplevel [list do_eqp_test $tn.1 $sql [string trim $eqp]]
+ uplevel [list do_execsql_test $tn.2 $sql $res]
+}
+
+do_execsql_eqp_test 1.1 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t1.a=t2.c);
+} {
+ USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five 7 seven
+}
+
+do_execsql_eqp_test 1.2 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c=t1.a);
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five 7 seven
+}
+
+do_execsql_eqp_test 1.3 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c+1=t1.a);
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 2 two 4 four 6 six
+}
+
+do_execsql_eqp_test 1.4 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c+1=t1.a+1);
+} {
+ SCAN TABLE t1
+} {
+ 1 one 3 three 5 five 7 seven
+}
+
+do_execsql_eqp_test 1.5 {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE t1.a=t2.c AND d IN (1, 2, 3)
+ );
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five
+}
+
+do_execsql_eqp_test 1.6 {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE d IN (1, 2, 3)AND t1.a=t2.c
+ );
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five
+}
+
+do_execsql_eqp_test 1.7 {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE d IN (1, 2, 3)AND t1.a=t2.c
+ );
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t3(a TEXT PRIMARY KEY, b TEXT, x INT) WITHOUT ROWID;
+ CREATE TABLE t4(c TEXT COLLATE nocase, y INT);
+
+ INSERT INTO t3 VALUES('one', 'i', 1);
+ INSERT INTO t3 VALUES('two', 'ii', 2);
+ INSERT INTO t3 VALUES('three', 'iii', 3);
+ INSERT INTO t3 VALUES('four', 'iv', 4);
+ INSERT INTO t3 VALUES('five', 'v', 5);
+
+ INSERT INTO t4 VALUES('FIVE',5), ('four',4), ('TWO',2), ('one',1);
+}
+
+do_execsql_test 2.1 { SELECT a FROM t3, t4 WHERE a=c } {four one}
+do_execsql_test 2.2 { SELECT a FROM t3, t4 WHERE c=a } {five four one two}
+
+do_execsql_eqp_test 2.3 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE a=c)
+} {
+ SEARCH TABLE t3 USING PRIMARY KEY
+} {
+ four one
+}
+
+do_execsql_eqp_test 2.4 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE c=a)
+} {
+ SCAN TABLE t3
+} {
+ five four one two
+}
+
+do_execsql_test 2.5 {
+ CREATE INDEX t3anc ON t3(a COLLATE nocase, x);
+}
+
+do_execsql_eqp_test 2.6 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE c=a)
+} {
+ SEARCH TABLE t3 USING COVERING INDEX t3anc
+} {
+ five four one two
+}
+do_execsql_test 2.6a {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE (c,y)=(a,x))
+} {five four one two}
+
+do_execsql_eqp_test 2.7 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE a=c)
+} {
+ SEARCH TABLE t3 USING PRIMARY KEY
+} {
+ four one
+}
+do_execsql_test 2.7a {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE (a,x)=(c,y))
+} {
+ four one
+}
+
+# EXISTS clauses using vector expressions in the WHERE clause.
+#
+reset_db
+do_execsql_test 3.0 {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1(a,b) VALUES(1,111),(2,222),(8,888);
+ CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t2(x,y) VALUES(2,222),(3,333),(7,333);
+ SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (x,y)=(a,b));
+} {222}
+do_execsql_test 3.1 {
+ SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (a,b)=(x,y));
+} {222}
+do_execsql_test 3.2 {
+ SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (x,b)=(a,y));
+} {222}
+
+
+
+
+
+finish_test
diff --git a/test/existsfault.test b/test/existsfault.test
new file mode 100644
index 0000000000..24e44a7b58
--- /dev/null
+++ b/test/existsfault.test
@@ -0,0 +1,52 @@
+# 2021 January 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing cases where EXISTS expressions are
+# transformed to IN() expressions by where.c
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix existsfault
+
+do_execsql_test 1 {
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(4, 'four');
+ INSERT INTO t1 VALUES(5, 'five');
+ INSERT INTO t1 VALUES(6, 'six');
+ INSERT INTO t1 VALUES(7, 'seven');
+
+ CREATE TABLE t2(c INTEGER, d INTEGER);
+ INSERT INTO t2 VALUES(1, 1);
+ INSERT INTO t2 VALUES(3, 2);
+ INSERT INTO t2 VALUES(5, 3);
+ INSERT INTO t2 VALUES(7, 4);
+}
+faultsim_save_and_close
+
+do_faultsim_test 1 -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE t2.c=t1.a AND d IN (1, 2, 3)
+ )
+ }
+} -test {
+ faultsim_test_result {0 {1 one 3 three 5 five}}
+}
+
+
+finish_test
+
diff --git a/test/fts3corrupt4.test b/test/fts3corrupt4.test
index bddcc9f5de..851119a2fe 100644
--- a/test/fts3corrupt4.test
+++ b/test/fts3corrupt4.test
@@ -6298,4 +6298,92 @@ do_catchsql_test 47.3 {
SELECT matchinfo(t1) FROM t1 WHERE t1 MATCH '"json1 enable"';
} {1 {database disk image is malformed}}
+#-------------------------------------------------------------------------
+#
+reset_db
+do_test 48.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 20480 pagesize 4096 filename sql038051.txt.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 00 .....@ ........
+| 96: 00 00 00 00 0d 0e fc 00 05 0e 13 00 0f ca 0f 6c ...............l
+| 112: 0f 04 0e 13 0e c9 00 00 00 00 00 00 00 00 00 00 ................
+| 3600: 00 00 00 81 33 04 07 17 1f 1f 01 82 35 74 61 62 ....3.......5tab
+| 3616: 6c 65 78 31 5f 73 65 67 64 69 72 78 31 5f 73 65 lex1_segdirx1_se
+| 3632: 67 64 69 72 04 43 52 45 41 54 45 20 54 41 42 4c gdir.CREATE TABL
+| 3648: 45 20 27 78 31 5f 73 65 67 64 69 72 27 28 6c 65 E 'x1_segdir'(le
+| 3664: 76 65 6c 20 49 4e 54 45 47 45 52 2c 69 64 78 20 vel INTEGER,idx
+| 3680: 49 4e 54 45 47 45 52 2c 73 74 61 72 74 5f 62 6c INTEGER,start_bl
+| 3696: 6f 63 6b 20 49 4e 54 45 47 45 52 2c 6c 65 61 76 ock INTEGER,leav
+| 3712: 65 73 5f 65 6e 64 5f 62 6c 6f 63 6b 20 49 4e 54 es_end_block INT
+| 3728: 45 47 45 52 2c 65 6e 64 5f 62 6c 6f 63 6b 20 49 EGER,end_block I
+| 3744: 4e 54 45 47 45 52 2c 72 6f 6f 74 20 42 4c 4f 42 NTEGER,root BLOB
+| 3760: 2c 50 52 49 4d 41 52 59 20 4b 45 59 28 6c 65 76 ,PRIMARY KEY(lev
+| 3776: 65 6c 2c 20 69 64 78 29 29 31 05 06 17 45 1f 01 el, idx))1...E..
+| 3792: 00 69 6e 64 65 78 73 71 6c 69 74 65 5f 61 75 74 .indexsqlite_aut
+| 3808: 6f 69 6e 64 65 78 5f 78 31 5f 73 65 67 64 69 72 oindex_x1_segdir
+| 3824: 5f 31 78 31 5f 73 65 67 64 69 72 05 00 00 00 08 _1x1_segdir.....
+| 3840: 00 00 00 00 66 03 07 17 23 23 01 81 13 74 61 62 ....f...##...tab
+| 3856: 6c 65 78 31 5f 73 65 67 6d 65 6e 74 73 78 31 5f lex1_segmentsx1_
+| 3872: 73 65 67 6d 65 6e 74 73 03 43 52 45 41 54 45 20 segments.CREATE
+| 3888: 54 41 42 4c 45 20 27 78 31 5f 73 65 67 6d 65 6e TABLE 'x1_segmen
+| 3904: 74 73 27 28 62 6c 6f 63 6b 69 64 20 49 4e 54 45 ts'(blockid INTE
+| 3920: 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c GER PRIMARY KEY,
+| 3936: 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 5c 02 07 17 block BLOB)....
+| 3952: 21 21 01 81 03 74 61 62 6c 65 78 31 5f 63 6f 6e !!...tablex1_con
+| 3968: 74 65 6e 74 78 31 5f 63 6f 6e 74 65 6e 74 02 43 tentx1_content.C
+| 3984: 52 45 41 54 45 20 54 41 42 4c 45 20 27 78 31 5f REATE TABLE 'x1_
+| 4000: 63 6f 6e 74 65 6e 74 27 28 64 6f 63 69 64 20 49 content'(docid I
+| 4016: 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b NTEGER PRIMARY K
+| 4032: 45 59 2c 20 27 63 30 78 27 29 34 01 06 17 11 11 EY, 'c0x')4.....
+| 4048: 08 57 74 61 62 6c 65 78 31 78 31 43 52 45 41 54 .Wtablex1x1CREAT
+| 4064: 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 E VIRTUAL TABLE
+| 4080: 78 31 20 55 53 49 4e 47 20 66 74 73 33 28 78 29 x1 USING fts3(x)
+| page 2 offset 4096
+| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3920: 00 00 00 2e 04 03 00 63 62 72 61 69 6e 73 74 65 .......cbrainste
+| 3936: 6d 20 62 72 61 69 6e 73 74 65 6d 73 20 62 72 61 m brainstems bra
+| 3952: 69 6e 73 74 6f 72 6d 20 62 72 61 69 6e 73 74 6f instorm brainsto
+| 3968: 72 6d 73 2b 03 03 00 5d 62 72 61 69 6e 20 62 72 rms+...]brain br
+| 3984: 61 69 6e 63 68 69 6c 64 20 62 72 61 69 6e 65 64 ainchild brained
+| 4000: 20 62 72 61 69 6e 69 6e 67 20 62 72 61 69 6e 73 braining brains
+| 4016: 26 02 03 00 53 62 72 61 67 73 20 62 72 61 69 64 &...Sbrags braid
+| 4032: 20 62 72 61 69 64 65 64 20 62 72 61 69 64 69 6e braided braidin
+| 4048: 67 20 62 72 61 69 64 73 26 01 03 00 53 62 72 61 g braids&...Sbra
+| 4064: 65 73 20 62 72 61 67 20 62 72 61 67 67 65 64 20 es brag bragged
+| 4080: 62 72 61 c3 67 65 72 20 62 72 61 67 67 69 6e 67 bra.ger bragging
+| page 3 offset 8192
+| 0: 0d 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 04 0f 20 00 0f c8 0f 90 0f 54 0f 20 ...... ......T.
+| 3872: 32 04 07 08 01 08 08 15 58 03 30 20 33 38 00 09 2.......X.0 38..
+| 3888: 62 72 61 69 6e 73 74 65 6d 03 04 02 00 09 01 73 brainstem......s
+| 3904: 03 04 03 00 07 03 6f 72 6d 03 04 04 00 0a 01 73 ......orm......s
+| 3920: 03 04 05 00 3a 03 07 08 01 08 08 15 68 02 30 20 ....:.......h.0
+| 3936: 34 36 00 05 62 72 61 69 6e 03 03 02 00 05 05 63 46..brain......c
+| 3952: 68 69 6c 64 03 03 03 00 05 02 65 64 03 03 04 00 hild......ed....
+| 3968: 05 03 69 6e 67 03 03 05 00 05 01 73 03 03 06 00 ..ing......s....
+| 3984: 36 02 07 08 09 08 08 15 62 30 20 34 33 00 05 62 6.......b0 43..b
+| 4000: 72 61 67 73 03 02 02 00 03 02 69 64 03 02 03 00 rags......id....
+| 4016: 05 02 65 64 03 02 04 00 05 03 69 6e 67 03 02 05 ..ed......ing...
+| 4032: 00 05 01 73 03 02 06 00 36 01 07 08 08 08 08 15 ...s....6.......
+| 4048: 62 30 20 34 33 00 05 62 72 61 65 73 03 01 02 00 b0 43..braes....
+| 4064: 03 01 68 03 01 03 00 04 03 67 65 74 03 01 04 00 ..h......get....
+| 4080: 06 01 72 03 01 05 00 05 03 69 6e 67 03 01 06 00 ..r......ing....
+| page 5 offset 16384
+| 0: 0a 00 00 00 04 0f e7 00 0f fb 0f f5 0f ee 0f e7 ................
+| 4064: 00 00 00 00 00 00 00 06 04 08 01 01 03 04 06 04 ................
+| 4080: 08 01 01 02 03 05 04 08 09 01 02 04 04 08 08 09 ................
+| end sql038051.txt.db
+}]} {}
+
+do_catchsql_test 48.1 {
+ INSERT INTO x1(x1) VALUES('nodesize=24'),('merge=3,4');
+ INSERT INTO x1(x1) VALUES( 'merge=3,4' ),('merge=3,4');
+} {1 {database disk image is malformed}}
+
+
finish_test
diff --git a/test/fts4rename.test b/test/fts4rename.test
index c647d6928e..5571ea7b15 100644
--- a/test/fts4rename.test
+++ b/test/fts4rename.test
@@ -29,7 +29,7 @@ do_execsql_test 1.0 {
do_catchsql_test 1.1 {
ALTER TABLE t1_content RENAME c0a TO docid;
-} {1 {duplicate column name: docid}}
+} {1 {error in table t1_content after rename: duplicate column name: docid}}
do_catchsql_test 1.2 {
UPDATE t1 SET Col0 = 1 ;
diff --git a/test/fts4unicode.test b/test/fts4unicode.test
index f4d1a33486..facf2bf9c4 100644
--- a/test/fts4unicode.test
+++ b/test/fts4unicode.test
@@ -567,4 +567,22 @@ do_execsql_test 11.1 {
berlin@street sydney.road
}
+# Test for embedded nul characters in fts4 unicode index.
+#
+do_execsql_test 12.0 {
+ CREATE VIRTUAL TABLE t12 USING fts4(tokenize=unicode61);
+ INSERT INTO t12 VALUES('abc' || char(0) || 'def');
+ SELECT hex(CAST(content AS blob)) FROM t12;
+} {61626300646566}
+do_execsql_test 12.1 {
+ INSERT INTO t12(t12) VALUES('integrity-check');
+} {}
+do_execsql_test 12.2 {
+ CREATE VIRTUAL TABLE t12aux USING fts4aux(t12);
+ SELECT * FROM t12aux;
+} {abc * 1 1 abc 0 1 1}
+do_execsql_test 12.3 {
+ SELECT hex(CAST(content AS blob)) FROM t12 WHERE t12 MATCH 'abc'
+} {61626300646566}
+
finish_test
diff --git a/test/func7.test b/test/func7.test
new file mode 100644
index 0000000000..c8ae2931e1
--- /dev/null
+++ b/test/func7.test
@@ -0,0 +1,245 @@
+# 2020-12-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.
+#
+#*************************************************************************
+#
+# Test cases for SQL functions based off the standard math library
+#
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable !mathlib {
+ finish_test
+ return
+}
+
+do_execsql_test func7-100 {
+ SELECT ceil(99.9), ceiling(-99.01), floor(17), floor(-17.99);
+} {100.0 -99.0 17 -18.0}
+do_execsql_test func7-110 {
+ SELECT quote(ceil(NULL)), ceil('-99.99');
+} {NULL -99.0}
+do_execsql_test func7-200 {
+ SELECT round(ln(5),2), log(100.0), log(100), log(2,'256');
+} {1.61 2.0 2.0 8.0}
+do_execsql_test func7-210 {
+ SELECT ln(-5), log(-5,100.0);
+} {{} {}}
+
+# Test cases derived from PostgreSQL documentation
+#
+do_execsql_test func7-pg-100 {
+ SELECT abs(-17.4)
+} {17.4}
+do_execsql_test func7-pg-110 {
+ SELECT ceil(42.2)
+} {43.0}
+do_execsql_test func7-pg-120 {
+ SELECT ceil(-42.2)
+} {-42.0}
+do_execsql_test func7-pg-130 {
+ SELECT round(exp(1.0),7)
+} {2.7182818}
+do_execsql_test func7-pg-140 {
+ SELECT floor(42.8)
+} {42.0}
+do_execsql_test func7-pg-150 {
+ SELECT floor(-42.8)
+} {-43.0}
+do_execsql_test func7-pg-160 {
+ SELECT round(ln(2.0),7)
+} {0.6931472}
+do_execsql_test func7-pg-170 {
+ SELECT log(100.0)
+} {2.0}
+do_execsql_test func7-pg-180 {
+ SELECT log10(1000.0)
+} {3.0}
+do_execsql_test func7-pg-190 {
+ SELECT log(2.0, 64.0)
+} {6.0}
+do_execsql_test func7-pg-200 {
+ SELECT mod(9,4);
+} {1.0}
+do_execsql_test func7-pg-210 {
+ SELECT round(pi(),7);
+} {3.1415927}
+do_execsql_test func7-pg-220 {
+ SELECT power(9,3);
+} {729.0}
+do_execsql_test func7-pg-230 {
+ SELECT round(radians(45.0),7);
+} {0.7853982}
+do_execsql_test func7-pg-240 {
+ SELECT round(42.4);
+} {42.0}
+do_execsql_test func7-pg-250 {
+ SELECT round(42.4382,2);
+} {42.44}
+do_execsql_test func7-pg-260 {
+ SELECT sign(-8.4);
+} {-1}
+do_execsql_test func7-pg-270 {
+ SELECT round( sqrt(2), 7);
+} {1.4142136}
+do_execsql_test func7-pg-280 {
+ SELECT trunc(42.8), trunc(-42.8);
+} {42.0 -42.0}
+do_execsql_test func7-pg-300 {
+ SELECT acos(1);
+} {0.0}
+do_execsql_test func7-pg-301 {
+ SELECT degrees(acos(0.5));
+} {60.0}
+do_execsql_test func7-pg-310 {
+ SELECT round( asin(1), 7);
+} {1.5707963}
+do_execsql_test func7-pg-311 {
+ SELECT degrees( asin(0.5) );
+} {30.0}
+do_execsql_test func7-pg-320 {
+ SELECT round( atan(1), 7);
+} {0.7853982}
+do_execsql_test func7-pg-321 {
+ SELECT degrees( atan(1) );
+} {45.0}
+do_execsql_test func7-pg-330 {
+ SELECT round( atan2(1,0), 7);
+} {1.5707963}
+do_execsql_test func7-pg-331 {
+ SELECT degrees( atan2(1,0) );
+} {90.0}
+do_execsql_test func7-pg-400 {
+ SELECT cos(0);
+} {1.0}
+do_execsql_test func7-pg-401 {
+ SELECT cos( radians(60.0) );
+} {0.5}
+do_execsql_test func7-pg-400 {
+ SELECT cos(0);
+} {1.0}
+do_execsql_test func7-pg-410 {
+ SELECT round( sin(1), 7);
+} {0.841471}
+do_execsql_test func7-pg-411 {
+ SELECT sin( radians(30) );
+} {0.5}
+do_execsql_test func7-pg-420 {
+ SELECT round( tan(1), 7);
+} {1.5574077}
+do_execsql_test func7-pg-421 {
+ SELECT tan( radians(45) );
+} {1.0}
+do_execsql_test func7-pg-500 {
+ SELECT round( sinh(1), 7);
+} {1.1752012}
+do_execsql_test func7-pg-510 {
+ SELECT round( cosh(0), 7);
+} {1.0}
+do_execsql_test func7-pg-520 {
+ SELECT round( tanh(1), 7);
+} {0.7615942}
+do_execsql_test func7-pg-530 {
+ SELECT round( asinh(1), 7);
+} {0.8813736}
+do_execsql_test func7-pg-540 {
+ SELECT round( acosh(1), 7);
+} {0.0}
+do_execsql_test func7-pg-550 {
+ SELECT round( atanh(0.5), 7);
+} {0.5493061}
+
+# Test cases derived from MySQL documentation
+#
+do_execsql_test func7-mysql-100 {
+ SELECT acos(1);
+} {0.0}
+do_execsql_test func7-mysql-110 {
+ SELECT acos(1.0001);
+} {{}}
+do_execsql_test func7-mysql-120 {
+ SELECT round( acos(0.0), 7);
+} {1.5707963}
+do_execsql_test func7-mysql-130 {
+ SELECT round( asin(0.2), 7);
+} {0.2013579}
+do_execsql_test func7-mysql-140 {
+ SELECT asin('foo');
+} {{}} ;# Note: MySQL returns 0 here, not NULL.
+ # SQLite deliberately returns NULL.
+ # SQLServer and Oracle throw an error.
+do_execsql_test func7-mysql-150 {
+ SELECT round( atan(2), 7), round( atan(-2), 7);
+} {1.1071487 -1.1071487}
+do_execsql_test func7-mysql-160 {
+ SELECT round( atan2(-2,2), 7), round( atan2(pi(),0), 7);
+} {-0.7853982 1.5707963}
+do_execsql_test func7-mysql-170 {
+ SELECT ceiling(1.23), ceiling(-1.23);
+} {2.0 -1.0}
+do_execsql_test func7-mysql-180 {
+ SELECT cos(pi());
+} {-1.0}
+do_execsql_test func7-mysql-190 {
+ SELECT degrees(pi()), degrees(pi()/2);
+} {180.0 90.0}
+do_execsql_test func7-mysql-190 {
+ SELECT round( exp(2), 7), round( exp(-2), 7), exp(0);
+} {7.3890561 0.1353353 1.0}
+do_execsql_test func7-mysql-200 {
+ SELECT floor(1.23), floor(-1.23);
+} {1.0 -2.0}
+do_execsql_test func7-mysql-210 {
+ SELECT round(ln(2),7), quote(ln(-2));
+} {0.6931472 NULL}
+#do_execsql_test func7-mysql-220 {
+# SELECT round(log(2),7), log(-2);
+#} {0.6931472 NULL}
+# log() means natural logarithm in MySQL
+do_execsql_test func7-mysql-230 {
+ SELECT log(2,65536), log(10,100), quote(log(1,100)), quote(log(0,100));
+} {16.0 2.0 NULL NULL}
+do_execsql_test func7-mysql-240 {
+ SELECT log2(65536), quote(log2(-100)), quote(log2(0));
+} {16.0 NULL NULL}
+do_execsql_test func7-mysql-250 {
+ SELECT round(log10(2),7), log10(100), quote(log10(-100));
+} {0.30103 2.0 NULL}
+do_execsql_test func7-mysql-260 {
+ SELECT mod(234,10), 253%7, mod(29,9), 29%9;
+} {4.0 1 2.0 2}
+do_execsql_test func7-mysql-270 {
+ SELECT mod(34.5,3);
+} {1.5}
+do_execsql_test func7-mysql-280 {
+ SELECT pow(2,2), pow(2,-2);
+} {4.0 0.25}
+do_execsql_test func7-mysql-281 {
+ SELECT power(2,2), power(2,-2);
+} {4.0 0.25}
+do_execsql_test func7-mysql-290 {
+ SELECT round(radians(90),7);
+} {1.5707963}
+do_execsql_test func7-mysql-300 {
+ SELECT sign(-32), sign(0), sign(234);
+} {-1 0 1}
+do_execsql_test func7-mysql-310 {
+ SELECT sin(pi()) BETWEEN -1.0e-15 AND 1.0e-15;
+} {1}
+do_execsql_test func7-mysql-320 {
+ SELECT sqrt(4), round(sqrt(20),7), quote(sqrt(-16));
+} {2.0 4.472136 NULL}
+do_execsql_test func7-mysql-330 {
+ SELECT tan(pi()) BETWEEN -1.0e-15 AND 1.0e-15;
+} {1}
+do_execsql_test func7-mysql-331 {
+ SELECT round(tan(pi()+1),7);
+} {1.5574077}
+
+
+finish_test
diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c
index cf49956b47..c6ad4cd5c3 100644
--- a/test/fuzzcheck.c
+++ b/test/fuzzcheck.c
@@ -455,7 +455,12 @@ static void blobListFree(Blob *p){
}
}
-/* Return the current wall-clock time */
+/* Return the current wall-clock time
+**
+** The number of milliseconds since the julian epoch.
+** 1907-01-01 00:00:00 -> 210866716800000
+** 2021-01-01 00:00:00 -> 212476176000000
+*/
static sqlite3_int64 timeOfDay(void){
static sqlite3_vfs *clockVfs = 0;
sqlite3_int64 t;
@@ -713,6 +718,7 @@ static int progress_handler(void *pClientData) {
sqlite3_int64 iNow = timeOfDay();
int rc = iNow>=p->iCutoffTime;
sqlite3_int64 iDiff = iNow - p->iLastCb;
+ /* printf("time-remaining: %lld\n", p->iCutoffTime - iNow); */
if( iDiff > p->mxInterval ) p->mxInterval = iDiff;
p->nCb++;
if( rc==0 && p->mxCb>0 && p->mxCb<=p->nCb ) rc = 1;
@@ -835,7 +841,7 @@ static int runDbSql(sqlite3 *db, const char *zSql){
}
/* Invoke this routine to run a single test case */
-int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte){
+int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte, int iTimeout){
int rc; /* SQLite API return value */
int iSql; /* Index in aData[] of start of SQL */
unsigned char *aDb = 0; /* Decoded database content */
@@ -882,7 +888,7 @@ int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte){
** elapsed since the start of the test.
*/
cx.iLastCb = timeOfDay();
- cx.iCutoffTime = cx.iLastCb + giTimeout; /* Now + giTimeout seconds */
+ cx.iCutoffTime = cx.iLastCb + (iTimeout=argc-1 ) fatalError("missing arguments on %s", argv[i]);
onlySqlid = integerValue(argv[++i]);
@@ -1894,6 +1905,7 @@ int main(int argc, char **argv){
*/
if( !verboseFlag && !quietFlag && !bSpinner ) printf("%s:", zDbName);
for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){
+ tmStart = timeOfDay();
if( isDbSql(pSql->a, pSql->sz) ){
sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d",pSql->id);
if( bSpinner ){
@@ -1917,9 +1929,13 @@ int main(int argc, char **argv){
if( nSkip>0 ){
nSkip--;
}else{
- runCombinedDbSqlInput(pSql->a, pSql->sz);
+ runCombinedDbSqlInput(pSql->a, pSql->sz, iTimeout);
}
nTest++;
+ if( bTimer ){
+ sqlite3_int64 tmEnd = timeOfDay();
+ printf("%lld %s\n", tmEnd - tmStart, g.zTestName);
+ }
g.zTestName[0] = 0;
disableOom();
continue;
@@ -1972,7 +1988,7 @@ int main(int argc, char **argv){
sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 100000000);
sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 50);
if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags);
- setAlarm(iTimeout);
+ setAlarm((iTimeout+999)/1000);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( sqlFuzz || vdbeLimitFlag ){
sqlite3_progress_handler(db, 100000, progressHandler,
@@ -1998,6 +2014,10 @@ int main(int argc, char **argv){
}
reformatVfs();
nTest++;
+ if( bTimer ){
+ sqlite3_int64 tmEnd = timeOfDay();
+ printf("%lld %s\n", tmEnd - tmStart, g.zTestName);
+ }
g.zTestName[0] = 0;
/* Simulate an error if the TEST_FAILURE environment variable is "5".
diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db
index 39191fc8b1..a5e9084192 100644
Binary files a/test/fuzzdata8.db and b/test/fuzzdata8.db differ
diff --git a/test/fuzzerfault.test b/test/fuzzerfault.test
index 6449612a66..e281cb592e 100644
--- a/test/fuzzerfault.test
+++ b/test/fuzzerfault.test
@@ -88,5 +88,24 @@ do_faultsim_test 3 -prep {
faultsim_test_result {0 2} {1 {vtable constructor failed: x1}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 4.0 {
+ CREATE TABLE t1_a(a INTEFDR PRIMARY KEY, b TEXT);
+ CREATE TABLE t3_a(k FnTEGER PRIMARY KEY, v TEXT);
+ CREATE TABLE t3_b(k INTEÀ5R PRIMARY KEY, v TEXT);
+ CREATE VIEW t3 AS SELECT * FROM t3_a UNION ALL SELECT * FROM t3_b;
+}
+faultsim_save_and_close
+
+do_faultsim_test 4 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ SELECT 1 FROM t1_a LEFT JOIN t3 ON ((1+1) AND k=1)
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
finish_test
diff --git a/test/having.test b/test/having.test
index a3882552d3..3bfa8120a1 100644
--- a/test/having.test
+++ b/test/having.test
@@ -65,8 +65,8 @@ foreach {tn sql1 sql2} {
3 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING a=2"
"SELECT a, sum(b) FROM t1 WHERE a=2 GROUP BY a COLLATE binary"
- 5 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING 0"
- "SELECT a, sum(b) FROM t1 WHERE 0 GROUP BY a COLLATE binary"
+ 5 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING 1"
+ "SELECT a, sum(b) FROM t1 WHERE 1 GROUP BY a COLLATE binary"
6 "SELECT count(*) FROM t1,t2 WHERE a=c GROUP BY b, d HAVING b=d"
"SELECT count(*) FROM t1,t2 WHERE a=c AND b=d GROUP BY b, d"
@@ -154,5 +154,38 @@ do_execsql_test 4.3 {
SELECT a, sum(b) FROM t3 WHERE nondeter(a) GROUP BY a
} {1 4 2 2}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(x, y);
+ INSERT INTO t1 VALUES('a', 'b');
+}
+
+# The WHERE clause (a=2), uses an aggregate column from the outer query.
+# If the HAVING term (0) is moved into the WHERE clause in this case,
+# SQLite would at one point optimize (a=2 AND 0) to simply (0). Which
+# is logically correct, but happened to cause problems in aggregate
+# processing for the outer query. This test case verifies that those
+# problems are no longer present.
+do_execsql_test 5.1 {
+ SELECT min(b), (
+ SELECT x FROM t2 WHERE a=2 GROUP BY y HAVING 0
+ ) FROM t1;
+} {b {}}
+
+# From chromium
+# https://bugs.chromium.org/p/chromium/issues/detail?id=1161869
+#
+do_execsql_test 5.2 {
+ SELECT EXISTS (
+ SELECT * FROM (
+ SELECT * FROM (
+ SELECT 1
+ ) WHERE Col0 = 1 GROUP BY 1
+ ) WHERE 0
+ )
+ FROM (SELECT 1 Col0) GROUP BY 1
+} {0}
finish_test
diff --git a/test/hook.test b/test/hook.test
index d137e9056f..0b72b2dcff 100644
--- a/test/hook.test
+++ b/test/hook.test
@@ -956,5 +956,63 @@ ifcapable analyze {
}]
}
+#-------------------------------------------------------------------------
+# Test that the pre-update hook is fired for INSERT statements that use
+# the xfer optimization on without rowid tables.
+#
+reset_db
+do_execsql_test 12.1 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
+
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t2 VALUES(5, 6);
+ INSERT INTO t2 VALUES(7, 8);
+
+ CREATE TABLE t3 (a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
+}
+
+db preupdate hook preupdate_cb
+db update_hook update_cb
+
+proc preupdate_cb {args} { lappend ::res "preupdate" $args }
+proc update_cb {args} { lappend ::res "update" $args }
+
+set ::res [list]
+do_test 12.2 {
+ execsql VACUUM
+ set ::res
+} {}
+
+do_test 12.3 {
+ set ::res [list]
+ execsql { INSERT INTO t3 SELECT a, b FROM t2 }
+ set ::res
+} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}}
+
+do_test 12.4 {
+ execsql { DELETE FROM t3 }
+ set ::res [list]
+ execsql { INSERT INTO t3 SELECT * FROM t2 }
+ set ::res
+} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}}
+
+do_execsql_test 12.5 {
+ CREATE TABLE t4(a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID;
+ INSERT INTO t4 VALUES('abc', 1);
+ INSERT INTO t4 VALUES('DEF', 2);
+}
+
+set ::res [list]
+do_test 12.6 {
+ execsql VACUUM
+ set ::res
+} {}
+
+do_catchsql_test 12.6 {
+ INSERT INTO t4 VALUES('def', 3);
+} {1 {UNIQUE constraint failed: t4.a}}
finish_test
+
diff --git a/test/in4.test b/test/in4.test
index 37eafdebec..bbb32d8a7a 100644
--- a/test/in4.test
+++ b/test/in4.test
@@ -365,4 +365,32 @@ ifcapable rtree {
} {{} 1 {} {} {}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 8.0 {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
+ CREATE UNIQUE INDEX t1y ON t1(y);
+ INSERT INTO t1 VALUES(111, 'AAA'),(222, 'BBB'),(333, 'CCC');
+ CREATE TABLE t2(z);
+ INSERT INTO t2 VALUES('BBB'),('AAA');
+ ANALYZE sqlite_schema;
+ INSERT INTO sqlite_stat1 VALUES('t1', 't1y','100 1');
+}
+
+db close
+sqlite3 db test.db
+
+do_execsql_test 8.1 {
+ SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y;
+} {222 111}
+
+do_execsql_test 8.2 {
+ SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y AND +t1.x IN (111, 222);
+} {222 111}
+
+do_execsql_test 8.3 {
+ SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y AND t1.x IN (111, 222);
+} {222 111}
+
+
finish_test
diff --git a/test/like.test b/test/like.test
index c29ebb2676..8ea7cff782 100644
--- a/test/like.test
+++ b/test/like.test
@@ -1131,4 +1131,11 @@ do_execsql_test 17.1 {
SELECT id FROM t1 WHERE x LIKE 'abc__' ESCAPE '_';
} {2}
+# 2021-02-15 ticket c0aeea67d58ae0fd
+#
+do_execsql_test 17.1 {
+ SELECT 'x' LIKE '%' ESCAPE '_';
+} {1}
+
+
finish_test
diff --git a/test/notnull2.test b/test/notnull2.test
new file mode 100644
index 0000000000..9ae190fe19
--- /dev/null
+++ b/test/notnull2.test
@@ -0,0 +1,101 @@
+# 2021 February 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing optimizations associated with "IS NULL"
+# and "IS NOT NULL" operators on columns with NOT NULL constraints.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix notnull2
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d NOT NULL);
+
+ WITH x(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<1000
+ )
+ INSERT INTO t1 SELECT i, i FROM x;
+ INSERT INTO t2 SELECT * FROM t1;
+}
+
+proc do_vmstep_test {tn sql nstep {res {}}} {
+ uplevel [list do_execsql_test $tn.0 $sql $res]
+
+ set vmstep [db status vmstep]
+ if {[string range $nstep 0 0]=="+"} {
+ set body "if {$vmstep<$nstep} {
+ error \"got $vmstep, expected more than [string range $nstep 1 end]\"
+ }"
+ } else {
+ set body "if {$vmstep>$nstep} {
+ error \"got $vmstep, expected less than $nstep\"
+ }"
+ }
+
+ # set name "$tn.vmstep=$vmstep,expect=$nstep"
+ set name "$tn.1"
+ uplevel [list do_test $name $body {}]
+}
+
+do_vmstep_test 1.1.1 {
+ SELECT * FROM t1 LEFT JOIN t2 WHERE a=c AND d IS NULL;
+} 100 {}
+do_vmstep_test 1.1.2 {
+ SELECT * FROM t1 LEFT JOIN t2 WHERE a=c AND c IS NULL;
+} +1000 {}
+
+do_vmstep_test 1.2.1 {
+ SELECT * FROM ( SELECT * FROM t2 ) WHERE d IS NULL
+} 100 {}
+do_vmstep_test 1.2.2 {
+ SELECT * FROM ( SELECT * FROM t2 ) WHERE c IS NULL
+} +1000 {}
+
+do_vmstep_test 1.3.1 {
+ SELECT * FROM t2 WHERE d IS NULL
+} 100 {}
+do_vmstep_test 1.3.2 {
+ SELECT * FROM t2 WHERE c IS NULL
+} +1000 {}
+
+do_vmstep_test 1.4.1 {
+ SELECT (d IS NOT NULL) FROM t2 WHERE 0==( d IS NOT NULL )
+} 100 {}
+do_vmstep_test 1.4.2 {
+ SELECT * FROM t2 WHERE 0==( c IS NOT NULL )
+} +1000 {}
+
+do_vmstep_test 1.5.1 {
+ SELECT count(*) FROM t2 WHERE EXISTS(
+ SELECT t2.d IS NULL FROM t1 WHERE t1.a=450
+ )
+} 10000 {1000}
+do_vmstep_test 1.5.2 {
+ SELECT count(*) FROM t2 WHERE EXISTS(
+ SELECT t2.c IS NULL FROM t1 WHERE t1.a=450
+ )
+} +100000 {1000}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE T1(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE T3(k, v);
+}
+
+do_execsql_test 2.1 {
+ SELECT * FROM (SELECT a, b FROM t1) LEFT JOIN t3 ON a IS NULL;
+}
+
+finish_test
+
diff --git a/test/permutations.test b/test/permutations.test
index 51dcec2341..972a7c6918 100644
--- a/test/permutations.test
+++ b/test/permutations.test
@@ -1098,7 +1098,8 @@ test_suite "no_optimization" -description {
where6.test where7.test where8.test where9.test \
whereA.test whereB.test wherelimit.test \
select1.test select2.test select3.test select4.test select5.test \
- select7.test select8.test selectA.test selectC.test
+ select7.test select8.test selectA.test selectC.test \
+ -exclude windowpushd.test
] -dbconfig {
optimization_control $::dbhandle all 0
}
diff --git a/test/releasetest_data.tcl b/test/releasetest_data.tcl
index 9692cf47fc..5aa27f6e6e 100644
--- a/test/releasetest_data.tcl
+++ b/test/releasetest_data.tcl
@@ -289,7 +289,7 @@ if {$tcl_platform(os)=="Darwin"} {
array set ::Platforms [strip_comments {
Linux-x86_64 {
"Check-Symbols*" checksymbols
- "Fast-One" "fuzztest test"
+ "Fast-One" "QUICKTEST_INCLUDE=rbu.test fuzztest test"
"Debug-One" "mptest test"
"Debug-Two" "test"
"Have-Not" test
@@ -586,9 +586,15 @@ proc main_tests {args} {
puts "$config \"$target\""
if {$bNodebug==0 && $bNosynthetic==0} {
set iHas [string first SQLITE_DEBUG $::Configs($config)]
- set dtarget test
- if {$target=="tcltest"} {
- set dtarget tcltest
+ set dtarget [list]
+ set iQTI [lsearch -glob $target QUICKTEST_*]
+ if {$iQTI>=0} {
+ lappend dtarget [lindex $target $iQTI]
+ }
+ if {[lsearch $target tcltest]>=0} {
+ lappend dtarget tcltest
+ } else {
+ lappend dtarget test
}
if {$iHas>=0} {
puts "$config-ndebug \"$dtarget\""
diff --git a/test/returning1.test b/test/returning1.test
new file mode 100644
index 0000000000..52fb8812a6
--- /dev/null
+++ b/test/returning1.test
@@ -0,0 +1,87 @@
+# 2021-01-28
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is the new RETURNING clause
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix returning1
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c DEFAULT 'pax');
+ INSERT INTO t1(b) VALUES(10),('happy'),(NULL) RETURNING a,b,c;
+} {1 10 pax 2 happy pax 3 {} pax}
+do_execsql_test 1.1 {
+ SELECT * FROM t1;
+} {1 10 pax 2 happy pax 3 {} pax}
+do_execsql_test 1.2 {
+ INSERT INTO t1(b,c) VALUES(5,99) RETURNING b,c,a,rowid;
+} {5 99 4 4}
+do_execsql_test 1.3 {
+ SELECT * FROM t1;
+} {1 10 pax 2 happy pax 3 {} pax 4 5 99}
+do_execsql_test 1.4 {
+ INSERT INTO t1 DEFAULT VALUES RETURNING *;
+} {5 {} pax}
+do_execsql_test 1.5 {
+ SELECT * FROM t1;
+} {1 10 pax 2 happy pax 3 {} pax 4 5 99 5 {} pax}
+do_execsql_test 1.6 {
+ CREATE TABLE t2(x,y,z);
+ INSERT INTO t2 VALUES(11,12,13),(21,'b','c'),(31,'b-value',4.75);
+}
+do_execsql_test 1.7 {
+ INSERT INTO t1 SELECT * FROM t2 RETURNING *;
+} {11 12 13 21 b c 31 b-value 4.75}
+do_execsql_test 1.8 {
+ SELECT *, '|' FROM t1;
+} {1 10 pax | 2 happy pax | 3 {} pax | 4 5 99 | 5 {} pax | 11 12 13 | 21 b c | 31 b-value 4.75 |}
+
+do_execsql_test 2.1 {
+ UPDATE t1 SET c='bellum' WHERE c='pax' RETURNING rowid, b, '|';
+} {1 10 | 2 happy | 3 {} | 5 {} |}
+do_execsql_test 2.2 {
+ SELECT *, '|' FROM t1;
+} {1 10 bellum | 2 happy bellum | 3 {} bellum | 4 5 99 | 5 {} bellum | 11 12 13 | 21 b c | 31 b-value 4.75 |}
+
+do_execsql_test 3.1 {
+ DELETE FROM t1 WHERE c='bellum' RETURNING rowid, *, '|';
+} {1 1 10 bellum | 2 2 happy bellum | 3 3 {} bellum | 5 5 {} bellum |}
+do_execsql_test 3.2 {
+ SELECT *, '|' FROM t1;
+} {4 5 99 | 11 12 13 | 21 b c | 31 b-value 4.75 |}
+
+do_execsql_test 4.1 {
+ CREATE TABLE t4(a INT, b INT DEFAULT 1234, c INT DEFAULT -16);
+ CREATE UNIQUE INDEX t4a ON t4(a);
+ INSERT INTO t4(a,b,c) VALUES(1,2,3);
+} {}
+do_execsql_test 4.2 {
+ INSERT INTO t4(a,b,c) VALUES(1,22,33)
+ ON CONFLICT(a) DO UPDATE SET b=44
+ RETURNING *;
+} {1 44 3}
+do_execsql_test 4.3 {
+ SELECT * FROM t4;
+} {1 44 3}
+do_execsql_test 4.4 {
+ DELETE FROM t4;
+ INSERT INTO t4 VALUES(1,2,3),(4,5,6),(7,8,9);
+} {}
+do_execsql_test 4.5 {
+ INSERT INTO t4(a,b,c) VALUES(2,3,4),(4,5,6),(5,6,7)
+ ON CONFLICT(a) DO UPDATE SET b=100
+ RETURNING *, '|';
+} {2 3 4 | 4 100 6 | 5 6 7 |}
+
+
+finish_test
diff --git a/test/rowvalue.test b/test/rowvalue.test
index e3b66a1096..91a59e71c3 100644
--- a/test/rowvalue.test
+++ b/test/rowvalue.test
@@ -644,4 +644,17 @@ do_catchsql_test 27.10 {
INSERT INTO t0(c0) VALUES(0) ON CONFLICT(c0) DO UPDATE SET c0 = 3;
} {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}}
+# 2021-02-03
+# https://bugs.chromium.org/p/chromium/issues/detail?id=1173511
+# Faulty assert() statement.
+#
+reset_db
+do_catchsql_test 28.10 {
+ CREATE TABLE t0(c0 PRIMARY KEY, c1);
+ CREATE TRIGGER trigger0 BEFORE DELETE ON t0 BEGIN
+ SELECT (SELECT c0,c1 FROM t0) FROM t0;
+ END ;
+ DELETE FROM t0;
+} {1 {sub-select returns 2 columns - expected 1}}
+
finish_test
diff --git a/test/select1.test b/test/select1.test
index 82e6ab55c8..e22907da1e 100644
--- a/test/select1.test
+++ b/test/select1.test
@@ -1167,17 +1167,17 @@ do_execsql_test select1-18.4 {
# 2019-12-17 gramfuzz find
#
-do_execsql_test select-19.10 {
+do_execsql_test select1-19.10 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x);
} {}
-do_catchsql_test select-19.20 {
+do_catchsql_test select1-19.20 {
INSERT INTO t1
SELECT 1,2,3,4,5,6,7
UNION ALL SELECT 1,2,3,4,5,6,7
ORDER BY 1;
} {1 {table t1 has 1 columns but 7 values were supplied}}
-do_catchsql_test select-19.21 {
+do_catchsql_test select1-19.21 {
INSERT INTO t1
SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
UNION ALL SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
@@ -1187,7 +1187,7 @@ do_catchsql_test select-19.21 {
# 2020-01-01 Found by Yongheng's fuzzer
#
reset_db
-do_execsql_test select-20.10 {
+do_execsql_test select1-20.10 {
CREATE TABLE t1 (
a INTEGER PRIMARY KEY,
b AS('Y') UNIQUE
@@ -1197,13 +1197,13 @@ do_execsql_test select-20.10 {
WHERE ((SELECT t1.a FROM t1 AS x GROUP BY b) AND b=0)
OR a = 10;
} {10 Y}
-do_execsql_test select-20.20 {
+do_execsql_test select1-20.20 {
SELECT ifnull(a, max((SELECT 123))), count(a) FROM t1 ;
} {10 1}
# 2020-10-02 dbsqlfuzz find
reset_db
-do_execsql_test select-21.1 {
+do_execsql_test select1-21.1 {
CREATE TABLE t1(a IMTEGES PRIMARY KEY,R);
CREATE TABLE t2(x UNIQUE);
CREATE VIEW v1a(z,y) AS SELECT x IS NULL, x FROM t2;
diff --git a/test/selectC.test b/test/selectC.test
index 4d79963007..63851ca5d7 100644
--- a/test/selectC.test
+++ b/test/selectC.test
@@ -261,10 +261,10 @@ do_execsql_test 5.2 {
}
do_execsql_test 5.3 {
- SELECT * FROM x1, (SELECT b FROM vvv UNION ALL SELECT c from x3);
+ SELECT * FROM x1, (SELECT b FROM vvv UNION ALL SELECT c from x3) ORDER BY 1,2;
} {
- a 21 a 22 a 23 a 24 a 25 a 302 a 303 a 301
- b 21 b 22 b 23 b 24 b 25 b 302 b 303 b 301
+ a 21 a 22 a 23 a 24 a 25 a 301 a 302 a 303
+ b 21 b 22 b 23 b 24 b 25 b 301 b 302 b 303
}
finish_test
diff --git a/test/shell1.test b/test/shell1.test
index a62b0d1a80..330276120d 100644
--- a/test/shell1.test
+++ b/test/shell1.test
@@ -642,7 +642,7 @@ do_test shell1-3.23.2 {
# .stats ON|OFF Turn stats on or off
#do_test shell1-3.23b.1 {
# catchcmd "test.db" ".stats"
-#} {1 {Usage: .stats on|off}}
+#} {1 {Usage: .stats on|off|stmt|vmstep}}
do_test shell1-3.23b.2 {
catchcmd "test.db" ".stats ON"
} {0 {}}
@@ -652,7 +652,7 @@ do_test shell1-3.23b.3 {
do_test shell1-3.23b.4 {
# too many arguments
catchcmd "test.db" ".stats OFF BAD"
-} {1 {Usage: .stats ?on|off?}}
+} {1 {Usage: .stats ?on|off|stmt|vmstep?}}
# Ticket 7be932dfa60a8a6b3b26bcf7623ec46e0a403ddb 2018-06-07
# Adverse interaction between .stats and .eqp
diff --git a/test/shell4.test b/test/shell4.test
index 386e51f6c7..9e3c58fbec 100644
--- a/test/shell4.test
+++ b/test/shell4.test
@@ -66,7 +66,7 @@ do_test shell4-1.3.3 {
do_test shell4-1.3.4 {
# too many arguments
catchcmd "test.db" ".stats OFF BAD"
-} {1 {Usage: .stats ?on|off?}}
+} {1 {Usage: .stats ?on|off|stmt|vmstep?}}
# NB. whitespace is important
do_test shell4-1.4.1 {
diff --git a/test/speedtest1.c b/test/speedtest1.c
index 3d7ac4c8e8..9e8c396085 100644
--- a/test/speedtest1.c
+++ b/test/speedtest1.c
@@ -707,9 +707,9 @@ void testset_main(void){
maxb = roundup_allones(sz);
speedtest1_begin_test(100, "%d INSERTs into table with no index", n);
speedtest1_exec("BEGIN");
- speedtest1_exec("CREATE%s TABLE t1(a INTEGER %s, b INTEGER %s, c TEXT %s);",
+ speedtest1_exec("CREATE%s TABLE z1(a INTEGER %s, b INTEGER %s, c TEXT %s);",
isTemp(9), g.zNN, g.zNN, g.zNN);
- speedtest1_prepare("INSERT INTO t1 VALUES(?1,?2,?3); -- %d times", n);
+ speedtest1_prepare("INSERT INTO z1 VALUES(?1,?2,?3); -- %d times", n);
for(i=1; i<=n; i++){
x1 = swizzle(i,maxb);
speedtest1_numbername(x1, zNum, sizeof(zNum));
@@ -726,9 +726,9 @@ void testset_main(void){
speedtest1_begin_test(110, "%d ordered INSERTS with one index/PK", n);
speedtest1_exec("BEGIN");
speedtest1_exec(
- "CREATE%s TABLE t2(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s",
+ "CREATE%s TABLE z2(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s",
isTemp(5), g.zNN, g.zPK, g.zNN, g.zNN, g.zWR);
- speedtest1_prepare("INSERT INTO t2 VALUES(?1,?2,?3); -- %d times", n);
+ speedtest1_prepare("INSERT INTO z2 VALUES(?1,?2,?3); -- %d times", n);
for(i=1; i<=n; i++){
x1 = swizzle(i,maxb);
speedtest1_numbername(x1, zNum, sizeof(zNum));
@@ -768,7 +768,7 @@ void testset_main(void){
speedtest1_begin_test(130, "%d SELECTS, numeric BETWEEN, unindexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT count(*), avg(b), sum(length(c)), group_concat(c) FROM t1\n"
+ "SELECT count(*), avg(b), sum(length(c)), group_concat(c) FROM z1\n"
" WHERE b BETWEEN ?1 AND ?2; -- %d times", n
);
for(i=1; i<=n; i++){
@@ -788,7 +788,7 @@ void testset_main(void){
speedtest1_begin_test(140, "%d SELECTS, LIKE, unindexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT count(*), avg(b), sum(length(c)), group_concat(c) FROM t1\n"
+ "SELECT count(*), avg(b), sum(length(c)), group_concat(c) FROM z1\n"
" WHERE c LIKE ?1; -- %d times", n
);
for(i=1; i<=n; i++){
@@ -810,7 +810,7 @@ void testset_main(void){
speedtest1_begin_test(142, "%d SELECTS w/ORDER BY, unindexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT a, b, c FROM t1 WHERE c LIKE ?1\n"
+ "SELECT a, b, c FROM z1 WHERE c LIKE ?1\n"
" ORDER BY a; -- %d times", n
);
for(i=1; i<=n; i++){
@@ -831,7 +831,7 @@ void testset_main(void){
speedtest1_begin_test(145, "%d SELECTS w/ORDER BY and LIMIT, unindexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT a, b, c FROM t1 WHERE c LIKE ?1\n"
+ "SELECT a, b, c FROM z1 WHERE c LIKE ?1\n"
" ORDER BY a LIMIT 10; -- %d times", n
);
for(i=1; i<=n; i++){
@@ -851,10 +851,10 @@ void testset_main(void){
speedtest1_begin_test(150, "CREATE INDEX five times");
speedtest1_exec("BEGIN;");
- speedtest1_exec("CREATE UNIQUE INDEX t1b ON t1(b);");
- speedtest1_exec("CREATE INDEX t1c ON t1(c);");
- speedtest1_exec("CREATE UNIQUE INDEX t2b ON t2(b);");
- speedtest1_exec("CREATE INDEX t2c ON t2(c DESC);");
+ speedtest1_exec("CREATE UNIQUE INDEX t1b ON z1(b);");
+ speedtest1_exec("CREATE INDEX t1c ON z1(c);");
+ speedtest1_exec("CREATE UNIQUE INDEX t2b ON z2(b);");
+ speedtest1_exec("CREATE INDEX t2c ON z2(c DESC);");
speedtest1_exec("CREATE INDEX t3bc ON t3(b,c);");
speedtest1_exec("COMMIT;");
speedtest1_end_test();
@@ -864,7 +864,7 @@ void testset_main(void){
speedtest1_begin_test(160, "%d SELECTS, numeric BETWEEN, indexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM t1\n"
+ "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM z1\n"
" WHERE b BETWEEN ?1 AND ?2; -- %d times", n
);
for(i=1; i<=n; i++){
@@ -884,7 +884,7 @@ void testset_main(void){
speedtest1_begin_test(161, "%d SELECTS, numeric BETWEEN, PK", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM t2\n"
+ "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM z2\n"
" WHERE a BETWEEN ?1 AND ?2; -- %d times", n
);
for(i=1; i<=n; i++){
@@ -904,7 +904,7 @@ void testset_main(void){
speedtest1_begin_test(170, "%d SELECTS, text BETWEEN, indexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM t1\n"
+ "SELECT count(*), avg(b), sum(length(c)), group_concat(a) FROM z1\n"
" WHERE c BETWEEN ?1 AND (?1||'~'); -- %d times", n
);
for(i=1; i<=n; i++){
@@ -930,14 +930,14 @@ void testset_main(void){
isTemp(1), g.zNN, g.zPK, g.zNN, g.zNN, g.zWR);
speedtest1_exec("CREATE INDEX t4b ON t4(b)");
speedtest1_exec("CREATE INDEX t4c ON t4(c)");
- speedtest1_exec("INSERT INTO t4 SELECT * FROM t1");
+ speedtest1_exec("INSERT INTO t4 SELECT * FROM z1");
speedtest1_exec("COMMIT");
speedtest1_end_test();
n = sz;
speedtest1_begin_test(190, "DELETE and REFILL one table", n);
- speedtest1_exec("DELETE FROM t2;");
- speedtest1_exec("INSERT INTO t2 SELECT * FROM t1;");
+ speedtest1_exec("DELETE FROM z2;");
+ speedtest1_exec("INSERT INTO z2 SELECT * FROM z1;");
speedtest1_end_test();
@@ -947,8 +947,8 @@ void testset_main(void){
speedtest1_begin_test(210, "ALTER TABLE ADD COLUMN, and query");
- speedtest1_exec("ALTER TABLE t2 ADD COLUMN d DEFAULT 123");
- speedtest1_exec("SELECT sum(d) FROM t2");
+ speedtest1_exec("ALTER TABLE z2 ADD COLUMN d DEFAULT 123");
+ speedtest1_exec("SELECT sum(d) FROM z2");
speedtest1_end_test();
@@ -956,7 +956,7 @@ void testset_main(void){
speedtest1_begin_test(230, "%d UPDATES, numeric BETWEEN, indexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "UPDATE t2 SET d=b*2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n
+ "UPDATE z2 SET d=b*2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n
);
for(i=1; i<=n; i++){
x1 = speedtest1_random()%maxb;
@@ -973,7 +973,7 @@ void testset_main(void){
speedtest1_begin_test(240, "%d UPDATES of individual rows", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "UPDATE t2 SET d=b*3 WHERE a=?1; -- %d times", n
+ "UPDATE z2 SET d=b*3 WHERE a=?1; -- %d times", n
);
for(i=1; i<=n; i++){
x1 = speedtest1_random()%sz + 1;
@@ -984,12 +984,12 @@ void testset_main(void){
speedtest1_end_test();
speedtest1_begin_test(250, "One big UPDATE of the whole %d-row table", sz);
- speedtest1_exec("UPDATE t2 SET d=b*4");
+ speedtest1_exec("UPDATE z2 SET d=b*4");
speedtest1_end_test();
speedtest1_begin_test(260, "Query added column after filling");
- speedtest1_exec("SELECT sum(d) FROM t2");
+ speedtest1_exec("SELECT sum(d) FROM z2");
speedtest1_end_test();
@@ -998,7 +998,7 @@ void testset_main(void){
speedtest1_begin_test(270, "%d DELETEs, numeric BETWEEN, indexed", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "DELETE FROM t2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n
+ "DELETE FROM z2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n
);
for(i=1; i<=n; i++){
x1 = speedtest1_random()%maxb + 1;
@@ -1027,16 +1027,16 @@ void testset_main(void){
speedtest1_begin_test(290, "Refill two %d-row tables using REPLACE", sz);
- speedtest1_exec("REPLACE INTO t2(a,b,c) SELECT a,b,c FROM t1");
- speedtest1_exec("REPLACE INTO t3(a,b,c) SELECT a,b,c FROM t1");
+ speedtest1_exec("REPLACE INTO z2(a,b,c) SELECT a,b,c FROM z1");
+ speedtest1_exec("REPLACE INTO t3(a,b,c) SELECT a,b,c FROM z1");
speedtest1_end_test();
speedtest1_begin_test(300, "Refill a %d-row table using (b&1)==(a&1)", sz);
- speedtest1_exec("DELETE FROM t2;");
- speedtest1_exec("INSERT INTO t2(a,b,c)\n"
- " SELECT a,b,c FROM t1 WHERE (b&1)==(a&1);");
- speedtest1_exec("INSERT INTO t2(a,b,c)\n"
- " SELECT a,b,c FROM t1 WHERE (b&1)<>(a&1);");
+ speedtest1_exec("DELETE FROM z2;");
+ speedtest1_exec("INSERT INTO z2(a,b,c)\n"
+ " SELECT a,b,c FROM z1 WHERE (b&1)==(a&1);");
+ speedtest1_exec("INSERT INTO z2(a,b,c)\n"
+ " SELECT a,b,c FROM z1 WHERE (b&1)<>(a&1);");
speedtest1_end_test();
@@ -1044,11 +1044,11 @@ void testset_main(void){
speedtest1_begin_test(310, "%d four-ways joins", n);
speedtest1_exec("BEGIN");
speedtest1_prepare(
- "SELECT t1.c FROM t1, t2, t3, t4\n"
+ "SELECT z1.c FROM z1, z2, t3, t4\n"
" WHERE t4.a BETWEEN ?1 AND ?2\n"
" AND t3.a=t4.b\n"
- " AND t2.a=t3.b\n"
- " AND t1.c=t2.c"
+ " AND z2.a=t3.b\n"
+ " AND z1.c=z2.c"
);
for(i=1; i<=n; i++){
x1 = speedtest1_random()%sz + 1;
@@ -1063,8 +1063,8 @@ void testset_main(void){
speedtest1_begin_test(320, "subquery in result set", n);
speedtest1_prepare(
"SELECT sum(a), max(c),\n"
- " avg((SELECT a FROM t2 WHERE 5+t2.b=t1.b) AND rowid1), max(c)\n"
- " FROM t1 WHERE rowid1;"
+ " avg((SELECT a FROM z2 WHERE 5+z2.b=z1.b) AND rowid1), max(c)\n"
+ " FROM z1 WHERE rowid1;"
);
sqlite3_bind_int(g.pStmt, 1, est_square_root(g.szTest)*50);
speedtest1_run();
@@ -1282,10 +1282,10 @@ void testset_cte(void){
speedtest1_begin_test(400, "EXCEPT operator on %d-element tables", nElem);
speedtest1_prepare(
"WITH RECURSIVE \n"
- " t1(x) AS (VALUES(2) UNION ALL SELECT x+2 FROM t1 WHERE x<%d),\n"
- " t2(y) AS (VALUES(3) UNION ALL SELECT y+3 FROM t2 WHERE y<%d)\n"
+ " z1(x) AS (VALUES(2) UNION ALL SELECT x+2 FROM z1 WHERE x<%d),\n"
+ " z2(y) AS (VALUES(3) UNION ALL SELECT y+3 FROM z2 WHERE y<%d)\n"
"SELECT count(x), avg(x) FROM (\n"
- " SELECT x FROM t1 EXCEPT SELECT y FROM t2 ORDER BY 1\n"
+ " SELECT x FROM z1 EXCEPT SELECT y FROM z2 ORDER BY 1\n"
");",
nElem, nElem
);
@@ -1318,9 +1318,9 @@ void testset_fp(void){
n = g.szTest*5000;
speedtest1_begin_test(100, "Fill a table with %d FP values", n*2);
speedtest1_exec("BEGIN");
- speedtest1_exec("CREATE%s TABLE t1(a REAL %s, b REAL %s);",
+ speedtest1_exec("CREATE%s TABLE z1(a REAL %s, b REAL %s);",
isTemp(1), g.zNN, g.zNN);
- speedtest1_prepare("INSERT INTO t1 VALUES(?1,?2); -- %d times", n);
+ speedtest1_prepare("INSERT INTO z1 VALUES(?1,?2); -- %d times", n);
for(i=1; i<=n; i++){
speedtest1_random_ascii_fp(zFP1);
speedtest1_random_ascii_fp(zFP2);
@@ -1333,7 +1333,7 @@ void testset_fp(void){
n = g.szTest/25 + 2;
speedtest1_begin_test(110, "%d range queries", n);
- speedtest1_prepare("SELECT sum(b) FROM t1 WHERE a BETWEEN ?1 AND ?2");
+ speedtest1_prepare("SELECT sum(b) FROM z1 WHERE a BETWEEN ?1 AND ?2");
for(i=1; i<=n; i++){
speedtest1_random_ascii_fp(zFP1);
speedtest1_random_ascii_fp(zFP2);
@@ -1345,15 +1345,15 @@ void testset_fp(void){
speedtest1_begin_test(120, "CREATE INDEX three times");
speedtest1_exec("BEGIN;");
- speedtest1_exec("CREATE INDEX t1a ON t1(a);");
- speedtest1_exec("CREATE INDEX t1b ON t1(b);");
- speedtest1_exec("CREATE INDEX t1ab ON t1(a,b);");
+ speedtest1_exec("CREATE INDEX t1a ON z1(a);");
+ speedtest1_exec("CREATE INDEX t1b ON z1(b);");
+ speedtest1_exec("CREATE INDEX t1ab ON z1(a,b);");
speedtest1_exec("COMMIT;");
speedtest1_end_test();
n = g.szTest/3 + 2;
speedtest1_begin_test(130, "%d indexed range queries", n);
- speedtest1_prepare("SELECT sum(b) FROM t1 WHERE a BETWEEN ?1 AND ?2");
+ speedtest1_prepare("SELECT sum(b) FROM z1 WHERE a BETWEEN ?1 AND ?2");
for(i=1; i<=n; i++){
speedtest1_random_ascii_fp(zFP1);
speedtest1_random_ascii_fp(zFP2);
@@ -1365,14 +1365,14 @@ void testset_fp(void){
n = g.szTest*5000;
speedtest1_begin_test(140, "%d calls to round()", n);
- speedtest1_exec("SELECT sum(round(a,2)+round(b,4)) FROM t1;");
+ speedtest1_exec("SELECT sum(round(a,2)+round(b,4)) FROM z1;");
speedtest1_end_test();
speedtest1_begin_test(150, "%d printf() calls", n*4);
speedtest1_exec(
"WITH c(fmt) AS (VALUES('%%g'),('%%e'),('%%!g'),('%%.20f'))"
- "SELECT sum(printf(fmt,a)) FROM t1, c"
+ "SELECT sum(printf(fmt,a)) FROM z1, c"
);
speedtest1_end_test();
}
@@ -1458,8 +1458,8 @@ void testset_rtree(int p1, int p2){
speedtest1_end_test();
speedtest1_begin_test(101, "Copy from rtree to a regular table");
- speedtest1_exec("CREATE TABLE t1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)");
- speedtest1_exec("INSERT INTO t1 SELECT * FROM rt1");
+ speedtest1_exec("CREATE TABLE z1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)");
+ speedtest1_exec("INSERT INTO z1 SELECT * FROM rt1");
speedtest1_end_test();
n = g.szTest*200;
@@ -1477,7 +1477,7 @@ void testset_rtree(int p1, int p2){
if( g.bVerify ){
n = g.szTest*200;
speedtest1_begin_test(111, "Verify result from 1-D intersect slice queries");
- speedtest1_prepare("SELECT count(*) FROM t1 WHERE x0>=?1 AND x1<=?2");
+ speedtest1_prepare("SELECT count(*) FROM z1 WHERE x0>=?1 AND x1<=?2");
iStep = mxCoord/n;
for(i=0; i=?1 AND y0<=?2");
+ speedtest1_prepare("SELECT count(*) FROM z1 WHERE y1>=?1 AND y0<=?2");
iStep = mxCoord/n;
for(i=0; i
+#include
+#include
+#include
+#include
+#include "sqlite3.h"
+
+static const char zHelp[] =
+ "Usage: %s COMMAND\n"
+ "Commands:\n"
+ " init Initialized the startup.db database file\n"
+ " run Run the startup performance test\n"
+ "Options:\n"
+ " --dbname NAME Set the name of the test database file\n"
+ " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n"
+ " --stats Show statistics at the end\n"
+/* TBD
+ " --journal M Set the journal_mode to M\n"
+ " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n"
+ " --mmap SZ MMAP the first SZ bytes of the database file\n"
+ " --multithread Set multithreaded mode\n"
+ " --nomemstat Disable memory statistics\n"
+ " --pagesize N Set the page size to N\n"
+ " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n"
+ " --serialized Set serialized threading mode\n"
+ " --singlethread Set single-threaded mode - disables all mutexing\n"
+ " --utf16be Set text encoding to UTF-16BE\n"
+ " --utf16le Set text encoding to UTF-16LE\n"
+ " --utf8 Set text encoding to UTF-8\n"
+*/
+;
+
+static void usage(const char *argv0){
+ printf(zHelp, argv0);
+ exit(1);
+}
+
+/*
+** The test schema is derived from the Fossil repository for SQLite itself.
+** The schema covers the repository, the local checkout database, and
+** the global configuration database.
+*/
+static const char zTestSchema[] =
+ "CREATE TABLE repo_blob(\n"
+ " rid INTEGER PRIMARY KEY,\n"
+ " rcvid INTEGER,\n"
+ " size INTEGER,\n"
+ " uuid TEXT UNIQUE NOT NULL,\n"
+ " content BLOB,\n"
+ " CHECK( length(uuid)>=40 AND rid>0 )\n"
+ ");\n"
+ "CREATE TABLE repo_delta(\n"
+ " rid INTEGER PRIMARY KEY,\n"
+ " srcid INTEGER NOT NULL REFERENCES blob\n"
+ ");\n"
+ "CREATE TABLE repo_rcvfrom(\n"
+ " rcvid INTEGER PRIMARY KEY,\n"
+ " uid INTEGER REFERENCES user,\n"
+ " mtime DATETIME,\n"
+ " nonce TEXT UNIQUE,\n"
+ " ipaddr TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_private(rid INTEGER PRIMARY KEY);\n"
+ "CREATE TABLE repo_accesslog(\n"
+ " uname TEXT,\n"
+ " ipaddr TEXT,\n"
+ " success BOOLEAN,\n"
+ " mtime TIMESTAMP);\n"
+ "CREATE TABLE repo_user(\n"
+ " uid INTEGER PRIMARY KEY,\n"
+ " login TEXT UNIQUE,\n"
+ " pw TEXT,\n"
+ " cap TEXT,\n"
+ " cookie TEXT,\n"
+ " ipaddr TEXT,\n"
+ " cexpire DATETIME,\n"
+ " info TEXT,\n"
+ " mtime DATE,\n"
+ " photo BLOB\n"
+ ");\n"
+ "CREATE TABLE repo_reportfmt(\n"
+ " rn INTEGER PRIMARY KEY,\n"
+ " owner TEXT,\n"
+ " title TEXT UNIQUE,\n"
+ " mtime INTEGER,\n"
+ " cols TEXT,\n"
+ " sqlcode TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_sqlite_stat2(tbl,idx,sampleno,sample);\n"
+ "CREATE TABLE repo_sqlite_stat1(tbl,idx,stat);\n"
+ "CREATE TABLE repo_sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);\n"
+ "CREATE TABLE repo_config(\n"
+ " name TEXT PRIMARY KEY NOT NULL,\n"
+ " value CLOB, mtime INTEGER,\n"
+ " CHECK( typeof(name)='text' AND length(name)>=1 )\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE repo_shun(uuid PRIMARY KEY,\n"
+ " mtime INTEGER,\n"
+ " scom TEXT) WITHOUT ROWID;\n"
+ "CREATE TABLE repo_concealed(\n"
+ " hash TEXT PRIMARY KEY,\n"
+ " content TEXT\n"
+ ", mtime INTEGER) WITHOUT ROWID;\n"
+ "CREATE TABLE repo_admin_log(\n"
+ " id INTEGER PRIMARY KEY,\n"
+ " time INTEGER, -- Seconds since 1970\n"
+ " page TEXT, -- path of page\n"
+ " who TEXT, -- User who made the change\n"
+ " what TEXT -- What changed\n"
+ ");\n"
+ "CREATE TABLE repo_unversioned(\n"
+ " name TEXT PRIMARY KEY,\n"
+ " rcvid INTEGER,\n"
+ " mtime DATETIME,\n"
+ " hash TEXT,\n"
+ " sz INTEGER,\n"
+ " encoding INT,\n"
+ " content BLOB\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE repo_subscriber(\n"
+ " subscriberId INTEGER PRIMARY KEY,\n"
+ " subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE,\n"
+ " semail TEXT UNIQUE COLLATE nocase,\n"
+ " suname TEXT,\n"
+ " sverified BOOLEAN DEFAULT true,\n"
+ " sdonotcall BOOLEAN,\n"
+ " sdigest BOOLEAN,\n"
+ " ssub TEXT,\n"
+ " sctime INTDATE,\n"
+ " mtime INTDATE,\n"
+ " smip TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_pending_alert(\n"
+ " eventid TEXT PRIMARY KEY,\n"
+ " sentSep BOOLEAN DEFAULT false,\n"
+ " sentDigest BOOLEAN DEFAULT false\n"
+ ", sentMod BOOLEAN DEFAULT false) WITHOUT ROWID;\n"
+ "CREATE INDEX repo_delta_i1 ON repo_delta(srcid);\n"
+ "CREATE INDEX repo_blob_rcvid ON repo_blob(rcvid);\n"
+ "CREATE INDEX repo_subscriberUname\n"
+ " ON repo_subscriber(suname) WHERE suname IS NOT NULL;\n"
+ "CREATE VIEW repo_artifact(rid,rcvid,size,atype,srcid,hash,content) AS\n"
+ " SELECT blob.rid,rcvid,size,1,srcid,uuid,content\n"
+ " FROM repo_blob LEFT JOIN repo_delta ON (blob.rid=delta.rid);\n"
+ "CREATE TABLE repo_filename(\n"
+ " fnid INTEGER PRIMARY KEY,\n"
+ " name TEXT UNIQUE\n"
+ ");\n"
+ "CREATE TABLE repo_mlink(\n"
+ " mid INTEGER,\n"
+ " fid INTEGER,\n"
+ " pmid INTEGER,\n"
+ " pid INTEGER,\n"
+ " fnid INTEGER REFERENCES filename,\n"
+ " pfnid INTEGER,\n"
+ " mperm INTEGER,\n"
+ " isaux BOOLEAN DEFAULT 0\n"
+ ");\n"
+ "CREATE INDEX repo_mlink_i1 ON repo_mlink(mid);\n"
+ "CREATE INDEX repo_mlink_i2 ON repo_mlink(fnid);\n"
+ "CREATE INDEX repo_mlink_i3 ON repo_mlink(fid);\n"
+ "CREATE INDEX repo_mlink_i4 ON repo_mlink(pid);\n"
+ "CREATE TABLE repo_plink(\n"
+ " pid INTEGER REFERENCES blob,\n"
+ " cid INTEGER REFERENCES blob,\n"
+ " isprim BOOLEAN,\n"
+ " mtime DATETIME,\n"
+ " baseid INTEGER REFERENCES blob,\n"
+ " UNIQUE(pid, cid)\n"
+ ");\n"
+ "CREATE INDEX repo_plink_i2 ON repo_plink(cid,pid);\n"
+ "CREATE TABLE repo_leaf(rid INTEGER PRIMARY KEY);\n"
+ "CREATE TABLE repo_event(\n"
+ " type TEXT,\n"
+ " mtime DATETIME,\n"
+ " objid INTEGER PRIMARY KEY,\n"
+ " tagid INTEGER,\n"
+ " uid INTEGER REFERENCES user,\n"
+ " bgcolor TEXT,\n"
+ " euser TEXT,\n"
+ " user TEXT,\n"
+ " ecomment TEXT,\n"
+ " comment TEXT,\n"
+ " brief TEXT,\n"
+ " omtime DATETIME\n"
+ ");\n"
+ "CREATE INDEX repo_event_i1 ON repo_event(mtime);\n"
+ "CREATE TABLE repo_phantom(\n"
+ " rid INTEGER PRIMARY KEY\n"
+ ");\n"
+ "CREATE TABLE repo_orphan(\n"
+ " rid INTEGER PRIMARY KEY,\n"
+ " baseline INTEGER\n"
+ ");\n"
+ "CREATE INDEX repo_orphan_baseline ON repo_orphan(baseline);\n"
+ "CREATE TABLE repo_unclustered(\n"
+ " rid INTEGER PRIMARY KEY\n"
+ ");\n"
+ "CREATE TABLE repo_unsent(\n"
+ " rid INTEGER PRIMARY KEY\n"
+ ");\n"
+ "CREATE TABLE repo_tag(\n"
+ " tagid INTEGER PRIMARY KEY,\n"
+ " tagname TEXT UNIQUE\n"
+ ");\n"
+ "CREATE TABLE repo_tagxref(\n"
+ " tagid INTEGER REFERENCES tag,\n"
+ " tagtype INTEGER,\n"
+ " srcid INTEGER REFERENCES blob,\n"
+ " origid INTEGER REFERENCES blob,\n"
+ " value TEXT,\n"
+ " mtime TIMESTAMP,\n"
+ " rid INTEGER REFERENCE blob,\n"
+ " UNIQUE(rid, tagid)\n"
+ ");\n"
+ "CREATE INDEX repo_tagxref_i1 ON repo_tagxref(tagid, mtime);\n"
+ "CREATE TABLE repo_backlink(\n"
+ " target TEXT,\n"
+ " srctype INT,\n"
+ " srcid INT,\n"
+ " mtime TIMESTAMP,\n"
+ " UNIQUE(target, srctype, srcid)\n"
+ ");\n"
+ "CREATE INDEX repo_backlink_src ON repo_backlink(srcid, srctype);\n"
+ "CREATE TABLE repo_attachment(\n"
+ " attachid INTEGER PRIMARY KEY,\n"
+ " isLatest BOOLEAN DEFAULT 0,\n"
+ " mtime TIMESTAMP,\n"
+ " src TEXT,\n"
+ " target TEXT,\n"
+ " filename TEXT,\n"
+ " comment TEXT,\n"
+ " user TEXT\n"
+ ");\n"
+ "CREATE INDEX repo_attachment_idx1\n"
+ " ON repo_attachment(target, filename, mtime);\n"
+ "CREATE INDEX repo_attachment_idx2 ON repo_attachment(src);\n"
+ "CREATE TABLE repo_cherrypick(\n"
+ " parentid INT,\n"
+ " childid INT,\n"
+ " isExclude BOOLEAN DEFAULT false,\n"
+ " PRIMARY KEY(parentid, childid)\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE INDEX repo_cherrypick_cid ON repo_cherrypick(childid);\n"
+ "CREATE TABLE repo_ticket(\n"
+ " -- Do not change any column that begins with tkt_\n"
+ " tkt_id INTEGER PRIMARY KEY,\n"
+ " tkt_uuid TEXT UNIQUE,\n"
+ " tkt_mtime DATE,\n"
+ " tkt_ctime DATE,\n"
+ " -- Add as many fields as required below this line\n"
+ " type TEXT,\n"
+ " status TEXT,\n"
+ " subsystem TEXT,\n"
+ " priority TEXT,\n"
+ " severity TEXT,\n"
+ " foundin TEXT,\n"
+ " private_contact TEXT,\n"
+ " resolution TEXT,\n"
+ " title TEXT,\n"
+ " comment TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_ticketchng(\n"
+ " -- Do not change any column that begins with tkt_\n"
+ " tkt_id INTEGER REFERENCES ticket,\n"
+ " tkt_rid INTEGER REFERENCES blob,\n"
+ " tkt_mtime DATE,\n"
+ " -- Add as many fields as required below this line\n"
+ " login TEXT,\n"
+ " username TEXT,\n"
+ " mimetype TEXT,\n"
+ " icomment TEXT\n"
+ ");\n"
+ "CREATE INDEX repo_ticketchng_idx1 ON repo_ticketchng(tkt_id, tkt_mtime);\n"
+ "CREATE TRIGGER repo_alert_trigger1\n"
+ "AFTER INSERT ON repo_event BEGIN\n"
+ " INSERT INTO repo_pending_alert(eventid)\n"
+ " SELECT printf('%.1c%d',new.type,new.objid) WHERE true\n"
+ " ON CONFLICT(eventId) DO NOTHING;\n"
+ "END;\n"
+ "CREATE TABLE repo_vcache(\n"
+ " vid INTEGER, -- check-in ID\n"
+ " fname TEXT, -- filename\n"
+ " rid INTEGER, -- artifact ID\n"
+ " PRIMARY KEY(vid,fname)\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE localdb_vvar(\n"
+ " name TEXT PRIMARY KEY NOT NULL,\n"
+ " value CLOB,\n"
+ " CHECK( typeof(name)='text' AND length(name)>=1 )\n"
+ ");\n"
+ "CREATE TABLE localdb_vfile(\n"
+ " id INTEGER PRIMARY KEY,\n"
+ " vid INTEGER REFERENCES blob,\n"
+ " chnged INT DEFAULT 0,\n"
+ " deleted BOOLEAN DEFAULT 0,\n"
+ " isexe BOOLEAN,\n"
+ " islink BOOLEAN,\n"
+ " rid INTEGER,\n"
+ " mrid INTEGER,\n"
+ " mtime INTEGER,\n"
+ " pathname TEXT,\n"
+ " origname TEXT, mhash,\n"
+ " UNIQUE(pathname,vid)\n"
+ ");\n"
+ "CREATE TABLE localdb_sqlite_stat1(tbl,idx,stat);\n"
+ "CREATE TABLE localdb_vcache(\n"
+ " vid INTEGER, -- check-in ID\n"
+ " fname TEXT, -- filename\n"
+ " rid INTEGER, -- artifact ID\n"
+ " PRIMARY KEY(vid,fname)\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE localdb_stash(\n"
+ " stashid INTEGER PRIMARY KEY,\n"
+ " vid INTEGER,\n"
+ " hash TEXT,\n"
+ " comment TEXT,\n"
+ " ctime TIMESTAMP\n"
+ ");\n"
+ "CREATE TABLE localdb_stashfile(\n"
+ " stashid INTEGER REFERENCES stash,\n"
+ " isAdded BOOLEAN,\n"
+ " isRemoved BOOLEAN,\n"
+ " isExec BOOLEAN,\n"
+ " isLink BOOLEAN,\n"
+ " rid INTEGER,\n"
+ " hash TEXT,\n"
+ " origname TEXT,\n"
+ " newname TEXT,\n"
+ " delta BLOB,\n"
+ " PRIMARY KEY(newname, stashid)\n"
+ ");\n"
+ "CREATE TABLE localdb_vmerge(\n"
+ " id INTEGER REFERENCES vfile,\n"
+ " merge INTEGER,\n"
+ " mhash TEXT\n"
+ ");\n"
+ "CREATE UNIQUE INDEX localdb_vmergex1 ON localdb_vmerge(id,mhash);\n"
+ "CREATE TRIGGER localdb_vmerge_ck1 AFTER INSERT ON localdb_vmerge\n"
+ "WHEN new.mhash IS NULL BEGIN\n"
+ " SELECT raise(FAIL,\n"
+ " 'trying to update a newer checkout with an older version of Fossil');\n"
+ "END;\n"
+ "CREATE TABLE configdb_global_config(\n"
+ " name TEXT PRIMARY KEY,\n"
+ " value TEXT\n"
+ ");\n"
+ "CREATE TABLE configdb_sqlite_stat1(tbl,idx,stat);\n"
+;
+
+#ifdef __linux__
+#include
+#include
+
+/*
+** Attempt to display I/O stats on Linux using /proc/PID/io
+*/
+static void displayLinuxIoStats(FILE *out){
+ FILE *in;
+ char z[200];
+ sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
+ in = fopen(z, "rb");
+ if( in==0 ) return;
+ while( fgets(z, sizeof(z), in)!=0 ){
+ static const struct {
+ const char *zPattern;
+ const char *zDesc;
+ } aTrans[] = {
+ { "rchar: ", "Bytes received by read():" },
+ { "wchar: ", "Bytes sent to write():" },
+ { "syscr: ", "Read() system calls:" },
+ { "syscw: ", "Write() system calls:" },
+ { "read_bytes: ", "Bytes rcvd from storage:" },
+ { "write_bytes: ", "Bytes sent to storage:" },
+ { "cancelled_write_bytes: ", "Cancelled write bytes:" },
+ };
+ int i;
+ for(i=0; i='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 int 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; i0x7fffffff ){
+ printf("ERROR: parameter too large - max 2147483648\n");
+ exit(1);
+ }
+ return (int)(isNeg? -v : v);
+}
+
+
+int main(int argc, char **argv){
+ const char *zCmd = 0;
+ int i;
+ int bAutovac = 0;
+ int showStats = 0;
+ const char *zDbName = "./startup.db";
+ int nHeap = 0;
+ int mnHeap = 0;
+
+ for(i=1; i=argc-2 ){
+ printf("ERROR: missing arguments on %s\n", argv[i]);
+ exit(1);
+ }
+ nHeap = integerValue(argv[i+1]);
+ mnHeap = integerValue(argv[i+2]);
+ i += 2;
+ }else
+ if( strcmp(z,"-stats")==0 ){
+ showStats = 1;
+ }else
+ {
+ printf("ERROR: unknown option \"%s\"\n", argv[i]);
+ usage(argv[0]);
+ }
+ }
+ if( zCmd==0 ){
+ printf("ERROR: no COMMAND specified\n");
+ usage(argv[0]);
+ }
+ if( strcmp(zCmd, "run")==0 ){
+ sqlite3 *db;
+ int rc;
+ char *zErr = 0;
+ void *pHeap = 0;
+ if( nHeap>0 ){
+ pHeap = malloc( nHeap );
+ if( pHeap==0 ){
+ printf("ERROR: cannot allocate %d-byte heap\n", nHeap);
+ exit(1);
+ }
+ rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, mnHeap);
+ if( rc ){
+ printf("ERROR: heap configuration failed: %d\n", rc);
+ exit(1);
+ }
+ }
+ rc = sqlite3_open(zDbName, &db);
+ if( rc ){
+ printf("SQLite error: %s\n", sqlite3_errmsg(db));
+ }else{
+ sqlite3_exec(db, "PRAGMA synchronous", 0, 0, &zErr);
+ }
+ if( zErr ){
+ printf("ERROR: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
+ if( showStats ){
+ int iCur, iHi;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHi, 0);
+ printf("-- Lookaside Slots Used: %d (max %d)\n", iCur,iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHi, 0);
+ printf("-- Successful lookasides: %d\n", iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur,&iHi,0);
+ printf("-- Lookaside size faults: %d\n", iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur,&iHi,0);
+ printf("-- Lookaside OOM faults: %d\n", iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHi, 0);
+ printf("-- Pager Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHi, 1);
+ printf("-- Page cache hits: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHi, 1);
+ printf("-- Page cache misses: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHi, 1);
+ printf("-- Page cache writes: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHi, 0);
+ printf("-- Schema Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHi, 0);
+ printf("-- Statement Heap Usage: %d bytes\n", iCur);
+ }
+ sqlite3_close(db);
+ free(pHeap);
+ /* Global memory usage statistics printed after the database connection
+ ** has closed. Memory usage should be zero at this point. */
+ if( showStats ){
+ int iCur, iHi;
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHi, 0);
+ printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0);
+ printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0);
+ printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Allocation: %d bytes\n",iHi);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Pcache Allocation: %d bytes\n",iHi);
+#ifdef __linux__
+ displayLinuxIoStats(stdout);
+#endif
+ }
+ return 0;
+ }
+ if( strcmp(zCmd, "init")==0 ){
+ sqlite3 *db;
+ char *zAux;
+ char *zErr = 0;
+ int rc;
+ unlink(zDbName);
+ zAux = sqlite3_mprintf("%s-journal", zDbName);
+ unlink(zAux);
+ sqlite3_free(zAux);
+ zAux = sqlite3_mprintf("%s-wal", zDbName);
+ unlink(zAux);
+ sqlite3_free(zAux);
+ rc = sqlite3_open(zDbName, &db);
+ if( rc ){
+ printf("SQLite error: %s\n", sqlite3_errmsg(db));
+ }else{
+ sqlite3_exec(db, "BEGIN", 0, 0, 0);
+ sqlite3_exec(db, zTestSchema, 0, 0, &zErr);
+ sqlite3_exec(db, "COMMIT", 0, 0, 0);
+ }
+ if( zErr ){
+ printf("ERROR: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
+ sqlite3_close(db);
+ return 0;
+
+ }
+}
diff --git a/test/tester.tcl b/test/tester.tcl
index cb9f5c9e58..c963394598 100644
--- a/test/tester.tcl
+++ b/test/tester.tcl
@@ -174,8 +174,14 @@ proc get_pwd {} {
# case of the result to what Tcl considers canonical, which would
# defeat the purpose of this procedure.
#
+ if {[info exists ::env(ComSpec)]} {
+ set comSpec $::env(ComSpec)
+ } else {
+ # NOTE: Hard-code the typical default value.
+ set comSpec {C:\Windows\system32\cmd.exe}
+ }
return [string map [list \\ /] \
- [string trim [exec -- $::env(ComSpec) /c echo %CD%]]]
+ [string trim [exec -- $comSpec /c echo %CD%]]]
} else {
return [pwd]
}
@@ -903,8 +909,8 @@ proc catchcmdex {db {cmd ""}} {
proc filepath_normalize {p} {
# test cases should be written to assume "unix"-like file paths
if {$::tcl_platform(platform)!="unix"} {
- # lreverse*2 as a hack to remove any unneeded {} after the string map
- lreverse [lreverse [string map {\\ /} [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]]]
+ string map [list \\ / \{/ / .db\} .db] \
+ [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]
} {
set p
}
@@ -1690,9 +1696,12 @@ proc crashsql {args} {
set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]]
set f [open crash.tcl w]
+ puts $f "sqlite3_initialize ; sqlite3_shutdown"
+ puts $f "catch { install_malloc_faultsim 1 }"
puts $f "sqlite3_crash_enable 1 $dfltvfs"
puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
+ puts $f "autoinstall_test_functions"
# This block sets the cache size of the main database to 10
# pages. This is done in case the build is configured to omit
@@ -1720,7 +1729,7 @@ proc crashsql {args} {
}
close $f
set r [catch {
- exec [info nameofexec] crash.tcl >@stdout
+ exec [info nameofexec] crash.tcl >@stdout 2>@stdout
} msg]
# Windows/ActiveState TCL returns a slightly different
diff --git a/test/trace3.test b/test/trace3.test
index 538dcd0942..e9935acfb8 100644
--- a/test/trace3.test
+++ b/test/trace3.test
@@ -251,4 +251,83 @@ do_test trace3-11.2 {
set ::stmtlist(record)
} {/^-?\d+$/}
+#-------------------------------------------------------------------------
+reset_db
+do_test 12.1.0 {
+ set ::STMT [sqlite3_prepare_v2 $DB \
+ "SELECT ?1 || ?1 || ?1 || ?2 || ?3 || ?4 || ? || ?1 || ?" -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $::STMT
+} {6}
+
+do_test 12.1.1 {
+ sqlite3_bind_text $STMT 1 "A" 1
+ sqlite3_bind_text $STMT 2 "B" 1
+ sqlite3_bind_text $STMT 3 "C" 1
+ sqlite3_bind_text $STMT 4 "D" 1
+ sqlite3_bind_text $STMT 5 "E" 1
+ sqlite3_bind_text $STMT 6 "F" 1
+ sqlite3_expanded_sql $STMT
+} {SELECT 'A' || 'A' || 'A' || 'B' || 'C' || 'D' || 'E' || 'A' || 'F'}
+
+do_test 12.1.2 {
+ sqlite3_step $STMT
+ sqlite3_column_text $STMT 0
+} {AAABCDEAF}
+
+do_test 12.1.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+do_test 12.2.0 {
+ execsql {
+ CREATE TABLE nameFtsFuzzySearchTable(
+ word, distance, langid, score, top, scope
+ );
+ }
+ set ::STMT [sqlite3_prepare_v2 $DB {
+ SELECT
+ substr(word,1,length(?1)-1) AS term,
+ distance,
+ langid,
+ score
+ FROM
+ nameFtsFuzzySearchTable
+ WHERE
+ word MATCH (?1) AND abs(?1) = abs(term)
+ AND top = ?2 AND distance > ?3 AND scope = ?4 AND langid = ?
+ GROUP BY term, langid
+ HAVING (1.0 - ((distance / 100.0) / CAST( length(?1) - 1 AS REAL ))) >= ?
+ } -1 TAIL]
+ sqlite3_bind_parameter_count $::STMT
+} {6}
+
+do_test 12.1.1 {
+ sqlite3_bind_text $STMT 1 "A" 1
+ sqlite3_bind_text $STMT 2 "B" 1
+ sqlite3_bind_text $STMT 3 "C" 1
+ sqlite3_bind_text $STMT 4 "D" 1
+ sqlite3_bind_text $STMT 5 "E" 1
+ sqlite3_bind_text $STMT 6 "F" 1
+ sqlite3_expanded_sql $STMT
+} {
+ SELECT
+ substr(word,1,length('A')-1) AS term,
+ distance,
+ langid,
+ score
+ FROM
+ nameFtsFuzzySearchTable
+ WHERE
+ word MATCH ('A') AND abs('A') = abs(term)
+ AND top = 'B' AND distance > 'C' AND scope = 'D' AND langid = 'E'
+ GROUP BY term, langid
+ HAVING (1.0 - ((distance / 100.0) / CAST( length('A') - 1 AS REAL ))) >= 'F'
+ }
+
+do_test 12.1.2 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+
finish_test
diff --git a/test/triggerE.test b/test/triggerE.test
index 9db7035325..de4b068582 100644
--- a/test/triggerE.test
+++ b/test/triggerE.test
@@ -58,6 +58,8 @@ foreach {tn defn} {
8 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = ?; END; }
9 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = 1 WHERE d = ?; END; }
10 { AFTER INSERT ON t1 BEGIN SELECT * FROM pragma_stats(?); END; }
+ 11 { BEFORE INSERT ON t1 BEGIN
+ INSERT INTO t1 SELECT max(b) OVER(ORDER BY $1) FROM t1; END }
} {
catchsql {drop trigger tr1}
do_catchsql_test 1.1.$tn "CREATE TRIGGER tr1 $defn" [list 1 $errmsg]
diff --git a/test/unionall.test b/test/unionall.test
new file mode 100644
index 0000000000..2f8027ccb7
--- /dev/null
+++ b/test/unionall.test
@@ -0,0 +1,358 @@
+# 2020-12-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is flattening UNION ALL sub-queries.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix unionall
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
+ CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
+ CREATE TABLE t1_c(e INTEGER PRIMARY KEY, f TEXT);
+
+ INSERT INTO t1_a VALUES(1, 'one'), (4, 'four');
+ INSERT INTO t1_b VALUES(2, 'two'), (5, 'five');
+ INSERT INTO t1_c VALUES(3, 'three'), (6, 'six');
+
+ CREATE VIEW t1 AS
+ SELECT a, b FROM t1_a UNION ALL
+ SELECT c, d FROM t1_b UNION ALL
+ SELECT e, f FROM t1_c;
+
+ CREATE TABLE i1(x);
+ INSERT INTO i1 VALUES(2), (5), (6), (1);
+}
+
+do_execsql_test 1.1 {
+ SELECT a, b FROM (
+ SELECT a, b FROM t1_a UNION ALL
+ SELECT c, d FROM t1_b UNION ALL
+ SELECT e, f FROM t1_c
+ ) ORDER BY a
+} {
+ 1 one 2 two 3 three 4 four 5 five 6 six
+}
+
+do_execsql_test 1.2 {
+ SELECT a, b FROM t1 ORDER BY a
+} {
+ 1 one 2 two 3 three 4 four 5 five 6 six
+}
+
+do_execsql_test 1.3 {
+ SELECT a, b FROM i1, t1 WHERE a=x ORDER BY a
+} {1 one 2 two 5 five 6 six}
+
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 2.1.0 {
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(1, 'ONE');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(2, 'TWO');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(3, 'THREE');
+}
+
+do_execsql_test 2.1.1 {
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<3
+ )
+ SELECT * FROM (
+ SELECT 0 AS i UNION ALL SELECT i FROM s UNION ALL SELECT 0
+ ), t1 WHERE x=i;
+} {
+ 1 1 one 1 1 ONE 2 2 two 2 2 TWO 3 3 three 3 3 THREE
+}
+
+do_catchsql_test 2.1.2 {
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<3 UNION ALL SELECT 4
+ )
+ SELECT * FROM s, t1 WHERE x=i;
+} {1 {circular reference: s}}
+
+do_execsql_test 2.2.0 {
+ CREATE TABLE t2_a(k INTEGER PRIMARY KEY, v TEXT);
+ CREATE TABLE t2_b(k INTEGER PRIMARY KEY, v TEXT);
+
+ CREATE VIEW t2 AS
+ SELECT * FROM t2_a
+ UNION ALL
+ SELECT * FROM t2_b;
+
+ CREATE TRIGGER t2_insert INSTEAD OF INSERT ON t2 BEGIN
+ INSERT INTO t2_a SELECT new.k, new.v WHERE (new.k%2)==0;
+ INSERT INTO t2_b SELECT new.k, new.v WHERE (new.k%2)==1;
+ END;
+
+ INSERT INTO t2 VALUES(5, 'v'), (4, 'iv'), (3, 'iii'), (2, 'ii');
+}
+
+do_execsql_test 2.2.1 {
+ SELECT * FROM t1, t2 WHERE x=k;
+} {
+ 2 two 2 ii 2 TWO 2 ii 3 three 3 iii 3 THREE 3 iii
+}
+
+do_execsql_test 2.2.2 {
+ SELECT * FROM t1 LEFT JOIN t2 ON (x=k);
+} {
+ 1 one {} {}
+ 1 ONE {} {}
+ 2 two 2 ii 2 TWO 2 ii 3 three 3 iii 3 THREE 3 iii
+}
+
+do_execsql_test 2.2.3 {
+ SELECT x1.*, x2.* FROM t2 AS x1, t2 AS x2 WHERE x1.k=x2.k+1
+} {
+ 4 iv 3 iii
+ 3 iii 2 ii
+ 5 v 4 iv
+}
+
+do_execsql_test 2.2.4 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY y;
+} {
+ 3 THREE 3 iii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 2 two 2 ii
+}
+do_execsql_test 2.2.5 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY y||'';
+} {
+ 3 THREE 3 iii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 2 two 2 ii
+}
+do_execsql_test 2.2.6 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY v
+} {
+ 2 two 2 ii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 3 THREE 3 iii
+}
+do_execsql_test 2.2.7 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY v||''
+} {
+ 2 two 2 ii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 3 THREE 3 iii
+}
+do_execsql_test 2.2.8 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY k,v||''
+} {
+ 2 two 2 ii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 3 THREE 3 iii
+}
+do_execsql_test 2.2.9a {
+ SELECT * FROM t1, t2 ORDER BY +k
+} {
+ 1 one 2 ii 1 ONE 2 ii 2 two 2 ii
+ 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii
+
+ 1 one 3 iii 1 ONE 3 iii 2 two 3 iii
+ 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii
+
+ 1 one 4 iv 1 ONE 4 iv 2 two 4 iv
+ 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv
+
+ 1 one 5 v 1 ONE 5 v 2 two 5 v
+ 2 TWO 5 v 3 three 5 v 3 THREE 5 v
+}
+
+do_execsql_test 2.2.9b {
+ SELECT * FROM t1, t2 ORDER BY k
+} {
+ 1 one 2 ii 1 ONE 2 ii 2 two 2 ii
+ 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii
+
+ 1 one 3 iii 1 ONE 3 iii 2 two 3 iii
+ 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii
+
+ 1 one 4 iv 1 ONE 4 iv 2 two 4 iv
+ 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv
+
+ 1 one 5 v 1 ONE 5 v 2 two 5 v
+ 2 TWO 5 v 3 three 5 v 3 THREE 5 v
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 3.0 {
+ CREATE TABLE t1(c INTEGER PRIMARY KEY, d TEXT);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE t3_a(k INTEGER PRIMARY KEY, v TEXT);
+ INSERT INTO t3_a VALUES(2,'ii');
+ CREATE TABLE t3_b(k INTEGER PRIMARY KEY, v TEXT);
+ CREATE VIEW t3 AS
+ SELECT * FROM t3_a
+ UNION ALL
+ SELECT * FROM t3_b;
+} {}
+
+do_execsql_test 3.1 {
+ SELECT * FROM t1, t3 ORDER BY k;
+} {1 2 2 ii}
+
+reset_db
+do_execsql_test 4.0 {
+
+ CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t1_a VALUES(123, 't1_a');
+ CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
+
+ CREATE VIEW t1 AS
+ SELECT a, b FROM t1_a
+ UNION ALL
+ SELECT c, d FROM t1_b;
+
+ CREATE TABLE t3_a(k INTEGER PRIMARY KEY, v TEXT);
+ INSERT INTO t3_a VALUES(456, 't3_a');
+ CREATE TABLE t3_b(k INTEGER PRIMARY KEY, v TEXT);
+
+ CREATE VIEW t3 AS
+ SELECT * FROM t3_a
+ UNION ALL
+ SELECT * FROM t3_b;
+}
+
+do_execsql_test 4.1 {
+ SELECT * FROM t1, t3 ORDER BY k;
+} {123 t1_a 456 t3_a}
+
+do_execsql_test 4.2 {
+ SELECT * FROM (SELECT * FROM t1, t3) ORDER BY k;
+} {123 t1_a 456 t3_a}
+
+do_execsql_test 4.3 {
+ SELECT * FROM (SELECT * FROM t1, t3), (
+ SELECT max(a) OVER () FROM t1
+ UNION ALL
+ SELECT min(a) OVER () FROM t1
+ )
+ ORDER BY k;
+} {
+ 123 t1_a 456 t3_a 123
+ 123 t1_a 456 t3_a 123
+}
+
+do_execsql_test 4.3 {
+ SELECT * FROM (SELECT * FROM t1, t3), (
+ SELECT group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a)
+ FROM t1
+ )
+ ORDER BY k;
+} {
+ 123 t1_a 456 t3_a 123 123 123 123 123 123 123 123 123
+}
+
+do_execsql_test 4.3 {
+ SELECT * FROM (SELECT * FROM t1, t3) AS o, (
+ SELECT * FROM t1 LEFT JOIN t3 ON a=k
+ );
+} {
+ 123 t1_a 456 t3_a 123 t1_a {} {}
+}
+
+# 2020-12-30: dbsqlfuzz find
+reset_db
+do_execsql_test 5.1 {
+ CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t1_a VALUES(1,'one');
+ INSERT INTO t1_a VALUES(0,NULL);
+ CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
+ INSERT INTO t1_b VALUES(2,'two');
+ INSERT INTO t1_b VALUES(5,'five');
+ CREATE TABLE t1_c(e INTEGER PRIMARY KEY, f TEXT);
+ INSERT INTO t1_c VALUES(3,'three');
+ INSERT INTO t1_c VALUES(6,'six');
+ CREATE TABLE t2(k,v);
+ INSERT INTO t2 VALUES(5,'v');
+ INSERT INTO t2 VALUES(4,'iv');
+ INSERT INTO t2 VALUES(3,'iii');
+ INSERT INTO t2 VALUES(2,'ii');
+ CREATE TABLE t3_a(k INTEGER PRIMARY KEY, v TEXT);
+ INSERT INTO t3_a VALUES(2,'ii');
+ INSERT INTO t3_a VALUES(4,'iv');
+ CREATE TABLE t3_b(k INTEG5R PRIMARY KEY, v TEXT);
+ INSERT INTO t3_b VALUES(NULL,'iii');
+ INSERT INTO t3_b VALUES(NULL,'v');
+ CREATE VIEW t1 AS
+ SELECT a, b FROM t1_a UNION ALL
+ SELECT c, d FROM t1_b UNION ALL
+ SELECT e, f FROM t1_c;
+ CREATE VIEW t3 AS
+ SELECT * FROM t3_a
+ UNION ALL
+ SELECT * FROM t3_b;
+ CREATE TRIGGER t3_insert INSTEAD OF INSERT ON t3 BEGIN
+ INSERT INTO t3_a SELECT new.k, new.v WHERE (new.k%2)==0;
+ INSERT INTO t3_b SELECT new.k, new.v WHERE (new.k%2)==1;
+ END;
+} {}
+do_execsql_test 5.10 {
+ SELECT *, '+' FROM t1 LEFT JOIN t2 ON (a NOT IN(SELECT v FROM t1, t3 WHERE a=k)=NOT EXISTS(SELECT 1 FROM t1 LEFT JOIN t3 ON (a=k)));
+} {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +}
+do_execsql_test 5.20 {
+ SELECT *, '+' FROM t1 LEFT JOIN t3 ON (a NOT IN(SELECT v FROM t1 LEFT JOIN t2 ON (a=k))=k);
+} {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +}
+
+reset_db
+do_execsql_test 6.0 {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES(3,4);
+
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 VALUES(5,6);
+ CREATE TABLE t4(a,b);
+ INSERT INTO t4 VALUES(7,8);
+
+ CREATE TABLE t5(a,b);
+ INSERT INTO t5 VALUES(9,10);
+}
+
+do_execsql_test 6.1 {
+ WITH x(c) AS (
+ SELECT 1000 FROM t1 UNION ALL SELECT 800 FROM t2
+ ),
+ y(d) AS (
+ SELECT 100 FROM t3 UNION ALL SELECT 400 FROM t4
+ )
+ SELECT * FROM t5, x, y;
+} {
+ 9 10 1000 100 9 10 1000 400
+ 9 10 800 100 9 10 800 400
+}
+
+finish_test
diff --git a/test/unionall2.test b/test/unionall2.test
new file mode 100644
index 0000000000..265804291b
--- /dev/null
+++ b/test/unionall2.test
@@ -0,0 +1,39 @@
+# 2020-12-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is flattening UNION ALL sub-queries.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix unionall2
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+
+ CREATE VIEW v1 AS SELECT * FROM t1, t2;
+ CREATE VIEW v2 AS SELECT * FROM t1, t2;
+
+ CREATE VIEW vA AS
+ SELECT * FROM v1, (
+ SELECT * FROM t1 LEFT JOIN t2 ON (a=c)
+ )
+ UNION ALL
+ SELECT * FROM v1, v2
+}
+
+do_execsql_test 1.1 {
+ SELECT 1 FROM vA, vA, vA, vA, vA, vA, vA, vA, vA, vA
+}
+
+
+finish_test
diff --git a/test/unionallfault.test b/test/unionallfault.test
new file mode 100644
index 0000000000..c78abe548c
--- /dev/null
+++ b/test/unionallfault.test
@@ -0,0 +1,36 @@
+# 2020-12-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix unionallfault
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x,y,z);
+ CREATE TABLE t3(x,y,z);
+}
+faultsim_save_and_close
+
+
+do_faultsim_test 1 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ SELECT * FROM t1, (
+ SELECT x FROM t1 UNION ALL SELECT y FROM t1
+ ), t3
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+finish_test
diff --git a/test/upsert5.test b/test/upsert5.test
new file mode 100644
index 0000000000..c4747ddf18
--- /dev/null
+++ b/test/upsert5.test
@@ -0,0 +1,397 @@
+# 2020-12-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.
+#
+#***********************************************************************
+#
+# Test cases for generalized UPSERT
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix upsert5
+
+foreach {tn sql} {
+ 1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE, d UNIQUE, e UNIQUE) }
+ 2 { CREATE TABLE t1(a INT PRIMARY KEY, b, c UNIQUE, d UNIQUE, e UNIQUE) }
+ 3 { CREATE TABLE t1(a INT PRIMARY KEY, b, c UNIQUE, d UNIQUE, e UNIQUE) WITHOUT ROWID}
+ 4 { CREATE TABLE t1(e UNIQUE, d UNIQUE, c UNIQUE, a INTEGER PRIMARY KEY, b) }
+ 5 { CREATE TABLE t1(e UNIQUE, d UNIQUE, c UNIQUE, a INT PRIMARY KEY, b) }
+ 6 { CREATE TABLE t1(e UNIQUE, d UNIQUE, c UNIQUE, a INT PRIMARY KEY, b) WITHOUT ROWID}
+} {
+ reset_db
+ execsql $sql
+
+ do_execsql_test 1.$tn.100 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,3,4,5)
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+ do_execsql_test 1.$tn.101 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,4,5)
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.102 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,4,5)
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 d 3 4 5}
+ do_execsql_test 1.$tn.103 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 e 3 4 5}
+ do_execsql_test 1.$tn.200 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+ do_execsql_test 1.$tn.201 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,3,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.202 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,3,4,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.203 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+ do_execsql_test 1.$tn.204 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,4,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+ do_execsql_test 1.$tn.210 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+ do_execsql_test 1.$tn.211 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,4,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 d 3 4 5}
+ do_execsql_test 1.$tn.212 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+ do_execsql_test 1.$tn.213 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(a) DO UPDATE SET b='a'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 e 3 4 5}
+ do_execsql_test 1.$tn.214 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e'
+ ON CONFLICT(a) DO UPDATE SET b='a';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 e 3 4 5}
+ do_execsql_test 1.$tn.215 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e'
+ ON CONFLICT(a) DO UPDATE SET b='a';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 e 3 4 5}
+ do_execsql_test 1.$tn.216 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(e) DO UPDATE SET b='e'
+ ON CONFLICT(a) DO UPDATE SET b='a';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a 3 4 5}
+
+ do_execsql_test 1.$tn.300 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(a) DO UPDATE SET b='a1'
+ ON CONFLICT(a) DO UPDATE SET b='a2'
+ ON CONFLICT(a) DO UPDATE SET b='a3'
+ ON CONFLICT(a) DO UPDATE SET b='a4'
+ ON CONFLICT(a) DO UPDATE SET b='a5'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 a1 3 4 5}
+ do_execsql_test 1.$tn.301 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT(a) DO UPDATE SET b='a1'
+ ON CONFLICT(a) DO UPDATE SET b='a2'
+ ON CONFLICT(a) DO UPDATE SET b='a3'
+ ON CONFLICT(a) DO UPDATE SET b='a4'
+ ON CONFLICT(a) DO UPDATE SET b='a5'
+ ON CONFLICT(e) DO UPDATE SET b='e';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 e 3 4 5}
+
+ do_execsql_test 1.$tn.400 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.401 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.402 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.403 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.404 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,4,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.405 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,4,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 d 3 4 5}
+
+ do_execsql_test 1.$tn.410 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.411 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.412 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,4,95)
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.413 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,94,95)
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+
+ do_execsql_test 1.$tn.420 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO NOTHING
+ ON CONFLICT(d) DO NOTHING
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.421 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(c) DO NOTHING
+ ON CONFLICT(d) DO NOTHING
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 x 3 4 5}
+ do_execsql_test 1.$tn.422 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,4,95)
+ ON CONFLICT(c) DO NOTHING
+ ON CONFLICT(d) DO NOTHING
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 2 3 4 5}
+ do_execsql_test 1.$tn.423 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,94,95)
+ ON CONFLICT(c) DO NOTHING
+ ON CONFLICT(d) DO NOTHING
+ ON CONFLICT DO UPDATE set b='x';
+ SELECT a,b,c,d,e FROM t1;
+ } {1 2 3 4 5}
+
+ do_execsql_test 1.$tn.500 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO NOTHING;
+ SELECT a,b,c,d,e FROM t1;
+ } {1 2 3 4 5}
+ do_execsql_test 1.$tn.501 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,93,94,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO NOTHING;
+ SELECT a,b,c,d,e FROM t1;
+ } {1 2 3 4 5}
+ do_execsql_test 1.$tn.502 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO NOTHING;
+ SELECT a,b,c,d,e FROM t1;
+ } {1 2 3 4 5}
+ do_execsql_test 1.$tn.503 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,94,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO NOTHING;
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.504 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(91,NULL,3,4,95)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO NOTHING;
+ SELECT a,b,c,d,e FROM t1;
+ } {1 c 3 4 5}
+ do_execsql_test 1.$tn.505 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,NULL,93,4,5)
+ ON CONFLICT(c) DO UPDATE SET b='c'
+ ON CONFLICT(d) DO UPDATE SET b='d'
+ ON CONFLICT DO NOTHING;
+ SELECT a,b,c,d,e FROM t1;
+ } {1 d 3 4 5}
+
+}
+
+finish_test
diff --git a/test/vacuum2.test b/test/vacuum2.test
index 0350c8ec42..89fdb506c5 100644
--- a/test/vacuum2.test
+++ b/test/vacuum2.test
@@ -56,7 +56,7 @@ do_test vacuum2-2.1 {
}
hexio_get_int [hexio_read test.db 24 4]
} [expr {[hexio_get_int [hexio_read test.db 24 4]]+3}]
-do_test vacuum2-2.1 {
+do_test vacuum2-2.2 {
execsql {
VACUUM
}
diff --git a/test/vacuum6.test b/test/vacuum6.test
new file mode 100644
index 0000000000..e9a1542f25
--- /dev/null
+++ b/test/vacuum6.test
@@ -0,0 +1,112 @@
+# 2016-08-19
+#
+# 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 test for VACUUM on attached databases.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix vacuum6
+
+# If the VACUUM statement is disabled in the current build, skip all
+# the tests in this file.
+#
+ifcapable !vacuum {
+ finish_test
+ return
+}
+
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t1 VALUES(1, 1);
+} {}
+
+do_execsql_test 1.1 {
+ VACUUM
+}
+
+reset_db
+do_execsql_test 1.2 {
+ CREATE TABLE t1(x,b);
+ CREATE INDEX x1 ON t1(x);
+ CREATE INDEX x2 ON t1(x);
+ CREATE INDEX x3 ON t1(x);
+ INSERT INTO t1 SELECT 2,'';
+ VACUUM;
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+foreach {tn sz} {1 400 2 4000 3 9999} {
+ reset_db
+ do_execsql_test 2.$tn.1 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
+ )
+ INSERT INTO t1 SELECT i, randomblob($sz) FROM s;
+ }
+
+ do_execsql_test 2.$tn.2 {
+ vacuum;
+ }
+
+ do_execsql_test 2.$tn.3 {
+ PRAGMA integrity_check;
+ } {ok}
+}
+
+reset_db
+do_execsql_test 3.0 {
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t1 VALUES(2, randomblob(1200));
+} {}
+do_execsql_test 3.1 {
+ PRAGMA page_size = 512;
+ VACUUM;
+}
+do_execsql_test 3.2 {
+ PRAGMA integrity_check
+} {ok}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 4.0 {
+ CREATE TABLE tx(a, b);
+ CREATE INDEX i1 ON tx(b);
+ WITH s(i) AS (
+ SELECT 8000 UNION ALL SELECT i+1 FROM s WHERE i<10000
+ )
+ INSERT INTO tx SELECT i, randomblob(i) FROM s;
+
+ SELECT sum(length(b)) FROM tx;
+} {18009000}
+foreach {tn pgsz av} {
+ 1 2048 0
+ 2 1024 1
+ 3 65536 0
+ 4 8192 1
+ 5 512 0
+ 6 4096 1
+} {
+ do_execsql_test 4.1.$tn.1 "
+ PRAGMA page_size = $pgsz;
+ PRAGMA auto_vacuum = $av;
+ "
+ do_execsql_test 4.1.$tn.2 VACUUM
+ integrity_check 4.1.$tn.3
+}
+
+finish_test
diff --git a/test/walvfs.test b/test/walvfs.test
index f21b65e7e9..d6c58656f0 100644
--- a/test/walvfs.test
+++ b/test/walvfs.test
@@ -146,16 +146,23 @@ proc xWrite {method file args} {
incr ::cnt -1
if {$::cnt==0} {
sqlite3_memdebug_fail 1 -repeat 0
- catchsql { SELECT 'a big long string!' }
+ # For this test to pass, the following statement must call malloc() at
+ # least once. Even if the lookaside is enabled.
+ set ::xwrite_stmt_res [catchsql { SELECT hex(randomblob(4000)) }]
sqlite3_interrupt db
}
}
return SQLITE_OK
}
+set ::xwrite_stmt_res ""
do_catchsql_test 3.2 {
PRAGMA wal_checkpoint
} {1 {out of memory}}
+do_test 3.2.2 {
+ set ::xwrite_stmt_res
+} {1 {out of memory}}
+unset ::xwrite_stmt_res
#-------------------------------------------------------------------------
#
diff --git a/test/whereL.test b/test/whereL.test
index fbb424e919..c78ae160b3 100644
--- a/test/whereL.test
+++ b/test/whereL.test
@@ -26,14 +26,13 @@ do_eqp_test 110 {
SELECT * FROM t1, v4 WHERE t1.a=?1 AND v4.a=t1.a;
} {
QUERY PLAN
- |--MATERIALIZE xxxxxx
- | `--COMPOUND QUERY
- | |--LEFT-MOST SUBQUERY
- | | `--SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
- | `--UNION ALL
- | `--SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (a=?)
- |--SCAN SUBQUERY xxxxxx
- `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | |--SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
+ | `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
+ `--UNION ALL
+ |--SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (a=?)
+ `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
}
# The scan of the t1 table goes first since that enables the ORDER BY
diff --git a/test/wherelimit.test b/test/wherelimit.test
index 8db7a0cc28..aeadc6f39b 100644
--- a/test/wherelimit.test
+++ b/test/wherelimit.test
@@ -94,21 +94,31 @@ ifcapable {update_delete_limit} {
execsql {DELETE FROM t1 ORDER BY x LIMIT 5}
execsql {SELECT count(*) FROM t1}
} {15}
+ create_test_data 4
+ do_test wherelimit-1.3b {
+ # limit 5
+ execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x, y LIMIT 5}
+ } {1 1 | 1 2 | 1 3 | 1 4 | 2 1 |}
+ do_test wherelimit-1.3c {
+ execsql {SELECT count(*) FROM t1}
+ } {11}
do_test wherelimit-1.4 {
# limit 5, offset 2
- execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET 2}
+ execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x LIMIT 5 OFFSET 2}
+ } {2 4 | 3 1 | 3 2 | 3 3 | 3 4 |}
+ do_test wherelimit-1.4cnt {
execsql {SELECT count(*) FROM t1}
- } {10}
+ } {6}
do_test wherelimit-1.5 {
# limit 5, offset -2
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET -2}
execsql {SELECT count(*) FROM t1}
- } {5}
+ } {1}
do_test wherelimit-1.6 {
# limit -5 (no limit), offset 2
execsql {DELETE FROM t1 ORDER BY x LIMIT 2, -5}
execsql {SELECT count(*) FROM t1}
- } {2}
+ } {1}
do_test wherelimit-1.7 {
# limit 5, offset -2 (no offset)
execsql {DELETE FROM t1 ORDER BY x LIMIT -2, 5}
@@ -227,7 +237,9 @@ ifcapable {update_delete_limit} {
} {11}
create_test_data 6
do_test wherelimit-3.2 {
- execsql {UPDATE t1 SET y=1 WHERE x=1 LIMIT 5}
+ execsql {UPDATE t1 SET y=1 WHERE x=1 RETURNING x, old.y, '|' LIMIT 5}
+ } {1 1 | 1 2 | 1 3 | 1 4 | 1 5 |}
+ do_test wherelimit-3.2cnt {
execsql {SELECT count(*) FROM t1 WHERE y=1}
} {10}
do_test wherelimit-3.3 {
diff --git a/test/windowfault.test b/test/windowfault.test
index aea2340215..27c7742b30 100644
--- a/test/windowfault.test
+++ b/test/windowfault.test
@@ -246,6 +246,7 @@ do_faultsim_test 10 -faults oom* -prep {
faultsim_test_result {0 {}}
}
+#-------------------------------------------------------------------------
reset_db
do_execsql_test 11.0 {
DROP TABLE IF EXISTS t0;
@@ -253,7 +254,7 @@ do_execsql_test 11.0 {
INSERT INTO t0 VALUES(0);
} {}
-do_faultsim_test 11 -faults oom* -prep {
+do_faultsim_test 11.1 -faults oom* -prep {
} -body {
execsql {
SELECT * FROM t0 WHERE
@@ -263,7 +264,7 @@ do_faultsim_test 11 -faults oom* -prep {
faultsim_test_result {0 {}}
}
-do_faultsim_test 11 -faults oom* -prep {
+do_faultsim_test 11.2 -faults oom* -prep {
} -body {
execsql {
VALUES(false),(current_date collate binary)
@@ -274,4 +275,21 @@ do_faultsim_test 11 -faults oom* -prep {
faultsim_test_result {0 {}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 12.0 {
+ CREATE TABLE t1(a, b, c);
+} {}
+do_faultsim_test 12 -faults oom* -prep {
+} -body {
+ execsql {
+ WITH v(a, b, row_number) AS (
+ SELECT a, b, row_number() OVER (PARTITION BY a ORDER BY b) FROM t1
+ )
+ SELECT * FROM v WHERE a=2
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
finish_test
diff --git a/test/windowpushd.test b/test/windowpushd.test
new file mode 100644
index 0000000000..4462a0b4b4
--- /dev/null
+++ b/test/windowpushd.test
@@ -0,0 +1,237 @@
+# 2021 February 23
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing the push-down optimization when
+# WHERE constraints are pushed down into a sub-query that uses
+# window functions.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix windowpushd
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(id INTEGER PRIMARY KEY, grp_id);
+ CREATE INDEX i1 ON t1(grp_id);
+ CREATE VIEW lll AS SELECT
+ row_number() OVER (PARTITION BY grp_id),
+ grp_id, id
+ FROM t1
+}
+
+do_execsql_test 1.1 {
+ INSERT INTO t1 VALUES
+ (1, 2), (2, 3), (3, 3), (4, 1), (5, 1),
+ (6, 1), (7, 1), (8, 1), (9, 3), (10, 3),
+ (11, 2), (12, 3), (13, 3), (14, 2), (15, 1),
+ (16, 2), (17, 1), (18, 2), (19, 3), (20, 2)
+}
+
+do_execsql_test 1.2 {
+ SELECT * FROM lll
+} {
+ 1 1 4 2 1 5 3 1 6 4 1 7 5 1 8 6 1 15 7 1 17
+ 1 2 1 2 2 11 3 2 14 4 2 16 5 2 18 6 2 20
+ 1 3 2 2 3 3 3 3 9 4 3 10 5 3 12 6 3 13 7 3 19
+}
+
+do_execsql_test 1.3 {
+ SELECT * FROM lll WHERE grp_id=2
+} {
+ 1 2 1 2 2 11 3 2 14 4 2 16 5 2 18 6 2 20
+}
+
+do_eqp_test 1.4 {
+ SELECT * FROM lll WHERE grp_id=2
+} {SEARCH TABLE t1 USING COVERING INDEX i1 (grp_id=?)}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a, b, c, d);
+ INSERT INTO t1 VALUES('A', 'C', 1, 0.1);
+ INSERT INTO t1 VALUES('A', 'D', 2, 0.2);
+ INSERT INTO t1 VALUES('A', 'E', 3, 0.3);
+ INSERT INTO t1 VALUES('A', 'C', 4, 0.4);
+ INSERT INTO t1 VALUES('B', 'D', 5, 0.5);
+ INSERT INTO t1 VALUES('B', 'E', 6, 0.6);
+ INSERT INTO t1 VALUES('B', 'C', 7, 0.7);
+ INSERT INTO t1 VALUES('B', 'D', 8, 0.8);
+ INSERT INTO t1 VALUES('C', 'E', 9, 0.9);
+ INSERT INTO t1 VALUES('C', 'C', 10, 1.0);
+ INSERT INTO t1 VALUES('C', 'D', 11, 1.1);
+ INSERT INTO t1 VALUES('C', 'E', 12, 1.2);
+
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+
+ CREATE VIEW v1 AS SELECT a, c, max(c) OVER (PARTITION BY a) FROM t1;
+
+ CREATE VIEW v2 AS SELECT a, c,
+ max(c) OVER (PARTITION BY a),
+ row_number() OVER ()
+ FROM t1;
+
+ CREATE VIEW v3 AS SELECT b, d,
+ max(d) OVER (PARTITION BY b),
+ row_number() OVER (PARTITION BY b)
+ FROM t1;
+
+ CREATE TABLE t2(x, y, z);
+ INSERT INTO t2 VALUES('W', 3, 1);
+ INSERT INTO t2 VALUES('W', 2, 2);
+ INSERT INTO t2 VALUES('X', 1, 4);
+ INSERT INTO t2 VALUES('X', 5, 7);
+ INSERT INTO t2 VALUES('Y', 1, 9);
+ INSERT INTO t2 VALUES('Y', 4, 2);
+ INSERT INTO t2 VALUES('Z', 3, 3);
+ INSERT INTO t2 VALUES('Z', 3, 4);
+}
+
+foreach tn {0 1} {
+ optimization_control db push-down $tn
+
+ do_execsql_test 2.$tn.1.1 {
+ SELECT * FROM v1;
+ } {
+ A 1 4 A 2 4 A 3 4 A 4 4
+ B 5 8 B 6 8 B 7 8 B 8 8
+ C 9 12 C 10 12 C 11 12 C 12 12
+ }
+
+ do_execsql_test 2.$tn.1.2 {
+ SELECT * FROM v1 WHERE a IN ('A', 'B');
+ } {
+ A 1 4 A 2 4 A 3 4 A 4 4
+ B 5 8 B 6 8 B 7 8 B 8 8
+ }
+
+ do_execsql_test 2.$tn.1.3 {
+ SELECT * FROM v1 WHERE a IS 'C'
+ } {
+ C 9 12 C 10 12 C 11 12 C 12 12
+ }
+
+ if {$tn==1} {
+ do_eqp_test 2.$tn.1.4 {
+ SELECT * FROM v1 WHERE a IN ('A', 'B');
+ } {USING INDEX i1 (a=?)}
+
+ do_eqp_test 2.$tn.1.5 {
+ SELECT * FROM v1 WHERE a = 'c' COLLATE nocase
+ } {USING INDEX i1}
+ }
+
+ do_execsql_test 2.$tn.2.1 {
+ SELECT * FROM v2;
+ } {
+ A 1 4 1 A 2 4 2 A 3 4 3 A 4 4 4
+ B 5 8 5 B 6 8 6 B 7 8 7 B 8 8 8
+ C 9 12 9 C 10 12 10 C 11 12 11 C 12 12 12
+ }
+
+ do_execsql_test 2.$tn.2.2 {
+ SELECT * FROM v2 WHERE a = 'C';
+ } {
+ C 9 12 9 C 10 12 10 C 11 12 11 C 12 12 12
+ }
+
+ do_execsql_test 2.$tn.3.1 { SELECT * FROM v3; } {
+ C 0.1 1.0 1 C 0.4 1.0 2 C 0.7 1.0 3 C 1.0 1.0 4
+ D 0.2 1.1 1 D 0.5 1.1 2 D 0.8 1.1 3 D 1.1 1.1 4
+ E 0.3 1.2 1 E 0.6 1.2 2 E 0.9 1.2 3 E 1.2 1.2 4
+ }
+
+ do_execsql_test 2.$tn.3.2 { SELECT * FROM v3 WHERE b<'E' } {
+ C 0.1 1.0 1 C 0.4 1.0 2 C 0.7 1.0 3 C 1.0 1.0 4
+ D 0.2 1.1 1 D 0.5 1.1 2 D 0.8 1.1 3 D 1.1 1.1 4
+ }
+
+ if {$tn==1} {
+ do_eqp_test 2.$tn.3.3 {
+ SELECT * FROM v3 WHERE b='E'
+ } {SEARCH TABLE t1 USING INDEX i2 (b=?)}
+ do_eqp_test 2.$tn.3.4 {
+ SELECT * FROM v3 WHERE b>'C'
+ } {SEARCH TABLE t1 USING INDEX i2 (b>?)}
+ }
+
+ do_execsql_test 2.$tn.3.5 { SELECT * FROM v3 WHERE d<0.55; } {
+ C 0.1 1.0 1 C 0.4 1.0 2
+ D 0.2 1.1 1 D 0.5 1.1 2
+ E 0.3 1.2 1
+ }
+ if {$tn==1} {
+ do_eqp_test 2.$tn.3.6 {
+ SELECT * FROM v3 WHERE d<0.55
+ } {SCAN TABLE t1 USING INDEX i2}
+ }
+
+ do_execsql_test 2.$tn.4.1 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m
+ FROM t2 GROUP BY x
+ )
+ } {
+ W 5 2
+ X 6 7
+ Y 5 9
+ Z 6 4
+ }
+
+ do_execsql_test 2.$tn.4.1 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m,
+ max( max(z) ) OVER (PARTITION BY sum(y)
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ )
+ FROM t2 GROUP BY x
+ )
+ } {
+ W 5 2 9
+ Y 5 9 9
+ X 6 7 7
+ Z 6 4 7
+ }
+
+ do_execsql_test 2.$tn.4.2 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m,
+ max( max(z) ) OVER (PARTITION BY sum(y)
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ )
+ FROM t2 GROUP BY x
+ ) WHERE s=6
+ } {
+ X 6 7 7
+ Z 6 4 7
+ }
+
+ do_execsql_test 2.$tn.4.3 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m,
+ max( max(z) ) OVER (PARTITION BY sum(y)
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ )
+ FROM t2 GROUP BY x
+ ) WHERE s<6
+ } {
+ W 5 2 9
+ Y 5 9 9
+ }
+
+}
+
+
+
+
+finish_test
+
diff --git a/test/with3.test b/test/with3.test
index a574263b89..3325ecc93f 100644
--- a/test/with3.test
+++ b/test/with3.test
@@ -198,4 +198,33 @@ do_execsql_test 4.2 {
GROUP BY 1;
} {elvis}
+# 2021-02-13
+# Avoid manifesting the same CTE multiple times.
+#
+do_eqp_test 5.1 {
+ WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<1)
+ SELECT x1.x||x2.x||x3.x||x4.x FROM c AS x1, c AS x2, c AS x3, c AS x4
+ ORDER BY 1;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--SETUP
+ | | `--SCAN CONSTANT ROW
+ | `--RECURSIVE STEP
+ | `--SCAN TABLE c
+ |--SCAN SUBQUERY xxxxxx AS x1
+ |--SCAN SUBQUERY xxxxxx AS x2
+ |--SCAN SUBQUERY xxxxxx AS x3
+ |--SCAN SUBQUERY xxxxxx AS x4
+ `--USE TEMP B-TREE FOR ORDER BY
+}
+do_execsql_test 5.2 {
+ WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<1)
+ SELECT x1.x||x2.x||x3.x||x4.x FROM c AS x1, c AS x2, c AS x3, c AS x4
+ ORDER BY 1;
+} {0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111}
+
+
+
+
finish_test
diff --git a/test/with6.test b/test/with6.test
new file mode 100644
index 0000000000..ef63ec8bee
--- /dev/null
+++ b/test/with6.test
@@ -0,0 +1,255 @@
+# 2021-02-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is the MATERIALIZED hint to common table expressions
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix with6
+
+ifcapable {!cte} {
+ finish_test
+ return
+}
+
+do_execsql_test 100 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 101 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 110 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 111 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+# Even though the CTE is not materialized, the self-join optimization
+# kicks in and does the materialization for us.
+#
+do_execsql_test 120 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 121 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 130 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 5) AS c2,
+ (SELECT x FROM c LIMIT 5) AS c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 131 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 5) AS c2,
+ (SELECT x FROM c LIMIT 5) AS c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--CO-ROUTINE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | |--CO-ROUTINE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | |--CO-ROUTINE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+# The (SELECT x FROM c LIMIT N) subqueries get materialized once each.
+# Show multiple materializations are shown. But there is only one
+# materialization for c, shown by the "SCAN 2 CONSTANT ROWS" line.
+#
+do_execsql_test 140 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 141 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--MATERIALIZE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 150 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 151 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--MATERIALIZE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 160 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {100 301}
+do_eqp_test 161 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--CORRELATED SCALAR SUBQUERY xxxxxx
+ `--SCAN SUBQUERY xxxxxx
+}
+
+do_execsql_test 170 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {100 301}
+do_eqp_test 171 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {
+ QUERY PLAN
+ |--CO-ROUTINE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--CORRELATED SCALAR SUBQUERY xxxxxx
+ |--CO-ROUTINE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ `--SCAN SUBQUERY xxxxxx
+}
+
+
+do_execsql_test 200 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES(4);
+ CREATE VIEW t2(y) AS
+ WITH c(z) AS (VALUES(4),(5),(6))
+ SELECT c1.z+c2.z*100+t1.x*10000
+ FROM t1,
+ (SELECT z FROM c LIMIT 5) AS c1,
+ (SELECT z FROM c LIMIT 5) AS c2;
+ SELECT y FROM t2 ORDER BY y;
+} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
+do_execsql_test 210 {
+ DROP VIEW t2;
+ CREATE VIEW t2(y) AS
+ WITH c(z) AS NOT MATERIALIZED (VALUES(4),(5),(6))
+ SELECT c1.z+c2.z*100+t1.x*10000
+ FROM t1,
+ (SELECT z FROM c LIMIT 5) AS c1,
+ (SELECT z FROM c LIMIT 5) AS c2;
+ SELECT y FROM t2 ORDER BY y;
+} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
+do_eqp_test 211 {
+ SELECT y FROM t2 ORDER BY y;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--MATERIALIZE xxxxxx
+ | | `--SCAN 3 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ |--SCAN TABLE t1
+ `--USE TEMP B-TREE FOR ORDER BY
+}
+do_execsql_test 220 {
+ DROP VIEW t2;
+ CREATE VIEW t2(y) AS
+ WITH c(z) AS MATERIALIZED (VALUES(4),(5),(6))
+ SELECT c1.z+c2.z*100+t1.x*10000
+ FROM t1,
+ (SELECT z FROM c LIMIT 5) AS c1,
+ (SELECT z FROM c LIMIT 5) AS c2;
+ SELECT y FROM t2 ORDER BY y;
+} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
+
+
+
+finish_test
diff --git a/tool/lemon.c b/tool/lemon.c
index 54c8946a0d..06ba1be2bd 100644
--- a/tool/lemon.c
+++ b/tool/lemon.c
@@ -401,7 +401,7 @@ struct lemon {
struct symbol *errsym; /* The error symbol */
struct symbol *wildcard; /* Token that matches anything */
char *name; /* Name of the generated parser */
- char *arg; /* Declaration of the 3th argument to parser */
+ char *arg; /* Declaration of the 3rd argument to parser */
char *ctx; /* Declaration of 2nd argument to constructor */
char *tokentype; /* Type of terminal symbols in the parser stack */
char *vartype; /* The default type of non-terminal symbols */
@@ -1027,7 +1027,7 @@ PRIVATE void buildshifts(struct lemon *lemp, struct state *stp)
struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
struct state *newstp; /* A pointer to a successor state */
- /* Each configuration becomes complete after it contibutes to a successor
+ /* Each configuration becomes complete after it contributes to a successor
** state. Initially, all configurations are incomplete */
for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE;
@@ -1887,7 +1887,7 @@ static char *merge(
**
** Return Value:
** A pointer to the head of a sorted list containing the elements
-** orginally in list.
+** originally in list.
**
** Side effects:
** The "next" pointers for elements in list are changed.
@@ -3513,7 +3513,7 @@ void ReportOutput(struct lemon *lemp)
}
/* Search for the file "name" which is in the same directory as
-** the exacutable */
+** the executable */
PRIVATE char *pathsearch(char *argv0, char *name, int modemask)
{
const char *pathlist;
@@ -3868,7 +3868,7 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){
lhsdirect = 1;
}else if( rp->rhsalias[0]==0 ){
/* The left-most RHS symbol has no value. LHS direct is ok. But
- ** we have to call the distructor on the RHS symbol first. */
+ ** we have to call the destructor on the RHS symbol first. */
lhsdirect = 1;
if( has_destructor(rp->rhs[0],lemp) ){
append_str(0,0,0,0);
@@ -4849,7 +4849,7 @@ void ReportTable(
** yyRuleInfoNRhs[].
**
** Note: This code depends on the fact that rules are number
- ** sequentually beginning with 0.
+ ** sequentially beginning with 0.
*/
for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){
fprintf(out," %4d, /* (%d) ", rp->lhs->index, i);
diff --git a/tool/lempar.c b/tool/lempar.c
index 71a51cf448..35c3768bb9 100644
--- a/tool/lempar.c
+++ b/tool/lempar.c
@@ -718,55 +718,6 @@ static YYACTIONTYPE yy_reduce(
(void)yyLookahead;
(void)yyLookaheadToken;
yymsp = yypParser->yytos;
- assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
-#ifndef NDEBUG
- if( yyTraceFILE ){
- yysize = yyRuleInfoNRhs[yyruleno];
- if( yysize ){
- fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
- yyTracePrompt,
- yyruleno, yyRuleName[yyruleno],
- yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){
- yypParser->yyhwm++;
- assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
- }
-#endif
-#if YYSTACKDEPTH>0
- if( yypParser->yytos>=yypParser->yystackEnd ){
- yyStackOverflow(yypParser);
- /* 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);
- /* 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;
- }
-#endif
- }
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
@@ -925,12 +876,56 @@ void Parse(
}
#endif
- do{
+ while(1){ /* Exit by "break" */
+ assert( yypParser->yytos>=yypParser->yystack );
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 ParseCTX_PARAM);
+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ int yysize = yyRuleInfoNRhs[yyruleno];
+ if( yysize ){
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
+ yyTracePrompt,
+ yyruleno, yyRuleName[yyruleno],
+ yyrulenoyytos[yysize].stateno);
+ }else{
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
+ yyTracePrompt, yyruleno, yyRuleName[yyruleno],
+ yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){
+ yypParser->yyhwm++;
+ assert( yypParser->yyhwm ==
+ (int)(yypParser->yytos - yypParser->yystack));
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yytos>=yypParser->yystackEnd ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+#else
+ if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
+ if( yyGrowStack(yypParser) ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+ }
+#endif
+ }
+ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
@@ -1043,7 +1038,7 @@ void Parse(
break;
#endif
}
- }while( yypParser->yytos>yypParser->yystack );
+ }
#ifndef NDEBUG
if( yyTraceFILE ){
yyStackEntry *i;
diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c
index 83ec179ba0..bbb0ccf293 100644
--- a/tool/mkkeywordhash.c
+++ b/tool/mkkeywordhash.c
@@ -155,10 +155,16 @@ struct Keyword {
# define WINDOWFUNC 0x00100000
#endif
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
-# define GENCOL 0
+# define GENCOL 0
#else
-# define GENCOL 0x00200000
+# define GENCOL 0x00200000
#endif
+#ifdef SQLITE_OMIT_RETURNING
+# define RETURNING 0
+#else
+# define RETURNING 0x00400000
+#endif
+
/*
** These are the keywords
@@ -223,7 +229,7 @@ static Keyword aKeywordTable[] = {
{ "FOREIGN", "TK_FOREIGN", FKEY, 1 },
{ "FROM", "TK_FROM", ALWAYS, 10 },
{ "FULL", "TK_JOIN_KW", ALWAYS, 3 },
- { "GENERATED", "TK_GENERATED", GENCOL, 1 },
+ { "GENERATED", "TK_GENERATED", ALWAYS, 1 },
{ "GLOB", "TK_LIKE_KW", ALWAYS, 3 },
{ "GROUP", "TK_GROUP", ALWAYS, 5 },
{ "GROUPS", "TK_GROUPS", WINDOWFUNC, 2 },
@@ -249,6 +255,7 @@ static Keyword aKeywordTable[] = {
{ "LIKE", "TK_LIKE_KW", ALWAYS, 5 },
{ "LIMIT", "TK_LIMIT", ALWAYS, 3 },
{ "MATCH", "TK_MATCH", ALWAYS, 2 },
+ { "MATERIALIZED", "TK_MATERIALIZED", CTE, 12 },
{ "NATURAL", "TK_JOIN_KW", ALWAYS, 3 },
{ "NO", "TK_NO", FKEY|WINDOWFUNC, 2 },
{ "NOT", "TK_NOT", ALWAYS, 10 },
@@ -280,6 +287,7 @@ static Keyword aKeywordTable[] = {
{ "RENAME", "TK_RENAME", ALTER, 1 },
{ "REPLACE", "TK_REPLACE", CONFLICT, 10 },
{ "RESTRICT", "TK_RESTRICT", FKEY, 1 },
+ { "RETURNING", "TK_RETURNING", RETURNING, 10 },
{ "RIGHT", "TK_JOIN_KW", ALWAYS, 0 },
{ "ROLLBACK", "TK_ROLLBACK", ALWAYS, 1 },
{ "ROW", "TK_ROW", TRIGGER, 1 },
@@ -381,6 +389,14 @@ static void reorder(int *pFrom){
reorder(&aKeywordTable[i].iNext);
}
+/* Parameter to the hash function
+*/
+#define HASH_OP ^
+#define HASH_CC '^'
+#define HASH_C0 4
+#define HASH_C1 3
+#define HASH_C2 1
+
/*
** This routine does the work. The generated code is printed on standard
** output.
@@ -411,8 +427,9 @@ int main(int argc, char **argv){
assert( p->lenzOrigName) );
memcpy(p->zOrigName, p->zName, p->len+1);
totalLen += p->len;
- p->hash = (charMap(p->zName[0])*4) ^
- (charMap(p->zName[p->len-1])*3) ^ (p->len*1);
+ p->hash = (charMap(p->zName[0])*HASH_C0) HASH_OP
+ (charMap(p->zName[p->len-1])*HASH_C1) HASH_OP
+ (p->len*HASH_C2);
p->id = i+1;
}
@@ -648,8 +665,9 @@ int main(int argc, char **argv){
printf(" int i, j;\n");
printf(" const char *zKW;\n");
printf(" if( n>=2 ){\n");
- printf(" i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) %% %d;\n",
- bestSize);
+ printf(" i = ((charMap(z[0])*%d) %c", HASH_C0, HASH_CC);
+ printf(" (charMap(z[n-1])*%d) %c", HASH_C1, HASH_CC);
+ printf(" n*%d) %% %d;\n", HASH_C2, bestSize);
printf(" for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){\n");
printf(" if( aKWLen[i]!=n ) continue;\n");
printf(" zKW = &zKWText[aKWOffset[i]];\n");
|