Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60405cd15c | |||
| b08dbd0f28 | |||
| 00a0b135ec | |||
| 90ab71bdc8 | |||
| 4dca7d4cfb | |||
| 1dc9cf947b | |||
| 384f5c26f4 | |||
| 9f4fb42730 | |||
| aeb4e6eebd | |||
| 56a5747e63 | |||
| cad760d16e | |||
| 6d19bf9460 | |||
| 3181706895 | |||
| 66efc393c5 | |||
| 1c5b23f170 | |||
| ec8962869a | |||
| 68b4034450 | |||
| d36f588f31 | |||
| 3ef2cb5991 | |||
| f82d78e9c2 | |||
| b7c46aa8c9 | |||
| f862b55a56 | |||
| 4989a530a1 | |||
| a7fc1686e4 | |||
| f5c3a75b63 | |||
| 2e3cb1382f | |||
| ef5b2d27b7 | |||
| de9873bb0a | |||
| d46beb06aa | |||
| 39acaec16c | |||
| 1335ec7df7 | |||
| 6a5e21149b | |||
| 6fc74a4454 | |||
| e5dc4a2097 | |||
| f4097026ef | |||
| 5dc18e33ae | |||
| 02e4b7d7df | |||
| f56a4bfcd1 | |||
| 8ea11095c9 | |||
| c30b78f6f3 | |||
| 8bb3961d40 | |||
| 5011bb8dd2 | |||
| 5a07d10fd1 | |||
| 32881bebbe | |||
| aff1a57f4d | |||
| 12c9994659 | |||
| ea847f1b94 | |||
| 7f42dcd901 | |||
| ea2487200f | |||
| b035b87ef8 | |||
| 75e6bbbeca | |||
| 235667a858 | |||
| 78043e891a | |||
| 4e451aad54 | |||
| 114cb01796 | |||
| 6d5ab2a1d6 | |||
| 2fa7818571 | |||
| 7a39faecc2 | |||
| ed7974dee5 | |||
| 19f7bd3b07 | |||
| d1d43efa4f | |||
| beb22d086b | |||
| 461ff3594b | |||
| 403869680b | |||
| 672f07c642 | |||
| 1a56fce614 | |||
| 94d6f3f836 | |||
| 787f6d418d | |||
| 07d7a8dca9 | |||
| 340558540e | |||
| a00a016740 | |||
| ff01ee34c9 | |||
| e93986a93b | |||
| 71cb422055 | |||
| f8c4c3a09f | |||
| d5157e01b8 | |||
| 05881886a6 | |||
| 7207be402d | |||
| f6b0464e36 | |||
| c6712647c6 | |||
| c196219dd4 | |||
| 936a30598e | |||
| bbb29ecfb4 | |||
| 4d906f1b5a | |||
| 1ca037f41e | |||
| 0897c3c056 | |||
| f46be6a1b9 | |||
| a344ad47f3 | |||
| 12a6a1eaf9 | |||
| 95dca8d0cf | |||
| 7093a3be03 | |||
| 7d14ffe4e1 | |||
| 64208b50ce | |||
| 9bb612f2c8 | |||
| 04e70ce07d | |||
| 4ad1a1c74c | |||
| d7d406e81c | |||
| a22c1c84cc | |||
| ccf578d435 | |||
| deaa61072d | |||
| 644f43c0f7 | |||
| 67306cb3b4 | |||
| 33a99fad08 | |||
| fad4dd0fd1 | |||
| 4f65b3bbfb | |||
| 395a60dad4 | |||
| af3711536b | |||
| 1e6c58dee2 | |||
| a957e22fa4 | |||
| f287d00108 | |||
| a54e1b1fc8 | |||
| 0b2949c36e | |||
| dfbaae7572 | |||
| c40076a8cf | |||
| 46f0f4e56d | |||
| f761d937c2 | |||
| 68cf0ace3d | |||
| 75fa266341 | |||
| f573b4fb94 | |||
| 273ee15121 | |||
| 7548ab20e6 | |||
| 9ca41fbfa9 | |||
| 79d9af960b | |||
| 68cffa65fb | |||
| 39c7125a97 | |||
| f2e151aeab | |||
| 3acd3fb9d8 | |||
| f2fa084cf1 | |||
| bc2e95140b | |||
| f1ea425560 | |||
| d96e3821e4 | |||
| 18ef40e563 | |||
| b5a69238b4 | |||
| ddcfe92105 | |||
| 86f477edaa | |||
| 94acc2ef42 | |||
| 4fb8452875 | |||
| 786142a994 | |||
| ebd2ecdda7 | |||
| 8ab0809473 | |||
| 2b75d3beb0 | |||
| 78f04753ae | |||
| 2b06b0769e | |||
| 52cfe0312a | |||
| 512aa78ca2 | |||
| 60c71b02ca | |||
| fa17e134b2 | |||
| d321b6f4ad | |||
| 37ccfcfeb9 | |||
| c51ceeb049 | |||
| acd3c8a995 | |||
| d49c0c83ef | |||
| 6acdee6765 | |||
| 8337da6678 | |||
| 72c38d87bf | |||
| 7b8524168e | |||
| e2678b93af | |||
| 832aa023c6 | |||
| 04cd60e18c | |||
| 92e21ef079 | |||
| 359941bd33 | |||
| ca3ee7e18a | |||
| 60081a0382 | |||
| 30497f4169 | |||
| 99744fa4ab | |||
| 58d6f633ba | |||
| 599456f0ab | |||
| 6c3b4b07d1 | |||
| 7b14b990d0 | |||
| 14f38b3d0a | |||
| 46fe138d98 | |||
| 1d42e619ac | |||
| fce445b8fa | |||
| 9191c702fc | |||
| 1d8f892a31 | |||
| 45d827cb6a | |||
| 289a0c849b | |||
| f0b41745c5 | |||
| 398221e2ad | |||
| 861889e4f8 | |||
| a3d6d6a93f | |||
| f8556d0106 | |||
| 68c0c71065 | |||
| 5e6d90fe15 | |||
| e0404386f2 | |||
| ef215fbf3b | |||
| 96e337df6b | |||
| a3e6495970 | |||
| b6a74caf93 | |||
| c08716a317 | |||
| 4dfdb86c8d | |||
| 91faeec8d0 | |||
| 8c1fbe81d0 | |||
| 24c22753fc | |||
| 47eb561c40 | |||
| 9f567eb9f5 | |||
| 26c48d2aab | |||
| 12a4c41917 | |||
| 08b9208660 | |||
| ed0af52cb1 | |||
| 80aff0847e | |||
| 14d093f847 | |||
| 8afc09dea3 | |||
| 07066d90f6 | |||
| 47d38e2444 | |||
| 35a37c2a47 | |||
| c664355c0f | |||
| a6c13b22b2 | |||
| d43c0cbed2 | |||
| 7033ce4fdc | |||
| 799443b14f | |||
| 26b9540fc2 | |||
| 88819d5870 | |||
| b19493434f | |||
| 2c40b8f3da | |||
| f82ce382f9 |
@@ -441,6 +441,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/expert/test_expert.c \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/carray.c \
|
||||
$(TOP)/ext/misc/cksumvfs.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
$(TOP)/ext/misc/csv.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
@@ -1086,6 +1087,7 @@ SHELL_SRC = \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
@@ -1232,6 +1234,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DESERIALIZE
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
|
||||
TESTFIXTURE_SRC1 = sqlite3.c
|
||||
|
||||
@@ -1558,6 +1558,7 @@ TESTEXT = \
|
||||
$(TOP)\ext\expert\test_expert.c \
|
||||
$(TOP)\ext\misc\amatch.c \
|
||||
$(TOP)\ext\misc\carray.c \
|
||||
$(TOP)\ext\misc\cksumvfs.c \
|
||||
$(TOP)\ext\misc\closure.c \
|
||||
$(TOP)\ext\misc\csv.c \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
@@ -2208,6 +2209,7 @@ SHELL_SRC = \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\series.c \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
@@ -2386,6 +2388,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CKSUMVFS_STATIC=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
|
||||
|
||||
@@ -153,7 +153,7 @@ Please `cd` to its location first.
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
PROJECT = tclsqlite3
|
||||
PROJECT = sqlite3
|
||||
!include "rules.vc"
|
||||
|
||||
# nmakehelp -V <file> <tag> will search the file for tag, skips until a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.33.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.34.1.
|
||||
#
|
||||
#
|
||||
# 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.33.0'
|
||||
PACKAGE_STRING='sqlite 3.33.0'
|
||||
PACKAGE_VERSION='3.34.1'
|
||||
PACKAGE_STRING='sqlite 3.34.1'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -799,7 +799,6 @@ TEMP_STORE
|
||||
ALLOWRELEASE
|
||||
SQLITE_THREADSAFE
|
||||
BUILD_CC
|
||||
VERSION_NUMBER
|
||||
RELEASE
|
||||
VERSION
|
||||
program_prefix
|
||||
@@ -1467,7 +1466,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.33.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.34.1 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1532,7 +1531,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.33.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.34.1:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1659,7 +1658,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.33.0
|
||||
sqlite configure 3.34.1
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2078,7 +2077,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.33.0, which was
|
||||
It was created by sqlite $as_me 3.34.1, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3936,13 +3935,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:3939: $ac_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:3938: $ac_compile\"" >&5)
|
||||
(eval "$ac_compile" 2>conftest.err)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3942: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval echo "\"\$as_me:3941: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3945: output\"" >&5)
|
||||
(eval echo "\"\$as_me:3944: output\"" >&5)
|
||||
cat conftest.out >&5
|
||||
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
|
||||
lt_cv_nm_interface="MS dumpbin"
|
||||
@@ -5148,7 +5147,7 @@ ia64-*-hpux*)
|
||||
;;
|
||||
*-*-irix6*)
|
||||
# Find out which ABI we are using.
|
||||
echo '#line 5151 "configure"' > conftest.$ac_ext
|
||||
echo '#line 5150 "configure"' > conftest.$ac_ext
|
||||
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
@@ -6673,11 +6672,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:6676: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6675: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:6680: \$? = $ac_status" >&5
|
||||
echo "$as_me:6679: \$? = $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.
|
||||
@@ -7012,11 +7011,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:7015: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7014: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:7019: \$? = $ac_status" >&5
|
||||
echo "$as_me:7018: \$? = $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.
|
||||
@@ -7117,11 +7116,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:7120: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7119: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7124: \$? = $ac_status" >&5
|
||||
echo "$as_me:7123: \$? = $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
|
||||
@@ -7172,11 +7171,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:7175: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7174: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7179: \$? = $ac_status" >&5
|
||||
echo "$as_me:7178: \$? = $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
|
||||
@@ -9552,7 +9551,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9555 "configure"
|
||||
#line 9554 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -9648,7 +9647,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9651 "configure"
|
||||
#line 9650 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -10388,12 +10387,6 @@ RELEASE=`cat $srcdir/VERSION`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: Release set to $RELEASE" >&5
|
||||
$as_echo "$as_me: Release set to $RELEASE" >&6;}
|
||||
|
||||
VERSION_NUMBER=`cat $srcdir/VERSION \
|
||||
| sed 's/[^0-9]/ /g' \
|
||||
| awk '{printf "%d%03d%03d",$1,$2,$3}'`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: Version number set to $VERSION_NUMBER" >&5
|
||||
$as_echo "$as_me: Version number set to $VERSION_NUMBER" >&6;}
|
||||
|
||||
|
||||
#########
|
||||
# Locate a compiler for the build machine. This compiler should
|
||||
@@ -12243,7 +12236,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.33.0, which was
|
||||
This file was extended by sqlite $as_me 3.34.1, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12309,7 +12302,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.33.0
|
||||
sqlite config.status 3.34.1
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -155,11 +155,6 @@ AC_SUBST(VERSION)
|
||||
RELEASE=`cat $srcdir/VERSION`
|
||||
AC_MSG_NOTICE(Release set to $RELEASE)
|
||||
AC_SUBST(RELEASE)
|
||||
VERSION_NUMBER=[`cat $srcdir/VERSION \
|
||||
| sed 's/[^0-9]/ /g' \
|
||||
| awk '{printf "%d%03d%03d",$1,$2,$3}'`]
|
||||
AC_MSG_NOTICE(Version number set to $VERSION_NUMBER)
|
||||
AC_SUBST(VERSION_NUMBER)
|
||||
|
||||
#########
|
||||
# Locate a compiler for the build machine. This compiler should
|
||||
|
||||
+329
-170
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
|
||||
20-11-2020
|
||||
|
||||
# Memory Allocation In vdbesort.c
|
||||
|
||||
Memory allocation is slightly different depending on:
|
||||
|
||||
* whether or not SQLITE_CONFIG_SMALL_MALLOC is set, and
|
||||
* whether or not worker threads are enabled.
|
||||
|
||||
## SQLITE_CONFIG_SMALL_MALLOC=0
|
||||
|
||||
Assuming SQLITE_CONFIG_SMALL_MALLOC is not set, keys passed to the sorter are
|
||||
added to an in-memory buffer. This buffer is grown using sqlite3Realloc() as
|
||||
required it reaches the size configured for the main pager cache using "PRAGMA
|
||||
cache_size". i.e. if the user has executed "PRAGMA main.cache_size = -2048",
|
||||
then this buffer is allowed to grow up to 2MB in size.
|
||||
|
||||
Once the buffer has grown to its threshold, keys are sorted and written to
|
||||
a temp file. If worker threads are not enabled, this is the only significant
|
||||
allocation the sorter module makes. After keys are sorted and flushed out to
|
||||
the temp file, the buffer is reused to accumulate the next batch of keys.
|
||||
|
||||
If worker threads are available, then the buffer is passed to a worker thread
|
||||
to sort and flush once it is full, and a new buffer allocated to allow the
|
||||
main thread to continue to accumulate keys. Buffers are reused once they
|
||||
have been flushed, so in this case at most (nWorker+1) buffers are allocated
|
||||
and used, where nWorker is the number of configured worker threads.
|
||||
|
||||
There are no other significant users of heap memory in the sorter module.
|
||||
Once sorted buffers of keys have been flushed to disk, they are read back
|
||||
either by mapping the file (via sqlite3_file.xFetch()) or else read back
|
||||
in one page at a time.
|
||||
|
||||
All buffers are allocated by the main thread. A sorter object is associated
|
||||
with a single database connection, to which it holds a pointer.
|
||||
|
||||
## SQLITE_CONFIG_SMALL_MALLOC=1
|
||||
|
||||
This case is similar to the above, except that instead of accumulating
|
||||
multiple keys in a single large buffer, sqlite3VdbeSorterWrite() stores
|
||||
keys in a regular heap-memory linked list (one allocation per element).
|
||||
List elements are freed as they are flushed to disk, either by the main
|
||||
thread or by a worker thread.
|
||||
|
||||
Each time a key is added the sorter (and an allocation made),
|
||||
sqlite3HeapNearlyFull() is called. If it returns true, the current
|
||||
list of keys is flushed to a temporary file, even if it has not yet
|
||||
reached the size threshold.
|
||||
@@ -326,6 +326,47 @@ do_setup_rec_test $tn.16 {
|
||||
SCAN TABLE t1
|
||||
}
|
||||
|
||||
do_setup_rec_test $tn.17.1 {
|
||||
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
|
||||
} {
|
||||
SELECT * FROM example WHERE a=?
|
||||
} {
|
||||
(no new indexes)
|
||||
SEARCH TABLE example USING INDEX sqlite_autoindex_example_1 (A=?)
|
||||
}
|
||||
do_setup_rec_test $tn.17.2 {
|
||||
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
|
||||
} {
|
||||
SELECT * FROM example WHERE b=?
|
||||
} {
|
||||
CREATE INDEX example_idx_00000042 ON example(B);
|
||||
SEARCH TABLE example USING INDEX example_idx_00000042 (B=?)
|
||||
}
|
||||
do_setup_rec_test $tn.17.3 {
|
||||
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
|
||||
} {
|
||||
SELECT * FROM example WHERE a=? AND b=?
|
||||
} {
|
||||
(no new indexes)
|
||||
SEARCH TABLE example USING INDEX sqlite_autoindex_example_1 (A=? AND B=?)
|
||||
}
|
||||
do_setup_rec_test $tn.17.4 {
|
||||
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
|
||||
} {
|
||||
SELECT * FROM example WHERE a=? AND b>?
|
||||
} {
|
||||
(no new indexes)
|
||||
SEARCH TABLE example USING INDEX sqlite_autoindex_example_1 (A=? AND B>?)
|
||||
}
|
||||
do_setup_rec_test $tn.17.5 {
|
||||
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
|
||||
} {
|
||||
SELECT * FROM example WHERE a>? AND b=?
|
||||
} {
|
||||
CREATE INDEX example_idx_0000cb3f ON example(B, A);
|
||||
SEARCH TABLE example USING INDEX example_idx_0000cb3f (B=? AND A>?)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
proc do_candidates_test {tn sql res} {
|
||||
|
||||
@@ -685,6 +685,7 @@ static int idxGetTableInfo(
|
||||
IdxTable *pNew = 0;
|
||||
int rc, rc2;
|
||||
char *pCsr = 0;
|
||||
int nPk = 0;
|
||||
|
||||
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
|
||||
@@ -695,6 +696,7 @@ static int idxGetTableInfo(
|
||||
);
|
||||
nByte += 1 + STRLEN(zCol);
|
||||
nCol++;
|
||||
nPk += (sqlite3_column_int(p1, 5)>0);
|
||||
}
|
||||
rc2 = sqlite3_reset(p1);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
@@ -714,7 +716,7 @@ static int idxGetTableInfo(
|
||||
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
|
||||
int nCopy = STRLEN(zCol) + 1;
|
||||
pNew->aCol[nCol].zName = pCsr;
|
||||
pNew->aCol[nCol].iPk = sqlite3_column_int(p1, 5);
|
||||
pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5)==1 && nPk==1);
|
||||
memcpy(pCsr, zCol, nCopy);
|
||||
pCsr += nCopy;
|
||||
|
||||
@@ -1719,10 +1721,12 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
idxFinalize(&rc, pIndexXInfo);
|
||||
idxFinalize(&rc, pWrite);
|
||||
|
||||
for(i=0; i<pCtx->nSlot; i++){
|
||||
sqlite3_free(pCtx->aSlot[i].z);
|
||||
if( pCtx ){
|
||||
for(i=0; i<pCtx->nSlot; 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);
|
||||
|
||||
+5
-1
@@ -3479,7 +3479,7 @@ static int fts3ColumnMethod(
|
||||
break;
|
||||
}else{
|
||||
iCol = p->nColumn;
|
||||
/* fall-through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -3722,9 +3722,13 @@ static void fts3SnippetFunc(
|
||||
|
||||
switch( nVal ){
|
||||
case 6: nToken = sqlite3_value_int(apVal[5]);
|
||||
/* no break */ deliberate_fall_through
|
||||
case 5: iCol = sqlite3_value_int(apVal[4]);
|
||||
/* no break */ deliberate_fall_through
|
||||
case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
|
||||
/* no break */ deliberate_fall_through
|
||||
case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
|
||||
/* no break */ deliberate_fall_through
|
||||
case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
|
||||
}
|
||||
if( !zEllipsis || !zEnd || !zStart ){
|
||||
|
||||
@@ -199,6 +199,8 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
|
||||
|
||||
#define deliberate_fall_through
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
||||
@@ -188,7 +188,8 @@ static int fts3tokConnectMethod(
|
||||
|
||||
assert( (rc==SQLITE_OK)==(pMod!=0) );
|
||||
if( rc==SQLITE_OK ){
|
||||
const char * const *azArg = (const char * const *)&azDequote[1];
|
||||
const char * const *azArg = 0;
|
||||
if( nDequote>1 ) azArg = (const char * const *)&azDequote[1];
|
||||
rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok);
|
||||
}
|
||||
|
||||
|
||||
@@ -742,6 +742,7 @@ proc print_categories {lMap} {
|
||||
}
|
||||
iTbl++;
|
||||
}
|
||||
aAscii[0] = 0; /* 0x00 is never a token character */
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
+23
-9
@@ -184,6 +184,7 @@ struct Fts5Config {
|
||||
Fts5Tokenizer *pTok;
|
||||
fts5_tokenizer *pTokApi;
|
||||
int bLock; /* True when table is preparing statement */
|
||||
int ePattern; /* FTS_PATTERN_XXX constant */
|
||||
|
||||
/* Values loaded from the %_config table */
|
||||
int iCookie; /* Incremented when %_config is modified */
|
||||
@@ -204,17 +205,19 @@ struct Fts5Config {
|
||||
};
|
||||
|
||||
/* Current expected value of %_config table 'version' field */
|
||||
#define FTS5_CURRENT_VERSION 4
|
||||
#define FTS5_CURRENT_VERSION 4
|
||||
|
||||
#define FTS5_CONTENT_NORMAL 0
|
||||
#define FTS5_CONTENT_NONE 1
|
||||
#define FTS5_CONTENT_EXTERNAL 2
|
||||
|
||||
#define FTS5_DETAIL_FULL 0
|
||||
#define FTS5_DETAIL_NONE 1
|
||||
#define FTS5_DETAIL_COLUMNS 2
|
||||
|
||||
#define FTS5_DETAIL_FULL 0
|
||||
#define FTS5_DETAIL_NONE 1
|
||||
#define FTS5_DETAIL_COLUMNS 2
|
||||
|
||||
#define FTS5_PATTERN_NONE 0
|
||||
#define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */
|
||||
#define FTS5_PATTERN_GLOB 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */
|
||||
|
||||
int sqlite3Fts5ConfigParse(
|
||||
Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char**
|
||||
@@ -484,7 +487,7 @@ int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int);
|
||||
/*
|
||||
** Functions called by the storage module as part of integrity-check.
|
||||
*/
|
||||
int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum);
|
||||
int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum, int bUseCksum);
|
||||
|
||||
/*
|
||||
** Called during virtual module initialization to register UDF
|
||||
@@ -554,8 +557,7 @@ int sqlite3Fts5GetTokenizer(
|
||||
Fts5Global*,
|
||||
const char **azArg,
|
||||
int nArg,
|
||||
Fts5Tokenizer**,
|
||||
fts5_tokenizer**,
|
||||
Fts5Config*,
|
||||
char **pzErr
|
||||
);
|
||||
|
||||
@@ -639,7 +641,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
|
||||
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
|
||||
int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
|
||||
|
||||
int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
|
||||
int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg);
|
||||
|
||||
int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**);
|
||||
void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
|
||||
@@ -684,11 +686,19 @@ struct Fts5Token {
|
||||
/* Parse a MATCH expression. */
|
||||
int sqlite3Fts5ExprNew(
|
||||
Fts5Config *pConfig,
|
||||
int bPhraseToAnd,
|
||||
int iCol, /* Column on LHS of MATCH operator */
|
||||
const char *zExpr,
|
||||
Fts5Expr **ppNew,
|
||||
char **pzErr
|
||||
);
|
||||
int sqlite3Fts5ExprPattern(
|
||||
Fts5Config *pConfig,
|
||||
int bGlob,
|
||||
int iCol,
|
||||
const char *zText,
|
||||
Fts5Expr **pp
|
||||
);
|
||||
|
||||
/*
|
||||
** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc);
|
||||
@@ -797,6 +807,10 @@ int sqlite3Fts5AuxInit(fts5_api*);
|
||||
*/
|
||||
|
||||
int sqlite3Fts5TokenizerInit(fts5_api*);
|
||||
int sqlite3Fts5TokenizerPattern(
|
||||
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
|
||||
Fts5Tokenizer *pTok
|
||||
);
|
||||
/*
|
||||
** End of interface to code in fts5_tokenizer.c.
|
||||
**************************************************************************/
|
||||
|
||||
+10
-12
@@ -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 && i<pData->nPhrase; 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; i<pData->nPhrase; 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);
|
||||
|
||||
@@ -325,7 +325,7 @@ static int fts5ConfigParseSpecial(
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = sqlite3Fts5GetTokenizer(pGlobal,
|
||||
(const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi,
|
||||
(const char**)azArg, (int)nArg, pConfig,
|
||||
pzErr
|
||||
);
|
||||
}
|
||||
@@ -397,9 +397,7 @@ static int fts5ConfigParseSpecial(
|
||||
*/
|
||||
static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
|
||||
assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
|
||||
return sqlite3Fts5GetTokenizer(
|
||||
pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
|
||||
);
|
||||
return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+215
-52
@@ -128,6 +128,7 @@ struct Fts5Parse {
|
||||
int nPhrase; /* Size of apPhrase array */
|
||||
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
|
||||
Fts5ExprNode *pExpr; /* Result of a successful parse */
|
||||
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
|
||||
};
|
||||
|
||||
void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
|
||||
@@ -216,6 +217,7 @@ static void fts5ParseFree(void *p){ sqlite3_free(p); }
|
||||
|
||||
int sqlite3Fts5ExprNew(
|
||||
Fts5Config *pConfig, /* FTS5 Configuration */
|
||||
int bPhraseToAnd,
|
||||
int iCol,
|
||||
const char *zExpr, /* Expression text */
|
||||
Fts5Expr **ppNew,
|
||||
@@ -231,6 +233,7 @@ int sqlite3Fts5ExprNew(
|
||||
*ppNew = 0;
|
||||
*pzErr = 0;
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.bPhraseToAnd = bPhraseToAnd;
|
||||
pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc);
|
||||
if( pEngine==0 ){ return SQLITE_NOMEM; }
|
||||
sParse.pConfig = pConfig;
|
||||
@@ -273,6 +276,7 @@ int sqlite3Fts5ExprNew(
|
||||
pNew->pConfig = pConfig;
|
||||
pNew->apExprPhrase = sParse.apPhrase;
|
||||
pNew->nPhrase = sParse.nPhrase;
|
||||
pNew->bDesc = 0;
|
||||
sParse.apPhrase = 0;
|
||||
}
|
||||
}else{
|
||||
@@ -284,6 +288,81 @@ int sqlite3Fts5ExprNew(
|
||||
return sParse.rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is only called when using the special 'trigram' tokenizer.
|
||||
** Argument zText contains the text of a LIKE or GLOB pattern matched
|
||||
** against column iCol. This function creates and compiles an FTS5 MATCH
|
||||
** expression that will match a superset of the rows matched by the LIKE or
|
||||
** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error
|
||||
** code.
|
||||
*/
|
||||
int sqlite3Fts5ExprPattern(
|
||||
Fts5Config *pConfig, int bGlob, int iCol, const char *zText, Fts5Expr **pp
|
||||
){
|
||||
i64 nText = strlen(zText);
|
||||
char *zExpr = (char*)sqlite3_malloc64(nText*4 + 1);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( zExpr==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
char aSpec[3];
|
||||
int iOut = 0;
|
||||
int i = 0;
|
||||
int iFirst = 0;
|
||||
|
||||
if( bGlob==0 ){
|
||||
aSpec[0] = '_';
|
||||
aSpec[1] = '%';
|
||||
aSpec[2] = 0;
|
||||
}else{
|
||||
aSpec[0] = '*';
|
||||
aSpec[1] = '?';
|
||||
aSpec[2] = '[';
|
||||
}
|
||||
|
||||
while( i<=nText ){
|
||||
if( i==nText
|
||||
|| zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2]
|
||||
){
|
||||
if( i-iFirst>=3 ){
|
||||
int jj;
|
||||
zExpr[iOut++] = '"';
|
||||
for(jj=iFirst; jj<i; jj++){
|
||||
zExpr[iOut++] = zText[jj];
|
||||
if( zText[jj]=='"' ) zExpr[iOut++] = '"';
|
||||
}
|
||||
zExpr[iOut++] = '"';
|
||||
zExpr[iOut++] = ' ';
|
||||
}
|
||||
if( zText[i]==aSpec[2] ){
|
||||
i += 2;
|
||||
if( zText[i-1]=='^' ) i++;
|
||||
while( i<nText && zText[i]!=']' ) i++;
|
||||
}
|
||||
iFirst = i+1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if( iOut>0 ){
|
||||
int bAnd = 0;
|
||||
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
||||
bAnd = 1;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_NONE ){
|
||||
iCol = pConfig->nCol;
|
||||
}
|
||||
}
|
||||
zExpr[iOut] = '\0';
|
||||
rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg);
|
||||
}else{
|
||||
*pp = 0;
|
||||
}
|
||||
sqlite3_free(zExpr);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the expression node object passed as the only argument.
|
||||
*/
|
||||
@@ -1661,6 +1740,20 @@ void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){
|
||||
pParse->pExpr = p;
|
||||
}
|
||||
|
||||
static int parseGrowPhraseArray(Fts5Parse *pParse){
|
||||
if( (pParse->nPhrase % 8)==0 ){
|
||||
sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
|
||||
Fts5ExprPhrase **apNew;
|
||||
apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte);
|
||||
if( apNew==0 ){
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pParse->apPhrase = apNew;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the parser to process a string token. The
|
||||
** string may or may not be quoted. In any case it is tokenized and a
|
||||
@@ -1696,16 +1789,9 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm(
|
||||
}else{
|
||||
|
||||
if( pAppend==0 ){
|
||||
if( (pParse->nPhrase % 8)==0 ){
|
||||
sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
|
||||
Fts5ExprPhrase **apNew;
|
||||
apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte);
|
||||
if( apNew==0 ){
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
fts5ExprPhraseFree(sCtx.pPhrase);
|
||||
return 0;
|
||||
}
|
||||
pParse->apPhrase = apNew;
|
||||
if( parseGrowPhraseArray(pParse) ){
|
||||
fts5ExprPhraseFree(sCtx.pPhrase);
|
||||
return 0;
|
||||
}
|
||||
pParse->nPhrase++;
|
||||
}
|
||||
@@ -2112,6 +2198,67 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used when parsing LIKE or GLOB patterns against
|
||||
** trigram indexes that specify either detail=column or detail=none.
|
||||
** It converts a phrase:
|
||||
**
|
||||
** abc + def + ghi
|
||||
**
|
||||
** into an AND tree:
|
||||
**
|
||||
** abc AND def AND ghi
|
||||
*/
|
||||
static Fts5ExprNode *fts5ParsePhraseToAnd(
|
||||
Fts5Parse *pParse,
|
||||
Fts5ExprNearset *pNear
|
||||
){
|
||||
int nTerm = pNear->apPhrase[0]->nTerm;
|
||||
int ii;
|
||||
int nByte;
|
||||
Fts5ExprNode *pRet;
|
||||
|
||||
assert( pNear->nPhrase==1 );
|
||||
assert( pParse->bPhraseToAnd );
|
||||
|
||||
nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
|
||||
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
|
||||
if( pRet ){
|
||||
pRet->eType = FTS5_AND;
|
||||
pRet->nChild = nTerm;
|
||||
fts5ExprAssignXNext(pRet);
|
||||
pParse->nPhrase--;
|
||||
for(ii=0; ii<nTerm; ii++){
|
||||
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
|
||||
&pParse->rc, sizeof(Fts5ExprPhrase)
|
||||
);
|
||||
if( pPhrase ){
|
||||
if( parseGrowPhraseArray(pParse) ){
|
||||
fts5ExprPhraseFree(pPhrase);
|
||||
}else{
|
||||
pParse->apPhrase[pParse->nPhrase++] = pPhrase;
|
||||
pPhrase->nTerm = 1;
|
||||
pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup(
|
||||
&pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1
|
||||
);
|
||||
pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
|
||||
0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pParse->rc ){
|
||||
sqlite3Fts5ParseNodeFree(pRet);
|
||||
pRet = 0;
|
||||
}else{
|
||||
sqlite3Fts5ParseNearsetFree(pNear);
|
||||
}
|
||||
}
|
||||
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return a new expression object. If anything goes wrong (i.e.
|
||||
** OOM error), leave an error code in pParse and return NULL.
|
||||
@@ -2136,51 +2283,58 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
|
||||
if( eType!=FTS5_STRING && pLeft==0 ) return pRight;
|
||||
if( eType!=FTS5_STRING && pRight==0 ) return pLeft;
|
||||
|
||||
if( eType==FTS5_NOT ){
|
||||
nChild = 2;
|
||||
}else if( eType==FTS5_AND || eType==FTS5_OR ){
|
||||
nChild = 2;
|
||||
if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
|
||||
if( pRight->eType==eType ) nChild += pRight->nChild-1;
|
||||
}
|
||||
if( eType==FTS5_STRING
|
||||
&& pParse->bPhraseToAnd
|
||||
&& pNear->apPhrase[0]->nTerm>1
|
||||
){
|
||||
pRet = fts5ParsePhraseToAnd(pParse, pNear);
|
||||
}else{
|
||||
if( eType==FTS5_NOT ){
|
||||
nChild = 2;
|
||||
}else if( eType==FTS5_AND || eType==FTS5_OR ){
|
||||
nChild = 2;
|
||||
if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
|
||||
if( pRight->eType==eType ) nChild += pRight->nChild-1;
|
||||
}
|
||||
|
||||
nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
|
||||
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
|
||||
nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
|
||||
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
|
||||
|
||||
if( pRet ){
|
||||
pRet->eType = eType;
|
||||
pRet->pNear = pNear;
|
||||
fts5ExprAssignXNext(pRet);
|
||||
if( eType==FTS5_STRING ){
|
||||
int iPhrase;
|
||||
for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
|
||||
pNear->apPhrase[iPhrase]->pNode = pRet;
|
||||
if( pNear->apPhrase[iPhrase]->nTerm==0 ){
|
||||
pRet->xNext = 0;
|
||||
pRet->eType = FTS5_EOF;
|
||||
if( pRet ){
|
||||
pRet->eType = eType;
|
||||
pRet->pNear = pNear;
|
||||
fts5ExprAssignXNext(pRet);
|
||||
if( eType==FTS5_STRING ){
|
||||
int iPhrase;
|
||||
for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
|
||||
pNear->apPhrase[iPhrase]->pNode = pRet;
|
||||
if( pNear->apPhrase[iPhrase]->nTerm==0 ){
|
||||
pRet->xNext = 0;
|
||||
pRet->eType = FTS5_EOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
|
||||
if( pNear->nPhrase!=1
|
||||
|| pPhrase->nTerm>1
|
||||
|| (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
|
||||
){
|
||||
assert( pParse->rc==SQLITE_OK );
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
assert( pParse->zErr==0 );
|
||||
pParse->zErr = sqlite3_mprintf(
|
||||
"fts5: %s queries are not supported (detail!=full)",
|
||||
pNear->nPhrase==1 ? "phrase": "NEAR"
|
||||
);
|
||||
sqlite3_free(pRet);
|
||||
pRet = 0;
|
||||
if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
|
||||
if( pNear->nPhrase!=1
|
||||
|| pPhrase->nTerm>1
|
||||
|| (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
|
||||
){
|
||||
assert( pParse->rc==SQLITE_OK );
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
assert( pParse->zErr==0 );
|
||||
pParse->zErr = sqlite3_mprintf(
|
||||
"fts5: %s queries are not supported (detail!=full)",
|
||||
pNear->nPhrase==1 ? "phrase": "NEAR"
|
||||
);
|
||||
sqlite3_free(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
fts5ExprAddChildren(pRet, pLeft);
|
||||
fts5ExprAddChildren(pRet, pRight);
|
||||
}
|
||||
}else{
|
||||
fts5ExprAddChildren(pRet, pLeft);
|
||||
fts5ExprAddChildren(pRet, pRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2401,8 +2555,17 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
|
||||
int iTerm;
|
||||
|
||||
if( pNear->pColset ){
|
||||
int iCol = pNear->pColset->aiCol[0];
|
||||
zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]);
|
||||
int ii;
|
||||
Fts5Colset *pColset = pNear->pColset;
|
||||
if( pColset->nCol>1 ) zRet = fts5PrintfAppend(zRet, "{");
|
||||
for(ii=0; ii<pColset->nCol; ii++){
|
||||
zRet = fts5PrintfAppend(zRet, "%s%s",
|
||||
pConfig->azCol[pColset->aiCol[ii]], ii==pColset->nCol-1 ? "" : " "
|
||||
);
|
||||
}
|
||||
if( zRet ){
|
||||
zRet = fts5PrintfAppend(zRet, "%s : ", pColset->nCol>1 ? "}" : "");
|
||||
}
|
||||
if( zRet==0 ) return 0;
|
||||
}
|
||||
|
||||
@@ -2525,7 +2688,7 @@ static void fts5ExprFunction(
|
||||
|
||||
rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr);
|
||||
rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
char *zText;
|
||||
|
||||
@@ -306,7 +306,6 @@ int sqlite3Fts5HashWrite(
|
||||
p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
|
||||
}
|
||||
|
||||
nIncr += p->nData;
|
||||
}else{
|
||||
|
||||
/* Appending to an existing hash-entry. Check that there is enough
|
||||
@@ -339,8 +338,9 @@ int sqlite3Fts5HashWrite(
|
||||
/* If this is a new rowid, append the 4-byte size field for the previous
|
||||
** entry, and the new rowid for this entry. */
|
||||
if( iRowid!=p->iRowid ){
|
||||
u64 iDiff = (u64)iRowid - (u64)p->iRowid;
|
||||
fts5HashAddPoslistSize(pHash, p, 0);
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff);
|
||||
p->iRowid = iRowid;
|
||||
bNew = 1;
|
||||
p->iSzPoslist = p->nData;
|
||||
|
||||
+13
-9
@@ -1754,7 +1754,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
|
||||
ASSERT_SZLEAF_OK(pIter->pLeaf);
|
||||
while( 1 ){
|
||||
i64 iDelta = 0;
|
||||
u64 iDelta = 0;
|
||||
|
||||
if( eDetail==FTS5_DETAIL_NONE ){
|
||||
/* todo */
|
||||
@@ -1769,7 +1769,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
i += nPos;
|
||||
}
|
||||
if( i>=n ) break;
|
||||
i += fts5GetVarint(&a[i], (u64*)&iDelta);
|
||||
i += fts5GetVarint(&a[i], &iDelta);
|
||||
pIter->iRowid += iDelta;
|
||||
|
||||
/* If necessary, grow the pIter->aRowidOffset[] array. */
|
||||
@@ -1868,7 +1868,7 @@ static void fts5SegIterNext_Reverse(
|
||||
if( pIter->iRowidOffset>0 ){
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
int iOff;
|
||||
i64 iDelta;
|
||||
u64 iDelta;
|
||||
|
||||
pIter->iRowidOffset--;
|
||||
pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset];
|
||||
@@ -1877,7 +1877,7 @@ static void fts5SegIterNext_Reverse(
|
||||
if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){
|
||||
iOff += pIter->nPos;
|
||||
}
|
||||
fts5GetVarint(&a[iOff], (u64*)&iDelta);
|
||||
fts5GetVarint(&a[iOff], &iDelta);
|
||||
pIter->iRowid -= iDelta;
|
||||
}else{
|
||||
fts5SegIterReverseNewPage(p, pIter);
|
||||
@@ -4997,7 +4997,9 @@ static void fts5MergePrefixLists(
|
||||
** at most 20 bytes of unexpected space. */
|
||||
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
|
||||
fts5BufferZero(&tmp);
|
||||
sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10);
|
||||
sqlite3Fts5BufferSize(&p->rc, &tmp,
|
||||
i1.nPoslist + i2.nPoslist + 10 + 10 + FTS5_DATA_ZERO_PADDING
|
||||
);
|
||||
if( p->rc ) break;
|
||||
|
||||
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
|
||||
@@ -5071,9 +5073,10 @@ static void fts5MergePrefixLists(
|
||||
}
|
||||
assert_nc( out.n<=(p1->n+p2->n+9) );
|
||||
|
||||
fts5BufferSet(&p->rc, p1, out.n, out.p);
|
||||
fts5BufferFree(p1);
|
||||
fts5BufferFree(&tmp);
|
||||
fts5BufferFree(&out);
|
||||
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
|
||||
*p1 = out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6059,7 +6062,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
** error, or some other SQLite error code if another error (e.g. OOM)
|
||||
** occurs.
|
||||
*/
|
||||
int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
|
||||
int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
|
||||
int eDetail = p->pConfig->eDetail;
|
||||
u64 cksum2 = 0; /* Checksum based on contents of indexes */
|
||||
Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
|
||||
@@ -6120,6 +6123,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
|
||||
}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);
|
||||
@@ -6130,7 +6134,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
|
||||
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
|
||||
|
||||
fts5MultiIterFree(pIter);
|
||||
if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
|
||||
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
|
||||
|
||||
fts5StructureRelease(pStruct);
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
||||
+80
-37
@@ -464,6 +464,23 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
|
||||
#endif
|
||||
}
|
||||
|
||||
static int fts5UsePatternMatch(
|
||||
Fts5Config *pConfig,
|
||||
struct sqlite3_index_constraint *p
|
||||
){
|
||||
assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
|
||||
assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
|
||||
if( pConfig->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){
|
||||
return 1;
|
||||
}
|
||||
if( pConfig->ePattern==FTS5_PATTERN_LIKE
|
||||
&& (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the xBestIndex method for FTS5 tables. Within the
|
||||
** WHERE constraint, it searches for the following:
|
||||
@@ -493,7 +510,9 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
|
||||
**
|
||||
** Match against table column: "m"
|
||||
** Match against rank column: "r"
|
||||
** Match against other column: "<column-number>"
|
||||
** Match against other column: "M<column-number>"
|
||||
** LIKE against other column: "L<column-number>"
|
||||
** GLOB against other column: "G<column-number>"
|
||||
** Equality constraint against the rowid: "="
|
||||
** A < or <= against the rowid: "<"
|
||||
** A > or >= against the rowid: ">"
|
||||
@@ -554,7 +573,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1);
|
||||
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
|
||||
if( idxStr==0 ) return SQLITE_NOMEM;
|
||||
pInfo->idxStr = idxStr;
|
||||
pInfo->needToFreeIdxStr = 1;
|
||||
@@ -578,25 +597,29 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
if( bSeenRank ) continue;
|
||||
idxStr[iIdxStr++] = 'r';
|
||||
bSeenRank = 1;
|
||||
}else{
|
||||
}else if( iCol>=0 ){
|
||||
bSeenMatch = 1;
|
||||
idxStr[iIdxStr++] = 'm';
|
||||
if( iCol<nCol ){
|
||||
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
|
||||
idxStr += strlen(&idxStr[iIdxStr]);
|
||||
assert( idxStr[iIdxStr]=='\0' );
|
||||
}
|
||||
idxStr[iIdxStr++] = 'M';
|
||||
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
|
||||
idxStr += strlen(&idxStr[iIdxStr]);
|
||||
assert( idxStr[iIdxStr]=='\0' );
|
||||
}
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
pInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
}
|
||||
else if( p->usable && bSeenEq==0
|
||||
&& p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0
|
||||
){
|
||||
idxStr[iIdxStr++] = '=';
|
||||
bSeenEq = 1;
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
}else if( p->usable ){
|
||||
if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){
|
||||
assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB );
|
||||
idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G';
|
||||
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
|
||||
idxStr += strlen(&idxStr[iIdxStr]);
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
assert( idxStr[iIdxStr]=='\0' );
|
||||
}else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
|
||||
idxStr[iIdxStr++] = '=';
|
||||
bSeenEq = 1;
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1229,19 +1252,14 @@ static int fts5FilterMethod(
|
||||
case 'r':
|
||||
pRank = apVal[i];
|
||||
break;
|
||||
case 'm': {
|
||||
case 'M': {
|
||||
const char *zText = (const char*)sqlite3_value_text(apVal[i]);
|
||||
if( zText==0 ) zText = "";
|
||||
|
||||
if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){
|
||||
iCol = 0;
|
||||
do{
|
||||
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
|
||||
iIdxStr++;
|
||||
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
|
||||
}else{
|
||||
iCol = pConfig->nCol;
|
||||
}
|
||||
iCol = 0;
|
||||
do{
|
||||
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
|
||||
iIdxStr++;
|
||||
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
|
||||
|
||||
if( zText[0]=='*' ){
|
||||
/* The user has issued a query of the form "MATCH '*...'". This
|
||||
@@ -1251,7 +1269,7 @@ static int fts5FilterMethod(
|
||||
goto filter_out;
|
||||
}else{
|
||||
char **pzErr = &pTab->p.base.zErrMsg;
|
||||
rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr);
|
||||
rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
|
||||
pExpr = 0;
|
||||
@@ -1261,6 +1279,25 @@ static int fts5FilterMethod(
|
||||
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
case 'G': {
|
||||
int bGlob = (idxStr[iIdxStr-1]=='G');
|
||||
const char *zText = (const char*)sqlite3_value_text(apVal[i]);
|
||||
iCol = 0;
|
||||
do{
|
||||
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
|
||||
iIdxStr++;
|
||||
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
|
||||
if( zText ){
|
||||
rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
|
||||
pExpr = 0;
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto filter_out;
|
||||
break;
|
||||
}
|
||||
case '=':
|
||||
pRowidEq = apVal[i];
|
||||
break;
|
||||
@@ -1508,7 +1545,8 @@ static int fts5SpecialInsert(
|
||||
int nMerge = sqlite3_value_int(pVal);
|
||||
rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
|
||||
}else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
|
||||
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
|
||||
int iArg = sqlite3_value_int(pVal);
|
||||
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg);
|
||||
#ifdef SQLITE_DEBUG
|
||||
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
|
||||
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
|
||||
@@ -2671,8 +2709,7 @@ int sqlite3Fts5GetTokenizer(
|
||||
Fts5Global *pGlobal,
|
||||
const char **azArg,
|
||||
int nArg,
|
||||
Fts5Tokenizer **ppTok,
|
||||
fts5_tokenizer **ppTokApi,
|
||||
Fts5Config *pConfig,
|
||||
char **pzErr
|
||||
){
|
||||
Fts5TokenizerModule *pMod;
|
||||
@@ -2684,16 +2721,22 @@ int sqlite3Fts5GetTokenizer(
|
||||
rc = SQLITE_ERROR;
|
||||
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
|
||||
}else{
|
||||
rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok);
|
||||
*ppTokApi = &pMod->x;
|
||||
if( rc!=SQLITE_OK && pzErr ){
|
||||
*pzErr = sqlite3_mprintf("error in tokenizer constructor");
|
||||
rc = pMod->x.xCreate(
|
||||
pMod->pUserData, &azArg[1], (nArg?nArg-1:0), &pConfig->pTok
|
||||
);
|
||||
pConfig->pTokApi = &pMod->x;
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
|
||||
}else{
|
||||
pConfig->ePattern = sqlite3Fts5TokenizerPattern(
|
||||
pMod->x.xCreate, pConfig->pTok
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
*ppTokApi = 0;
|
||||
*ppTok = 0;
|
||||
pConfig->pTokApi = 0;
|
||||
pConfig->pTok = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
+78
-65
@@ -429,9 +429,16 @@ static int fts5StorageDeleteFromIndex(
|
||||
zText, nText, (void*)&ctx, fts5StorageInsertCallback
|
||||
);
|
||||
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
|
||||
if( p->aTotalSize[iCol-1]<0 ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
p->nTotalRow--;
|
||||
if( rc==SQLITE_OK && p->nTotalRow<1 ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}else{
|
||||
p->nTotalRow--;
|
||||
}
|
||||
|
||||
rc2 = sqlite3_reset(pSeek);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
@@ -874,13 +881,14 @@ static int fts5StorageIntegrityCallback(
|
||||
** some other SQLite error code if an error occurs while attempting to
|
||||
** determine this.
|
||||
*/
|
||||
int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
|
||||
int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc; /* Return code */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int *aColSize; /* Array of size pConfig->nCol */
|
||||
i64 *aTotalSize; /* Array of size pConfig->nCol */
|
||||
Fts5IntegrityCtx ctx;
|
||||
sqlite3_stmt *pScan;
|
||||
int bUseCksum;
|
||||
|
||||
memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
|
||||
ctx.pConfig = p->pConfig;
|
||||
@@ -889,83 +897,88 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
|
||||
aColSize = (int*)&aTotalSize[pConfig->nCol];
|
||||
memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
|
||||
|
||||
/* Generate the expected index checksum based on the contents of the
|
||||
** %_content table. This block stores the checksum in ctx.cksum. */
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
while( SQLITE_ROW==sqlite3_step(pScan) ){
|
||||
int i;
|
||||
ctx.iRowid = sqlite3_column_int64(pScan, 0);
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->bColumnsize ){
|
||||
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i] ) continue;
|
||||
ctx.iCol = i;
|
||||
bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL
|
||||
|| (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg)
|
||||
);
|
||||
if( bUseCksum ){
|
||||
/* Generate the expected index checksum based on the contents of the
|
||||
** %_content table. This block stores the checksum in ctx.cksum. */
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
while( SQLITE_ROW==sqlite3_step(pScan) ){
|
||||
int i;
|
||||
ctx.iRowid = sqlite3_column_int64(pScan, 0);
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
if( pConfig->bColumnsize ){
|
||||
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zText = (const char*)sqlite3_column_text(pScan, i+1);
|
||||
int nText = sqlite3_column_bytes(pScan, i+1);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
aTotalSize[i] += ctx.szCol;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i] ) continue;
|
||||
ctx.iCol = i;
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zText = (const char*)sqlite3_column_text(pScan, i+1);
|
||||
int nText = sqlite3_column_bytes(pScan, i+1);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
aTotalSize[i] += ctx.szCol;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
}
|
||||
}
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc2 = sqlite3_reset(pScan);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
rc2 = sqlite3_reset(pScan);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
|
||||
/* Test that the "totals" (sometimes called "averages") record looks Ok */
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
rc = fts5StorageLoadTotals(p, 0);
|
||||
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
|
||||
if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
|
||||
/* Test that the "totals" (sometimes called "averages") record looks Ok */
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
rc = fts5StorageLoadTotals(p, 0);
|
||||
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
|
||||
if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that the %_docsize and %_content tables contain the expected
|
||||
** number of rows. */
|
||||
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
i64 nRow = 0;
|
||||
rc = fts5StorageCount(p, "content", &nRow);
|
||||
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize ){
|
||||
i64 nRow = 0;
|
||||
rc = fts5StorageCount(p, "docsize", &nRow);
|
||||
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
|
||||
/* Check that the %_docsize and %_content tables contain the expected
|
||||
** number of rows. */
|
||||
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
i64 nRow = 0;
|
||||
rc = fts5StorageCount(p, "content", &nRow);
|
||||
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize ){
|
||||
i64 nRow = 0;
|
||||
rc = fts5StorageCount(p, "docsize", &nRow);
|
||||
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass the expected checksum down to the FTS index module. It will
|
||||
** verify, amongst other things, that it matches the checksum generated by
|
||||
** inspecting the index itself. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
|
||||
rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum);
|
||||
}
|
||||
|
||||
sqlite3_free(aTotalSize);
|
||||
|
||||
@@ -1258,6 +1258,133 @@ static int fts5PorterTokenize(
|
||||
);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
** Start of trigram implementation.
|
||||
*/
|
||||
typedef struct TrigramTokenizer TrigramTokenizer;
|
||||
struct TrigramTokenizer {
|
||||
int bFold; /* True to fold to lower-case */
|
||||
};
|
||||
|
||||
/*
|
||||
** Free a trigram tokenizer.
|
||||
*/
|
||||
static void fts5TriDelete(Fts5Tokenizer *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a trigram tokenizer.
|
||||
*/
|
||||
static int fts5TriCreate(
|
||||
void *pUnused,
|
||||
const char **azArg,
|
||||
int nArg,
|
||||
Fts5Tokenizer **ppOut
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
|
||||
UNUSED_PARAM(pUnused);
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int i;
|
||||
pNew->bFold = 1;
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNew->bFold = (zArg[0]=='0');
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5TriDelete((Fts5Tokenizer*)pNew);
|
||||
pNew = 0;
|
||||
}
|
||||
}
|
||||
*ppOut = (Fts5Tokenizer*)pNew;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Trigram tokenizer tokenize routine.
|
||||
*/
|
||||
static int fts5TriTokenize(
|
||||
Fts5Tokenizer *pTok,
|
||||
void *pCtx,
|
||||
int unusedFlags,
|
||||
const char *pText, int nText,
|
||||
int (*xToken)(void*, int, const char*, int, int, int)
|
||||
){
|
||||
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
|
||||
int rc = SQLITE_OK;
|
||||
char aBuf[32];
|
||||
const unsigned char *zIn = (const unsigned char*)pText;
|
||||
const unsigned char *zEof = &zIn[nText];
|
||||
u32 iCode;
|
||||
|
||||
UNUSED_PARAM(unusedFlags);
|
||||
while( 1 ){
|
||||
char *zOut = aBuf;
|
||||
int iStart = zIn - (const unsigned char*)pText;
|
||||
const unsigned char *zNext;
|
||||
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
zNext = zIn;
|
||||
if( zIn<zEof ){
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
if( zIn<zEof ){
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
zIn = zNext;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument xCreate is a pointer to a constructor function for a tokenizer.
|
||||
** pTok is a tokenizer previously created using the same method. This function
|
||||
** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB
|
||||
** indicating the style of pattern matching that the tokenizer can support.
|
||||
** In practice, this is:
|
||||
**
|
||||
** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB
|
||||
** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE
|
||||
** all other tokenizers - FTS5_PATTERN_NONE
|
||||
*/
|
||||
int sqlite3Fts5TokenizerPattern(
|
||||
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
|
||||
Fts5Tokenizer *pTok
|
||||
){
|
||||
if( xCreate==fts5TriCreate ){
|
||||
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
|
||||
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
|
||||
}
|
||||
return FTS5_PATTERN_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register all built-in tokenizers with FTS5.
|
||||
*/
|
||||
@@ -1269,6 +1396,7 @@ int sqlite3Fts5TokenizerInit(fts5_api *pApi){
|
||||
{ "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
|
||||
{ "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
|
||||
{ "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
|
||||
{ "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}},
|
||||
};
|
||||
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
|
||||
@@ -773,4 +773,6 @@ void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
|
||||
}
|
||||
iTbl++;
|
||||
}
|
||||
aAscii[0] = 0; /* 0x00 is never a token character */
|
||||
}
|
||||
|
||||
|
||||
@@ -82,5 +82,24 @@ foreach_detail_mode $::testprefix {
|
||||
} {1 {unable to use function MATCH in the requested context}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Confirm that the expression parser creates the same expression tree
|
||||
# for:
|
||||
#
|
||||
# {a b} : (abc AND def)
|
||||
# -{c d} : (abc AND def)
|
||||
#
|
||||
# Assuming that the table columns are (a, b, c, d).
|
||||
#
|
||||
do_execsql_test 5.1 {
|
||||
SELECT fts5_expr('abcd AND cdef');
|
||||
} {{"abcd" AND "cdef"}}
|
||||
do_execsql_test 5.2 {
|
||||
SELECT fts5_expr('{a b} : (abcd AND cdef)', 'a', 'b', 'c', 'd');
|
||||
} {{{a b} : "abcd" AND {a b} : "cdef"}}
|
||||
do_execsql_test 5.3 {
|
||||
SELECT fts5_expr('-{c d} : (abcd AND cdef)', 'a', 'b', 'c', 'd');
|
||||
} {{{a b} : "abcd" AND {a b} : "cdef"}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -9691,7 +9691,7 @@ do_test 65.0 {
|
||||
|
||||
do_catchsql_test 65.1 {
|
||||
SELECT ( MATCH (t1,591)) FROM t1 WHERE t1 MATCH 'e*eŸ'
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {malformed database schema (t2) - invalid rootpage}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -10324,6 +10324,190 @@ do_catchsql_test 69.2 {
|
||||
SELECT * FROM t1 WHERE a MATCH 'fx*'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 70.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename sql022250.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 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 15 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 4d 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 IMTEGER PRIMARY
|
||||
| 3728: 4b 45 59 2c 20 73 7a 20 42 4c 4f 20 29 5d 04 07 KEY, sz BLO )]..
|
||||
| 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 54 00 05 0e 81 00 0f e7 0e 81 0f af 0f 58 ..T............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 14 03 03 08 03 03 08 03 bf ................
|
||||
| 3792: 07 f2 f3 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 15 04 05 05 01 01 03 06 04 04 06 04 04 a1 04 ................
|
||||
| 3840: 04 06 04 03 03 01 65 03 14 04 05 07 05 05 01 01 ......e.........
|
||||
| 3856: 02 09 0a 01 20 04 05 07 05 07 05 07 05 05 01 01 .... ...........
|
||||
| 3872: 02 0f da 0a 0a 04 01 64 f3 02 0a 01 06 0a 0a 0a .......d........
|
||||
| 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 01 30 65 02 1a 02 05 05 07 ..$...G.0e......
|
||||
| 3952: 05 01 01 04 03 03 08 03 03 02 01 65 02 1d f3 05 ...........e....
|
||||
| 3968: 05 04 05 05 01 01 04 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 03 02 01 66 01 12 03 ............f...
|
||||
| 4048: 05 05 01 01 03 06 04 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 01 01 00 01 01 01 01 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 2f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ./..............
|
||||
| page 4 offset 12288
|
||||
| 3344: 00 00 00 00 00 00 00 00 00 00 81 52 04 06 10 81 ...........R....
|
||||
| 3360: 5d 81 55 65 20 65 65 20 65 65 65 20 65 20 65 65 ].Ue ee eee e ee
|
||||
| 3376: 20 65 65 65 20 65 20 65 65 20 65 65 65 65 20 65 eee e ee eeee e
|
||||
| 3392: 65 20 65 65 65 24 a5 20 65 65 20 65 65 65 20 65 e eee$. ee eee e
|
||||
| 3408: 24 05 65 20 65 65 65 65 20 65 65 20 65 65 65 20 $.e eeee ee eee
|
||||
| 3424: 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 e ee eee e ee ee
|
||||
| 3440: 65 65 20 65 65 20 65 65 65 20 65 20 65 65 10 65 ee ee eee e ee.e
|
||||
| 3456: 65 65 20 65 20 65 65 20 65 65 65 65 65 65 20 65 ee e ee eeeeee e
|
||||
| 3472: 65 20 65 20 65 20 65 20 65 65 20 65 65 65 20 65 e e e e ee eee e
|
||||
| 3488: 1f 20 65 65 66 65 65 20 65 65 20 65 20 65 20 2d . eefee ee e e -
|
||||
| page 5 offset 16384
|
||||
| 4064: 00 00 00 00 05 04 d0 00 10 21 21 05 03 03 00 10 .........!!.....
|
||||
| 4080: 11 11 05 02 03 00 10 11 10 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 sql022250.txt.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 70.1 {
|
||||
SELECT snippet(ttt, -1, '', '','','>')FROM ttt('e* NOT ee*e* NOT ee*');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
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}}
|
||||
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -50,4 +50,47 @@ do_test 1.2 {
|
||||
}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE test (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT,
|
||||
value TEXT
|
||||
);
|
||||
CREATE VIRTUAL TABLE test_idx USING fts5(
|
||||
name, content=test, content_rowid=id
|
||||
);
|
||||
}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 1, 'quick');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
INSERT INTO test_idx(rowid, name) VALUES(123, 'one one one');
|
||||
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
|
||||
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
DROP TABLE test_idx;
|
||||
CREATE VIRTUAL TABLE test_idx USING fts5(
|
||||
name, content=test, content_rowid=id
|
||||
);
|
||||
|
||||
INSERT INTO test_idx(rowid, name) VALUES(123, 'one one one');
|
||||
INSERT INTO test_idx(rowid, name) VALUES(124, 'two two two');
|
||||
INSERT INTO test_idx(rowid, name) VALUES(125, 'two two two');
|
||||
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
|
||||
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
|
||||
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
|
||||
}
|
||||
|
||||
do_catchsql_test 2.4 {
|
||||
SELECT rowid FROM test_idx WHERE test_idx MATCH 'two' ORDER BY rank;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5faultA
|
||||
set testprefix fts5faultD
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# 2016 February 2
|
||||
#
|
||||
# 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 is focused on OOM errors.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5faultE
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING fts5(x, y, tokenize=trigram) }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, tokenize=trigram);
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 2 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES('abcdefghijklmnopqrstuvwxyz', NULL);
|
||||
SELECT count(*) FROM t1 WHERE x LIKE '%mnop%' AND t1 MATCH 'jkl';
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 1} {1 {vtable constructor failed: t1}}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, tokenize=trigram, detail=none);
|
||||
INSERT INTO t1 VALUES('abcdefghijklmnopqrstuvwxyz', NULL);
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1 WHERE x LIKE '%mnopqrs%' AND t1 MATCH 'abc'
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 1} {1 {vtable constructor failed: t1}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -112,7 +112,7 @@ foreach_detail_mode $testprefix {
|
||||
# Add a small and very large token with the same hash value to an
|
||||
# empty table. At one point this would provoke an asan error.
|
||||
#
|
||||
do_test 2.0 {
|
||||
do_test 1.5 {
|
||||
set big [string repeat 12345 40]
|
||||
set hash [sqlite3_fts5_token_hash 1024 $big]
|
||||
while {1} {
|
||||
@@ -128,4 +128,41 @@ foreach_detail_mode $testprefix {
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('hashsize', 1024);
|
||||
INSERT INTO t1(t1, rank) VALUES('automerge', 0);
|
||||
INSERT INTO t1(t1, rank) VALUES('crisismerge', 1000);
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('abc def ghi');
|
||||
SELECT count(*) FROM t1_data;
|
||||
} {2}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1024
|
||||
)
|
||||
INSERT INTO t1 SELECT 'abc def ghi' FROM s;
|
||||
SELECT (SELECT count(*) FROM t1_data) > 10;
|
||||
} {1}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
COMMIT;
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('hashsize', 1024);
|
||||
INSERT INTO t1(t1, rank) VALUES('automerge', 0);
|
||||
INSERT INTO t1(t1, rank) VALUES('crisismerge', 1000);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1024
|
||||
)
|
||||
INSERT INTO t1 SELECT 'abc' || i || ' def' || i || ' ghi' || i FROM s;
|
||||
SELECT (SELECT count(*) FROM t1_data) > 100;
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -273,7 +273,6 @@ do_execsql_test 9.1.2 {
|
||||
SELECT quote(x'37');
|
||||
} {X'37'}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 9.2 {
|
||||
INSERT INTO vt0 VALUES (SUBSTR(x'37', 0));
|
||||
-- INSERT INTO vt0 VALUES (x'37');
|
||||
@@ -282,4 +281,40 @@ do_execsql_test 9.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 10.0 {
|
||||
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(a, b, content=t1);
|
||||
INSERT INTO vt0(rowid, a, b) VALUES(1, 'abc', 'def');
|
||||
}
|
||||
do_catchsql_test 10.1 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
} {0 {}}
|
||||
do_catchsql_test 10.2 {
|
||||
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
|
||||
} {0 {}}
|
||||
do_catchsql_test 10.3 {
|
||||
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
|
||||
} {1 {database disk image is malformed}}
|
||||
do_catchsql_test 10.3 {
|
||||
INSERT INTO t1 VALUES(1, 'abc', 'def');
|
||||
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 10.4 {
|
||||
CREATE VIRTUAL TABLE vt1 USING fts5(a, b, content=);
|
||||
INSERT INTO vt1(rowid, a, b) VALUES(1, 'abc', 'def');
|
||||
}
|
||||
|
||||
do_catchsql_test 10.5.1 {
|
||||
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
|
||||
} {0 {}}
|
||||
do_catchsql_test 10.5.2 {
|
||||
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
|
||||
} {0 {}}
|
||||
do_catchsql_test 10.5.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
} {0 {}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -31,7 +31,7 @@ do_eqp_test 1.1 {
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN TABLE t1
|
||||
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
|
||||
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:M1
|
||||
}
|
||||
|
||||
do_eqp_test 1.2 {
|
||||
@@ -46,7 +46,7 @@ do_eqp_test 1.3 {
|
||||
SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
|
||||
|--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:M1
|
||||
`--USE TEMP B-TREE FOR ORDER BY
|
||||
}
|
||||
|
||||
|
||||
@@ -111,5 +111,40 @@ do_catchsql_test 2.1 {
|
||||
SELECT * FROM t4;
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Embedded 0x00 characters.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(z);
|
||||
CREATE VIRTUAL TABLE tt USING fts5vocab(t1, 'instance');
|
||||
INSERT INTO t1 VALUES('abc' || char(0) || 'def');
|
||||
SELECT * FROM tt;
|
||||
} { abc 1 z 0 def 1 z 1 }
|
||||
do_execsql_test 3.1.1 {
|
||||
SELECT hex(z) FROM t1;
|
||||
} {61626300646566}
|
||||
do_execsql_test 3.1.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
do_execsql_test 3.2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(z,
|
||||
tokenize="unicode61 categories 'L* N* Co Cc'"
|
||||
);
|
||||
CREATE VIRTUAL TABLE tu USING fts5vocab(t2, 'instance');
|
||||
|
||||
INSERT INTO t2 VALUES('abc' || char(0) || 'def');
|
||||
SELECT * FROM tu;
|
||||
} { abc 1 z 0 def 1 z 1 }
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT hex(z) FROM t1;
|
||||
} {61626300646566}
|
||||
|
||||
do_execsql_test 3.2.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
# 2020 September 30
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# Tests for the fts5 "trigram" tokenizer.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
ifcapable !fts5 { finish_test ; return }
|
||||
set ::testprefix fts5trigram
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize=trigram);
|
||||
INSERT INTO t1 VALUES('abcdefghijklm');
|
||||
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
|
||||
}
|
||||
|
||||
foreach {tn s res} {
|
||||
1 abc "(abc)defghijklm"
|
||||
2 defgh "abc(defgh)ijklm"
|
||||
3 abcdefghijklm "(abcdefghijklm)"
|
||||
4 กรุ "(กรุ)งเทพมหานคร"
|
||||
5 งเทพมห "กรุ(งเทพมห)านคร"
|
||||
6 กรุงเทพมหานคร "(กรุงเทพมหานคร)"
|
||||
7 Abc "(abc)defghijklm"
|
||||
8 deFgh "abc(defgh)ijklm"
|
||||
9 aBcdefGhijKlm "(abcdefghijklm)"
|
||||
} {
|
||||
do_execsql_test 1.1.$tn {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1($s)
|
||||
} $res
|
||||
}
|
||||
|
||||
do_execsql_test 1.2.0 {
|
||||
SELECT fts5_expr('ABCD', 'tokenize=trigram')
|
||||
} {{"abc" + "bcd"}}
|
||||
|
||||
do_execsql_test 1.2.1 {
|
||||
SELECT * FROM t1 WHERE y LIKE ? ESCAPE 'a'
|
||||
}
|
||||
|
||||
foreach {tn like res} {
|
||||
1 {%cDef%} 1
|
||||
2 {cDef%} {}
|
||||
3 {%f%} 1
|
||||
4 {%f_h%} 1
|
||||
5 {%f_g%} {}
|
||||
6 {abc%klm} 1
|
||||
7 {ABCDEFG%} 1
|
||||
8 {%รุงเ%} 2
|
||||
} {
|
||||
do_execsql_test 1.3.$tn {
|
||||
SELECT rowid FROM t1 WHERE y LIKE $like
|
||||
} $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize="trigram case_sensitive 1");
|
||||
INSERT INTO t1 VALUES('abcdefghijklm');
|
||||
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
|
||||
}
|
||||
|
||||
foreach {tn s res} {
|
||||
1 abc "(abc)defghijklm"
|
||||
2 defgh "abc(defgh)ijklm"
|
||||
3 abcdefghijklm "(abcdefghijklm)"
|
||||
4 กรุ "(กรุ)งเทพมหานคร"
|
||||
5 งเทพมห "กรุ(งเทพมห)านคร"
|
||||
6 กรุงเทพมหานคร "(กรุงเทพมหานคร)"
|
||||
7 Abc ""
|
||||
8 deFgh ""
|
||||
9 aBcdefGhijKlm ""
|
||||
} {
|
||||
do_execsql_test 2.1.$tn {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1($s)
|
||||
} $res
|
||||
}
|
||||
foreach {tn like res} {
|
||||
1 {%cDef%} 1
|
||||
2 {cDef%} {}
|
||||
3 {%f%} 1
|
||||
4 {%f_h%} 1
|
||||
5 {%f_g%} {}
|
||||
6 {abc%klm} 1
|
||||
7 {ABCDEFG%} 1
|
||||
8 {%รุงเ%} 2
|
||||
} {
|
||||
do_execsql_test 2.2.$tn {
|
||||
SELECT rowid FROM t1 WHERE y LIKE $like
|
||||
} $res
|
||||
}
|
||||
foreach {tn like res} {
|
||||
1 {*cdef*} 1
|
||||
2 {cdef*} {}
|
||||
3 {*f*} 1
|
||||
4 {*f?h*} 1
|
||||
5 {*f?g*} {}
|
||||
6 {abc*klm} 1
|
||||
7 {abcdefg*} 1
|
||||
8 {*รุงเ*} 2
|
||||
9 {abc[d]efg*} 1
|
||||
10 {abc[]d]efg*} 1
|
||||
11 {abc[^]d]efg*} {}
|
||||
12 {abc[^]XYZ]efg*} 1
|
||||
} {
|
||||
do_execsql_test 2.3.$tn {
|
||||
SELECT rowid FROM t1 WHERE y GLOB $like
|
||||
} $res
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.null.1 {
|
||||
SELECT rowid FROM t1 WHERE y LIKE NULL
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_catchsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE ttt USING fts5(c, tokenize="trigram case_sensitive 2");
|
||||
} {1 {error in tokenizer constructor}}
|
||||
do_catchsql_test 3.2 {
|
||||
CREATE VIRTUAL TABLE ttt USING fts5(c, tokenize="trigram case_sensitive 11");
|
||||
} {1 {error in tokenizer constructor}}
|
||||
do_catchsql_test 3.3 {
|
||||
CREATE VIRTUAL TABLE ttt USING fts5(c, "tokenize=trigram case_sensitive 1");
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t0 USING fts5(b, tokenize = "trigram");
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO t0 VALUES (x'000b01');
|
||||
}
|
||||
do_execsql_test 4.2 {
|
||||
INSERT INTO t0(t0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
foreach_detail_mode $::testprefix {
|
||||
foreach {ci} {0 1} {
|
||||
reset_db
|
||||
do_execsql_test 5.cs=$ci.0.1 "
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(
|
||||
y, tokenize=\"trigram case_sensitive $ci\", detail=%DETAIL%
|
||||
);
|
||||
"
|
||||
do_execsql_test 5.cs=$ci.0.2 {
|
||||
INSERT INTO t1 VALUES('abcdefghijklm');
|
||||
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
|
||||
}
|
||||
|
||||
foreach {tn like res} {
|
||||
1 {%cDef%} 1
|
||||
2 {cDef%} {}
|
||||
3 {%f%} 1
|
||||
4 {%f_h%} 1
|
||||
5 {%f_g%} {}
|
||||
6 {abc%klm} 1
|
||||
7 {ABCDEFG%} 1
|
||||
8 {%รุงเ%} 2
|
||||
} {
|
||||
do_execsql_test 5.cs=$ci.1.$tn {
|
||||
SELECT rowid FROM t1 WHERE y LIKE $like
|
||||
} $res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE ci0 USING fts5(x, tokenize="trigram");
|
||||
CREATE VIRTUAL TABLE ci1 USING fts5(x, tokenize="trigram case_sensitive 1");
|
||||
}
|
||||
|
||||
# LIKE and GLOB both work with case-insensitive tokenizers. Only GLOB works
|
||||
# with case-sensitive.
|
||||
do_eqp_test 6.1 {
|
||||
SELECT * FROM ci0 WHERE x LIKE ?
|
||||
} {VIRTUAL TABLE INDEX 0:L0}
|
||||
do_eqp_test 6.2 {
|
||||
SELECT * FROM ci0 WHERE x GLOB ?
|
||||
} {VIRTUAL TABLE INDEX 0:G0}
|
||||
do_eqp_test 6.3 {
|
||||
SELECT * FROM ci1 WHERE x LIKE ?
|
||||
} {{SCAN TABLE ci1 VIRTUAL TABLE INDEX 0:}}
|
||||
do_eqp_test 6.4 {
|
||||
SELECT * FROM ci1 WHERE x GLOB ?
|
||||
} {VIRTUAL TABLE INDEX 0:G0}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -12,6 +12,7 @@ proc process_cmdline {} {
|
||||
cmdline::process ::A $::argv {
|
||||
{fts5 "use fts5 (this is the default)"}
|
||||
{fts4 "use fts4"}
|
||||
{trigram "Use tokenize=trigram"}
|
||||
{colsize "10 10 10" "list of column sizes"}
|
||||
{tblname "t1" "table name to create"}
|
||||
{detail "full" "Fts5 detail mode to use"}
|
||||
@@ -175,6 +176,7 @@ proc create_table {} {
|
||||
set sql "CREATE VIRTUAL TABLE IF NOT EXISTS $A(tblname) USING $A(fts) ("
|
||||
append sql [join $cols ,]
|
||||
if {$A(fts)=="fts5"} { append sql ",detail=$A(detail)" }
|
||||
if {$A(trigram)} { append sql ",tokenize=trigram" }
|
||||
append sql ", prefix='$A(prefix)');"
|
||||
|
||||
db eval $sql
|
||||
|
||||
+156
-41
@@ -57,21 +57,32 @@ SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
|
||||
** Must exactly match the definitions in carray.h.
|
||||
*/
|
||||
#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
|
||||
#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
|
||||
#define CARRAY_DOUBLE 2 /* Data is doubles */
|
||||
#define CARRAY_TEXT 3 /* Data is char* */
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Allowed datatypes
|
||||
*/
|
||||
#define CARRAY_INT32 0
|
||||
#define CARRAY_INT64 1
|
||||
#define CARRAY_DOUBLE 2
|
||||
#define CARRAY_TEXT 3
|
||||
|
||||
/*
|
||||
** Names of types
|
||||
** Names of allowed datatypes
|
||||
*/
|
||||
static const char *azType[] = { "int32", "int64", "double", "char*" };
|
||||
|
||||
/*
|
||||
** Structure used to hold the sqlite3_carray_bind() information
|
||||
*/
|
||||
typedef struct carray_bind carray_bind;
|
||||
struct carray_bind {
|
||||
void *aData; /* The data */
|
||||
int nData; /* Number of elements */
|
||||
int mFlags; /* Control flags */
|
||||
void (*xDel)(void*); /* Destructor for aData */
|
||||
};
|
||||
|
||||
|
||||
/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
|
||||
** serve as the underlying representation of a cursor that scans
|
||||
@@ -239,28 +250,39 @@ static int carrayFilter(
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
carray_cursor *pCur = (carray_cursor *)pVtabCursor;
|
||||
if( idxNum ){
|
||||
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
|
||||
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
|
||||
if( idxNum<3 ){
|
||||
pCur->eType = CARRAY_INT32;
|
||||
}else{
|
||||
unsigned char i;
|
||||
const char *zType = (const char*)sqlite3_value_text(argv[2]);
|
||||
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
|
||||
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
|
||||
}
|
||||
if( i>=sizeof(azType)/sizeof(azType[0]) ){
|
||||
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
|
||||
"unknown datatype: %Q", zType);
|
||||
return SQLITE_ERROR;
|
||||
}else{
|
||||
pCur->eType = i;
|
||||
}
|
||||
pCur->pPtr = 0;
|
||||
pCur->iCnt = 0;
|
||||
switch( idxNum ){
|
||||
case 1: {
|
||||
carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
|
||||
if( pBind==0 ) break;
|
||||
pCur->pPtr = pBind->aData;
|
||||
pCur->iCnt = pBind->nData;
|
||||
pCur->eType = pBind->mFlags & 0x03;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3: {
|
||||
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
|
||||
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
|
||||
if( idxNum<3 ){
|
||||
pCur->eType = CARRAY_INT32;
|
||||
}else{
|
||||
unsigned char i;
|
||||
const char *zType = (const char*)sqlite3_value_text(argv[2]);
|
||||
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
|
||||
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
|
||||
}
|
||||
if( i>=sizeof(azType)/sizeof(azType[0]) ){
|
||||
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
|
||||
"unknown datatype: %Q", zType);
|
||||
return SQLITE_ERROR;
|
||||
}else{
|
||||
pCur->eType = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
pCur->pPtr = 0;
|
||||
pCur->iCnt = 0;
|
||||
}
|
||||
pCur->iRowid = 1;
|
||||
return SQLITE_OK;
|
||||
@@ -275,9 +297,16 @@ static int carrayFilter(
|
||||
** In this implementation idxNum is used to represent the
|
||||
** query plan. idxStr is unused.
|
||||
**
|
||||
** idxNum is 2 if the pointer= and count= constraints exist,
|
||||
** 3 if the ctype= constraint also exists, and is 0 otherwise.
|
||||
** If idxNum is 0, then carray becomes an empty table.
|
||||
** idxNum is:
|
||||
**
|
||||
** 1 If only the pointer= constraint exists. In this case, the
|
||||
** parameter must be bound using sqlite3_carray_bind().
|
||||
**
|
||||
** 2 if the pointer= and count= constraints exist.
|
||||
**
|
||||
** 3 if the ctype= constraint also exists.
|
||||
**
|
||||
** idxNum is 0 otherwise and carray becomes an empty table.
|
||||
*/
|
||||
static int carrayBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
@@ -305,18 +334,21 @@ static int carrayBestIndex(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( ptrIdx>=0 && cntIdx>=0 ){
|
||||
if( ptrIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
|
||||
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
|
||||
pIdxInfo->estimatedCost = (double)1;
|
||||
pIdxInfo->estimatedRows = 100;
|
||||
pIdxInfo->idxNum = 2;
|
||||
if( ctypeIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
|
||||
pIdxInfo->idxNum = 3;
|
||||
pIdxInfo->idxNum = 1;
|
||||
if( cntIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
|
||||
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
|
||||
pIdxInfo->idxNum = 2;
|
||||
if( ctypeIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
|
||||
pIdxInfo->idxNum = 3;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
pIdxInfo->estimatedCost = (double)2147483647;
|
||||
@@ -353,6 +385,89 @@ static sqlite3_module carrayModule = {
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Destructor for the carray_bind object
|
||||
*/
|
||||
static void carrayBindDel(void *pPtr){
|
||||
carray_bind *p = (carray_bind*)pPtr;
|
||||
if( p->xDel!=SQLITE_STATIC ){
|
||||
p->xDel(p->aData);
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke this interface in order to bind to the single-argument
|
||||
** version of CARRAY().
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_carray_bind(
|
||||
sqlite3_stmt *pStmt,
|
||||
int idx,
|
||||
void *aData,
|
||||
int nData,
|
||||
int mFlags,
|
||||
void (*xDestroy)(void*)
|
||||
){
|
||||
carray_bind *pNew;
|
||||
int i;
|
||||
pNew = sqlite3_malloc64(sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
|
||||
xDestroy(aData);
|
||||
}
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pNew->nData = nData;
|
||||
pNew->mFlags = mFlags;
|
||||
if( xDestroy==SQLITE_TRANSIENT ){
|
||||
sqlite3_int64 sz = nData;
|
||||
switch( mFlags & 0x03 ){
|
||||
case CARRAY_INT32: sz *= 4; break;
|
||||
case CARRAY_INT64: sz *= 8; break;
|
||||
case CARRAY_DOUBLE: sz *= 8; break;
|
||||
case CARRAY_TEXT: sz *= sizeof(char*); break;
|
||||
}
|
||||
if( (mFlags & 0x03)==CARRAY_TEXT ){
|
||||
for(i=0; i<nData; i++){
|
||||
const char *z = ((char**)aData)[i];
|
||||
if( z ) sz += strlen(z) + 1;
|
||||
}
|
||||
}
|
||||
pNew->aData = sqlite3_malloc64( sz );
|
||||
if( pNew->aData==0 ){
|
||||
sqlite3_free(pNew);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( (mFlags & 0x03)==CARRAY_TEXT ){
|
||||
char **az = (char**)pNew->aData;
|
||||
char *z = (char*)&az[nData];
|
||||
for(i=0; i<nData; i++){
|
||||
const char *zData = ((char**)aData)[i];
|
||||
sqlite3_int64 n;
|
||||
if( zData==0 ){
|
||||
az[i] = 0;
|
||||
continue;
|
||||
}
|
||||
az[i] = z;
|
||||
n = strlen(zData);
|
||||
memcpy(z, zData, n+1);
|
||||
z += n+1;
|
||||
}
|
||||
}else{
|
||||
memcpy(pNew->aData, aData, sz*nData);
|
||||
}
|
||||
pNew->xDel = sqlite3_free;
|
||||
}else{
|
||||
pNew->aData = aData;
|
||||
pNew->xDel = xDestroy;
|
||||
}
|
||||
return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** For testing purpose in the TCL test harness, we need a method for
|
||||
** setting the pointer value. The inttoptr(X) SQL function accomplishes
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
** Interface definitions for the CARRAY table-valued function
|
||||
** extension.
|
||||
*/
|
||||
|
||||
/* Use this interface to bind an array to the single-argument version
|
||||
** of CARRAY().
|
||||
*/
|
||||
int sqlite3_carray_bind(
|
||||
sqlite3_stmt *pStmt, /* Statement to be bound */
|
||||
int i, /* Parameter index */
|
||||
void *aData, /* Pointer to array data */
|
||||
int nData, /* Number of data elements */
|
||||
int mFlags, /* CARRAY flags */
|
||||
void (*xDel)(void*) /* Destructgor for aData*/
|
||||
);
|
||||
|
||||
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
|
||||
*/
|
||||
#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
|
||||
#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
|
||||
#define CARRAY_DOUBLE 2 /* Data is doubles */
|
||||
#define CARRAY_TEXT 3 /* Data is char* */
|
||||
+29
-8
@@ -54,11 +54,11 @@
|
||||
** statically linked against the application, initialize it using
|
||||
** a single API call as follows:
|
||||
**
|
||||
** sqlite3_cksumvfs_init();
|
||||
** sqlite3_register_cksumvfs();
|
||||
**
|
||||
** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new
|
||||
** default VFS and it uses the prior default VFS as the next VFS
|
||||
** down in the stack. This is normally what you want. However, it
|
||||
** down in the stack. This is normally what you want. However, in
|
||||
** complex situations where multiple VFS shims are being loaded,
|
||||
** it might be important to ensure that cksumvfs is loaded in the
|
||||
** correct order so that it sequences itself into the default VFS
|
||||
@@ -79,7 +79,7 @@
|
||||
** the reserve-bytes value to 8 by runing:
|
||||
**
|
||||
** int n = 8;
|
||||
** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVED_BYTES, &n);
|
||||
** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
|
||||
**
|
||||
** If you do this immediately after creating a new database file,
|
||||
** before anything else has been written into the file, then that
|
||||
@@ -157,6 +157,9 @@
|
||||
** value at offset 20 the SQLite database header is exactly 8. If
|
||||
** the reserved-space value is not 8, this module is a no-op.
|
||||
*/
|
||||
#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC)
|
||||
# define SQLITE_CKSUMVFS_STATIC
|
||||
#endif
|
||||
#ifdef SQLITE_CKSUMVFS_STATIC
|
||||
# include "sqlite3.h"
|
||||
#else
|
||||
@@ -176,7 +179,7 @@ typedef struct CksmFile CksmFile;
|
||||
/*
|
||||
** Useful datatype abbreviations
|
||||
*/
|
||||
#if !defined(SQLITE_CORE)
|
||||
#if !defined(SQLITE_AMALGAMATION)
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
#endif
|
||||
@@ -395,7 +398,9 @@ static int cksmRead(
|
||||
pFile = ORIGFILE(pFile);
|
||||
rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iOfst==0 && iAmt>=100 && memcmp(zBuf,"SQLite format 3",16)==0 ){
|
||||
if( iOfst==0 && iAmt>=100 && (
|
||||
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
|
||||
)){
|
||||
u8 *d = (u8*)zBuf;
|
||||
char hasCorrectReserveSize = (d[20]==8);
|
||||
cksmSetFlags(p, hasCorrectReserveSize);
|
||||
@@ -434,7 +439,9 @@ static int cksmWrite(
|
||||
){
|
||||
CksmFile *p = (CksmFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( iOfst==0 && iAmt>=100 && memcmp(zBuf,"SQLite format 3",16)==0 ){
|
||||
if( iOfst==0 && iAmt>=100 && (
|
||||
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
|
||||
)){
|
||||
u8 *d = (u8*)zBuf;
|
||||
char hasCorrectReserveSize = (d[20]==8);
|
||||
cksmSetFlags(p, hasCorrectReserveSize);
|
||||
@@ -604,13 +611,20 @@ static int cksmFetch(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
|
||||
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){
|
||||
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
|
||||
}
|
||||
*pp = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Release a memory-mapped page */
|
||||
static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
|
||||
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){
|
||||
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -763,6 +777,13 @@ int sqlite3_register_cksumvfs(const char *NotUsed){
|
||||
(void)NotUsed;
|
||||
return cksmRegisterVfs();
|
||||
}
|
||||
int sqlite3_unregister_cksumvfs(void){
|
||||
if( sqlite3_vfs_find("cksmvfs") ){
|
||||
sqlite3_vfs_unregister(&cksm_vfs);
|
||||
sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* defined(SQLITE_CKSUMVFS_STATIC */
|
||||
|
||||
#if !defined(SQLITE_CKSUMVFS_STATIC)
|
||||
|
||||
+1
-1
@@ -776,7 +776,7 @@ static int csvtabColumn(
|
||||
CsvCursor *pCur = (CsvCursor*)cur;
|
||||
CsvTable *pTab = (CsvTable*)cur->pVtab;
|
||||
if( i>=0 && i<pTab->nCol && pCur->azVal[i]!=0 ){
|
||||
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_STATIC);
|
||||
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
+2
-1
@@ -599,7 +599,6 @@ int sqlite3_decimal_init(
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
static const struct {
|
||||
const char *zFuncName;
|
||||
int nArg;
|
||||
@@ -614,6 +613,8 @@ int sqlite3_decimal_init(
|
||||
unsigned int i;
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
|
||||
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
|
||||
+7
-2
@@ -42,6 +42,10 @@ SQLITE_EXTENSION_INIT1
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
#ifndef deliberate_fall_through
|
||||
# define deliberate_fall_through
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Versions of isspace(), isalnum() and isdigit() to which it is safe
|
||||
** to pass signed char values.
|
||||
@@ -460,7 +464,7 @@ static void jsonRenderNode(
|
||||
jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
|
||||
break;
|
||||
}
|
||||
/* Fall through into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case JSON_REAL:
|
||||
case JSON_INT: {
|
||||
@@ -601,7 +605,7 @@ static void jsonReturn(
|
||||
sqlite3_result_int64(pCtx, i);
|
||||
int_done:
|
||||
break;
|
||||
int_as_real: /* fall through to real */;
|
||||
int_as_real: i=0; /* no break */ deliberate_fall_through
|
||||
}
|
||||
case JSON_REAL: {
|
||||
double r;
|
||||
@@ -2304,6 +2308,7 @@ static int jsonEachColumn(
|
||||
}
|
||||
/* For json_each() path and root are the same so fall through
|
||||
** into the root case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
default: {
|
||||
const char *zRoot = p->zRoot;
|
||||
|
||||
@@ -286,6 +286,13 @@ static const unsigned char sqlite3CtypeMap[256] = {
|
||||
#define TK_VARIABLE TK_LITERAL
|
||||
#define TK_BLOB TK_LITERAL
|
||||
|
||||
/* Disable nuisence warnings about case fall-through */
|
||||
#if !defined(deliberate_fall_through) && defined(__GCC__) && __GCC__>=7
|
||||
# define deliberate_fall_through __attribute__((fallthrough));
|
||||
#else
|
||||
# define deliberate_fall_through
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the length (in bytes) of the token that begins at z[0].
|
||||
** Store the token type in *tokenType before returning.
|
||||
@@ -436,6 +443,7 @@ static int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
}
|
||||
/* If the next character is a digit, this is a floating point
|
||||
** number that begins with ".". Fall thru into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case CC_DIGIT: {
|
||||
*tokenType = TK_INTEGER;
|
||||
@@ -528,6 +536,7 @@ static int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
}
|
||||
/* If it is not a BLOB literal, then it must be an ID, since no
|
||||
** SQL keywords start with the letter 'x'. Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case CC_ID: {
|
||||
i = 1;
|
||||
|
||||
+17
-6
@@ -106,10 +106,10 @@ struct series_cursor {
|
||||
*/
|
||||
static int seriesConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
void *pUnused,
|
||||
int argcUnused, const char *const*argvUnused,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
char **pzErrUnused
|
||||
){
|
||||
sqlite3_vtab *pNew;
|
||||
int rc;
|
||||
@@ -120,6 +120,10 @@ static int seriesConnect(
|
||||
#define SERIES_COLUMN_STOP 2
|
||||
#define SERIES_COLUMN_STEP 3
|
||||
|
||||
(void)pUnused;
|
||||
(void)argcUnused;
|
||||
(void)argvUnused;
|
||||
(void)pzErrUnused;
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -142,8 +146,9 @@ static int seriesDisconnect(sqlite3_vtab *pVtab){
|
||||
/*
|
||||
** Constructor for a new series_cursor object.
|
||||
*/
|
||||
static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
|
||||
series_cursor *pCur;
|
||||
(void)pUnused;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
@@ -250,11 +255,12 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
|
||||
*/
|
||||
static int seriesFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int idxNum, const char *idxStrUnused,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
series_cursor *pCur = (series_cursor *)pVtabCursor;
|
||||
int i = 0;
|
||||
(void)idxStrUnused;
|
||||
if( idxNum & 1 ){
|
||||
pCur->mnValue = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
@@ -311,7 +317,7 @@ static int seriesFilter(
|
||||
** (8) output in descending order
|
||||
*/
|
||||
static int seriesBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_vtab *tabUnused,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i, j; /* Loop over constraints */
|
||||
@@ -325,6 +331,7 @@ static int seriesBestIndex(
|
||||
** are the last three columns in the virtual table. */
|
||||
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
|
||||
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
|
||||
(void)tabUnused;
|
||||
aIdx[0] = aIdx[1] = aIdx[2] = -1;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
@@ -398,6 +405,10 @@ static sqlite3_module seriesModule = {
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
+5
-3
@@ -621,9 +621,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) ){
|
||||
|
||||
+9
-7
@@ -523,14 +523,16 @@ static int zipfileAppendData(
|
||||
const u8 *aWrite,
|
||||
int nWrite
|
||||
){
|
||||
size_t n;
|
||||
fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
|
||||
n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
|
||||
if( (int)n!=nWrite ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
|
||||
return SQLITE_ERROR;
|
||||
if( nWrite>0 ){
|
||||
size_t n = nWrite;
|
||||
fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
|
||||
n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
|
||||
if( (int)n!=nWrite ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pTab->szCurrent += nWrite;
|
||||
}
|
||||
pTab->szCurrent += nWrite;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1075,7 +1075,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
|
||||
geopolyAddSegments(p, p1, 1);
|
||||
geopolyAddSegments(p, p2, 2);
|
||||
pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
|
||||
rX = pThisEvent->x==0.0 ? -1.0 : 0.0;
|
||||
rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0;
|
||||
memset(aOverlap, 0, sizeof(aOverlap));
|
||||
while( pThisEvent ){
|
||||
if( pThisEvent->x!=rX ){
|
||||
|
||||
+1
-1
@@ -759,7 +759,7 @@ static int nodeAcquire(
|
||||
** are the leaves, and so on. If the depth as specified on the root node
|
||||
** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt.
|
||||
*/
|
||||
if( pNode && iNode==1 ){
|
||||
if( pNode && rc==SQLITE_OK && iNode==1 ){
|
||||
pRtree->iDepth = readInt16(pNode->zData);
|
||||
if( pRtree->iDepth>RTREE_MAX_DEPTH ){
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
|
||||
@@ -2956,8 +2956,13 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
|
||||
}
|
||||
|
||||
p->apValue = (sqlite3_value**)p->tblhdr.aBuf;
|
||||
p->abPK = (u8*)&p->apValue[p->nCol*2];
|
||||
p->zTab = (char*)&p->abPK[p->nCol];
|
||||
if( p->apValue==0 ){
|
||||
p->abPK = 0;
|
||||
p->zTab = 0;
|
||||
}else{
|
||||
p->abPK = (u8*)&p->apValue[p->nCol*2];
|
||||
p->zTab = p->abPK ? (char*)&p->abPK[p->nCol] : 0;
|
||||
}
|
||||
return (p->rc = rc);
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ static int SQLITE_TCLAPI test_session_cmd(
|
||||
){
|
||||
TestSession *p = (TestSession*)clientData;
|
||||
sqlite3_session *pSession = p->pSession;
|
||||
struct SessionSubcmd {
|
||||
static struct SessionSubcmd {
|
||||
const char *zSub;
|
||||
int nArg;
|
||||
const char *zMsg;
|
||||
@@ -1131,7 +1131,7 @@ static int SQLITE_TCLAPI test_rebaser_cmd(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
struct RebaseSubcmd {
|
||||
static struct RebaseSubcmd {
|
||||
const char *zSub;
|
||||
int nArg;
|
||||
const char *zMsg;
|
||||
@@ -1248,7 +1248,7 @@ static int SQLITE_TCLAPI test_sqlite3session_config(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
struct ConfigOpt {
|
||||
static struct ConfigOpt {
|
||||
const char *zSub;
|
||||
int op;
|
||||
} aSub[] = {
|
||||
|
||||
@@ -361,6 +361,7 @@ TESTSRC = \
|
||||
TESTSRC += \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/carray.c \
|
||||
$(TOP)/ext/misc/cksumvfs.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
$(TOP)/ext/misc/csv.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
@@ -912,6 +913,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC
|
||||
|
||||
testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
af07d8458ae5e2933134b706f80f9538f011970221904bf9f50ed6e47d306e14
|
||||
10e20c0b43500cfb9bbc0eaa061c57514f715d87238f4d835880cd846b9ebd1f
|
||||
+20
-6
@@ -1194,15 +1194,23 @@ static int renameResolveTrigger(Parse *pParse){
|
||||
SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
|
||||
if( pSrc ){
|
||||
int i;
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
for(i=0; i<pSrc->nSrc && rc==SQLITE_OK; i++){
|
||||
struct SrcList_item *p = &pSrc->a[i];
|
||||
p->pTab = sqlite3LocateTableItem(pParse, 0, p);
|
||||
p->iCursor = pParse->nTab++;
|
||||
if( p->pTab==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
if( p->pSelect ){
|
||||
sqlite3SelectPrep(pParse, p->pSelect, 0);
|
||||
sqlite3ExpandSubquery(pParse, p);
|
||||
assert( i>0 );
|
||||
assert( pStep->pFrom->a[i-1].pSelect );
|
||||
sqlite3SelectPrep(pParse, pStep->pFrom->a[i-1].pSelect, 0);
|
||||
}else{
|
||||
p->pTab->nTabRef++;
|
||||
rc = sqlite3ViewGetColumnNames(pParse, p->pTab);
|
||||
p->pTab = sqlite3LocateTableItem(pParse, 0, p);
|
||||
if( p->pTab==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p->pTab->nTabRef++;
|
||||
rc = sqlite3ViewGetColumnNames(pParse, p->pTab);
|
||||
}
|
||||
}
|
||||
}
|
||||
sNC.pSrcList = pSrc;
|
||||
@@ -1264,6 +1272,12 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
|
||||
sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
|
||||
sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
|
||||
}
|
||||
if( pStep->pFrom ){
|
||||
int i;
|
||||
for(i=0; i<pStep->pFrom->nSrc; i++){
|
||||
sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].pSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1772,6 +1772,7 @@ static int loadStatTbl(
|
||||
}
|
||||
pSpace = (tRowcnt*)&pIdx->aSample[nSample];
|
||||
pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
|
||||
pIdx->pTable->tabFlags |= TF_HasStat4;
|
||||
for(i=0; i<nSample; i++){
|
||||
pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
|
||||
pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
|
||||
|
||||
+3
-1
@@ -293,7 +293,9 @@ static void detachFunc(
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
|
||||
if( sqlite3BtreeTxnState(pDb->pBt)!=SQLITE_TXN_NONE
|
||||
|| sqlite3BtreeIsInBackup(pDb->pBt)
|
||||
){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
|
||||
+4
-4
@@ -123,7 +123,7 @@ static int setDestPgsz(sqlite3_backup *p){
|
||||
** message in database handle db.
|
||||
*/
|
||||
static int checkReadTransaction(sqlite3 *db, Btree *p){
|
||||
if( sqlite3BtreeIsInReadTrans(p) ){
|
||||
if( sqlite3BtreeTxnState(p)!=SQLITE_TXN_NONE ){
|
||||
sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@@ -354,7 +354,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
** one now. If a transaction is opened here, then it will be closed
|
||||
** before this function exits.
|
||||
*/
|
||||
if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){
|
||||
if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){
|
||||
rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0);
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
@@ -726,7 +726,7 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
sqlite3BtreeEnter(pTo);
|
||||
sqlite3BtreeEnter(pFrom);
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo) );
|
||||
assert( sqlite3BtreeTxnState(pTo)==SQLITE_TXN_WRITE );
|
||||
pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
|
||||
if( pFd->pMethods ){
|
||||
i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
|
||||
@@ -762,7 +762,7 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
|
||||
}
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo)==0 );
|
||||
assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE );
|
||||
copy_finished:
|
||||
sqlite3BtreeLeave(pFrom);
|
||||
sqlite3BtreeLeave(pTo);
|
||||
|
||||
+23
-13
@@ -112,6 +112,17 @@ int sqlite3_enable_shared_cache(int enable){
|
||||
#define hasReadConflicts(a, b) 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Return and reset the seek counter for a Btree object.
|
||||
*/
|
||||
sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){
|
||||
u64 n = pBt->nSeek;
|
||||
pBt->nSeek = 0;
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single
|
||||
** (MemPage*) as an argument. The (MemPage*) must not be NULL.
|
||||
@@ -2605,7 +2616,7 @@ btree_open_out:
|
||||
** do not change the pager-cache size.
|
||||
*/
|
||||
if( sqlite3BtreeSchema(p, 0, 0)==0 ){
|
||||
sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE);
|
||||
sqlite3BtreeSetCacheSize(p, SQLITE_DEFAULT_CACHE_SIZE);
|
||||
}
|
||||
|
||||
pFile = sqlite3PagerFile(pBt->pPager);
|
||||
@@ -5459,6 +5470,10 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
pCur->pBtree->nSeek++; /* Performance measurement during testing */
|
||||
#endif
|
||||
|
||||
if( pIdxKey ){
|
||||
xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
|
||||
pIdxKey->errCode = 0;
|
||||
@@ -5735,7 +5750,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){
|
||||
|
||||
pPage = pCur->pPage;
|
||||
idx = ++pCur->ix;
|
||||
if( !pPage->isInit ){
|
||||
if( !pPage->isInit || sqlite3FaultSim(412) ){
|
||||
/* The only known way for this to happen is for there to be a
|
||||
** recursive SQL function that does a DELETE operation as part of a
|
||||
** SELECT which deletes content out from under an active cursor
|
||||
@@ -10262,8 +10277,8 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up and report errors.
|
||||
@@ -10310,11 +10325,12 @@ const char *sqlite3BtreeGetJournalname(Btree *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return non-zero if a transaction is active.
|
||||
** Return one of SQLITE_TXN_NONE, SQLITE_TXN_READ, or SQLITE_TXN_WRITE
|
||||
** to describe the current transaction state of Btree p.
|
||||
*/
|
||||
int sqlite3BtreeIsInTrans(Btree *p){
|
||||
int sqlite3BtreeTxnState(Btree *p){
|
||||
assert( p==0 || sqlite3_mutex_held(p->db->mutex) );
|
||||
return (p && (p->inTrans==TRANS_WRITE));
|
||||
return p ? p->inTrans : 0;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
@@ -10343,14 +10359,8 @@ int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return non-zero if a read (or write) transaction is active.
|
||||
** Return true if there is currently a backup running on Btree p.
|
||||
*/
|
||||
int sqlite3BtreeIsInReadTrans(Btree *p){
|
||||
assert( p );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
return p->inTrans!=TRANS_NONE;
|
||||
}
|
||||
|
||||
int sqlite3BtreeIsInBackup(Btree *p){
|
||||
assert( p );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
+16
-2
@@ -85,16 +85,24 @@ int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*,int,int);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeTxnState(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
int sqlite3BtreeSchemaLocked(Btree *pBtree);
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
|
||||
#endif
|
||||
|
||||
/* Savepoints are named, nestable SQL transactions mostly implemented */
|
||||
/* in vdbe.c and pager.c See https://sqlite.org/lang_savepoint.html */
|
||||
int sqlite3BtreeSavepoint(Btree *, int, int);
|
||||
|
||||
/* "Checkpoint" only refers to WAL. See https://sqlite.org/wal.html#ckpt */
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
|
||||
#endif
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
const char *sqlite3BtreeGetJournalname(Btree *);
|
||||
int sqlite3BtreeCopyFile(Btree *, Btree *);
|
||||
@@ -331,6 +339,12 @@ int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
|
||||
int sqlite3BtreeIsReadonly(Btree *pBt);
|
||||
int sqlite3HeaderSizeBtree(void);
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
sqlite3_uint64 sqlite3BtreeSeekCount(Btree*);
|
||||
#else
|
||||
# define sqlite3BtreeSeekCount(X) 0
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
int sqlite3BtreeCursorIsValid(BtCursor*);
|
||||
#endif
|
||||
|
||||
@@ -353,6 +353,9 @@ struct Btree {
|
||||
u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
|
||||
Btree *pNext; /* List of other sharable Btrees from the same db */
|
||||
Btree *pPrev; /* Back pointer of the same list */
|
||||
#ifdef SQLITE_DEBUG
|
||||
u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
BtLock lock; /* Object used to lock page 1 */
|
||||
#endif
|
||||
@@ -364,11 +367,25 @@ struct Btree {
|
||||
** If the shared-data extension is enabled, there may be multiple users
|
||||
** of the Btree structure. At most one of these may open a write transaction,
|
||||
** but any number may have active read transactions.
|
||||
**
|
||||
** These values must match SQLITE_TXN_NONE, SQLITE_TXN_READ, and
|
||||
** SQLITE_TXN_WRITE
|
||||
*/
|
||||
#define TRANS_NONE 0
|
||||
#define TRANS_READ 1
|
||||
#define TRANS_WRITE 2
|
||||
|
||||
#if TRANS_NONE!=SQLITE_TXN_NONE
|
||||
# error wrong numeric code for no-transaction
|
||||
#endif
|
||||
#if TRANS_READ!=SQLITE_TXN_READ
|
||||
# error wrong numeric code for read-transaction
|
||||
#endif
|
||||
#if TRANS_WRITE!=SQLITE_TXN_WRITE
|
||||
# error wrong numeric code for write-transaction
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** An instance of this object represents a single database file.
|
||||
**
|
||||
|
||||
+49
-29
@@ -53,7 +53,7 @@ void sqlite3TableLock(
|
||||
u8 isWriteLock, /* True for a write lock */
|
||||
const char *zName /* Name of the table to be locked */
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
Parse *pToplevel;
|
||||
int i;
|
||||
int nBytes;
|
||||
TableLock *p;
|
||||
@@ -61,6 +61,7 @@ void sqlite3TableLock(
|
||||
|
||||
if( iDb==1 ) return;
|
||||
if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
|
||||
pToplevel = sqlite3ParseToplevel(pParse);
|
||||
for(i=0; i<pToplevel->nTableLock; i++){
|
||||
p = &pToplevel->aTableLock[i];
|
||||
if( p->iDb==iDb && p->iTab==iTab ){
|
||||
@@ -90,10 +91,8 @@ void sqlite3TableLock(
|
||||
*/
|
||||
static void codeTableLocks(Parse *pParse){
|
||||
int i;
|
||||
Vdbe *pVdbe;
|
||||
|
||||
pVdbe = sqlite3GetVdbe(pParse);
|
||||
assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */
|
||||
Vdbe *pVdbe = pParse->pVdbe;
|
||||
assert( pVdbe!=0 );
|
||||
|
||||
for(i=0; i<pParse->nTableLock; i++){
|
||||
TableLock *p = &pParse->aTableLock[i];
|
||||
@@ -1618,8 +1617,10 @@ primary_key_exit:
|
||||
** Add a new CHECK constraint to the table currently under construction.
|
||||
*/
|
||||
void sqlite3AddCheckConstraint(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pCheckExpr /* The check expression */
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pCheckExpr, /* The check expression */
|
||||
const char *zStart, /* Opening "(" */
|
||||
const char *zEnd /* Closing ")" */
|
||||
){
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
Table *pTab = pParse->pNewTable;
|
||||
@@ -1630,6 +1631,13 @@ void sqlite3AddCheckConstraint(
|
||||
pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
|
||||
if( pParse->constraintName.n ){
|
||||
sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1);
|
||||
}else{
|
||||
Token t;
|
||||
for(zStart++; sqlite3Isspace(zStart[0]); zStart++){}
|
||||
while( sqlite3Isspace(zEnd[-1]) ){ zEnd--; }
|
||||
t.z = zStart;
|
||||
t.n = (int)(zEnd - t.z);
|
||||
sqlite3ExprListSetName(pParse, pTab->pCheck, &t, 1);
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
@@ -1648,7 +1656,7 @@ void sqlite3AddCollateType(Parse *pParse, Token *pToken){
|
||||
char *zColl; /* Dequoted name of collation sequence */
|
||||
sqlite3 *db;
|
||||
|
||||
if( (p = pParse->pNewTable)==0 ) return;
|
||||
if( (p = pParse->pNewTable)==0 || IN_RENAME_OBJECT ) return;
|
||||
i = p->nCol-1;
|
||||
db = pParse->db;
|
||||
zColl = sqlite3NameFromToken(db, pToken);
|
||||
@@ -1883,12 +1891,15 @@ static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
|
||||
int nByte;
|
||||
if( pIdx->nColumn>=N ) return SQLITE_OK;
|
||||
assert( pIdx->isResized==0 );
|
||||
nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
|
||||
nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*N;
|
||||
zExtra = sqlite3DbMallocZero(db, nByte);
|
||||
if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
|
||||
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
|
||||
pIdx->azColl = (const char**)zExtra;
|
||||
zExtra += sizeof(char*)*N;
|
||||
memcpy(zExtra, pIdx->aiRowLogEst, sizeof(LogEst)*(pIdx->nKeyCol+1));
|
||||
pIdx->aiRowLogEst = (LogEst*)zExtra;
|
||||
zExtra += sizeof(LogEst)*N;
|
||||
memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
|
||||
pIdx->aiColumn = (i16*)zExtra;
|
||||
zExtra += sizeof(i16)*N;
|
||||
@@ -2703,10 +2714,8 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
assert( pTable->pSelect );
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
||||
if( pSel ){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
u8 eParseMode = pParse->eParseMode;
|
||||
pParse->eParseMode = PARSE_MODE_NORMAL;
|
||||
#endif
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
@@ -2754,9 +2763,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
EnableLookaside;
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
pParse->eParseMode = eParseMode;
|
||||
#endif
|
||||
} else {
|
||||
nErr++;
|
||||
}
|
||||
@@ -4094,7 +4101,7 @@ void sqlite3DefaultRowEst(Index *pIdx){
|
||||
** table but other parts we are having to guess at, then do not let the
|
||||
** estimated number of rows in the table be less than 1000 (LogEst 99).
|
||||
** Failure to do this can cause the indexes for which we do not have
|
||||
** stat1 data to be ignored by the query planner. tag-20200527-1
|
||||
** stat1 data to be ignored by the query planner.
|
||||
*/
|
||||
x = pIdx->pTable->nRowLogEst;
|
||||
assert( 99==sqlite3LogEst(1000) );
|
||||
@@ -4453,7 +4460,7 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
|
||||
assert(pList || pParse->db->mallocFailed );
|
||||
if( pList ){
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pItem->iCursor>=0 ) break;
|
||||
if( pItem->iCursor>=0 ) continue;
|
||||
pItem->iCursor = pParse->nTab++;
|
||||
if( pItem->pSelect ){
|
||||
sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc);
|
||||
@@ -4470,15 +4477,15 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
|
||||
struct SrcList_item *pItem;
|
||||
if( pList==0 ) return;
|
||||
for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
|
||||
sqlite3DbFree(db, pItem->zDatabase);
|
||||
if( pItem->zDatabase ) sqlite3DbFreeNN(db, pItem->zDatabase);
|
||||
sqlite3DbFree(db, pItem->zName);
|
||||
sqlite3DbFree(db, pItem->zAlias);
|
||||
if( pItem->zAlias ) sqlite3DbFreeNN(db, pItem->zAlias);
|
||||
if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
|
||||
if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
|
||||
sqlite3DeleteTable(db, pItem->pTab);
|
||||
sqlite3SelectDelete(db, pItem->pSelect);
|
||||
sqlite3ExprDelete(db, pItem->pOn);
|
||||
sqlite3IdListDelete(db, pItem->pUsing);
|
||||
if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect);
|
||||
if( pItem->pOn ) sqlite3ExprDelete(db, pItem->pOn);
|
||||
if( pItem->pUsing ) sqlite3IdListDelete(db, pItem->pUsing);
|
||||
}
|
||||
sqlite3DbFreeNN(db, pList);
|
||||
}
|
||||
@@ -4650,7 +4657,16 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
|
||||
if( !v ) return;
|
||||
if( type!=TK_DEFERRED ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
|
||||
int eTxnType;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeIsReadonly(pBt) ){
|
||||
eTxnType = 0; /* Read txn */
|
||||
}else if( type==TK_EXCLUSIVE ){
|
||||
eTxnType = 2; /* Exclusive txn */
|
||||
}else{
|
||||
eTxnType = 1; /* Write txn */
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Transaction, i, eTxnType);
|
||||
sqlite3VdbeUsesBtree(v, i);
|
||||
}
|
||||
}
|
||||
@@ -4739,13 +4755,11 @@ int sqlite3OpenTempDatabase(Parse *pParse){
|
||||
** will occur at the end of the top-level VDBE and will be generated
|
||||
** later, by sqlite3FinishCoding().
|
||||
*/
|
||||
void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
|
||||
assert( iDb>=0 && iDb<pParse->db->nDb );
|
||||
assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 );
|
||||
static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){
|
||||
assert( iDb>=0 && iDb<pToplevel->db->nDb );
|
||||
assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 );
|
||||
assert( iDb<SQLITE_MAX_ATTACHED+2 );
|
||||
assert( sqlite3SchemaMutexHeld(pParse->db, iDb, 0) );
|
||||
assert( sqlite3SchemaMutexHeld(pToplevel->db, iDb, 0) );
|
||||
if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
|
||||
DbMaskSet(pToplevel->cookieMask, iDb);
|
||||
if( !OMIT_TEMPDB && iDb==1 ){
|
||||
@@ -4753,6 +4767,10 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
|
||||
}
|
||||
}
|
||||
}
|
||||
void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
|
||||
sqlite3CodeVerifySchemaAtToplevel(sqlite3ParseToplevel(pParse), iDb);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each
|
||||
@@ -4784,7 +4802,7 @@ void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
|
||||
*/
|
||||
void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3CodeVerifySchemaAtToplevel(pToplevel, iDb);
|
||||
DbMaskSet(pToplevel->writeMask, iDb);
|
||||
pToplevel->isMultiWrite |= setStatement;
|
||||
}
|
||||
@@ -4835,7 +4853,9 @@ void sqlite3HaltConstraint(
|
||||
i8 p4type, /* P4_STATIC or P4_TRANSIENT */
|
||||
u8 p5Errmsg /* P5_ErrMsg type */
|
||||
){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
Vdbe *v;
|
||||
assert( pParse->pVdbe!=0 );
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
|
||||
if( onError==OE_Abort ){
|
||||
sqlite3MayAbort(pParse);
|
||||
|
||||
+5
-12
@@ -425,7 +425,7 @@ void sqlite3DeleteFrom(
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
{
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE;
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
|
||||
if( sNC.ncFlags & NC_VarSelect ) bComplex = 1;
|
||||
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
|
||||
if( HasRowid(pTab) ){
|
||||
@@ -461,6 +461,9 @@ void sqlite3DeleteFrom(
|
||||
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
|
||||
assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
|
||||
if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse);
|
||||
if( sqlite3WhereUsesDeferredSeek(pWInfo) ){
|
||||
sqlite3VdbeAddOp1(v, OP_FinishSeek, iTabCur);
|
||||
}
|
||||
|
||||
/* Keep track of the number of rows to be deleted */
|
||||
if( memCnt ){
|
||||
@@ -495,6 +498,7 @@ void sqlite3DeleteFrom(
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
|
||||
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
|
||||
addrBypass = sqlite3VdbeMakeLabel(pParse);
|
||||
}else{
|
||||
if( pPk ){
|
||||
/* Add the PK key for this row to the temporary table */
|
||||
@@ -508,13 +512,6 @@ void sqlite3DeleteFrom(
|
||||
nKey = 1; /* OP_DeferredSeek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
}
|
||||
}
|
||||
|
||||
/* If this DELETE cannot use the ONEPASS strategy, this is the
|
||||
** end of the WHERE loop */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
addrBypass = sqlite3VdbeMakeLabel(pParse);
|
||||
}else{
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
@@ -945,10 +942,6 @@ int sqlite3GenerateIndexKey(
|
||||
}
|
||||
if( regOut ){
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
|
||||
if( pIdx->pTable->pSelect ){
|
||||
const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx);
|
||||
sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
return regBase;
|
||||
|
||||
+18
-11
@@ -44,8 +44,10 @@ char sqlite3TableColumnAffinity(Table *pTab, int iCol){
|
||||
*/
|
||||
char sqlite3ExprAffinity(const Expr *pExpr){
|
||||
int op;
|
||||
while( ExprHasProperty(pExpr, EP_Skip) ){
|
||||
assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
|
||||
while( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
|
||||
assert( pExpr->op==TK_COLLATE
|
||||
|| pExpr->op==TK_IF_NULL_ROW
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
|
||||
pExpr = pExpr->pLeft;
|
||||
assert( pExpr!=0 );
|
||||
}
|
||||
@@ -115,7 +117,7 @@ Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
|
||||
*/
|
||||
Expr *sqlite3ExprSkipCollate(Expr *pExpr){
|
||||
while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
|
||||
assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
|
||||
assert( pExpr->op==TK_COLLATE );
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
return pExpr;
|
||||
@@ -134,7 +136,7 @@ Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
|
||||
assert( pExpr->op==TK_FUNCTION );
|
||||
pExpr = pExpr->x.pList->a[0].pExpr;
|
||||
}else{
|
||||
assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
|
||||
assert( pExpr->op==TK_COLLATE );
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
}
|
||||
@@ -768,6 +770,7 @@ int sqlite3SelectExprHeight(Select *p){
|
||||
** Expr.flags.
|
||||
*/
|
||||
void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
|
||||
if( pParse->nErr ) return;
|
||||
if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){
|
||||
p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList);
|
||||
}
|
||||
@@ -2029,7 +2032,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
if( sqlite3ExprIdToTrueFalse(pExpr) ){
|
||||
return WRC_Prune;
|
||||
}
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_COLUMN:
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_AGG_COLUMN:
|
||||
@@ -2043,7 +2046,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
|
||||
return WRC_Continue;
|
||||
}
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_IF_NULL_ROW:
|
||||
case TK_REGISTER:
|
||||
case TK_DOT:
|
||||
@@ -2064,7 +2067,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
pWalker->eCode = 0;
|
||||
return WRC_Abort;
|
||||
}
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
default:
|
||||
testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
|
||||
testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
|
||||
@@ -3620,6 +3623,7 @@ void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
|
||||
*/
|
||||
static void exprToRegister(Expr *pExpr, int iReg){
|
||||
Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
|
||||
if( NEVER(p==0) ) return;
|
||||
p->op2 = p->op;
|
||||
p->op = TK_REGISTER;
|
||||
p->iTable = iReg;
|
||||
@@ -3836,6 +3840,7 @@ expr_code_doover:
|
||||
return target;
|
||||
}
|
||||
/* Otherwise, fall thru into the TK_COLUMN case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case TK_COLUMN: {
|
||||
int iTab = pExpr->iTable;
|
||||
@@ -4606,6 +4611,7 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
|
||||
int r2;
|
||||
pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
|
||||
if( ConstFactorOk(pParse)
|
||||
&& ALWAYS(pExpr!=0)
|
||||
&& pExpr->op!=TK_REGISTER
|
||||
&& sqlite3ExprIsConstantNotJoin(pExpr)
|
||||
){
|
||||
@@ -4901,7 +4907,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
testcase( op==TK_ISNOT );
|
||||
op = (op==TK_IS) ? TK_EQ : TK_NE;
|
||||
jumpIfNull = SQLITE_NULLEQ;
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
@@ -5077,7 +5083,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
testcase( pExpr->op==TK_ISNOT );
|
||||
op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
|
||||
jumpIfNull = SQLITE_NULLEQ;
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
@@ -5389,13 +5395,13 @@ static int exprImpliesNotNull(
|
||||
case TK_RSHIFT:
|
||||
case TK_CONCAT:
|
||||
seenNot = 1;
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_STAR:
|
||||
case TK_REM:
|
||||
case TK_BITAND:
|
||||
case TK_SLASH: {
|
||||
if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1;
|
||||
/* Fall thru into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case TK_SPAN:
|
||||
case TK_COLLATE:
|
||||
@@ -5544,6 +5550,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
|
||||
){
|
||||
return WRC_Prune;
|
||||
}
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
default:
|
||||
return WRC_Continue;
|
||||
|
||||
@@ -1359,6 +1359,7 @@ static Trigger *fkActionTrigger(
|
||||
pStep->op = TK_DELETE;
|
||||
break;
|
||||
}
|
||||
/* no break */ deliberate_fall_through
|
||||
default:
|
||||
pStep->op = TK_UPDATE;
|
||||
}
|
||||
|
||||
@@ -1995,6 +1995,8 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
FUNCTION(substring, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substring, 3, 0, 0, substrFunc ),
|
||||
WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
|
||||
WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
|
||||
WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
|
||||
|
||||
+1
-1
@@ -303,7 +303,7 @@ int sqlite3PendingByte = 0x40000000;
|
||||
/*
|
||||
** Flags for select tracing and the ".selecttrace" macro of the CLI
|
||||
*/
|
||||
/**/ u32 sqlite3SelectTrace = 0;
|
||||
u32 sqlite3_unsupported_selecttrace = 0;
|
||||
|
||||
#include "opcodes.h"
|
||||
/*
|
||||
|
||||
+10
-9
@@ -32,7 +32,8 @@ void sqlite3OpenTable(
|
||||
){
|
||||
Vdbe *v;
|
||||
assert( !IsVirtual(pTab) );
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( pParse->pVdbe!=0 );
|
||||
v = pParse->pVdbe;
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum,
|
||||
(opcode==OP_OpenWrite)?1:0, pTab->zName);
|
||||
@@ -1531,7 +1532,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
isUpdate = regOldData!=0;
|
||||
db = pParse->db;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
nCol = pTab->nCol;
|
||||
@@ -1612,7 +1613,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
case OE_Abort:
|
||||
sqlite3MayAbort(pParse);
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
case OE_Rollback:
|
||||
case OE_Fail: {
|
||||
char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
|
||||
@@ -1685,7 +1686,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeGoto(v, ignoreDest);
|
||||
}else{
|
||||
char *zName = pCheck->a[i].zEName;
|
||||
if( zName==0 ) zName = pTab->zName;
|
||||
assert( zName!=0 || pParse->db->mallocFailed );
|
||||
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-26383-51744 */
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
|
||||
onError, zName, P4_TRANSIENT,
|
||||
@@ -1840,7 +1841,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
switch( onError ){
|
||||
default: {
|
||||
onError = OE_Abort;
|
||||
/* Fall thru into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
@@ -1901,7 +1902,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
case OE_Update: {
|
||||
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur);
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
#endif
|
||||
case OE_Ignore: {
|
||||
@@ -2122,7 +2123,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
case OE_Update: {
|
||||
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix);
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
#endif
|
||||
case OE_Ignore: {
|
||||
@@ -2304,7 +2305,7 @@ void sqlite3CompleteInsertion(
|
||||
|| update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION)
|
||||
);
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
@@ -2405,7 +2406,7 @@ int sqlite3OpenTableAndIndices(
|
||||
return 0;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
if( iBase<0 ) iBase = pParse->nTab;
|
||||
iDataCur = iBase++;
|
||||
|
||||
@@ -478,6 +478,8 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_create_filename,
|
||||
sqlite3_free_filename,
|
||||
sqlite3_database_file_object,
|
||||
/* Version 3.34.0 and later */
|
||||
sqlite3_txn_state,
|
||||
};
|
||||
|
||||
/* True if x is the directory separator character
|
||||
|
||||
+61
-10
@@ -897,7 +897,7 @@ int sqlite3_db_cacheflush(sqlite3 *db){
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeIsInTrans(pBt) ){
|
||||
if( pBt && sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
rc = sqlite3PagerFlush(pPager);
|
||||
if( rc==SQLITE_BUSY ){
|
||||
@@ -1202,7 +1202,7 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( db->mTrace & SQLITE_TRACE_CLOSE ){
|
||||
db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
}
|
||||
|
||||
/* Force xDisconnect calls on all virtual tables */
|
||||
@@ -1241,6 +1241,36 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the transaction state for a single databse, or the maximum
|
||||
** transaction state over all attached databases if zSchema is null.
|
||||
*/
|
||||
int sqlite3_txn_state(sqlite3 *db, const char *zSchema){
|
||||
int iDb, nDb;
|
||||
int iTxn = -1;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( zSchema ){
|
||||
nDb = iDb = sqlite3FindDbName(db, zSchema);
|
||||
if( iDb<0 ) nDb--;
|
||||
}else{
|
||||
iDb = 0;
|
||||
nDb = db->nDb-1;
|
||||
}
|
||||
for(; iDb<=nDb; iDb++){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
int x = pBt!=0 ? sqlite3BtreeTxnState(pBt) : SQLITE_TXN_NONE;
|
||||
if( x>iTxn ) iTxn = x;
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return iTxn;
|
||||
}
|
||||
|
||||
/*
|
||||
** Two variations on the public interface for closing a database
|
||||
** connection. The sqlite3_close() version returns SQLITE_BUSY and
|
||||
@@ -1401,7 +1431,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
if( p ){
|
||||
if( sqlite3BtreeIsInTrans(p) ){
|
||||
if( sqlite3BtreeTxnState(p)==SQLITE_TXN_WRITE ){
|
||||
inTrans = 1;
|
||||
}
|
||||
sqlite3BtreeRollback(p, tripCode, !schemaChange);
|
||||
@@ -2091,7 +2121,7 @@ void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pTraceArg;
|
||||
db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
|
||||
db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
|
||||
db->trace.xLegacy = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
@@ -2115,7 +2145,7 @@ int sqlite3_trace_v2(
|
||||
if( mTrace==0 ) xTrace = 0;
|
||||
if( xTrace==0 ) mTrace = 0;
|
||||
db->mTrace = mTrace;
|
||||
db->xTrace = xTrace;
|
||||
db->trace.xV2 = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
@@ -3822,7 +3852,9 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
int nSave = db->busyHandler.nBusy;
|
||||
rc = sqlite3OsFileControl(fd, op, pArg);
|
||||
db->busyHandler.nBusy = nSave;
|
||||
}
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
}
|
||||
@@ -4095,7 +4127,7 @@ int sqlite3_test_control(int op, ...){
|
||||
** 2020-07-22: Disabling EXTRA_SCHEMA_CHECKS also disables the
|
||||
** verification of rootpage numbers when parsing the schema. This
|
||||
** is useful to make it easier to reach strange internal error states
|
||||
** during testing. The EXTRA_SCHEMA_CHECKS settting is always enabled
|
||||
** during testing. The EXTRA_SCHEMA_CHECKS setting is always enabled
|
||||
** in production.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: {
|
||||
@@ -4205,6 +4237,25 @@ int sqlite3_test_control(int op, ...){
|
||||
sqlite3ResultIntReal(pCtx);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_SEEK_COUNT,
|
||||
** sqlite3 *db, // Database connection
|
||||
** u64 *pnSeek // Write seek count here
|
||||
** );
|
||||
**
|
||||
** This test-control queries the seek-counter on the "main" database
|
||||
** file. The seek-counter is written into *pnSeek and is then reset.
|
||||
** The seek-count is only available if compiled with SQLITE_DEBUG.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_SEEK_COUNT: {
|
||||
sqlite3 *db = va_arg(ap, sqlite3*);
|
||||
u64 *pn = va_arg(ap, sqlite3_uint64*);
|
||||
*pn = sqlite3BtreeSeekCount(db->aDb->pBt);
|
||||
(void)db; /* Silence harmless unused variable warning */
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_UNTESTABLE */
|
||||
@@ -4440,7 +4491,7 @@ int sqlite3_snapshot_get(
|
||||
int iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
||||
if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
||||
@@ -4476,10 +4527,10 @@ int sqlite3_snapshot_open(
|
||||
iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( sqlite3BtreeIsInTrans(pBt)==0 ){
|
||||
if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ){
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
int bUnlock = 0;
|
||||
if( sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_NONE ){
|
||||
if( db->nVdbeActive==0 ){
|
||||
rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -4528,7 +4579,7 @@ int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
|
||||
iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
if( SQLITE_TXN_NONE==sqlite3BtreeTxnState(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
|
||||
|
||||
+16
-8
@@ -474,12 +474,17 @@ void *sqlite3Realloc(void *pOld, u64 nBytes){
|
||||
if( nOld==nNew ){
|
||||
pNew = pOld;
|
||||
}else if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_int64 nUsed;
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
|
||||
nDiff = nNew - nOld;
|
||||
if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
|
||||
if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >=
|
||||
mem0.alarmThreshold-nDiff ){
|
||||
sqlite3MallocAlarm(nDiff);
|
||||
if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
@@ -786,12 +791,15 @@ void sqlite3OomClear(sqlite3 *db){
|
||||
}
|
||||
|
||||
/*
|
||||
** Take actions at the end of an API call to indicate an OOM error
|
||||
** Take actions at the end of an API call to deal with error codes.
|
||||
*/
|
||||
static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
|
||||
sqlite3OomClear(db);
|
||||
sqlite3Error(db, SQLITE_NOMEM);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){
|
||||
if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomClear(db);
|
||||
sqlite3Error(db, SQLITE_NOMEM);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
return rc & db->errMask;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -813,8 +821,8 @@ int sqlite3ApiExit(sqlite3* db, int rc){
|
||||
*/
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
|
||||
return apiOomError(db);
|
||||
if( db->mallocFailed || rc ){
|
||||
return apiHandleError(db, rc);
|
||||
}
|
||||
return rc & db->errMask;
|
||||
}
|
||||
|
||||
+15
-4
@@ -126,11 +126,14 @@ static const sqlite3_io_methods memdb_io_methods = {
|
||||
** Close an memdb-file.
|
||||
**
|
||||
** The pData pointer is owned by the application, so there is nothing
|
||||
** to free.
|
||||
** to free. Unless the SQLITE_DESERIALIZE_FREEONCLOSE flag is set,
|
||||
** in which case we own the pData pointer and need to free it.
|
||||
*/
|
||||
static int memdbClose(sqlite3_file *pFile){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData);
|
||||
if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){
|
||||
sqlite3_free(p->aData);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -573,8 +576,12 @@ int sqlite3_deserialize(
|
||||
goto end_deserialize;
|
||||
}
|
||||
zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema);
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( rc ) goto end_deserialize;
|
||||
db->init.iDb = (u8)iDb;
|
||||
db->init.reopenMemdb = 1;
|
||||
@@ -589,6 +596,7 @@ int sqlite3_deserialize(
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p->aData = pData;
|
||||
pData = 0;
|
||||
p->sz = szDb;
|
||||
p->szAlloc = szBuf;
|
||||
p->szMax = szBuf;
|
||||
@@ -601,6 +609,9 @@ int sqlite3_deserialize(
|
||||
|
||||
end_deserialize:
|
||||
sqlite3_finalize(pStmt);
|
||||
if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){
|
||||
sqlite3_free(pData);
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+165
-56
@@ -122,7 +122,8 @@
|
||||
# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
|
||||
(__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
|
||||
# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
|
||||
&& (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
|
||||
&& (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))\
|
||||
&& (!defined(TARGET_OS_MACCATALYST) || (TARGET_OS_MACCATALYST==0))
|
||||
# undef HAVE_GETHOSTUUID
|
||||
# define HAVE_GETHOSTUUID 1
|
||||
# else
|
||||
@@ -1544,6 +1545,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward declaration*/
|
||||
static int unixSleep(sqlite3_vfs*,int);
|
||||
|
||||
/*
|
||||
** Set a posix-advisory-lock.
|
||||
**
|
||||
@@ -1573,7 +1577,7 @@ static int osSetPosixAdvisoryLock(
|
||||
** generic posix, however, there is no such API. So we simply try the
|
||||
** lock once every millisecond until either the timeout expires, or until
|
||||
** the lock is obtained. */
|
||||
usleep(1000);
|
||||
unixSleep(0,1000);
|
||||
rc = osFcntl(h,F_SETLK,pLock);
|
||||
tm--;
|
||||
}
|
||||
@@ -2144,6 +2148,7 @@ static int unixClose(sqlite3_file *id){
|
||||
}
|
||||
sqlite3_mutex_leave(pInode->pLockMutex);
|
||||
releaseInodeInfo(pFile);
|
||||
assert( pFile->pShm==0 );
|
||||
rc = closeUnixFile(id);
|
||||
unixLeaveMutex();
|
||||
return rc;
|
||||
@@ -3370,7 +3375,24 @@ static int unixRead(
|
||||
if( got==amt ){
|
||||
return SQLITE_OK;
|
||||
}else if( got<0 ){
|
||||
/* lastErrno set by seekAndRead */
|
||||
/* pFile->lastErrno has been set by seekAndRead().
|
||||
** Usually we return SQLITE_IOERR_READ here, though for some
|
||||
** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The
|
||||
** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT
|
||||
** prior to returning to the application by the sqlite3ApiExit()
|
||||
** routine.
|
||||
*/
|
||||
switch( pFile->lastErrno ){
|
||||
case ERANGE:
|
||||
case EIO:
|
||||
#ifdef ENXIO
|
||||
case ENXIO:
|
||||
#endif
|
||||
#ifdef EDEVERR
|
||||
case EDEVERR:
|
||||
#endif
|
||||
return SQLITE_IOERR_CORRUPTFS;
|
||||
}
|
||||
return SQLITE_IOERR_READ;
|
||||
}else{
|
||||
storeLastErrno(pFile, 0); /* not a system error */
|
||||
@@ -4254,6 +4276,7 @@ struct unixShmNode {
|
||||
char **apRegion; /* Array of mapped shared-memory regions */
|
||||
int nRef; /* Number of unixShm objects pointing to this */
|
||||
unixShm *pFirst; /* All unixShm objects pointing to this */
|
||||
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
|
||||
#ifdef SQLITE_DEBUG
|
||||
u8 exclMask; /* Mask of exclusive locks held */
|
||||
u8 sharedMask; /* Mask of shared locks held */
|
||||
@@ -4794,6 +4817,38 @@ shmpage_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check that the pShmNode->aLock[] array comports with the locking bitmasks
|
||||
** held by each client. Return true if it does, or false otherwise. This
|
||||
** is to be used in an assert(). e.g.
|
||||
**
|
||||
** assert( assertLockingArrayOk(pShmNode) );
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
static int assertLockingArrayOk(unixShmNode *pShmNode){
|
||||
unixShm *pX;
|
||||
int aLock[SQLITE_SHM_NLOCK];
|
||||
assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
|
||||
|
||||
memset(aLock, 0, sizeof(aLock));
|
||||
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
||||
int i;
|
||||
for(i=0; i<SQLITE_SHM_NLOCK; i++){
|
||||
if( pX->exclMask & (1<<i) ){
|
||||
assert( aLock[i]==0 );
|
||||
aLock[i] = -1;
|
||||
}else if( pX->sharedMask & (1<<i) ){
|
||||
assert( aLock[i]>=0 );
|
||||
aLock[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
|
||||
return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Change the lock state for a shared-memory segment.
|
||||
**
|
||||
@@ -4810,10 +4865,10 @@ static int unixShmLock(
|
||||
){
|
||||
unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */
|
||||
unixShm *p = pDbFd->pShm; /* The shared memory being locked */
|
||||
unixShm *pX; /* For looping over all siblings */
|
||||
unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
|
||||
int rc = SQLITE_OK; /* Result code */
|
||||
u16 mask; /* Mask of locks to take or release */
|
||||
int *aLock = pShmNode->aLock;
|
||||
|
||||
assert( pShmNode==pDbFd->pInode->pShmNode );
|
||||
assert( pShmNode->pInode==pDbFd->pInode );
|
||||
@@ -4852,78 +4907,76 @@ static int unixShmLock(
|
||||
mask = (1<<(ofst+n)) - (1<<ofst);
|
||||
assert( n>1 || mask==(1<<ofst) );
|
||||
sqlite3_mutex_enter(pShmNode->pShmMutex);
|
||||
assert( assertLockingArrayOk(pShmNode) );
|
||||
if( flags & SQLITE_SHM_UNLOCK ){
|
||||
u16 allMask = 0; /* Mask of locks held by siblings */
|
||||
if( (p->exclMask|p->sharedMask) & mask ){
|
||||
int ii;
|
||||
int bUnlock = 1;
|
||||
|
||||
/* See if any siblings hold this same lock */
|
||||
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
||||
if( pX==p ) continue;
|
||||
assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
|
||||
allMask |= pX->sharedMask;
|
||||
for(ii=ofst; ii<ofst+n; ii++){
|
||||
if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){
|
||||
bUnlock = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( bUnlock ){
|
||||
rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(&aLock[ofst], 0, sizeof(int)*n);
|
||||
}
|
||||
}else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
|
||||
assert( n==1 && aLock[ofst]>1 );
|
||||
aLock[ofst]--;
|
||||
}
|
||||
|
||||
/* Undo the local locks */
|
||||
if( rc==SQLITE_OK ){
|
||||
p->exclMask &= ~mask;
|
||||
p->sharedMask &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the system-level locks */
|
||||
if( (mask & allMask)==0 ){
|
||||
rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Undo the local locks */
|
||||
if( rc==SQLITE_OK ){
|
||||
p->exclMask &= ~mask;
|
||||
p->sharedMask &= ~mask;
|
||||
}
|
||||
}else if( flags & SQLITE_SHM_SHARED ){
|
||||
u16 allShared = 0; /* Union of locks held by connections other than "p" */
|
||||
|
||||
/* Find out which shared locks are already held by sibling connections.
|
||||
** If any sibling already holds an exclusive lock, go ahead and return
|
||||
** SQLITE_BUSY.
|
||||
*/
|
||||
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
||||
if( (pX->exclMask & mask)!=0 ){
|
||||
assert( n==1 );
|
||||
assert( (p->exclMask & (1<<ofst))==0 );
|
||||
if( (p->sharedMask & mask)==0 ){
|
||||
if( aLock[ofst]<0 ){
|
||||
rc = SQLITE_BUSY;
|
||||
break;
|
||||
}
|
||||
allShared |= pX->sharedMask;
|
||||
}
|
||||
|
||||
/* Get shared locks at the system level, if necessary */
|
||||
if( rc==SQLITE_OK ){
|
||||
if( (allShared & mask)==0 ){
|
||||
}else if( aLock[ofst]==0 ){
|
||||
rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the local shared locks */
|
||||
if( rc==SQLITE_OK ){
|
||||
p->sharedMask |= mask;
|
||||
/* Get the local shared locks */
|
||||
if( rc==SQLITE_OK ){
|
||||
p->sharedMask |= mask;
|
||||
aLock[ofst]++;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* Make sure no sibling connections hold locks that will block this
|
||||
** lock. If any do, return SQLITE_BUSY right away.
|
||||
*/
|
||||
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
||||
if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
|
||||
** lock. If any do, return SQLITE_BUSY right away. */
|
||||
int ii;
|
||||
for(ii=ofst; ii<ofst+n; ii++){
|
||||
assert( (p->sharedMask & mask)==0 );
|
||||
if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
|
||||
rc = SQLITE_BUSY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the exclusive locks at the system level. Then if successful
|
||||
** also mark the local connection as being locked.
|
||||
*/
|
||||
|
||||
/* Get the exclusive locks at the system level. Then if successful
|
||||
** also update the in-memory values. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( (p->sharedMask & mask)==0 );
|
||||
p->exclMask |= mask;
|
||||
for(ii=ofst; ii<ofst+n; ii++){
|
||||
aLock[ii] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( assertLockingArrayOk(pShmNode) );
|
||||
sqlite3_mutex_leave(pShmNode->pShmMutex);
|
||||
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
|
||||
p->id, osGetpid(0), p->sharedMask, p->exclMask));
|
||||
@@ -6300,7 +6353,26 @@ static int unixAccess(
|
||||
}
|
||||
|
||||
/*
|
||||
** If the last component of the pathname in z[0]..z[j-1] is something
|
||||
** other than ".." then back it out and return true. If the last
|
||||
** component is empty or if it is ".." then return false.
|
||||
*/
|
||||
static int unixBackupDir(const char *z, int *pJ){
|
||||
int j = *pJ;
|
||||
int i;
|
||||
if( j<=0 ) return 0;
|
||||
for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){}
|
||||
if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
|
||||
*pJ = i-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a relative pathname into a full pathname. Also
|
||||
** simplify the pathname as follows:
|
||||
**
|
||||
** Remove all instances of /./
|
||||
** Remove all isntances of /X/../ for any X
|
||||
*/
|
||||
static int mkFullPathname(
|
||||
const char *zPath, /* Input path */
|
||||
@@ -6309,6 +6381,7 @@ static int mkFullPathname(
|
||||
){
|
||||
int nPath = sqlite3Strlen30(zPath);
|
||||
int iOff = 0;
|
||||
int i, j;
|
||||
if( zPath[0]!='/' ){
|
||||
if( osGetcwd(zOut, nOut-2)==0 ){
|
||||
return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
|
||||
@@ -6323,6 +6396,41 @@ static int mkFullPathname(
|
||||
return SQLITE_CANTOPEN_BKPT;
|
||||
}
|
||||
sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
|
||||
|
||||
/* Remove duplicate '/' characters. Except, two // at the beginning
|
||||
** of a pathname is allowed since this is important on windows. */
|
||||
for(i=j=1; zOut[i]; i++){
|
||||
zOut[j++] = zOut[i];
|
||||
while( zOut[i]=='/' && zOut[i+1]=='/' ) i++;
|
||||
}
|
||||
zOut[j] = 0;
|
||||
|
||||
assert( zOut[0]=='/' );
|
||||
for(i=j=0; zOut[i]; i++){
|
||||
if( zOut[i]=='/' ){
|
||||
/* Skip over internal "/." directory components */
|
||||
if( zOut[i+1]=='.' && zOut[i+2]=='/' ){
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this is a "/.." directory component then back out the
|
||||
** previous term of the directory if it is something other than "..".
|
||||
*/
|
||||
if( zOut[i+1]=='.'
|
||||
&& zOut[i+2]=='.'
|
||||
&& zOut[i+3]=='/'
|
||||
&& unixBackupDir(zOut, &j)
|
||||
){
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if( ALWAYS(j>=0) ) zOut[j] = zOut[i];
|
||||
j++;
|
||||
}
|
||||
if( NEVER(j==0) ) zOut[j++] = '/';
|
||||
zOut[j] = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -6543,7 +6651,8 @@ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return microseconds;
|
||||
#elif defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
usleep(microseconds);
|
||||
if( microseconds>=1000000 ) sleep(microseconds/1000000);
|
||||
if( microseconds%1000000 ) usleep(microseconds%1000000);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return microseconds;
|
||||
#else
|
||||
@@ -7116,7 +7225,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
|
||||
|
||||
if( nTries==1 ){
|
||||
conchModTime = buf.st_mtimespec;
|
||||
usleep(500000); /* wait 0.5 sec and try the lock again*/
|
||||
unixSleep(0,500000); /* wait 0.5 sec and try the lock again*/
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7142,7 +7251,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
|
||||
/* don't break the lock on short read or a version mismatch */
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
usleep(10000000); /* wait 10 sec and try the lock again */
|
||||
unixSleep(0,10000000); /* wait 10 sec and try the lock again */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -5126,7 +5126,11 @@ static int winOpen(
|
||||
dwCreationDisposition = OPEN_EXISTING;
|
||||
}
|
||||
|
||||
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
if( 0==sqlite3_uri_boolean(zName, "exclusive", 0) ){
|
||||
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
}else{
|
||||
dwShareMode = 0;
|
||||
}
|
||||
|
||||
if( isDelete ){
|
||||
#if SQLITE_OS_WINCE
|
||||
|
||||
+15
-5
@@ -2486,18 +2486,20 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){
|
||||
i64 nSuperJournal; /* Size of super-journal file */
|
||||
char *zJournal; /* Pointer to one journal within MJ file */
|
||||
char *zSuperPtr; /* Space to hold super-journal filename */
|
||||
char *zFree = 0; /* Free this buffer */
|
||||
int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
|
||||
|
||||
/* Allocate space for both the pJournal and pSuper file descriptors.
|
||||
** If successful, open the super-journal file for reading.
|
||||
*/
|
||||
pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
|
||||
pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile);
|
||||
if( !pSuper ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
pJournal = 0;
|
||||
}else{
|
||||
const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL);
|
||||
rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0);
|
||||
pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto delsuper_out;
|
||||
|
||||
@@ -2509,11 +2511,13 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){
|
||||
rc = sqlite3OsFileSize(pSuper, &nSuperJournal);
|
||||
if( rc!=SQLITE_OK ) goto delsuper_out;
|
||||
nSuperPtr = pVfs->mxPathname+1;
|
||||
zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2);
|
||||
if( !zSuperJournal ){
|
||||
zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2);
|
||||
if( !zFree ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto delsuper_out;
|
||||
}
|
||||
zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0;
|
||||
zSuperJournal = &zFree[4];
|
||||
zSuperPtr = &zSuperJournal[nSuperJournal+2];
|
||||
rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0);
|
||||
if( rc!=SQLITE_OK ) goto delsuper_out;
|
||||
@@ -2561,7 +2565,7 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){
|
||||
rc = sqlite3OsDelete(pVfs, zSuper, 0);
|
||||
|
||||
delsuper_out:
|
||||
sqlite3_free(zSuperJournal);
|
||||
sqlite3_free(zFree);
|
||||
if( pSuper ){
|
||||
sqlite3OsClose(pSuper);
|
||||
assert( !isOpen(pJournal) );
|
||||
@@ -2899,7 +2903,11 @@ end_playback:
|
||||
pPager->changeCountDone = pPager->tempFile;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
zSuper = pPager->pTmpSpace;
|
||||
/* Leave 4 bytes of space before the super-journal filename in memory.
|
||||
** This is because it may end up being passed to sqlite3OsOpen(), in
|
||||
** which case it requires 4 0x00 bytes in memory immediately before
|
||||
** the filename. */
|
||||
zSuper = &pPager->pTmpSpace[4];
|
||||
rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
}
|
||||
@@ -2916,6 +2924,8 @@ end_playback:
|
||||
/* If there was a super-journal and this routine will return success,
|
||||
** see if it is possible to delete the super-journal.
|
||||
*/
|
||||
assert( zSuper==&pPager->pTmpSpace[4] );
|
||||
memset(&zSuper[-4], 0, 4);
|
||||
rc = pager_delsuper(pPager, zSuper);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
}
|
||||
|
||||
+14
-8
@@ -1,5 +1,6 @@
|
||||
%include {
|
||||
/*
|
||||
** 2001 September 15
|
||||
** 2001-09-15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
@@ -9,11 +10,16 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains SQLite's grammar for SQL. Process this file
|
||||
** using the lemon parser generator to generate C code that runs
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
** This file contains SQLite's SQL parser.
|
||||
**
|
||||
** The canonical source code to this file ("parse.y") is a Lemon grammar
|
||||
** file that specifies the input grammar and actions to take while parsing.
|
||||
** That input file is processed by Lemon to generate a C-language
|
||||
** implementation of a parser for the given grammer. You might be reading
|
||||
** this comment as part of the translated C-code. Edits should be made
|
||||
** to the original parse.y sources.
|
||||
*/
|
||||
}
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
%token_prefix TK_
|
||||
@@ -366,7 +372,7 @@ ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I).
|
||||
{sqlite3AddPrimaryKey(pParse,0,R,I,Z);}
|
||||
ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0,0,0,
|
||||
SQLITE_IDXTYPE_UNIQUE);}
|
||||
ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse,X);}
|
||||
ccons ::= CHECK LP(A) expr(X) RP(B). {sqlite3AddCheckConstraint(pParse,X,A.z,B.z);}
|
||||
ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R).
|
||||
{sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
|
||||
ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
|
||||
@@ -420,8 +426,8 @@ tcons ::= PRIMARY KEY LP sortlist(X) autoinc(I) RP onconf(R).
|
||||
tcons ::= UNIQUE LP sortlist(X) RP onconf(R).
|
||||
{sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0,
|
||||
SQLITE_IDXTYPE_UNIQUE);}
|
||||
tcons ::= CHECK LP expr(E) RP onconf.
|
||||
{sqlite3AddCheckConstraint(pParse,E);}
|
||||
tcons ::= CHECK LP(A) expr(E) RP(B) onconf.
|
||||
{sqlite3AddCheckConstraint(pParse,E,A.z,B.z);}
|
||||
tcons ::= FOREIGN KEY LP eidlist(FA) RP
|
||||
REFERENCES nm(T) eidlist_opt(TA) refargs(R) defer_subclause_opt(D). {
|
||||
sqlite3CreateForeignKey(pParse, FA, &T, TA, R);
|
||||
|
||||
+5
-3
@@ -131,7 +131,9 @@ static int getTempStore(const char *z){
|
||||
static int invalidateTempStorage(Parse *pParse){
|
||||
sqlite3 *db = pParse->db;
|
||||
if( db->aDb[1].pBt!=0 ){
|
||||
if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){
|
||||
if( !db->autoCommit
|
||||
|| sqlite3BtreeTxnState(db->aDb[1].pBt)!=SQLITE_TXN_NONE
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "temporary storage cannot be changed "
|
||||
"from within a transaction");
|
||||
return SQLITE_ERROR;
|
||||
@@ -1451,7 +1453,7 @@ void sqlite3Pragma(
|
||||
aiCols = 0;
|
||||
if( pParent ){
|
||||
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
|
||||
assert( x==0 );
|
||||
assert( x==0 || db->mallocFailed );
|
||||
}
|
||||
addrOk = sqlite3VdbeMakeLabel(pParse);
|
||||
|
||||
@@ -1476,7 +1478,7 @@ void sqlite3Pragma(
|
||||
int jmp = sqlite3VdbeCurrentAddr(v)+2;
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, i, jmp, regRow); VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, addrOk);
|
||||
assert( pFK->nCol==1 );
|
||||
assert( pFK->nCol==1 || db->mallocFailed );
|
||||
}
|
||||
|
||||
/* Generate code to report an FK violation to the caller. */
|
||||
|
||||
+3
-2
@@ -240,7 +240,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
** on the b-tree database, open one now. If a transaction is opened, it
|
||||
** will be closed before this function returns. */
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
|
||||
if( sqlite3BtreeTxnState(pDb->pBt)==SQLITE_TXN_NONE ){
|
||||
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
|
||||
@@ -483,7 +483,7 @@ static void schemaIsValid(Parse *pParse){
|
||||
/* If there is not already a read-only (or read-write) transaction opened
|
||||
** on the b-tree database, open one now. If a transaction is opened, it
|
||||
** will be closed immediately after reading the meta-value. */
|
||||
if( !sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_NONE ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
@@ -746,6 +746,7 @@ static int sqlite3LockAndPrepare(
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( (rc&db->errMask)==rc );
|
||||
db->busyHandler.nBusy = 0;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+6
-7
@@ -407,11 +407,11 @@ void sqlite3_str_vappendf(
|
||||
case etPOINTER:
|
||||
flag_long = sizeof(char*)==sizeof(i64) ? 2 :
|
||||
sizeof(char*)==sizeof(long int) ? 1 : 0;
|
||||
/* Fall through into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
case etORDINAL:
|
||||
case etRADIX:
|
||||
cThousand = 0;
|
||||
/* Fall through into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
case etDECIMAL:
|
||||
if( infop->flags & FLAG_SIGNED ){
|
||||
i64 v;
|
||||
@@ -427,11 +427,10 @@ void sqlite3_str_vappendf(
|
||||
v = va_arg(ap,int);
|
||||
}
|
||||
if( v<0 ){
|
||||
if( v==SMALLEST_INT64 ){
|
||||
longvalue = ((u64)1)<<63;
|
||||
}else{
|
||||
longvalue = -v;
|
||||
}
|
||||
testcase( v==SMALLEST_INT64 );
|
||||
testcase( v==(-1) );
|
||||
longvalue = ~v;
|
||||
longvalue++;
|
||||
prefix = '-';
|
||||
}else{
|
||||
longvalue = v;
|
||||
|
||||
+4
-2
@@ -1075,7 +1075,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
assert( !ExprHasProperty(pExpr, EP_Reduced) );
|
||||
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
|
||||
** and "x IS NOT FALSE". */
|
||||
if( pRight && pRight->op==TK_ID ){
|
||||
if( pRight && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){
|
||||
int rc = resolveExprStep(pWalker, pRight);
|
||||
if( rc==WRC_Abort ) return WRC_Abort;
|
||||
if( pRight->op==TK_TRUEFALSE ){
|
||||
@@ -1084,7 +1084,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
return WRC_Continue;
|
||||
}
|
||||
}
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case TK_BETWEEN:
|
||||
case TK_EQ:
|
||||
@@ -1284,6 +1284,7 @@ static int resolveCompoundOrderBy(
|
||||
Expr *pE, *pDup;
|
||||
if( pItem->done ) continue;
|
||||
pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
|
||||
if( NEVER(pE==0) ) continue;
|
||||
if( sqlite3ExprIsInteger(pE, &iCol) ){
|
||||
if( iCol<=0 || iCol>pEList->nExpr ){
|
||||
resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
|
||||
@@ -1463,6 +1464,7 @@ static int resolveOrderGroupBy(
|
||||
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
|
||||
Expr *pE = pItem->pExpr;
|
||||
Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
|
||||
if( NEVER(pE2==0) ) continue;
|
||||
if( zType[0]!='G' ){
|
||||
iCol = resolveAsName(pParse, pSelect->pEList, pE2);
|
||||
if( iCol>0 ){
|
||||
|
||||
+90
-60
@@ -1972,6 +1972,7 @@ int sqlite3ColumnsFromExprList(
|
||||
char *zName; /* Column name */
|
||||
int nName; /* Size of name in zName[] */
|
||||
Hash ht; /* Hash table of column names */
|
||||
Table *pTab;
|
||||
|
||||
sqlite3HashInit(&ht);
|
||||
if( pEList ){
|
||||
@@ -1994,15 +1995,13 @@ int sqlite3ColumnsFromExprList(
|
||||
/* If the column contains an "AS <name>" phrase, use <name> as the name */
|
||||
}else{
|
||||
Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
|
||||
while( pColExpr->op==TK_DOT ){
|
||||
while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){
|
||||
pColExpr = pColExpr->pRight;
|
||||
assert( pColExpr!=0 );
|
||||
}
|
||||
if( pColExpr->op==TK_COLUMN ){
|
||||
if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){
|
||||
/* For columns use the column name name */
|
||||
int iCol = pColExpr->iColumn;
|
||||
Table *pTab = pColExpr->y.pTab;
|
||||
assert( pTab!=0 );
|
||||
if( iCol<0 ) iCol = pTab->iPKey;
|
||||
zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
|
||||
}else if( pColExpr->op==TK_ID ){
|
||||
@@ -2340,6 +2339,7 @@ static void generateWithRecursiveQuery(
|
||||
int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
|
||||
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
|
||||
Select *pSetup = p->pPrior; /* The setup query */
|
||||
Select *pFirstRec; /* Left-most recursive term */
|
||||
int addrTop; /* Top of the loop */
|
||||
int addrCont, addrBreak; /* CONTINUE and BREAK addresses */
|
||||
int iCurrent = 0; /* The Current table */
|
||||
@@ -2415,7 +2415,25 @@ static void generateWithRecursiveQuery(
|
||||
/* Detach the ORDER BY clause from the compound SELECT */
|
||||
p->pOrderBy = 0;
|
||||
|
||||
/* Figure out how many elements of the compound SELECT are part of the
|
||||
** recursive query. Make sure no recursive elements use aggregate
|
||||
** functions. Mark the recursive elements as UNION ALL even if they
|
||||
** are really UNION because the distinctness will be enforced by the
|
||||
** iDistinct table. pFirstRec is left pointing to the left-most
|
||||
** recursive term of the CTE.
|
||||
*/
|
||||
pFirstRec = p;
|
||||
for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){
|
||||
if( pFirstRec->selFlags & SF_Aggregate ){
|
||||
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
||||
goto end_of_recursive_query;
|
||||
}
|
||||
pFirstRec->op = TK_ALL;
|
||||
if( (pFirstRec->pPrior->selFlags & SF_Recursive)==0 ) break;
|
||||
}
|
||||
|
||||
/* Store the results of the setup-query in Queue. */
|
||||
pSetup = pFirstRec->pPrior;
|
||||
pSetup->pNext = 0;
|
||||
ExplainQueryPlan((pParse, 1, "SETUP"));
|
||||
rc = sqlite3Select(pParse, pSetup, &destQueue);
|
||||
@@ -2448,15 +2466,11 @@ static void generateWithRecursiveQuery(
|
||||
/* Execute the recursive SELECT taking the single row in Current as
|
||||
** the value for the recursive-table. Store the results in the Queue.
|
||||
*/
|
||||
if( p->selFlags & SF_Aggregate ){
|
||||
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
||||
}else{
|
||||
p->pPrior = 0;
|
||||
ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
|
||||
sqlite3Select(pParse, p, &destQueue);
|
||||
assert( p->pPrior==0 );
|
||||
p->pPrior = pSetup;
|
||||
}
|
||||
pFirstRec->pPrior = 0;
|
||||
ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
|
||||
sqlite3Select(pParse, p, &destQueue);
|
||||
assert( pFirstRec->pPrior==0 );
|
||||
pFirstRec->pPrior = pSetup;
|
||||
|
||||
/* Keep running the loop until the Queue is empty */
|
||||
sqlite3VdbeGoto(v, addrTop);
|
||||
@@ -2525,6 +2539,16 @@ static int multiSelectValues(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the SELECT statement which is known to be the recursive
|
||||
** part of a recursive CTE still has its anchor terms attached. If the
|
||||
** anchor terms have already been removed, then return false.
|
||||
*/
|
||||
static int hasAnchor(Select *p){
|
||||
while( p && (p->selFlags & SF_Recursive)!=0 ){ p = p->pPrior; }
|
||||
return p!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to process a compound query form from
|
||||
** two or more separate queries using UNION, UNION ALL, EXCEPT, or
|
||||
@@ -2610,7 +2634,7 @@ static int multiSelect(
|
||||
assert( p->pEList->nExpr==pPrior->pEList->nExpr );
|
||||
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
if( p->selFlags & SF_Recursive ){
|
||||
if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){
|
||||
generateWithRecursiveQuery(pParse, p, &dest);
|
||||
}else
|
||||
#endif
|
||||
@@ -2701,6 +2725,7 @@ static int multiSelect(
|
||||
findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
||||
assert( p->pEList );
|
||||
}
|
||||
|
||||
|
||||
/* Code the SELECT statements to our left
|
||||
*/
|
||||
@@ -3506,7 +3531,7 @@ static Expr *substExpr(
|
||||
ifNullRow.op = TK_IF_NULL_ROW;
|
||||
ifNullRow.pLeft = pCopy;
|
||||
ifNullRow.iTable = pSubst->iNewTable;
|
||||
ifNullRow.flags = EP_Skip;
|
||||
ifNullRow.flags = EP_IfNullRow;
|
||||
pCopy = &ifNullRow;
|
||||
}
|
||||
testcase( ExprHasProperty(pCopy, EP_Subquery) );
|
||||
@@ -3515,8 +3540,7 @@ static Expr *substExpr(
|
||||
ExprSetProperty(pNew, EP_CanBeNull);
|
||||
}
|
||||
if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){
|
||||
pNew->iRightJoinTable = pExpr->iRightJoinTable;
|
||||
ExprSetProperty(pNew, EP_FromJoin);
|
||||
sqlite3SetJoinExpr(pNew, pExpr->iRightJoinTable);
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
pExpr = pNew;
|
||||
@@ -4186,7 +4210,7 @@ static int flattenSubquery(
|
||||
sqlite3SelectDelete(db, pSub1);
|
||||
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -4795,8 +4819,10 @@ static int withExpand(
|
||||
ExprList *pEList;
|
||||
Select *pSel;
|
||||
Select *pLeft; /* Left-most SELECT statement */
|
||||
Select *pRecTerm; /* Left-most recursive term */
|
||||
int bMayRecursive; /* True if compound joined by UNION [ALL] */
|
||||
With *pSavedWith; /* Initial value of pParse->pWith */
|
||||
int iRecTab = -1; /* Cursor for recursive table */
|
||||
|
||||
/* 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
|
||||
@@ -4821,44 +4847,48 @@ static int withExpand(
|
||||
assert( pFrom->pSelect );
|
||||
|
||||
/* Check if this is a recursive CTE. */
|
||||
pSel = pFrom->pSelect;
|
||||
pRecTerm = pSel = pFrom->pSelect;
|
||||
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
|
||||
if( bMayRecursive ){
|
||||
while( bMayRecursive && pRecTerm->op==pSel->op ){
|
||||
int i;
|
||||
SrcList *pSrc = pFrom->pSelect->pSrc;
|
||||
SrcList *pSrc = pRecTerm->pSrc;
|
||||
assert( pRecTerm->pPrior!=0 );
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
struct SrcList_item *pItem = &pSrc->a[i];
|
||||
if( pItem->zDatabase==0
|
||||
&& pItem->zName!=0
|
||||
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
|
||||
){
|
||||
){
|
||||
pItem->pTab = pTab;
|
||||
pItem->fg.isRecursive = 1;
|
||||
pTab->nTabRef++;
|
||||
pSel->selFlags |= SF_Recursive;
|
||||
pItem->fg.isRecursive = 1;
|
||||
if( pRecTerm->selFlags & SF_Recursive ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"multiple references to recursive table: %s", pCte->zName
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pRecTerm->selFlags |= SF_Recursive;
|
||||
if( iRecTab<0 ) iRecTab = pParse->nTab++;
|
||||
pItem->iCursor = iRecTab;
|
||||
}
|
||||
}
|
||||
if( (pRecTerm->selFlags & SF_Recursive)==0 ) break;
|
||||
pRecTerm = pRecTerm->pPrior;
|
||||
}
|
||||
|
||||
/* Only one recursive reference is permitted. */
|
||||
if( pTab->nTabRef>2 ){
|
||||
sqlite3ErrorMsg(
|
||||
pParse, "multiple references to recursive table: %s", pCte->zName
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
assert( pTab->nTabRef==1 ||
|
||||
((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 ));
|
||||
|
||||
pCte->zCteErr = "circular reference: %s";
|
||||
pSavedWith = pParse->pWith;
|
||||
pParse->pWith = pWith;
|
||||
if( bMayRecursive ){
|
||||
Select *pPrior = pSel->pPrior;
|
||||
assert( pPrior->pWith==0 );
|
||||
pPrior->pWith = pSel->pWith;
|
||||
sqlite3WalkSelect(pWalker, pPrior);
|
||||
pPrior->pWith = 0;
|
||||
if( pSel->selFlags & SF_Recursive ){
|
||||
assert( pRecTerm!=0 );
|
||||
assert( (pRecTerm->selFlags & SF_Recursive)==0 );
|
||||
assert( pRecTerm->pNext!=0 );
|
||||
assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 );
|
||||
assert( pRecTerm->pWith==0 );
|
||||
pRecTerm->pWith = pSel->pWith;
|
||||
sqlite3WalkSelect(pWalker, pRecTerm);
|
||||
pRecTerm->pWith = 0;
|
||||
}else{
|
||||
sqlite3WalkSelect(pWalker, pSel);
|
||||
}
|
||||
@@ -5585,7 +5615,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 ){
|
||||
@@ -5624,7 +5656,7 @@ static void havingToWhere(Parse *pParse, Select *p){
|
||||
sWalker.u.pSelect = p;
|
||||
sqlite3WalkExpr(&sWalker, p->pHaving);
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){
|
||||
if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){
|
||||
SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -5746,7 +5778,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
p->selFlags &= ~SF_Aggregate;
|
||||
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x400 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -5799,7 +5831,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( sqlite3SelectTrace & 0x100 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x100 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -5808,13 +5840,11 @@ int sqlite3Select(
|
||||
assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
|
||||
assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
|
||||
assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue );
|
||||
if( IgnorableOrderby(pDest) ){
|
||||
assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
|
||||
pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
|
||||
pDest->eDest==SRT_Queue || pDest->eDest==SRT_DistFifo ||
|
||||
pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_Fifo);
|
||||
/* If ORDER BY makes no difference in the output then neither does
|
||||
** DISTINCT so it can be removed too. */
|
||||
if( IgnorableDistinct(pDest) ){
|
||||
assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
|
||||
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;
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
@@ -5826,7 +5856,7 @@ int sqlite3Select(
|
||||
}
|
||||
assert( p->pEList!=0 );
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x104 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x104 ){
|
||||
SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -5861,7 +5891,7 @@ int sqlite3Select(
|
||||
goto select_end;
|
||||
}
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){
|
||||
if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){
|
||||
SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -5968,7 +5998,7 @@ int sqlite3Select(
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
|
||||
if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -5987,7 +6017,7 @@ int sqlite3Select(
|
||||
&& propagateConstants(pParse, p)
|
||||
){
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -6075,7 +6105,7 @@ int sqlite3Select(
|
||||
(pItem->fg.jointype & JT_OUTER)!=0)
|
||||
){
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,
|
||||
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
@@ -6175,7 +6205,7 @@ int sqlite3Select(
|
||||
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
|
||||
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x400 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -6211,7 +6241,7 @@ int sqlite3Select(
|
||||
assert( sDistinct.isTnct );
|
||||
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x400 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -6459,7 +6489,7 @@ int sqlite3Select(
|
||||
pAggInfo->mxReg = pParse->nMem;
|
||||
if( db->mallocFailed ) goto select_end;
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x400 ){
|
||||
if( sqlite3_unsupported_selecttrace & 0x400 ){
|
||||
int ii;
|
||||
SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
@@ -6878,7 +6908,7 @@ select_end:
|
||||
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end processing\n"));
|
||||
if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
+198
-78
@@ -553,8 +553,6 @@ static void utf8_width_print(FILE *pOut, int w, const char *zUtf){
|
||||
int i;
|
||||
int n;
|
||||
int aw = w<0 ? -w : w;
|
||||
char zBuf[1000];
|
||||
if( aw>(int)sizeof(zBuf)/3 ) aw = (int)sizeof(zBuf)/3;
|
||||
for(i=n=0; zUtf[i]; i++){
|
||||
if( (zUtf[i]&0xc0)!=0x80 ){
|
||||
n++;
|
||||
@@ -1028,6 +1026,7 @@ INCLUDE ../ext/misc/memtrace.c
|
||||
INCLUDE ../ext/misc/uint.c
|
||||
INCLUDE ../ext/misc/decimal.c
|
||||
INCLUDE ../ext/misc/ieee754.c
|
||||
INCLUDE ../ext/misc/series.c
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
INCLUDE ../ext/misc/zipfile.c
|
||||
INCLUDE ../ext/misc/sqlar.c
|
||||
@@ -1185,6 +1184,8 @@ struct ShellState {
|
||||
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
|
||||
#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
|
||||
#define SHFLG_HeaderSet 0x00000080 /* .header has been used */
|
||||
#define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */
|
||||
#define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */
|
||||
|
||||
/*
|
||||
** Macros for testing and setting shellFlgs
|
||||
@@ -2881,7 +2882,7 @@ static void explain_data_delete(ShellState *p){
|
||||
** Disable and restore .wheretrace and .selecttrace settings.
|
||||
*/
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
extern int sqlite3SelectTrace;
|
||||
extern unsigned int sqlite3_unsupported_selecttrace;
|
||||
static int savedSelectTrace;
|
||||
#endif
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
|
||||
@@ -2890,8 +2891,8 @@ static int savedWhereTrace;
|
||||
#endif
|
||||
static void disable_debug_trace_modes(void){
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
savedSelectTrace = sqlite3SelectTrace;
|
||||
sqlite3SelectTrace = 0;
|
||||
savedSelectTrace = sqlite3_unsupported_selecttrace;
|
||||
sqlite3_unsupported_selecttrace = 0;
|
||||
#endif
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
|
||||
savedWhereTrace = sqlite3WhereTrace;
|
||||
@@ -2900,7 +2901,7 @@ static void disable_debug_trace_modes(void){
|
||||
}
|
||||
static void restore_debug_trace_modes(void){
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
sqlite3SelectTrace = savedSelectTrace;
|
||||
sqlite3_unsupported_selecttrace = savedSelectTrace;
|
||||
#endif
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
|
||||
sqlite3WhereTrace = savedWhereTrace;
|
||||
@@ -3051,25 +3052,38 @@ static void exec_prepared_stmt_columnar(
|
||||
ShellState *p, /* Pointer to ShellState */
|
||||
sqlite3_stmt *pStmt /* Statment to run */
|
||||
){
|
||||
int nRow = 0;
|
||||
sqlite3_int64 nRow = 0;
|
||||
int nColumn = 0;
|
||||
char **azData = 0;
|
||||
char *zMsg = 0;
|
||||
sqlite3_int64 nAlloc = 0;
|
||||
const char *z;
|
||||
int rc;
|
||||
int i, j, nTotal, w, n;
|
||||
sqlite3_int64 i, nData;
|
||||
int j, nTotal, w, n;
|
||||
const char *colSep = 0;
|
||||
const char *rowSep = 0;
|
||||
|
||||
rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt),
|
||||
&azData, &nRow, &nColumn, &zMsg);
|
||||
if( rc ){
|
||||
utf8_printf(p->out, "ERROR: %s\n", zMsg);
|
||||
sqlite3_free(zMsg);
|
||||
sqlite3_free_table(azData);
|
||||
return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ) return;
|
||||
nColumn = sqlite3_column_count(pStmt);
|
||||
nAlloc = nColumn*4;
|
||||
azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
|
||||
if( azData==0 ) shell_out_of_memory();
|
||||
for(i=0; i<nColumn; i++){
|
||||
azData[i] = strdup(sqlite3_column_name(pStmt,i));
|
||||
}
|
||||
if( nRow==0 || nColumn==0 ) goto columnar_end;
|
||||
do{
|
||||
if( (nRow+2)*nColumn >= nAlloc ){
|
||||
nAlloc *= 2;
|
||||
azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
|
||||
if( azData==0 ) shell_out_of_memory();
|
||||
}
|
||||
nRow++;
|
||||
for(i=0; i<nColumn; i++){
|
||||
z = (const char*)sqlite3_column_text(pStmt,i);
|
||||
azData[nRow*nColumn + i] = z ? strdup(z) : 0;
|
||||
}
|
||||
}while( (rc = sqlite3_step(pStmt))==SQLITE_ROW );
|
||||
if( nColumn>p->nWidth ){
|
||||
p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int));
|
||||
if( p->colWidth==0 ) shell_out_of_memory();
|
||||
@@ -3179,7 +3193,9 @@ columnar_end:
|
||||
if( seenInterrupt ){
|
||||
utf8_printf(p->out, "Interrupt\n");
|
||||
}
|
||||
sqlite3_free_table(azData);
|
||||
nData = (nRow+1)*nColumn;
|
||||
for(i=0; i<nData; i++) free(azData[i]);
|
||||
sqlite3_free(azData);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3690,19 +3706,25 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
|
||||
const char *zType;
|
||||
const char *zSql;
|
||||
ShellState *p = (ShellState *)pArg;
|
||||
int dataOnly;
|
||||
int noSys;
|
||||
|
||||
UNUSED_PARAMETER(azNotUsed);
|
||||
if( nArg!=3 || azArg==0 ) return 0;
|
||||
zTable = azArg[0];
|
||||
zType = azArg[1];
|
||||
zSql = azArg[2];
|
||||
dataOnly = (p->shellFlgs & SHFLG_DumpDataOnly)!=0;
|
||||
noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0;
|
||||
|
||||
if( strcmp(zTable, "sqlite_sequence")==0 ){
|
||||
raw_printf(p->out, "DELETE FROM sqlite_sequence;\n");
|
||||
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
|
||||
raw_printf(p->out, "ANALYZE sqlite_schema;\n");
|
||||
if( strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
|
||||
if( !dataOnly ) raw_printf(p->out, "DELETE FROM sqlite_sequence;\n");
|
||||
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){
|
||||
if( !dataOnly ) raw_printf(p->out, "ANALYZE sqlite_schema;\n");
|
||||
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
|
||||
return 0;
|
||||
}else if( dataOnly ){
|
||||
/* no-op */
|
||||
}else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
|
||||
char *zIns;
|
||||
if( !p->writableSchema ){
|
||||
@@ -3875,8 +3897,10 @@ static const char *(azHelp[]) = {
|
||||
".dbinfo ?DB? Show status information about the database",
|
||||
".dump ?TABLE? Render database content as SQL",
|
||||
" Options:",
|
||||
" --preserve-rowids Include ROWID values in the output",
|
||||
" --data-only Output only INSERT statements",
|
||||
" --newlines Allow unescaped newline characters in output",
|
||||
" --nosys Omit system tables (ex: \"sqlite_stat1\")",
|
||||
" --preserve-rowids Include ROWID values in the output",
|
||||
" TABLE is a LIKE pattern for the tables to dump",
|
||||
" Additional LIKE patterns can be given in subsequent arguments",
|
||||
".echo on|off Turn command echo on or off",
|
||||
@@ -4001,8 +4025,9 @@ static const char *(azHelp[]) = {
|
||||
".save FILE Write in-memory database into FILE",
|
||||
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
|
||||
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
|
||||
" Options:",
|
||||
" --indent Try to pretty-print the schema",
|
||||
" Options:",
|
||||
" --indent Try to pretty-print the schema",
|
||||
" --nosys Omit objects whose names start with \"sqlite_\"",
|
||||
".selftest ?OPTIONS? Run tests defined in the SELFTEST table",
|
||||
" Options:",
|
||||
" --init Create a new SELFTEST table",
|
||||
@@ -4411,6 +4436,20 @@ static void shellIdQuote(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
|
||||
*/
|
||||
static void shellUSleepFunc(
|
||||
sqlite3_context *context,
|
||||
int argcUnused,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int sleep = sqlite3_value_int(argv[0]);
|
||||
(void)argcUnused;
|
||||
sqlite3_sleep(sleep/1000);
|
||||
sqlite3_result_int(context, sleep);
|
||||
}
|
||||
|
||||
/*
|
||||
** Scalar function "shell_escape_crnl" used by the .recover command.
|
||||
** The argument passed to this function is the output of built-in
|
||||
@@ -4575,6 +4614,7 @@ static void open_db(ShellState *p, int openFlags){
|
||||
sqlite3_uint_init(p->db, 0, 0);
|
||||
sqlite3_decimal_init(p->db, 0, 0);
|
||||
sqlite3_ieee_init(p->db, 0, 0);
|
||||
sqlite3_series_init(p->db, 0, 0);
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
sqlite3_dbdata_init(p->db, 0, 0);
|
||||
#endif
|
||||
@@ -4594,6 +4634,8 @@ static void open_db(ShellState *p, int openFlags){
|
||||
shellInt32, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_idquote", 1, SQLITE_UTF8, 0,
|
||||
shellIdQuote, 0, 0);
|
||||
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
|
||||
shellUSleepFunc, 0, 0);
|
||||
#ifndef SQLITE_NOHAVE_SYSTEM
|
||||
sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0,
|
||||
editFunc, 0, 0);
|
||||
@@ -7568,21 +7610,41 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}else
|
||||
|
||||
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
|
||||
ShellState data;
|
||||
char *zErrMsg = 0;
|
||||
char **azName = 0;
|
||||
int nName = 0;
|
||||
sqlite3_stmt *pStmt;
|
||||
int i;
|
||||
open_db(p, 0);
|
||||
memcpy(&data, p, sizeof(data));
|
||||
data.showHeader = 0;
|
||||
data.cMode = data.mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(data.colSeparator),data.colSeparator,": ");
|
||||
data.cnt = 0;
|
||||
sqlite3_exec(p->db, "SELECT name, file FROM pragma_database_list",
|
||||
callback, &data, &zErrMsg);
|
||||
if( zErrMsg ){
|
||||
utf8_printf(stderr,"Error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
|
||||
if( rc ){
|
||||
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
||||
rc = 1;
|
||||
}else{
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
|
||||
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
|
||||
azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*));
|
||||
if( azName==0 ){ shell_out_of_memory(); /* Does not return */ }
|
||||
azName[nName*2] = strdup(zSchema);
|
||||
azName[nName*2+1] = strdup(zFile);
|
||||
nName++;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
for(i=0; i<nName; i++){
|
||||
int eTxn = sqlite3_txn_state(p->db, azName[i*2]);
|
||||
int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]);
|
||||
const char *z = azName[i*2+1];
|
||||
utf8_printf(p->out, "%s: %s %s%s\n",
|
||||
azName[i*2],
|
||||
z && z[0] ? z : "\"\"",
|
||||
bRdonly ? "r/o" : "r/w",
|
||||
eTxn==SQLITE_TXN_NONE ? "" :
|
||||
eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn");
|
||||
free(azName[i*2]);
|
||||
free(azName[i*2+1]);
|
||||
}
|
||||
sqlite3_free(azName);
|
||||
}else
|
||||
|
||||
if( c=='d' && n>=3 && strncmp(azArg[0], "dbconfig", n)==0 ){
|
||||
@@ -7641,7 +7703,9 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
int i;
|
||||
int savedShowHeader = p->showHeader;
|
||||
int savedShellFlags = p->shellFlgs;
|
||||
ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
|
||||
ShellClearFlag(p,
|
||||
SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo
|
||||
|SHFLG_DumpDataOnly|SHFLG_DumpNoSys);
|
||||
for(i=1; i<nArg; i++){
|
||||
if( azArg[i][0]=='-' ){
|
||||
const char *z = azArg[i]+1;
|
||||
@@ -7660,6 +7724,12 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( strcmp(z,"newlines")==0 ){
|
||||
ShellSetFlag(p, SHFLG_Newlines);
|
||||
}else
|
||||
if( strcmp(z,"data-only")==0 ){
|
||||
ShellSetFlag(p, SHFLG_DumpDataOnly);
|
||||
}else
|
||||
if( strcmp(z,"nosys")==0 ){
|
||||
ShellSetFlag(p, SHFLG_DumpNoSys);
|
||||
}else
|
||||
{
|
||||
raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
|
||||
rc = 1;
|
||||
@@ -7676,11 +7746,13 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
open_db(p, 0);
|
||||
|
||||
/* When playing back a "dump", the content might appear in an order
|
||||
** which causes immediate foreign key constraints to be violated.
|
||||
** So disable foreign-key constraint enforcement to prevent problems. */
|
||||
raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n");
|
||||
raw_printf(p->out, "BEGIN TRANSACTION;\n");
|
||||
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
|
||||
/* When playing back a "dump", the content might appear in an order
|
||||
** which causes immediate foreign key constraints to be violated.
|
||||
** So disable foreign-key constraint enforcement to prevent problems. */
|
||||
raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n");
|
||||
raw_printf(p->out, "BEGIN TRANSACTION;\n");
|
||||
}
|
||||
p->writableSchema = 0;
|
||||
p->showHeader = 0;
|
||||
/* Set writable_schema=ON since doing so forces SQLite to initialize
|
||||
@@ -7698,14 +7770,16 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
);
|
||||
run_schema_dump_query(p,zSql);
|
||||
sqlite3_free(zSql);
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT sql FROM sqlite_schema "
|
||||
"WHERE (%s) AND sql NOT NULL"
|
||||
" AND type IN ('index','trigger','view')",
|
||||
zLike
|
||||
);
|
||||
run_table_dump_query(p, zSql);
|
||||
sqlite3_free(zSql);
|
||||
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT sql FROM sqlite_schema "
|
||||
"WHERE (%s) AND sql NOT NULL"
|
||||
" AND type IN ('index','trigger','view')",
|
||||
zLike
|
||||
);
|
||||
run_table_dump_query(p, zSql);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
sqlite3_free(zLike);
|
||||
if( p->writableSchema ){
|
||||
raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
|
||||
@@ -7713,7 +7787,9 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
|
||||
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
|
||||
raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");
|
||||
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
|
||||
raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");
|
||||
}
|
||||
p->showHeader = savedShowHeader;
|
||||
p->shellFlgs = savedShellFlags;
|
||||
}else
|
||||
@@ -8152,7 +8228,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
while( (nSkip--)>0 ){
|
||||
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
|
||||
}
|
||||
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
|
||||
zSql = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
|
||||
if( zSql==0 ){
|
||||
import_cleanup(&sCtx);
|
||||
shell_out_of_memory();
|
||||
@@ -8161,7 +8237,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
|
||||
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
|
||||
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
|
||||
char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\"", zTable);
|
||||
char cSep = '(';
|
||||
while( xRead(&sCtx) ){
|
||||
zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z);
|
||||
@@ -8182,7 +8258,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
|
||||
sqlite3_free(zCreate);
|
||||
if( rc ){
|
||||
utf8_printf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable,
|
||||
utf8_printf(stderr, "CREATE TABLE \"%s\"(...) failed: %s\n", zTable,
|
||||
sqlite3_errmsg(p->db));
|
||||
import_cleanup(&sCtx);
|
||||
rc = 1;
|
||||
@@ -8947,9 +9023,16 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
if( notNormalFile(azArg[1])
|
||||
|| (p->in = fopen(azArg[1], "rb"))==0
|
||||
){
|
||||
if( azArg[1][0]=='|' ){
|
||||
p->in = popen(azArg[1]+1, "r");
|
||||
if( p->in==0 ){
|
||||
utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
|
||||
rc = 1;
|
||||
}else{
|
||||
rc = process_input(p);
|
||||
pclose(p->in);
|
||||
}
|
||||
}else if( notNormalFile(azArg[1]) || (p->in = fopen(azArg[1], "rb"))==0 ){
|
||||
utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
|
||||
rc = 1;
|
||||
}else{
|
||||
@@ -9031,6 +9114,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
const char *zName = 0;
|
||||
int iSchema = 0;
|
||||
int bDebug = 0;
|
||||
int bNoSystemTabs = 0;
|
||||
int ii;
|
||||
|
||||
open_db(p, 0);
|
||||
@@ -9043,10 +9127,16 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
data.cMode = data.mode = MODE_Pretty;
|
||||
}else if( optionMatch(azArg[ii],"debug") ){
|
||||
bDebug = 1;
|
||||
}else if( optionMatch(azArg[ii],"nosys") ){
|
||||
bNoSystemTabs = 1;
|
||||
}else if( azArg[ii][0]=='-' ){
|
||||
utf8_printf(stderr, "Unknown option: \"%s\"\n", azArg[ii]);
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}else if( zName==0 ){
|
||||
zName = azArg[ii];
|
||||
}else{
|
||||
raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n");
|
||||
raw_printf(stderr, "Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
@@ -9132,7 +9222,10 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
appendText(&sSelect, " AND ", 0);
|
||||
sqlite3_free(zQarg);
|
||||
}
|
||||
appendText(&sSelect, "type!='meta' AND sql IS NOT NULL"
|
||||
if( bNoSystemTabs ){
|
||||
appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0);
|
||||
}
|
||||
appendText(&sSelect, "sql IS NOT NULL"
|
||||
" ORDER BY snum, rowid", 0);
|
||||
if( bDebug ){
|
||||
utf8_printf(p->out, "SQL: %s;\n", sSelect.z);
|
||||
@@ -9155,7 +9248,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
|
||||
sqlite3SelectTrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff;
|
||||
sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff;
|
||||
}else
|
||||
#endif
|
||||
|
||||
@@ -9829,6 +9922,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
{ "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
|
||||
{ "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
|
||||
{ "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
|
||||
{ "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, "" },
|
||||
};
|
||||
int testctrl = -1;
|
||||
int iCtrl = -1;
|
||||
@@ -9891,7 +9985,6 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
/* sqlite3_test_control(int) */
|
||||
case SQLITE_TESTCTRL_PRNG_SAVE:
|
||||
case SQLITE_TESTCTRL_PRNG_RESTORE:
|
||||
case SQLITE_TESTCTRL_PRNG_RESET:
|
||||
case SQLITE_TESTCTRL_BYTEORDER:
|
||||
if( nArg==2 ){
|
||||
rc2 = sqlite3_test_control(testctrl);
|
||||
@@ -9965,6 +10058,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
break;
|
||||
|
||||
case SQLITE_TESTCTRL_SEEK_COUNT: {
|
||||
u64 x = 0;
|
||||
rc2 = sqlite3_test_control(testctrl, p->db, &x);
|
||||
utf8_printf(p->out, "%llu\n", x);
|
||||
isOk = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef YYCOVERAGE
|
||||
case SQLITE_TESTCTRL_PARSER_COVERAGE:
|
||||
if( nArg==2 ){
|
||||
@@ -10543,8 +10644,11 @@ static void process_sqliterc(
|
||||
if( stdin_is_interactive ){
|
||||
utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc);
|
||||
}
|
||||
process_input(p);
|
||||
if( process_input(p) && bail_on_error ) exit(1);
|
||||
fclose(p->in);
|
||||
}else if( sqliterc_override!=0 ){
|
||||
utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc);
|
||||
if( bail_on_error ) exit(1);
|
||||
}
|
||||
p->in = inSaved;
|
||||
p->lineno = savedLineno;
|
||||
@@ -10603,6 +10707,7 @@ static const char zOptions[] =
|
||||
#endif
|
||||
" -stats print memory stats before each finalize\n"
|
||||
" -table set output mode to 'table'\n"
|
||||
" -tabs set output mode to 'tabs'\n"
|
||||
" -version show SQLite version\n"
|
||||
" -vfs NAME use NAME as the default VFS\n"
|
||||
#ifdef SQLITE_ENABLE_VFSTRACE
|
||||
@@ -10860,11 +10965,14 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
(void)cmdline_option_value(argc, argv, ++i);
|
||||
#endif
|
||||
}else if( strcmp(z,"-pagecache")==0 ){
|
||||
int n, sz;
|
||||
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
|
||||
sqlite3_int64 n, sz;
|
||||
sz = integerValue(cmdline_option_value(argc,argv,++i));
|
||||
if( sz>70000 ) sz = 70000;
|
||||
if( sz<0 ) sz = 0;
|
||||
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
|
||||
n = integerValue(cmdline_option_value(argc,argv,++i));
|
||||
if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
|
||||
n = 0xffffffffffffLL/sz;
|
||||
}
|
||||
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
|
||||
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
|
||||
data.shellFlgs |= SHFLG_Pagecache;
|
||||
@@ -10926,6 +11034,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
#endif
|
||||
}else if( strcmp(z, "-memtrace")==0 ){
|
||||
sqlite3MemTraceActivate(stderr);
|
||||
}else if( strcmp(z,"-bail")==0 ){
|
||||
bail_on_error = 1;
|
||||
}
|
||||
}
|
||||
verify_uninitialized();
|
||||
@@ -11000,6 +11110,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
data.mode = MODE_List;
|
||||
}else if( strcmp(z,"-quote")==0 ){
|
||||
data.mode = MODE_Quote;
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Comma);
|
||||
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Row);
|
||||
}else if( strcmp(z,"-line")==0 ){
|
||||
data.mode = MODE_Line;
|
||||
}else if( strcmp(z,"-column")==0 ){
|
||||
@@ -11033,10 +11145,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
|
||||
}else if( strcmp(z,"-ascii")==0 ){
|
||||
data.mode = MODE_Ascii;
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
|
||||
SEP_Unit);
|
||||
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
|
||||
SEP_Record);
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Unit);
|
||||
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Record);
|
||||
}else if( strcmp(z,"-tabs")==0 ){
|
||||
data.mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Tab);
|
||||
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Row);
|
||||
}else if( strcmp(z,"-separator")==0 ){
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
|
||||
"%s",cmdline_option_value(argc,argv,++i));
|
||||
@@ -11068,7 +11182,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
*/
|
||||
ShellSetFlag(&data, SHFLG_Backslash);
|
||||
}else if( strcmp(z,"-bail")==0 ){
|
||||
bail_on_error = 1;
|
||||
/* No-op. The bail_on_error flag should already be set. */
|
||||
}else if( strcmp(z,"-version")==0 ){
|
||||
printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
|
||||
return 0;
|
||||
@@ -11156,20 +11270,25 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
for(i=0; i<nCmd; i++){
|
||||
if( azCmd[i][0]=='.' ){
|
||||
rc = do_meta_command(azCmd[i], &data);
|
||||
if( rc ) return rc==2 ? 0 : rc;
|
||||
if( rc ){
|
||||
free(azCmd);
|
||||
return rc==2 ? 0 : rc;
|
||||
}
|
||||
}else{
|
||||
open_db(&data, 0);
|
||||
rc = shell_exec(&data, azCmd[i], &zErrMsg);
|
||||
if( zErrMsg!=0 ){
|
||||
utf8_printf(stderr,"Error: %s\n", zErrMsg);
|
||||
if( zErrMsg || rc ){
|
||||
if( zErrMsg!=0 ){
|
||||
utf8_printf(stderr,"Error: %s\n", zErrMsg);
|
||||
}else{
|
||||
utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
|
||||
}
|
||||
sqlite3_free(zErrMsg);
|
||||
free(azCmd);
|
||||
return rc!=0 ? rc : 1;
|
||||
}else if( rc!=0 ){
|
||||
utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(azCmd);
|
||||
}else{
|
||||
/* Run commands received from standard input
|
||||
*/
|
||||
@@ -11215,6 +11334,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
rc = process_input(&data);
|
||||
}
|
||||
}
|
||||
free(azCmd);
|
||||
set_table_name(&data, 0);
|
||||
if( data.db ){
|
||||
session_close_all(&data);
|
||||
|
||||
+69
-6
@@ -504,6 +504,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
|
||||
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
|
||||
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
|
||||
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
@@ -3696,7 +3697,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
|
||||
@@ -6186,6 +6187,57 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
||||
*/
|
||||
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine the transaction state of a database
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_txn_state(D,S) interface returns the current
|
||||
** [transaction state] of schema S in database connection D. ^If S is NULL,
|
||||
** then the highest transaction state of any schema on database connection D
|
||||
** is returned. Transaction states are (in order of lowest to highest):
|
||||
** <ol>
|
||||
** <li value="0"> SQLITE_TXN_NONE
|
||||
** <li value="1"> SQLITE_TXN_READ
|
||||
** <li value="2"> SQLITE_TXN_WRITE
|
||||
** </ol>
|
||||
** ^If the S argument to sqlite3_txn_state(D,S) is not the name of
|
||||
** a valid schema, then -1 is returned.
|
||||
*/
|
||||
int sqlite3_txn_state(sqlite3*,const char *zSchema);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
|
||||
** KEYWORDS: {transaction state}
|
||||
**
|
||||
** These constants define the current transaction state of a database file.
|
||||
** ^The [sqlite3_txn_state(D,S)] interface returns one of these
|
||||
** constants in order to describe the transaction state of schema S
|
||||
** in [database connection] D.
|
||||
**
|
||||
** <dl>
|
||||
** [[SQLITE_TXN_NONE]] <dt>SQLITE_TXN_NONE</dt>
|
||||
** <dd>The SQLITE_TXN_NONE state means that no transaction is currently
|
||||
** pending.</dd>
|
||||
**
|
||||
** [[SQLITE_TXN_READ]] <dt>SQLITE_TXN_READ</dt>
|
||||
** <dd>The SQLITE_TXN_READ state means that the database is currently
|
||||
** in a read transaction. Content has been read from the database file
|
||||
** but nothing in the database file has changed. The transaction state
|
||||
** will advanced to SQLITE_TXN_WRITE if any changes occur and there are
|
||||
** no other conflicting concurrent write transactions. The transaction
|
||||
** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or
|
||||
** [COMMIT].</dd>
|
||||
**
|
||||
** [[SQLITE_TXN_WRITE]] <dt>SQLITE_TXN_WRITE</dt>
|
||||
** <dd>The SQLITE_TXN_WRITE state means that the database is currently
|
||||
** in a write transaction. Content has been written to the database file
|
||||
** but has not yet committed. The transaction state will change to
|
||||
** to SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].</dd>
|
||||
*/
|
||||
#define SQLITE_TXN_NONE 0
|
||||
#define SQLITE_TXN_READ 1
|
||||
#define SQLITE_TXN_WRITE 2
|
||||
|
||||
/*
|
||||
** CAPI3REF: Find the next prepared statement
|
||||
** METHOD: sqlite3
|
||||
@@ -7712,7 +7764,8 @@ 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_LAST 30 /* Largest TESTCTRL */
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Keyword Checking
|
||||
@@ -9192,10 +9245,11 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
|
||||
** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE
|
||||
**
|
||||
** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn]
|
||||
** method of a [virtual table], then it returns true if and only if the
|
||||
** method of a [virtual table], then it might return true if the
|
||||
** column is being fetched as part of an UPDATE operation during which the
|
||||
** column value will not change. Applications might use this to substitute
|
||||
** a return value that is less expensive to compute and that the corresponding
|
||||
** column value will not change. The virtual table implementation can use
|
||||
** this hint as permission to substitute a return value that is less
|
||||
** expensive to compute and that the corresponding
|
||||
** [xUpdate] method understands as a "no-change" value.
|
||||
**
|
||||
** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
|
||||
@@ -9204,6 +9258,12 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
|
||||
** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
|
||||
** In that case, [sqlite3_value_nochange(X)] will return true for the
|
||||
** same column in the [xUpdate] method.
|
||||
**
|
||||
** The sqlite3_vtab_nochange() routine is an optimization. Virtual table
|
||||
** implementations should continue to give a correct answer even if the
|
||||
** sqlite3_vtab_nochange() interface were to always return false. In the
|
||||
** current implementation, the sqlite3_vtab_nochange() interface does always
|
||||
** returns false for the enhanced [UPDATE FROM] statement.
|
||||
*/
|
||||
int sqlite3_vtab_nochange(sqlite3_context*);
|
||||
|
||||
@@ -9345,6 +9405,7 @@ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flush caches to disk mid-transaction
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^If a write-transaction is open on [database connection] D when the
|
||||
** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
|
||||
@@ -9377,6 +9438,7 @@ int sqlite3_db_cacheflush(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: The pre-update hook.
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^These interfaces are only available if SQLite is compiled using the
|
||||
** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
|
||||
@@ -9417,7 +9479,7 @@ int sqlite3_db_cacheflush(sqlite3*);
|
||||
** seventh parameter is the final rowid value of the row being inserted
|
||||
** or updated. The value of the seventh parameter passed to the callback
|
||||
** function is not defined for operations on WITHOUT ROWID tables, or for
|
||||
** INSERT operations on rowid tables.
|
||||
** DELETE operations on rowid tables.
|
||||
**
|
||||
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
|
||||
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
|
||||
@@ -9479,6 +9541,7 @@ int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Low-level system error code
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^Attempt to return the underlying operating system error code or error
|
||||
** number that caused the most recent I/O error or failure to open a file.
|
||||
|
||||
@@ -335,6 +335,8 @@ struct sqlite3_api_routines {
|
||||
int,const char**);
|
||||
void (*free_filename)(char*);
|
||||
sqlite3_file *(*database_file_object)(const char*);
|
||||
/* Version 3.34.0 and later */
|
||||
int (*txn_state)(sqlite3*,const char*);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -639,6 +641,8 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
||||
/* Version 3.34.0 and later */
|
||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
||||
+30
-22
@@ -128,6 +128,15 @@
|
||||
# define _BSD_SOURCE
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macro to disable warnings about missing "break" at the end of a "case".
|
||||
*/
|
||||
#if GCC_VERSION>=7000000
|
||||
# define deliberate_fall_through __attribute__((fallthrough));
|
||||
#else
|
||||
# define deliberate_fall_through
|
||||
#endif
|
||||
|
||||
/*
|
||||
** For MinGW, check to see if we can include the header file containing its
|
||||
** version information, among other things. Normally, this internal MinGW
|
||||
@@ -983,7 +992,7 @@ typedef INT16_TYPE LogEst;
|
||||
#if defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
# define SELECTTRACE_ENABLED 1
|
||||
# define SELECTTRACE(K,P,S,X) \
|
||||
if(sqlite3SelectTrace&(K)) \
|
||||
if(sqlite3_unsupported_selecttrace&(K)) \
|
||||
sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
|
||||
sqlite3DebugPrintf X
|
||||
#else
|
||||
@@ -1046,7 +1055,7 @@ struct BusyHandler {
|
||||
** pointer will work here as long as it is distinct from SQLITE_STATIC
|
||||
** and SQLITE_TRANSIENT.
|
||||
*/
|
||||
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize)
|
||||
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault)
|
||||
|
||||
/*
|
||||
** When SQLITE_OMIT_WSD is defined, it means that the target platform does
|
||||
@@ -1497,7 +1506,10 @@ struct sqlite3 {
|
||||
int nVDestroy; /* Number of active OP_VDestroy operations */
|
||||
int nExtension; /* Number of loaded extensions */
|
||||
void **aExtension; /* Array of shared library handles */
|
||||
int (*xTrace)(u32,void*,void*,void*); /* Trace function */
|
||||
union {
|
||||
void (*xLegacy)(void*,const char*); /* Legacy trace function */
|
||||
int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */
|
||||
} trace;
|
||||
void *pTraceArg; /* Argument to the trace function */
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
void (*xProfile)(void*,const char*,u64); /* Profiling function */
|
||||
@@ -2175,6 +2187,7 @@ struct Table {
|
||||
#define TF_OOOHidden 0x0400 /* Out-of-Order hidden columns */
|
||||
#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 */
|
||||
|
||||
/*
|
||||
** Test to see whether or not a table is a virtual table. This is
|
||||
@@ -2540,11 +2553,6 @@ struct AggInfo {
|
||||
AggInfo *pNext; /* Next in list of them all */
|
||||
};
|
||||
|
||||
/*
|
||||
** Value for AggInfo.iAggMagic when the structure is valid
|
||||
*/
|
||||
#define AggInfoMagic 0x2059e99e
|
||||
|
||||
/*
|
||||
** The datatype ynVar is a signed integer, either 16-bit or 32-bit.
|
||||
** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater
|
||||
@@ -2708,7 +2716,7 @@ struct Expr {
|
||||
#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Win 0x008000 /* Contains window functions */
|
||||
#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
|
||||
/* 0x020000 // available for reuse */
|
||||
#define EP_IfNullRow 0x020000 /* The TK_IF_NULL_ROW opcode */
|
||||
#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
|
||||
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
|
||||
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
||||
@@ -2946,9 +2954,9 @@ struct SrcList {
|
||||
#define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */
|
||||
#define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */
|
||||
#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
|
||||
#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */
|
||||
/* 0x0400 not currently used */
|
||||
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
|
||||
#define WHERE_SEEK_UNIQ_TABLE 0x1000 /* Do not defer seeks if unique */
|
||||
/* 0x1000 not currently used */
|
||||
/* 0x2000 not currently used */
|
||||
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
|
||||
/* 0x8000 not currently used */
|
||||
@@ -3145,9 +3153,6 @@ struct Select {
|
||||
** statements within triggers whose only purpose is
|
||||
** the side-effects of functions.
|
||||
**
|
||||
** All of the above are free to ignore their ORDER BY clause. Those that
|
||||
** follow must honor the ORDER BY clause.
|
||||
**
|
||||
** SRT_Output Generate a row of output (using the OP_ResultRow
|
||||
** opcode) for each row in the result set.
|
||||
**
|
||||
@@ -3204,13 +3209,18 @@ struct Select {
|
||||
#define SRT_Except 2 /* Remove result from a UNION index */
|
||||
#define SRT_Exists 3 /* Store 1 if the result is not empty */
|
||||
#define SRT_Discard 4 /* Do not save the results anywhere */
|
||||
#define SRT_Fifo 5 /* Store result as data with an automatic rowid */
|
||||
#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */
|
||||
#define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */
|
||||
#define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */
|
||||
|
||||
/* The DISTINCT clause is ignored for all of the above. Not that
|
||||
** IgnorableDistinct() implies IgnorableOrderby() */
|
||||
#define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue)
|
||||
|
||||
#define SRT_Queue 7 /* Store result in an queue */
|
||||
#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */
|
||||
#define SRT_Fifo 8 /* Store result as data with an automatic rowid */
|
||||
|
||||
/* The ORDER BY clause is ignored for all of the above */
|
||||
#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue)
|
||||
#define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo)
|
||||
|
||||
#define SRT_Output 9 /* Output each row of result */
|
||||
#define SRT_Mem 10 /* Store result in a memory cell */
|
||||
@@ -3383,9 +3393,7 @@ struct Parse {
|
||||
ynVar nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
#if !(defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE))
|
||||
u8 eParseMode; /* PARSE_MODE_XXX constant */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
int nVtabLock; /* Number of virtual tables to lock */
|
||||
#endif
|
||||
@@ -4179,7 +4187,7 @@ void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
|
||||
void sqlite3AddColumn(Parse*,Token*,Token*);
|
||||
void sqlite3AddNotNull(Parse*, int);
|
||||
void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
|
||||
void sqlite3AddCheckConstraint(Parse*, Expr*);
|
||||
void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*);
|
||||
void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
|
||||
void sqlite3AddCollateType(Parse*, Token*);
|
||||
void sqlite3AddGenerated(Parse*,Expr*,Token*);
|
||||
@@ -4586,7 +4594,7 @@ extern const unsigned char sqlite3UpperToLower[];
|
||||
extern const unsigned char sqlite3CtypeMap[];
|
||||
extern SQLITE_WSD struct Sqlite3Config sqlite3Config;
|
||||
extern FuncDefHash sqlite3BuiltinFunctions;
|
||||
extern u32 sqlite3SelectTrace;
|
||||
extern u32 sqlite3_unsupported_selecttrace;
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
extern int sqlite3PendingByte;
|
||||
#endif
|
||||
|
||||
+1
-5
@@ -60,11 +60,7 @@
|
||||
** The maximum depth of an expression tree. This is limited to
|
||||
** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
|
||||
** want to place more severe limits on the complexity of an
|
||||
** expression.
|
||||
**
|
||||
** A value of 0 used to mean that the limit was not enforced.
|
||||
** But that is no longer true. The limit is now strictly enforced
|
||||
** at all times.
|
||||
** expression. A value of 0 means that there is no limit.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_EXPR_DEPTH
|
||||
# define SQLITE_MAX_EXPR_DEPTH 1000
|
||||
|
||||
+1
-1
@@ -352,7 +352,7 @@ int sqlite3_db_status(
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_SPILL:
|
||||
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
|
||||
/* Fall through into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
case SQLITE_DBSTATUS_CACHE_HIT:
|
||||
case SQLITE_DBSTATUS_CACHE_MISS:
|
||||
case SQLITE_DBSTATUS_CACHE_WRITE:{
|
||||
|
||||
+335
-4
@@ -3940,7 +3940,7 @@ static int SQLITE_TCLAPI test_bind_blob(
|
||||
char zBuf[200];
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf,
|
||||
"cannot use %d blob bytes, have %d", bytes, len);
|
||||
Tcl_AppendResult(interp, zBuf, -1);
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -3953,6 +3953,195 @@ static int SQLITE_TCLAPI test_bind_blob(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** sqlite3_carray_bind [options...] STMT NAME VALUE ...
|
||||
**
|
||||
** Options:
|
||||
** -transient
|
||||
** -static
|
||||
** -int32
|
||||
** -int64
|
||||
** -double
|
||||
** -text
|
||||
**
|
||||
** Each call clears static data. Called with no options does nothing
|
||||
** but clear static data.
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_carray_bind(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int eType = 0; /* CARRAY_INT32 */
|
||||
int nData = 0;
|
||||
void *aData = 0;
|
||||
int isTransient = 0;
|
||||
int isStatic = 0;
|
||||
int idx;
|
||||
int i, j;
|
||||
int rc;
|
||||
void (*xDel)(void*) = sqlite3_free;
|
||||
static void *aStaticData = 0;
|
||||
static int nStaticData = 0;
|
||||
static int eStaticType = 0;
|
||||
extern int sqlite3_carray_bind(
|
||||
sqlite3_stmt *pStmt,
|
||||
int i,
|
||||
void *aData,
|
||||
int nData,
|
||||
int mFlags,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
|
||||
if( aStaticData ){
|
||||
/* Always clear preexisting static data on every call */
|
||||
if( eStaticType==3 ){
|
||||
for(i=0; i<nStaticData; i++){
|
||||
sqlite3_free(((char**)aStaticData)[i]);
|
||||
}
|
||||
}
|
||||
sqlite3_free(aStaticData);
|
||||
aStaticData = 0;
|
||||
nStaticData = 0;
|
||||
eStaticType = 0;
|
||||
}
|
||||
if( objc==1 ) return TCL_OK;
|
||||
|
||||
for(i=1; i<objc && Tcl_GetString(objv[i])[0]=='-'; i++){
|
||||
const char *z = Tcl_GetString(objv[i]);
|
||||
if( strcmp(z, "-transient")==0 ){
|
||||
isTransient = 1;
|
||||
xDel = SQLITE_TRANSIENT;
|
||||
}else
|
||||
if( strcmp(z, "-static")==0 ){
|
||||
isStatic = 1;
|
||||
xDel = SQLITE_STATIC;
|
||||
}else
|
||||
if( strcmp(z, "-int32")==0 ){
|
||||
eType = 0; /* CARRAY_INT32 */
|
||||
}else
|
||||
if( strcmp(z, "-int64")==0 ){
|
||||
eType = 1; /* CARRAY_INT64 */
|
||||
}else
|
||||
if( strcmp(z, "-double")==0 ){
|
||||
eType = 2; /* CARRAY_DOUBLE */
|
||||
}else
|
||||
if( strcmp(z, "-text")==0 ){
|
||||
eType = 3; /* CARRAY_TEXT */
|
||||
}else
|
||||
if( strcmp(z, "--")==0 ){
|
||||
break;
|
||||
}else
|
||||
{
|
||||
Tcl_AppendResult(interp, "unknown option: ", z, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
if( eType==3 && !isStatic && !isTransient ){
|
||||
Tcl_AppendResult(interp, "text data must be either -static or -transient",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( isStatic && isTransient ){
|
||||
Tcl_AppendResult(interp, "cannot be both -static and -transient",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( objc-i < 2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "[OPTIONS] STMT IDX VALUE ...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[i]), &pStmt) ) return TCL_ERROR;
|
||||
i++;
|
||||
if( Tcl_GetIntFromObj(interp, objv[i], &idx) ) return TCL_ERROR;
|
||||
i++;
|
||||
nData = objc - i;
|
||||
switch( eType + 4*(nData<=0) ){
|
||||
case 0: { /* INT32 */
|
||||
int *a = sqlite3_malloc( sizeof(int)*nData );
|
||||
if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
|
||||
for(j=0; j<nData; j++){
|
||||
int v;
|
||||
if( Tcl_GetIntFromObj(interp, objv[i+j], &v) ){
|
||||
sqlite3_free(a);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
a[j] = v;
|
||||
}
|
||||
aData = a;
|
||||
break;
|
||||
}
|
||||
case 1: { /* INT64 */
|
||||
sqlite3_int64 *a = sqlite3_malloc( sizeof(sqlite3_int64)*nData );
|
||||
if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
|
||||
for(j=0; j<nData; j++){
|
||||
Tcl_WideInt v;
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[i+j], &v) ){
|
||||
sqlite3_free(a);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
a[j] = v;
|
||||
}
|
||||
aData = a;
|
||||
break;
|
||||
}
|
||||
case 2: { /* DOUBLE */
|
||||
double *a = sqlite3_malloc( sizeof(double)*nData );
|
||||
if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
|
||||
for(j=0; j<nData; j++){
|
||||
double v;
|
||||
if( Tcl_GetDoubleFromObj(interp, objv[i+j], &v) ){
|
||||
sqlite3_free(a);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
a[j] = v;
|
||||
}
|
||||
aData = a;
|
||||
break;
|
||||
}
|
||||
case 3: { /* TEXT */
|
||||
char **a = sqlite3_malloc( sizeof(char*)*nData );
|
||||
if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
|
||||
for(j=0; j<nData; j++){
|
||||
const char *v = Tcl_GetString(objv[i+j]);
|
||||
a[j] = sqlite3_mprintf("%s", v);
|
||||
}
|
||||
aData = a;
|
||||
break;
|
||||
}
|
||||
case 4: { /* nData==0 */
|
||||
aData = "";
|
||||
xDel = SQLITE_STATIC;
|
||||
isTransient = 0;
|
||||
isStatic = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( isStatic ){
|
||||
aStaticData = aData;
|
||||
nStaticData = nData;
|
||||
eStaticType = eType;
|
||||
}
|
||||
rc = sqlite3_carray_bind(pStmt, idx, aData, nData, eType, xDel);
|
||||
if( isTransient ){
|
||||
if( eType==3 ){
|
||||
for(i=0; i<nData; i++) sqlite3_free(((char**)aData)[i]);
|
||||
}
|
||||
sqlite3_free(aData);
|
||||
}
|
||||
carray_bind_done:
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, sqlite3_errstr(rc), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_bind_parameter_count STMT
|
||||
**
|
||||
@@ -6239,6 +6428,36 @@ static int SQLITE_TCLAPI file_control_vfsname(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_reservebytes DB N
|
||||
*/
|
||||
static int SQLITE_TCLAPI file_control_reservebytes(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zDbName = "main";
|
||||
int n = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
|
||||
|| Tcl_GetIntFromObj(interp, objv[2], &n)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_file_control(db, zDbName, SQLITE_FCNTL_RESERVE_BYTES, (void*)&n);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_tempfilename DB ?AUXDB?
|
||||
**
|
||||
@@ -7749,6 +7968,33 @@ static int SQLITE_TCLAPI test_sqlite3_db_config(
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(v));
|
||||
return TCL_OK;
|
||||
}
|
||||
/*
|
||||
** tclcmd: sqlite3_txn_state DB ?SCHEMA?
|
||||
**
|
||||
** Invoke sqlite3_txn_state(DB,SCHEMA) and return the
|
||||
** numeric value that results. Use NULL for SCHEMA if the 3 argument
|
||||
** is omitted.
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3_txn_state(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zSchema;
|
||||
int iTxn;
|
||||
|
||||
if( objc!=2 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB ?SCHEMA?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSchema = objc==3 ? Tcl_GetString(objv[2]) : 0;
|
||||
iTxn = sqlite3_txn_state(db, zSchema);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iTxn));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the name of the main database schema from "main" to "icecube".
|
||||
@@ -7802,6 +8048,83 @@ static int SQLITE_TCLAPI test_mmap_warm(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: test_write_db DB OFFSET DATA
|
||||
**
|
||||
** Obtain the sqlite3_file* object for the database file for the "main" db
|
||||
** of handle DB. Then invoke its xWrite method to write data DATA to offset
|
||||
** OFFSET.
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_write_db(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db = 0;
|
||||
Tcl_WideInt iOff = 0;
|
||||
const unsigned char *aData = 0;
|
||||
int nData = 0;
|
||||
sqlite3_file *pFile = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB OFFSET DATA");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[2], &iOff) ) return TCL_ERROR;
|
||||
aData = Tcl_GetByteArrayFromObj(objv[3], &nData);
|
||||
|
||||
sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFile);
|
||||
rc = pFile->pMethods->xWrite(pFile, aData, nData, iOff);
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_register_cksumvfs
|
||||
**
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_register_cksumvfs(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
extern int sqlite3_register_cksumvfs(const char*);
|
||||
int rc = sqlite3_register_cksumvfs(0);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_unregister_cksumvfs
|
||||
**
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_unregister_cksumvfs(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
extern int sqlite3_unregister_cksumvfs(void);
|
||||
int rc = sqlite3_unregister_cksumvfs();
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: decode_hexdb TEXT
|
||||
**
|
||||
@@ -7950,6 +8273,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "sqlite3_db_config", test_sqlite3_db_config, 0 },
|
||||
{ "sqlite3_txn_state", test_sqlite3_txn_state, 0 },
|
||||
{ "bad_behavior", test_bad_behavior, (void*)&iZero },
|
||||
{ "register_dbstat_vtab", test_register_dbstat_vtab },
|
||||
{ "sqlite3_connection_pointer", get_sqlite_pointer, 0 },
|
||||
@@ -7966,6 +8290,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_bind_text", test_bind_text ,0 },
|
||||
{ "sqlite3_bind_text16", test_bind_text16 ,0 },
|
||||
{ "sqlite3_bind_blob", test_bind_blob ,0 },
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
{ "sqlite3_carray_bind", test_carray_bind ,0 },
|
||||
#endif
|
||||
{ "sqlite3_bind_parameter_count", test_bind_parameter_count, 0},
|
||||
{ "sqlite3_bind_parameter_name", test_bind_parameter_name, 0},
|
||||
{ "sqlite3_bind_parameter_index", test_bind_parameter_index, 0},
|
||||
@@ -8097,6 +8424,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "file_control_persist_wal", file_control_persist_wal, 0 },
|
||||
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
|
||||
{ "file_control_vfsname", file_control_vfsname, 0 },
|
||||
{ "file_control_reservebytes", file_control_reservebytes, 0 },
|
||||
{ "file_control_tempfilename", file_control_tempfilename, 0 },
|
||||
{ "sqlite3_vfs_list", vfs_list, 0 },
|
||||
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
|
||||
@@ -8166,6 +8494,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_mmap_warm", test_mmap_warm, 0 },
|
||||
{ "sqlite3_config_sorterref", test_config_sorterref, 0 },
|
||||
{ "decode_hexdb", test_decode_hexdb, 0 },
|
||||
{ "test_write_db", test_write_db, 0 },
|
||||
{ "sqlite3_register_cksumvfs", test_register_cksumvfs, 0 },
|
||||
{ "sqlite3_unregister_cksumvfs", test_unregister_cksumvfs, 0 },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
|
||||
@@ -8191,7 +8522,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
#endif
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
extern u32 sqlite3SelectTrace;
|
||||
extern u32 sqlite3_unsupported_selecttrace;
|
||||
#endif
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
@@ -8279,8 +8610,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
Tcl_LinkVar(interp, "sqlite_fullsync_count",
|
||||
(char*)&sqlite3_fullsync_count, TCL_LINK_INT);
|
||||
#if defined(SQLITE_ENABLE_SELECTTRACE)
|
||||
Tcl_LinkVar(interp, "sqlite3SelectTrace",
|
||||
(char*)&sqlite3SelectTrace, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "sqlite3_unsupported_selecttrace",
|
||||
(char*)&sqlite3_unsupported_selecttrace, TCL_LINK_INT);
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_TEST)
|
||||
Tcl_LinkVar(interp, "sqlite_fts3_enable_parentheses",
|
||||
|
||||
+8
-4
@@ -550,13 +550,16 @@ static int cfDeviceCharacteristics(sqlite3_file *pFile){
|
||||
** Pass-throughs for WAL support.
|
||||
*/
|
||||
static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
||||
return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, ofst, n, flags);
|
||||
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
|
||||
return pReal->pMethods->xShmLock(pReal, ofst, n, flags);
|
||||
}
|
||||
static void cfShmBarrier(sqlite3_file *pFile){
|
||||
sqlite3OsShmBarrier(((CrashFile*)pFile)->pRealFile);
|
||||
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
|
||||
pReal->pMethods->xShmBarrier(pReal);
|
||||
}
|
||||
static int cfShmUnmap(sqlite3_file *pFile, int delFlag){
|
||||
return sqlite3OsShmUnmap(((CrashFile*)pFile)->pRealFile, delFlag);
|
||||
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
|
||||
return pReal->pMethods->xShmUnmap(pReal, delFlag);
|
||||
}
|
||||
static int cfShmMap(
|
||||
sqlite3_file *pFile, /* Handle open on database file */
|
||||
@@ -565,7 +568,8 @@ static int cfShmMap(
|
||||
int w, /* True to extend file if necessary */
|
||||
void volatile **pp /* OUT: Mapped memory */
|
||||
){
|
||||
return sqlite3OsShmMap(((CrashFile*)pFile)->pRealFile, iRegion, sz, w, pp);
|
||||
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
|
||||
return pReal->pMethods->xShmMap(pReal, iRegion, sz, w, pp);
|
||||
}
|
||||
|
||||
static const sqlite3_io_methods CrashFileVtab = {
|
||||
|
||||
+4
-4
@@ -191,7 +191,7 @@ static int devsymDeviceCharacteristics(sqlite3_file *pFile){
|
||||
*/
|
||||
static int devsymShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsShmLock(p->pReal, ofst, n, flags);
|
||||
return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
|
||||
}
|
||||
static int devsymShmMap(
|
||||
sqlite3_file *pFile,
|
||||
@@ -201,15 +201,15 @@ static int devsymShmMap(
|
||||
void volatile **pp
|
||||
){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
|
||||
return p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
|
||||
}
|
||||
static void devsymShmBarrier(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
sqlite3OsShmBarrier(p->pReal);
|
||||
p->pReal->pMethods->xShmBarrier(p->pReal);
|
||||
}
|
||||
static int devsymShmUnmap(sqlite3_file *pFile, int delFlag){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsShmUnmap(p->pReal, delFlag);
|
||||
return p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -168,7 +168,7 @@ static int SQLITE_TCLAPI hexio_write(
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
|
||||
zFile = Tcl_GetString(objv[1]);
|
||||
zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn);
|
||||
aOut = sqlite3_malloc( nIn/2 );
|
||||
aOut = sqlite3_malloc( 1 + nIn/2 );
|
||||
if( aOut==0 ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ static int SQLITE_TCLAPI hexio_get_int(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn);
|
||||
aOut = sqlite3_malloc( nIn/2 );
|
||||
aOut = sqlite3_malloc( 1 + nIn/2 );
|
||||
if( aOut==0 ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -309,7 +309,7 @@ static int SQLITE_TCLAPI utf8_to_utf8(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n);
|
||||
z = sqlite3_malloc( n+3 );
|
||||
z = sqlite3_malloc( n+4 );
|
||||
n = sqlite3TestHexToBin(zOrig, n, z);
|
||||
z[n] = 0;
|
||||
nOut = sqlite3Utf8To8(z);
|
||||
|
||||
+8
-4
@@ -895,7 +895,8 @@ static int tvfsShmMap(
|
||||
Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
|
||||
|
||||
if( p->isFullshm ){
|
||||
return sqlite3OsShmMap(pFd->pReal, iPage, pgsz, isWrite, pp);
|
||||
sqlite3_file *pReal = pFd->pReal;
|
||||
return pReal->pMethods->xShmMap(pReal, iPage, pgsz, isWrite, pp);
|
||||
}
|
||||
|
||||
if( 0==pFd->pShm ){
|
||||
@@ -945,7 +946,8 @@ static int tvfsShmLock(
|
||||
char zLock[80];
|
||||
|
||||
if( p->isFullshm ){
|
||||
return sqlite3OsShmLock(pFd->pReal, ofst, n, flags);
|
||||
sqlite3_file *pReal = pFd->pReal;
|
||||
return pReal->pMethods->xShmLock(pReal, ofst, n, flags);
|
||||
}
|
||||
|
||||
if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
|
||||
@@ -1009,7 +1011,8 @@ static void tvfsShmBarrier(sqlite3_file *pFile){
|
||||
}
|
||||
|
||||
if( p->isFullshm ){
|
||||
sqlite3OsShmBarrier(pFd->pReal);
|
||||
sqlite3_file *pReal = pFd->pReal;
|
||||
pReal->pMethods->xShmBarrier(pReal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1025,7 +1028,8 @@ static int tvfsShmUnmap(
|
||||
TestvfsFd **ppFd;
|
||||
|
||||
if( p->isFullshm ){
|
||||
return sqlite3OsShmUnmap(pFd->pReal, deleteFlag);
|
||||
sqlite3_file *pReal = pFd->pReal;
|
||||
return pReal->pMethods->xShmUnmap(pReal, deleteFlag);
|
||||
}
|
||||
|
||||
if( !pBuffer ) return SQLITE_OK;
|
||||
|
||||
@@ -422,6 +422,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
}
|
||||
/* If the next character is a digit, this is a floating point
|
||||
** number that begins with ".". Fall thru into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case CC_DIGIT: {
|
||||
testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' );
|
||||
@@ -526,6 +527,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
#endif
|
||||
/* If it is not a BLOB literal, then it must be an ID, since no
|
||||
** SQL keywords start with the letter 'x'. Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case CC_ID: {
|
||||
i = 1;
|
||||
|
||||
+21
-15
@@ -157,22 +157,11 @@ void sqlite3BeginTrigger(
|
||||
pTab = sqlite3SrcListLookup(pParse, pTableName);
|
||||
if( !pTab ){
|
||||
/* The table does not exist. */
|
||||
if( db->init.iDb==1 ){
|
||||
/* Ticket #3810.
|
||||
** Normally, whenever a table is dropped, all associated triggers are
|
||||
** dropped too. But if a TEMP trigger is created on a non-TEMP table
|
||||
** and the table is dropped by a different database connection, the
|
||||
** trigger is not visible to the database connection that does the
|
||||
** drop so the trigger cannot be dropped. This results in an
|
||||
** "orphaned trigger" - a trigger whose associated table is missing.
|
||||
*/
|
||||
db->init.orphanTrigger = 1;
|
||||
}
|
||||
goto trigger_cleanup;
|
||||
goto trigger_orphan_error;
|
||||
}
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
|
||||
goto trigger_cleanup;
|
||||
goto trigger_orphan_error;
|
||||
}
|
||||
|
||||
/* Check that the trigger name is not reserved and that no trigger of the
|
||||
@@ -210,12 +199,12 @@ void sqlite3BeginTrigger(
|
||||
if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
|
||||
sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
|
||||
(tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
|
||||
goto trigger_cleanup;
|
||||
goto trigger_orphan_error;
|
||||
}
|
||||
if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
|
||||
sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
|
||||
" trigger on table: %S", pTableName, 0);
|
||||
goto trigger_cleanup;
|
||||
goto trigger_orphan_error;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
@@ -275,6 +264,23 @@ trigger_cleanup:
|
||||
}else{
|
||||
assert( pParse->pNewTrigger==pTrigger );
|
||||
}
|
||||
return;
|
||||
|
||||
trigger_orphan_error:
|
||||
if( db->init.iDb==1 ){
|
||||
/* Ticket #3810.
|
||||
** Normally, whenever a table is dropped, all associated triggers are
|
||||
** dropped too. But if a TEMP trigger is created on a non-TEMP table
|
||||
** and the table is dropped by a different database connection, the
|
||||
** trigger is not visible to the database connection that does the
|
||||
** drop so the trigger cannot be dropped. This results in an
|
||||
** "orphaned trigger" - a trigger whose associated table is missing.
|
||||
**
|
||||
** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df
|
||||
*/
|
||||
db->init.orphanTrigger = 1;
|
||||
}
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+29
-13
@@ -234,7 +234,7 @@ static void updateFromSelect(
|
||||
#endif
|
||||
pList = sqlite3ExprListAppend(pParse, pList, pNew);
|
||||
}
|
||||
eDest = SRT_Upfrom;
|
||||
eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
|
||||
}else if( pTab->pSelect ){
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i));
|
||||
@@ -651,6 +651,8 @@ void sqlite3Update(
|
||||
|
||||
if( nChangeFrom==0 && HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
iEph = pParse->nTab++;
|
||||
addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet);
|
||||
}else{
|
||||
assert( pPk!=0 || HasRowid(pTab) );
|
||||
nPk = pPk ? pPk->nKeyCol : 0;
|
||||
@@ -705,7 +707,7 @@ void sqlite3Update(
|
||||
** be deleted as a result of REPLACE conflict handling. Any of these
|
||||
** things might disturb a cursor being used to scan through the table
|
||||
** or index, causing a single-pass approach to malfunction. */
|
||||
flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
||||
flags = WHERE_ONEPASS_DESIRED;
|
||||
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
||||
flags |= WHERE_ONEPASS_MULTIROW;
|
||||
}
|
||||
@@ -742,9 +744,10 @@ void sqlite3Update(
|
||||
** leave it in register regOldRowid. */
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
/* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */
|
||||
aRegIdx[nAllIdx] = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid);
|
||||
}else{
|
||||
if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||
}
|
||||
}else{
|
||||
/* Read the PK of the current row into an array of registers. In
|
||||
@@ -832,8 +835,9 @@ void sqlite3Update(
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
}else{
|
||||
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
|
||||
regOldRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
||||
labelContinue = sqlite3VdbeMakeLabel(pParse);
|
||||
addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
@@ -1083,11 +1087,9 @@ void sqlite3Update(
|
||||
}else if( eOnePass==ONEPASS_MULTI ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else if( pPk || nChangeFrom ){
|
||||
}else{
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeGoto(v, labelContinue);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, labelBreak);
|
||||
|
||||
@@ -1187,12 +1189,26 @@ static void updateVirtualTable(
|
||||
regArg = pParse->nMem + 1;
|
||||
pParse->nMem += nArg;
|
||||
if( pSrc->nSrc>1 ){
|
||||
Index *pPk = 0;
|
||||
Expr *pRow;
|
||||
ExprList *pList;
|
||||
if( pRowid ){
|
||||
pRow = sqlite3ExprDup(db, pRowid, 0);
|
||||
if( HasRowid(pTab) ){
|
||||
if( pRowid ){
|
||||
pRow = sqlite3ExprDup(db, pRowid, 0);
|
||||
}else{
|
||||
pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
}
|
||||
}else{
|
||||
pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
i16 iPk; /* PRIMARY KEY column */
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
assert( pPk->nKeyCol==1 );
|
||||
iPk = pPk->aiColumn[0];
|
||||
if( aXRef[iPk]>=0 ){
|
||||
pRow = sqlite3ExprDup(db, pChanges->a[aXRef[iPk]].pExpr, 0);
|
||||
}else{
|
||||
pRow = exprRowColumn(pParse, iPk);
|
||||
}
|
||||
}
|
||||
pList = sqlite3ExprListAppend(pParse, 0, pRow);
|
||||
|
||||
@@ -1206,7 +1222,7 @@ static void updateVirtualTable(
|
||||
}
|
||||
}
|
||||
|
||||
updateFromSelect(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0);
|
||||
updateFromSelect(pParse, ephemTab, pPk, pList, pSrc, pWhere, 0, 0);
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
eOnePass = ONEPASS_OFF;
|
||||
}else{
|
||||
|
||||
@@ -681,6 +681,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
incr = 1;
|
||||
}else{
|
||||
incr = 2;
|
||||
length &= ~1;
|
||||
assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
||||
for(i=3-enc; i<length && zNum[i]==0; i+=2){}
|
||||
nonNum = i<length;
|
||||
|
||||
+2
-2
@@ -339,8 +339,8 @@ SQLITE_NOINLINE int sqlite3RunVacuum(
|
||||
BTREE_APPLICATION_ID, 0, /* Preserve the application id */
|
||||
};
|
||||
|
||||
assert( 1==sqlite3BtreeIsInTrans(pTemp) );
|
||||
assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) );
|
||||
assert( SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pTemp) );
|
||||
assert( pOut!=0 || SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pMain) );
|
||||
|
||||
/* Copy Btree meta values */
|
||||
for(i=0; i<ArraySize(aCopy); i+=2){
|
||||
|
||||
+237
-36
@@ -1033,6 +1033,7 @@ case OP_HaltIfNull: { /* in3 */
|
||||
#endif
|
||||
if( (pIn3->flags & MEM_Null)==0 ) break;
|
||||
/* Fall through into OP_Halt */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
|
||||
/* Opcode: Halt P1 P2 * P4 P5
|
||||
@@ -1203,6 +1204,7 @@ case OP_String8: { /* same as TK_STRING, out2 */
|
||||
pOp->opcode = OP_String;
|
||||
assert( rc==SQLITE_OK );
|
||||
/* Fall through to the next case, OP_String */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
|
||||
/* Opcode: String P1 P2 P3 P4 P5
|
||||
@@ -1514,7 +1516,7 @@ case OP_ResultRow: {
|
||||
if( db->mallocFailed ) goto no_mem;
|
||||
|
||||
if( db->mTrace & SQLITE_TRACE_ROW ){
|
||||
db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0);
|
||||
db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -3485,7 +3487,8 @@ case OP_AutoCommit: {
|
||||
** active.
|
||||
** If P2 is non-zero, then a write-transaction is started, or if a
|
||||
** read-transaction is already active, it is upgraded to a write-transaction.
|
||||
** If P2 is zero, then a read-transaction is started.
|
||||
** If P2 is zero, then a read-transaction is started. If P2 is 2 or more
|
||||
** then an exclusive transaction is started.
|
||||
**
|
||||
** P1 is the index of the database file on which the transaction is
|
||||
** started. Index 0 is the main database file and index 1 is the
|
||||
@@ -3519,6 +3522,7 @@ case OP_Transaction: {
|
||||
|
||||
assert( p->bIsReader );
|
||||
assert( p->readOnly==0 || pOp->p2==0 );
|
||||
assert( pOp->p2>=0 && pOp->p2<=2 );
|
||||
assert( pOp->p1>=0 && pOp->p1<db->nDb );
|
||||
assert( DbMaskTest(p->btreeMask, pOp->p1) );
|
||||
if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){
|
||||
@@ -3544,7 +3548,7 @@ case OP_Transaction: {
|
||||
&& pOp->p2
|
||||
&& (db->autoCommit==0 || db->nVdbeRead>1)
|
||||
){
|
||||
assert( sqlite3BtreeIsInTrans(pBt) );
|
||||
assert( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE );
|
||||
if( p->iStatement==0 ){
|
||||
assert( db->nStatement>=0 && db->nSavepoint>=0 );
|
||||
db->nStatement++;
|
||||
@@ -3897,7 +3901,7 @@ case OP_OpenDup: {
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 P5
|
||||
/* Opcode: OpenEphemeral P1 P2 P3 P4 P5
|
||||
** Synopsis: nColumn=P2
|
||||
**
|
||||
** Open a new cursor P1 to a transient table.
|
||||
@@ -3917,6 +3921,10 @@ case OP_OpenDup: {
|
||||
** in btree.h. These flags control aspects of the operation of
|
||||
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
|
||||
** added automatically.
|
||||
**
|
||||
** If P3 is positive, then reg[P3] is modified slightly so that it
|
||||
** can be used as zero-length data for OP_Insert. This is an optimization
|
||||
** that avoids an extra OP_Blob opcode to initialize that register.
|
||||
*/
|
||||
/* Opcode: OpenAutoindex P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
@@ -3939,6 +3947,15 @@ case OP_OpenEphemeral: {
|
||||
SQLITE_OPEN_TRANSIENT_DB;
|
||||
assert( pOp->p1>=0 );
|
||||
assert( pOp->p2>=0 );
|
||||
if( pOp->p3>0 ){
|
||||
/* Make register reg[P3] into a value that can be used as the data
|
||||
** form sqlite3BtreeInsert() where the length of the data is zero. */
|
||||
assert( pOp->p2==0 ); /* Only used when number of columns is zero */
|
||||
assert( pOp->opcode==OP_OpenEphemeral );
|
||||
assert( aMem[pOp->p3].flags & MEM_Null );
|
||||
aMem[pOp->p3].n = 0;
|
||||
aMem[pOp->p3].z = "";
|
||||
}
|
||||
pCx = p->apCsr[pOp->p1];
|
||||
if( pCx && pCx->pBtx ){
|
||||
/* If the ephermeral table is already open, erase all existing content
|
||||
@@ -4381,22 +4398,172 @@ seek_not_found:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SeekHit P1 P2 * * *
|
||||
** Synopsis: seekHit=P2
|
||||
|
||||
/* Opcode: SeekScan P1 P2 * * *
|
||||
** Synopsis: Scan-ahead up to P1 rows
|
||||
**
|
||||
** Set the seekHit flag on cursor P1 to the value in P2.
|
||||
** The seekHit flag is used by the IfNoHope opcode.
|
||||
** This opcode is a prefix opcode to OP_SeekGE. In other words, this
|
||||
** opcode must be immediately followed by OP_SeekGE. This constraint is
|
||||
** checked by assert() statements.
|
||||
**
|
||||
** P1 must be a valid b-tree cursor. P2 must be a boolean value,
|
||||
** either 0 or 1.
|
||||
** This opcode uses the P1 through P4 operands of the subsequent
|
||||
** OP_SeekGE. In the text that follows, the operands of the subsequent
|
||||
** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only
|
||||
** the P1 and P2 operands of this opcode are also used, and are called
|
||||
** This.P1 and This.P2.
|
||||
**
|
||||
** This opcode helps to optimize IN operators on a multi-column index
|
||||
** where the IN operator is on the later terms of the index by avoiding
|
||||
** unnecessary seeks on the btree, substituting steps to the next row
|
||||
** of the b-tree instead. A correct answer is obtained if this opcode
|
||||
** is omitted or is a no-op.
|
||||
**
|
||||
** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which
|
||||
** is the desired entry that we want the cursor SeekGE.P1 to be pointing
|
||||
** to. Call this SeekGE.P4/P5 row the "target".
|
||||
**
|
||||
** If the SeekGE.P1 cursor is not currently pointing to a valid row,
|
||||
** then this opcode is a no-op and control passes through into the OP_SeekGE.
|
||||
**
|
||||
** If the SeekGE.P1 cursor is pointing to a valid row, then that row
|
||||
** might be the target row, or it might be near and slightly before the
|
||||
** target row. This opcode attempts to position the cursor on the target
|
||||
** row by, perhaps by invoking sqlite3BtreeStep() on the cursor
|
||||
** between 0 and This.P1 times.
|
||||
**
|
||||
** There are three possible outcomes from this opcode:<ol>
|
||||
**
|
||||
** <li> If after This.P1 steps, the cursor is still point to a place that
|
||||
** is earlier in the btree than the target row,
|
||||
** then fall through into the subsquence OP_SeekGE opcode.
|
||||
**
|
||||
** <li> If the cursor is successfully moved to the target row by 0 or more
|
||||
** sqlite3BtreeNext() calls, then jump to This.P2, which will land just
|
||||
** past the OP_IdxGT opcode that follows the OP_SeekGE.
|
||||
**
|
||||
** <li> If the cursor ends up past the target row (indicating the the target
|
||||
** row does not exist in the btree) then jump to SeekOP.P2.
|
||||
** </ol>
|
||||
*/
|
||||
case OP_SeekScan: {
|
||||
VdbeCursor *pC;
|
||||
int res;
|
||||
int nStep;
|
||||
UnpackedRecord r;
|
||||
|
||||
assert( pOp[1].opcode==OP_SeekGE );
|
||||
|
||||
/* pOp->p2 points to the first instruction past the OP_IdxGT that
|
||||
** follows the OP_SeekGE. */
|
||||
assert( pOp->p2>=(int)(pOp-aOp)+2 );
|
||||
assert( aOp[pOp->p2-1].opcode==OP_IdxGT );
|
||||
assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
|
||||
assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
|
||||
assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
|
||||
|
||||
assert( pOp->p1>0 );
|
||||
pC = p->apCsr[pOp[1].p1];
|
||||
assert( pC!=0 );
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
assert( !pC->isTable );
|
||||
if( !sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... cursor not valid - fall through\n");
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
nStep = pOp->p1;
|
||||
assert( nStep>=1 );
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)pOp[1].p4.i;
|
||||
r.default_rc = 0;
|
||||
r.aMem = &aMem[pOp[1].p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<r.nField; i++){
|
||||
assert( memIsValid(&r.aMem[i]) );
|
||||
REGISTER_TRACE(pOp[1].p3+i, &aMem[pOp[1].p3+i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
res = 0; /* Not needed. Only used to silence a warning. */
|
||||
while(1){
|
||||
rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( res>0 ){
|
||||
seekscan_search_fail:
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... %d steps and then skip\n", pOp->p1 - nStep);
|
||||
}
|
||||
#endif
|
||||
VdbeBranchTaken(1,3);
|
||||
pOp++;
|
||||
goto jump_to_p2;
|
||||
}
|
||||
if( res==0 ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... %d steps and then success\n", pOp->p1 - nStep);
|
||||
}
|
||||
#endif
|
||||
VdbeBranchTaken(2,3);
|
||||
goto jump_to_p2;
|
||||
break;
|
||||
}
|
||||
if( nStep<=0 ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... fall through after %d steps\n", pOp->p1);
|
||||
}
|
||||
#endif
|
||||
VdbeBranchTaken(0,3);
|
||||
break;
|
||||
}
|
||||
nStep--;
|
||||
rc = sqlite3BtreeNext(pC->uc.pCursor, 0);
|
||||
if( rc ){
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
goto seekscan_search_fail;
|
||||
}else{
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: SeekHit P1 P2 P3 * *
|
||||
** Synopsis: set P2<=seekHit<=P3
|
||||
**
|
||||
** Increase or decrease the seekHit value for cursor P1, if necessary,
|
||||
** so that it is no less than P2 and no greater than P3.
|
||||
**
|
||||
** The seekHit integer represents the maximum of terms in an index for which
|
||||
** there is known to be at least one match. If the seekHit value is smaller
|
||||
** than the total number of equality terms in an index lookup, then the
|
||||
** OP_IfNoHope opcode might run to see if the IN loop can be abandoned
|
||||
** early, thus saving work. This is part of the IN-early-out optimization.
|
||||
**
|
||||
** P1 must be a valid b-tree cursor.
|
||||
*/
|
||||
case OP_SeekHit: {
|
||||
VdbeCursor *pC;
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
assert( pOp->p2==0 || pOp->p2==1 );
|
||||
pC->seekHit = pOp->p2 & 1;
|
||||
assert( pOp->p3>=pOp->p2 );
|
||||
if( pC->seekHit<pOp->p2 ){
|
||||
pC->seekHit = pOp->p2;
|
||||
}else if( pC->seekHit>pOp->p3 ){
|
||||
pC->seekHit = pOp->p3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4454,16 +4621,20 @@ case OP_IfNotOpen: { /* jump */
|
||||
** Synopsis: key=r[P3@P4]
|
||||
**
|
||||
** Register P3 is the first of P4 registers that form an unpacked
|
||||
** record.
|
||||
** record. Cursor P1 is an index btree. P2 is a jump destination.
|
||||
** In other words, the operands to this opcode are the same as the
|
||||
** operands to OP_NotFound and OP_IdxGT.
|
||||
**
|
||||
** Cursor P1 is on an index btree. If the seekHit flag is set on P1, then
|
||||
** this opcode is a no-op. But if the seekHit flag of P1 is clear, then
|
||||
** check to see if there is any entry in P1 that matches the
|
||||
** prefix identified by P3 and P4. If no entry matches the prefix,
|
||||
** jump to P2. Otherwise fall through.
|
||||
** This opcode is an optimization attempt only. If this opcode always
|
||||
** falls through, the correct answer is still obtained, but extra works
|
||||
** is performed.
|
||||
**
|
||||
** This opcode behaves like OP_NotFound if the seekHit
|
||||
** flag is clear and it behaves like OP_Noop if the seekHit flag is set.
|
||||
** A value of N in the seekHit flag of cursor P1 means that there exists
|
||||
** a key P3:N that will match some record in the index. We want to know
|
||||
** if it is possible for a record P3:P4 to match some record in the
|
||||
** index. If it is not possible, we can skips some work. So if seekHit
|
||||
** is less than P4, attempt to find out if a match is possible by running
|
||||
** OP_NotFound.
|
||||
**
|
||||
** This opcode is used in IN clause processing for a multi-column key.
|
||||
** If an IN clause is attached to an element of the key other than the
|
||||
@@ -4505,8 +4676,9 @@ case OP_IfNoHope: { /* jump, in3 */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
if( pC->seekHit ) break;
|
||||
if( pC->seekHit>=pOp->p4.i ) break;
|
||||
/* Fall through into OP_NotFound */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case OP_NoConflict: /* jump, in3 */
|
||||
case OP_NotFound: /* jump, in3 */
|
||||
@@ -4586,6 +4758,7 @@ case OP_Found: { /* jump, in3 */
|
||||
}else{
|
||||
VdbeBranchTaken(takeJump||alreadyExists==0,2);
|
||||
if( takeJump || !alreadyExists ) goto jump_to_p2;
|
||||
if( pOp->opcode==OP_IfNoHope ) pC->seekHit = pOp->p4.i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4661,6 +4834,7 @@ case OP_SeekRowid: { /* jump, in3 */
|
||||
goto notExistsWithKey;
|
||||
}
|
||||
/* Fall through into OP_NotExists */
|
||||
/* no break */ deliberate_fall_through
|
||||
case OP_NotExists: /* jump, in3 */
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
|
||||
@@ -4941,7 +5115,7 @@ case OP_Insert: {
|
||||
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
|
||||
assert( pData->flags & (MEM_Blob|MEM_Str) );
|
||||
assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 );
|
||||
x.pData = pData->z;
|
||||
x.nData = pData->n;
|
||||
seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
|
||||
@@ -5432,6 +5606,7 @@ case OP_Sort: { /* jump */
|
||||
#endif
|
||||
p->aCounter[SQLITE_STMTSTATUS_SORT]++;
|
||||
/* Fall through into OP_Rewind */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
/* Opcode: Rewind P1 P2 * * *
|
||||
**
|
||||
@@ -5816,7 +5991,7 @@ case OP_FinishSeek: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IdxGE P1 P2 P3 P4 P5
|
||||
/* Opcode: IdxGE P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
**
|
||||
** The P4 register values beginning with P3 form an unpacked index
|
||||
@@ -5827,7 +6002,7 @@ case OP_FinishSeek: {
|
||||
** If the P1 index entry is greater than or equal to the key value
|
||||
** then jump to P2. Otherwise fall through to the next instruction.
|
||||
*/
|
||||
/* Opcode: IdxGT P1 P2 P3 P4 P5
|
||||
/* Opcode: IdxGT P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
**
|
||||
** The P4 register values beginning with P3 form an unpacked index
|
||||
@@ -5838,7 +6013,7 @@ case OP_FinishSeek: {
|
||||
** If the P1 index entry is greater than the key value
|
||||
** then jump to P2. Otherwise fall through to the next instruction.
|
||||
*/
|
||||
/* Opcode: IdxLT P1 P2 P3 P4 P5
|
||||
/* Opcode: IdxLT P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
**
|
||||
** The P4 register values beginning with P3 form an unpacked index
|
||||
@@ -5849,7 +6024,7 @@ case OP_FinishSeek: {
|
||||
** If the P1 index entry is less than the key value then jump to P2.
|
||||
** Otherwise fall through to the next instruction.
|
||||
*/
|
||||
/* Opcode: IdxLE P1 P2 P3 P4 P5
|
||||
/* Opcode: IdxLE P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
**
|
||||
** The P4 register values beginning with P3 form an unpacked index
|
||||
@@ -5875,7 +6050,6 @@ case OP_IdxGE: { /* jump */
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
assert( pC->uc.pCursor!=0);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
assert( pOp->p5==0 || pOp->p5==1 );
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)pOp->p4.i;
|
||||
@@ -5896,8 +6070,31 @@ case OP_IdxGE: { /* jump */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
res = 0; /* Not needed. Only used to silence a warning. */
|
||||
rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
|
||||
|
||||
/* Inlined version of sqlite3VdbeIdxKeyCompare() */
|
||||
{
|
||||
i64 nCellKey = 0;
|
||||
BtCursor *pCur;
|
||||
Mem m;
|
||||
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
pCur = pC->uc.pCursor;
|
||||
assert( sqlite3BtreeCursorIsValid(pCur) );
|
||||
nCellKey = sqlite3BtreePayloadSize(pCur);
|
||||
/* nCellKey will always be between 0 and 0xffffffff because of the way
|
||||
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
|
||||
if( nCellKey<=0 || nCellKey>0x7fffffff ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
sqlite3VdbeMemInit(&m, db, 0);
|
||||
rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0);
|
||||
sqlite3VdbeMemRelease(&m);
|
||||
}
|
||||
/* End of inlined sqlite3VdbeIdxKeyCompare() */
|
||||
|
||||
assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) );
|
||||
if( (pOp->opcode&1)==(OP_IdxLT&1) ){
|
||||
assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT );
|
||||
@@ -5907,7 +6104,7 @@ case OP_IdxGE: { /* jump */
|
||||
res++;
|
||||
}
|
||||
VdbeBranchTaken(res>0,2);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
assert( rc==SQLITE_OK );
|
||||
if( res>0 ) goto jump_to_p2;
|
||||
break;
|
||||
}
|
||||
@@ -6785,6 +6982,7 @@ case OP_AggStep: {
|
||||
|
||||
pOp->opcode = OP_AggStep1;
|
||||
/* Fall through into OP_AggStep */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case OP_AggStep1: {
|
||||
int i;
|
||||
@@ -7032,7 +7230,7 @@ case OP_JournalMode: { /* out2 */
|
||||
/* Open a transaction on the database file. Regardless of the journal
|
||||
** mode, this transaction always uses a rollback journal.
|
||||
*/
|
||||
assert( sqlite3BtreeIsInTrans(pBt)==0 );
|
||||
assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
|
||||
}
|
||||
@@ -7774,18 +7972,17 @@ case OP_Init: { /* jump */
|
||||
){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
if( db->mTrace & SQLITE_TRACE_LEGACY ){
|
||||
void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace;
|
||||
char *z = sqlite3VdbeExpandSql(p, zTrace);
|
||||
x(db->pTraceArg, z);
|
||||
db->trace.xLegacy(db->pTraceArg, z);
|
||||
sqlite3_free(z);
|
||||
}else
|
||||
#endif
|
||||
if( db->nVdbeExec>1 ){
|
||||
char *z = sqlite3MPrintf(db, "-- %s", zTrace);
|
||||
(void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, z);
|
||||
(void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z);
|
||||
sqlite3DbFree(db, z);
|
||||
}else{
|
||||
(void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace);
|
||||
(void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace);
|
||||
}
|
||||
}
|
||||
#ifdef SQLITE_USE_FCNTL_TRACE
|
||||
@@ -7973,7 +8170,11 @@ default: { /* This is really OP_Noop, OP_Explain */
|
||||
** an error of some kind.
|
||||
*/
|
||||
abort_due_to_error:
|
||||
if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT;
|
||||
if( db->mallocFailed ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else if( rc==SQLITE_IOERR_CORRUPTFS ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
assert( rc );
|
||||
if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
|
||||
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ struct VdbeCursor {
|
||||
Bool isEphemeral:1; /* True for an ephemeral table */
|
||||
Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
|
||||
Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
|
||||
Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */
|
||||
u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */
|
||||
Btree *pBtx; /* Separate file holding temporary table */
|
||||
i64 seqCount; /* Sequence counter */
|
||||
u32 *aAltMap; /* Mapping from table to index column numbers */
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
|
||||
}
|
||||
#endif
|
||||
if( db->mTrace & SQLITE_TRACE_PROFILE ){
|
||||
db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse);
|
||||
db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse);
|
||||
}
|
||||
p->startTime = 0;
|
||||
}
|
||||
|
||||
+12
-7
@@ -475,6 +475,7 @@ void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
|
||||
int j;
|
||||
sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
|
||||
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
|
||||
sqlite3MayAbort(p->pParse);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -703,7 +704,7 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|
||||
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
|
||||
|| opcode==OP_VDestroy
|
||||
|| opcode==OP_VCreate
|
||||
|| (opcode==OP_ParseSchema && pOp->p4.z==0)
|
||||
|| opcode==OP_ParseSchema
|
||||
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
|
||||
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
|
||||
){
|
||||
@@ -808,7 +809,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
switch( pOp->opcode ){
|
||||
case OP_Transaction: {
|
||||
if( pOp->p2!=0 ) p->readOnly = 0;
|
||||
/* fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case OP_AutoCommit:
|
||||
case OP_Savepoint: {
|
||||
@@ -855,6 +856,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
n = pOp[-1].p1;
|
||||
if( n>nMaxArgs ) nMaxArgs = n;
|
||||
/* Fall through into the default case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
@@ -1520,7 +1522,7 @@ char *sqlite3VdbeDisplayComment(
|
||||
sqlite3_str_appendf(&x, "%d", v1);
|
||||
}else if( pCtx->argc>1 ){
|
||||
sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1);
|
||||
}else{
|
||||
}else if( x.accError==0 ){
|
||||
assert( x.nChar>2 );
|
||||
x.nChar -= 2;
|
||||
ii++;
|
||||
@@ -2662,7 +2664,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
||||
*/
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( sqlite3BtreeIsInTrans(pBt) ){
|
||||
if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
|
||||
/* Whether or not a database might need a super-journal depends upon
|
||||
** its journal mode (among other things). This matrix determines which
|
||||
** journal modes use a super-journal and which do not */
|
||||
@@ -2797,7 +2799,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( sqlite3BtreeIsInTrans(pBt) ){
|
||||
if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
|
||||
char const *zFile = sqlite3BtreeGetJournalname(pBt);
|
||||
if( zFile==0 ){
|
||||
continue; /* Ignore TEMP and :memory: databases */
|
||||
@@ -4293,9 +4295,12 @@ SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
|
||||
static int sqlite3IntFloatCompare(i64 i, double r){
|
||||
if( sizeof(LONGDOUBLE_TYPE)>8 ){
|
||||
LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
|
||||
testcase( x<r );
|
||||
testcase( x>r );
|
||||
testcase( x==r );
|
||||
if( x<r ) return -1;
|
||||
if( x>r ) return +1;
|
||||
return 0;
|
||||
if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */
|
||||
return 0; /*NO_TEST*/ /* work around bugs in gcov */
|
||||
}else{
|
||||
i64 y;
|
||||
double s;
|
||||
|
||||
+4
-1
@@ -970,13 +970,16 @@ int sqlite3VdbeSorterInit(
|
||||
if( pSorter==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
Btree *pBt = db->aDb[0].pBt;
|
||||
pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
|
||||
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
|
||||
pKeyInfo->db = 0;
|
||||
if( nField && nWorker==0 ){
|
||||
pKeyInfo->nKeyField = nField;
|
||||
}
|
||||
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
pSorter->nTask = nWorker + 1;
|
||||
pSorter->iPrev = (u8)(nWorker - 1);
|
||||
pSorter->bUseThreads = (pSorter->nTask>1);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user