mirror of
https://github.com/status-im/sqlcipher.git
synced 2025-02-24 09:48:10 +00:00
Merge branch 'zetetic-prerelease' into zetetic-master
This commit is contained in:
commit
7695ec9f32
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,6 +1,18 @@
|
||||
# SQLCipher Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [4.3.0] - (November 2019 - [4.3.0 changes])
|
||||
- Updates baseline to upstream SQLite 3.30.1
|
||||
- PRAGMA key now returns text result value "ok" after execution
|
||||
- Adjusts backup API so that encrypted to encrypted backups are permitted
|
||||
- Adds NSS crypto provider implementation
|
||||
- Fixes OpenSSL provider compatibility with BoringSSL
|
||||
- Separates memory related traces to reduce verbosity of logging
|
||||
- Fixes output of PRAGMA cipher_integrity_check on big endian platforms
|
||||
- Cryptograpic provider interface cleanup
|
||||
- Rework of mutex allocation and management
|
||||
- Resolves miscellaneous build warnings
|
||||
|
||||
## [4.2.0] - (May 2019 - [4.2.0 changes])
|
||||
- Adds PRAGMA cipher_integrity_check to perform independent verification of page HMACs
|
||||
- Updates baseline to upstream SQLite 3.28.0
|
||||
@ -145,7 +157,9 @@ All notable changes to this project will be documented in this file.
|
||||
### Security
|
||||
- Change KDF iteration length from 4,000 to 64,000
|
||||
|
||||
[unreleased]: https://github.com/sqlcipher/sqlcipher/compare/v4.2.0...prerelease
|
||||
[unreleased]: https://github.com/sqlcipher/sqlcipher/compare/v4.3.0...prerelease
|
||||
[4.3.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.3.0
|
||||
[4.3.0 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.2.0...v4.3.0
|
||||
[4.2.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.2.0
|
||||
[4.2.0 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.1.0...v4.2.0
|
||||
[4.1.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.1.0
|
||||
|
@ -148,14 +148,16 @@ CRYPTOLIBOBJ = \
|
||||
crypto_impl.lo \
|
||||
crypto_openssl.lo \
|
||||
crypto_libtomcrypt.lo \
|
||||
crypto_nss.lo \
|
||||
crypto_cc.lo
|
||||
|
||||
|
||||
CRYPTOSRC = \
|
||||
$(TOP)/src/crypto.h \
|
||||
$(TOP)/src/sqlcipher.h \
|
||||
$(TOP)/src/crypto.c \
|
||||
$(TOP)/src/crypto_impl.c \
|
||||
$(TOP)/src/crypto_libtomcrypt.c \
|
||||
$(TOP)/src/crypto_nss.c \
|
||||
$(TOP)/src/crypto_openssl.c \
|
||||
$(TOP)/src/crypto_cc.c
|
||||
|
||||
@ -629,7 +631,6 @@ SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DESERIALIZE
|
||||
SHELL_OPT += -DSQLITE_INTROSPECTION_PRAGMAS
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
|
||||
FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
|
||||
@ -820,6 +821,8 @@ crypto_impl.lo: $(TOP)/src/crypto_impl.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto_impl.c
|
||||
crypto_openssl.lo: $(TOP)/src/crypto_openssl.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto_openssl.c
|
||||
crypto_nss.lo: $(TOP)/src/crypto_nss.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto_nss.c
|
||||
crypto_libtomcrypt.lo: $(TOP)/src/crypto_libtomcrypt.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto_libtomcrypt.c
|
||||
crypto_cc.lo: $(TOP)/src/crypto_cc.c $(HDR)
|
||||
|
@ -19,7 +19,7 @@ TOP = ../sqlite
|
||||
#### C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
#
|
||||
BCC = gcc -g -O2
|
||||
BCC = gcc -g -O0
|
||||
#BCC = /opt/ancic/bin/c89 -0
|
||||
|
||||
#### If the target operating system supports the "usleep()" system
|
||||
@ -38,8 +38,8 @@ THREADSAFE = -DTHREADSAFE=0
|
||||
#### Specify any extra linker options needed to make the library
|
||||
# thread safe
|
||||
#
|
||||
#THREADLIB = -lpthread
|
||||
THREADLIB =
|
||||
THREADLIB = -lpthread -lm -ldl
|
||||
#THREADLIB =
|
||||
|
||||
#### Specify any extra libraries needed to access required functions.
|
||||
#
|
||||
@ -54,11 +54,9 @@ TLIBS =
|
||||
# You can make the library go almost twice as fast if you compile
|
||||
# with -DNDEBUG=1
|
||||
#
|
||||
#OPTS = -DSQLITE_DEBUG=2
|
||||
#OPTS = -DSQLITE_DEBUG=1
|
||||
#OPTS =
|
||||
OPTS = -DNDEBUG=1
|
||||
OPTS += -DHAVE_FDATASYNC=1
|
||||
OPTS += -DSQLITE_DEBUG=1
|
||||
OPTS += -DSQLITE_ENABLE_WHERETRACE
|
||||
OPTS += -DSQLITE_ENABLE_SELECTTRACE
|
||||
|
||||
#### The suffix to add to executable files. ".exe" for windows.
|
||||
# Nothing for unix.
|
||||
@ -70,7 +68,7 @@ EXE =
|
||||
# will run on the target platform. This is usually the same
|
||||
# as BCC, unless you are cross-compiling.
|
||||
#
|
||||
TCC = gcc -O6
|
||||
TCC = gcc -O0
|
||||
#TCC = gcc -g -O0 -Wall
|
||||
#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
|
||||
#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6
|
||||
@ -91,18 +89,12 @@ SHPREFIX = lib
|
||||
|
||||
#### Extra compiler options needed for programs that use the TCL library.
|
||||
#
|
||||
#TCL_FLAGS =
|
||||
#TCL_FLAGS = -DSTATIC_BUILD=1
|
||||
TCL_FLAGS = -I/home/drh/tcltk/8.5linux
|
||||
#TCL_FLAGS = -I/home/drh/tcltk/8.5win -DSTATIC_BUILD=1
|
||||
#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux
|
||||
TCL_FLAGS = -I/home/drh/tcl/include/tcl8.6
|
||||
|
||||
#### Linker options needed to link against the TCL library.
|
||||
#
|
||||
#LIBTCL = -ltcl -lm -ldl
|
||||
LIBTCL = /home/drh/tcltk/8.5linux/libtcl8.5g.a -lm -ldl
|
||||
#LIBTCL = /home/drh/tcltk/8.5win/libtcl85s.a -lmsvcrt
|
||||
#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc
|
||||
LIBTCL = /home/drh/tcl/lib/libtcl8.6.a -lm -lpthread -ldl -lz
|
||||
|
||||
#### Additional objects for SQLite library when TCL support is enabled.
|
||||
#TCLOBJ =
|
||||
|
@ -73,7 +73,7 @@ API_ARMOR = 0
|
||||
!IFNDEF NO_WARN
|
||||
!IF $(USE_FULLWARN)!=0
|
||||
NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4130 -wd4152 -wd4189 -wd4206
|
||||
NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4305 -wd4306 -wd4702 -wd4706
|
||||
NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
@ -351,7 +351,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_INTROSPECTION_PRAGMAS=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
|
||||
!ENDIF
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
@ -1270,6 +1269,7 @@ SRC00 = \
|
||||
$(TOP)\src\crypto_cc.c \
|
||||
$(TOP)\src\crypto_impl.c \
|
||||
$(TOP)\src\crypto_libtomcrypt.c \
|
||||
$(TOP)\src\crypto_nss.c \
|
||||
$(TOP)\src\crypto_openssl.c \
|
||||
$(TOP)\src\crypto.h \
|
||||
$(TOP)\src\sqlcipher.h \
|
||||
|
27
README.md
27
README.md
@ -437,30 +437,13 @@ describes its purpose and role within the larger system.
|
||||
<a name="vauth"></a>
|
||||
## Verifying Code Authenticity
|
||||
|
||||
If you obtained an SQLite source tree from a secondary source, such as a
|
||||
GitHub mirror, and you want to verify that it has not been altered, there
|
||||
are a couple of ways to do that.
|
||||
|
||||
If you have a release version of SQLite, and you are using the
|
||||
`sqlite3.c` amalgamation, then SHA3-256 hashes for the amalgamation are
|
||||
available in the [change log](https://www.sqlite.org/changes.html) on
|
||||
the official website. After building the `sqlite3.c` file, you can check
|
||||
that it is authentic by comparing the hash. This does not ensure that the
|
||||
test scripts are unaltered, but it does validate the deliverable part of
|
||||
the code and the verification process only involves computing and
|
||||
comparing a single hash.
|
||||
|
||||
For versions other than an official release, or if you are building the
|
||||
`sqlite3.c` amalgamation using non-standard build options, the verification
|
||||
process is a little more involved. The `manifest` file at the root directory
|
||||
of the source tree
|
||||
The `manifest` file at the root directory of the source tree
|
||||
contains either a SHA3-256 hash (for newer files) or a SHA1 hash (for
|
||||
older files) for every source file in the repository. You can write a script
|
||||
to extracts hashes from `manifest` and verifies the hashes against the
|
||||
corresponding files in the source tree. The SHA3-256 hash of the `manifest`
|
||||
older files) for every source file in the repository.
|
||||
The SHA3-256 hash of the `manifest`
|
||||
file itself is the official name of the version of the source tree that you
|
||||
have. The `manifest.uuid` file should contain the SHA3-256 hash of the
|
||||
`manifest` file. If all of the above hash comparisons are correct, then
|
||||
have. The `manifest.uuid` file should contain the SHA3-256 hash of the
|
||||
`manifest` file. If all of the above hash comparisons are correct, then
|
||||
you can be confident that your source tree is authentic and unadulterated.
|
||||
|
||||
The format of the `manifest` file should be mostly self-explanatory, but
|
||||
|
@ -15,10 +15,10 @@
|
||||
"requires_arc": false,
|
||||
"source": {
|
||||
"git": "https://github.com/sqlcipher/sqlcipher.git",
|
||||
"tag": "v4.2.0"
|
||||
"tag": "v4.3.0"
|
||||
},
|
||||
"summary": "Full Database Encryption for SQLite.",
|
||||
"version": "4.2.0",
|
||||
"version": "4.3.0",
|
||||
"subspecs": [
|
||||
{
|
||||
"compiler_flags": [
|
||||
|
@ -73,7 +73,7 @@ API_ARMOR = 0
|
||||
!IFNDEF NO_WARN
|
||||
!IF $(USE_FULLWARN)!=0
|
||||
NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4130 -wd4152 -wd4189 -wd4206
|
||||
NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4305 -wd4306 -wd4702 -wd4706
|
||||
NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
|
1340
config.guess
vendored
1340
config.guess
vendored
File diff suppressed because it is too large
Load Diff
@ -33,6 +33,9 @@
|
||||
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||
#undef HAVE_LIBCRYPTO
|
||||
|
||||
/* Define to 1 if you have the `nss3' library (-lnss3). */
|
||||
#undef HAVE_LIBNSS3
|
||||
|
||||
/* Define to 1 if you have the `tomcrypt' library (-ltomcrypt). */
|
||||
#undef HAVE_LIBTOMCRYPT
|
||||
|
||||
|
2680
config.sub
vendored
2680
config.sub
vendored
File diff suppressed because it is too large
Load Diff
72
configure
vendored
72
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.28.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.30.1.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@ -587,8 +587,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlcipher'
|
||||
PACKAGE_TARNAME='sqlcipher'
|
||||
PACKAGE_VERSION='3.28.0'
|
||||
PACKAGE_STRING='sqlcipher 3.28.0'
|
||||
PACKAGE_VERSION='3.30.1'
|
||||
PACKAGE_STRING='sqlcipher 3.30.1'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -1337,7 +1337,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlcipher 3.28.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlcipher 3.30.1 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1402,7 +1402,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.28.0:";;
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.30.1:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1538,7 +1538,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlcipher configure 3.28.0
|
||||
sqlcipher configure 3.30.1
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@ -1957,7 +1957,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlcipher $as_me 3.28.0, which was
|
||||
It was created by sqlcipher $as_me 3.30.1, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -12029,6 +12029,59 @@ else
|
||||
fi
|
||||
|
||||
else
|
||||
if test "$crypto_lib" = "nss"; then
|
||||
CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS"
|
||||
BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: nss3" >&5
|
||||
$as_echo "nss3" >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PK11_Decrypt in -lnss3" >&5
|
||||
$as_echo_n "checking for PK11_Decrypt in -lnss3... " >&6; }
|
||||
if ${ac_cv_lib_nss3_PK11_Decrypt+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lnss3 $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char PK11_Decrypt ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return PK11_Decrypt ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_nss3_PK11_Decrypt=yes
|
||||
else
|
||||
ac_cv_lib_nss3_PK11_Decrypt=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nss3_PK11_Decrypt" >&5
|
||||
$as_echo "$ac_cv_lib_nss3_PK11_Decrypt" >&6; }
|
||||
if test "x$ac_cv_lib_nss3_PK11_Decrypt" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_LIBNSS3 1
|
||||
_ACEOF
|
||||
|
||||
LIBS="-lnss3 $LIBS"
|
||||
|
||||
else
|
||||
as_fn_error $? "Library crypto not found. Install nss!\"" "$LINENO" 5
|
||||
fi
|
||||
|
||||
else
|
||||
CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL"
|
||||
BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: openssl" >&5
|
||||
@ -12080,6 +12133,7 @@ else
|
||||
as_fn_error $? "Library crypto not found. Install openssl!\"" "$LINENO" 5
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@ -13754,7 +13808,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlcipher $as_me 3.28.0, which was
|
||||
This file was extended by sqlcipher $as_me 3.30.1, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -13820,7 +13874,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlcipher config.status 3.28.0
|
||||
sqlcipher config.status 3.30.1
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@ -219,11 +219,19 @@ else
|
||||
AC_CHECK_LIB([tomcrypt], [register_cipher], ,
|
||||
AC_MSG_ERROR([Library crypto not found. Install libtomcrypt!"]))
|
||||
else
|
||||
if test "$crypto_lib" = "nss"; then
|
||||
CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS"
|
||||
BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS"
|
||||
AC_MSG_RESULT([nss3])
|
||||
AC_CHECK_LIB([nss3], [PK11_Decrypt], ,
|
||||
AC_MSG_ERROR([Library crypto not found. Install nss!"]))
|
||||
else
|
||||
CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL"
|
||||
BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL"
|
||||
AC_MSG_RESULT([openssl])
|
||||
AC_CHECK_LIB([crypto], [HMAC_Init_ex], ,
|
||||
AC_MSG_ERROR([Library crypto not found. Install openssl!"]))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
@ -134,6 +134,7 @@ do_setup_rec_test $tn.5 {
|
||||
SEARCH TABLE t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
|
||||
}
|
||||
|
||||
if 0 {
|
||||
do_setup_rec_test $tn.6 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
} {
|
||||
@ -142,6 +143,7 @@ do_setup_rec_test $tn.6 {
|
||||
CREATE INDEX t1_idx_00000061 ON t1(a);
|
||||
SEARCH TABLE t1 USING COVERING INDEX t1_idx_00000061
|
||||
}
|
||||
}
|
||||
|
||||
do_setup_rec_test $tn.7 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
|
@ -308,6 +308,18 @@
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following are copied from sqliteInt.h.
|
||||
**
|
||||
** Constants for the largest and smallest possible 64-bit signed integers.
|
||||
** These macros are designed to work correctly on both 32-bit and 64-bit
|
||||
** compilers.
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
static int fts3EvalNext(Fts3Cursor *pCsr);
|
||||
static int fts3EvalStart(Fts3Cursor *pCsr);
|
||||
static int fts3TermSegReaderCursor(
|
||||
@ -2086,10 +2098,11 @@ static void fts3ColumnlistCopy(char **pp, char **ppPoslist){
|
||||
}
|
||||
|
||||
/*
|
||||
** Value used to signify the end of an position-list. This is safe because
|
||||
** it is not possible to have a document with 2^31 terms.
|
||||
** Value used to signify the end of an position-list. This must be
|
||||
** as large or larger than any value that might appear on the
|
||||
** position-list, even a position list that has been corrupted.
|
||||
*/
|
||||
#define POSITION_LIST_END 0x7fffffff
|
||||
#define POSITION_LIST_END LARGEST_INT64
|
||||
|
||||
/*
|
||||
** This function is used to help parse position-lists. When this function is
|
||||
@ -2165,14 +2178,14 @@ static int fts3PoslistMerge(
|
||||
fts3GetVarint32(&p1[1], &iCol1);
|
||||
if( iCol1==0 ) return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
|
||||
else if( *p1==POS_END ) iCol1 = 0x7fffffff;
|
||||
else iCol1 = 0;
|
||||
|
||||
if( *p2==POS_COLUMN ){
|
||||
fts3GetVarint32(&p2[1], &iCol2);
|
||||
if( iCol2==0 ) return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
|
||||
else if( *p2==POS_END ) iCol2 = 0x7fffffff;
|
||||
else iCol2 = 0;
|
||||
|
||||
if( iCol1==iCol2 ){
|
||||
@ -2474,7 +2487,8 @@ static void fts3PutDeltaVarint3(
|
||||
iWrite = *piPrev - iVal;
|
||||
}
|
||||
assert( *pbFirst || *piPrev==0 );
|
||||
assert( *pbFirst==0 || iWrite>0 );
|
||||
assert_fts3_nc( *pbFirst==0 || iWrite>0 );
|
||||
assert( *pbFirst==0 || iWrite>=0 );
|
||||
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
|
||||
*piPrev = iVal;
|
||||
*pbFirst = 1;
|
||||
@ -2580,6 +2594,8 @@ static int fts3DoclistOrMerge(
|
||||
fts3PoslistCopy(&p, &p2);
|
||||
fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
|
||||
}
|
||||
|
||||
assert( (p-aOut)<=((p1?(p1-a1):n1)+(p2?(p2-a2):n2)+FTS3_VARINT_MAX-1) );
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -3179,18 +3195,6 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following are copied from sqliteInt.h.
|
||||
**
|
||||
** Constants for the largest and smallest possible 64-bit signed integers.
|
||||
** These macros are designed to work correctly on both 32-bit and 64-bit
|
||||
** compilers.
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If the numeric type of argument pVal is "integer", then return it
|
||||
** converted to a 64-bit signed integer. Otherwise, return a copy of
|
||||
|
@ -433,10 +433,10 @@ static void fts3SnippetDetails(
|
||||
|
||||
while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){
|
||||
int j;
|
||||
u64 mPhrase = (u64)1 << i;
|
||||
u64 mPhrase = (u64)1 << (i%64);
|
||||
u64 mPos = (u64)1 << (iCsr - iStart);
|
||||
assert( iCsr>=iStart && (iCsr - iStart)<=64 );
|
||||
assert( i>=0 && i<=64 );
|
||||
assert( i>=0 );
|
||||
if( (mCover|mCovered)&mPhrase ){
|
||||
iScore++;
|
||||
}else{
|
||||
|
@ -2619,14 +2619,14 @@ static void fts3ColumnFilter(
|
||||
|
||||
nList -= (int)(p - pList);
|
||||
pList = p;
|
||||
if( nList==0 ){
|
||||
if( nList<=0 ){
|
||||
break;
|
||||
}
|
||||
p = &pList[1];
|
||||
p += fts3GetVarint32(p, &iCurrent);
|
||||
}
|
||||
|
||||
if( bZero && &pList[nList]!=pEnd ){
|
||||
if( bZero && (pEnd - &pList[nList])>0){
|
||||
memset(&pList[nList], 0, pEnd - &pList[nList]);
|
||||
}
|
||||
*ppList = pList;
|
||||
@ -3754,7 +3754,7 @@ static int nodeReaderNext(NodeReader *p){
|
||||
}
|
||||
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
|
||||
|
||||
if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){
|
||||
if( nPrefix>p->term.n || nSuffix>p->nNode-p->iOff || nSuffix==0 ){
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
|
||||
@ -3773,7 +3773,7 @@ static int nodeReaderNext(NodeReader *p){
|
||||
}
|
||||
}
|
||||
|
||||
assert( p->iOff<=p->nNode );
|
||||
assert_fts3_nc( p->iOff<=p->nNode );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -3797,14 +3797,14 @@ static int nodeReaderInit(NodeReader *p, const char *aNode, int nNode){
|
||||
p->nNode = nNode;
|
||||
|
||||
/* Figure out if this is a leaf or an internal node. */
|
||||
if( p->aNode[0] ){
|
||||
if( aNode && aNode[0] ){
|
||||
/* An internal node. */
|
||||
p->iOff = 1 + sqlite3Fts3GetVarint(&p->aNode[1], &p->iChild);
|
||||
}else{
|
||||
p->iOff = 1;
|
||||
}
|
||||
|
||||
return nodeReaderNext(p);
|
||||
return aNode ? nodeReaderNext(p) : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3934,13 +3934,14 @@ static int fts3AppendToNode(
|
||||
/* Node must have already been started. There must be a doclist for a
|
||||
** leaf node, and there must not be a doclist for an internal node. */
|
||||
assert( pNode->n>0 );
|
||||
assert( (pNode->a[0]=='\0')==(aDoclist!=0) );
|
||||
assert_fts3_nc( (pNode->a[0]=='\0')==(aDoclist!=0) );
|
||||
|
||||
blobGrowBuffer(pPrev, nTerm, &rc);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
|
||||
nSuffix = nTerm - nPrefix;
|
||||
if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
|
||||
memcpy(pPrev->a, zTerm, nTerm);
|
||||
pPrev->n = nTerm;
|
||||
|
||||
@ -4150,7 +4151,7 @@ static int fts3TermCmp(
|
||||
int nCmp = MIN(nLhs, nRhs);
|
||||
int res;
|
||||
|
||||
res = memcmp(zLhs, zRhs, nCmp);
|
||||
res = (nCmp ? memcmp(zLhs, zRhs, nCmp) : 0);
|
||||
if( res==0 ) res = nLhs - nRhs;
|
||||
|
||||
return res;
|
||||
@ -4282,34 +4283,42 @@ static int fts3IncrmergeLoad(
|
||||
|
||||
pNode = &pWriter->aNodeWriter[nHeight];
|
||||
pNode->iBlock = pWriter->iStart + pWriter->nLeafEst*nHeight;
|
||||
blobGrowBuffer(&pNode->block, MAX(nRoot, p->nNodeSize), &rc);
|
||||
blobGrowBuffer(&pNode->block,
|
||||
MAX(nRoot, p->nNodeSize)+FTS3_NODE_PADDING, &rc
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNode->block.a, aRoot, nRoot);
|
||||
pNode->block.n = nRoot;
|
||||
memset(&pNode->block.a[nRoot], 0, FTS3_NODE_PADDING);
|
||||
}
|
||||
|
||||
for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){
|
||||
NodeReader reader;
|
||||
pNode = &pWriter->aNodeWriter[i];
|
||||
|
||||
rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
|
||||
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
|
||||
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNode->key.a, reader.term.a, reader.term.n);
|
||||
pNode->key.n = reader.term.n;
|
||||
if( i>0 ){
|
||||
char *aBlock = 0;
|
||||
int nBlock = 0;
|
||||
pNode = &pWriter->aNodeWriter[i-1];
|
||||
pNode->iBlock = reader.iChild;
|
||||
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
|
||||
blobGrowBuffer(&pNode->block, MAX(nBlock, p->nNodeSize), &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNode->block.a, aBlock, nBlock);
|
||||
pNode->block.n = nBlock;
|
||||
if( pNode->block.a){
|
||||
rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
|
||||
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
|
||||
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNode->key.a, reader.term.a, reader.term.n);
|
||||
pNode->key.n = reader.term.n;
|
||||
if( i>0 ){
|
||||
char *aBlock = 0;
|
||||
int nBlock = 0;
|
||||
pNode = &pWriter->aNodeWriter[i-1];
|
||||
pNode->iBlock = reader.iChild;
|
||||
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
|
||||
blobGrowBuffer(&pNode->block,
|
||||
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNode->block.a, aBlock, nBlock);
|
||||
pNode->block.n = nBlock;
|
||||
memset(&pNode->block.a[nBlock], 0, FTS3_NODE_PADDING);
|
||||
}
|
||||
sqlite3_free(aBlock);
|
||||
}
|
||||
sqlite3_free(aBlock);
|
||||
}
|
||||
}
|
||||
nodeReaderRelease(&reader);
|
||||
@ -4552,7 +4561,10 @@ static int fts3TruncateNode(
|
||||
NodeReader reader; /* Reader object */
|
||||
Blob prev = {0, 0, 0}; /* Previous term written to new node */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int bLeaf = aNode[0]=='\0'; /* True for a leaf node */
|
||||
int bLeaf; /* True for a leaf node */
|
||||
|
||||
if( nNode<1 ) return FTS_CORRUPT_VTAB;
|
||||
bLeaf = aNode[0]=='\0';
|
||||
|
||||
/* Allocate required output space */
|
||||
blobGrowBuffer(pNew, nNode, &rc);
|
||||
|
@ -178,6 +178,7 @@ struct Fts5Config {
|
||||
char *zContentExprlist;
|
||||
Fts5Tokenizer *pTok;
|
||||
fts5_tokenizer *pTokApi;
|
||||
int bLock; /* True when table is preparing statement */
|
||||
|
||||
/* Values loaded from the %_config table */
|
||||
int iCookie; /* Incremented when %_config is modified */
|
||||
@ -694,6 +695,7 @@ int sqlite3Fts5ExprEof(Fts5Expr*);
|
||||
i64 sqlite3Fts5ExprRowid(Fts5Expr*);
|
||||
|
||||
void sqlite3Fts5ExprFree(Fts5Expr*);
|
||||
int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2);
|
||||
|
||||
/* Called during startup to register a UDF with SQLite */
|
||||
int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
|
||||
|
@ -178,10 +178,19 @@ int sqlite3Fts5PoslistNext64(
|
||||
i64 iOff = *piOff;
|
||||
int iVal;
|
||||
fts5FastGetVarint32(a, i, iVal);
|
||||
if( iVal==1 ){
|
||||
if( iVal<=1 ){
|
||||
if( iVal==0 ){
|
||||
*pi = i;
|
||||
return 0;
|
||||
}
|
||||
fts5FastGetVarint32(a, i, iVal);
|
||||
iOff = ((i64)iVal) << 32;
|
||||
fts5FastGetVarint32(a, i, iVal);
|
||||
if( iVal<2 ){
|
||||
/* This is a corrupt record. So stop parsing it here. */
|
||||
*piOff = -1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
*piOff = iOff + ((iVal-2) & 0x7FFFFFFF);
|
||||
*pi = i;
|
||||
|
@ -683,7 +683,7 @@ int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
|
||||
rc = sqlite3_declare_vtab(pConfig->db, zSql);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -309,6 +309,42 @@ void sqlite3Fts5ExprFree(Fts5Expr *p){
|
||||
}
|
||||
}
|
||||
|
||||
int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
|
||||
Fts5Parse sParse;
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
|
||||
if( *pp1 ){
|
||||
Fts5Expr *p1 = *pp1;
|
||||
int nPhrase = p1->nPhrase + p2->nPhrase;
|
||||
|
||||
p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
|
||||
p2->pRoot = 0;
|
||||
|
||||
if( sParse.rc==SQLITE_OK ){
|
||||
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
|
||||
p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
|
||||
);
|
||||
if( ap==0 ){
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int i;
|
||||
memmove(&ap[p2->nPhrase], ap, p1->nPhrase*sizeof(Fts5ExprPhrase*));
|
||||
for(i=0; i<p2->nPhrase; i++){
|
||||
ap[i] = p2->apExprPhrase[i];
|
||||
}
|
||||
p1->nPhrase = nPhrase;
|
||||
p1->apExprPhrase = ap;
|
||||
}
|
||||
}
|
||||
sqlite3_free(p2->apExprPhrase);
|
||||
sqlite3_free(p2);
|
||||
}else{
|
||||
*pp1 = p2;
|
||||
}
|
||||
|
||||
return sParse.rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument pTerm must be a synonym iterator. Return the current rowid
|
||||
** that it points to.
|
||||
|
@ -690,6 +690,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
}else{
|
||||
/* TODO1: Fix this */
|
||||
pRet->p[nByte] = 0x00;
|
||||
pRet->p[nByte+1] = 0x00;
|
||||
pRet->szLeaf = fts5GetU16(&pRet->p[2]);
|
||||
}
|
||||
}
|
||||
@ -712,7 +713,7 @@ static void fts5DataRelease(Fts5Data *pData){
|
||||
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
|
||||
Fts5Data *pRet = fts5DataRead(p, iRowid);
|
||||
if( pRet ){
|
||||
if( pRet->szLeaf>pRet->nn ){
|
||||
if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
fts5DataRelease(pRet);
|
||||
pRet = 0;
|
||||
@ -992,7 +993,7 @@ static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
|
||||
/* TODO: Do we need this if the leaf-index is appended? Probably... */
|
||||
memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
|
||||
p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
|
||||
if( p->rc==SQLITE_OK && pConfig->iCookie!=iCookie ){
|
||||
if( p->rc==SQLITE_OK && (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
|
||||
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
|
||||
}
|
||||
fts5DataRelease(pData);
|
||||
@ -4953,8 +4954,14 @@ static void fts5MergePrefixLists(
|
||||
** first rowid in one input is a large negative number, and the first in
|
||||
** the other a non-negative number, the delta for the non-negative
|
||||
** number will be larger on disk than the literal integer value
|
||||
** was. */
|
||||
if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9) ) return;
|
||||
** was.
|
||||
**
|
||||
** Or, if the input position-lists are corrupt, then the output might
|
||||
** include up to 2 extra 10-byte positions created by interpreting -1
|
||||
** (the value PoslistNext64() uses for EOF) as a position and appending
|
||||
** it to the output. This can happen at most once for each input
|
||||
** position-list, hence two 10 byte paddings. */
|
||||
if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return;
|
||||
fts5DoclistIterInit(p1, &i1);
|
||||
fts5DoclistIterInit(p2, &i2);
|
||||
|
||||
@ -4965,6 +4972,7 @@ static void fts5MergePrefixLists(
|
||||
fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
|
||||
fts5DoclistIterNext(&i1);
|
||||
if( i1.aPoslist==0 ) break;
|
||||
assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
|
||||
}
|
||||
else if( i2.iRowid!=i1.iRowid ){
|
||||
/* Copy entry from i2 */
|
||||
@ -4972,6 +4980,7 @@ static void fts5MergePrefixLists(
|
||||
fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
|
||||
fts5DoclistIterNext(&i2);
|
||||
if( i2.aPoslist==0 ) break;
|
||||
assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
|
||||
}
|
||||
else{
|
||||
/* Merge the two position lists. */
|
||||
@ -4988,14 +4997,17 @@ static void fts5MergePrefixLists(
|
||||
Fts5PoslistWriter writer;
|
||||
memset(&writer, 0, sizeof(writer));
|
||||
|
||||
/* See the earlier comment in this function for an explanation of why
|
||||
** corrupt input position lists might cause the output to consume
|
||||
** at most 20 bytes of unexpected space. */
|
||||
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
|
||||
fts5BufferZero(&tmp);
|
||||
sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist);
|
||||
sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10);
|
||||
if( p->rc ) break;
|
||||
|
||||
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
|
||||
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
|
||||
assert( iPos1>=0 && iPos2>=0 );
|
||||
assert_nc( iPos1>=0 && iPos2>=0 );
|
||||
|
||||
if( iPos1<iPos2 ){
|
||||
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
|
||||
@ -5004,7 +5016,6 @@ static void fts5MergePrefixLists(
|
||||
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
|
||||
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
|
||||
}
|
||||
|
||||
if( iPos1>=0 && iPos2>=0 ){
|
||||
while( 1 ){
|
||||
if( iPos1<iPos2 ){
|
||||
@ -5029,7 +5040,7 @@ static void fts5MergePrefixLists(
|
||||
aCopy = &a1[iOff1];
|
||||
nCopy = i1.nPoslist - iOff1;
|
||||
}else{
|
||||
assert( iPos2>=0 && iPos2!=iPrev );
|
||||
assert_nc( iPos2>=0 && iPos2!=iPrev );
|
||||
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
|
||||
aCopy = &a2[iOff2];
|
||||
nCopy = i2.nPoslist - iOff2;
|
||||
@ -5039,12 +5050,19 @@ static void fts5MergePrefixLists(
|
||||
}
|
||||
|
||||
/* WRITEPOSLISTSIZE */
|
||||
assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist );
|
||||
assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 );
|
||||
if( tmp.n>i1.nPoslist+i2.nPoslist ){
|
||||
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
|
||||
break;
|
||||
}
|
||||
fts5BufferSafeAppendVarint(&out, tmp.n * 2);
|
||||
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
|
||||
fts5DoclistIterNext(&i1);
|
||||
fts5DoclistIterNext(&i2);
|
||||
assert( out.n<=(p1->n+p2->n+9) );
|
||||
assert_nc( out.n<=(p1->n+p2->n+9) );
|
||||
if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
|
||||
assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -5056,7 +5074,7 @@ static void fts5MergePrefixLists(
|
||||
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
|
||||
fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
|
||||
}
|
||||
assert( out.n<=(p1->n+p2->n+9) );
|
||||
assert_nc( out.n<=(p1->n+p2->n+9) );
|
||||
|
||||
fts5BufferSet(&p->rc, p1, out.n, out.p);
|
||||
fts5BufferFree(&tmp);
|
||||
|
@ -465,17 +465,39 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
|
||||
** Implementation of the xBestIndex method for FTS5 tables. Within the
|
||||
** WHERE constraint, it searches for the following:
|
||||
**
|
||||
** 1. A MATCH constraint against the special column.
|
||||
** 1. A MATCH constraint against the table column.
|
||||
** 2. A MATCH constraint against the "rank" column.
|
||||
** 3. An == constraint against the rowid column.
|
||||
** 4. A < or <= constraint against the rowid column.
|
||||
** 5. A > or >= constraint against the rowid column.
|
||||
** 3. A MATCH constraint against some other column.
|
||||
** 4. An == constraint against the rowid column.
|
||||
** 5. A < or <= constraint against the rowid column.
|
||||
** 6. A > or >= constraint against the rowid column.
|
||||
**
|
||||
** Within the ORDER BY, either:
|
||||
** Within the ORDER BY, the following are supported:
|
||||
**
|
||||
** 5. ORDER BY rank [ASC|DESC]
|
||||
** 6. ORDER BY rowid [ASC|DESC]
|
||||
**
|
||||
** Information for the xFilter call is passed via both the idxNum and
|
||||
** idxStr variables. Specifically, idxNum is a bitmask of the following
|
||||
** flags used to encode the ORDER BY clause:
|
||||
**
|
||||
** FTS5_BI_ORDER_RANK
|
||||
** FTS5_BI_ORDER_ROWID
|
||||
** FTS5_BI_ORDER_DESC
|
||||
**
|
||||
** idxStr is used to encode data from the WHERE clause. For each argument
|
||||
** passed to the xFilter method, the following is appended to idxStr:
|
||||
**
|
||||
** Match against table column: "m"
|
||||
** Match against rank column: "r"
|
||||
** Match against other column: "<column-number>"
|
||||
** Equality constraint against the rowid: "="
|
||||
** A < or <= against the rowid: "<"
|
||||
** A > or >= against the rowid: ">"
|
||||
**
|
||||
** This function ensures that there is at most one "r" or "=". And that if
|
||||
** there exists an "=" then there is no "<" or ">".
|
||||
**
|
||||
** Costs are assigned as follows:
|
||||
**
|
||||
** a) If an unusable MATCH operator is present in the WHERE clause, the
|
||||
@ -503,32 +525,18 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
Fts5Config *pConfig = pTab->pConfig;
|
||||
const int nCol = pConfig->nCol;
|
||||
int idxFlags = 0; /* Parameter passed through to xFilter() */
|
||||
int bHasMatch;
|
||||
int iNext;
|
||||
int i;
|
||||
|
||||
struct Constraint {
|
||||
int op; /* Mask against sqlite3_index_constraint.op */
|
||||
int fts5op; /* FTS5 mask for idxFlags */
|
||||
int iCol; /* 0==rowid, 1==tbl, 2==rank */
|
||||
int omit; /* True to omit this if found */
|
||||
int iConsIndex; /* Index in pInfo->aConstraint[] */
|
||||
} aConstraint[] = {
|
||||
{SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
|
||||
FTS5_BI_MATCH, 1, 1, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
|
||||
FTS5_BI_RANK, 2, 1, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
|
||||
FTS5_BI_ROWID_LE, 0, 0, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
|
||||
FTS5_BI_ROWID_GE, 0, 0, -1},
|
||||
};
|
||||
char *idxStr;
|
||||
int iIdxStr = 0;
|
||||
int iCons = 0;
|
||||
|
||||
int bSeenEq = 0;
|
||||
int bSeenGt = 0;
|
||||
int bSeenLt = 0;
|
||||
int bSeenMatch = 0;
|
||||
int bSeenRank = 0;
|
||||
|
||||
int aColMap[3];
|
||||
aColMap[0] = -1;
|
||||
aColMap[1] = nCol;
|
||||
aColMap[2] = nCol+1;
|
||||
|
||||
assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
|
||||
assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
|
||||
@ -536,40 +544,85 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
|
||||
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
|
||||
|
||||
/* Set idxFlags flags for all WHERE clause terms that will be used. */
|
||||
if( pConfig->bLock ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf(
|
||||
"recursively defined fts5 content table"
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1);
|
||||
if( idxStr==0 ) return SQLITE_NOMEM;
|
||||
pInfo->idxStr = idxStr;
|
||||
pInfo->needToFreeIdxStr = 1;
|
||||
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
|
||||
int iCol = p->iColumn;
|
||||
|
||||
if( (p->op==SQLITE_INDEX_CONSTRAINT_MATCH && iCol>=0 && iCol<=nCol)
|
||||
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol==nCol)
|
||||
if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
||||
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
|
||||
){
|
||||
/* A MATCH operator or equivalent */
|
||||
if( p->usable ){
|
||||
idxFlags = (idxFlags & 0xFFFF) | FTS5_BI_MATCH | (iCol << 16);
|
||||
aConstraint[0].iConsIndex = i;
|
||||
}else{
|
||||
if( p->usable==0 || iCol<0 ){
|
||||
/* As there exists an unusable MATCH constraint this is an
|
||||
** unusable plan. Set a prohibitively high cost. */
|
||||
pInfo->estimatedCost = 1e50;
|
||||
assert( iIdxStr < pInfo->nConstraint*6 + 1 );
|
||||
idxStr[iIdxStr] = 0;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
if( iCol==nCol+1 ){
|
||||
if( bSeenRank ) continue;
|
||||
idxStr[iIdxStr++] = 'r';
|
||||
bSeenRank = 1;
|
||||
}else{
|
||||
bSeenMatch = 1;
|
||||
idxStr[iIdxStr++] = 'm';
|
||||
if( iCol<nCol ){
|
||||
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->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){
|
||||
int j;
|
||||
for(j=1; j<ArraySize(aConstraint); j++){
|
||||
struct Constraint *pC = &aConstraint[j];
|
||||
if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){
|
||||
pC->iConsIndex = i;
|
||||
idxFlags |= pC->fts5op;
|
||||
}
|
||||
else if( p->usable && bSeenEq==0
|
||||
&& p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0
|
||||
){
|
||||
idxStr[iIdxStr++] = '=';
|
||||
bSeenEq = 1;
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
}
|
||||
}
|
||||
|
||||
if( bSeenEq==0 ){
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
|
||||
if( p->iColumn<0 && p->usable ){
|
||||
int op = p->op;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LT || op==SQLITE_INDEX_CONSTRAINT_LE ){
|
||||
if( bSeenLt ) continue;
|
||||
idxStr[iIdxStr++] = '<';
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
bSeenLt = 1;
|
||||
}else
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_GT || op==SQLITE_INDEX_CONSTRAINT_GE ){
|
||||
if( bSeenGt ) continue;
|
||||
idxStr[iIdxStr++] = '>';
|
||||
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
|
||||
bSeenGt = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
idxStr[iIdxStr] = '\0';
|
||||
|
||||
/* Set idxFlags flags for the ORDER BY clause */
|
||||
if( pInfo->nOrderBy==1 ){
|
||||
int iSort = pInfo->aOrderBy[0].iColumn;
|
||||
if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){
|
||||
if( iSort==(pConfig->nCol+1) && bSeenMatch ){
|
||||
idxFlags |= FTS5_BI_ORDER_RANK;
|
||||
}else if( iSort==-1 ){
|
||||
idxFlags |= FTS5_BI_ORDER_ROWID;
|
||||
@ -583,26 +636,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
}
|
||||
|
||||
/* Calculate the estimated cost based on the flags set in idxFlags. */
|
||||
bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
|
||||
if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
|
||||
pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
|
||||
if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
|
||||
}else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
|
||||
pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
|
||||
}else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
|
||||
pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
|
||||
if( bSeenEq ){
|
||||
pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0;
|
||||
if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
|
||||
}else if( bSeenLt && bSeenGt ){
|
||||
pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0;
|
||||
}else if( bSeenLt || bSeenGt ){
|
||||
pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0;
|
||||
}else{
|
||||
pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0;
|
||||
}
|
||||
|
||||
/* Assign argvIndex values to each constraint in use. */
|
||||
iNext = 1;
|
||||
for(i=0; i<ArraySize(aConstraint); i++){
|
||||
struct Constraint *pC = &aConstraint[i];
|
||||
if( pC->iConsIndex>=0 ){
|
||||
pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
|
||||
pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
|
||||
}
|
||||
pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0;
|
||||
}
|
||||
|
||||
pInfo->idxNum = idxFlags;
|
||||
@ -925,7 +967,7 @@ static int fts5CursorFirstSorted(
|
||||
**
|
||||
** If SQLite a built-in statement cache, this wouldn't be a problem. */
|
||||
rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
|
||||
"SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
|
||||
"SELECT rowid, rank FROM %Q.%Q ORDER BY %s(\"%w\"%s%s) %s",
|
||||
pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
|
||||
(zRankArgs ? ", " : ""),
|
||||
(zRankArgs ? zRankArgs : ""),
|
||||
@ -981,10 +1023,10 @@ static int fts5SpecialMatch(
|
||||
assert( pTab->p.base.zErrMsg==0 );
|
||||
pCsr->ePlan = FTS5_PLAN_SPECIAL;
|
||||
|
||||
if( 0==sqlite3_strnicmp("reads", z, n) ){
|
||||
if( n==5 && 0==sqlite3_strnicmp("reads", z, n) ){
|
||||
pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex);
|
||||
}
|
||||
else if( 0==sqlite3_strnicmp("id", z, n) ){
|
||||
else if( n==2 && 0==sqlite3_strnicmp("id", z, n) ){
|
||||
pCsr->iSpecial = pCsr->iCsrId;
|
||||
}
|
||||
else{
|
||||
@ -1125,7 +1167,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
|
||||
static int fts5FilterMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
|
||||
int idxNum, /* Strategy index */
|
||||
const char *zUnused, /* Unused */
|
||||
const char *idxStr, /* Unused */
|
||||
int nVal, /* Number of elements in apVal */
|
||||
sqlite3_value **apVal /* Arguments for the indexing scheme */
|
||||
){
|
||||
@ -1133,19 +1175,17 @@ static int fts5FilterMethod(
|
||||
Fts5Config *pConfig = pTab->p.pConfig;
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
|
||||
int rc = SQLITE_OK; /* Error code */
|
||||
int iVal = 0; /* Counter for apVal[] */
|
||||
int bDesc; /* True if ORDER BY [rank|rowid] DESC */
|
||||
int bOrderByRank; /* True if ORDER BY rank */
|
||||
sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
|
||||
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
|
||||
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
|
||||
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
|
||||
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
|
||||
int iCol; /* Column on LHS of MATCH operator */
|
||||
char **pzErrmsg = pConfig->pzErrmsg;
|
||||
|
||||
UNUSED_PARAM(zUnused);
|
||||
UNUSED_PARAM(nVal);
|
||||
int i;
|
||||
int iIdxStr = 0;
|
||||
Fts5Expr *pExpr = 0;
|
||||
|
||||
if( pCsr->ePlan ){
|
||||
fts5FreeCursorComponents(pCsr);
|
||||
@ -1158,23 +1198,60 @@ static int fts5FilterMethod(
|
||||
assert( pCsr->pRank==0 );
|
||||
assert( pCsr->zRank==0 );
|
||||
assert( pCsr->zRankArgs==0 );
|
||||
assert( pTab->pSortCsr==0 || nVal==0 );
|
||||
|
||||
assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
|
||||
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
|
||||
|
||||
/* Decode the arguments passed through to this function.
|
||||
**
|
||||
** Note: The following set of if(...) statements must be in the same
|
||||
** order as the corresponding entries in the struct at the top of
|
||||
** fts5BestIndexMethod(). */
|
||||
if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
|
||||
if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++];
|
||||
if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++];
|
||||
if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++];
|
||||
if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
|
||||
iCol = (idxNum>>16);
|
||||
assert( iCol>=0 && iCol<=pConfig->nCol );
|
||||
assert( iVal==nVal );
|
||||
/* Decode the arguments passed through to this function. */
|
||||
for(i=0; i<nVal; i++){
|
||||
switch( idxStr[iIdxStr++] ){
|
||||
case 'r':
|
||||
pRank = apVal[i];
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
if( zText[0]=='*' ){
|
||||
/* The user has issued a query of the form "MATCH '*...'". This
|
||||
** indicates that the MATCH expression is not a full text query,
|
||||
** but a request for an internal parameter. */
|
||||
rc = fts5SpecialMatch(pTab, pCsr, &zText[1]);
|
||||
goto filter_out;
|
||||
}else{
|
||||
char **pzErr = &pTab->p.base.zErrMsg;
|
||||
rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
|
||||
pExpr = 0;
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto filter_out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case '=':
|
||||
pRowidEq = apVal[i];
|
||||
break;
|
||||
case '<':
|
||||
pRowidLe = apVal[i];
|
||||
break;
|
||||
default: assert( idxStr[iIdxStr-1]=='>' );
|
||||
pRowidGe = apVal[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
|
||||
pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
|
||||
|
||||
@ -1201,7 +1278,7 @@ static int fts5FilterMethod(
|
||||
** (pCursor) is used to execute the query issued by function
|
||||
** fts5CursorFirstSorted() above. */
|
||||
assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
|
||||
assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
|
||||
assert( nVal==0 && bOrderByRank==0 && bDesc==0 );
|
||||
assert( pCsr->iLastRowid==LARGEST_INT64 );
|
||||
assert( pCsr->iFirstRowid==SMALLEST_INT64 );
|
||||
if( pTab->pSortCsr->bDesc ){
|
||||
@ -1214,29 +1291,15 @@ static int fts5FilterMethod(
|
||||
pCsr->ePlan = FTS5_PLAN_SOURCE;
|
||||
pCsr->pExpr = pTab->pSortCsr->pExpr;
|
||||
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
||||
}else if( pMatch ){
|
||||
const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
|
||||
if( zExpr==0 ) zExpr = "";
|
||||
|
||||
}else if( pCsr->pExpr ){
|
||||
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( zExpr[0]=='*' ){
|
||||
/* The user has issued a query of the form "MATCH '*...'". This
|
||||
** indicates that the MATCH expression is not a full text query,
|
||||
** but a request for an internal parameter. */
|
||||
rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
|
||||
if( bOrderByRank ){
|
||||
pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
|
||||
rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
|
||||
}else{
|
||||
char **pzErr = &pTab->p.base.zErrMsg;
|
||||
rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( bOrderByRank ){
|
||||
pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
|
||||
rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
|
||||
}else{
|
||||
pCsr->ePlan = FTS5_PLAN_MATCH;
|
||||
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
||||
}
|
||||
}
|
||||
pCsr->ePlan = FTS5_PLAN_MATCH;
|
||||
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
||||
}
|
||||
}
|
||||
}else if( pConfig->zContent==0 ){
|
||||
@ -1253,7 +1316,7 @@ static int fts5FilterMethod(
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pCsr->ePlan==FTS5_PLAN_ROWID ){
|
||||
sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
|
||||
sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
|
||||
}else{
|
||||
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
|
||||
sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
|
||||
@ -1262,6 +1325,8 @@ static int fts5FilterMethod(
|
||||
}
|
||||
}
|
||||
|
||||
filter_out:
|
||||
sqlite3Fts5ExprFree(pExpr);
|
||||
pConfig->pzErrmsg = pzErrmsg;
|
||||
return rc;
|
||||
}
|
||||
@ -2232,7 +2297,7 @@ static void fts5ApiCallback(
|
||||
iCsrId = sqlite3_value_int64(argv[0]);
|
||||
|
||||
pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
|
||||
if( pCsr==0 ){
|
||||
if( pCsr==0 || pCsr->ePlan==0 ){
|
||||
char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
|
@ -138,7 +138,9 @@ static int fts5StorageGetStmt(
|
||||
}else{
|
||||
int f = SQLITE_PREPARE_PERSISTENT;
|
||||
if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB;
|
||||
p->pConfig->bLock++;
|
||||
rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0);
|
||||
p->pConfig->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK && pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
|
||||
|
@ -573,8 +573,10 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
|
||||
while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
|
||||
assert( pCsr->iCol<pCsr->pFts5->pConfig->nCol );
|
||||
for(/* noop */; pCsr->iCol<nCol && pCsr->aDoc[pCsr->iCol]==0; pCsr->iCol++);
|
||||
if( pCsr->iCol==nCol ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -253,5 +253,20 @@ do_execsql_test 6.2 {
|
||||
SELECT name FROM sqlite_master;
|
||||
} {}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Check that an fts5 table cannot be its own content table.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, c=t1 );
|
||||
INSERT INTO t1( a ) VALUES('abc');
|
||||
}
|
||||
do_catchsql_test 7.2 {
|
||||
SELECT * FROM t1;
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
do_catchsql_test 7.3 {
|
||||
SELECT * FROM t1('abc');
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
61
ext/fts5/test/fts5corrupt4.test
Normal file
61
ext/fts5/test/fts5corrupt4.test
Normal file
@ -0,0 +1,61 @@
|
||||
# 2019 May 16
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5corrupt4
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
sqlite3_fts5_may_be_corrupt 1
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ttt USING fts5(a, b);
|
||||
INSERT INTO ttt
|
||||
VALUES('e ee eee e ee eee e ee eee', 'eee ee e e e ee eee ee ee');
|
||||
INSERT INTO ttt SELECT a||a, b||b FROM ttt;
|
||||
INSERT INTO ttt SELECT a||a, b||b FROM ttt;
|
||||
}
|
||||
|
||||
proc mutate {blob i} {
|
||||
set o [expr {$i % [string length $blob]}]
|
||||
set a [string range $blob 0 $o-1]
|
||||
set b [string range $blob $o+1 end]
|
||||
set v [expr int(rand()*255) - 127]
|
||||
return "$a[binary format c $v]$b"
|
||||
}
|
||||
db func mutate mutate
|
||||
|
||||
for {set j 1000} {$j <= 5000} {incr j 1000} {
|
||||
do_test 1.$j {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql {
|
||||
BEGIN;
|
||||
UPDATE ttt_data SET block = mutate(block, $i) WHERE id>10;
|
||||
}
|
||||
foreach sql {
|
||||
{SELECT snippet(ttt, -1, '.', '..', '[', ']'), * FROM ttt('e*')}
|
||||
{SELECT snippet(ttt, -1, '.', '..', '[', ']'), * FROM ttt('e* NOT ee*')}
|
||||
} {
|
||||
catch { execsql $sql }
|
||||
}
|
||||
execsql ROLLBACK
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
@ -147,5 +147,27 @@ do_faultsim_test 5.1 -faults oom* -body {
|
||||
faultsim_test_result {0 {1 4}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test OOM injection in a query with two MATCH expressions
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
INSERT INTO t1 VALUES('a b c d'); -- 1
|
||||
INSERT INTO t1 VALUES('d a b c'); -- 2
|
||||
INSERT INTO t1 VALUES('c d a b'); -- 3
|
||||
INSERT INTO t1 VALUES('b c d a'); -- 4
|
||||
}
|
||||
do_faultsim_test 6.1 -faults oom* -body {
|
||||
execsql { SELECT rowid FROM t1 WHERE t1 MATCH 'a' AND t1 MATCH 'b' }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 3 4}}
|
||||
}
|
||||
do_faultsim_test 6.2 -faults oom* -body {
|
||||
execsql { SELECT rowid FROM t1 WHERE t1 MATCH 'a OR b' AND t1 MATCH 'c OR d' }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 3 4}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
111
ext/fts5/test/fts5misc.test
Normal file
111
ext/fts5/test/fts5misc.test
Normal file
@ -0,0 +1,111 @@
|
||||
# 2019 September 02
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5misc
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
}
|
||||
|
||||
do_catchsql_test 1.1.1 {
|
||||
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*');
|
||||
} {1 {unknown special query: }}
|
||||
do_catchsql_test 1.1.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*'));
|
||||
} {1 {unknown special query: }}
|
||||
|
||||
do_catchsql_test 1.2.1 {
|
||||
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*id');
|
||||
} {0 {{}}}
|
||||
|
||||
do_catchsql_test 1.2.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*id'));
|
||||
} {0 {}}
|
||||
|
||||
do_catchsql_test 1.3.1 {
|
||||
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads');
|
||||
} {1 {no such cursor: 1}}
|
||||
|
||||
do_catchsql_test 1.3.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
|
||||
} {1 {no such cursor: 1}}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 1.3.3 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
|
||||
} {1 {no such cursor: 1}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t0(c0);
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
}
|
||||
do_execsql_test 2.1.1 {
|
||||
BEGIN TRANSACTION;
|
||||
INSERT INTO vt0(c0) VALUES ('xyz');
|
||||
}
|
||||
do_execsql_test 2.1.2 {
|
||||
ALTER TABLE t0 ADD COLUMN c5;
|
||||
}
|
||||
do_execsql_test 2.1.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
do_execsql_test 2.1.4 {
|
||||
INSERT INTO vt0(c0) VALUES ('abc');
|
||||
COMMIT
|
||||
}
|
||||
do_execsql_test 2.1.5 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.2.1 {
|
||||
CREATE TABLE t0(c0);
|
||||
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
|
||||
BEGIN TRANSACTION;
|
||||
INSERT INTO vt0(c0) VALUES ('xyz');
|
||||
}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 2.2.2 {
|
||||
ALTER TABLE t0 RENAME TO t1;
|
||||
}
|
||||
do_execsql_test 2.2.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
do_execsql_test 2.2.4 {
|
||||
INSERT INTO vt0(c0) VALUES ('abc');
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 2.2.5 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
99
ext/fts5/test/fts5multi.test
Normal file
99
ext/fts5/test/fts5multi.test
Normal file
@ -0,0 +1,99 @@
|
||||
# 2014 September 13
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5multi
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
|
||||
INSERT INTO t1 VALUES('gg bb bb' ,'gg ff gg' ,'ii ii');
|
||||
INSERT INTO t1 VALUES('dd dd hh kk','jj' ,'aa');
|
||||
INSERT INTO t1 VALUES('kk gg ee' ,'hh cc' ,'hh jj aa cc');
|
||||
INSERT INTO t1 VALUES('hh' ,'bb jj cc' ,'kk ii');
|
||||
INSERT INTO t1 VALUES('kk dd kk ii','aa ee aa' ,'ee');
|
||||
INSERT INTO t1 VALUES('ee' ,'ff gg kk aa','ee ff ee');
|
||||
INSERT INTO t1 VALUES('ff jj' ,'gg ee' ,'kk ee gg kk');
|
||||
INSERT INTO t1 VALUES('ff ee dd hh','kk ee' ,'gg dd');
|
||||
INSERT INTO t1 VALUES('bb' ,'aa' ,'bb aa');
|
||||
INSERT INTO t1 VALUES('hh cc bb' ,'ff bb' ,'cc');
|
||||
INSERT INTO t1 VALUES('jj' ,'ff dd bb aa','dd dd ff ff');
|
||||
INSERT INTO t1 VALUES('ff dd gg dd','gg aa bb ff','cc');
|
||||
INSERT INTO t1 VALUES('ff aa cc jj','kk' ,'ii dd');
|
||||
INSERT INTO t1 VALUES('jj dd' ,'cc' ,'ii hh ee aa');
|
||||
INSERT INTO t1 VALUES('ff ii hh' ,'dd' ,'gg');
|
||||
INSERT INTO t1 VALUES('ff dd gg hh','hh' ,'ff dd');
|
||||
INSERT INTO t1 VALUES('cc cc' ,'ff dd ff' ,'bb');
|
||||
INSERT INTO t1 VALUES('ii' ,'bb ii' ,'jj kk');
|
||||
INSERT INTO t1 VALUES('ff hh' ,'hh bb' ,'bb dd ee');
|
||||
INSERT INTO t1 VALUES('jj kk' ,'jj' ,'gg ff cc');
|
||||
INSERT INTO t1 VALUES('dd kk' ,'ii gg' ,'dd');
|
||||
INSERT INTO t1 VALUES('cc' ,'aa ff' ,'ii');
|
||||
INSERT INTO t1 VALUES('bb ff bb ii','bb kk bb aa','hh ff ii dd');
|
||||
INSERT INTO t1 VALUES('aa' ,'ee bb jj jj','dd');
|
||||
INSERT INTO t1 VALUES('kk dd cc' ,'aa jj' ,'ee aa ff');
|
||||
INSERT INTO t1 VALUES('aa gg aa' ,'jj' ,'ii kk hh gg');
|
||||
INSERT INTO t1 VALUES('ff hh aa' ,'jj ii' ,'hh dd bb jj');
|
||||
INSERT INTO t1 VALUES('hh' ,'aa gg kk' ,'bb ee');
|
||||
INSERT INTO t1 VALUES('bb' ,'ee' ,'gg');
|
||||
INSERT INTO t1 VALUES('dd kk' ,'kk bb aa' ,'ee');
|
||||
}
|
||||
|
||||
foreach {tn c1 e1 c2 e2} {
|
||||
1 t1 aa t1 bb
|
||||
2 a aa b bb
|
||||
3 a "aa OR bb OR cc" b "jj OR ii OR hh"
|
||||
4 t1 "aa AND bb" t1 "cc"
|
||||
5 c "kk" b "aa OR bb OR cc OR dd OR ee"
|
||||
} {
|
||||
if {$c1=="t1"} {
|
||||
set lhs "( $e1 )"
|
||||
} else {
|
||||
set lhs "$c1 : ( $e1 )"
|
||||
}
|
||||
if {$c2=="t1"} {
|
||||
set rhs "( $e2 )"
|
||||
} else {
|
||||
set rhs "$c2 : ( $e2 )"
|
||||
}
|
||||
|
||||
set q1 "t1 MATCH '($lhs) AND ($rhs)'"
|
||||
set q2 "$c1 MATCH '$e1' AND $c2 MATCH '$e2'"
|
||||
|
||||
set ret [execsql "SELECT rowid FROM t1 WHERE $q1"]
|
||||
set N [llength $ret]
|
||||
do_execsql_test 1.$tn.1.($N) "SELECT rowid FROM t1 WHERE $q2" $ret
|
||||
|
||||
set ret [execsql "SELECT fts5_test_poslist(t1) FROM t1 WHERE $q1"]
|
||||
do_execsql_test 1.$tn.2.($N) "
|
||||
SELECT fts5_test_poslist(t1) FROM t1 WHERE $q2
|
||||
" $ret
|
||||
}
|
||||
|
||||
do_catchsql_test 2.1.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '(NOT' AND t1 MATCH 'aa bb';
|
||||
} {1 {fts5: syntax error near "NOT"}}
|
||||
do_catchsql_test 2.1.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'aa bb' AND t1 MATCH '(NOT';
|
||||
} {1 {fts5: syntax error near "NOT"}}
|
||||
|
||||
finish_test
|
||||
|
@ -31,7 +31,7 @@ do_eqp_test 1.1 {
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN TABLE t1
|
||||
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:
|
||||
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
|
||||
}
|
||||
|
||||
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 65537:
|
||||
|--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
|
||||
`--USE TEMP B-TREE FOR ORDER BY
|
||||
}
|
||||
|
||||
@ -60,6 +60,6 @@ do_eqp_test 1.4 {
|
||||
|
||||
do_eqp_test 1.5 {
|
||||
SELECT * FROM f1 WHERE rank MATCH ?
|
||||
} {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:}
|
||||
} {SCAN TABLE f1 VIRTUAL TABLE INDEX 0:r}
|
||||
|
||||
finish_test
|
||||
|
@ -162,4 +162,22 @@ do_execsql_test 5.1 {
|
||||
SELECT rowid FROM ttt('word') WHERE rowid BETWEEN 30 AND 40 ORDER BY rank;
|
||||
} {30 31 32 33 34 35 36 37 38 39 40}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE "My.Table" USING fts5(Text);
|
||||
|
||||
INSERT INTO "My.Table" VALUES ('hello this is a test');
|
||||
INSERT INTO "My.Table" VALUES ('of trying to order by');
|
||||
INSERT INTO "My.Table" VALUES ('rank on an fts5 table');
|
||||
INSERT INTO "My.Table" VALUES ('that have periods in');
|
||||
INSERT INTO "My.Table" VALUES ('the table names.');
|
||||
INSERT INTO "My.Table" VALUES ('table table table');
|
||||
}
|
||||
do_execsql_test 6.1 {
|
||||
SELECT * FROM "My.Table" WHERE Text MATCH 'table' ORDER BY rank;
|
||||
} {
|
||||
{table table table} {the table names.} {rank on an fts5 table}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -467,4 +467,17 @@ do_execsql_test 21.3 {
|
||||
SELECT rowid FROM x1($doc);
|
||||
} {11112}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
INSERT INTO x1(x) VALUES('a b c');
|
||||
INSERT INTO x1(x) VALUES('x y z');
|
||||
INSERT INTO x1(x) VALUES('c b a');
|
||||
INSERT INTO x1(x) VALUES('z y x');
|
||||
}
|
||||
|
||||
do_catchsql_test 22.1 {SELECT * FROM x1('')} {1 {fts5: syntax error near ""}}
|
||||
do_catchsql_test 22.2 {SELECT * FROM x1(NULL)} {1 {fts5: syntax error near ""}}
|
||||
|
||||
finish_test
|
||||
|
@ -43,10 +43,10 @@ LSMTESTSRC = $(LSMDIR)/lsm-test/lsmtest1.c $(LSMDIR)/lsm-test/lsmtest2.c \
|
||||
|
||||
# all: lsm.so
|
||||
|
||||
LSMOPTS += -DLSM_MUTEX_PTHREADS=1 -I$(LSMDIR) -DHAVE_ZLIB
|
||||
LSMOPTS += -fPIC -DLSM_MUTEX_PTHREADS=1 -I$(LSMDIR) -DHAVE_ZLIB
|
||||
|
||||
lsm.so: $(LSMOBJ)
|
||||
$(TCCX) -shared -o lsm.so $(LSMOBJ)
|
||||
$(TCCX) -shared -fPIC -o lsm.so $(LSMOBJ)
|
||||
|
||||
%.o: $(LSMDIR)/%.c $(LSMHDR) sqlite3.h
|
||||
$(TCCX) $(LSMOPTS) -c $<
|
||||
|
@ -842,7 +842,7 @@ static int lsm1BestIndex(
|
||||
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( pConstraint->iColumn!=0 ) continue;
|
||||
switch( pConstraint->op ){
|
||||
|
@ -88,6 +88,65 @@ do_execsql_test 210 {
|
||||
do_execsql_test 211 {
|
||||
SELECT quote(a), quote(lsm1_key), quote(lsm1_value), '|' FROM x1;
|
||||
} {'12' X'3132' X'05320000000000000A401FFB42ABE9DB' | '15' X'3135' X'4284C6' | '8' X'38' X'2162616E6A6F1633323105' |}
|
||||
do_execsql_test 212 {
|
||||
SELECT quote(a), quote(lsm1_key), quote(lsm1_value) FROM x1 WHERE a='12';
|
||||
} {'12' X'3132' X'05320000000000000A401FFB42ABE9DB'}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete testlsm.db
|
||||
load_lsm1_vtab db
|
||||
do_execsql_test 300 {
|
||||
CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,TEXT,b,c,d);
|
||||
}
|
||||
do_eqp_test 310 {
|
||||
SELECT * FROM x1 WHERE a=?
|
||||
} {SCAN TABLE x1 VIRTUAL TABLE INDEX 0:}
|
||||
|
||||
do_eqp_test 320 {
|
||||
SELECT * FROM x1 WHERE a>?
|
||||
} {SCAN TABLE x1 VIRTUAL TABLE INDEX 2:}
|
||||
|
||||
do_eqp_test 330 {
|
||||
SELECT * FROM x1 WHERE a<?
|
||||
} {SCAN TABLE x1 VIRTUAL TABLE INDEX 3:}
|
||||
do_eqp_test 340 {
|
||||
SELECT * FROM x1 WHERE a BETWEEN ? AND ?
|
||||
} {SCAN TABLE x1 VIRTUAL TABLE INDEX 1:}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete testlsm.db
|
||||
load_lsm1_vtab db
|
||||
do_execsql_test 400 {
|
||||
CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,TEXT,b);
|
||||
INSERT INTO x1 VALUES('one', 1);
|
||||
INSERT INTO x1 VALUES('two', 2);
|
||||
INSERT INTO x1 VALUES('three', 3);
|
||||
INSERT INTO x1 VALUES('four', 4);
|
||||
INSERT INTO x1 VALUES('five', 5);
|
||||
}
|
||||
do_execsql_test 410 {
|
||||
SELECT b FROM x1 WHERE a = 'two'
|
||||
} {2}
|
||||
do_execsql_test 411 {
|
||||
SELECT b FROM x1 WHERE a = 'one'
|
||||
} {1}
|
||||
do_execsql_test 412 {
|
||||
SELECT b FROM x1 WHERE a = 'five'
|
||||
} {5}
|
||||
|
||||
do_execsql_test 420 {
|
||||
SELECT b FROM x1 WHERE a BETWEEN 'one' AND 'three';
|
||||
} {1 3}
|
||||
do_execsql_test 421 {
|
||||
SELECT b FROM x1 WHERE a BETWEEN 'five' AND 'two';
|
||||
} {5 4 1 3 2}
|
||||
do_execsql_test 421 {
|
||||
SELECT b FROM x1 WHERE a > 'five';
|
||||
} {4 1 3 2}
|
||||
do_execsql_test 421 {
|
||||
SELECT b FROM x1 WHERE a <= 'three';
|
||||
} {3 1 4 5}
|
||||
|
||||
finish_test
|
||||
|
@ -76,7 +76,7 @@ static void readblobFunc(
|
||||
sqlite3_blob_close(pBlob);
|
||||
if( rc ){
|
||||
sqlite3_free(aData);
|
||||
sqlite3_result_error(context, "BLOB write failed", -1);
|
||||
sqlite3_result_error(context, "BLOB read failed", -1);
|
||||
}else{
|
||||
sqlite3_result_blob(context, aData, nData, sqlite3_free);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
**
|
||||
** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
|
||||
** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
|
||||
** sqlite3_bind_value(pStmt, i, aX, "carray", 0);
|
||||
** sqlite3_bind_pointer(pStmt, i, aX, "carray", 0);
|
||||
**
|
||||
** There is an optional third parameter to determine the datatype of
|
||||
** the C-language array. Allowed values of the third parameter are
|
||||
|
851
ext/misc/dbdata.c
Normal file
851
ext/misc/dbdata.c
Normal file
@ -0,0 +1,851 @@
|
||||
/*
|
||||
** 2019-04-17
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains an implementation of two eponymous virtual tables,
|
||||
** "sqlite_dbdata" and "sqlite_dbptr". Both modules require that the
|
||||
** "sqlite_dbpage" eponymous virtual table be available.
|
||||
**
|
||||
** SQLITE_DBDATA:
|
||||
** sqlite_dbdata is used to extract data directly from a database b-tree
|
||||
** page and its associated overflow pages, bypassing the b-tree layer.
|
||||
** The table schema is equivalent to:
|
||||
**
|
||||
** CREATE TABLE sqlite_dbdata(
|
||||
** pgno INTEGER,
|
||||
** cell INTEGER,
|
||||
** field INTEGER,
|
||||
** value ANY,
|
||||
** schema TEXT HIDDEN
|
||||
** );
|
||||
**
|
||||
** IMPORTANT: THE VIRTUAL TABLE SCHEMA ABOVE IS SUBJECT TO CHANGE. IN THE
|
||||
** FUTURE NEW NON-HIDDEN COLUMNS MAY BE ADDED BETWEEN "value" AND
|
||||
** "schema".
|
||||
**
|
||||
** Each page of the database is inspected. If it cannot be interpreted as
|
||||
** a b-tree page, or if it is a b-tree page containing 0 entries, the
|
||||
** sqlite_dbdata table contains no rows for that page. Otherwise, the
|
||||
** table contains one row for each field in the record associated with
|
||||
** each cell on the page. For intkey b-trees, the key value is stored in
|
||||
** field -1.
|
||||
**
|
||||
** For example, for the database:
|
||||
**
|
||||
** CREATE TABLE t1(a, b); -- root page is page 2
|
||||
** INSERT INTO t1(rowid, a, b) VALUES(5, 'v', 'five');
|
||||
** INSERT INTO t1(rowid, a, b) VALUES(10, 'x', 'ten');
|
||||
**
|
||||
** the sqlite_dbdata table contains, as well as from entries related to
|
||||
** page 1, content equivalent to:
|
||||
**
|
||||
** INSERT INTO sqlite_dbdata(pgno, cell, field, value) VALUES
|
||||
** (2, 0, -1, 5 ),
|
||||
** (2, 0, 0, 'v' ),
|
||||
** (2, 0, 1, 'five'),
|
||||
** (2, 1, -1, 10 ),
|
||||
** (2, 1, 0, 'x' ),
|
||||
** (2, 1, 1, 'ten' );
|
||||
**
|
||||
** If database corruption is encountered, this module does not report an
|
||||
** error. Instead, it attempts to extract as much data as possible and
|
||||
** ignores the corruption.
|
||||
**
|
||||
** SQLITE_DBPTR:
|
||||
** The sqlite_dbptr table has the following schema:
|
||||
**
|
||||
** CREATE TABLE sqlite_dbptr(
|
||||
** pgno INTEGER,
|
||||
** child INTEGER,
|
||||
** schema TEXT HIDDEN
|
||||
** );
|
||||
**
|
||||
** It contains one entry for each b-tree pointer between a parent and
|
||||
** child page in the database.
|
||||
*/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define DBDATA_PADDING_BYTES 100
|
||||
|
||||
typedef struct DbdataTable DbdataTable;
|
||||
typedef struct DbdataCursor DbdataCursor;
|
||||
|
||||
/* Cursor object */
|
||||
struct DbdataCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
sqlite3_stmt *pStmt; /* For fetching database pages */
|
||||
|
||||
int iPgno; /* Current page number */
|
||||
u8 *aPage; /* Buffer containing page */
|
||||
int nPage; /* Size of aPage[] in bytes */
|
||||
int nCell; /* Number of cells on aPage[] */
|
||||
int iCell; /* Current cell number */
|
||||
int bOnePage; /* True to stop after one page */
|
||||
int szDb;
|
||||
sqlite3_int64 iRowid;
|
||||
|
||||
/* Only for the sqlite_dbdata table */
|
||||
u8 *pRec; /* Buffer containing current record */
|
||||
int nRec; /* Size of pRec[] in bytes */
|
||||
int nHdr; /* Size of header in bytes */
|
||||
int iField; /* Current field number */
|
||||
u8 *pHdrPtr;
|
||||
u8 *pPtr;
|
||||
|
||||
sqlite3_int64 iIntkey; /* Integer key value */
|
||||
};
|
||||
|
||||
/* Table object */
|
||||
struct DbdataTable {
|
||||
sqlite3_vtab base; /* Base class. Must be first */
|
||||
sqlite3 *db; /* The database connection */
|
||||
sqlite3_stmt *pStmt; /* For fetching database pages */
|
||||
int bPtr; /* True for sqlite3_dbptr table */
|
||||
};
|
||||
|
||||
/* Column and schema definitions for sqlite_dbdata */
|
||||
#define DBDATA_COLUMN_PGNO 0
|
||||
#define DBDATA_COLUMN_CELL 1
|
||||
#define DBDATA_COLUMN_FIELD 2
|
||||
#define DBDATA_COLUMN_VALUE 3
|
||||
#define DBDATA_COLUMN_SCHEMA 4
|
||||
#define DBDATA_SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
" pgno INTEGER," \
|
||||
" cell INTEGER," \
|
||||
" field INTEGER," \
|
||||
" value ANY," \
|
||||
" schema TEXT HIDDEN" \
|
||||
")"
|
||||
|
||||
/* Column and schema definitions for sqlite_dbptr */
|
||||
#define DBPTR_COLUMN_PGNO 0
|
||||
#define DBPTR_COLUMN_CHILD 1
|
||||
#define DBPTR_COLUMN_SCHEMA 2
|
||||
#define DBPTR_SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
" pgno INTEGER," \
|
||||
" child INTEGER," \
|
||||
" schema TEXT HIDDEN" \
|
||||
")"
|
||||
|
||||
/*
|
||||
** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual
|
||||
** table.
|
||||
*/
|
||||
static int dbdataConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
DbdataTable *pTab = 0;
|
||||
int rc = sqlite3_declare_vtab(db, pAux ? DBPTR_SCHEMA : DBDATA_SCHEMA);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (DbdataTable*)sqlite3_malloc64(sizeof(DbdataTable));
|
||||
if( pTab==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pTab, 0, sizeof(DbdataTable));
|
||||
pTab->db = db;
|
||||
pTab->bPtr = (pAux!=0);
|
||||
}
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect from or destroy a sqlite_dbdata or sqlite_dbptr virtual table.
|
||||
*/
|
||||
static int dbdataDisconnect(sqlite3_vtab *pVtab){
|
||||
DbdataTable *pTab = (DbdataTable*)pVtab;
|
||||
if( pTab ){
|
||||
sqlite3_finalize(pTab->pStmt);
|
||||
sqlite3_free(pVtab);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function interprets two types of constraints:
|
||||
**
|
||||
** schema=?
|
||||
** pgno=?
|
||||
**
|
||||
** If neither are present, idxNum is set to 0. If schema=? is present,
|
||||
** the 0x01 bit in idxNum is set. If pgno=? is present, the 0x02 bit
|
||||
** in idxNum is set.
|
||||
**
|
||||
** If both parameters are present, schema is in position 0 and pgno in
|
||||
** position 1.
|
||||
*/
|
||||
static int dbdataBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdx){
|
||||
DbdataTable *pTab = (DbdataTable*)tab;
|
||||
int i;
|
||||
int iSchema = -1;
|
||||
int iPgno = -1;
|
||||
int colSchema = (pTab->bPtr ? DBPTR_COLUMN_SCHEMA : DBDATA_COLUMN_SCHEMA);
|
||||
|
||||
for(i=0; i<pIdx->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdx->aConstraint[i];
|
||||
if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
if( p->iColumn==colSchema ){
|
||||
if( p->usable==0 ) return SQLITE_CONSTRAINT;
|
||||
iSchema = i;
|
||||
}
|
||||
if( p->iColumn==DBDATA_COLUMN_PGNO && p->usable ){
|
||||
iPgno = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( iSchema>=0 ){
|
||||
pIdx->aConstraintUsage[iSchema].argvIndex = 1;
|
||||
pIdx->aConstraintUsage[iSchema].omit = 1;
|
||||
}
|
||||
if( iPgno>=0 ){
|
||||
pIdx->aConstraintUsage[iPgno].argvIndex = 1 + (iSchema>=0);
|
||||
pIdx->aConstraintUsage[iPgno].omit = 1;
|
||||
pIdx->estimatedCost = 100;
|
||||
pIdx->estimatedRows = 50;
|
||||
|
||||
if( pTab->bPtr==0 && pIdx->nOrderBy && pIdx->aOrderBy[0].desc==0 ){
|
||||
int iCol = pIdx->aOrderBy[0].iColumn;
|
||||
if( pIdx->nOrderBy==1 ){
|
||||
pIdx->orderByConsumed = (iCol==0 || iCol==1);
|
||||
}else if( pIdx->nOrderBy==2 && pIdx->aOrderBy[1].desc==0 && iCol==0 ){
|
||||
pIdx->orderByConsumed = (pIdx->aOrderBy[1].iColumn==1);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
pIdx->estimatedCost = 100000000;
|
||||
pIdx->estimatedRows = 1000000000;
|
||||
}
|
||||
pIdx->idxNum = (iSchema>=0 ? 0x01 : 0x00) | (iPgno>=0 ? 0x02 : 0x00);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new sqlite_dbdata or sqlite_dbptr cursor.
|
||||
*/
|
||||
static int dbdataOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
DbdataCursor *pCsr;
|
||||
|
||||
pCsr = (DbdataCursor*)sqlite3_malloc64(sizeof(DbdataCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pCsr, 0, sizeof(DbdataCursor));
|
||||
pCsr->base.pVtab = pVTab;
|
||||
}
|
||||
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Restore a cursor object to the state it was in when first allocated
|
||||
** by dbdataOpen().
|
||||
*/
|
||||
static void dbdataResetCursor(DbdataCursor *pCsr){
|
||||
DbdataTable *pTab = (DbdataTable*)(pCsr->base.pVtab);
|
||||
if( pTab->pStmt==0 ){
|
||||
pTab->pStmt = pCsr->pStmt;
|
||||
}else{
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
}
|
||||
pCsr->pStmt = 0;
|
||||
pCsr->iPgno = 1;
|
||||
pCsr->iCell = 0;
|
||||
pCsr->iField = 0;
|
||||
pCsr->bOnePage = 0;
|
||||
sqlite3_free(pCsr->aPage);
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->pRec = 0;
|
||||
pCsr->aPage = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an sqlite_dbdata or sqlite_dbptr cursor.
|
||||
*/
|
||||
static int dbdataClose(sqlite3_vtab_cursor *pCursor){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
dbdataResetCursor(pCsr);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Utility methods to decode 16 and 32-bit big-endian unsigned integers.
|
||||
*/
|
||||
static unsigned int get_uint16(unsigned char *a){
|
||||
return (a[0]<<8)|a[1];
|
||||
}
|
||||
static unsigned int get_uint32(unsigned char *a){
|
||||
return ((unsigned int)a[0]<<24)
|
||||
| ((unsigned int)a[1]<<16)
|
||||
| ((unsigned int)a[2]<<8)
|
||||
| ((unsigned int)a[3]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Load page pgno from the database via the sqlite_dbpage virtual table.
|
||||
** If successful, set (*ppPage) to point to a buffer containing the page
|
||||
** data, (*pnPage) to the size of that buffer in bytes and return
|
||||
** SQLITE_OK. In this case it is the responsibility of the caller to
|
||||
** eventually free the buffer using sqlite3_free().
|
||||
**
|
||||
** Or, if an error occurs, set both (*ppPage) and (*pnPage) to 0 and
|
||||
** return an SQLite error code.
|
||||
*/
|
||||
static int dbdataLoadPage(
|
||||
DbdataCursor *pCsr, /* Cursor object */
|
||||
unsigned int pgno, /* Page number of page to load */
|
||||
u8 **ppPage, /* OUT: pointer to page buffer */
|
||||
int *pnPage /* OUT: Size of (*ppPage) in bytes */
|
||||
){
|
||||
int rc2;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pStmt = pCsr->pStmt;
|
||||
|
||||
*ppPage = 0;
|
||||
*pnPage = 0;
|
||||
sqlite3_bind_int64(pStmt, 2, pgno);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int nCopy = sqlite3_column_bytes(pStmt, 0);
|
||||
if( nCopy>0 ){
|
||||
u8 *pPage;
|
||||
pPage = (u8*)sqlite3_malloc64(nCopy + DBDATA_PADDING_BYTES);
|
||||
if( pPage==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
const u8 *pCopy = sqlite3_column_blob(pStmt, 0);
|
||||
memcpy(pPage, pCopy, nCopy);
|
||||
memset(&pPage[nCopy], 0, DBDATA_PADDING_BYTES);
|
||||
}
|
||||
*ppPage = pPage;
|
||||
*pnPage = nCopy;
|
||||
}
|
||||
}
|
||||
rc2 = sqlite3_reset(pStmt);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a varint. Put the value in *pVal and return the number of bytes.
|
||||
*/
|
||||
static int dbdataGetVarint(const u8 *z, sqlite3_int64 *pVal){
|
||||
sqlite3_int64 v = 0;
|
||||
int i;
|
||||
for(i=0; i<8; i++){
|
||||
v = (v<<7) + (z[i]&0x7f);
|
||||
if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
|
||||
}
|
||||
v = (v<<8) + (z[i]&0xff);
|
||||
*pVal = v;
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes of space used by an SQLite value of type
|
||||
** eType.
|
||||
*/
|
||||
static int dbdataValueBytes(int eType){
|
||||
switch( eType ){
|
||||
case 0: case 8: case 9:
|
||||
case 10: case 11:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1;
|
||||
case 2:
|
||||
return 2;
|
||||
case 3:
|
||||
return 3;
|
||||
case 4:
|
||||
return 4;
|
||||
case 5:
|
||||
return 6;
|
||||
case 6:
|
||||
case 7:
|
||||
return 8;
|
||||
default:
|
||||
if( eType>0 ){
|
||||
return ((eType-12) / 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Load a value of type eType from buffer pData and use it to set the
|
||||
** result of context object pCtx.
|
||||
*/
|
||||
static void dbdataValue(
|
||||
sqlite3_context *pCtx,
|
||||
int eType,
|
||||
u8 *pData,
|
||||
int nData
|
||||
){
|
||||
if( eType>=0 && dbdataValueBytes(eType)<=nData ){
|
||||
switch( eType ){
|
||||
case 0:
|
||||
case 10:
|
||||
case 11:
|
||||
sqlite3_result_null(pCtx);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
sqlite3_result_int(pCtx, 0);
|
||||
break;
|
||||
case 9:
|
||||
sqlite3_result_int(pCtx, 1);
|
||||
break;
|
||||
|
||||
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
|
||||
sqlite3_uint64 v = (signed char)pData[0];
|
||||
pData++;
|
||||
switch( eType ){
|
||||
case 7:
|
||||
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 4: v = (v<<8) + pData[0]; pData++;
|
||||
case 3: v = (v<<8) + pData[0]; pData++;
|
||||
case 2: v = (v<<8) + pData[0]; pData++;
|
||||
}
|
||||
|
||||
if( eType==7 ){
|
||||
double r;
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
sqlite3_result_double(pCtx, r);
|
||||
}else{
|
||||
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int n = ((eType-12) / 2);
|
||||
if( eType % 2 ){
|
||||
sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry.
|
||||
*/
|
||||
static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
||||
|
||||
pCsr->iRowid++;
|
||||
while( 1 ){
|
||||
int rc;
|
||||
int iOff = (pCsr->iPgno==1 ? 100 : 0);
|
||||
int bNextPage = 0;
|
||||
|
||||
if( pCsr->aPage==0 ){
|
||||
while( 1 ){
|
||||
if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK;
|
||||
rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( pCsr->aPage ) break;
|
||||
pCsr->iPgno++;
|
||||
}
|
||||
pCsr->iCell = pTab->bPtr ? -2 : 0;
|
||||
pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]);
|
||||
}
|
||||
|
||||
if( pTab->bPtr ){
|
||||
if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){
|
||||
pCsr->iCell = pCsr->nCell;
|
||||
}
|
||||
pCsr->iCell++;
|
||||
if( pCsr->iCell>=pCsr->nCell ){
|
||||
sqlite3_free(pCsr->aPage);
|
||||
pCsr->aPage = 0;
|
||||
if( pCsr->bOnePage ) return SQLITE_OK;
|
||||
pCsr->iPgno++;
|
||||
}else{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}else{
|
||||
/* If there is no record loaded, load it now. */
|
||||
if( pCsr->pRec==0 ){
|
||||
int bHasRowid = 0;
|
||||
int nPointer = 0;
|
||||
sqlite3_int64 nPayload = 0;
|
||||
sqlite3_int64 nHdr = 0;
|
||||
int iHdr;
|
||||
int U, X;
|
||||
int nLocal;
|
||||
|
||||
switch( pCsr->aPage[iOff] ){
|
||||
case 0x02:
|
||||
nPointer = 4;
|
||||
break;
|
||||
case 0x0a:
|
||||
break;
|
||||
case 0x0d:
|
||||
bHasRowid = 1;
|
||||
break;
|
||||
default:
|
||||
/* This is not a b-tree page with records on it. Continue. */
|
||||
pCsr->iCell = pCsr->nCell;
|
||||
break;
|
||||
}
|
||||
|
||||
if( pCsr->iCell>=pCsr->nCell ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
|
||||
iOff += 8 + nPointer + pCsr->iCell*2;
|
||||
if( iOff>pCsr->nPage ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
iOff = get_uint16(&pCsr->aPage[iOff]);
|
||||
}
|
||||
|
||||
/* For an interior node cell, skip past the child-page number */
|
||||
iOff += nPointer;
|
||||
|
||||
/* Load the "byte of payload including overflow" field */
|
||||
if( bNextPage || iOff>pCsr->nPage ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &nPayload);
|
||||
}
|
||||
|
||||
/* If this is a leaf intkey cell, load the rowid */
|
||||
if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
|
||||
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
|
||||
}
|
||||
|
||||
/* Figure out how much data to read from the local page */
|
||||
U = pCsr->nPage;
|
||||
if( bHasRowid ){
|
||||
X = U-35;
|
||||
}else{
|
||||
X = ((U-12)*64/255)-23;
|
||||
}
|
||||
if( nPayload<=X ){
|
||||
nLocal = nPayload;
|
||||
}else{
|
||||
int M, K;
|
||||
M = ((U-12)*32/255)-23;
|
||||
K = M+((nPayload-M)%(U-4));
|
||||
if( K<=X ){
|
||||
nLocal = K;
|
||||
}else{
|
||||
nLocal = M;
|
||||
}
|
||||
}
|
||||
|
||||
if( bNextPage || nLocal+iOff>pCsr->nPage ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
|
||||
/* Allocate space for payload. And a bit more to catch small buffer
|
||||
** overruns caused by attempting to read a varint or similar from
|
||||
** near the end of a corrupt record. */
|
||||
pCsr->pRec = (u8*)sqlite3_malloc64(nPayload+DBDATA_PADDING_BYTES);
|
||||
if( pCsr->pRec==0 ) return SQLITE_NOMEM;
|
||||
memset(pCsr->pRec, 0, nPayload+DBDATA_PADDING_BYTES);
|
||||
pCsr->nRec = nPayload;
|
||||
|
||||
/* Load the nLocal bytes of payload */
|
||||
memcpy(pCsr->pRec, &pCsr->aPage[iOff], nLocal);
|
||||
iOff += nLocal;
|
||||
|
||||
/* Load content from overflow pages */
|
||||
if( nPayload>nLocal ){
|
||||
sqlite3_int64 nRem = nPayload - nLocal;
|
||||
unsigned int pgnoOvfl = get_uint32(&pCsr->aPage[iOff]);
|
||||
while( nRem>0 ){
|
||||
u8 *aOvfl = 0;
|
||||
int nOvfl = 0;
|
||||
int nCopy;
|
||||
rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl);
|
||||
assert( rc!=SQLITE_OK || aOvfl==0 || nOvfl==pCsr->nPage );
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( aOvfl==0 ) break;
|
||||
|
||||
nCopy = U-4;
|
||||
if( nCopy>nRem ) nCopy = nRem;
|
||||
memcpy(&pCsr->pRec[nPayload-nRem], &aOvfl[4], nCopy);
|
||||
nRem -= nCopy;
|
||||
|
||||
pgnoOvfl = get_uint32(aOvfl);
|
||||
sqlite3_free(aOvfl);
|
||||
}
|
||||
}
|
||||
|
||||
iHdr = dbdataGetVarint(pCsr->pRec, &nHdr);
|
||||
pCsr->nHdr = nHdr;
|
||||
pCsr->pHdrPtr = &pCsr->pRec[iHdr];
|
||||
pCsr->pPtr = &pCsr->pRec[pCsr->nHdr];
|
||||
pCsr->iField = (bHasRowid ? -1 : 0);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
pCsr->iField++;
|
||||
if( pCsr->iField>0 ){
|
||||
sqlite3_int64 iType;
|
||||
if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
pCsr->pHdrPtr += dbdataGetVarint(pCsr->pHdrPtr, &iType);
|
||||
pCsr->pPtr += dbdataValueBytes(iType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( bNextPage ){
|
||||
sqlite3_free(pCsr->aPage);
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->aPage = 0;
|
||||
pCsr->pRec = 0;
|
||||
if( pCsr->bOnePage ) return SQLITE_OK;
|
||||
pCsr->iPgno++;
|
||||
}else{
|
||||
if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Advance to the next cell. The next iteration of the loop will load
|
||||
** the record and so on. */
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->pRec = 0;
|
||||
pCsr->iCell++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert( !"can't get here" );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the cursor is at EOF.
|
||||
*/
|
||||
static int dbdataEof(sqlite3_vtab_cursor *pCursor){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
return pCsr->aPage==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Determine the size in pages of database zSchema (where zSchema is
|
||||
** "main", "temp" or the name of an attached database) and set
|
||||
** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise,
|
||||
** an SQLite error code.
|
||||
*/
|
||||
static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){
|
||||
DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab;
|
||||
char *zSql = 0;
|
||||
int rc, rc2;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
pCsr->szDb = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
rc2 = sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter method for sqlite_dbdata and sqlite_dbptr.
|
||||
*/
|
||||
static int dbdataFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
const char *zSchema = "main";
|
||||
|
||||
dbdataResetCursor(pCsr);
|
||||
assert( pCsr->iPgno==1 );
|
||||
if( idxNum & 0x01 ){
|
||||
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
}
|
||||
if( idxNum & 0x02 ){
|
||||
pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]);
|
||||
pCsr->bOnePage = 1;
|
||||
}else{
|
||||
pCsr->nPage = dbdataDbsize(pCsr, zSchema);
|
||||
rc = dbdataDbsize(pCsr, zSchema);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTab->pStmt ){
|
||||
pCsr->pStmt = pTab->pStmt;
|
||||
pTab->pStmt = 0;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pTab->db,
|
||||
"SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1,
|
||||
&pCsr->pStmt, 0
|
||||
);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = dbdataNext(pCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a column for the sqlite_dbdata or sqlite_dbptr table.
|
||||
*/
|
||||
static int dbdataColumn(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
||||
if( pTab->bPtr ){
|
||||
switch( i ){
|
||||
case DBPTR_COLUMN_PGNO:
|
||||
sqlite3_result_int64(ctx, pCsr->iPgno);
|
||||
break;
|
||||
case DBPTR_COLUMN_CHILD: {
|
||||
int iOff = pCsr->iPgno==1 ? 100 : 0;
|
||||
if( pCsr->iCell<0 ){
|
||||
iOff += 8;
|
||||
}else{
|
||||
iOff += 12 + pCsr->iCell*2;
|
||||
if( iOff>pCsr->nPage ) return SQLITE_OK;
|
||||
iOff = get_uint16(&pCsr->aPage[iOff]);
|
||||
}
|
||||
if( iOff<=pCsr->nPage ){
|
||||
sqlite3_result_int64(ctx, get_uint32(&pCsr->aPage[iOff]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
switch( i ){
|
||||
case DBDATA_COLUMN_PGNO:
|
||||
sqlite3_result_int64(ctx, pCsr->iPgno);
|
||||
break;
|
||||
case DBDATA_COLUMN_CELL:
|
||||
sqlite3_result_int(ctx, pCsr->iCell);
|
||||
break;
|
||||
case DBDATA_COLUMN_FIELD:
|
||||
sqlite3_result_int(ctx, pCsr->iField);
|
||||
break;
|
||||
case DBDATA_COLUMN_VALUE: {
|
||||
if( pCsr->iField<0 ){
|
||||
sqlite3_result_int64(ctx, pCsr->iIntkey);
|
||||
}else{
|
||||
sqlite3_int64 iType;
|
||||
dbdataGetVarint(pCsr->pHdrPtr, &iType);
|
||||
dbdataValue(
|
||||
ctx, iType, pCsr->pPtr, &pCsr->pRec[pCsr->nRec] - pCsr->pPtr
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for an sqlite_dbdata or sqlite_dptr table.
|
||||
*/
|
||||
static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
*pRowid = pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Invoke this routine to register the "sqlite_dbdata" virtual table module
|
||||
*/
|
||||
static int sqlite3DbdataRegister(sqlite3 *db){
|
||||
static sqlite3_module dbdata_module = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
dbdataConnect, /* xConnect */
|
||||
dbdataBestIndex, /* xBestIndex */
|
||||
dbdataDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
dbdataOpen, /* xOpen - open a cursor */
|
||||
dbdataClose, /* xClose - close a cursor */
|
||||
dbdataFilter, /* xFilter - configure scan constraints */
|
||||
dbdataNext, /* xNext - advance a cursor */
|
||||
dbdataEof, /* xEof - check for end of scan */
|
||||
dbdataColumn, /* xColumn - read data */
|
||||
dbdataRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
};
|
||||
|
||||
int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_dbdata_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return sqlite3DbdataRegister(db);
|
||||
}
|
@ -36,6 +36,7 @@
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
/*
|
||||
** The "u32" type must be an unsigned 32-bit integer. Adjust this
|
||||
*/
|
||||
@ -47,6 +48,8 @@ typedef unsigned int u32;
|
||||
typedef short int s16;
|
||||
typedef unsigned short int u16;
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
|
||||
/*
|
||||
** The width of a hash window in bytes. The algorithm only works if this
|
||||
@ -849,6 +852,7 @@ static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
*/
|
||||
static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
|
||||
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
|
||||
sqlite3_free(pCur->aDelta);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -1083,6 +1083,7 @@ static JsonNode *jsonLookupStep(
|
||||
const char *zKey;
|
||||
JsonNode *pRoot = &pParse->aNode[iRoot];
|
||||
if( zPath[0]==0 ) return pRoot;
|
||||
if( pRoot->jnFlags & JNODE_REPLACE ) return 0;
|
||||
if( zPath[0]=='.' ){
|
||||
if( pRoot->eType!=JSON_OBJECT ) return 0;
|
||||
zPath++;
|
||||
@ -1123,7 +1124,7 @@ static JsonNode *jsonLookupStep(
|
||||
u32 iStart, iLabel;
|
||||
JsonNode *pNode;
|
||||
iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
|
||||
iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
|
||||
iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
|
||||
zPath += i;
|
||||
pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
|
||||
if( pParse->oom ) return 0;
|
||||
@ -1819,7 +1820,7 @@ static void jsonArrayStep(
|
||||
if( pStr->zBuf==0 ){
|
||||
jsonInit(pStr, ctx);
|
||||
jsonAppendChar(pStr, '[');
|
||||
}else{
|
||||
}else if( pStr->nUsed>1 ){
|
||||
jsonAppendChar(pStr, ',');
|
||||
pStr->pCtx = ctx;
|
||||
}
|
||||
@ -1867,9 +1868,11 @@ static void jsonGroupInverse(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int i;
|
||||
unsigned int i;
|
||||
int inStr = 0;
|
||||
int nNest = 0;
|
||||
char *z;
|
||||
char c;
|
||||
JsonString *pStr;
|
||||
UNUSED_PARAM(argc);
|
||||
UNUSED_PARAM(argv);
|
||||
@ -1880,12 +1883,18 @@ static void jsonGroupInverse(
|
||||
if( NEVER(!pStr) ) return;
|
||||
#endif
|
||||
z = pStr->zBuf;
|
||||
for(i=1; z[i]!=',' || inStr; i++){
|
||||
assert( i<pStr->nUsed );
|
||||
if( z[i]=='"' ){
|
||||
for(i=1; (c = z[i])!=',' || inStr || nNest; i++){
|
||||
if( i>=pStr->nUsed ){
|
||||
pStr->nUsed = 1;
|
||||
return;
|
||||
}
|
||||
if( c=='"' ){
|
||||
inStr = !inStr;
|
||||
}else if( z[i]=='\\' ){
|
||||
}else if( c=='\\' ){
|
||||
i++;
|
||||
}else if( !inStr ){
|
||||
if( c=='{' || c=='[' ) nNest++;
|
||||
if( c=='}' || c==']' ) nNest--;
|
||||
}
|
||||
}
|
||||
pStr->nUsed -= i;
|
||||
@ -1915,7 +1924,7 @@ static void jsonObjectStep(
|
||||
if( pStr->zBuf==0 ){
|
||||
jsonInit(pStr, ctx);
|
||||
jsonAppendChar(pStr, '{');
|
||||
}else{
|
||||
}else if( pStr->nUsed>1 ){
|
||||
jsonAppendChar(pStr, ',');
|
||||
pStr->pCtx = ctx;
|
||||
}
|
||||
@ -2503,14 +2512,14 @@ int sqlite3Json1Init(sqlite3 *db){
|
||||
#endif
|
||||
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
||||
(void*)&aFunc[i].flag,
|
||||
aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
|
||||
SQLITE_SUBTYPE | SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
|
||||
aAgg[i].xStep, aAgg[i].xFinal,
|
||||
aAgg[i].xValue, jsonGroupInverse, 0);
|
||||
}
|
||||
|
@ -610,7 +610,7 @@ static const char *re_subcompile_string(ReCompiled *p){
|
||||
** regular expression. Applications should invoke this routine once
|
||||
** for every call to re_compile() to avoid memory leaks.
|
||||
*/
|
||||
void re_free(ReCompiled *pRe){
|
||||
static void re_free(ReCompiled *pRe){
|
||||
if( pRe ){
|
||||
sqlite3_free(pRe->aOp);
|
||||
sqlite3_free(pRe->aArg);
|
||||
@ -624,7 +624,7 @@ void re_free(ReCompiled *pRe){
|
||||
** compiled regular expression in *ppRe. Return NULL on success or an
|
||||
** error message if something goes wrong.
|
||||
*/
|
||||
const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
|
||||
static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
|
||||
ReCompiled *pRe;
|
||||
const char *zErr;
|
||||
int i, j;
|
||||
|
@ -89,16 +89,16 @@ proc step_rbu_legacy {target rbu} {
|
||||
proc do_rbu_vacuum_test {tn step {statedb state.db}} {
|
||||
forcedelete $statedb
|
||||
if {$statedb=="" && $step==1} breakpoint
|
||||
uplevel [list do_test $tn.1 [string map [list %state% $statedb] {
|
||||
if {$step==0} { sqlite3rbu_vacuum rbu test.db {%state%}}
|
||||
uplevel [list do_test $tn.1 [string map [list %state% $statedb %step% $step] {
|
||||
if {%step%==0} { sqlite3rbu_vacuum rbu test.db {%state%}}
|
||||
while 1 {
|
||||
if {$step==1} { sqlite3rbu_vacuum rbu test.db {%state%}}
|
||||
if {%step%==1} { sqlite3rbu_vacuum rbu test.db {%state%}}
|
||||
set state [rbu state]
|
||||
check_prestep_state test.db $state
|
||||
set rc [rbu step]
|
||||
check_poststep_state $rc test.db $state
|
||||
if {$rc!="SQLITE_OK"} break
|
||||
if {$step==1} { rbu close }
|
||||
if {%step%==1} { rbu close }
|
||||
}
|
||||
rbu close
|
||||
}] {SQLITE_DONE}]
|
||||
|
93
ext/rbu/rbuexpr.test
Normal file
93
ext/rbu/rbuexpr.test
Normal file
@ -0,0 +1,93 @@
|
||||
# 2014 August 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rbuexpr
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c PRIMARY KEY);
|
||||
CREATE INDEX i1 ON t1(a, null, b+1);
|
||||
CREATE INDEX i2 ON t1(a+1, b+1, c+1);
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
INSERT INTO t1 VALUES(10, 11, 12);
|
||||
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
forcedelete rbu.db
|
||||
sqlite3 db2 rbu.db
|
||||
do_execsql_test -db db2 1.1 {
|
||||
CREATE TABLE data_t1(a, b, c, rbu_control);
|
||||
INSERT INTO data_t1 VALUES(13, 14, 15, 0);
|
||||
INSERT INTO data_t1 VALUES(NULL, NULL, 6, 1);
|
||||
INSERT INTO data_t1 VALUES(NULL, 'three', 3, '.x.');
|
||||
}
|
||||
db2 close
|
||||
db close
|
||||
|
||||
do_test 1.2 {
|
||||
run_rbu test.db rbu.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM t1 WHERE a=4;
|
||||
}
|
||||
|
||||
integrity_check 1.4
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(c1, c2, c3, i INTEGER PRIMARY KEY);
|
||||
INSERT INTO t1 VALUES('one', 'one', 'one', 1);
|
||||
INSERT INTO t1 VALUES('two', 'two', 'two', 2);
|
||||
INSERT INTO t1 VALUES('three', 'three', 'three', 3);
|
||||
INSERT INTO t1 VALUES('four', 'four', 'four', 4);
|
||||
|
||||
CREATE INDEX i1 ON t1( substr(c1, 1, 2) );
|
||||
CREATE INDEX i2 ON t1( c1 || c2 || c3 );
|
||||
CREATE INDEX i3 ON t1( length(c1) + length(c2) - 1, c3||i );
|
||||
}
|
||||
|
||||
forcedelete rbu.db
|
||||
sqlite3 db2 rbu.db
|
||||
do_execsql_test -db db2 2.1 {
|
||||
CREATE TABLE data_t1(c1, c2, c3, i, rbu_control);
|
||||
INSERT INTO data_t1 VALUES(NULL, NULL, NULL, 2, 1);
|
||||
INSERT INTO data_t1 VALUES('thirty', NULL, NULL, 3, 'xx..');
|
||||
INSERT INTO data_t1 VALUES('five', 'five', 'five', 5, 0);
|
||||
}
|
||||
db2 close
|
||||
|
||||
db close
|
||||
|
||||
do_test 2.2 {
|
||||
run_rbu test.db rbu.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
sqlite3 db test.db
|
||||
integrity_check 2.3
|
||||
|
||||
finish_test
|
||||
|
@ -52,6 +52,15 @@ do_faultsim_test 1 -faults oom* -prep {
|
||||
}
|
||||
|
||||
|
||||
sqlite3rbu_create_vfs -default rbu ""
|
||||
sqlite3 db test.db
|
||||
set ::vfsname [file_control_vfsname db]
|
||||
do_faultsim_test 2 -faults oom* -prep {
|
||||
} -body {
|
||||
file_control_vfsname db
|
||||
}
|
||||
db close
|
||||
sqlite3rbu_destroy_vfs rbu
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -83,7 +83,6 @@ foreach {fault errlist} {
|
||||
|
||||
do_faultsim_test 3 -faults $fault -prep {
|
||||
faultsim_restore_and_reopen
|
||||
forcedelete test.db2
|
||||
} -body {
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
rbu step
|
||||
@ -91,7 +90,6 @@ foreach {fault errlist} {
|
||||
} -test {
|
||||
eval [list faultsim_test_result {0 SQLITE_OK} {*}$::errlist]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
180
ext/rbu/rbumisc.test
Normal file
180
ext/rbu/rbumisc.test
Normal file
@ -0,0 +1,180 @@
|
||||
# 2014 August 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rbumisc
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
reset_db
|
||||
|
||||
proc populate_rbu_db {} {
|
||||
forcedelete rbu.db
|
||||
sqlite3 rbu rbu.db
|
||||
rbu eval {
|
||||
CREATE TABLE data_x1(a, b, c, rbu_control);
|
||||
INSERT INTO data_x1 VALUES(1, 1, 1, 0);
|
||||
INSERT INTO data_x1 VALUES(2, 2, 2, 0);
|
||||
|
||||
CREATE TABLE dat(a, b, c, rbu_control);
|
||||
CREATE TABLE "data x1"(a, b, c, rbu_control);
|
||||
CREATE TABLE datax1(a, b, c, rbu_control);
|
||||
CREATE TABLE data_(a, b, c, rbu_control);
|
||||
|
||||
INSERT INTO "data x1" VALUES(3, 3, 3, 0);
|
||||
INSERT INTO datax1 VALUES(3, 3, 3, 0);
|
||||
INSERT INTO data_ VALUES(3, 3, 3, 0);
|
||||
INSERT INTO dat VALUES(3, 3, 3, 0);
|
||||
}
|
||||
rbu close
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ensure that RBU is not confused by oddly named tables in an RBU
|
||||
# database.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(a, b, c INTEGER PRIMARY KEY);
|
||||
}
|
||||
do_test 1.1 {
|
||||
populate_rbu_db
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
step_rbu test.db rbu.db
|
||||
db eval { SELECT * FROM x1 }
|
||||
} {1 1 1 2 2 2}
|
||||
|
||||
do_test 1.3 {
|
||||
db eval { DELETE FROM x1 }
|
||||
sqlite3 rbu rbu.db
|
||||
rbu eval { DELETE FROM rbu_state }
|
||||
rbu close
|
||||
step_rbu test.db rbu.db
|
||||
db eval { SELECT * FROM x1 }
|
||||
} {1 1 1 2 2 2}
|
||||
|
||||
do_test 1.4 {
|
||||
db eval { DELETE FROM x1 }
|
||||
populate_rbu_db
|
||||
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
rbu step
|
||||
rbu close
|
||||
|
||||
forcecopy test.db-oal test.db-wal
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {1 {SQLITE_ERROR - cannot update wal mode database}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the effect of a wal file appearing after the target database has
|
||||
# been opened, but before it has been locked.
|
||||
#
|
||||
catch { db close }
|
||||
testvfs tvfs -default 1
|
||||
|
||||
for {set N 1} {$N < 10} {incr N} {
|
||||
reset_db
|
||||
populate_rbu_db
|
||||
do_execsql_test 2.$N.0 {
|
||||
CREATE TABLE x1(a, b, c INTEGER PRIMARY KEY);
|
||||
}
|
||||
|
||||
set nAccessCnt 0
|
||||
do_test 2.$N.1 {
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
rbu step
|
||||
rbu close
|
||||
} {SQLITE_OK}
|
||||
|
||||
tvfs script xAccess
|
||||
tvfs filter xAccess
|
||||
set nAccessCnt 0
|
||||
proc xAccess {method file args} {
|
||||
global nAccessCnt
|
||||
if {[file tail $file]=="test.db-wal"} {
|
||||
incr nAccessCnt -1
|
||||
if {$nAccessCnt==0} {
|
||||
set fd [open test.db-wal w]
|
||||
puts -nonewline $fd [string repeat 0 2000]
|
||||
close $fd
|
||||
}
|
||||
}
|
||||
return SQLITE_OK
|
||||
}
|
||||
|
||||
foreach r {
|
||||
{1 {SQLITE_ERROR - cannot update wal mode database}}
|
||||
{0 SQLITE_OK}
|
||||
{1 {SQLITE_CANTOPEN - unable to open database file}}
|
||||
} {
|
||||
set RES($r) 1
|
||||
}
|
||||
do_test 2.$N.2 {
|
||||
set ::nAccessCnt $N
|
||||
set res [list [catch {
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
rbu close
|
||||
} msg ] $msg]
|
||||
set RES($res)
|
||||
} {1}
|
||||
catch {rbu close}
|
||||
}
|
||||
catch {db close}
|
||||
catch {tvfs delete}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
testvfs tvfs -default 1
|
||||
reset_db
|
||||
populate_rbu_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE x1(a, b, c INTEGER PRIMARY KEY);
|
||||
}
|
||||
|
||||
tvfs script xFileControl
|
||||
tvfs filter xFileControl
|
||||
|
||||
proc xFileControl {method file verb args} {
|
||||
if {$verb=="ZIPVFS" && [info exists ::zipvfs_filecontrol]} {
|
||||
return $::zipvfs_filecontrol
|
||||
}
|
||||
return "SQLITE_NOTFOUND"
|
||||
}
|
||||
|
||||
breakpoint
|
||||
foreach {tn ret err} {
|
||||
1 SQLITE_OK 0
|
||||
2 SQLITE_ERROR 1
|
||||
3 SQLITE_NOTFOUND 0
|
||||
4 SQLITE_OMIT 1
|
||||
} {
|
||||
set ::zipvfs_filecontrol $ret
|
||||
do_test 3.$tn.1 {
|
||||
catch {
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
rbu close
|
||||
}
|
||||
} $err
|
||||
}
|
||||
catch {db close}
|
||||
catch {tvfs delete}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
finish_test
|
@ -40,6 +40,15 @@ foreach {tn without_rowid a b c d} {
|
||||
CREATE INDEX i1c3 ON t1(%C%) WHERE %C% IS NOT NULL;
|
||||
|
||||
CREATE INDEX i1c4 ON t1(%C%) WHERE %D% < 'd';
|
||||
CREATE INDEX i1c5 ON t1(
|
||||
%C% -- for (c = ... expressions
|
||||
) WHERE %D% < 'd';
|
||||
CREATE INDEX i1c6 ON t1(
|
||||
%C% /* Again, for (c=... expr */, %D%
|
||||
) WHERE %D% < 'd';
|
||||
|
||||
CREATE INDEX i1c7 ON t1(
|
||||
%C% /* As before, for (c=... "expr */) WHERE %D% < 'd';
|
||||
}
|
||||
|
||||
do_execsql_test $tn.1.1 {
|
||||
@ -80,6 +89,10 @@ foreach {tn without_rowid a b c d} {
|
||||
|
||||
set step 0
|
||||
do_rbu_vacuum_test $tn.1.5 0
|
||||
|
||||
do_test $tn.1.6 {
|
||||
execsql { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -414,5 +414,37 @@ foreach {bReopen} { 0 1 } {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that sqlite3_bp_progress() works with an RBU vacuum if there
|
||||
# is an rbu_count table in the db being vacuumed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO t1 SELECT i, i, i FROM s;
|
||||
CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
|
||||
INSERT INTO rbu_count VALUES('t1', (SELECT count(*) FROM t1));
|
||||
INSERT INTO rbu_count VALUES('rbu_count', 2);
|
||||
}
|
||||
|
||||
forcedelete state.db
|
||||
do_test 6.1 {
|
||||
set maxA 0
|
||||
set maxB 0
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
while {[rbu step]=="SQLITE_OK"} {
|
||||
foreach {a b} [rbu bp_progress] {
|
||||
if {$a > $maxA} { set maxA $a }
|
||||
if {$b > $maxB} { set maxB $b }
|
||||
}
|
||||
}
|
||||
list [rbu close] $maxA $maxB
|
||||
} {SQLITE_DONE 10000 10000}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -65,6 +65,7 @@ proc step_rbu_cachesize {target rbu stepsize cachesize temp_limit} {
|
||||
while 1 {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
rbu temp_size_limit $temp_limit
|
||||
if { [rbu temp_size_limit -1]!=$temp_limit } { error "round trip problem!" }
|
||||
sqlite3_exec_nr [rbu db 1] "PRAGMA cache_size = $cachesize"
|
||||
for {set i 0} {$i < $stepsize} {incr i} {
|
||||
set rc [rbu step]
|
||||
|
116
ext/rbu/rbuvacuum4.test
Normal file
116
ext/rbu/rbuvacuum4.test
Normal file
@ -0,0 +1,116 @@
|
||||
# 2019 Jan 3
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the RBU module. More specifically, it
|
||||
# contains tests to ensure that the sqlite3rbu_vacuum() API works as
|
||||
# expected.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set testprefix rbuvacuum4
|
||||
|
||||
set step 1
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
}
|
||||
do_rbu_vacuum_test 1.1 1
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b, c)) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
}
|
||||
do_rbu_vacuum_test 2.1 1
|
||||
do_execsql_test 2.2 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1 oN t1(b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
|
||||
CREATE TABLE t2(a, b, c INTEGER, PRIMARY KEY(c));
|
||||
CREATE INDEX i2 oN t2(b, a);
|
||||
INSERT INTO t2 VALUES('a', 'b', -1);
|
||||
INSERT INTO t2 VALUES('c', 'd', -2);
|
||||
INSERT INTO t2 VALUES('e', 'f', -3);
|
||||
}
|
||||
|
||||
do_rbu_vacuum_test 3.1 1
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t2;
|
||||
} {1 2 3 4 5 6 7 8 9 e f -3 c d -2 a b -1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE x1(a, b, c, d, PRIMARY KEY(c, b)) WITHOUT ROWID;
|
||||
INSERT INTO x1 VALUES(1, 1, 1, 1);
|
||||
INSERT INTO x1 VALUES(1, 1, 2, 1);
|
||||
INSERT INTO x1 VALUES(1, 2, 2, 1);
|
||||
|
||||
INSERT INTO x1 VALUES(NULL, 2, 3, NULL);
|
||||
INSERT INTO x1 VALUES(NULL, 2, 4, NULL);
|
||||
INSERT INTO x1 VALUES(NULL, 2, 5, NULL);
|
||||
|
||||
CREATE INDEX x1ad ON x1(d, a);
|
||||
CREATE INDEX x1null ON x1(d, a) WHERE d>15;
|
||||
}
|
||||
|
||||
do_rbu_vacuum_test 4.1.1 1
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
SELECT count(*) fROM x1
|
||||
} 6
|
||||
|
||||
do_rbu_vacuum_test 4.1.2 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE "a b c"(a, "b b" PRIMARY KEY, "c c");
|
||||
CREATE INDEX abc1 ON "a b c"(a, "c c");
|
||||
|
||||
INSERT INTO "a b c" VALUES(NULL, 'a', NULL);
|
||||
INSERT INTO "a b c" VALUES(NULL, 'b', NULL);
|
||||
INSERT INTO "a b c" VALUES(NULL, 'c', NULL);
|
||||
|
||||
INSERT INTO "a b c" VALUES(1, 2, 3);
|
||||
INSERT INTO "a b c" VALUES(3, 9, 1);
|
||||
INSERT INTO "a b c" VALUES('aaa', 'bbb', 'ccc');
|
||||
|
||||
CREATE INDEX abc2 ON "a b c"("c c" DESC, a);
|
||||
|
||||
CREATE TABLE x(a);
|
||||
INSERT INTO x VALUES('a'), ('b'), ('d');
|
||||
CREATE UNIQUE INDEX y ON x(a);
|
||||
}
|
||||
|
||||
do_rbu_vacuum_test 5.1 1
|
||||
|
||||
finish_test
|
||||
|
@ -182,6 +182,7 @@
|
||||
typedef struct RbuFrame RbuFrame;
|
||||
typedef struct RbuObjIter RbuObjIter;
|
||||
typedef struct RbuState RbuState;
|
||||
typedef struct RbuSpan RbuSpan;
|
||||
typedef struct rbu_vfs rbu_vfs;
|
||||
typedef struct rbu_file rbu_file;
|
||||
typedef struct RbuUpdateStmt RbuUpdateStmt;
|
||||
@ -226,6 +227,11 @@ struct RbuUpdateStmt {
|
||||
RbuUpdateStmt *pNext;
|
||||
};
|
||||
|
||||
struct RbuSpan {
|
||||
const char *zSpan;
|
||||
int nSpan;
|
||||
};
|
||||
|
||||
/*
|
||||
** An iterator of this type is used to iterate through all objects in
|
||||
** the target database that require updating. For each such table, the
|
||||
@ -275,6 +281,9 @@ struct RbuObjIter {
|
||||
sqlite3_stmt *pInsert; /* Statement for INSERT operations */
|
||||
sqlite3_stmt *pDelete; /* Statement for DELETE ops */
|
||||
sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
|
||||
int nIdxCol;
|
||||
RbuSpan *aIdxCol;
|
||||
char *zIdxSql;
|
||||
|
||||
/* Last UPDATE used (for PK b-tree updates only), or NULL. */
|
||||
RbuUpdateStmt *pRbuUpdate;
|
||||
@ -809,6 +818,8 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
|
||||
sqlite3_free(pUp);
|
||||
pUp = pTmp;
|
||||
}
|
||||
sqlite3_free(pIter->aIdxCol);
|
||||
sqlite3_free(pIter->zIdxSql);
|
||||
|
||||
pIter->pSelect = 0;
|
||||
pIter->pInsert = 0;
|
||||
@ -816,6 +827,9 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
|
||||
pIter->pRbuUpdate = 0;
|
||||
pIter->pTmpInsert = 0;
|
||||
pIter->nCol = 0;
|
||||
pIter->nIdxCol = 0;
|
||||
pIter->aIdxCol = 0;
|
||||
pIter->zIdxSql = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -930,6 +944,7 @@ static void rbuTargetNameFunc(
|
||||
zIn = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zIn ){
|
||||
if( rbuIsVacuum(p) ){
|
||||
assert( argc==2 || argc==1 );
|
||||
if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
|
||||
sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
|
||||
}
|
||||
@ -1088,14 +1103,15 @@ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
|
||||
static char *rbuStrndup(const char *zStr, int *pRc){
|
||||
char *zRet = 0;
|
||||
|
||||
assert( *pRc==SQLITE_OK );
|
||||
if( zStr ){
|
||||
size_t nCopy = strlen(zStr) + 1;
|
||||
zRet = (char*)sqlite3_malloc64(nCopy);
|
||||
if( zRet ){
|
||||
memcpy(zRet, zStr, nCopy);
|
||||
}else{
|
||||
*pRc = SQLITE_NOMEM;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
if( zStr ){
|
||||
size_t nCopy = strlen(zStr) + 1;
|
||||
zRet = (char*)sqlite3_malloc64(nCopy);
|
||||
if( zRet ){
|
||||
memcpy(zRet, zStr, nCopy);
|
||||
}else{
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1267,6 +1283,9 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
|
||||
int iCid = sqlite3_column_int(pXInfo, 1);
|
||||
if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
|
||||
if( iCid==-2 ){
|
||||
memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol);
|
||||
}
|
||||
}
|
||||
rbuFinalize(p, pXInfo);
|
||||
bIndex = 1;
|
||||
@ -1381,7 +1400,8 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
}
|
||||
|
||||
pIter->azTblType[iOrder] = rbuStrndup(zType, &p->rc);
|
||||
pIter->abTblPk[iOrder] = (iPk!=0);
|
||||
assert( iPk>=0 );
|
||||
pIter->abTblPk[iOrder] = (u8)iPk;
|
||||
pIter->abNotNull[iOrder] = (u8)bNotNull || (iPk!=0);
|
||||
iOrder++;
|
||||
}
|
||||
@ -1416,6 +1436,213 @@ static char *rbuObjIterGetCollist(
|
||||
return zList;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a comma separated list of the quoted PRIMARY KEY column names,
|
||||
** in order, for the current table. Before each column name, add the text
|
||||
** zPre. After each column name, add the zPost text. Use zSeparator as
|
||||
** the separator text (usually ", ").
|
||||
*/
|
||||
static char *rbuObjIterGetPkList(
|
||||
sqlite3rbu *p, /* RBU object */
|
||||
RbuObjIter *pIter, /* Object iterator for column names */
|
||||
const char *zPre, /* Before each quoted column name */
|
||||
const char *zSeparator, /* Separator to use between columns */
|
||||
const char *zPost /* After each quoted column name */
|
||||
){
|
||||
int iPk = 1;
|
||||
char *zRet = 0;
|
||||
const char *zSep = "";
|
||||
while( 1 ){
|
||||
int i;
|
||||
for(i=0; i<pIter->nTblCol; i++){
|
||||
if( (int)pIter->abTblPk[i]==iPk ){
|
||||
const char *zCol = pIter->azTblCol[i];
|
||||
zRet = rbuMPrintf(p, "%z%s%s\"%w\"%s", zRet, zSep, zPre, zCol, zPost);
|
||||
zSep = zSeparator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i==pIter->nTblCol ) break;
|
||||
iPk++;
|
||||
}
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called as part of restarting an RBU vacuum within
|
||||
** stage 1 of the process (while the *-oal file is being built) while
|
||||
** updating a table (not an index). The table may be a rowid table or
|
||||
** a WITHOUT ROWID table. It queries the target database to find the
|
||||
** largest key that has already been written to the target table and
|
||||
** constructs a WHERE clause that can be used to extract the remaining
|
||||
** rows from the source table. For a rowid table, the WHERE clause
|
||||
** is of the form:
|
||||
**
|
||||
** "WHERE _rowid_ > ?"
|
||||
**
|
||||
** and for WITHOUT ROWID tables:
|
||||
**
|
||||
** "WHERE (key1, key2) > (?, ?)"
|
||||
**
|
||||
** Instead of "?" placeholders, the actual WHERE clauses created by
|
||||
** this function contain literal SQL values.
|
||||
*/
|
||||
static char *rbuVacuumTableStart(
|
||||
sqlite3rbu *p, /* RBU handle */
|
||||
RbuObjIter *pIter, /* RBU iterator object */
|
||||
int bRowid, /* True for a rowid table */
|
||||
const char *zWrite /* Target table name prefix */
|
||||
){
|
||||
sqlite3_stmt *pMax = 0;
|
||||
char *zRet = 0;
|
||||
if( bRowid ){
|
||||
p->rc = prepareFreeAndCollectError(p->dbMain, &pMax, &p->zErrmsg,
|
||||
sqlite3_mprintf(
|
||||
"SELECT max(_rowid_) FROM \"%s%w\"", zWrite, pIter->zTbl
|
||||
)
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
|
||||
sqlite3_int64 iMax = sqlite3_column_int64(pMax, 0);
|
||||
zRet = rbuMPrintf(p, " WHERE _rowid_ > %lld ", iMax);
|
||||
}
|
||||
rbuFinalize(p, pMax);
|
||||
}else{
|
||||
char *zOrder = rbuObjIterGetPkList(p, pIter, "", ", ", " DESC");
|
||||
char *zSelect = rbuObjIterGetPkList(p, pIter, "quote(", "||','||", ")");
|
||||
char *zList = rbuObjIterGetPkList(p, pIter, "", ", ", "");
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareFreeAndCollectError(p->dbMain, &pMax, &p->zErrmsg,
|
||||
sqlite3_mprintf(
|
||||
"SELECT %s FROM \"%s%w\" ORDER BY %s LIMIT 1",
|
||||
zSelect, zWrite, pIter->zTbl, zOrder
|
||||
)
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
|
||||
const char *zVal = (const char*)sqlite3_column_text(pMax, 0);
|
||||
zRet = rbuMPrintf(p, " WHERE (%s) > (%s) ", zList, zVal);
|
||||
}
|
||||
rbuFinalize(p, pMax);
|
||||
}
|
||||
|
||||
sqlite3_free(zOrder);
|
||||
sqlite3_free(zSelect);
|
||||
sqlite3_free(zList);
|
||||
}
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called as part of restating an RBU vacuum when the
|
||||
** current operation is writing content to an index. If possible, it
|
||||
** queries the target index b-tree for the largest key already written to
|
||||
** it, then composes and returns an expression that can be used in a WHERE
|
||||
** clause to select the remaining required rows from the source table.
|
||||
** It is only possible to return such an expression if:
|
||||
**
|
||||
** * The index contains no DESC columns, and
|
||||
** * The last key written to the index before the operation was
|
||||
** suspended does not contain any NULL values.
|
||||
**
|
||||
** The expression is of the form:
|
||||
**
|
||||
** (index-field1, index-field2, ...) > (?, ?, ...)
|
||||
**
|
||||
** except that the "?" placeholders are replaced with literal values.
|
||||
**
|
||||
** If the expression cannot be created, NULL is returned. In this case,
|
||||
** the caller has to use an OFFSET clause to extract only the required
|
||||
** rows from the sourct table, just as it does for an RBU update operation.
|
||||
*/
|
||||
char *rbuVacuumIndexStart(
|
||||
sqlite3rbu *p, /* RBU handle */
|
||||
RbuObjIter *pIter /* RBU iterator object */
|
||||
){
|
||||
char *zOrder = 0;
|
||||
char *zLhs = 0;
|
||||
char *zSelect = 0;
|
||||
char *zVector = 0;
|
||||
char *zRet = 0;
|
||||
int bFailed = 0;
|
||||
const char *zSep = "";
|
||||
int iCol = 0;
|
||||
sqlite3_stmt *pXInfo = 0;
|
||||
|
||||
p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
|
||||
sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
|
||||
);
|
||||
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
|
||||
int iCid = sqlite3_column_int(pXInfo, 1);
|
||||
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
|
||||
const char *zCol;
|
||||
if( sqlite3_column_int(pXInfo, 3) ){
|
||||
bFailed = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if( iCid<0 ){
|
||||
if( pIter->eType==RBU_PK_IPK ){
|
||||
int i;
|
||||
for(i=0; pIter->abTblPk[i]==0; i++);
|
||||
assert( i<pIter->nTblCol );
|
||||
zCol = pIter->azTblCol[i];
|
||||
}else{
|
||||
zCol = "_rowid_";
|
||||
}
|
||||
}else{
|
||||
zCol = pIter->azTblCol[iCid];
|
||||
}
|
||||
|
||||
zLhs = rbuMPrintf(p, "%z%s \"%w\" COLLATE %Q",
|
||||
zLhs, zSep, zCol, zCollate
|
||||
);
|
||||
zOrder = rbuMPrintf(p, "%z%s \"rbu_imp_%d%w\" COLLATE %Q DESC",
|
||||
zOrder, zSep, iCol, zCol, zCollate
|
||||
);
|
||||
zSelect = rbuMPrintf(p, "%z%s quote(\"rbu_imp_%d%w\")",
|
||||
zSelect, zSep, iCol, zCol
|
||||
);
|
||||
zSep = ", ";
|
||||
iCol++;
|
||||
}
|
||||
rbuFinalize(p, pXInfo);
|
||||
if( bFailed ) goto index_start_out;
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pSel = 0;
|
||||
|
||||
p->rc = prepareFreeAndCollectError(p->dbMain, &pSel, &p->zErrmsg,
|
||||
sqlite3_mprintf("SELECT %s FROM \"rbu_imp_%w\" ORDER BY %s LIMIT 1",
|
||||
zSelect, pIter->zTbl, zOrder
|
||||
)
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSel) ){
|
||||
zSep = "";
|
||||
for(iCol=0; iCol<pIter->nCol; iCol++){
|
||||
const char *zQuoted = (const char*)sqlite3_column_text(pSel, iCol);
|
||||
if( zQuoted[0]=='N' ){
|
||||
bFailed = 1;
|
||||
break;
|
||||
}
|
||||
zVector = rbuMPrintf(p, "%z%s%s", zVector, zSep, zQuoted);
|
||||
zSep = ", ";
|
||||
}
|
||||
|
||||
if( !bFailed ){
|
||||
zRet = rbuMPrintf(p, "(%s) > (%s)", zLhs, zVector);
|
||||
}
|
||||
}
|
||||
rbuFinalize(p, pSel);
|
||||
}
|
||||
|
||||
index_start_out:
|
||||
sqlite3_free(zOrder);
|
||||
sqlite3_free(zSelect);
|
||||
sqlite3_free(zVector);
|
||||
sqlite3_free(zLhs);
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to create a SELECT list (the list of SQL
|
||||
** expressions that follows a SELECT keyword) for a SELECT statement
|
||||
@ -1470,29 +1697,37 @@ static char *rbuObjIterGetIndexCols(
|
||||
int iCid = sqlite3_column_int(pXInfo, 1);
|
||||
int bDesc = sqlite3_column_int(pXInfo, 3);
|
||||
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
|
||||
const char *zCol;
|
||||
const char *zCol = 0;
|
||||
const char *zType;
|
||||
|
||||
if( iCid<0 ){
|
||||
/* An integer primary key. If the table has an explicit IPK, use
|
||||
** its name. Otherwise, use "rbu_rowid". */
|
||||
if( pIter->eType==RBU_PK_IPK ){
|
||||
int i;
|
||||
for(i=0; pIter->abTblPk[i]==0; i++);
|
||||
assert( i<pIter->nTblCol );
|
||||
zCol = pIter->azTblCol[i];
|
||||
}else if( rbuIsVacuum(p) ){
|
||||
zCol = "_rowid_";
|
||||
if( iCid==-2 ){
|
||||
int iSeq = sqlite3_column_int(pXInfo, 0);
|
||||
zRet = sqlite3_mprintf("%z%s(%.*s) COLLATE %Q", zRet, zCom,
|
||||
pIter->aIdxCol[iSeq].nSpan, pIter->aIdxCol[iSeq].zSpan, zCollate
|
||||
);
|
||||
zType = "";
|
||||
}else {
|
||||
if( iCid<0 ){
|
||||
/* An integer primary key. If the table has an explicit IPK, use
|
||||
** its name. Otherwise, use "rbu_rowid". */
|
||||
if( pIter->eType==RBU_PK_IPK ){
|
||||
int i;
|
||||
for(i=0; pIter->abTblPk[i]==0; i++);
|
||||
assert( i<pIter->nTblCol );
|
||||
zCol = pIter->azTblCol[i];
|
||||
}else if( rbuIsVacuum(p) ){
|
||||
zCol = "_rowid_";
|
||||
}else{
|
||||
zCol = "rbu_rowid";
|
||||
}
|
||||
zType = "INTEGER";
|
||||
}else{
|
||||
zCol = "rbu_rowid";
|
||||
zCol = pIter->azTblCol[iCid];
|
||||
zType = pIter->azTblType[iCid];
|
||||
}
|
||||
zType = "INTEGER";
|
||||
}else{
|
||||
zCol = pIter->azTblCol[iCid];
|
||||
zType = pIter->azTblType[iCid];
|
||||
zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom,zCol,zCollate);
|
||||
}
|
||||
|
||||
zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
|
||||
if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
|
||||
const char *zOrder = (bDesc ? " DESC" : "");
|
||||
zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
|
||||
@ -1972,6 +2207,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
int rc = p->rc;
|
||||
char *zRet = 0;
|
||||
|
||||
assert( pIter->zIdxSql==0 && pIter->nIdxCol==0 && pIter->aIdxCol==0 );
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
|
||||
"SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?"
|
||||
@ -1981,21 +2218,50 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
int rc2;
|
||||
rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
char *zSql = (char*)sqlite3_column_text(pStmt, 0);
|
||||
if( zSql ){
|
||||
pIter->zIdxSql = zSql = rbuStrndup(zSql, &rc);
|
||||
}
|
||||
if( zSql ){
|
||||
int nParen = 0; /* Number of open parenthesis */
|
||||
int i;
|
||||
int iIdxCol = 0;
|
||||
int nIdxAlloc = 0;
|
||||
for(i=0; zSql[i]; i++){
|
||||
char c = zSql[i];
|
||||
|
||||
/* If necessary, grow the pIter->aIdxCol[] array */
|
||||
if( iIdxCol==nIdxAlloc ){
|
||||
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
|
||||
pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
|
||||
);
|
||||
if( aIdxCol==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
pIter->aIdxCol = aIdxCol;
|
||||
nIdxAlloc += 16;
|
||||
}
|
||||
|
||||
if( c=='(' ){
|
||||
if( nParen==0 ){
|
||||
assert( iIdxCol==0 );
|
||||
pIter->aIdxCol[0].zSpan = &zSql[i+1];
|
||||
}
|
||||
nParen++;
|
||||
}
|
||||
else if( c==')' ){
|
||||
nParen--;
|
||||
if( nParen==0 ){
|
||||
int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
|
||||
pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}else if( c==',' && nParen==1 ){
|
||||
int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
|
||||
pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
|
||||
pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1];
|
||||
}else if( c=='"' || c=='\'' || c=='`' ){
|
||||
for(i++; 1; i++){
|
||||
if( zSql[i]==c ){
|
||||
@ -2007,11 +2273,19 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
for(i++; 1; i++){
|
||||
if( zSql[i]==']' ) break;
|
||||
}
|
||||
}else if( c=='-' && zSql[i+1]=='-' ){
|
||||
for(i=i+2; zSql[i] && zSql[i]!='\n'; i++);
|
||||
if( zSql[i]=='\0' ) break;
|
||||
}else if( c=='/' && zSql[i+1]=='*' ){
|
||||
for(i=i+2; zSql[i] && (zSql[i]!='*' || zSql[i+1]!='/'); i++);
|
||||
if( zSql[i]=='\0' ) break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if( zSql[i] ){
|
||||
zRet = rbuStrndup(&zSql[i], &rc);
|
||||
}
|
||||
pIter->nIdxCol = iIdxCol;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2056,11 +2330,11 @@ static int rbuObjIterPrepareAll(
|
||||
int nBind = 0;
|
||||
|
||||
assert( pIter->eType!=RBU_PK_VTAB );
|
||||
zPart = rbuObjIterGetIndexWhere(p, pIter);
|
||||
zCollist = rbuObjIterGetIndexCols(
|
||||
p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
|
||||
);
|
||||
zBind = rbuObjIterGetBindlist(p, nBind);
|
||||
zPart = rbuObjIterGetIndexWhere(p, pIter);
|
||||
|
||||
/* Create the imposter table used to write to this index. */
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
|
||||
@ -2092,12 +2366,24 @@ static int rbuObjIterPrepareAll(
|
||||
if( p->rc==SQLITE_OK ){
|
||||
char *zSql;
|
||||
if( rbuIsVacuum(p) ){
|
||||
char *zStart = 0;
|
||||
if( nOffset ){
|
||||
zStart = rbuVacuumIndexStart(p, pIter);
|
||||
if( zStart ){
|
||||
sqlite3_free(zLimit);
|
||||
zLimit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT %s, 0 AS rbu_control FROM '%q' %s ORDER BY %s%s",
|
||||
"SELECT %s, 0 AS rbu_control FROM '%q' %s %s %s ORDER BY %s%s",
|
||||
zCollist,
|
||||
pIter->zDataTbl,
|
||||
zPart, zCollist, zLimit
|
||||
zPart,
|
||||
(zStart ? (zPart ? "AND" : "WHERE") : ""), zStart,
|
||||
zCollist, zLimit
|
||||
);
|
||||
sqlite3_free(zStart);
|
||||
}else
|
||||
|
||||
if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
|
||||
@ -2120,7 +2406,11 @@ static int rbuObjIterPrepareAll(
|
||||
zCollist, zLimit
|
||||
);
|
||||
}
|
||||
p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareFreeAndCollectError(p->dbRbu,&pIter->pSelect,pz,zSql);
|
||||
}else{
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(zImposterCols);
|
||||
@ -2220,18 +2510,42 @@ static int rbuObjIterPrepareAll(
|
||||
/* Create the SELECT statement to read keys from data_xxx */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
const char *zRbuRowid = "";
|
||||
char *zStart = 0;
|
||||
char *zOrder = 0;
|
||||
if( bRbuRowid ){
|
||||
zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
|
||||
}
|
||||
p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
|
||||
sqlite3_mprintf(
|
||||
"SELECT %s,%s rbu_control%s FROM '%q'%s",
|
||||
zCollist,
|
||||
(rbuIsVacuum(p) ? "0 AS " : ""),
|
||||
zRbuRowid,
|
||||
pIter->zDataTbl, zLimit
|
||||
)
|
||||
);
|
||||
|
||||
if( rbuIsVacuum(p) ){
|
||||
if( nOffset ){
|
||||
zStart = rbuVacuumTableStart(p, pIter, bRbuRowid, zWrite);
|
||||
if( zStart ){
|
||||
sqlite3_free(zLimit);
|
||||
zLimit = 0;
|
||||
}
|
||||
}
|
||||
if( bRbuRowid ){
|
||||
zOrder = rbuMPrintf(p, "_rowid_");
|
||||
}else{
|
||||
zOrder = rbuObjIterGetPkList(p, pIter, "", ", ", "");
|
||||
}
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
|
||||
sqlite3_mprintf(
|
||||
"SELECT %s,%s rbu_control%s FROM '%q'%s %s %s %s",
|
||||
zCollist,
|
||||
(rbuIsVacuum(p) ? "0 AS " : ""),
|
||||
zRbuRowid,
|
||||
pIter->zDataTbl, (zStart ? zStart : ""),
|
||||
(zOrder ? "ORDER BY" : ""), zOrder,
|
||||
zLimit
|
||||
)
|
||||
);
|
||||
}
|
||||
sqlite3_free(zStart);
|
||||
sqlite3_free(zOrder);
|
||||
}
|
||||
|
||||
sqlite3_free(zWhere);
|
||||
@ -3546,10 +3860,11 @@ static void rbuIndexCntFunc(
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char *zErrmsg = 0;
|
||||
int rc;
|
||||
sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
|
||||
|
||||
assert( nVal==1 );
|
||||
|
||||
rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
|
||||
rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
|
||||
sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
|
||||
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
|
||||
);
|
||||
@ -3564,7 +3879,7 @@ static void rbuIndexCntFunc(
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_int(pCtx, nIndex);
|
||||
}else{
|
||||
sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
|
||||
sqlite3_result_error(pCtx, sqlite3_errmsg(db), -1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4458,9 +4773,7 @@ static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
}else if( rc==SQLITE_NOTFOUND ){
|
||||
pRbu->pTargetFd = p;
|
||||
p->pRbu = pRbu;
|
||||
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
||||
rbuMainlistAdd(p);
|
||||
}
|
||||
rbuMainlistAdd(p);
|
||||
if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
@ -4523,10 +4836,7 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
||||
if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
|
||||
}else{
|
||||
int bCapture = 0;
|
||||
if( n==1 && (flags & SQLITE_SHM_EXCLUSIVE)
|
||||
&& pRbu && pRbu->eStage==RBU_STAGE_CAPTURE
|
||||
&& (ofst==WAL_LOCK_WRITE || ofst==WAL_LOCK_CKPT || ofst==WAL_LOCK_READ0)
|
||||
){
|
||||
if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
|
||||
bCapture = 1;
|
||||
}
|
||||
|
||||
@ -4559,20 +4869,24 @@ static int rbuVfsShmMap(
|
||||
** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space
|
||||
** instead of a file on disk. */
|
||||
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
||||
if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
|
||||
if( iRegion<=p->nShm ){
|
||||
sqlite3_int64 nByte = (iRegion+1) * sizeof(char*);
|
||||
char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
|
||||
if( apNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
|
||||
p->apShm = apNew;
|
||||
p->nShm = iRegion+1;
|
||||
}
|
||||
if( eStage==RBU_STAGE_OAL ){
|
||||
sqlite3_int64 nByte = (iRegion+1) * sizeof(char*);
|
||||
char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
|
||||
|
||||
/* This is an RBU connection that uses its own heap memory for the
|
||||
** pages of the *-shm file. Since no other process can have run
|
||||
** recovery, the connection must request *-shm pages in order
|
||||
** from start to finish. */
|
||||
assert( iRegion==p->nShm );
|
||||
if( apNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
|
||||
p->apShm = apNew;
|
||||
p->nShm = iRegion+1;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
|
||||
if( rc==SQLITE_OK ){
|
||||
char *pNew = (char*)sqlite3_malloc64(szRegion);
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
@ -4801,7 +5115,8 @@ static int rbuVfsAccess(
|
||||
*/
|
||||
if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
|
||||
rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath, 1);
|
||||
if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
|
||||
if( pDb && pDb->pRbu->eStage==RBU_STAGE_OAL ){
|
||||
assert( pDb->pRbu );
|
||||
if( *pResOut ){
|
||||
rc = SQLITE_CANTOPEN;
|
||||
}else{
|
||||
|
@ -63,10 +63,6 @@
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
#include "sqlite3rtree.h"
|
||||
typedef sqlite3_int64 i64;
|
||||
@ -74,7 +70,17 @@ typedef sqlite3_uint64 u64;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
|
||||
# define NDEBUG 1
|
||||
#endif
|
||||
#if defined(NDEBUG) && defined(SQLITE_DEBUG)
|
||||
# undef NDEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* The following macro is used to suppress compiler warnings.
|
||||
*/
|
||||
@ -663,7 +669,6 @@ static int nodeAcquire(
|
||||
** increase its reference count and return it.
|
||||
*/
|
||||
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
|
||||
assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
|
||||
if( pParent && !pNode->pParent ){
|
||||
if( nodeInParentChain(pNode, pParent) ){
|
||||
RTREE_IS_CORRUPT(pRtree);
|
||||
@ -671,6 +676,9 @@ static int nodeAcquire(
|
||||
}
|
||||
pParent->nRef++;
|
||||
pNode->pParent = pParent;
|
||||
}else if( pParent && pNode->pParent && pParent!=pNode->pParent ){
|
||||
RTREE_IS_CORRUPT(pRtree);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
pNode->nRef++;
|
||||
*ppNode = pNode;
|
||||
@ -1558,13 +1566,14 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
|
||||
|
||||
eInt = pRtree->eCoordType==RTREE_COORD_INT32;
|
||||
while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
|
||||
u8 *pCellData;
|
||||
pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
|
||||
if( rc ) return rc;
|
||||
nCell = NCELL(pNode);
|
||||
assert( nCell<200 );
|
||||
pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
|
||||
while( p->iCell<nCell ){
|
||||
sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
|
||||
u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
|
||||
eWithin = FULLY_WITHIN;
|
||||
for(ii=0; ii<nConstraint; ii++){
|
||||
RtreeConstraint *pConstraint = pCur->aConstraint + ii;
|
||||
@ -1577,13 +1586,23 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
|
||||
}else{
|
||||
rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin);
|
||||
}
|
||||
if( eWithin==NOT_WITHIN ) break;
|
||||
if( eWithin==NOT_WITHIN ){
|
||||
p->iCell++;
|
||||
pCellData += pRtree->nBytesPerCell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
p->iCell++;
|
||||
if( eWithin==NOT_WITHIN ) continue;
|
||||
p->iCell++;
|
||||
x.iLevel = p->iLevel - 1;
|
||||
if( x.iLevel ){
|
||||
x.id = readInt64(pCellData);
|
||||
for(ii=0; ii<pCur->nPoint; ii++){
|
||||
if( pCur->aPoint[ii].id==x.id ){
|
||||
RTREE_IS_CORRUPT(pRtree);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
}
|
||||
x.iCell = 0;
|
||||
}else{
|
||||
x.id = p->id;
|
||||
|
@ -465,6 +465,7 @@ do_test rtreefuzz001-100 {
|
||||
| end c1b.db
|
||||
}]
|
||||
catchsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
SELECT rtreecheck('t1');
|
||||
}
|
||||
} {1 {SQL logic error}}
|
||||
@ -774,4 +775,275 @@ do_test rtreefuzz001-400 {
|
||||
}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_test rtreefuzz001-500 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 16384 pagesize 4096 filename crash-2e81f5dce5cbd4.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 05 0e 6d 00 0f c8 0f 7b ..........m.....
|
||||
| 112: 0f 20 0e cd 0e 6d 00 00 00 00 00 00 00 00 00 00 . ...m..........
|
||||
| 3680: 00 00 00 00 00 00 00 00 00 00 00 00 00 5e 05 07 .............^..
|
||||
| 3696: 17 1f 1f 01 81 0b 74 61 62 6c 65 74 31 5f 70 61 ......tablet1_pa
|
||||
| 3712: 72 65 6e 74 74 31 5f 70 61 72 65 6e 74 05 43 52 rentt1_parent.CR
|
||||
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 70 EATE TABLE .t1_p
|
||||
| 3744: 61 72 65 6e 74 22 28 6e 6f 64 65 6e 6f 20 49 4e arent.(nodeno IN
|
||||
| 3760: 54 45 47 45 42 20 50 52 49 4d 41 52 59 20 4b 45 TEGEB PRIMARY KE
|
||||
| 3776: 59 2c 70 61 72 65 6e 74 6e 6f 64 65 29 51 04 06 Y,parentnode)Q..
|
||||
| 3792: 17 1b 1b 01 7b 74 61 62 6c 65 74 31 5f 6e 6f 64 .....tablet1_nod
|
||||
| 3808: 65 74 31 5f 6e 6f 64 65 04 43 52 45 41 54 45 20 et1_node.CREATE
|
||||
| 3824: 54 41 42 4c 45 20 22 74 31 5f 6e 6f 64 65 22 28 TABLE .t1_node.(
|
||||
| 3840: 6e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 52 20 50 nodeno INTEGER P
|
||||
| 3856: 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 74 61 29 RIMARY KEY,data)
|
||||
| 3872: 59 03 07 17 1d 1d 01 81 05 74 61 62 6c 65 84 31 Y........table.1
|
||||
| 3888: 5f 72 6f 77 69 64 74 31 5f 72 6f 87 69 64 03 43 _rowidt1_ro.id.C
|
||||
| 3904: 52 45 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f REATE TABLE .t1_
|
||||
| 3920: 72 6f 77 69 64 22 28 72 6f 77 69 64 20 49 4e 54 rowid.(rowid INT
|
||||
| 3936: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
|
||||
| 3952: 2c 6e f8 64 65 6e 6f 2c 61 30 29 4b 02 07 17 11 ,n.deno,a0)K....
|
||||
| 3968: 11 08 81 03 74 22 62 6c 65 74 31 74 31 43 52 45 ....t.blet1t1CRE
|
||||
| 3984: 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c ATE VIRTUAL TABL
|
||||
| 4000: 45 20 74 31 20 55 53 49 4e 47 20 72 74 72 65 65 E t1 USING rtree
|
||||
| 4016: 5f 69 33 32 28 69 cc 2c 78 30 2c 78 31 2c 79 30 _i32(i.,x0,x1,y0
|
||||
| 4032: 2c 79 31 2c 2b 65 78 29 36 01 06 17 17 17 01 4d ,y1,+ex)6......M
|
||||
| 4048: 74 61 62 6c 65 63 6f 6f 72 64 63 6f 6f 72 64 02 tablecoordcoord.
|
||||
| 4064: 43 52 45 41 54 45 20 54 41 42 4c 45 20 63 6f 6f CREATE TABLE coo
|
||||
| 4080: 71 64 28 76 20 49 4e 54 2c 20 77 20 49 4e 54 29 qd(v INT, w INT)
|
||||
| page 2 offset 4096
|
||||
| 4016: 00 00 00 00 00 00 00 00 00 00 00 05 0a 03 01 01 ................
|
||||
| 4032: 0a 02 05 09 03 01 01 09 02 05 08 03 01 01 08 02 ................
|
||||
| 4048: 05 07 03 01 01 07 02 05 06 03 11 01 06 02 05 05 ................
|
||||
| 4064: 03 01 01 05 02 05 04 03 01 01 04 02 05 03 03 01 ................
|
||||
| 4080: 01 03 02 05 02 03 01 01 02 02 04 01 03 09 01 02 ................
|
||||
| page 3 offset 8192
|
||||
| 0: 0d 0e 4f 00 64 0b 5a 12 0d bb 0d 84 0f eb 0d c6 ..O.d.Z.........
|
||||
| 16: 0f d7 0e cc 0f c1 0f b6 0f ab 0f 9f 0f 94 0d 8f ................
|
||||
| 32: 0f 86 0d d1 0f 62 0f 67 0f 5c 0f 51 1f 46 0f 3a .....b.g...Q.F.:
|
||||
| 48: 0f 30 0d 9a 0f 21 0d dc 0f 00 00 00 00 00 00 00 .0...!..........
|
||||
| 2896: 00 00 00 00 00 00 00 00 00 00 0a ce 1a 04 00 01 ................
|
||||
| 2912: 17 03 31 30 78 31 30 0a 4e 19 03 ff f1 15 03 31 ..10x10.N......1
|
||||
| 2928: 30 78 39 09 ce 18 04 00 01 15 03 31 30 78 38 09 0x9........10x8.
|
||||
| 2944: ce 17 04 00 01 15 03 31 30 78 37 09 ce 16 04 00 .......10x7.....
|
||||
| 2960: 12 15 03 31 30 78 36 09 ce 15 04 00 01 15 03 31 ...10x6........1
|
||||
| 2976: 30 78 35 09 ce 14 04 00 01 15 0d a1 30 78 34 09 0x5.........0x4.
|
||||
| 2992: ce 13 04 00 01 15 03 31 30 78 33 09 ce 12 04 00 .......10x3.....
|
||||
| 3008: 01 15 03 31 40 78 32 09 ce 11 04 00 01 15 03 31 ...1@x2........1
|
||||
| 3024: 30 78 31 09 c6 32 04 00 01 15 03 39 78 31 30 08 0x1..2.....9x10.
|
||||
| 3040: c6 31 04 00 01 13 03 39 78 39 08 c6 30 04 00 01 .1.....9x9..0...
|
||||
| 3056: 13 03 39 78 38 08 c6 2f 04 00 01 14 03 39 78 37 ..9x8../.....9x7
|
||||
| 3072: 08 c6 2e 04 00 01 13 03 39 78 36 08 c6 2d 04 00 ........9x6..-..
|
||||
| 3088: 01 13 03 39 78 34 f8 c6 2c 04 00 01 13 03 39 78 ...9x4..,.....9x
|
||||
| 3104: 34 08 c6 2b 04 00 60 13 03 39 79 13 08 c6 2a 04 4..+..`..9y...*.
|
||||
| 3120: 00 11 13 03 39 78 32 08 c6 29 04 00 01 13 03 39 ....9x2..).....9
|
||||
| 3136: 78 31 09 be 4a 04 00 01 15 03 38 78 31 30 08 be x1..J.....8x10..
|
||||
| 3152: 49 04 00 01 13 03 38 78 39 08 be 48 04 00 01 13 I.....8x9..H....
|
||||
| 3168: 03 38 77 98 08 be 47 04 00 01 14 23 38 78 37 08 .8w...G....#8x7.
|
||||
| 3184: be 46 04 00 01 13 03 38 78 36 08 be 45 04 00 01 .F.....8x6..E...
|
||||
| 3200: 13 03 38 78 35 08 be 44 04 00 01 13 03 38 78 34 ..8x5..D.....8x4
|
||||
| 3216: 08 be 43 04 00 01 13 03 38 78 33 08 be 42 04 00 ..C.....8x3..B..
|
||||
| 3232: 01 13 03 38 78 32 08 be 41 04 00 01 13 03 38 78 ...8x2..A.....8x
|
||||
| 3248: 31 09 b6 62 04 00 01 15 03 37 68 31 30 08 b6 61 1..b.....7h10..a
|
||||
| 3264: 04 00 01 13 03 37 79 39 08 b6 60 04 00 01 12 f3 .....7y9..`.....
|
||||
| 3280: 37 78 38 08 b6 5e 04 00 01 13 03 37 78 37 08 b6 7x8..^.....7x7..
|
||||
| 3296: 5e 04 00 01 13 03 37 78 36 08 b6 5d 04 00 01 13 ^.....7x6..]....
|
||||
| 3312: 03 37 78 35 08 b6 5c 04 00 00 13 03 37 78 34 08 .7x5........7x4.
|
||||
| 3328: b6 5b 04 00 01 13 03 37 78 33 08 b6 5a 04 00 01 .[.....7x3..Z...
|
||||
| 3344: 13 03 37 78 32 08 b6 59 04 00 01 13 03 37 78 31 ..7x2..Y.....7x1
|
||||
| 3360: 09 ae 7a 04 00 01 15 03 36 78 31 30 08 ae 79 04 ..z.....6x10..y.
|
||||
| 3376: 00 01 e2 03 36 78 39 08 ae 78 04 00 01 13 03 36 ....6x9..x.....6
|
||||
| 3392: 78 38 08 ae 77 04 00 01 13 03 36 78 37 08 ae 76 x8..w.....6x7..v
|
||||
| 3408: 04 00 01 13 03 36 78 36 08 ae 85 04 00 01 13 03 .....6x6........
|
||||
| 3424: 36 78 35 08 ae 73 f4 00 01 13 03 36 78 34 08 ae 6x5..s.....6x4..
|
||||
| 3440: 73 04 00 01 13 03 36 78 33 08 ae 72 04 00 01 13 s.....6x3..r....
|
||||
| 3456: 03 36 78 32 08 87 6a 04 00 01 13 02 3d e8 32 08 .6x2..j.....=.2.
|
||||
| 3472: 8f 52 04 00 01 13 02 32 78 32 08 97 3b 04 00 01 .R.....2x2..;...
|
||||
| 3488: 13 02 33 78 32 08 9f 22 04 00 01 13 02 34 78 32 ..3x2........4x2
|
||||
| 3504: 08 a7 0a 04 00 01 13 02 35 78 32 08 87 69 04 00 ........5x2..i..
|
||||
| 3520: 01 13 02 31 78 31 08 87 6c 04 00 01 13 02 31 78 ...1x1..l.....1x
|
||||
| 3536: 34 08 8f 54 04 00 01 13 02 32 78 34 08 97 3c 04 4..T.....2x4..<.
|
||||
| 3552: 00 01 12 f2 33 78 34 08 9f 24 04 00 01 13 02 34 ....3x4..$.....4
|
||||
| 3568: 78 34 08 a7 0c 04 00 01 13 02 35 78 34 0e 6c 00 x4........5x4.l.
|
||||
| 3584: 08 ae 71 04 00 01 13 03 36 78 31 09 a7 12 04 00 ..q.....6x1.....
|
||||
| 3600: 01 15 02 35 78 31 30 08 a7 11 04 00 01 13 02 35 ...5x10........5
|
||||
| 3616: 78 39 08 a7 10 04 00 01 13 02 35 78 38 08 a7 0f x9........5x8...
|
||||
| 3632: 04 00 01 14 02 35 78 37 08 a7 0e 04 00 01 13 02 .....5x7........
|
||||
| 3648: 35 78 36 08 a7 0d 04 00 01 13 02 35 78 35 0e 0e 5x6........5x5..
|
||||
| 3664: b3 00 08 00 01 00 03 08 a7 0b 04 00 01 13 02 35 ...............5
|
||||
| 3680: 78 33 0e d1 00 08 a7 09 04 00 01 13 02 35 78 31 x3...........5x1
|
||||
| 3696: 09 9f 2a 04 00 01 15 02 34 78 31 30 03 cf 29 04 ..*.....4x10..).
|
||||
| 3712: 00 01 13 02 34 78 39 08 9f 28 04 00 01 13 02 34 ....4x9..(.....4
|
||||
| 3728: 78 38 09 9f 27 04 00 01 13 02 34 78 37 08 9f 26 x8..'.....4x7..&
|
||||
| 3744: 04 00 01 13 0e a4 78 36 08 9f 25 04 00 01 13 02 ......x6..%.....
|
||||
| 3760: 34 78 35 0f 18 00 09 00 09 13 34 78 08 9f 23 04 4x5.......4x..#.
|
||||
| 3776: 00 01 13 02 34 78 33 0f 36 00 08 9f 21 04 00 01 ....4x3.6...!...
|
||||
| 3792: 13 02 34 78 31 09 97 42 04 00 01 15 02 33 78 31 ..4x1..B.....3x1
|
||||
| 3808: 30 08 97 41 04 00 01 13 02 33 78 39 08 97 40 04 0..A.....3x9..@.
|
||||
| 3824: 00 01 13 02 33 78 38 18 97 3f 04 00 01 13 02 33 ....3x8..?.....3
|
||||
| 3840: 78 37 08 97 3e 04 00 01 13 02 33 78 36 08 97 3d x7..>.....3x6..=
|
||||
| 3856: 04 00 01 13 02 33 78 35 1f 7d 00 09 00 09 13 33 .....3x5.......3
|
||||
| 3872: 78 07 97 3b 04 00 01 13 02 33 78 33 0f 9b 00 08 x..;.....3x3....
|
||||
| 3888: 97 39 04 00 01 13 02 33 78 31 09 8f 5a 04 00 01 .9.....3x1..Z...
|
||||
| 3904: 15 02 32 79 31 30 08 8f 59 04 00 01 13 fa 32 78 ..2y10..Y.....2x
|
||||
| 3920: 39 08 8f 58 04 00 01 13 02 32 78 38 08 8f 57 04 9..X.....2x8..W.
|
||||
| 3936: 00 01 13 02 32 78 37 08 8f 56 04 00 01 13 02 32 ....2x7..V.....2
|
||||
| 3952: 78 36 08 8f 55 04 00 01 13 02 32 78 35 0f e2 00 x6..U.....2x5...
|
||||
| 3968: 09 00 09 13 32 78 08 8f 53 04 00 01 13 02 32 78 ....2x..S.....2x
|
||||
| 3984: 33 00 00 00 08 8f 51 04 00 01 13 02 aa 78 31 09 3.....Q......x1.
|
||||
| 4000: 87 72 04 00 01 15 02 31 78 31 30 08 87 71 04 00 .r.....1x10..q..
|
||||
| 4016: 01 13 03 31 78 39 08 87 70 04 00 01 13 02 31 78 ...1x9..p.....1x
|
||||
| 4032: 38 08 87 6f 04 00 01 13 02 31 78 37 08 87 6e 04 8..o.....1x7..n.
|
||||
| 4048: 00 01 13 02 31 78 36 08 87 6d 04 00 01 13 02 31 ....1x6..m.....1
|
||||
| 4064: 7d 25 0f f9 00 08 ff f9 13 31 78 08 87 6b 04 00 .%.......1x..k..
|
||||
| 4080: 01 13 02 31 78 33 00 00 00 00 00 08 00 01 00 03 ...1x3..........
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 03 01 87 00 0b 2d 06 5a 01 87 00 00 .........-.Z....
|
||||
| 384: 00 00 00 00 00 00 00 89 50 01 54 00 93 24 00 00 ........P.T..$..
|
||||
| 400: 00 32 00 00 00 00 00 00 23 2f 00 00 00 09 00 00 .2......#/......
|
||||
| 416: 00 0b 00 00 00 07 00 00 00 09 00 00 00 00 00 00 ................
|
||||
| 432: 23 2e 00 00 10 09 00 00 00 0b 00 00 00 06 00 00 #...............
|
||||
| 448: 00 08 00 00 00 00 00 00 23 2d 00 00 00 09 00 00 ........#-......
|
||||
| 464: 00 0b 00 00 00 05 00 00 00 07 00 00 00 00 00 00 ................
|
||||
| 480: 23 2c 00 00 00 09 00 00 00 0b 00 00 00 04 00 00 #,..............
|
||||
| 496: 00 06 00 00 00 00 00 00 23 2b 00 00 00 09 00 00 ........#+......
|
||||
| 512: 00 0b 00 00 00 03 00 00 00 05 00 00 00 00 00 00 ................
|
||||
| 528: 23 2a 00 00 00 09 00 00 00 0b 00 00 00 02 00 00 #*..............
|
||||
| 544: 00 04 00 00 00 00 00 00 23 29 00 00 00 09 00 00 ........#)......
|
||||
| 560: 00 0b 00 00 00 01 00 00 00 03 00 00 00 00 00 00 ................
|
||||
| 576: 1f 4a 00 00 00 08 00 00 00 0a 00 00 00 0a 00 00 .J..............
|
||||
| 592: 00 0c 00 00 00 00 00 00 0f 49 00 00 00 08 00 00 .........I......
|
||||
| 608: 00 0a 00 00 00 09 00 00 00 0b 00 00 00 00 00 00 ................
|
||||
| 624: 1f 48 00 00 00 08 00 00 00 0a 00 00 00 08 00 06 .H..............
|
||||
| 640: 00 0a 00 00 00 00 00 00 1f 47 00 00 00 08 00 00 .........G......
|
||||
| 656: 00 0a 00 00 00 07 00 00 00 09 00 00 00 00 00 00 ................
|
||||
| 672: 15 d6 00 00 00 08 00 00 00 0a 00 00 00 06 00 00 ................
|
||||
| 688: 00 08 00 00 00 00 00 00 1f 45 00 00 00 08 00 00 .........E......
|
||||
| 704: 00 0a 00 00 00 05 00 00 00 07 00 00 00 00 00 00 ................
|
||||
| 720: 1f 44 00 00 00 08 00 00 00 0a 00 00 00 04 00 00 .D..............
|
||||
| 736: 00 06 00 00 00 00 00 00 1f 43 00 00 00 07 ff ff .........C......
|
||||
| 752: f0 0a 00 00 00 03 00 00 00 05 00 00 00 00 00 00 ................
|
||||
| 768: 1f 42 00 00 00 08 00 00 00 0a 00 00 00 01 ff f0 .B..............
|
||||
| 784: 00 03 ff ff ff ff ff ff 1f 41 00 00 00 08 00 00 .........A......
|
||||
| 800: 00 0a 00 00 00 01 00 00 00 03 00 00 00 00 00 00 ................
|
||||
| 816: 1b 62 00 00 00 07 00 00 00 09 00 00 00 0a 00 00 .b..............
|
||||
| 832: 00 0c 05 00 00 00 00 00 1b 64 10 00 00 07 00 00 .........d......
|
||||
| 848: 00 09 00 00 00 09 00 00 00 0b 00 00 00 00 00 00 ................
|
||||
| 864: 1b 60 00 00 00 07 00 00 00 09 00 00 00 08 00 00 .`..............
|
||||
| 880: 00 0a 00 00 00 00 00 00 1b 5f 00 00 00 07 00 00 ........._......
|
||||
| 896: 00 09 00 00 00 07 00 00 00 09 00 00 00 00 00 00 ................
|
||||
| 912: 1b 5e 00 00 00 07 00 00 00 09 00 00 00 06 00 00 .^..............
|
||||
| 928: 00 08 00 00 00 00 00 00 1b 5d 00 00 00 08 00 00 .........]......
|
||||
| 944: 00 09 00 00 00 05 00 00 00 07 00 00 00 00 00 00 ................
|
||||
| 960: 1b 5c 00 00 00 07 00 00 00 09 00 00 00 04 00 00 ................
|
||||
| 976: 06 46 00 00 00 00 00 00 1b 5b 00 00 00 07 00 00 .F.......[......
|
||||
| 992: 00 09 00 00 00 03 00 00 00 04 ff f0 00 00 00 00 ................
|
||||
| 1008: 1b 5a 00 00 00 07 00 00 00 19 00 00 00 02 00 00 .Z..............
|
||||
| 1024: 00 04 00 00 00 00 00 00 1b 59 00 00 00 07 00 00 .........Y......
|
||||
| 1040: 00 09 00 00 00 01 00 00 00 03 00 00 00 00 ff f0 ................
|
||||
| 1056: 17 7a 00 00 00 06 00 00 00 08 00 00 00 0a 00 00 .z..............
|
||||
| 1072: 00 0c 00 00 00 00 00 00 17 79 00 00 00 06 00 00 .........y......
|
||||
| 1088: 00 08 00 00 00 09 00 00 00 0b 00 00 00 00 00 00 ................
|
||||
| 1104: 17 78 00 00 00 06 00 00 00 08 00 00 00 08 00 00 .x..............
|
||||
| 1120: 00 0a 00 00 00 00 00 00 17 77 00 00 00 06 10 00 .........w......
|
||||
| 1136: 00 08 00 00 00 07 00 09 c0 09 00 00 00 00 00 00 ................
|
||||
| 1152: 17 76 00 00 00 06 00 00 00 08 00 00 00 06 00 00 .v..............
|
||||
| 1168: 00 08 00 00 00 00 00 00 17 75 00 00 00 06 00 00 .........u......
|
||||
| 1184: 00 08 00 00 00 05 00 00 00 07 00 00 00 00 00 00 ................
|
||||
| 1200: 17 74 00 00 00 06 00 00 00 08 00 00 00 03 ff ff .t..............
|
||||
| 1216: f0 06 00 00 00 83 00 00 17 73 00 00 00 06 00 00 .........s......
|
||||
| 1232: 00 08 00 00 00 03 00 00 00 05 00 00 00 00 00 00 ................
|
||||
| 1248: 17 71 ff 00 00 06 00 00 10 08 00 00 00 02 00 00 .q..............
|
||||
| 1264: 00 04 00 00 c0 00 00 00 17 0d 00 00 00 06 00 00 ................
|
||||
| 1280: 00 08 00 00 e7 01 00 00 00 03 00 00 09 e0 00 00 ................
|
||||
| 1296: 23 30 00 00 00 09 00 00 00 0a 00 00 00 08 00 00 #0..............
|
||||
| 1312: 00 0a 00 00 00 00 bb 00 23 31 00 00 00 09 00 00 ........#1......
|
||||
| 1328: 00 0b 00 00 00 09 00 00 00 0b 00 00 00 00 00 00 ................
|
||||
| 1344: 23 32 00 00 00 09 00 00 00 0b 00 00 00 0a 00 00 #2..............
|
||||
| 1360: 00 0c 00 00 00 00 00 00 27 11 00 00 00 0a 00 00 ........'.......
|
||||
| 1376: 00 0c 00 00 00 01 00 08 c0 03 00 00 00 00 00 00 ................
|
||||
| 1392: 27 12 00 00 00 0a 00 00 00 0c 51 00 00 02 00 00 '.........Q.....
|
||||
| 1408: 00 04 6f 00 00 00 00 00 27 13 00 00 00 09 ff ff ..o.....'.......
|
||||
| 1424: 00 0c 00 00 00 03 00 00 00 05 00 00 00 00 00 00 ................
|
||||
| 1440: 27 14 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 '...............
|
||||
| 1616: 00 00 00 00 00 00 00 00 00 00 89 50 02 04 00 93 ...........P....
|
||||
| 1632: 24 00 00 00 32 00 00 00 00 00 00 23 8c 00 00 00 $...2......#....
|
||||
| 1648: 05 00 00 00 07 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 1664: 00 00 00 0f a4 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 1680: 04 00 00 00 06 00 00 00 00 00 00 0b bc 00 00 00 ................
|
||||
| 1696: 03 00 00 00 05 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 1712: 00 00 00 07 d4 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 1728: 04 00 00 00 06 00 00 00 10 00 00 03 ec 00 00 00 ................
|
||||
| 1744: 01 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 1760: 00 00 00 13 8d 00 00 00 05 00 00 00 07 00 00 00 ................
|
||||
| 1776: 05 00 00 00 07 00 00 00 00 00 00 0f a5 00 00 00 ................
|
||||
| 1792: 04 00 00 00 06 00 00 00 05 00 00 00 07 00 00 00 ................
|
||||
| 1808: 00 00 00 0b bd 00 00 00 03 00 00 00 05 00 00 00 ................
|
||||
| 1824: 05 00 00 00 07 00 00 00 00 00 00 07 d5 00 00 00 ................
|
||||
| 1840: 02 00 00 00 05 00 00 00 05 00 00 00 07 00 00 00 ................
|
||||
| 1856: 00 00 00 03 ed 00 00 00 01 00 00 00 03 00 00 00 ................
|
||||
| 1872: 05 00 00 00 07 00 00 00 00 00 00 13 8e 00 00 00 ................
|
||||
| 1888: 05 00 00 00 07 00 00 00 06 00 00 00 08 00 00 00 ................
|
||||
| 1904: 00 00 00 0f a6 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 1920: 06 00 00 00 07 ff ff 00 00 00 00 0b be 00 00 00 ................
|
||||
| 1936: 0b 40 00 00 05 00 00 00 06 00 00 00 08 00 00 00 .@..............
|
||||
| 1952: 00 00 00 07 d6 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 1968: 05 00 00 00 08 00 00 00 00 00 00 03 ee 00 00 00 ................
|
||||
| 1984: 01 00 00 00 02 ff ff 00 06 00 00 00 08 00 00 00 ................
|
||||
| 2000: 00 00 00 13 8f 00 00 00 05 00 00 00 07 00 00 00 ................
|
||||
| 2016: 07 00 00 00 09 00 00 00 00 00 00 0f a7 00 00 00 ................
|
||||
| 2032: 04 00 00 00 06 00 00 00 07 00 00 00 09 00 00 08 ................
|
||||
| 2048: 30 00 00 0b bf 00 00 00 03 00 00 00 05 00 00 00 0...............
|
||||
| 2064: 07 00 00 00 09 00 00 00 00 00 00 07 d7 00 00 00 ................
|
||||
| 2080: 02 00 00 00 04 00 00 00 07 00 00 00 09 00 00 00 ................
|
||||
| 2096: 00 00 00 03 ef 00 00 00 01 00 00 00 03 00 00 00 ................
|
||||
| 2112: 07 00 00 00 09 00 00 00 00 00 00 13 90 00 00 00 ................
|
||||
| 2128: 05 00 01 00 07 00 00 00 08 00 00 00 0a 00 00 00 ................
|
||||
| 2144: 00 00 00 0f a8 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 2160: 08 00 00 00 0a 00 00 00 00 00 00 0b f2 00 00 00 ................
|
||||
| 2176: 03 00 00 00 05 00 00 00 08 00 00 00 0a 00 00 01 ................
|
||||
| 2192: 00 00 00 07 d8 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 2208: 08 00 00 00 0a 00 00 00 00 00 00 03 f0 00 00 00 ................
|
||||
| 2224: 01 00 00 00 03 00 00 00 08 00 00 00 09 ff 00 00 ................
|
||||
| 2240: 00 00 00 13 91 00 00 00 05 00 00 00 07 00 00 00 ................
|
||||
| 2256: 09 00 00 00 0b 00 00 00 00 00 00 0f a9 00 00 00 ................
|
||||
| 2272: 04 00 00 00 06 00 00 00 09 00 00 00 0b 00 00 00 ................
|
||||
| 2288: 00 00 00 0b c1 00 00 00 03 00 00 00 05 00 00 00 ................
|
||||
| 2304: 09 00 00 00 0b 00 00 00 00 00 00 07 d9 00 00 00 ................
|
||||
| 2320: 02 00 00 00 04 00 00 00 09 00 00 00 0b 00 00 01 ................
|
||||
| 2336: 00 00 00 03 f0 ff ff 00 01 00 00 00 03 00 00 00 ................
|
||||
| 2352: 09 00 00 00 0b 00 00 00 00 00 00 13 92 00 00 00 ................
|
||||
| 2368: 05 00 00 00 07 00 00 00 0a 00 00 00 0c 00 00 00 ................
|
||||
| 2384: 00 00 00 0f aa 00 00 00 04 00 00 00 06 00 00 00 ................
|
||||
| 2400: 0a 00 00 00 0c 00 00 00 00 00 00 0b c2 00 00 00 ................
|
||||
| 2416: 03 00 00 00 05 00 00 00 0a 00 00 00 0c 00 00 00 ................
|
||||
| 2432: 00 00 00 07 da 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 2448: 0a 00 00 00 0c 00 00 00 00 00 00 03 f2 00 00 00 ................
|
||||
| 2464: 01 00 00 10 03 00 00 00 0a 00 00 00 0c 00 00 00 ................
|
||||
| 2480: 00 00 00 03 eb 00 00 00 01 00 00 00 03 00 00 00 ................
|
||||
| 2496: 03 00 00 00 05 00 00 00 00 00 00 07 d3 00 00 00 ................
|
||||
| 2512: 02 00 00 00 04 00 00 00 03 00 00 00 05 00 00 00 ................
|
||||
| 2528: 00 00 00 0b bb 00 00 00 03 00 00 00 05 00 00 00 ................
|
||||
| 2544: 03 00 00 00 05 00 00 00 00 00 00 0f a3 00 00 00 ................
|
||||
| 2560: 04 00 00 00 06 00 00 00 03 00 00 00 05 00 00 00 ................
|
||||
| 2576: 00 00 00 13 8b 00 00 00 05 00 00 00 07 00 00 00 ................
|
||||
| 2592: 03 00 00 00 05 00 00 00 00 00 00 03 ea 00 00 00 ................
|
||||
| 2608: 01 00 00 00 03 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 2624: 00 00 00 07 d2 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 2640: 02 00 00 00 04 00 00 00 00 00 00 0b ba 00 00 00 ................
|
||||
| 2656: 03 00 00 00 05 00 00 00 02 00 00 00 04 00 00 00 ................
|
||||
| 2672: 00 00 00 0f a1 ff ff ff 04 00 00 00 06 00 00 00 ................
|
||||
| 2688: 02 00 00 00 04 00 00 00 00 00 00 13 8a 00 00 00 ................
|
||||
| 2704: 05 00 00 00 06 ff ff ff f2 00 00 00 04 00 00 00 ................
|
||||
| 2720: 00 00 00 03 e9 00 00 00 01 00 00 00 03 00 00 00 ................
|
||||
| 2736: 01 00 00 00 03 00 00 00 00 00 00 07 d1 00 00 00 ................
|
||||
| 2848: 00 00 00 00 00 00 00 00 00 00 00 00 00 89 50 01 ..............P.
|
||||
| 2864: 04 00 93 24 00 01 00 02 00 00 00 00 00 00 00 02 ...$............
|
||||
| 2880: ff ff ff 06 00 00 00 0c 00 00 00 01 00 00 00 0b ................
|
||||
| 2896: 00 00 00 00 00 00 00 02 40 00 00 00 00 00 00 00 ........@.......
|
||||
| end crash-2e81f5dce5cbd4.db}]
|
||||
execsql { PRAGMA writable_schema = 1;}
|
||||
catchsql {UPDATE t1 SET ex= ex ISNULL}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -1624,7 +1624,9 @@ int sqlite3session_diff(
|
||||
}
|
||||
sqlite3_free((char*)azCol);
|
||||
if( bMismatch ){
|
||||
*pzErrMsg = sqlite3_mprintf("table schemas do not match");
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("table schemas do not match");
|
||||
}
|
||||
rc = SQLITE_SCHEMA;
|
||||
}
|
||||
if( bHasPk==0 ){
|
||||
@ -1830,12 +1832,12 @@ int sqlite3session_attach(
|
||||
** set *pRc to SQLITE_NOMEM and return non-zero.
|
||||
*/
|
||||
static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){
|
||||
if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
|
||||
if( *pRc==SQLITE_OK && (size_t)(p->nAlloc-p->nBuf)<nByte ){
|
||||
u8 *aNew;
|
||||
i64 nNew = p->nAlloc ? p->nAlloc : 128;
|
||||
do {
|
||||
nNew = nNew*2;
|
||||
}while( (nNew-p->nBuf)<nByte );
|
||||
}while( (size_t)(nNew-p->nBuf)<nByte );
|
||||
|
||||
aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
|
||||
if( 0==aNew ){
|
||||
|
2
main.mk
2
main.mk
@ -527,7 +527,6 @@ SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
SHELL_OPT += -DSQLITE_INTROSPECTION_PRAGMAS
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
|
||||
FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
|
||||
@ -738,6 +737,7 @@ SHELL_SRC = \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/dbdata.c \
|
||||
$(TOP)/src/test_windirent.c
|
||||
|
||||
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
|
||||
|
@ -1 +1 @@
|
||||
884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50
|
||||
18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b
|
||||
|
41
src/alter.c
41
src/alter.c
@ -54,7 +54,7 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT 1 "
|
||||
"FROM \"%w\".%s "
|
||||
"WHERE name NOT LIKE 'sqlite_%%'"
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
" AND sql NOT LIKE 'create virtual%%'"
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
|
||||
zDb, MASTER_NAME,
|
||||
@ -65,7 +65,7 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT 1 "
|
||||
"FROM temp.%s "
|
||||
"WHERE name NOT LIKE 'sqlite_%%'"
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
" AND sql NOT LIKE 'create virtual%%'"
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
|
||||
MASTER_NAME, zDb
|
||||
@ -136,8 +136,8 @@ void sqlite3AlterRenameTable(
|
||||
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
|
||||
goto exit_rename_table;
|
||||
}
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto
|
||||
exit_rename_table;
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse,zName,"table",zName) ){
|
||||
goto exit_rename_table;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
@ -186,7 +186,7 @@ void sqlite3AlterRenameTable(
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
|
||||
"WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
|
||||
"AND name NOT LIKE 'sqlite_%%'"
|
||||
"AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
, zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
|
||||
);
|
||||
|
||||
@ -197,7 +197,8 @@ void sqlite3AlterRenameTable(
|
||||
"tbl_name = %Q, "
|
||||
"name = CASE "
|
||||
"WHEN type='table' THEN %Q "
|
||||
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
|
||||
"WHEN name LIKE 'sqliteX_autoindex%%' ESCAPE 'X' "
|
||||
" AND type='index' THEN "
|
||||
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
|
||||
"ELSE name END "
|
||||
"WHERE tbl_name=%Q COLLATE nocase AND "
|
||||
@ -434,6 +435,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
goto exit_begin_add_column;
|
||||
}
|
||||
|
||||
sqlite3MayAbort(pParse);
|
||||
assert( pTab->addColOffset>0 );
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
||||
@ -571,7 +573,8 @@ void sqlite3AlterRenameColumn(
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
|
||||
"WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)"
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
|
||||
" AND (type != 'index' OR tbl_name = %Q)"
|
||||
" AND sql NOT LIKE 'create virtual%%'",
|
||||
zDb, MASTER_NAME,
|
||||
zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
|
||||
@ -725,6 +728,29 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Walker callback used by sqlite3RenameExprUnmap().
|
||||
*/
|
||||
static int renameUnmapSelectCb(Walker *pWalker, Select *p){
|
||||
Parse *pParse = pWalker->pParse;
|
||||
int i;
|
||||
if( ALWAYS(p->pEList) ){
|
||||
ExprList *pList = p->pEList;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( pList->a[i].zName ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */
|
||||
SrcList *pSrc = p->pSrc;
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
|
||||
}
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove all nodes that are part of expression pExpr from the rename list.
|
||||
*/
|
||||
@ -733,6 +759,7 @@ void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
sWalker.pParse = pParse;
|
||||
sWalker.xExprCallback = renameUnmapExprCb;
|
||||
sWalker.xSelectCallback = renameUnmapSelectCb;
|
||||
sqlite3WalkExpr(&sWalker, pExpr);
|
||||
}
|
||||
|
||||
|
213
src/analyze.c
213
src/analyze.c
@ -27,13 +27,13 @@
|
||||
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
|
||||
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
|
||||
** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
|
||||
** created and used by SQLite versions 3.7.9 and later and with
|
||||
** created and used by SQLite versions 3.7.9 through 3.29.0 when
|
||||
** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
|
||||
** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
|
||||
** version of sqlite_stat3 and is only available when compiled with
|
||||
** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
|
||||
** not possible to enable both STAT3 and STAT4 at the same time. If they
|
||||
** are both enabled, then STAT4 takes precedence.
|
||||
** is a superset of sqlite_stat2 and is also now deprecated. The
|
||||
** sqlite_stat4 is an enhanced version of sqlite_stat3 and is only
|
||||
** available when compiled with SQLITE_ENABLE_STAT4 and in SQLite
|
||||
** versions 3.8.1 and later. STAT4 is the only variant that is still
|
||||
** supported.
|
||||
**
|
||||
** For most applications, sqlite_stat1 provides all the statistics required
|
||||
** for the query planner to make good choices.
|
||||
@ -144,17 +144,11 @@
|
||||
|
||||
#if defined(SQLITE_ENABLE_STAT4)
|
||||
# define IsStat4 1
|
||||
# define IsStat3 0
|
||||
#elif defined(SQLITE_ENABLE_STAT3)
|
||||
# define IsStat4 0
|
||||
# define IsStat3 1
|
||||
#else
|
||||
# define IsStat4 0
|
||||
# define IsStat3 0
|
||||
# undef SQLITE_STAT4_SAMPLES
|
||||
# define SQLITE_STAT4_SAMPLES 1
|
||||
#endif
|
||||
#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
|
||||
|
||||
/*
|
||||
** This routine generates code that opens the sqlite_statN tables.
|
||||
@ -183,14 +177,10 @@ static void openStatTable(
|
||||
{ "sqlite_stat1", "tbl,idx,stat" },
|
||||
#if defined(SQLITE_ENABLE_STAT4)
|
||||
{ "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
|
||||
{ "sqlite_stat3", 0 },
|
||||
#elif defined(SQLITE_ENABLE_STAT3)
|
||||
{ "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
|
||||
{ "sqlite_stat4", 0 },
|
||||
#else
|
||||
{ "sqlite_stat3", 0 },
|
||||
{ "sqlite_stat4", 0 },
|
||||
#endif
|
||||
{ "sqlite_stat3", 0 },
|
||||
};
|
||||
int i;
|
||||
sqlite3 *db = pParse->db;
|
||||
@ -271,7 +261,7 @@ typedef struct Stat4Sample Stat4Sample;
|
||||
struct Stat4Sample {
|
||||
tRowcnt *anEq; /* sqlite_stat4.nEq */
|
||||
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
tRowcnt *anLt; /* sqlite_stat4.nLt */
|
||||
union {
|
||||
i64 iRowid; /* Rowid in main table of the key */
|
||||
@ -302,7 +292,7 @@ struct Stat4Accum {
|
||||
|
||||
/* Reclaim memory used by a Stat4Sample
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleClear(sqlite3 *db, Stat4Sample *p){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ){
|
||||
@ -314,7 +304,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
|
||||
|
||||
/* Initialize the BLOB value of a ROWID
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
||||
@ -330,7 +320,7 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
|
||||
|
||||
/* Initialize the INTEGER value of a ROWID.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
||||
@ -343,7 +333,7 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
|
||||
/*
|
||||
** Copy the contents of object (*pFrom) into (*pTo).
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
|
||||
pTo->isPSample = pFrom->isPSample;
|
||||
pTo->iCol = pFrom->iCol;
|
||||
@ -364,7 +354,7 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
|
||||
*/
|
||||
static void stat4Destructor(void *pOld){
|
||||
Stat4Accum *p = (Stat4Accum*)pOld;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
|
||||
for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
|
||||
@ -384,7 +374,7 @@ static void stat4Destructor(void *pOld){
|
||||
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
|
||||
** total number of columns in the table.
|
||||
**
|
||||
** Note 2: C is only used for STAT3 and STAT4.
|
||||
** Note 2: C is only used for STAT4.
|
||||
**
|
||||
** For indexes on ordinary rowid tables, N==K+1. But for indexes on
|
||||
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
|
||||
@ -407,7 +397,7 @@ static void statInit(
|
||||
int nColUp; /* nCol rounded up for alignment */
|
||||
int n; /* Bytes of space to allocate */
|
||||
sqlite3 *db; /* Database connection */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
int mxSample = SQLITE_STAT4_SAMPLES;
|
||||
#endif
|
||||
|
||||
@ -424,7 +414,7 @@ static void statInit(
|
||||
n = sizeof(*p)
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
|
||||
+ sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
|
||||
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
|
||||
@ -444,7 +434,7 @@ static void statInit(
|
||||
p->current.anDLt = (tRowcnt*)&p[1];
|
||||
p->current.anEq = &p->current.anDLt[nColUp];
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
{
|
||||
u8 *pSpace; /* Allocated space not yet assigned */
|
||||
int i; /* Used to iterate through p->aSample[] */
|
||||
@ -479,7 +469,7 @@ static void statInit(
|
||||
sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
|
||||
}
|
||||
static const FuncDef statInitFuncdef = {
|
||||
2+IsStat34, /* nArg */
|
||||
2+IsStat4, /* nArg */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
@ -519,7 +509,7 @@ static int sampleIsBetterPost(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/*
|
||||
** Return true if pNew is to be preferred over pOld.
|
||||
**
|
||||
@ -538,15 +528,11 @@ static int sampleIsBetter(
|
||||
assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
|
||||
|
||||
if( (nEqNew>nEqOld) ) return 1;
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( nEqNew==nEqOld ){
|
||||
if( pNew->iCol<pOld->iCol ) return 1;
|
||||
return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -559,7 +545,6 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
|
||||
assert( IsStat4 || nEqZero==0 );
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0
|
||||
** values in the anEq[] array of any sample in Stat4Accum.a[]. In
|
||||
** other words, if nMaxEqZero is n, then it is guaranteed that there
|
||||
@ -593,7 +578,6 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
goto find_new_min;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If necessary, remove sample iMin to make room for the new sample. */
|
||||
if( p->nSample>=p->mxSample ){
|
||||
@ -614,10 +598,8 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
/* The "rows less-than" for the rowid column must be greater than that
|
||||
** for the last sample in the p->a[] array. Otherwise, the samples would
|
||||
** be out of order. */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
assert( p->nSample==0
|
||||
|| pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
|
||||
#endif
|
||||
|
||||
/* Insert the new sample */
|
||||
pSample = &p->a[p->nSample];
|
||||
@ -627,9 +609,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
/* Zero the first nEqZero entries in the anEq[] array. */
|
||||
memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
find_new_min:
|
||||
#endif
|
||||
find_new_min:
|
||||
if( p->nSample>=p->mxSample ){
|
||||
int iMin = -1;
|
||||
for(i=0; i<p->mxSample; i++){
|
||||
@ -642,7 +622,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
p->iMin = iMin;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/*
|
||||
** Field iChng of the index being scanned has changed. So at this point
|
||||
@ -683,28 +663,7 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
|
||||
if( iChng==0 ){
|
||||
tRowcnt nLt = p->current.anLt[0];
|
||||
tRowcnt nEq = p->current.anEq[0];
|
||||
|
||||
/* Check if this is to be a periodic sample. If so, add it. */
|
||||
if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
|
||||
p->current.isPSample = 1;
|
||||
sampleInsert(p, &p->current, 0);
|
||||
p->current.isPSample = 0;
|
||||
}else
|
||||
|
||||
/* Or if it is a non-periodic sample. Add it in this case too. */
|
||||
if( p->nSample<p->mxSample
|
||||
|| sampleIsBetter(p, &p->current, &p->a[p->iMin])
|
||||
){
|
||||
sampleInsert(p, &p->current, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifndef SQLITE_ENABLE_STAT4
|
||||
UNUSED_PARAMETER( p );
|
||||
UNUSED_PARAMETER( iChng );
|
||||
#endif
|
||||
@ -724,7 +683,7 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
|
||||
** index being analyzed. The stat_get() SQL function will later be used to
|
||||
** extract relevant information for constructing the sqlite_statN tables.
|
||||
**
|
||||
** The R parameter is only used for STAT3 and STAT4
|
||||
** The R parameter is only used for STAT4
|
||||
*/
|
||||
static void statPush(
|
||||
sqlite3_context *context,
|
||||
@ -756,14 +715,14 @@ static void statPush(
|
||||
}
|
||||
for(i=iChng; i<p->nCol; i++){
|
||||
p->current.anDLt[i]++;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
p->current.anLt[i] += p->current.anEq[i];
|
||||
#endif
|
||||
p->current.anEq[i] = 1;
|
||||
}
|
||||
}
|
||||
p->nRow++;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
|
||||
sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
|
||||
}else{
|
||||
@ -796,7 +755,7 @@ static void statPush(
|
||||
#endif
|
||||
}
|
||||
static const FuncDef statPushFuncdef = {
|
||||
2+IsStat34, /* nArg */
|
||||
2+IsStat4, /* nArg */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
@ -827,7 +786,7 @@ static const FuncDef statPushFuncdef = {
|
||||
** parameter will always be a poiner to a Stat4Accum object, never a
|
||||
** NULL.
|
||||
**
|
||||
** If neither STAT3 nor STAT4 are enabled, then J is always
|
||||
** If STAT4 is not enabled, then J is always
|
||||
** STAT_GET_STAT1 and is hence omitted and this routine becomes
|
||||
** a one-parameter function, stat_get(P), that always returns the
|
||||
** stat1 table entry information.
|
||||
@ -838,8 +797,8 @@ static void statGet(
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
/* STAT3 and STAT4 have a parameter on this routine. */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/* STAT4 has a parameter on this routine. */
|
||||
int eCall = sqlite3_value_int(argv[1]);
|
||||
assert( argc==2 );
|
||||
assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
|
||||
@ -894,7 +853,7 @@ static void statGet(
|
||||
|
||||
sqlite3_result_text(context, zRet, -1, sqlite3_free);
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
else if( eCall==STAT_GET_ROWID ){
|
||||
if( p->iGet<0 ){
|
||||
samplePushPrevious(p, 0);
|
||||
@ -923,9 +882,7 @@ static void statGet(
|
||||
}
|
||||
}
|
||||
|
||||
if( IsStat3 ){
|
||||
sqlite3_result_int64(context, (i64)aCnt[0]);
|
||||
}else{
|
||||
{
|
||||
char *zRet = sqlite3MallocZero(p->nCol * 25);
|
||||
if( zRet==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
@ -942,13 +899,13 @@ static void statGet(
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
#ifndef SQLITE_DEBUG
|
||||
UNUSED_PARAMETER( argc );
|
||||
#endif
|
||||
}
|
||||
static const FuncDef statGetFuncdef = {
|
||||
1+IsStat34, /* nArg */
|
||||
1+IsStat4, /* nArg */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
@ -961,7 +918,7 @@ static const FuncDef statGetFuncdef = {
|
||||
|
||||
static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
|
||||
assert( regOut!=regStat4 && regOut!=regStat4+1 );
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
|
||||
#elif SQLITE_DEBUG
|
||||
assert( iParam==STAT_GET_STAT1 );
|
||||
@ -970,7 +927,7 @@ static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
|
||||
#endif
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
|
||||
(char*)&statGetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 1 + IsStat34);
|
||||
sqlite3VdbeChangeP5(v, 1 + IsStat4);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -997,7 +954,7 @@ static void analyzeOneTable(
|
||||
int regNewRowid = iMem++; /* Rowid for the inserted record */
|
||||
int regStat4 = iMem++; /* Register to hold Stat4Accum object */
|
||||
int regChng = iMem++; /* Index of changed index field */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
|
||||
#endif
|
||||
int regTemp = iMem++; /* Temporary use register */
|
||||
@ -1131,16 +1088,16 @@ static void analyzeOneTable(
|
||||
** (3) the number of rows in the index,
|
||||
**
|
||||
**
|
||||
** The third argument is only used for STAT3 and STAT4
|
||||
** The third argument is only used for STAT4
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
|
||||
(char*)&statInitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat34);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat4);
|
||||
|
||||
/* Implementation of the following:
|
||||
**
|
||||
@ -1211,12 +1168,12 @@ static void analyzeOneTable(
|
||||
|
||||
/*
|
||||
** chng_addr_N:
|
||||
** regRowid = idx(rowid) // STAT34 only
|
||||
** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
|
||||
** regRowid = idx(rowid) // STAT4 only
|
||||
** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
|
||||
** Next csr
|
||||
** if !eof(csr) goto next_row;
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
assert( regRowid==(regStat4+2) );
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
|
||||
@ -1237,7 +1194,7 @@ static void analyzeOneTable(
|
||||
assert( regChng==(regStat4+1) );
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
|
||||
(char*)&statPushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat34);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat4);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
|
||||
|
||||
/* Add the entry to the stat1 table. */
|
||||
@ -1251,8 +1208,8 @@ static void analyzeOneTable(
|
||||
#endif
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
|
||||
/* Add the entries to the stat3 or stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
/* Add the entries to the stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
{
|
||||
int regEq = regStat1;
|
||||
int regLt = regStat1+1;
|
||||
@ -1275,21 +1232,17 @@ static void analyzeOneTable(
|
||||
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
|
||||
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
|
||||
VdbeCoverage(v);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
|
||||
#else
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
|
||||
#endif
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */
|
||||
sqlite3VdbeJumpHere(v, addrIsNull);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/* End of analysis */
|
||||
sqlite3VdbeJumpHere(v, addrRewind);
|
||||
@ -1464,7 +1417,7 @@ static void decodeIntArray(
|
||||
int i;
|
||||
tRowcnt v;
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( z==0 ) z = "";
|
||||
#else
|
||||
assert( z!=0 );
|
||||
@ -1475,7 +1428,7 @@ static void decodeIntArray(
|
||||
v = v*10 + c - '0';
|
||||
z++;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( aOut ) aOut[i] = v;
|
||||
if( aLog ) aLog[i] = sqlite3LogEst(v);
|
||||
#else
|
||||
@ -1486,7 +1439,7 @@ static void decodeIntArray(
|
||||
#endif
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifndef SQLITE_ENABLE_STAT4
|
||||
assert( pIndex!=0 ); {
|
||||
#else
|
||||
if( pIndex ){
|
||||
@ -1497,7 +1450,9 @@ static void decodeIntArray(
|
||||
if( sqlite3_strglob("unordered*", z)==0 ){
|
||||
pIndex->bUnordered = 1;
|
||||
}else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
|
||||
pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
|
||||
int sz = sqlite3Atoi(z+3);
|
||||
if( sz<2 ) sz = 2;
|
||||
pIndex->szIdxRow = sqlite3LogEst(sz);
|
||||
}else if( sqlite3_strglob("noskipscan*", z)==0 ){
|
||||
pIndex->noSkipScan = 1;
|
||||
}
|
||||
@ -1551,7 +1506,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
if( pIndex ){
|
||||
tRowcnt *aiRowEst = 0;
|
||||
int nCol = pIndex->nKeyCol+1;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/* Index.aiRowEst may already be set here if there are duplicate
|
||||
** sqlite_stat1 entries for this index. In that case just clobber
|
||||
** the old data with the new instead of allocating a new array. */
|
||||
@ -1587,7 +1542,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
** and its contents.
|
||||
*/
|
||||
void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( pIdx->aSample ){
|
||||
int j;
|
||||
for(j=0; j<pIdx->nSample; j++){
|
||||
@ -1603,10 +1558,10 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
UNUSED_PARAMETER(pIdx);
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/*
|
||||
** Populate the pIdx->aAvgEq[] array based on the samples currently
|
||||
** stored in pIdx->aSample[].
|
||||
@ -1684,12 +1639,11 @@ static Index *findIndexOrPrimaryKey(
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content from either the sqlite_stat4 or sqlite_stat3 table
|
||||
** Load the content from either the sqlite_stat4
|
||||
** into the relevant Index.aSample[] arrays.
|
||||
**
|
||||
** Arguments zSql1 and zSql2 must point to SQL statements that return
|
||||
** data equivalent to the following (statements are different for stat3,
|
||||
** see the caller of this function for details):
|
||||
** data equivalent to the following:
|
||||
**
|
||||
** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
|
||||
** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
|
||||
@ -1698,7 +1652,6 @@ static Index *findIndexOrPrimaryKey(
|
||||
*/
|
||||
static int loadStatTbl(
|
||||
sqlite3 *db, /* Database handle */
|
||||
int bStat3, /* Assume single column records only */
|
||||
const char *zSql1, /* SQL statement 1 (see above) */
|
||||
const char *zSql2, /* SQL statement 2 (see above) */
|
||||
const char *zDb /* Database name (e.g. "main") */
|
||||
@ -1732,17 +1685,13 @@ static int loadStatTbl(
|
||||
if( zIndex==0 ) continue;
|
||||
nSample = sqlite3_column_int(pStmt, 1);
|
||||
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
||||
assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
|
||||
/* Index.nSample is non-zero at this point if data has already been
|
||||
** loaded from the stat4 table. In this case ignore stat3 data. */
|
||||
if( pIdx==0 || pIdx->nSample ) continue;
|
||||
if( bStat3==0 ){
|
||||
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
|
||||
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
|
||||
nIdxCol = pIdx->nKeyCol;
|
||||
}else{
|
||||
nIdxCol = pIdx->nColumn;
|
||||
}
|
||||
assert( pIdx==0 || pIdx->nSample==0 );
|
||||
if( pIdx==0 ) continue;
|
||||
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
|
||||
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
|
||||
nIdxCol = pIdx->nKeyCol;
|
||||
}else{
|
||||
nIdxCol = pIdx->nColumn;
|
||||
}
|
||||
pIdx->nSampleCol = nIdxCol;
|
||||
nByte = sizeof(IndexSample) * nSample;
|
||||
@ -1784,9 +1733,8 @@ static int loadStatTbl(
|
||||
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
/* This next condition is true if data has already been loaded from
|
||||
** the sqlite_stat4 table. In this case ignore stat3 data. */
|
||||
** the sqlite_stat4 table. */
|
||||
nCol = pIdx->nSampleCol;
|
||||
if( bStat3 && nCol>1 ) continue;
|
||||
if( pIdx!=pPrevIdx ){
|
||||
initAvgEq(pPrevIdx);
|
||||
pPrevIdx = pIdx;
|
||||
@ -1819,7 +1767,7 @@ static int loadStatTbl(
|
||||
}
|
||||
|
||||
/*
|
||||
** Load content from the sqlite_stat4 and sqlite_stat3 tables into
|
||||
** Load content from the sqlite_stat4 table into
|
||||
** the Index.aSample[] arrays of all indices.
|
||||
*/
|
||||
static int loadStat4(sqlite3 *db, const char *zDb){
|
||||
@ -1827,37 +1775,28 @@ static int loadStat4(sqlite3 *db, const char *zDb){
|
||||
|
||||
assert( db->lookaside.bDisable );
|
||||
if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
|
||||
rc = loadStatTbl(db, 0,
|
||||
rc = loadStatTbl(db,
|
||||
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
|
||||
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
|
||||
zDb
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
|
||||
rc = loadStatTbl(db, 1,
|
||||
"SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
|
||||
"SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
|
||||
zDb
|
||||
);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat4 tables. The
|
||||
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
|
||||
** arrays. The contents of sqlite_stat3/4 are used to populate the
|
||||
** arrays. The contents of sqlite_stat4 are used to populate the
|
||||
** Index.aSample[] arrays.
|
||||
**
|
||||
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
|
||||
** during compilation and the sqlite_stat3/4 table is present, no data is
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT4 was defined
|
||||
** during compilation and the sqlite_stat4 table is present, no data is
|
||||
** read from it.
|
||||
**
|
||||
** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
|
||||
** If SQLITE_ENABLE_STAT4 was defined during compilation and the
|
||||
** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
|
||||
** returned. However, in this case, data is read from the sqlite_stat1
|
||||
** table (if it is present) before returning.
|
||||
@ -1885,7 +1824,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
pIdx->hasStat1 = 0;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3DeleteIndexSamples(db, pIdx);
|
||||
pIdx->aSample = 0;
|
||||
#endif
|
||||
@ -1913,7 +1852,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
}
|
||||
|
||||
/* Load the statistics from the sqlite_stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( rc==SQLITE_OK ){
|
||||
db->lookaside.bDisable++;
|
||||
rc = loadStat4(db, sInfo.zDatabase);
|
||||
|
14
src/attach.c
14
src/attach.c
@ -299,6 +299,7 @@ static void detachFunc(
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int i;
|
||||
Db *pDb = 0;
|
||||
HashElem *pEntry;
|
||||
char zErr[128];
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
@ -323,6 +324,18 @@ static void detachFunc(
|
||||
goto detach_error;
|
||||
}
|
||||
|
||||
/* If any TEMP triggers reference the schema being detached, move those
|
||||
** triggers to reference the TEMP schema itself. */
|
||||
assert( db->aDb[1].pSchema );
|
||||
pEntry = sqliteHashFirst(&db->aDb[1].pSchema->trigHash);
|
||||
while( pEntry ){
|
||||
Trigger *pTrig = (Trigger*)sqliteHashData(pEntry);
|
||||
if( pTrig->pTabSchema==pDb->pSchema ){
|
||||
pTrig->pTabSchema = pTrig->pSchema;
|
||||
}
|
||||
pEntry = sqliteHashNext(pEntry);
|
||||
}
|
||||
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
pDb->pSchema = 0;
|
||||
@ -560,6 +573,7 @@ int sqlite3FixExpr(
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
ExprSetProperty(pExpr, EP_Indirect);
|
||||
if( pExpr->op==TK_VARIABLE ){
|
||||
if( pFix->pParse->db->init.busy ){
|
||||
pExpr->op = TK_NULL;
|
||||
|
@ -78,7 +78,7 @@ int sqlite3_set_authorizer(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->xAuth = (sqlite3_xauth)xAuth;
|
||||
db->pAuthArg = pArg;
|
||||
sqlite3ExpirePreparedStatements(db, 0);
|
||||
if( db->xAuth ) sqlite3ExpirePreparedStatements(db, 1);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -165,7 +165,8 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
sqlite3CodecGetKey(pDestDb, sqlcipher_find_db_index(pDestDb, zDestDb), &zKey, &destNKey);
|
||||
zKey = NULL;
|
||||
|
||||
if(srcNKey || destNKey) {
|
||||
/* either both databases must be plaintext, or both must be encrypted */
|
||||
if((srcNKey == 0 && destNKey > 0) || (srcNKey > 0 && destNKey == 0)) {
|
||||
sqlite3ErrorWithMsg(pDestDb, SQLITE_ERROR, "backup is not supported with encrypted databases");
|
||||
return NULL;
|
||||
}
|
||||
@ -294,7 +295,7 @@ static int backupOnePage(
|
||||
if( nSrcReserve!=nDestReserve ){
|
||||
u32 newPgsz = nSrcPgsz;
|
||||
rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve);
|
||||
if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY;
|
||||
if( rc==SQLITE_OK && newPgsz!=(u32)nSrcPgsz ) rc = SQLITE_READONLY;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -639,8 +640,10 @@ int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
}
|
||||
if( p->isAttached ){
|
||||
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
|
||||
assert( pp!=0 );
|
||||
while( *pp!=p ){
|
||||
pp = &(*pp)->pNext;
|
||||
assert( pp!=0 );
|
||||
}
|
||||
*pp = p->pNext;
|
||||
}
|
||||
|
89
src/btree.c
89
src/btree.c
@ -1628,7 +1628,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
** However, that integer is too large to be stored in a 2-byte unsigned
|
||||
** integer, so a value of 0 is used in its place. */
|
||||
top = get2byte(&data[hdr+5]);
|
||||
assert( top<=(int)pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */
|
||||
assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */
|
||||
if( gap>top ){
|
||||
if( top==0 && pPage->pBt->usableSize==65536 ){
|
||||
top = 65536;
|
||||
@ -1647,9 +1647,12 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
|
||||
u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
|
||||
if( pSpace ){
|
||||
assert( pSpace>=data && (pSpace - data)<65536 );
|
||||
*pIdx = (int)(pSpace - data);
|
||||
return SQLITE_OK;
|
||||
assert( pSpace+nByte<=data+pPage->pBt->usableSize );
|
||||
if( (*pIdx = (int)(pSpace-data))<=gap ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}else{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}else if( rc ){
|
||||
return rc;
|
||||
}
|
||||
@ -1925,7 +1928,7 @@ static int btreeComputeFreeSpace(MemPage *pPage){
|
||||
** serves to verify that the offset to the start of the cell-content
|
||||
** area, according to the page header, lies within the page.
|
||||
*/
|
||||
if( nFree>usableSize ){
|
||||
if( nFree>usableSize || nFree<iCellFirst ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
pPage->nFree = (u16)(nFree - iCellFirst);
|
||||
@ -4153,6 +4156,18 @@ int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the pBt->nPage field correctly, according to the current
|
||||
** state of the database. Assume pBt->pPage1 is valid.
|
||||
*/
|
||||
static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){
|
||||
int nPage = get4byte(&pPage1->aData[28]);
|
||||
testcase( nPage==0 );
|
||||
if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
|
||||
testcase( pBt->nPage!=nPage );
|
||||
pBt->nPage = nPage;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rollback the transaction in progress.
|
||||
**
|
||||
@ -4198,11 +4213,7 @@ int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
|
||||
** call btreeGetPage() on page 1 again to make
|
||||
** sure pPage1->aData is set correctly. */
|
||||
if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){
|
||||
int nPage = get4byte(28+(u8*)pPage1->aData);
|
||||
testcase( nPage==0 );
|
||||
if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
|
||||
testcase( pBt->nPage!=nPage );
|
||||
pBt->nPage = nPage;
|
||||
btreeSetNPage(pBt, pPage1);
|
||||
releasePageOne(pPage1);
|
||||
}
|
||||
assert( countValidCursors(pBt, 1)==0 );
|
||||
@ -4282,12 +4293,11 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
||||
pBt->nPage = 0;
|
||||
}
|
||||
rc = newDatabase(pBt);
|
||||
pBt->nPage = get4byte(28 + pBt->pPage1->aData);
|
||||
btreeSetNPage(pBt, pBt->pPage1);
|
||||
|
||||
/* The database size was written into the offset 28 of the header
|
||||
** when the transaction started, so we know that the value at offset
|
||||
** 28 is nonzero. */
|
||||
assert( pBt->nPage>0 );
|
||||
/* pBt->nPage might be zero if the database was corrupt when
|
||||
** the transaction was started. Otherwise, it must be at least 1. */
|
||||
assert( CORRUPT_DB || pBt->nPage>0 );
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
}
|
||||
@ -4869,6 +4879,7 @@ static int accessPayload(
|
||||
assert( aWrite>=pBufStart ); /* due to (6) */
|
||||
memcpy(aSave, aWrite, 4);
|
||||
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
|
||||
if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
|
||||
nextPage = get4byte(aWrite);
|
||||
memcpy(aWrite, aSave, 4);
|
||||
}else
|
||||
@ -5295,6 +5306,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
assert( pCur->ix==pCur->pPage->nCell-1 );
|
||||
assert( pCur->pPage->leaf );
|
||||
#endif
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -5516,6 +5528,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
** case this happens. */
|
||||
void *pCellKey;
|
||||
u8 * const pCellBody = pCell - pPage->childPtrSize;
|
||||
const int nOverrun = 18; /* Size of the overrun padding */
|
||||
pPage->xParseCell(pPage, pCellBody, &pCur->info);
|
||||
nCell = (int)pCur->info.nKey;
|
||||
testcase( nCell<0 ); /* True if key size is 2^32 or more */
|
||||
@ -5526,13 +5539,14 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
rc = SQLITE_CORRUPT_PAGE(pPage);
|
||||
goto moveto_finish;
|
||||
}
|
||||
pCellKey = sqlite3Malloc( nCell+18 );
|
||||
pCellKey = sqlite3Malloc( nCell+nOverrun );
|
||||
if( pCellKey==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
goto moveto_finish;
|
||||
}
|
||||
pCur->ix = (u16)idx;
|
||||
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
|
||||
memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */
|
||||
pCur->curFlags &= ~BTCF_ValidOvfl;
|
||||
if( rc ){
|
||||
sqlite3_free(pCellKey);
|
||||
@ -6654,12 +6668,7 @@ static void insertCell(
|
||||
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
|
||||
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
/* The cell should normally be sized correctly. However, when moving a
|
||||
** malformed cell from a leaf page to an interior page, if the cell size
|
||||
** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
|
||||
** might be less than 8 (leaf-size + pointer) on the interior node. Hence
|
||||
** the term after the || in the following assert(). */
|
||||
assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
|
||||
assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
|
||||
assert( pPage->nFree>=0 );
|
||||
if( pPage->nOverflow || sz+2>pPage->nFree ){
|
||||
if( pTemp ){
|
||||
@ -6891,7 +6900,7 @@ static int rebuildPage(
|
||||
|
||||
assert( i<iEnd );
|
||||
j = get2byte(&aData[hdr+5]);
|
||||
if( NEVER(j>(u32)usableSize) ){ j = 0; }
|
||||
if( j>(u32)usableSize ){ j = 0; }
|
||||
memcpy(&pTmp[j], &aData[j], usableSize - j);
|
||||
|
||||
for(k=0; pCArray->ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
|
||||
@ -6983,7 +6992,8 @@ static int pageInsertArray(
|
||||
while( 1 /*Exit by break*/ ){
|
||||
int sz, rc;
|
||||
u8 *pSlot;
|
||||
sz = cachedCellSize(pCArray, i);
|
||||
assert( pCArray->szCell[i]!=0 );
|
||||
sz = pCArray->szCell[i];
|
||||
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
|
||||
if( (pData - pBegin)<sz ) return 1;
|
||||
pData -= sz;
|
||||
@ -7144,6 +7154,7 @@ static int editPage(
|
||||
memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
|
||||
}
|
||||
nCell++;
|
||||
cachedCellSize(pCArray, iCell+iNew);
|
||||
if( pageInsertArray(
|
||||
pPg, pBegin, &pData, pCellptr,
|
||||
iCell+iNew, 1, pCArray
|
||||
@ -7637,6 +7648,7 @@ static int balance_nonroot(
|
||||
u16 maskPage = pOld->maskPage;
|
||||
u8 *piCell = aData + pOld->cellOffset;
|
||||
u8 *piEnd;
|
||||
VVA_ONLY( int nCellAtStart = b.nCell; )
|
||||
|
||||
/* Verify that all sibling pages are of the same "type" (table-leaf,
|
||||
** table-interior, index-leaf, or index-interior).
|
||||
@ -7665,6 +7677,10 @@ static int balance_nonroot(
|
||||
*/
|
||||
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
|
||||
if( pOld->nOverflow>0 ){
|
||||
if( NEVER(limit<pOld->aiOvfl[0]) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
limit = pOld->aiOvfl[0];
|
||||
for(j=0; j<limit; j++){
|
||||
b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
|
||||
@ -7684,6 +7700,7 @@ static int balance_nonroot(
|
||||
piCell += 2;
|
||||
b.nCell++;
|
||||
}
|
||||
assert( (b.nCell-nCellAtStart)==(pOld->nCell+pOld->nOverflow) );
|
||||
|
||||
cntOld[i] = b.nCell;
|
||||
if( i<nOld-1 && !leafData){
|
||||
@ -7946,6 +7963,8 @@ static int balance_nonroot(
|
||||
));
|
||||
|
||||
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
|
||||
assert( nNew>=1 && nNew<=ArraySize(apNew) );
|
||||
assert( apNew[nNew-1]!=0 );
|
||||
put4byte(pRight, apNew[nNew-1]->pgno);
|
||||
|
||||
/* If the sibling pages are not leaves, ensure that the right-child pointer
|
||||
@ -7984,6 +8003,7 @@ static int balance_nonroot(
|
||||
while( i==cntOldNext ){
|
||||
iOld++;
|
||||
assert( iOld<nNew || iOld<nOld );
|
||||
assert( iOld>=0 && iOld<NB );
|
||||
pOld = iOld<nNew ? apNew[iOld] : apOld[iOld];
|
||||
cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
|
||||
}
|
||||
@ -8290,11 +8310,13 @@ static int balance(BtCursor *pCur){
|
||||
VVA_ONLY( int balance_deeper_called = 0 );
|
||||
|
||||
do {
|
||||
int iPage = pCur->iPage;
|
||||
int iPage;
|
||||
MemPage *pPage = pCur->pPage;
|
||||
|
||||
if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break;
|
||||
if( iPage==0 ){
|
||||
if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
|
||||
break;
|
||||
}else if( (iPage = pCur->iPage)==0 ){
|
||||
if( pPage->nOverflow ){
|
||||
/* The root page of the b-tree is overfull. In this case call the
|
||||
** balance_deeper() function to create a new child for the root-page
|
||||
@ -8315,8 +8337,6 @@ static int balance(BtCursor *pCur){
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
|
||||
break;
|
||||
}else{
|
||||
MemPage * const pParent = pCur->apPage[iPage-1];
|
||||
int const iIdx = pCur->aiIdx[iPage-1];
|
||||
@ -8458,7 +8478,9 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
|
||||
Pgno ovflPgno; /* Next overflow page to write */
|
||||
u32 ovflPageSize; /* Size to write on overflow page */
|
||||
|
||||
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
|
||||
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
|
||||
|| pCur->info.pPayload < pPage->aData + pPage->cellOffset
|
||||
){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
/* Overwrite the local portion first */
|
||||
@ -8699,6 +8721,8 @@ int sqlite3BtreeInsert(
|
||||
memcpy(newCell, oldCell, 4);
|
||||
}
|
||||
rc = clearCell(pPage, oldCell, &info);
|
||||
testcase( pCur->curFlags & BTCF_ValidOvfl );
|
||||
invalidateOverflowCache(pCur);
|
||||
if( info.nSize==szNew && info.nLocal==info.nPayload
|
||||
&& (!ISAUTOVACUUM || szNew<pPage->minLocal)
|
||||
){
|
||||
@ -8712,7 +8736,12 @@ int sqlite3BtreeInsert(
|
||||
** new entry uses overflow pages, as the insertCell() call below is
|
||||
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
|
||||
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
|
||||
if( oldCell+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
|
||||
if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( oldCell+szNew > pPage->aDataEnd ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
memcpy(oldCell, newCell, szNew);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
226
src/build.c
226
src/build.c
@ -456,7 +456,7 @@ void sqlite3FreeIndex(sqlite3 *db, Index *p){
|
||||
sqlite3ExprListDelete(db, p->aColExpr);
|
||||
sqlite3DbFree(db, p->zColAff);
|
||||
if( p->isResized ) sqlite3DbFree(db, (void *)p->azColl);
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3_free(p->aiRowEst);
|
||||
#endif
|
||||
sqlite3DbFree(db, p);
|
||||
@ -618,10 +618,14 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Record the number of outstanding lookaside allocations in schema Tables
|
||||
** prior to doing any free() operations. Since schema Tables do not use
|
||||
** lookaside, this number should not change. */
|
||||
** prior to doing any free() operations. Since schema Tables do not use
|
||||
** lookaside, this number should not change.
|
||||
**
|
||||
** If malloc has already failed, it may be that it failed while allocating
|
||||
** a Table object that was going to be marked ephemeral. So do not check
|
||||
** that no lookaside memory is used in this case either. */
|
||||
int nLookaside = 0;
|
||||
if( db && (pTable->tabFlags & TF_Ephemeral)==0 ){
|
||||
if( db && !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){
|
||||
nLookaside = sqlite3LookasideUsed(db, 0);
|
||||
}
|
||||
#endif
|
||||
@ -825,13 +829,40 @@ int sqlite3WritableSchema(sqlite3 *db){
|
||||
** trigger). All names are legal except those that begin with the string
|
||||
** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
|
||||
** is reserved for internal use.
|
||||
**
|
||||
** When parsing the sqlite_master table, this routine also checks to
|
||||
** make sure the "type", "name", and "tbl_name" columns are consistent
|
||||
** with the SQL.
|
||||
*/
|
||||
int sqlite3CheckObjectName(Parse *pParse, const char *zName){
|
||||
if( !pParse->db->init.busy && pParse->nested==0
|
||||
&& sqlite3WritableSchema(pParse->db)==0
|
||||
&& 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
|
||||
sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
|
||||
return SQLITE_ERROR;
|
||||
int sqlite3CheckObjectName(
|
||||
Parse *pParse, /* Parsing context */
|
||||
const char *zName, /* Name of the object to check */
|
||||
const char *zType, /* Type of this object */
|
||||
const char *zTblName /* Parent table name for triggers and indexes */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
if( sqlite3WritableSchema(db) || db->init.imposterTable ){
|
||||
/* Skip these error checks for writable_schema=ON */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( db->init.busy ){
|
||||
if( sqlite3_stricmp(zType, db->init.azInit[0])
|
||||
|| sqlite3_stricmp(zName, db->init.azInit[1])
|
||||
|| sqlite3_stricmp(zTblName, db->init.azInit[2])
|
||||
){
|
||||
if( sqlite3Config.bExtraSchemaChecks ){
|
||||
sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( pParse->nested==0
|
||||
&& 0==sqlite3StrNICmp(zName, "sqlite_", 7)
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s",
|
||||
zName);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -912,7 +943,7 @@ void sqlite3StartTable(
|
||||
}
|
||||
pParse->sNameToken = *pName;
|
||||
if( zName==0 ) return;
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
||||
if( sqlite3CheckObjectName(pParse, zName, isView?"view":"table", zName) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
if( db->init.iDb==1 ) isTemp = 1;
|
||||
@ -1329,7 +1360,7 @@ void sqlite3AddDefaultValue(
|
||||
** accept it. This routine does the necessary conversion. It converts
|
||||
** the expression given in its argument from a TK_STRING into a TK_ID
|
||||
** if the expression is just a TK_STRING with an optional COLLATE clause.
|
||||
** If the epxression is anything other than TK_STRING, the expression is
|
||||
** If the expression is anything other than TK_STRING, the expression is
|
||||
** unchanged.
|
||||
*/
|
||||
static void sqlite3StringToId(Expr *p){
|
||||
@ -1412,7 +1443,7 @@ void sqlite3AddPrimaryKey(
|
||||
pTab->keyConf = (u8)onError;
|
||||
assert( autoInc==0 || autoInc==1 );
|
||||
pTab->tabFlags |= autoInc*TF_Autoincrement;
|
||||
if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
|
||||
if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags;
|
||||
}else if( autoInc ){
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
|
||||
@ -1726,10 +1757,51 @@ static void estimateIndexWidth(Index *pIdx){
|
||||
pIdx->szIdxRow = sqlite3LogEst(wIndex*4);
|
||||
}
|
||||
|
||||
/* Return true if value x is found any of the first nCol entries of aiCol[]
|
||||
/* Return true if column number x is any of the first nCol entries of aiCol[].
|
||||
** This is used to determine if the column number x appears in any of the
|
||||
** first nCol entries of an index.
|
||||
*/
|
||||
static int hasColumn(const i16 *aiCol, int nCol, int x){
|
||||
while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1;
|
||||
while( nCol-- > 0 ){
|
||||
assert( aiCol[0]>=0 );
|
||||
if( x==*(aiCol++) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if any of the first nKey entries of index pIdx exactly
|
||||
** match the iCol-th entry of pPk. pPk is always a WITHOUT ROWID
|
||||
** PRIMARY KEY index. pIdx is an index on the same table. pIdx may
|
||||
** or may not be the same index as pPk.
|
||||
**
|
||||
** The first nKey entries of pIdx are guaranteed to be ordinary columns,
|
||||
** not a rowid or expression.
|
||||
**
|
||||
** This routine differs from hasColumn() in that both the column and the
|
||||
** collating sequence must match for this routine, but for hasColumn() only
|
||||
** the column name must match.
|
||||
*/
|
||||
static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){
|
||||
int i, j;
|
||||
assert( nKey<=pIdx->nColumn );
|
||||
assert( iCol<MAX(pPk->nColumn,pPk->nKeyCol) );
|
||||
assert( pPk->idxType==SQLITE_IDXTYPE_PRIMARYKEY );
|
||||
assert( pPk->pTable->tabFlags & TF_WithoutRowid );
|
||||
assert( pPk->pTable==pIdx->pTable );
|
||||
testcase( pPk==pIdx );
|
||||
j = pPk->aiColumn[iCol];
|
||||
assert( j!=XN_ROWID && j!=XN_EXPR );
|
||||
for(i=0; i<nKey; i++){
|
||||
assert( pIdx->aiColumn[i]>=0 || j>=0 );
|
||||
if( pIdx->aiColumn[i]==j
|
||||
&& sqlite3StrICmp(pIdx->azColl[i], pPk->azColl[iCol])==0
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1786,6 +1858,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
Index *pIdx;
|
||||
Index *pPk;
|
||||
int nPk;
|
||||
int nExtra;
|
||||
int i, j;
|
||||
sqlite3 *db = pParse->db;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
@ -1818,13 +1891,17 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
pList = sqlite3ExprListAppend(pParse, 0,
|
||||
sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0));
|
||||
if( pList==0 ) return;
|
||||
pList->a[0].sortOrder = pParse->iPkSortOrder;
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey);
|
||||
}
|
||||
pList->a[0].sortFlags = pParse->iPkSortOrder;
|
||||
assert( pParse->pNewTable==pTab );
|
||||
pTab->iPKey = -1;
|
||||
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
|
||||
SQLITE_IDXTYPE_PRIMARYKEY);
|
||||
if( db->mallocFailed || pParse->nErr ) return;
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
pTab->iPKey = -1;
|
||||
assert( pPk->nKeyCol==1 );
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
@ -1835,9 +1912,12 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
** code assumes the PRIMARY KEY contains no repeated columns.
|
||||
*/
|
||||
for(i=j=1; i<pPk->nKeyCol; i++){
|
||||
if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){
|
||||
if( isDupColumn(pPk, j, pPk, i) ){
|
||||
pPk->nColumn--;
|
||||
}else{
|
||||
testcase( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) );
|
||||
pPk->azColl[j] = pPk->azColl[i];
|
||||
pPk->aSortOrder[j] = pPk->aSortOrder[i];
|
||||
pPk->aiColumn[j++] = pPk->aiColumn[i];
|
||||
}
|
||||
}
|
||||
@ -1846,7 +1926,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
assert( pPk!=0 );
|
||||
pPk->isCovering = 1;
|
||||
if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
|
||||
nPk = pPk->nKeyCol;
|
||||
nPk = pPk->nColumn = pPk->nKeyCol;
|
||||
|
||||
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
|
||||
** table entry. This is only required if currently generating VDBE
|
||||
@ -1867,7 +1947,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
int n;
|
||||
if( IsPrimaryKeyIndex(pIdx) ) continue;
|
||||
for(i=n=0; i<nPk; i++){
|
||||
if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++;
|
||||
if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
|
||||
testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if( n==0 ){
|
||||
/* This index is a superset of the primary key */
|
||||
@ -1876,9 +1959,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
}
|
||||
if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
|
||||
for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
|
||||
if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){
|
||||
if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
|
||||
testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
|
||||
pIdx->aiColumn[j] = pPk->aiColumn[i];
|
||||
pIdx->azColl[j] = pPk->azColl[i];
|
||||
if( pPk->aSortOrder[i] ){
|
||||
/* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */
|
||||
pIdx->bAscKeyBug = 1;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
@ -1888,21 +1976,21 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
|
||||
/* Add all table columns to the PRIMARY KEY index
|
||||
*/
|
||||
if( nPk<pTab->nCol ){
|
||||
if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
|
||||
for(i=0, j=nPk; i<pTab->nCol; i++){
|
||||
if( !hasColumn(pPk->aiColumn, j, i) ){
|
||||
assert( j<pPk->nColumn );
|
||||
pPk->aiColumn[j] = i;
|
||||
pPk->azColl[j] = sqlite3StrBINARY;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
assert( pPk->nColumn==j );
|
||||
assert( pTab->nCol==j );
|
||||
}else{
|
||||
pPk->nColumn = pTab->nCol;
|
||||
nExtra = 0;
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( !hasColumn(pPk->aiColumn, nPk, i) ) nExtra++;
|
||||
}
|
||||
if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
|
||||
for(i=0, j=nPk; i<pTab->nCol; i++){
|
||||
if( !hasColumn(pPk->aiColumn, j, i) ){
|
||||
assert( j<pPk->nColumn );
|
||||
pPk->aiColumn[j] = i;
|
||||
pPk->azColl[j] = sqlite3StrBINARY;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
assert( pPk->nColumn==j );
|
||||
assert( pTab->nCol<=j );
|
||||
recomputeColumnsNotIndexed(pPk);
|
||||
}
|
||||
|
||||
@ -2099,7 +2187,7 @@ void sqlite3EndTable(
|
||||
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
||||
if( pParse->nErr ) return;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect, SQLITE_AFF_BLOB);
|
||||
if( pSelTab==0 ) return;
|
||||
assert( p->aCol==0 );
|
||||
p->nCol = pSelTab->nCol;
|
||||
@ -2363,10 +2451,10 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
|
||||
db->xAuth = xAuth;
|
||||
#else
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
|
||||
#endif
|
||||
pParse->nTab = n;
|
||||
if( pTable->pCheck ){
|
||||
@ -2382,7 +2470,8 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
&& pParse->nErr==0
|
||||
&& pTable->nCol==pSel->pEList->nExpr
|
||||
){
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel,
|
||||
SQLITE_AFF_NONE);
|
||||
}
|
||||
}else if( pSelTab ){
|
||||
/* CREATE VIEW name AS... without an argument list. Construct
|
||||
@ -2727,7 +2816,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
#endif
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
|
||||
&& sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
|
||||
&& sqlite3StrNICmp(pTab->zName+7, "stat", 4)!=0
|
||||
&& sqlite3StrNICmp(pTab->zName+7, "parameters", 10)!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
|
||||
goto exit_drop_table;
|
||||
}
|
||||
@ -2997,10 +3087,27 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
}else{
|
||||
/* Most CREATE INDEX and REINDEX statements that are not UNIQUE can not
|
||||
** abort. The exception is if one of the indexed expressions contains a
|
||||
** user function that throws an exception when it is evaluated. But the
|
||||
** overhead of adding a statement journal to a CREATE INDEX statement is
|
||||
** very small (since most of the pages written do not contain content that
|
||||
** needs to be restored if the statement aborts), so we call
|
||||
** sqlite3MayAbort() for all CREATE INDEX statements. */
|
||||
sqlite3MayAbort(pParse);
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
|
||||
if( !pIndex->bAscKeyBug ){
|
||||
/* This OP_SeekEnd opcode makes index insert for a REINDEX go much
|
||||
** faster by avoiding unnecessary seeks. But the optimization does
|
||||
** not work for UNIQUE constraint indexes on WITHOUT ROWID tables
|
||||
** with DESC primary keys, since those indexes have there keys in
|
||||
** a different order from the main table.
|
||||
** See ticket: https://www.sqlite.org/src/info/bba7b69f9849b5bf
|
||||
*/
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
@ -3047,6 +3154,27 @@ Index *sqlite3AllocateIndexObject(
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** If expression list pList contains an expression that was parsed with
|
||||
** an explicit "NULLS FIRST" or "NULLS LAST" clause, leave an error in
|
||||
** pParse and return non-zero. Otherwise, return zero.
|
||||
*/
|
||||
int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){
|
||||
if( pList ){
|
||||
int i;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( pList->a[i].bNulls ){
|
||||
u8 sf = pList->a[i].sortFlags;
|
||||
sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s",
|
||||
(sf==0 || sf==3) ? "FIRST" : "LAST"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new index for an SQL table. pName1.pName2 is the name of the index
|
||||
** and pTblList is the name of the table that is to be indexed. Both will
|
||||
@ -3098,6 +3226,9 @@ void sqlite3CreateIndex(
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( sqlite3HasExplicitNulls(pParse, pList) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the table that is to be indexed. Return early if not found.
|
||||
@ -3196,7 +3327,7 @@ void sqlite3CreateIndex(
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
if( zName==0 ) goto exit_create_index;
|
||||
assert( pName->z!=0 );
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName,"index",pTab->zName) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
@ -3262,7 +3393,7 @@ void sqlite3CreateIndex(
|
||||
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
|
||||
if( pList==0 ) goto exit_create_index;
|
||||
assert( pList->nExpr==1 );
|
||||
sqlite3ExprListSetSortOrder(pList, sortOrder);
|
||||
sqlite3ExprListSetSortOrder(pList, sortOrder, SQLITE_SO_UNDEFINED);
|
||||
}else{
|
||||
sqlite3ExprListCheckLength(pParse, pList, "index");
|
||||
if( pParse->nErr ) goto exit_create_index;
|
||||
@ -3380,7 +3511,7 @@ void sqlite3CreateIndex(
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->azColl[i] = zColl;
|
||||
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
|
||||
requestedSortOrder = pListItem->sortFlags & sortOrderMask;
|
||||
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
|
||||
}
|
||||
|
||||
@ -3392,9 +3523,10 @@ void sqlite3CreateIndex(
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
int x = pPk->aiColumn[j];
|
||||
assert( x>=0 );
|
||||
if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
|
||||
if( isDupColumn(pIndex, pIndex->nKeyCol, pPk, j) ){
|
||||
pIndex->nColumn--;
|
||||
}else{
|
||||
testcase( hasColumn(pIndex->aiColumn,pIndex->nKeyCol,x) );
|
||||
pIndex->aiColumn[i] = x;
|
||||
pIndex->azColl[i] = pPk->azColl[j];
|
||||
pIndex->aSortOrder[i] = pPk->aSortOrder[j];
|
||||
@ -3554,6 +3686,7 @@ void sqlite3CreateIndex(
|
||||
/* Gather the complete text of the CREATE INDEX statement into
|
||||
** the zStmt variable
|
||||
*/
|
||||
assert( pName!=0 || pStart==0 );
|
||||
if( pStart ){
|
||||
int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
|
||||
if( pName->z[n-1]==';' ) n--;
|
||||
@ -4596,7 +4729,8 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
|
||||
const char *zColl = pIdx->azColl[i];
|
||||
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
|
||||
sqlite3LocateCollSeq(pParse, zColl);
|
||||
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
||||
pKey->aSortFlags[i] = pIdx->aSortOrder[i];
|
||||
assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
|
||||
}
|
||||
if( pParse->nErr ){
|
||||
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
|
||||
|
12
src/crypto.c
12
src/crypto.c
@ -35,8 +35,8 @@
|
||||
#include "sqlcipher.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef SQLCIPHER_LICENSE
|
||||
#include "sqlcipher-license.h"
|
||||
#ifdef SQLCIPHER_EXT
|
||||
#include "sqlcipher_ext.h"
|
||||
#endif
|
||||
|
||||
/* Generate code to return a string value */
|
||||
@ -97,7 +97,7 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
|
||||
CODEC_TRACE("sqlcipher_codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx);
|
||||
|
||||
#ifdef SQLCIPHER_LICENSE
|
||||
#ifdef SQLCIPHER_EXT
|
||||
if( sqlite3StrICmp(zLeft, "cipher_license")==0 && zRight ){
|
||||
char *license_result = sqlite3_mprintf("%d", sqlcipher_license_key(zRight));
|
||||
codec_vdbe_return_string(pParse, "cipher_license", license_result, P4_DYNAMIC);
|
||||
@ -105,7 +105,7 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
if( sqlite3StrICmp(zLeft, "cipher_license")==0 && !zRight ){
|
||||
if(ctx) {
|
||||
char *license_result = sqlite3_mprintf("%d", ctx
|
||||
? sqlcipher_license_key_status(ctx)
|
||||
? sqlcipher_license_key_status(ctx->provider)
|
||||
: SQLITE_ERROR);
|
||||
codec_vdbe_return_string(pParse, "cipher_license", license_result, P4_DYNAMIC);
|
||||
}
|
||||
@ -667,7 +667,7 @@ static void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
|
||||
|
||||
CODEC_TRACE("sqlite3Codec: entered pgno=%d, mode=%d, page_sz=%d\n", pgno, mode, page_sz);
|
||||
|
||||
#ifdef SQLCIPHER_LICENSE
|
||||
#ifdef SQLCIPHER_EXT
|
||||
if(sqlcipher_license_check(ctx) != SQLITE_OK) return NULL;
|
||||
#endif
|
||||
|
||||
@ -753,7 +753,7 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
CODEC_TRACE_MUTEX("sqlite3CodecAttach: entered database mutex %p\n", db->mutex);
|
||||
|
||||
#ifdef SQLCIPHER_LICENSE
|
||||
#ifdef SQLCIPHER_EXT
|
||||
if((rc = sqlite3_set_authorizer(db, sqlcipher_license_authorizer, db)) != SQLITE_OK) {
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
|
70
src/crypto.h
70
src/crypto.h
@ -48,6 +48,7 @@ void sqlite3pager_reset(Pager *pPager);
|
||||
|
||||
#if !defined (SQLCIPHER_CRYPTO_CC) \
|
||||
&& !defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) \
|
||||
&& !defined (SQLCIPHER_CRYPTO_NSS) \
|
||||
&& !defined (SQLCIPHER_CRYPTO_OPENSSL)
|
||||
#define SQLCIPHER_CRYPTO_OPENSSL
|
||||
#endif
|
||||
@ -58,7 +59,7 @@ void sqlite3pager_reset(Pager *pPager);
|
||||
#define CIPHER_STR(s) #s
|
||||
|
||||
#ifndef CIPHER_VERSION_NUMBER
|
||||
#define CIPHER_VERSION_NUMBER 4.2.0
|
||||
#define CIPHER_VERSION_NUMBER 4.3.0
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER_VERSION_BUILD
|
||||
@ -114,16 +115,6 @@ void sqlite3pager_reset(Pager *pPager);
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
#ifdef CODEC_DEBUG_MUTEX
|
||||
#ifdef __ANDROID__
|
||||
#define CODEC_TRACE_MUTEX(...) {__android_log_print(ANDROID_LOG_DEBUG, "sqlcipher", __VA_ARGS__);}
|
||||
#else
|
||||
#define CODEC_TRACE_MUTEX(...) {fprintf(stderr, __VA_ARGS__);fflush(stderr);}
|
||||
#endif
|
||||
#else
|
||||
#define CODEC_TRACE_MUTEX(...)
|
||||
#endif
|
||||
|
||||
#ifdef CODEC_DEBUG
|
||||
#ifdef __ANDROID__
|
||||
#define CODEC_TRACE(...) {__android_log_print(ANDROID_LOG_DEBUG, "sqlcipher", __VA_ARGS__);}
|
||||
@ -134,6 +125,18 @@ void sqlite3pager_reset(Pager *pPager);
|
||||
#define CODEC_TRACE(...)
|
||||
#endif
|
||||
|
||||
#ifdef CODEC_DEBUG_MUTEX
|
||||
#define CODEC_TRACE_MUTEX(...) CODEC_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define CODEC_TRACE_MUTEX(...)
|
||||
#endif
|
||||
|
||||
#ifdef CODEC_DEBUG_MEMORY
|
||||
#define CODEC_TRACE_MEMORY(...) CODEC_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define CODEC_TRACE_MEMORY(...)
|
||||
#endif
|
||||
|
||||
#ifdef CODEC_DEBUG_PAGEDATA
|
||||
#define CODEC_HEXDUMP(DESC,BUFFER,LEN) \
|
||||
{ \
|
||||
@ -189,7 +192,46 @@ static int cipher_isHex(const unsigned char *hex, int sz){
|
||||
}
|
||||
|
||||
/* extensions defined in crypto_impl.c */
|
||||
typedef struct codec_ctx codec_ctx;
|
||||
/* the default implementation of SQLCipher uses a cipher_ctx
|
||||
to keep track of read / write state separately. The following
|
||||
struct and associated functions are defined here */
|
||||
typedef struct {
|
||||
int derive_key;
|
||||
int pass_sz;
|
||||
unsigned char *key;
|
||||
unsigned char *hmac_key;
|
||||
unsigned char *pass;
|
||||
char *keyspec;
|
||||
} cipher_ctx;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int store_pass;
|
||||
int kdf_iter;
|
||||
int fast_kdf_iter;
|
||||
int kdf_salt_sz;
|
||||
int key_sz;
|
||||
int iv_sz;
|
||||
int block_sz;
|
||||
int page_sz;
|
||||
int keyspec_sz;
|
||||
int reserve_sz;
|
||||
int hmac_sz;
|
||||
int plaintext_header_sz;
|
||||
int hmac_algorithm;
|
||||
int kdf_algorithm;
|
||||
unsigned int skip_read_hmac;
|
||||
unsigned int need_kdf_salt;
|
||||
unsigned int flags;
|
||||
unsigned char *kdf_salt;
|
||||
unsigned char *hmac_kdf_salt;
|
||||
unsigned char *buffer;
|
||||
Btree *pBt;
|
||||
cipher_ctx *read_ctx;
|
||||
cipher_ctx *write_ctx;
|
||||
sqlcipher_provider *provider;
|
||||
void *provider_ctx;
|
||||
} codec_ctx ;
|
||||
|
||||
/* crypto.c functions */
|
||||
int sqlcipher_codec_pragma(sqlite3*, int, Parse*, const char *, const char*);
|
||||
@ -265,10 +307,6 @@ void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value);
|
||||
int sqlcipher_codec_fips_status(codec_ctx *ctx);
|
||||
const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx);
|
||||
|
||||
int sqlcipher_codec_hmac_sha1(const codec_ctx *ctx, const unsigned char *hmac_key, int key_sz,
|
||||
unsigned char* in, int in_sz, unsigned char *in2, int in2_sz,
|
||||
unsigned char *out);
|
||||
|
||||
int sqlcipher_set_default_plaintext_header_size(int size);
|
||||
int sqlcipher_get_default_plaintext_header_size(void);
|
||||
int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size);
|
||||
|
@ -154,14 +154,6 @@ static int sqlcipher_cc_get_hmac_sz(void *ctx, int algorithm) {
|
||||
}
|
||||
}
|
||||
|
||||
static int sqlcipher_cc_ctx_copy(void *target_ctx, void *source_ctx) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_cc_ctx_cmp(void *c1, void *c2) {
|
||||
return 1; /* always indicate contexts are the same */
|
||||
}
|
||||
|
||||
static int sqlcipher_cc_ctx_init(void **ctx) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -174,6 +166,14 @@ static int sqlcipher_cc_fips_status(void *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sqlcipher_cc_id(void *ctx) {
|
||||
return 1633265;
|
||||
}
|
||||
|
||||
static void* sqlcipher_cc_status(void *ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sqlcipher_cc_setup(sqlcipher_provider *p) {
|
||||
p->random = sqlcipher_cc_random;
|
||||
p->get_provider_name = sqlcipher_cc_get_provider_name;
|
||||
@ -185,13 +185,13 @@ int sqlcipher_cc_setup(sqlcipher_provider *p) {
|
||||
p->get_iv_sz = sqlcipher_cc_get_iv_sz;
|
||||
p->get_block_sz = sqlcipher_cc_get_block_sz;
|
||||
p->get_hmac_sz = sqlcipher_cc_get_hmac_sz;
|
||||
p->ctx_copy = sqlcipher_cc_ctx_copy;
|
||||
p->ctx_cmp = sqlcipher_cc_ctx_cmp;
|
||||
p->ctx_init = sqlcipher_cc_ctx_init;
|
||||
p->ctx_free = sqlcipher_cc_ctx_free;
|
||||
p->add_random = sqlcipher_cc_add_random;
|
||||
p->fips_status = sqlcipher_cc_fips_status;
|
||||
p->get_provider_version = sqlcipher_cc_get_provider_version;
|
||||
p->id = sqlcipher_cc_id;
|
||||
p->status = sqlcipher_cc_status;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -56,49 +56,14 @@ static volatile int mem_security_initialized = 0;
|
||||
static volatile int mem_security_activated = 0;
|
||||
static volatile unsigned int sqlcipher_activate_count = 0;
|
||||
static volatile sqlite3_mem_methods default_mem_methods;
|
||||
static sqlite3_mutex* sqlcipher_provider_mutex = NULL;
|
||||
static sqlcipher_provider *default_provider = NULL;
|
||||
|
||||
/* the default implementation of SQLCipher uses a cipher_ctx
|
||||
to keep track of read / write state separately. The following
|
||||
struct and associated functions are defined here */
|
||||
typedef struct {
|
||||
int derive_key;
|
||||
int pass_sz;
|
||||
unsigned char *key;
|
||||
unsigned char *hmac_key;
|
||||
unsigned char *pass;
|
||||
char *keyspec;
|
||||
} cipher_ctx;
|
||||
static sqlite3_mutex* sqlcipher_static_mutex[SQLCIPHER_MUTEX_COUNT];
|
||||
|
||||
|
||||
struct codec_ctx {
|
||||
int store_pass;
|
||||
int kdf_iter;
|
||||
int fast_kdf_iter;
|
||||
int kdf_salt_sz;
|
||||
int key_sz;
|
||||
int iv_sz;
|
||||
int block_sz;
|
||||
int page_sz;
|
||||
int keyspec_sz;
|
||||
int reserve_sz;
|
||||
int hmac_sz;
|
||||
int plaintext_header_sz;
|
||||
int hmac_algorithm;
|
||||
int kdf_algorithm;
|
||||
unsigned int skip_read_hmac;
|
||||
unsigned int need_kdf_salt;
|
||||
unsigned int flags;
|
||||
unsigned char *kdf_salt;
|
||||
unsigned char *hmac_kdf_salt;
|
||||
unsigned char *buffer;
|
||||
Btree *pBt;
|
||||
cipher_ctx *read_ctx;
|
||||
cipher_ctx *write_ctx;
|
||||
sqlcipher_provider *provider;
|
||||
void *provider_ctx;
|
||||
};
|
||||
sqlite3_mutex* sqlcipher_mutex(int mutex) {
|
||||
if(mutex < 0 || mutex >= SQLCIPHER_MUTEX_COUNT) return NULL;
|
||||
return sqlcipher_static_mutex[mutex];
|
||||
}
|
||||
|
||||
static int sqlcipher_mem_init(void *pAppData) {
|
||||
return default_mem_methods.xInit(pAppData);
|
||||
@ -109,7 +74,7 @@ static void sqlcipher_mem_shutdown(void *pAppData) {
|
||||
static void *sqlcipher_mem_malloc(int n) {
|
||||
void *ptr = default_mem_methods.xMalloc(n);
|
||||
if(mem_security_on) {
|
||||
CODEC_TRACE("sqlcipher_mem_malloc: calling sqlcipher_mlock(%p,%d)\n", ptr, n);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_mem_malloc: calling sqlcipher_mlock(%p,%d)\n", ptr, n);
|
||||
sqlcipher_mlock(ptr, n);
|
||||
if(!mem_security_activated) mem_security_activated = 1;
|
||||
}
|
||||
@ -122,7 +87,7 @@ static void sqlcipher_mem_free(void *p) {
|
||||
int sz;
|
||||
if(mem_security_on) {
|
||||
sz = sqlcipher_mem_size(p);
|
||||
CODEC_TRACE("sqlcipher_mem_free: calling sqlcipher_memset(%p,0,%d) and sqlcipher_munlock(%p, %d) \n", p, sz, p, sz);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_mem_free: calling sqlcipher_memset(%p,0,%d) and sqlcipher_munlock(%p, %d) \n", p, sz, p, sz);
|
||||
sqlcipher_memset(p, 0, sz);
|
||||
sqlcipher_munlock(p, sz);
|
||||
if(!mem_security_activated) mem_security_activated = 1;
|
||||
@ -157,9 +122,9 @@ void sqlcipher_init_memmethods() {
|
||||
}
|
||||
|
||||
int sqlcipher_register_provider(sqlcipher_provider *p) {
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: entering sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_enter(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: entered sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: entering SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: entered SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
|
||||
if(default_provider != NULL && default_provider != p) {
|
||||
/* only free the current registerd provider if it has been initialized
|
||||
@ -168,9 +133,9 @@ int sqlcipher_register_provider(sqlcipher_provider *p) {
|
||||
sqlcipher_free(default_provider, sizeof(sqlcipher_provider));
|
||||
}
|
||||
default_provider = p;
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: leaving sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_leave(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: left sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: leaving SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_register_provider: left SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -187,11 +152,12 @@ void sqlcipher_activate() {
|
||||
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_activate: entered static master mutex\n");
|
||||
|
||||
if(sqlcipher_provider_mutex == NULL) {
|
||||
/* allocate a new mutex to guard access to the provider */
|
||||
CODEC_TRACE_MUTEX("sqlcipher_activate: allocating sqlcipher provider mutex\n");
|
||||
sqlcipher_provider_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_activate: allocated sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
/* allocate new mutexes */
|
||||
if(sqlcipher_activate_count == 0) {
|
||||
int i;
|
||||
for(i = 0; i < SQLCIPHER_MUTEX_COUNT; i++) {
|
||||
sqlcipher_static_mutex[i] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
}
|
||||
}
|
||||
|
||||
/* check to see if there is a provider registered at this point
|
||||
@ -205,6 +171,9 @@ void sqlcipher_activate() {
|
||||
#elif defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT)
|
||||
extern int sqlcipher_ltc_setup(sqlcipher_provider *p);
|
||||
sqlcipher_ltc_setup(p);
|
||||
#elif defined (SQLCIPHER_CRYPTO_NSS)
|
||||
extern int sqlcipher_nss_setup(sqlcipher_provider *p);
|
||||
sqlcipher_nss_setup(p);
|
||||
#elif defined (SQLCIPHER_CRYPTO_OPENSSL)
|
||||
extern int sqlcipher_openssl_setup(sqlcipher_provider *p);
|
||||
sqlcipher_openssl_setup(p);
|
||||
@ -212,6 +181,9 @@ void sqlcipher_activate() {
|
||||
#error "NO DEFAULT SQLCIPHER CRYPTO PROVIDER DEFINED"
|
||||
#endif
|
||||
CODEC_TRACE("sqlcipher_activate: calling sqlcipher_register_provider(%p)\n", p);
|
||||
#ifdef SQLCIPHER_EXT
|
||||
sqlcipher_ext_provider_setup(p);
|
||||
#endif
|
||||
sqlcipher_register_provider(p);
|
||||
CODEC_TRACE("sqlcipher_activate: called sqlcipher_register_provider(%p)\n",p);
|
||||
}
|
||||
@ -231,26 +203,31 @@ void sqlcipher_deactivate() {
|
||||
sqlcipher_activate_count--;
|
||||
/* if no connections are using sqlcipher, cleanup globals */
|
||||
if(sqlcipher_activate_count < 1) {
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: entering sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_enter(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: entered sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: entering SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: entered SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
|
||||
if(default_provider != NULL) {
|
||||
sqlcipher_free(default_provider, sizeof(sqlcipher_provider));
|
||||
default_provider = NULL;
|
||||
}
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: leaving sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_leave(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: left sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
|
||||
/* last connection closed, free provider mutex*/
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: freeing sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_free(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: freed sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_deactivate: left SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
|
||||
sqlcipher_provider_mutex = NULL;
|
||||
#ifdef SQLCIPHER_EXT
|
||||
sqlcipher_ext_provider_destroy();
|
||||
#endif
|
||||
|
||||
/* last connection closed, free mutexes */
|
||||
if(sqlcipher_activate_count == 0) {
|
||||
int i;
|
||||
for(i = 0; i < SQLCIPHER_MUTEX_COUNT; i++) {
|
||||
sqlite3_mutex_free(sqlcipher_static_mutex[i]);
|
||||
}
|
||||
}
|
||||
sqlcipher_activate_count = 0; /* reset activation count */
|
||||
}
|
||||
|
||||
@ -269,7 +246,7 @@ void* sqlcipher_memset(void *v, unsigned char value, int len) {
|
||||
|
||||
if (v == NULL) return v;
|
||||
|
||||
CODEC_TRACE("sqlcipher_memset: setting %p[0-%d]=%d)\n", a, len, value);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_memset: setting %p[0-%d]=%d)\n", a, len, value);
|
||||
for(i = 0; i < len; i++) {
|
||||
a[i] = value;
|
||||
}
|
||||
@ -313,10 +290,10 @@ void sqlcipher_mlock(void *ptr, int sz) {
|
||||
|
||||
if(ptr == NULL || sz == 0) return;
|
||||
|
||||
CODEC_TRACE("sqlcipher_mem_lock: calling mlock(%p,%lu); _SC_PAGESIZE=%lu\n", ptr - offset, sz + offset, pagesize);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_mem_lock: calling mlock(%p,%lu); _SC_PAGESIZE=%lu\n", ptr - offset, sz + offset, pagesize);
|
||||
rc = mlock(ptr - offset, sz + offset);
|
||||
if(rc!=0) {
|
||||
CODEC_TRACE("sqlcipher_mem_lock: mlock(%p,%lu) returned %d errno=%d\n", ptr - offset, sz + offset, rc, errno);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_mem_lock: mlock(%p,%lu) returned %d errno=%d\n", ptr - offset, sz + offset, rc, errno);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP))
|
||||
@ -340,10 +317,10 @@ void sqlcipher_munlock(void *ptr, int sz) {
|
||||
|
||||
if(ptr == NULL || sz == 0) return;
|
||||
|
||||
CODEC_TRACE("sqlcipher_mem_unlock: calling munlock(%p,%lu)\n", ptr - offset, sz + offset);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_mem_unlock: calling munlock(%p,%lu)\n", ptr - offset, sz + offset);
|
||||
rc = munlock(ptr - offset, sz + offset);
|
||||
if(rc!=0) {
|
||||
CODEC_TRACE("sqlcipher_mem_unlock: munlock(%p,%lu) returned %d errno=%d\n", ptr - offset, sz + offset, rc, errno);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_mem_unlock: munlock(%p,%lu) returned %d errno=%d\n", ptr - offset, sz + offset, rc, errno);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP))
|
||||
@ -367,7 +344,7 @@ void sqlcipher_munlock(void *ptr, int sz) {
|
||||
* memory segment so it can be paged
|
||||
*/
|
||||
void sqlcipher_free(void *ptr, int sz) {
|
||||
CODEC_TRACE("sqlcipher_free: calling sqlcipher_memset(%p,0,%d)\n", ptr, sz);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_free: calling sqlcipher_memset(%p,0,%d)\n", ptr, sz);
|
||||
sqlcipher_memset(ptr, 0, sz);
|
||||
sqlcipher_munlock(ptr, sz);
|
||||
sqlite3_free(ptr);
|
||||
@ -380,9 +357,9 @@ void sqlcipher_free(void *ptr, int sz) {
|
||||
*/
|
||||
void* sqlcipher_malloc(int sz) {
|
||||
void *ptr;
|
||||
CODEC_TRACE("sqlcipher_malloc: calling sqlite3Malloc(%d)\n", sz);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_malloc: calling sqlite3Malloc(%d)\n", sz);
|
||||
ptr = sqlite3Malloc(sz);
|
||||
CODEC_TRACE("sqlcipher_malloc: calling sqlcipher_memset(%p,0,%d)\n", ptr, sz);
|
||||
CODEC_TRACE_MEMORY("sqlcipher_malloc: calling sqlcipher_memset(%p,0,%d)\n", ptr, sz);
|
||||
sqlcipher_memset(ptr, 0, sz);
|
||||
sqlcipher_mlock(ptr, sz);
|
||||
return ptr;
|
||||
@ -877,15 +854,15 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, const voi
|
||||
if(ctx->provider == NULL) return SQLITE_NOMEM;
|
||||
|
||||
/* make a copy of the provider to be used for the duration of the context */
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: entering sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_enter(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: entered sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: entering SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: entered SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
|
||||
memcpy(ctx->provider, default_provider, sizeof(sqlcipher_provider));
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: leaving sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
sqlite3_mutex_leave(sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: left sqlcipher provider mutex %p\n", sqlcipher_provider_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: leaving SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_codec_ctx_init: left SQLCIPHER_MUTEX_PROVIDER\n");
|
||||
|
||||
CODEC_TRACE("sqlcipher_codec_ctx_init: calling provider ctx_init\n");
|
||||
if((rc = ctx->provider->ctx_init(&ctx->provider_ctx)) != SQLITE_OK) return rc;
|
||||
@ -1256,7 +1233,7 @@ cleanup:
|
||||
|
||||
int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *column) {
|
||||
Pgno page = 1;
|
||||
int i, rc = 0;
|
||||
int rc = 0;
|
||||
char *result;
|
||||
unsigned char *hmac_out = NULL;
|
||||
sqlite3_file *fd = sqlite3PagerFile(ctx->pBt->pBt->pPager);
|
||||
@ -1317,7 +1294,7 @@ int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *col
|
||||
}
|
||||
|
||||
if(file_sz % ctx->page_sz != 0) {
|
||||
result = sqlite3_mprintf("page %d has an invalid size of %d bytes", page, file_sz - ((file_sz / ctx->page_sz) * ctx->page_sz));
|
||||
result = sqlite3_mprintf("page %d has an invalid size of %lld bytes", page, file_sz - ((file_sz / ctx->page_sz) * ctx->page_sz));
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, result, P4_DYNAMIC);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
|
||||
}
|
||||
@ -1582,12 +1559,5 @@ const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx) {
|
||||
return ctx->provider->get_provider_version(ctx->provider_ctx);
|
||||
}
|
||||
|
||||
int sqlcipher_codec_hmac_sha1(const codec_ctx *ctx, const unsigned char *hmac_key, int key_sz,
|
||||
unsigned char* in, int in_sz, unsigned char *in2, int in2_sz,
|
||||
unsigned char *out) {
|
||||
return ctx->provider->hmac(ctx->provider_ctx, SQLCIPHER_HMAC_SHA1, (unsigned char *)hmac_key, key_sz, in, in_sz, in2, in2_sz, out);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
@ -39,7 +39,6 @@
|
||||
static prng_state prng;
|
||||
static volatile unsigned int ltc_init = 0;
|
||||
static volatile unsigned int ltc_ref_count = 0;
|
||||
static sqlite3_mutex* ltc_rand_mutex = NULL;
|
||||
|
||||
#define LTC_CIPHER "rijndael"
|
||||
|
||||
@ -48,34 +47,37 @@ static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) {
|
||||
int data_to_read = length;
|
||||
int block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
|
||||
const unsigned char * data = (const unsigned char *)buffer;
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
while(data_to_read > 0){
|
||||
rc = fortuna_add_entropy(data, block_sz, &prng);
|
||||
rc = rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK;
|
||||
if(rc != SQLITE_OK){
|
||||
break;
|
||||
}
|
||||
data_to_read -= block_sz;
|
||||
data += block_sz;
|
||||
block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
|
||||
while(data_to_read > 0){
|
||||
rc = fortuna_add_entropy(data, block_sz, &prng);
|
||||
rc = rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK;
|
||||
if(rc != SQLITE_OK){
|
||||
break;
|
||||
}
|
||||
fortuna_ready(&prng);
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
#endif
|
||||
data_to_read -= block_sz;
|
||||
data += block_sz;
|
||||
block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
|
||||
}
|
||||
fortuna_ready(&prng);
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_activate(void *ctx) {
|
||||
unsigned char random_buffer[FORTUNA_MAX_SZ];
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
if(ltc_rand_mutex == NULL){
|
||||
ltc_rand_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
}
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
|
||||
sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
|
||||
if(ltc_init == 0) {
|
||||
if(register_prng(&fortuna_desc) < 0) return SQLITE_ERROR;
|
||||
@ -86,41 +88,42 @@ static int sqlcipher_ltc_activate(void *ctx) {
|
||||
if(fortuna_start(&prng) != CRYPT_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
ltc_init = 1;
|
||||
}
|
||||
ltc_ref_count++;
|
||||
|
||||
#ifndef SQLCIPHER_TEST
|
||||
sqlite3_randomness(FORTUNA_MAX_SZ, random_buffer);
|
||||
#endif
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
#endif
|
||||
|
||||
if(sqlcipher_ltc_add_random(ctx, random_buffer, FORTUNA_MAX_SZ) != SQLITE_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_deactivate(void *ctx) {
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
|
||||
ltc_ref_count--;
|
||||
if(ltc_ref_count == 0){
|
||||
fortuna_done(&prng);
|
||||
sqlcipher_memset((void *)&prng, 0, sizeof(prng));
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
sqlite3_mutex_free(ltc_rand_mutex);
|
||||
ltc_rand_mutex = NULL;
|
||||
#endif
|
||||
}
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
else {
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -133,13 +136,16 @@ static const char* sqlcipher_ltc_get_provider_version(void *ctx) {
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_random(void *ctx, void *buffer, int length) {
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
|
||||
fortuna_read(buffer, length, &prng);
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
#endif
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_ltc_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -250,14 +256,6 @@ static int sqlcipher_ltc_get_hmac_sz(void *ctx, int algorithm) {
|
||||
return hash_descriptor[hash_idx].hashsize;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_ctx_copy(void *target_ctx, void *source_ctx) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_ctx_cmp(void *c1, void *c2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_ctx_init(void **ctx) {
|
||||
sqlcipher_ltc_activate(NULL);
|
||||
return SQLITE_OK;
|
||||
@ -272,6 +270,14 @@ static int sqlcipher_ltc_fips_status(void *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_id(void *ctx) {
|
||||
return 4658016;
|
||||
}
|
||||
|
||||
static void* sqlcipher_ltc_status(void *ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sqlcipher_ltc_setup(sqlcipher_provider *p) {
|
||||
p->activate = sqlcipher_ltc_activate;
|
||||
p->deactivate = sqlcipher_ltc_deactivate;
|
||||
@ -285,13 +291,13 @@ int sqlcipher_ltc_setup(sqlcipher_provider *p) {
|
||||
p->get_iv_sz = sqlcipher_ltc_get_iv_sz;
|
||||
p->get_block_sz = sqlcipher_ltc_get_block_sz;
|
||||
p->get_hmac_sz = sqlcipher_ltc_get_hmac_sz;
|
||||
p->ctx_copy = sqlcipher_ltc_ctx_copy;
|
||||
p->ctx_cmp = sqlcipher_ltc_ctx_cmp;
|
||||
p->ctx_init = sqlcipher_ltc_ctx_init;
|
||||
p->ctx_free = sqlcipher_ltc_ctx_free;
|
||||
p->add_random = sqlcipher_ltc_add_random;
|
||||
p->fips_status = sqlcipher_ltc_fips_status;
|
||||
p->get_provider_version = sqlcipher_ltc_get_provider_version;
|
||||
p->id = sqlcipher_ltc_id;
|
||||
p->status = sqlcipher_ltc_status;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
316
src/crypto_nss.c
Normal file
316
src/crypto_nss.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
** SQLCipher
|
||||
** http://sqlcipher.net
|
||||
**
|
||||
** Copyright (c) 2008 - 2013, ZETETIC LLC
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of the ZETETIC LLC nor the
|
||||
** names of its contributors may be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
|
||||
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
|
||||
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
*/
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
#ifdef SQLCIPHER_CRYPTO_NSS
|
||||
#include "crypto.h"
|
||||
#include "sqlcipher.h"
|
||||
#include <nss/blapit.h>
|
||||
#include <nss/nss.h>
|
||||
#include <nss/pk11pub.h>
|
||||
|
||||
static NSSInitContext* nss_init_context = NULL;
|
||||
static unsigned int nss_init_count = 0;
|
||||
|
||||
int sqlcipher_nss_setup(sqlcipher_provider *p);
|
||||
|
||||
static int sqlcipher_nss_activate(void *ctx) {
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
if (nss_init_context == NULL) {
|
||||
nss_init_context = NSS_InitContext("", "", "", "", NULL,
|
||||
NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
|
||||
NSS_INIT_FORCEOPEN | NSS_INIT_OPTIMIZESPACE | NSS_INIT_NOROOTINIT);
|
||||
}
|
||||
nss_init_count++;
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_deactivate(void *ctx) {
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
|
||||
nss_init_count--;
|
||||
if (nss_init_count == 0 && nss_init_context != NULL) {
|
||||
NSS_ShutdownContext(nss_init_context);
|
||||
nss_init_context = NULL;
|
||||
}
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_add_random(void *ctx, void *buffer, int length) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* generate a defined number of random bytes */
|
||||
static int sqlcipher_nss_random (void *ctx, void *buffer, int length) {
|
||||
// PK11_GenerateRandom should be thread-safe.
|
||||
return (PK11_GenerateRandom((unsigned char *)buffer, length) == SECSuccess) ? SQLITE_OK : SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static const char* sqlcipher_nss_get_provider_name(void *ctx) {
|
||||
return "nss";
|
||||
}
|
||||
|
||||
static const char* sqlcipher_nss_get_provider_version(void *ctx) {
|
||||
return NSS_GetVersion();
|
||||
}
|
||||
|
||||
static const char* sqlcipher_nss_get_cipher(void *ctx) {
|
||||
return "aes-256-cbc";
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_get_key_sz(void *ctx) {
|
||||
return AES_256_KEY_LENGTH;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_get_iv_sz(void *ctx) {
|
||||
return AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_get_block_sz(void *ctx) {
|
||||
return AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_get_hmac_sz(void *ctx, int algorithm) {
|
||||
switch(algorithm) {
|
||||
case SQLCIPHER_HMAC_SHA1:
|
||||
return SHA1_LENGTH;
|
||||
break;
|
||||
case SQLCIPHER_HMAC_SHA256:
|
||||
return SHA256_LENGTH;
|
||||
break;
|
||||
case SQLCIPHER_HMAC_SHA512:
|
||||
return SHA512_LENGTH;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) {
|
||||
int rc = SQLITE_OK;
|
||||
unsigned int length;
|
||||
unsigned int outLen;
|
||||
PK11Context* context = NULL;
|
||||
PK11SlotInfo * slot = NULL;
|
||||
PK11SymKey* symKey = NULL;
|
||||
if(in == NULL) goto error;
|
||||
CK_MECHANISM_TYPE mech;
|
||||
switch(algorithm) {
|
||||
case SQLCIPHER_HMAC_SHA1:
|
||||
mech = CKM_SHA_1_HMAC;
|
||||
break;
|
||||
case SQLCIPHER_HMAC_SHA256:
|
||||
mech = CKM_SHA256_HMAC;
|
||||
break;
|
||||
case SQLCIPHER_HMAC_SHA512:
|
||||
mech = CKM_SHA512_HMAC;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
length = sqlcipher_nss_get_hmac_sz(ctx, algorithm);
|
||||
slot = PK11_GetInternalSlot();
|
||||
if (slot == NULL) goto error;
|
||||
SECItem keyItem;
|
||||
keyItem.data = hmac_key;
|
||||
keyItem.len = key_sz;
|
||||
symKey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
|
||||
CKA_SIGN, &keyItem, NULL);
|
||||
if (symKey == NULL) goto error;
|
||||
SECItem noParams;
|
||||
noParams.data = 0;
|
||||
noParams.len = 0;
|
||||
context = PK11_CreateContextBySymKey(mech, CKA_SIGN, symKey, &noParams);
|
||||
if (context == NULL) goto error;
|
||||
if (PK11_DigestBegin(context) != SECSuccess) goto error;
|
||||
if (PK11_DigestOp(context, in, in_sz) != SECSuccess) goto error;
|
||||
if (in2 != NULL) {
|
||||
if (PK11_DigestOp(context, in2, in2_sz) != SECSuccess) goto error;
|
||||
}
|
||||
if (PK11_DigestFinal(context, out, &outLen, length) != SECSuccess) goto error;
|
||||
|
||||
goto cleanup;
|
||||
error:
|
||||
rc = SQLITE_ERROR;
|
||||
cleanup:
|
||||
if (context) PK11_DestroyContext(context, PR_TRUE);
|
||||
if (symKey) PK11_FreeSymKey(symKey);
|
||||
if (slot) PK11_FreeSlot(slot);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
|
||||
int rc = SQLITE_OK;
|
||||
PK11SlotInfo * slot = NULL;
|
||||
SECAlgorithmID * algid = NULL;
|
||||
PK11SymKey* symKey = NULL;
|
||||
SECOidTag oidtag;
|
||||
switch(algorithm) {
|
||||
case SQLCIPHER_HMAC_SHA1:
|
||||
oidtag = SEC_OID_HMAC_SHA1;
|
||||
break;
|
||||
case SQLCIPHER_HMAC_SHA256:
|
||||
oidtag = SEC_OID_HMAC_SHA256;
|
||||
break;
|
||||
case SQLCIPHER_HMAC_SHA512:
|
||||
oidtag = SEC_OID_HMAC_SHA512;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
SECItem secSalt;
|
||||
secSalt.data = salt;
|
||||
secSalt.len = salt_sz;
|
||||
// Always pass SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this parameter
|
||||
// is unused for key generation. It is currently only used
|
||||
// for PBKDF2 authentication or key (un)wrapping when specifying an
|
||||
// encryption algorithm (PBES2).
|
||||
algid = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
|
||||
oidtag, key_sz, workfactor, &secSalt);
|
||||
if (algid == NULL) goto error;
|
||||
slot = PK11_GetInternalSlot();
|
||||
if (slot == NULL) goto error;
|
||||
SECItem pwItem;
|
||||
pwItem.data = (unsigned char *) pass; // PK11_PBEKeyGen doesn't modify the key.
|
||||
pwItem.len = pass_sz;
|
||||
symKey = PK11_PBEKeyGen(slot, algid, &pwItem, PR_FALSE, NULL);
|
||||
if (symKey == NULL) goto error;
|
||||
if (PK11_ExtractKeyValue(symKey) != SECSuccess) goto error;
|
||||
// No need to free keyData as it is a buffer managed by symKey.
|
||||
SECItem* keyData = PK11_GetKeyData(symKey);
|
||||
if (keyData == NULL) goto error;
|
||||
memcpy(key, keyData->data, key_sz);
|
||||
|
||||
goto cleanup;
|
||||
error:
|
||||
rc = SQLITE_ERROR;
|
||||
cleanup:
|
||||
if (slot) PK11_FreeSlot(slot);
|
||||
if (algid) SECOID_DestroyAlgorithmID(algid, PR_TRUE);
|
||||
if (symKey) PK11_FreeSymKey(symKey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) {
|
||||
int rc = SQLITE_OK;
|
||||
PK11SlotInfo * slot = NULL;
|
||||
PK11SymKey* symKey = NULL;
|
||||
unsigned int outLen;
|
||||
SECItem params;
|
||||
params.data = iv;
|
||||
params.len = sqlcipher_nss_get_iv_sz(ctx);
|
||||
slot = PK11_GetInternalSlot();
|
||||
if (slot == NULL) goto error;
|
||||
SECItem keyItem;
|
||||
keyItem.data = key;
|
||||
keyItem.len = key_sz;
|
||||
symKey = PK11_ImportSymKey(slot, CKM_AES_CBC, PK11_OriginUnwrap,
|
||||
CKA_ENCRYPT, &keyItem, NULL);
|
||||
if (symKey == NULL) goto error;
|
||||
SECStatus rv;
|
||||
if (mode == CIPHER_ENCRYPT) {
|
||||
rv = PK11_Encrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen,
|
||||
in_sz + 16, in, in_sz);
|
||||
} else {
|
||||
rv = PK11_Decrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen,
|
||||
in_sz + 16, in, in_sz);
|
||||
}
|
||||
if (rv != SECSuccess) goto error;
|
||||
|
||||
goto cleanup;
|
||||
error:
|
||||
rc = SQLITE_ERROR;
|
||||
cleanup:
|
||||
if (slot) PK11_FreeSlot(slot);
|
||||
if (symKey) PK11_FreeSymKey(symKey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_ctx_init(void **ctx) {
|
||||
sqlcipher_nss_activate(NULL);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_ctx_free(void **ctx) {
|
||||
sqlcipher_nss_deactivate(NULL);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_fips_status(void *ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_id(void *ctx) {
|
||||
return 6342402;
|
||||
}
|
||||
|
||||
static void* sqlcipher_nss_status(void *ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sqlcipher_nss_setup(sqlcipher_provider *p) {
|
||||
p->activate = sqlcipher_nss_activate;
|
||||
p->deactivate = sqlcipher_nss_deactivate;
|
||||
p->random = sqlcipher_nss_random;
|
||||
p->get_provider_name = sqlcipher_nss_get_provider_name;
|
||||
p->hmac = sqlcipher_nss_hmac;
|
||||
p->kdf = sqlcipher_nss_kdf;
|
||||
p->cipher = sqlcipher_nss_cipher;
|
||||
p->get_cipher = sqlcipher_nss_get_cipher;
|
||||
p->get_key_sz = sqlcipher_nss_get_key_sz;
|
||||
p->get_iv_sz = sqlcipher_nss_get_iv_sz;
|
||||
p->get_block_sz = sqlcipher_nss_get_block_sz;
|
||||
p->get_hmac_sz = sqlcipher_nss_get_hmac_sz;
|
||||
p->ctx_init = sqlcipher_nss_ctx_init;
|
||||
p->ctx_free = sqlcipher_nss_ctx_free;
|
||||
p->add_random = sqlcipher_nss_add_random;
|
||||
p->fips_status = sqlcipher_nss_fips_status;
|
||||
p->get_provider_version = sqlcipher_nss_get_provider_version;
|
||||
p->id = sqlcipher_nss_id;
|
||||
p->status = sqlcipher_nss_status;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
@ -36,6 +36,7 @@
|
||||
#include "sqlcipher.h"
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/objects.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
@ -45,7 +46,6 @@ typedef struct {
|
||||
|
||||
static unsigned int openssl_external_init = 0;
|
||||
static unsigned int openssl_init_count = 0;
|
||||
static sqlite3_mutex* openssl_rand_mutex = NULL;
|
||||
|
||||
#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
|
||||
static HMAC_CTX *HMAC_CTX_new(void)
|
||||
@ -72,15 +72,15 @@ static void HMAC_CTX_free(HMAC_CTX *ctx)
|
||||
|
||||
static int sqlcipher_openssl_add_random(void *ctx, void *buffer, int length) {
|
||||
#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: entering openssl_rand_mutex %p\n", openssl_rand_mutex);
|
||||
sqlite3_mutex_enter(openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: entered openssl_rand_mutex %p\n", openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
#endif
|
||||
RAND_add(buffer, length, 0);
|
||||
#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: leaving openssl_rand_mutex %p\n", openssl_rand_mutex);
|
||||
sqlite3_mutex_leave(openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: left openssl_rand_mutex %p\n", openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -98,9 +98,10 @@ static int sqlcipher_openssl_activate(void *ctx) {
|
||||
/* initialize openssl and increment the internal init counter
|
||||
but only if it hasn't been initalized outside of SQLCipher by this program
|
||||
e.g. on startup */
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: entering static master mutex");
|
||||
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: entered static master mutex");
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
|
||||
if(openssl_init_count == 0 && EVP_get_cipherbyname(OPENSSL_CIPHER) != NULL) {
|
||||
/* if openssl has not yet been initialized by this library, but
|
||||
@ -112,8 +113,15 @@ static int sqlcipher_openssl_activate(void *ctx) {
|
||||
#ifdef SQLCIPHER_FIPS
|
||||
if(!FIPS_mode()){
|
||||
if(!FIPS_mode_set(1)){
|
||||
unsigned long err = 0;
|
||||
ERR_load_crypto_strings();
|
||||
#ifdef __ANDROID__
|
||||
while((err = ERR_get_error()) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "sqlcipher","error: %lx. %s.", err, ERR_error_string(err, NULL));
|
||||
}
|
||||
#else
|
||||
ERR_print_errors_fp(stderr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -125,19 +133,10 @@ static int sqlcipher_openssl_activate(void *ctx) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND
|
||||
if(openssl_rand_mutex == NULL) {
|
||||
/* allocate a mutex to guard against concurrent calls to RAND_bytes() */
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: allocating openssl_rand_mutex");
|
||||
openssl_rand_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: allocated openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
openssl_init_count++;
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: leaving static master mutex");
|
||||
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: left static master mutex");
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -145,12 +144,13 @@ static int sqlcipher_openssl_activate(void *ctx) {
|
||||
freeing the EVP structures on the final deactivation to ensure that
|
||||
OpenSSL memory is cleaned up */
|
||||
static int sqlcipher_openssl_deactivate(void *ctx) {
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: entering static master mutex");
|
||||
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: entered static master mutex");
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
openssl_init_count--;
|
||||
|
||||
if(openssl_init_count == 0) {
|
||||
sqlite3_mutex *temp_mutex;
|
||||
if(openssl_external_init == 0) {
|
||||
/* if OpenSSL hasn't be initialized externally, and the counter reaches zero
|
||||
after it's decremented, release EVP memory
|
||||
@ -163,16 +163,11 @@ static int sqlcipher_openssl_deactivate(void *ctx) {
|
||||
} else {
|
||||
openssl_external_init = 0;
|
||||
}
|
||||
#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: freeing openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
sqlite3_mutex_free(openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: freed openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
openssl_rand_mutex = NULL;
|
||||
#endif
|
||||
}
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: leaving static master mutex");
|
||||
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: left static master mutex");
|
||||
}
|
||||
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE\n");
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -194,15 +189,15 @@ static int sqlcipher_openssl_random (void *ctx, void *buffer, int length) {
|
||||
but a more proper solution is that applications setup platform-appropriate
|
||||
thread saftey in openssl externally */
|
||||
#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: entering openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
sqlite3_mutex_enter(openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: entered openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
#endif
|
||||
rc = RAND_bytes((unsigned char *)buffer, length);
|
||||
#ifndef SQLCIPHER_OPENSSL_NO_MUTEX_RAND
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: leaving openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
sqlite3_mutex_leave(openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: left openssl_rand_mutex %p", openssl_rand_mutex);
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
CODEC_TRACE_MUTEX("sqlcipher_openssl_random: left SQLCIPHER_MUTEX_PROVIDER_RAND\n");
|
||||
#endif
|
||||
return (rc == 1) ? SQLITE_OK : SQLITE_ERROR;
|
||||
}
|
||||
@ -292,7 +287,7 @@ cleanup:
|
||||
}
|
||||
|
||||
static const char* sqlcipher_openssl_get_cipher(void *ctx) {
|
||||
return EVP_CIPHER_name(((openssl_ctx *)ctx)->evp_cipher);
|
||||
return OBJ_nid2sn(EVP_CIPHER_nid(((openssl_ctx *)ctx)->evp_cipher));
|
||||
}
|
||||
|
||||
static int sqlcipher_openssl_get_key_sz(void *ctx) {
|
||||
@ -323,15 +318,6 @@ static int sqlcipher_openssl_get_hmac_sz(void *ctx, int algorithm) {
|
||||
}
|
||||
}
|
||||
|
||||
static int sqlcipher_openssl_ctx_copy(void *target_ctx, void *source_ctx) {
|
||||
memcpy(target_ctx, source_ctx, sizeof(openssl_ctx));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_openssl_ctx_cmp(void *c1, void *c2) {
|
||||
return ((openssl_ctx *)c1)->evp_cipher == ((openssl_ctx *)c2)->evp_cipher;
|
||||
}
|
||||
|
||||
static int sqlcipher_openssl_ctx_init(void **ctx) {
|
||||
openssl_ctx *o_ctx;
|
||||
|
||||
@ -358,6 +344,14 @@ static int sqlcipher_openssl_fips_status(void *ctx) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sqlcipher_openssl_id(void *ctx) {
|
||||
return 2678498;
|
||||
}
|
||||
|
||||
static void* sqlcipher_openssl_status(void *ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sqlcipher_openssl_setup(sqlcipher_provider *p) {
|
||||
p->activate = sqlcipher_openssl_activate;
|
||||
p->deactivate = sqlcipher_openssl_deactivate;
|
||||
@ -371,13 +365,13 @@ int sqlcipher_openssl_setup(sqlcipher_provider *p) {
|
||||
p->get_iv_sz = sqlcipher_openssl_get_iv_sz;
|
||||
p->get_block_sz = sqlcipher_openssl_get_block_sz;
|
||||
p->get_hmac_sz = sqlcipher_openssl_get_hmac_sz;
|
||||
p->ctx_copy = sqlcipher_openssl_ctx_copy;
|
||||
p->ctx_cmp = sqlcipher_openssl_ctx_cmp;
|
||||
p->ctx_init = sqlcipher_openssl_ctx_init;
|
||||
p->ctx_free = sqlcipher_openssl_ctx_free;
|
||||
p->add_random = sqlcipher_openssl_add_random;
|
||||
p->fips_status = sqlcipher_openssl_fips_status;
|
||||
p->get_provider_version = sqlcipher_openssl_get_provider_version;
|
||||
p->id = sqlcipher_openssl_id;
|
||||
p->status = sqlcipher_openssl_status;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
** SQLite was built with.
|
||||
*/
|
||||
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */
|
||||
|
||||
/*
|
||||
** Include the configuration header output by 'configure' if we're using the
|
||||
@ -306,8 +306,6 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT4)
|
||||
"ENABLE_STAT4",
|
||||
#elif defined(SQLITE_ENABLE_STAT3)
|
||||
"ENABLE_STAT3",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_STMTVTAB
|
||||
"ENABLE_STMTVTAB",
|
||||
|
@ -388,7 +388,7 @@ static int parseDateOrTime(
|
||||
return 0;
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
|
||||
return setDateTimeToCurrent(context, p);
|
||||
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
|
||||
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){
|
||||
setRawDateNumber(p, r);
|
||||
return 0;
|
||||
}
|
||||
@ -722,7 +722,7 @@ static int parseModifier(
|
||||
** date is already on the appropriate weekday, this is a no-op.
|
||||
*/
|
||||
if( sqlite3_strnicmp(z, "weekday ", 8)==0
|
||||
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
|
||||
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0
|
||||
&& (n=(int)r)==r && n>=0 && r<7 ){
|
||||
sqlite3_int64 Z;
|
||||
computeYMD_HMS(p);
|
||||
@ -781,7 +781,7 @@ static int parseModifier(
|
||||
double rRounder;
|
||||
int i;
|
||||
for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
|
||||
if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
|
||||
if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
621
src/expr.c
621
src/expr.c
File diff suppressed because it is too large
Load Diff
17
src/fkey.c
17
src/fkey.c
@ -478,13 +478,13 @@ static Expr *exprTableRegister(
|
||||
if( iCol>=0 && iCol!=pTab->iPKey ){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pExpr->iTable = regBase + iCol + 1;
|
||||
pExpr->affinity = pCol->affinity;
|
||||
pExpr->affExpr = pCol->affinity;
|
||||
zColl = pCol->zColl;
|
||||
if( zColl==0 ) zColl = db->pDfltColl->zName;
|
||||
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
|
||||
}else{
|
||||
pExpr->iTable = regBase;
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
}
|
||||
return pExpr;
|
||||
@ -591,7 +591,7 @@ static void fkScanChildren(
|
||||
zCol = pFKey->pFrom->aCol[iCol].zName;
|
||||
pRight = sqlite3Expr(db, TK_ID, zCol);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
pWhere = sqlite3ExprAnd(pParse, pWhere, pEq);
|
||||
}
|
||||
|
||||
/* If the child table is the same as the parent table, then add terms
|
||||
@ -625,11 +625,11 @@ static void fkScanChildren(
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
||||
pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName);
|
||||
pEq = sqlite3PExpr(pParse, TK_IS, pLeft, pRight);
|
||||
pAll = sqlite3ExprAnd(db, pAll, pEq);
|
||||
pAll = sqlite3ExprAnd(pParse, pAll, pEq);
|
||||
}
|
||||
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0);
|
||||
}
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pNe);
|
||||
pWhere = sqlite3ExprAnd(pParse, pWhere, pNe);
|
||||
}
|
||||
|
||||
/* Resolve the references in the WHERE clause. */
|
||||
@ -1235,7 +1235,7 @@ static Trigger *fkActionTrigger(
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0)
|
||||
);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
pWhere = sqlite3ExprAnd(pParse, pWhere, pEq);
|
||||
|
||||
/* For ON UPDATE, construct the next term of the WHEN clause.
|
||||
** The final WHEN clause will be like this:
|
||||
@ -1251,7 +1251,7 @@ static Trigger *fkActionTrigger(
|
||||
sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0))
|
||||
);
|
||||
pWhen = sqlite3ExprAnd(db, pWhen, pEq);
|
||||
pWhen = sqlite3ExprAnd(pParse, pWhen, pEq);
|
||||
}
|
||||
|
||||
if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){
|
||||
@ -1287,7 +1287,7 @@ static Trigger *fkActionTrigger(
|
||||
tFrom.n = nFrom;
|
||||
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
|
||||
if( pRaise ){
|
||||
pRaise->affinity = OE_Abort;
|
||||
pRaise->affExpr = OE_Abort;
|
||||
}
|
||||
pSelect = sqlite3SelectNew(pParse,
|
||||
sqlite3ExprListAppend(pParse, 0, pRaise),
|
||||
@ -1332,6 +1332,7 @@ static Trigger *fkActionTrigger(
|
||||
return 0;
|
||||
}
|
||||
assert( pStep!=0 );
|
||||
assert( pTrigger!=0 );
|
||||
|
||||
switch( action ){
|
||||
case OE_Restrict:
|
||||
|
85
src/func.c
85
src/func.c
@ -16,6 +16,7 @@
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "vdbeInt.h"
|
||||
|
||||
/*
|
||||
@ -202,6 +203,8 @@ static void instrFunc(
|
||||
int N = 1;
|
||||
int isText;
|
||||
unsigned char firstChar;
|
||||
sqlite3_value *pC1 = 0;
|
||||
sqlite3_value *pC2 = 0;
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
typeHaystack = sqlite3_value_type(argv[0]);
|
||||
@ -214,12 +217,22 @@ static void instrFunc(
|
||||
zHaystack = sqlite3_value_blob(argv[0]);
|
||||
zNeedle = sqlite3_value_blob(argv[1]);
|
||||
isText = 0;
|
||||
}else{
|
||||
}else if( typeHaystack!=SQLITE_BLOB && typeNeedle!=SQLITE_BLOB ){
|
||||
zHaystack = sqlite3_value_text(argv[0]);
|
||||
zNeedle = sqlite3_value_text(argv[1]);
|
||||
isText = 1;
|
||||
}else{
|
||||
pC1 = sqlite3_value_dup(argv[0]);
|
||||
zHaystack = sqlite3_value_text(pC1);
|
||||
if( zHaystack==0 ) goto endInstrOOM;
|
||||
nHaystack = sqlite3_value_bytes(pC1);
|
||||
pC2 = sqlite3_value_dup(argv[1]);
|
||||
zNeedle = sqlite3_value_text(pC2);
|
||||
if( zNeedle==0 ) goto endInstrOOM;
|
||||
nNeedle = sqlite3_value_bytes(pC2);
|
||||
isText = 1;
|
||||
}
|
||||
if( zNeedle==0 || (nHaystack && zHaystack==0) ) return;
|
||||
if( zNeedle==0 || (nHaystack && zHaystack==0) ) goto endInstrOOM;
|
||||
firstChar = zNeedle[0];
|
||||
while( nNeedle<=nHaystack
|
||||
&& (zHaystack[0]!=firstChar || memcmp(zHaystack, zNeedle, nNeedle)!=0)
|
||||
@ -233,6 +246,13 @@ static void instrFunc(
|
||||
if( nNeedle>nHaystack ) N = 0;
|
||||
}
|
||||
sqlite3_result_int(context, N);
|
||||
endInstr:
|
||||
sqlite3_value_free(pC1);
|
||||
sqlite3_value_free(pC2);
|
||||
return;
|
||||
endInstrOOM:
|
||||
sqlite3_result_error_nomem(context);
|
||||
goto endInstr;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -386,10 +406,10 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
** handle the rounding directly,
|
||||
** otherwise use printf.
|
||||
*/
|
||||
if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
|
||||
r = (double)((sqlite_int64)(r+0.5));
|
||||
}else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
|
||||
r = -(double)((sqlite_int64)((-r)+0.5));
|
||||
if( r<-4503599627370496.0 || r>+4503599627370496.0 ){
|
||||
/* The value has no fractional part so there is nothing to round */
|
||||
}else if( n==0 ){
|
||||
r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5)));
|
||||
}else{
|
||||
zBuf = sqlite3_mprintf("%.*f",n,r);
|
||||
if( zBuf==0 ){
|
||||
@ -843,8 +863,6 @@ static void likeFunc(
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
zB = sqlite3_value_text(argv[0]);
|
||||
zA = sqlite3_value_text(argv[1]);
|
||||
|
||||
/* Limit the length of the LIKE or GLOB pattern to avoid problems
|
||||
** of deep recursion and N*N behavior in patternCompare().
|
||||
@ -856,8 +874,6 @@ static void likeFunc(
|
||||
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
|
||||
return;
|
||||
}
|
||||
assert( zB==sqlite3_value_text(argv[0]) ); /* Encoding did not change */
|
||||
|
||||
if( argc==3 ){
|
||||
/* The escape character string must consist of a single UTF-8 character.
|
||||
** Otherwise, return an error.
|
||||
@ -873,6 +889,8 @@ static void likeFunc(
|
||||
}else{
|
||||
escape = pInfo->matchSet;
|
||||
}
|
||||
zB = sqlite3_value_text(argv[0]);
|
||||
zA = sqlite3_value_text(argv[1]);
|
||||
if( zA && zB ){
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_like_count++;
|
||||
@ -1791,13 +1809,6 @@ static void groupConcatValue(sqlite3_context *context){
|
||||
*/
|
||||
void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){
|
||||
int rc = sqlite3_overload_function(db, "MATCH", 2);
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
#ifndef OMIT_EXPORT
|
||||
extern void sqlcipher_exportFunc(sqlite3_context *, int, sqlite3_value **);
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
@ -1805,46 +1816,37 @@ void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
#ifndef OMIT_EXPORT
|
||||
sqlite3CreateFunc(db, "sqlcipher_export", -1, SQLITE_TEXT, 0, sqlcipher_exportFunc, 0, 0, 0, 0, 0);
|
||||
{
|
||||
extern void sqlcipher_exportFunc(sqlite3_context *, int, sqlite3_value **);
|
||||
sqlite3CreateFunc(db, "sqlcipher_export", -1, SQLITE_TEXT, 0, sqlcipher_exportFunc, 0, 0, 0, 0, 0);
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLCIPHER_EXT
|
||||
#include "sqlcipher_funcs_init.h"
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the LIKEOPT flag on the 2-argument function with the given name.
|
||||
*/
|
||||
static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
|
||||
FuncDef *pDef;
|
||||
pDef = sqlite3FindFunction(db, zName, 2, SQLITE_UTF8, 0);
|
||||
if( ALWAYS(pDef) ){
|
||||
pDef->funcFlags |= flagVal;
|
||||
}
|
||||
pDef = sqlite3FindFunction(db, zName, 3, SQLITE_UTF8, 0);
|
||||
if( pDef ){
|
||||
pDef->funcFlags |= flagVal;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the built-in LIKE and GLOB functions. The caseSensitive
|
||||
** Re-register the built-in LIKE functions. The caseSensitive
|
||||
** parameter determines whether or not the LIKE operator is case
|
||||
** sensitive. GLOB is always case sensitive.
|
||||
** sensitive.
|
||||
*/
|
||||
void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
struct compareInfo *pInfo;
|
||||
int flags;
|
||||
if( caseSensitive ){
|
||||
pInfo = (struct compareInfo*)&likeInfoAlt;
|
||||
flags = SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE;
|
||||
}else{
|
||||
pInfo = (struct compareInfo*)&likeInfoNorm;
|
||||
flags = SQLITE_FUNC_LIKE;
|
||||
}
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0, 0, 0, 0, 0);
|
||||
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
|
||||
setLikeOptFlag(db, "like",
|
||||
caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
|
||||
sqlite3FindFunction(db, "like", 2, SQLITE_UTF8, 0)->funcFlags |= flags;
|
||||
sqlite3FindFunction(db, "like", 3, SQLITE_UTF8, 0)->funcFlags |= flags;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2016,9 +2018,6 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
sqlite3AlterFunctions();
|
||||
#endif
|
||||
sqlite3WindowFunctions();
|
||||
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
||||
sqlite3AnalyzeFunctions();
|
||||
#endif
|
||||
sqlite3RegisterDateTimeFunctions();
|
||||
sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
|
||||
|
||||
|
19
src/global.c
19
src/global.c
@ -153,8 +153,15 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if
|
||||
** that compile-time option is omitted.
|
||||
*/
|
||||
#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN
|
||||
#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN)
|
||||
# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
|
||||
#else
|
||||
# if !SQLITE_ALLOW_COVERING_INDEX_SCAN
|
||||
# error "Compile-time disabling of covering index scan using the\
|
||||
-DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\
|
||||
Contact SQLite developers if this is a problem for you, and\
|
||||
delete this #error macro to continue with your build."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* The minimum PMA size is set to this value multiplied by the database
|
||||
@ -207,6 +214,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_USE_URI, /* bOpenUri */
|
||||
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
|
||||
0, /* bSmallMalloc */
|
||||
1, /* bExtraSchemaChecks */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
0, /* neverCorrupt */
|
||||
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
|
||||
@ -253,6 +261,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* bInternalFunctions */
|
||||
0x7ffffffe, /* iOnceResetThreshold */
|
||||
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
|
||||
0, /* iPrngSeed */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -262,14 +271,6 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
*/
|
||||
FuncDefHash sqlite3BuiltinFunctions;
|
||||
|
||||
/*
|
||||
** Constant tokens for values 0 and 1.
|
||||
*/
|
||||
const Token sqlite3IntTokens[] = {
|
||||
{ "0", 1 },
|
||||
{ "1", 1 }
|
||||
};
|
||||
|
||||
#ifdef VDBE_PROFILE
|
||||
/*
|
||||
** The following performance counter can be used in place of
|
||||
|
53
src/insert.c
53
src/insert.c
@ -88,18 +88,19 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
|
||||
}
|
||||
for(n=0; n<pIdx->nColumn; n++){
|
||||
i16 x = pIdx->aiColumn[n];
|
||||
char aff;
|
||||
if( x>=0 ){
|
||||
pIdx->zColAff[n] = pTab->aCol[x].affinity;
|
||||
aff = pTab->aCol[x].affinity;
|
||||
}else if( x==XN_ROWID ){
|
||||
pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
|
||||
aff = SQLITE_AFF_INTEGER;
|
||||
}else{
|
||||
char aff;
|
||||
assert( x==XN_EXPR );
|
||||
assert( pIdx->aColExpr!=0 );
|
||||
aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
|
||||
if( aff==0 ) aff = SQLITE_AFF_BLOB;
|
||||
pIdx->zColAff[n] = aff;
|
||||
}
|
||||
if( aff<SQLITE_AFF_BLOB ) aff = SQLITE_AFF_BLOB;
|
||||
if( aff>SQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC;
|
||||
pIdx->zColAff[n] = aff;
|
||||
}
|
||||
pIdx->zColAff[n] = 0;
|
||||
}
|
||||
@ -139,11 +140,12 @@ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
|
||||
}
|
||||
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
assert( pTab->aCol[i].affinity!=0 );
|
||||
zColAff[i] = pTab->aCol[i].affinity;
|
||||
}
|
||||
do{
|
||||
zColAff[i--] = 0;
|
||||
}while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
|
||||
}while( i>=0 && zColAff[i]<=SQLITE_AFF_BLOB );
|
||||
pTab->zColAff = zColAff;
|
||||
}
|
||||
assert( zColAff!=0 );
|
||||
@ -814,7 +816,7 @@ void sqlite3Insert(
|
||||
int nIdx;
|
||||
nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
|
||||
&iDataCur, &iIdxCur);
|
||||
aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1));
|
||||
aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+2));
|
||||
if( aRegIdx==0 ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
@ -823,6 +825,7 @@ void sqlite3Insert(
|
||||
aRegIdx[i] = ++pParse->nMem;
|
||||
pParse->nMem += pIdx->nColumn;
|
||||
}
|
||||
aRegIdx[i] = ++pParse->nMem; /* Register to store the table record */
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pUpsert ){
|
||||
@ -831,6 +834,9 @@ void sqlite3Insert(
|
||||
pTab->zName);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
pUpsert->pUpsertSrc = pTabList;
|
||||
pUpsert->regData = regData;
|
||||
@ -1226,6 +1232,14 @@ int sqlite3ExprReferencesUpdatedColumn(
|
||||
** the same as the order of indices on the linked list of indices
|
||||
** at pTab->pIndex.
|
||||
**
|
||||
** (2019-05-07) The generated code also creates a new record for the
|
||||
** main table, if pTab is a rowid table, and stores that record in the
|
||||
** register identified by aRegIdx[nIdx] - in other words in the first
|
||||
** entry of aRegIdx[] past the last index. It is important that the
|
||||
** record be generated during constraint checks to avoid affinity changes
|
||||
** to the register content that occur after constraint checks but before
|
||||
** the new record is inserted.
|
||||
**
|
||||
** The caller must have already opened writeable cursors on the main
|
||||
** table and all applicable indices (that is to say, all indices for which
|
||||
** aRegIdx[] is not zero). iDataCur is the cursor for the main table when
|
||||
@ -1416,7 +1430,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
}else{
|
||||
char *zName = pCheck->a[i].zName;
|
||||
if( zName==0 ) zName = pTab->zName;
|
||||
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
|
||||
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-26383-51744 */
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
|
||||
onError, zName, P4_TRANSIENT,
|
||||
P5_ConstraintCheck);
|
||||
@ -1845,6 +1859,16 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3VdbeJumpHere(v, ipkBottom);
|
||||
}
|
||||
|
||||
/* Generate the table record */
|
||||
if( HasRowid(pTab) ){
|
||||
int regRec = aRegIdx[ix];
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData+1, pTab->nCol, regRec);
|
||||
sqlite3SetMakeRecordP5(v, pTab);
|
||||
if( !bAffinityDone ){
|
||||
sqlite3TableAffinity(v, pTab, 0);
|
||||
}
|
||||
}
|
||||
|
||||
*pbMayReplace = seenReplace;
|
||||
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
|
||||
}
|
||||
@ -1894,10 +1918,7 @@ void sqlite3CompleteInsertion(
|
||||
Vdbe *v; /* Prepared statements under construction */
|
||||
Index *pIdx; /* An index being inserted or updated */
|
||||
u8 pik_flags; /* flag values passed to the btree insert */
|
||||
int regData; /* Content registers (after the rowid) */
|
||||
int regRec; /* Register holding assembled record for the table */
|
||||
int i; /* Loop counter */
|
||||
u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
|
||||
|
||||
assert( update_flags==0
|
||||
|| update_flags==OPFLAG_ISUPDATE
|
||||
@ -1909,7 +1930,6 @@ void sqlite3CompleteInsertion(
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
if( aRegIdx[i]==0 ) continue;
|
||||
bAffinityDone = 1;
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
@ -1937,13 +1957,6 @@ void sqlite3CompleteInsertion(
|
||||
sqlite3VdbeChangeP5(v, pik_flags);
|
||||
}
|
||||
if( !HasRowid(pTab) ) return;
|
||||
regData = regNewData + 1;
|
||||
regRec = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
|
||||
sqlite3SetMakeRecordP5(v, pTab);
|
||||
if( !bAffinityDone ){
|
||||
sqlite3TableAffinity(v, pTab, 0);
|
||||
}
|
||||
if( pParse->nested ){
|
||||
pik_flags = 0;
|
||||
}else{
|
||||
@ -1956,7 +1969,7 @@ void sqlite3CompleteInsertion(
|
||||
if( useSeekResult ){
|
||||
pik_flags |= OPFLAG_USESEEKRESULT;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, aRegIdx[i], regNewData);
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
||||
}
|
||||
|
@ -461,7 +461,13 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
#endif
|
||||
/* Version 3.28.0 and later */
|
||||
sqlite3_stmt_isexplain,
|
||||
sqlite3_value_frombind
|
||||
sqlite3_value_frombind,
|
||||
/* Version 3.30.0 and later */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_drop_modules,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
160
src/main.c
160
src/main.c
@ -836,6 +836,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
} aFlagOp[] = {
|
||||
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
|
||||
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
|
||||
{ SQLITE_DBCONFIG_ENABLE_VIEW, SQLITE_EnableView },
|
||||
{ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
|
||||
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
|
||||
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
|
||||
@ -845,6 +846,9 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
{ SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive },
|
||||
{ SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema|
|
||||
SQLITE_NoSchemaError },
|
||||
{ SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, SQLITE_LegacyAlter },
|
||||
{ SQLITE_DBCONFIG_DQS_DDL, SQLITE_DqsDDL },
|
||||
{ SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML },
|
||||
};
|
||||
unsigned int i;
|
||||
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
||||
@ -875,28 +879,17 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return true if the buffer z[0..n-1] contains all spaces.
|
||||
*/
|
||||
static int allSpaces(const char *z, int n){
|
||||
while( n>0 && z[n-1]==' ' ){ n--; }
|
||||
return n==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the default collating function named "BINARY" which is always
|
||||
** available.
|
||||
**
|
||||
** If the padFlag argument is not NULL then space padding at the end
|
||||
** of strings is ignored. This implements the RTRIM collation.
|
||||
*/
|
||||
static int binCollFunc(
|
||||
void *padFlag,
|
||||
void *NotUsed,
|
||||
int nKey1, const void *pKey1,
|
||||
int nKey2, const void *pKey2
|
||||
){
|
||||
int rc, n;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
n = nKey1<nKey2 ? nKey1 : nKey2;
|
||||
/* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares
|
||||
** strings byte by byte using the memcmp() function from the standard C
|
||||
@ -904,29 +897,33 @@ static int binCollFunc(
|
||||
assert( pKey1 && pKey2 );
|
||||
rc = memcmp(pKey1, pKey2, n);
|
||||
if( rc==0 ){
|
||||
if( padFlag
|
||||
&& allSpaces(((char*)pKey1)+n, nKey1-n)
|
||||
&& allSpaces(((char*)pKey2)+n, nKey2-n)
|
||||
){
|
||||
/* EVIDENCE-OF: R-31624-24737 RTRIM is like BINARY except that extra
|
||||
** spaces at the end of either string do not change the result. In other
|
||||
** words, strings will compare equal to one another as long as they
|
||||
** differ only in the number of spaces at the end.
|
||||
*/
|
||||
}else{
|
||||
rc = nKey1 - nKey2;
|
||||
}
|
||||
rc = nKey1 - nKey2;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the collating function named "RTRIM" which is always
|
||||
** available. Ignore trailing spaces.
|
||||
*/
|
||||
static int rtrimCollFunc(
|
||||
void *pUser,
|
||||
int nKey1, const void *pKey1,
|
||||
int nKey2, const void *pKey2
|
||||
){
|
||||
const u8 *pK1 = (const u8*)pKey1;
|
||||
const u8 *pK2 = (const u8*)pKey2;
|
||||
while( nKey1 && pK1[nKey1-1]==' ' ) nKey1--;
|
||||
while( nKey2 && pK2[nKey2-1]==' ' ) nKey2--;
|
||||
return binCollFunc(pUser, nKey1, pKey1, nKey2, pKey2);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if CollSeq is the default built-in BINARY.
|
||||
*/
|
||||
int sqlite3IsBinary(const CollSeq *p){
|
||||
assert( p==0 || p->xCmp!=binCollFunc || p->pUser!=0
|
||||
|| strcmp(p->zName,"BINARY")==0 );
|
||||
return p==0 || (p->xCmp==binCollFunc && p->pUser==0);
|
||||
assert( p==0 || p->xCmp!=binCollFunc || strcmp(p->zName,"BINARY")==0 );
|
||||
return p==0 || p->xCmp==binCollFunc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1239,11 +1236,8 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){
|
||||
Module *pMod = (Module *)sqliteHashData(i);
|
||||
if( pMod->xDestroy ){
|
||||
pMod->xDestroy(pMod->pAux);
|
||||
}
|
||||
sqlite3VtabEponymousTableClear(db, pMod);
|
||||
sqlite3DbFree(db, pMod);
|
||||
sqlite3VtabModuleUnref(db, pMod);
|
||||
}
|
||||
sqlite3HashClear(&db->aModule);
|
||||
#endif
|
||||
@ -1724,7 +1718,8 @@ int sqlite3CreateFunc(
|
||||
}
|
||||
|
||||
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
|
||||
extraFlags = enc & SQLITE_DETERMINISTIC;
|
||||
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
|
||||
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|SQLITE_SUBTYPE);
|
||||
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
@ -1787,6 +1782,7 @@ int sqlite3CreateFunc(
|
||||
p->u.pDestructor = pDestructor;
|
||||
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
|
||||
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
|
||||
testcase( p->funcFlags & SQLITE_DIRECTONLY );
|
||||
p->xSFunc = xSFunc ? xSFunc : xStep;
|
||||
p->xFinalize = xFinal;
|
||||
p->xValue = xValue;
|
||||
@ -3077,7 +3073,36 @@ static int openDatabase(
|
||||
db->szMmap = sqlite3GlobalConfig.szMmap;
|
||||
db->nextPagesize = 0;
|
||||
db->nMaxSorterMmap = 0x7FFFFFFF;
|
||||
db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
|
||||
db->flags |= SQLITE_ShortColNames
|
||||
| SQLITE_EnableTrigger
|
||||
| SQLITE_EnableView
|
||||
| SQLITE_CacheSpill
|
||||
|
||||
/* The SQLITE_DQS compile-time option determines the default settings
|
||||
** for SQLITE_DBCONFIG_DQS_DDL and SQLITE_DBCONFIG_DQS_DML.
|
||||
**
|
||||
** SQLITE_DQS SQLITE_DBCONFIG_DQS_DDL SQLITE_DBCONFIG_DQS_DML
|
||||
** ---------- ----------------------- -----------------------
|
||||
** undefined on on
|
||||
** 3 on on
|
||||
** 2 on off
|
||||
** 1 off on
|
||||
** 0 off off
|
||||
**
|
||||
** Legacy behavior is 3 (double-quoted string literals are allowed anywhere)
|
||||
** and so that is the default. But developers are encouranged to use
|
||||
** -DSQLITE_DQS=0 (best) or -DSQLITE_DQS=1 (second choice) if possible.
|
||||
*/
|
||||
#if !defined(SQLITE_DQS)
|
||||
# define SQLITE_DQS 3
|
||||
#endif
|
||||
#if (SQLITE_DQS&1)==1
|
||||
| SQLITE_DqsDML
|
||||
#endif
|
||||
#if (SQLITE_DQS&2)==2
|
||||
| SQLITE_DqsDDL
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
|
||||
| SQLITE_AutoIndex
|
||||
#endif
|
||||
@ -3128,7 +3153,7 @@ static int openDatabase(
|
||||
createCollation(db, sqlite3StrBINARY, SQLITE_UTF16BE, 0, binCollFunc, 0);
|
||||
createCollation(db, sqlite3StrBINARY, SQLITE_UTF16LE, 0, binCollFunc, 0);
|
||||
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
|
||||
createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
|
||||
createCollation(db, "RTRIM", SQLITE_UTF8, 0, rtrimCollFunc, 0);
|
||||
if( db->mallocFailed ){
|
||||
goto opendb_out;
|
||||
}
|
||||
@ -3282,6 +3307,13 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLCIPHER_EXT
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
extern int sqlcipherVtabInit(sqlite3 *);
|
||||
rc = sqlcipherVtabInit(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
|
||||
** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
|
||||
** mode. Doing nothing at all also makes NORMAL the default.
|
||||
@ -3800,12 +3832,33 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset the PRNG back to its uninitialized state. The next call
|
||||
** to sqlite3_randomness() will reseed the PRNG using a single call
|
||||
** to the xRandomness method of the default VFS.
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SEED, int x, sqlite3 *db);
|
||||
**
|
||||
** Control the seed for the pseudo-random number generator (PRNG) that
|
||||
** is built into SQLite. Cases:
|
||||
**
|
||||
** x!=0 && db!=0 Seed the PRNG to the current value of the
|
||||
** schema cookie in the main database for db, or
|
||||
** x if the schema cookie is zero. This case
|
||||
** is convenient to use with database fuzzers
|
||||
** as it allows the fuzzer some control over the
|
||||
** the PRNG seed.
|
||||
**
|
||||
** x!=0 && db==0 Seed the PRNG to the value of x.
|
||||
**
|
||||
** x==0 && db==0 Revert to default behavior of using the
|
||||
** xRandomness method on the primary VFS.
|
||||
**
|
||||
** This test-control also resets the PRNG so that the new seed will
|
||||
** be used for the next call to sqlite3_randomness().
|
||||
*/
|
||||
case SQLITE_TESTCTRL_PRNG_RESET: {
|
||||
case SQLITE_TESTCTRL_PRNG_SEED: {
|
||||
int x = va_arg(ap, int);
|
||||
int y;
|
||||
sqlite3 *db = va_arg(ap, sqlite3*);
|
||||
assert( db==0 || db->aDb[0].pSchema!=0 );
|
||||
if( db && (y = db->aDb[0].pSchema->schema_cookie)!=0 ){ x = y; }
|
||||
sqlite3Config.iPrngSeed = x;
|
||||
sqlite3_randomness(0,0);
|
||||
break;
|
||||
}
|
||||
@ -4018,6 +4071,17 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int);
|
||||
**
|
||||
** Set or clear a flag that causes SQLite to verify that type, name,
|
||||
** and tbl_name fields of the sqlite_master table. This is normally
|
||||
** on, but it is sometimes useful to turn it off for testing.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: {
|
||||
sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the threshold at which OP_Once counters reset back to zero.
|
||||
** By default this is 0x7ffffffe (over 2 billion), but that value is
|
||||
** too big to test in a reasonable amount of time, so this control is
|
||||
@ -4104,6 +4168,22 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
#endif /* defined(YYCOVERAGE) */
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, sqlite3_context*);
|
||||
**
|
||||
** This test-control causes the most recent sqlite3_result_int64() value
|
||||
** to be interpreted as a MEM_IntReal instead of as an MEM_Int. Normally,
|
||||
** MEM_IntReal values only arise during an INSERT operation of integer
|
||||
** values into a REAL column, so they can be challenging to test. This
|
||||
** test-control enables us to write an intreal() SQL function that can
|
||||
** inject an intreal() value at arbitrary places in an SQL statement,
|
||||
** for testing purposes.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_RESULT_INTREAL: {
|
||||
sqlite3_context *pCtx = va_arg(ap, sqlite3_context*);
|
||||
sqlite3ResultIntReal(pCtx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_UNTESTABLE */
|
||||
|
@ -96,14 +96,9 @@ static int memjrnlRead(
|
||||
int iChunkOffset;
|
||||
FileChunk *pChunk;
|
||||
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
if( (iAmt+iOfst)>p->endpoint.iOffset ){
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert( (iAmt+iOfst)<=p->endpoint.iOffset );
|
||||
assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
|
||||
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
|
||||
sqlite3_int64 iOff = 0;
|
||||
|
@ -33,4 +33,9 @@
|
||||
#pragma warning(disable : 4706)
|
||||
#endif /* defined(_MSC_VER) */
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_WIN64)
|
||||
#undef SQLITE_4_BYTE_ALIGNED_MALLOC
|
||||
#define SQLITE_4_BYTE_ALIGNED_MALLOC
|
||||
#endif /* defined(_MSC_VER) && !defined(_WIN64) */
|
||||
|
||||
#endif /* SQLITE_MSVC_H */
|
||||
|
@ -67,4 +67,5 @@
|
||||
#define MUTEX_LOGIC(X)
|
||||
#else
|
||||
#define MUTEX_LOGIC(X) X
|
||||
int sqlite3_mutex_held(sqlite3_mutex*);
|
||||
#endif /* defined(SQLITE_MUTEX_OMIT) */
|
||||
|
10
src/os.c
10
src/os.c
@ -258,7 +258,15 @@ void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return pVfs->xRandomness(pVfs, nByte, zBufOut);
|
||||
if( sqlite3Config.iPrngSeed ){
|
||||
memset(zBufOut, 0, nByte);
|
||||
if( ALWAYS(nByte>(signed)sizeof(unsigned)) ) nByte = sizeof(unsigned int);
|
||||
memcpy(zBufOut, &sqlite3Config.iPrngSeed, nByte);
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
return pVfs->xRandomness(pVfs, nByte, zBufOut);
|
||||
}
|
||||
|
||||
}
|
||||
int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return pVfs->xSleep(pVfs, nMicro);
|
||||
|
@ -105,13 +105,29 @@
|
||||
# include <sys/param.h>
|
||||
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
|
||||
|
||||
#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))
|
||||
# define HAVE_GETHOSTUUID 1
|
||||
# else
|
||||
# warning "gethostuuid() is disabled."
|
||||
/*
|
||||
** Try to determine if gethostuuid() is available based on standard
|
||||
** macros. This might sometimes compute the wrong value for some
|
||||
** obscure platforms. For those cases, simply compile with one of
|
||||
** the following:
|
||||
**
|
||||
** -DHAVE_GETHOSTUUID=0
|
||||
** -DHAVE_GETHOSTUUID=1
|
||||
**
|
||||
** None if this matters except when building on Apple products with
|
||||
** -DSQLITE_ENABLE_LOCKING_STYLE.
|
||||
*/
|
||||
#ifndef HAVE_GETHOSTUUID
|
||||
# define HAVE_GETHOSTUUID 0
|
||||
# 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))
|
||||
# undef HAVE_GETHOSTUUID
|
||||
# define HAVE_GETHOSTUUID 1
|
||||
# else
|
||||
# warning "gethostuuid() is disabled."
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -521,13 +537,14 @@ static struct unix_syscall {
|
||||
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
# ifdef __ANDROID__
|
||||
{ "ioctl", (sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 },
|
||||
#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
|
||||
# else
|
||||
{ "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
|
||||
#define osIoctl ((int(*)(int,unsigned long,...))aSyscall[28].pCurrent)
|
||||
# endif
|
||||
#else
|
||||
{ "ioctl", (sqlite3_syscall_ptr)0, 0 },
|
||||
#endif
|
||||
#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
@ -5769,6 +5786,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
UnixUnusedFd **pp;
|
||||
assert( sqlite3_mutex_notheld(pInode->pLockMutex) );
|
||||
sqlite3_mutex_enter(pInode->pLockMutex);
|
||||
flags &= (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
|
||||
for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
|
||||
pUnused = *pp;
|
||||
if( pUnused ){
|
||||
@ -5822,7 +5840,7 @@ static int getFileMode(
|
||||
** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
|
||||
** original filename is unavailable. But 8_3_NAMES is only used for
|
||||
** FAT filesystems and permissions do not matter there, so just use
|
||||
** the default permissions.
|
||||
** the default permissions. In 8_3_NAMES mode, leave *pMode set to zero.
|
||||
*/
|
||||
static int findCreateFileMode(
|
||||
const char *zPath, /* Path of file (possibly) being created */
|
||||
@ -6057,11 +6075,19 @@ static int unixOpen(
|
||||
goto open_finished;
|
||||
}
|
||||
|
||||
/* If this process is running as root and if creating a new rollback
|
||||
** journal or WAL file, set the ownership of the journal or WAL to be
|
||||
** the same as the original database.
|
||||
/* The owner of the rollback journal or WAL file should always be the
|
||||
** same as the owner of the database file. Try to ensure that this is
|
||||
** the case. The chown() system call will be a no-op if the current
|
||||
** process lacks root privileges, be we should at least try. Without
|
||||
** this step, if a root process opens a database file, it can leave
|
||||
** behinds a journal/WAL that is owned by root and hence make the
|
||||
** database inaccessible to unprivileged processes.
|
||||
**
|
||||
** If openMode==0, then that means uid and gid are not set correctly
|
||||
** (probably because SQLite is configured to use 8+3 filename mode) and
|
||||
** in that case we do not want to attempt the chown().
|
||||
*/
|
||||
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
||||
if( openMode && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
|
||||
robustFchown(fd, uid, gid);
|
||||
}
|
||||
}
|
||||
@ -6072,7 +6098,8 @@ static int unixOpen(
|
||||
|
||||
if( p->pPreallocatedUnused ){
|
||||
p->pPreallocatedUnused->fd = fd;
|
||||
p->pPreallocatedUnused->flags = flags;
|
||||
p->pPreallocatedUnused->flags =
|
||||
flags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
|
||||
}
|
||||
|
||||
if( isDelete ){
|
||||
@ -6918,7 +6945,7 @@ int sqlite3_hostid_num = 0;
|
||||
|
||||
#define PROXY_HOSTIDLEN 16 /* conch file host id length */
|
||||
|
||||
#ifdef HAVE_GETHOSTUUID
|
||||
#if HAVE_GETHOSTUUID
|
||||
/* Not always defined in the headers as it ought to be */
|
||||
extern int gethostuuid(uuid_t id, const struct timespec *wait);
|
||||
#endif
|
||||
@ -6929,7 +6956,7 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
|
||||
static int proxyGetHostID(unsigned char *pHostID, int *pError){
|
||||
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
|
||||
memset(pHostID, 0, PROXY_HOSTIDLEN);
|
||||
#ifdef HAVE_GETHOSTUUID
|
||||
#if HAVE_GETHOSTUUID
|
||||
{
|
||||
struct timespec timeout = {1, 0}; /* 1 sec timeout */
|
||||
if( gethostuuid(pHostID, &timeout) ){
|
||||
@ -7603,7 +7630,7 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
assert( 0 ); /* The call assures that only valid opcodes are sent */
|
||||
}
|
||||
}
|
||||
/*NOTREACHED*/
|
||||
/*NOTREACHED*/ assert(0);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
|
@ -4215,6 +4215,7 @@ static int winShmMap(
|
||||
rc = winOpenSharedMemory(pDbFd);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
pShm = pDbFd->pShm;
|
||||
assert( pShm!=0 );
|
||||
}
|
||||
pShmNode = pShm->pShmNode;
|
||||
|
||||
@ -4517,6 +4518,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
||||
}
|
||||
}
|
||||
if( pFd->mmapSize >= iOff+nAmt ){
|
||||
assert( pFd->pMapRegion!=0 );
|
||||
*pp = &((u8 *)pFd->pMapRegion)[iOff];
|
||||
pFd->nFetchOut++;
|
||||
}
|
||||
|
128
src/parse.y
128
src/parse.y
@ -211,6 +211,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
|
||||
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
|
||||
QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS
|
||||
ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
|
||||
NULLS FIRST LAST
|
||||
%ifdef SQLITE_OMIT_COMPOUND_SELECT
|
||||
EXCEPT INTERSECT UNION
|
||||
%endif SQLITE_OMIT_COMPOUND_SELECT
|
||||
@ -301,6 +302,10 @@ scanpt(A) ::= . {
|
||||
assert( yyLookahead!=YYNOCODE );
|
||||
A = yyLookaheadToken.z;
|
||||
}
|
||||
scantok(A) ::= . {
|
||||
assert( yyLookahead!=YYNOCODE );
|
||||
A = yyLookaheadToken;
|
||||
}
|
||||
|
||||
// "carglist" is a list of additional constraints that come after the
|
||||
// column name and column type in a CREATE TABLE statement.
|
||||
@ -308,17 +313,17 @@ scanpt(A) ::= . {
|
||||
carglist ::= carglist ccons.
|
||||
carglist ::= .
|
||||
ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;}
|
||||
ccons ::= DEFAULT scanpt(A) term(X) scanpt(Z).
|
||||
{sqlite3AddDefaultValue(pParse,X,A,Z);}
|
||||
ccons ::= DEFAULT scantok(A) term(X).
|
||||
{sqlite3AddDefaultValue(pParse,X,A.z,&A.z[A.n]);}
|
||||
ccons ::= DEFAULT LP(A) expr(X) RP(Z).
|
||||
{sqlite3AddDefaultValue(pParse,X,A.z+1,Z.z);}
|
||||
ccons ::= DEFAULT PLUS(A) term(X) scanpt(Z).
|
||||
{sqlite3AddDefaultValue(pParse,X,A.z,Z);}
|
||||
ccons ::= DEFAULT MINUS(A) term(X) scanpt(Z). {
|
||||
ccons ::= DEFAULT PLUS(A) scantok(Z) term(X).
|
||||
{sqlite3AddDefaultValue(pParse,X,A.z,&Z.z[Z.n]);}
|
||||
ccons ::= DEFAULT MINUS(A) scantok(Z) term(X). {
|
||||
Expr *p = sqlite3PExpr(pParse, TK_UMINUS, X, 0);
|
||||
sqlite3AddDefaultValue(pParse,p,A.z,Z);
|
||||
sqlite3AddDefaultValue(pParse,p,A.z,&Z.z[Z.n]);
|
||||
}
|
||||
ccons ::= DEFAULT scanpt id(X). {
|
||||
ccons ::= DEFAULT scantok id(X). {
|
||||
Expr *p = tokenExpr(pParse, TK_STRING, X);
|
||||
if( p ){
|
||||
sqlite3ExprIdToTrueFalse(p);
|
||||
@ -454,6 +459,7 @@ cmd ::= select(X). {
|
||||
** SQLITE_LIMIT_COMPOUND_SELECT.
|
||||
*/
|
||||
static void parserDoubleLinkSelect(Parse *pParse, Select *p){
|
||||
assert( p!=0 );
|
||||
if( p->pPrior ){
|
||||
Select *pNext = 0, *pLoop;
|
||||
int mxSelect, cnt = 0;
|
||||
@ -776,13 +782,13 @@ using_opt(U) ::= . {U = 0;}
|
||||
|
||||
orderby_opt(A) ::= . {A = 0;}
|
||||
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
||||
sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z). {
|
||||
sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z) nulls(X). {
|
||||
A = sqlite3ExprListAppend(pParse,A,Y);
|
||||
sqlite3ExprListSetSortOrder(A,Z);
|
||||
sqlite3ExprListSetSortOrder(A,Z,X);
|
||||
}
|
||||
sortlist(A) ::= expr(Y) sortorder(Z). {
|
||||
sortlist(A) ::= expr(Y) sortorder(Z) nulls(X). {
|
||||
A = sqlite3ExprListAppend(pParse,0,Y); /*A-overwrites-Y*/
|
||||
sqlite3ExprListSetSortOrder(A,Z);
|
||||
sqlite3ExprListSetSortOrder(A,Z,X);
|
||||
}
|
||||
|
||||
%type sortorder {int}
|
||||
@ -791,6 +797,11 @@ sortorder(A) ::= ASC. {A = SQLITE_SO_ASC;}
|
||||
sortorder(A) ::= DESC. {A = SQLITE_SO_DESC;}
|
||||
sortorder(A) ::= . {A = SQLITE_SO_UNDEFINED;}
|
||||
|
||||
%type nulls {int}
|
||||
nulls(A) ::= NULLS FIRST. {A = SQLITE_SO_ASC;}
|
||||
nulls(A) ::= NULLS LAST. {A = SQLITE_SO_DESC;}
|
||||
nulls(A) ::= . {A = SQLITE_SO_UNDEFINED;}
|
||||
|
||||
%type groupby_opt {ExprList*}
|
||||
%destructor groupby_opt {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
groupby_opt(A) ::= . {A = 0;}
|
||||
@ -944,7 +955,7 @@ idlist(A) ::= nm(Y).
|
||||
if( p ){
|
||||
/* memset(p, 0, sizeof(Expr)); */
|
||||
p->op = (u8)op;
|
||||
p->affinity = 0;
|
||||
p->affExpr = 0;
|
||||
p->flags = EP_Leaf;
|
||||
p->iAgg = -1;
|
||||
p->pLeft = p->pRight = 0;
|
||||
@ -1040,11 +1051,11 @@ expr(A) ::= id(X) LP STAR RP. {
|
||||
}
|
||||
|
||||
%ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP over_clause(Z). {
|
||||
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP filter_over(Z). {
|
||||
A = sqlite3ExprFunction(pParse, Y, &X, D);
|
||||
sqlite3WindowAttach(pParse, A, Z);
|
||||
}
|
||||
expr(A) ::= id(X) LP STAR RP over_clause(Z). {
|
||||
expr(A) ::= id(X) LP STAR RP filter_over(Z). {
|
||||
A = sqlite3ExprFunction(pParse, 0, &X, 0);
|
||||
sqlite3WindowAttach(pParse, A, Z);
|
||||
}
|
||||
@ -1064,7 +1075,7 @@ expr(A) ::= LP nexprlist(X) COMMA expr(Y) RP. {
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= expr(A) AND(OP) expr(Y). {A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) AND expr(Y). {A=sqlite3ExprAnd(pParse,A,Y);}
|
||||
expr(A) ::= expr(A) OR(OP) expr(Y). {A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
|
||||
{A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
@ -1169,37 +1180,8 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
** simplify to constants 0 (false) and 1 (true), respectively,
|
||||
** regardless of the value of expr1.
|
||||
*/
|
||||
if( IN_RENAME_OBJECT==0 ){
|
||||
sqlite3ExprDelete(pParse->db, A);
|
||||
A = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[N],1);
|
||||
}
|
||||
}else if( Y->nExpr==1 ){
|
||||
/* Expressions of the form:
|
||||
**
|
||||
** expr1 IN (?1)
|
||||
** expr1 NOT IN (?2)
|
||||
**
|
||||
** with exactly one value on the RHS can be simplified to something
|
||||
** like this:
|
||||
**
|
||||
** expr1 == ?1
|
||||
** expr1 <> ?2
|
||||
**
|
||||
** But, the RHS of the == or <> is marked with the EP_Generic flag
|
||||
** so that it may not contribute to the computation of comparison
|
||||
** affinity or the collating sequence to use for comparison. Otherwise,
|
||||
** the semantics would be subtly different from IN or NOT IN.
|
||||
*/
|
||||
Expr *pRHS = Y->a[0].pExpr;
|
||||
Y->a[0].pExpr = 0;
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
/* pRHS cannot be NULL because a malloc error would have been detected
|
||||
** before now and control would have never reached this point */
|
||||
if( ALWAYS(pRHS) ){
|
||||
pRHS->flags &= ~EP_Collate;
|
||||
pRHS->flags |= EP_Generic;
|
||||
}
|
||||
A = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A, pRHS);
|
||||
sqlite3ExprUnmapAndDelete(pParse, A);
|
||||
A = sqlite3Expr(pParse->db, TK_INTEGER, N ? "1" : "0");
|
||||
}else{
|
||||
A = sqlite3PExpr(pParse, TK_IN, A, 0);
|
||||
if( A ){
|
||||
@ -1505,13 +1487,13 @@ trigger_cmd(A) ::= scanpt(B) select(X) scanpt(E).
|
||||
expr(A) ::= RAISE LP IGNORE RP. {
|
||||
A = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
|
||||
if( A ){
|
||||
A->affinity = OE_Ignore;
|
||||
A->affExpr = OE_Ignore;
|
||||
}
|
||||
}
|
||||
expr(A) ::= RAISE LP raisetype(T) COMMA nm(Z) RP. {
|
||||
A = sqlite3ExprAlloc(pParse->db, TK_RAISE, &Z, 1);
|
||||
if( A ) {
|
||||
A->affinity = (char)T;
|
||||
A->affExpr = (char)T;
|
||||
}
|
||||
}
|
||||
%endif !SQLITE_OMIT_TRIGGER
|
||||
@ -1655,8 +1637,14 @@ windowdefn(A) ::= nm(X) AS LP window(Y) RP. {
|
||||
%type part_opt {ExprList*}
|
||||
%destructor part_opt {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
|
||||
%type filter_opt {Expr*}
|
||||
%destructor filter_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
%type filter_clause {Expr*}
|
||||
%destructor filter_clause {sqlite3ExprDelete(pParse->db, $$);}
|
||||
|
||||
%type over_clause {Window*}
|
||||
%destructor over_clause {sqlite3WindowDelete(pParse->db, $$);}
|
||||
|
||||
%type filter_over {Window*}
|
||||
%destructor filter_over {sqlite3WindowDelete(pParse->db, $$);}
|
||||
|
||||
%type range_or_rows {int}
|
||||
|
||||
@ -1722,25 +1710,35 @@ frame_exclude(A) ::= GROUP|TIES(X). {A = @X; /*A-overwrites-X*/}
|
||||
%destructor window_clause {sqlite3WindowListDelete(pParse->db, $$);}
|
||||
window_clause(A) ::= WINDOW windowdefn_list(B). { A = B; }
|
||||
|
||||
%type over_clause {Window*}
|
||||
%destructor over_clause {sqlite3WindowDelete(pParse->db, $$);}
|
||||
over_clause(A) ::= filter_opt(W) OVER LP window(Z) RP. {
|
||||
A = Z;
|
||||
assert( A!=0 );
|
||||
A->pFilter = W;
|
||||
filter_over(A) ::= filter_clause(F) over_clause(O). {
|
||||
O->pFilter = F;
|
||||
A = O;
|
||||
}
|
||||
over_clause(A) ::= filter_opt(W) OVER nm(Z). {
|
||||
filter_over(A) ::= over_clause(O). {
|
||||
A = O;
|
||||
}
|
||||
filter_over(A) ::= filter_clause(F). {
|
||||
A = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
|
||||
if( A ){
|
||||
A->zName = sqlite3DbStrNDup(pParse->db, Z.z, Z.n);
|
||||
A->pFilter = W;
|
||||
A->eFrmType = TK_FILTER;
|
||||
A->pFilter = F;
|
||||
}else{
|
||||
sqlite3ExprDelete(pParse->db, W);
|
||||
sqlite3ExprDelete(pParse->db, F);
|
||||
}
|
||||
}
|
||||
|
||||
filter_opt(A) ::= . { A = 0; }
|
||||
filter_opt(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
|
||||
over_clause(A) ::= OVER LP window(Z) RP. {
|
||||
A = Z;
|
||||
assert( A!=0 );
|
||||
}
|
||||
over_clause(A) ::= OVER nm(Z). {
|
||||
A = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
|
||||
if( A ){
|
||||
A->zName = sqlite3DbStrNDup(pParse->db, Z.z, Z.n);
|
||||
}
|
||||
}
|
||||
|
||||
filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
|
||||
%endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
@ -1748,12 +1746,12 @@ filter_opt(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
|
||||
** are synthesized and do not actually appear in the grammar:
|
||||
*/
|
||||
%token
|
||||
TRUEFALSE /* True or false keyword */
|
||||
ISNOT /* Combination of IS and NOT */
|
||||
FUNCTION /* A function invocation */
|
||||
COLUMN /* Reference to a table column */
|
||||
AGG_FUNCTION /* An aggregate function */
|
||||
AGG_COLUMN /* An aggregated column */
|
||||
TRUEFALSE /* True or false keyword */
|
||||
ISNOT /* Combination of IS and NOT */
|
||||
FUNCTION /* A function invocation */
|
||||
UMINUS /* Unary minus */
|
||||
UPLUS /* Unary plus */
|
||||
TRUTH /* IS TRUE or IS FALSE or IS NOT TRUE or IS NOT FALSE */
|
||||
|
@ -243,9 +243,10 @@ static int numberOfCachePages(PCache *p){
|
||||
** suggested cache size is set to N. */
|
||||
return p->szCache;
|
||||
}else{
|
||||
/* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then
|
||||
** the number of cache pages is adjusted to use approximately abs(N*1024)
|
||||
** bytes of memory. */
|
||||
/* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the
|
||||
** number of cache pages is adjusted to be a number of pages that would
|
||||
** use approximately abs(N*1024) bytes of memory based on the current
|
||||
** page size. */
|
||||
return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
|
||||
}
|
||||
}
|
||||
@ -261,6 +262,7 @@ int sqlite3PcacheInitialize(void){
|
||||
** built-in default page cache is used instead of the application defined
|
||||
** page cache. */
|
||||
sqlite3PCacheSetDefault();
|
||||
assert( sqlite3GlobalConfig.pcache2.xInit!=0 );
|
||||
}
|
||||
return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
|
||||
}
|
||||
|
@ -424,6 +424,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
|
||||
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
|
||||
assert( pCache->pFree!=0 );
|
||||
p = pCache->pFree;
|
||||
pCache->pFree = p->pNext;
|
||||
p->pNext = 0;
|
||||
@ -778,6 +779,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
|
||||
}else{
|
||||
pGroup = &pcache1.grp;
|
||||
}
|
||||
pcache1EnterMutex(pGroup);
|
||||
if( pGroup->lru.isAnchor==0 ){
|
||||
pGroup->lru.isAnchor = 1;
|
||||
pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
|
||||
@ -787,7 +789,6 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
|
||||
pCache->szExtra = szExtra;
|
||||
pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
|
||||
pCache->bPurgeable = (bPurgeable ? 1 : 0);
|
||||
pcache1EnterMutex(pGroup);
|
||||
pcache1ResizeHash(pCache);
|
||||
if( bPurgeable ){
|
||||
pCache->nMin = 10;
|
||||
|
56
src/pragma.c
56
src/pragma.c
@ -659,6 +659,11 @@ void sqlite3Pragma(
|
||||
** then do a query */
|
||||
eMode = PAGER_JOURNALMODE_QUERY;
|
||||
}
|
||||
if( eMode==PAGER_JOURNALMODE_OFF && (db->flags & SQLITE_Defensive)!=0 ){
|
||||
/* Do not allow journal-mode "OFF" in defensive since the database
|
||||
** can become corrupted using ordinary SQL when the journal is off */
|
||||
eMode = PAGER_JOURNALMODE_QUERY;
|
||||
}
|
||||
}
|
||||
if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){
|
||||
/* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */
|
||||
@ -1167,6 +1172,15 @@ void sqlite3Pragma(
|
||||
Index *pIdx;
|
||||
Table *pTab;
|
||||
pIdx = sqlite3FindIndex(db, zRight, zDb);
|
||||
if( pIdx==0 ){
|
||||
/* If there is no index named zRight, check to see if there is a
|
||||
** WITHOUT ROWID table named zRight, and if there is, show the
|
||||
** structure of the PRIMARY KEY index for that table. */
|
||||
pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
|
||||
if( pTab && !HasRowid(pTab) ){
|
||||
pIdx = sqlite3PrimaryKeyIndex(pTab);
|
||||
}
|
||||
}
|
||||
if( pIdx ){
|
||||
int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
|
||||
int i;
|
||||
@ -1246,7 +1260,7 @@ void sqlite3Pragma(
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef SQLITE_INTROSPECTION_PRAGMAS
|
||||
#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS
|
||||
case PragTyp_FUNCTION_LIST: {
|
||||
int i;
|
||||
HashElem *j;
|
||||
@ -1436,6 +1450,7 @@ void sqlite3Pragma(
|
||||
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
|
||||
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
|
||||
|
||||
#ifndef SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA
|
||||
/* Reinstall the LIKE and GLOB functions. The variant of LIKE
|
||||
** used will be case sensitive or not depending on the RHS.
|
||||
*/
|
||||
@ -1445,6 +1460,7 @@ void sqlite3Pragma(
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif /* SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA */
|
||||
|
||||
#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX
|
||||
# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100
|
||||
@ -2138,28 +2154,30 @@ void sqlite3Pragma(
|
||||
*/
|
||||
case PragTyp_KEY: {
|
||||
if( zRight ){
|
||||
int n = pPragma->iArg<4 ? sqlite3Strlen30(zRight) : -1;
|
||||
if( (pPragma->iArg & 1)==0 ){
|
||||
sqlite3_key_v2(db, zDb, zRight, n);
|
||||
char zBuf[40];
|
||||
const char *zKey = zRight;
|
||||
int n;
|
||||
if( pPragma->iArg==2 || pPragma->iArg==3 ){
|
||||
u8 iByte;
|
||||
int i;
|
||||
for(i=0, iByte=0; i<sizeof(zBuf)*2 && sqlite3Isxdigit(zRight[i]); i++){
|
||||
iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
|
||||
if( (i&1)!=0 ) zBuf[i/2] = iByte;
|
||||
}
|
||||
zKey = zBuf;
|
||||
n = i/2;
|
||||
}else{
|
||||
sqlite3_rekey_v2(db, zDb, zRight, n);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PragTyp_HEXKEY: {
|
||||
if( zRight ){
|
||||
u8 iByte;
|
||||
int i;
|
||||
char zKey[40];
|
||||
for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zRight[i]); i++){
|
||||
iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
|
||||
if( (i&1)!=0 ) zKey[i/2] = iByte;
|
||||
n = pPragma->iArg<4 ? sqlite3Strlen30(zRight) : -1;
|
||||
}
|
||||
if( (pPragma->iArg & 1)==0 ){
|
||||
sqlite3_key_v2(db, zDb, zKey, i/2);
|
||||
rc = sqlite3_key_v2(db, zDb, zKey, n);
|
||||
}else{
|
||||
sqlite3_rekey_v2(db, zDb, zKey, i/2);
|
||||
rc = sqlite3_rekey_v2(db, zDb, zKey, n);
|
||||
}
|
||||
if( rc==SQLITE_OK && n!=0 ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "ok", SQLITE_STATIC);
|
||||
returnSingleText(v, "ok");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
21
src/pragma.h
21
src/pragma.h
@ -46,10 +46,9 @@
|
||||
#define PragTyp_WAL_AUTOCHECKPOINT 38
|
||||
#define PragTyp_WAL_CHECKPOINT 39
|
||||
#define PragTyp_ACTIVATE_EXTENSIONS 40
|
||||
#define PragTyp_HEXKEY 41
|
||||
#define PragTyp_KEY 42
|
||||
#define PragTyp_LOCK_STATUS 43
|
||||
#define PragTyp_STATS 44
|
||||
#define PragTyp_KEY 41
|
||||
#define PragTyp_LOCK_STATUS 42
|
||||
#define PragTyp_STATS 43
|
||||
|
||||
/* Property flags associated with various pragma. */
|
||||
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
|
||||
@ -178,11 +177,13 @@ static const PragmaName aPragmaName[] = {
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA)
|
||||
{/* zName: */ "case_sensitive_like",
|
||||
/* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
|
||||
/* ePragFlg: */ PragFlg_NoColumns,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
{/* zName: */ "cell_size_check",
|
||||
/* ePragTyp: */ PragTyp_FLAG,
|
||||
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
|
||||
@ -310,7 +311,7 @@ static const PragmaName aPragmaName[] = {
|
||||
/* iArg: */ SQLITE_FullFSync },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
#if defined(SQLITE_INTROSPECTION_PRAGMAS)
|
||||
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
|
||||
{/* zName: */ "function_list",
|
||||
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
@ -320,12 +321,12 @@ static const PragmaName aPragmaName[] = {
|
||||
#endif
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
{/* zName: */ "hexkey",
|
||||
/* ePragTyp: */ PragTyp_HEXKEY,
|
||||
/* ePragTyp: */ PragTyp_KEY,
|
||||
/* ePragFlg: */ 0,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 2 },
|
||||
{/* zName: */ "hexrekey",
|
||||
/* ePragTyp: */ PragTyp_HEXKEY,
|
||||
/* ePragTyp: */ PragTyp_KEY,
|
||||
/* ePragFlg: */ 0,
|
||||
/* ColNames: */ 0, 0,
|
||||
/* iArg: */ 3 },
|
||||
@ -434,7 +435,7 @@ static const PragmaName aPragmaName[] = {
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
#if defined(SQLITE_INTROSPECTION_PRAGMAS)
|
||||
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
|
||||
{/* zName: */ "module_list",
|
||||
/* ePragTyp: */ PragTyp_MODULE_LIST,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
@ -469,7 +470,7 @@ static const PragmaName aPragmaName[] = {
|
||||
/* iArg: */ SQLITE_ParserTrace },
|
||||
#endif
|
||||
#endif
|
||||
#if defined(SQLITE_INTROSPECTION_PRAGMAS)
|
||||
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
|
||||
{/* zName: */ "pragma_list",
|
||||
/* ePragTyp: */ PragTyp_PRAGMA_LIST,
|
||||
/* ePragFlg: */ PragFlg_Result0,
|
||||
@ -667,4 +668,4 @@ static const PragmaName aPragmaName[] = {
|
||||
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
|
||||
#endif
|
||||
};
|
||||
/* Number of pragmas: 62 on by default, 81 total. */
|
||||
/* Number of pragmas: 65 on by default, 81 total. */
|
||||
|
@ -64,9 +64,11 @@ int sqlite3IndexHasDuplicateRootPage(Index *pIndex){
|
||||
**
|
||||
** Each callback contains the following information:
|
||||
**
|
||||
** argv[0] = name of thing being created
|
||||
** argv[1] = root page number for table or index. 0 for trigger or view.
|
||||
** argv[2] = SQL text for the CREATE statement.
|
||||
** argv[0] = type of object: "table", "index", "trigger", or "view".
|
||||
** argv[1] = name of thing being created
|
||||
** argv[2] = associated table if an index or trigger
|
||||
** argv[3] = root page number for table or index. 0 for trigger or view.
|
||||
** argv[4] = SQL text for the CREATE statement.
|
||||
**
|
||||
*/
|
||||
int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
@ -74,21 +76,21 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
sqlite3 *db = pData->db;
|
||||
int iDb = pData->iDb;
|
||||
|
||||
assert( argc==3 );
|
||||
assert( argc==5 );
|
||||
UNUSED_PARAMETER2(NotUsed, argc);
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
DbClearProperty(db, iDb, DB_Empty);
|
||||
pData->nInitRow++;
|
||||
if( db->mallocFailed ){
|
||||
corruptSchema(pData, argv[0], 0);
|
||||
corruptSchema(pData, argv[1], 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
|
||||
if( argv[1]==0 ){
|
||||
corruptSchema(pData, argv[0], 0);
|
||||
}else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){
|
||||
if( argv[3]==0 ){
|
||||
corruptSchema(pData, argv[1], 0);
|
||||
}else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){
|
||||
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
|
||||
** But because db->init.busy is set to 1, no VDBE code is generated
|
||||
** or executed. All the parser does is build the internal data
|
||||
@ -101,9 +103,10 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
|
||||
assert( db->init.busy );
|
||||
db->init.iDb = iDb;
|
||||
db->init.newTnum = sqlite3Atoi(argv[1]);
|
||||
db->init.newTnum = sqlite3Atoi(argv[3]);
|
||||
db->init.orphanTrigger = 0;
|
||||
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
|
||||
db->init.azInit = argv;
|
||||
TESTONLY(rcp = ) sqlite3_prepare(db, argv[4], -1, &pStmt, 0);
|
||||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = saved_iDb;
|
||||
@ -112,17 +115,17 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
if( db->init.orphanTrigger ){
|
||||
assert( iDb==1 );
|
||||
}else{
|
||||
pData->rc = rc;
|
||||
if( rc > pData->rc ) pData->rc = rc;
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
|
||||
corruptSchema(pData, argv[0], sqlite3_errmsg(db));
|
||||
corruptSchema(pData, argv[1], sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
|
||||
corruptSchema(pData, argv[0], 0);
|
||||
}else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
|
||||
corruptSchema(pData, argv[1], 0);
|
||||
}else{
|
||||
/* If the SQL column is blank it means this is an index that
|
||||
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
|
||||
@ -131,13 +134,13 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
** to do here is record the root page number for that index.
|
||||
*/
|
||||
Index *pIndex;
|
||||
pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
|
||||
pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
|
||||
if( pIndex==0
|
||||
|| sqlite3GetInt32(argv[1],&pIndex->tnum)==0
|
||||
|| sqlite3GetInt32(argv[3],&pIndex->tnum)==0
|
||||
|| pIndex->tnum<2
|
||||
|| sqlite3IndexHasDuplicateRootPage(pIndex)
|
||||
){
|
||||
corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
|
||||
corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -158,7 +161,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
int size;
|
||||
#endif
|
||||
Db *pDb;
|
||||
char const *azArg[4];
|
||||
char const *azArg[6];
|
||||
int meta[5];
|
||||
InitData initData;
|
||||
const char *zMasterName;
|
||||
@ -177,18 +180,20 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
** table name will be inserted automatically by the parser so we can just
|
||||
** use the abbreviation "x" here. The parser will also automatically tag
|
||||
** the schema table as read-only. */
|
||||
azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
|
||||
azArg[1] = "1";
|
||||
azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
|
||||
azArg[0] = "table";
|
||||
azArg[1] = zMasterName = SCHEMA_TABLE(iDb);
|
||||
azArg[2] = azArg[1];
|
||||
azArg[3] = "1";
|
||||
azArg[4] = "CREATE TABLE x(type text,name text,tbl_name text,"
|
||||
"rootpage int,sql text)";
|
||||
azArg[3] = 0;
|
||||
azArg[5] = 0;
|
||||
initData.db = db;
|
||||
initData.iDb = iDb;
|
||||
initData.rc = SQLITE_OK;
|
||||
initData.pzErrMsg = pzErrMsg;
|
||||
initData.mInitFlags = mFlags;
|
||||
initData.nInitRow = 0;
|
||||
sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
|
||||
sqlite3InitCallback(&initData, 5, (char **)azArg, 0);
|
||||
if( initData.rc ){
|
||||
rc = initData.rc;
|
||||
goto error_out;
|
||||
@ -314,7 +319,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
|
||||
{
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
|
||||
"SELECT*FROM\"%w\".%s ORDER BY rowid",
|
||||
db->aDb[iDb].zDbSName, zMasterName);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
@ -635,7 +640,10 @@ static int sqlite3Prepare(
|
||||
rc = sParse.rc;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
|
||||
/* Justification for the ALWAYS(): The only way for rc to be SQLITE_OK and
|
||||
** sParse.pVdbe to be NULL is if the input SQL is an empty string, but in
|
||||
** that case, sParse.explain will be false. */
|
||||
if( sParse.explain && rc==SQLITE_OK && ALWAYS(sParse.pVdbe) ){
|
||||
static const char * const azColName[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
|
||||
"id", "parent", "notused", "detail"
|
||||
@ -660,8 +668,8 @@ static int sqlite3Prepare(
|
||||
if( db->init.busy==0 ){
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
|
||||
}
|
||||
if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
if( rc!=SQLITE_OK || db->mallocFailed ){
|
||||
if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
assert(!(*ppStmt));
|
||||
}else{
|
||||
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
|
||||
|
20
src/printf.c
20
src/printf.c
@ -99,6 +99,12 @@ static const et_info fmtinfo[] = {
|
||||
{ 'r', 10, 1, etORDINAL, 0, 0 },
|
||||
};
|
||||
|
||||
/* Floating point constants used for rounding */
|
||||
static const double arRound[] = {
|
||||
5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05,
|
||||
5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10,
|
||||
};
|
||||
|
||||
/*
|
||||
** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point
|
||||
** conversions will work.
|
||||
@ -517,8 +523,18 @@ void sqlite3_str_vappendf(
|
||||
}
|
||||
if( xtype==etGENERIC && precision>0 ) precision--;
|
||||
testcase( precision>0xfff );
|
||||
for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){}
|
||||
if( xtype==etFLOAT ) realvalue += rounder;
|
||||
idx = precision & 0xfff;
|
||||
rounder = arRound[idx%10];
|
||||
while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; }
|
||||
if( xtype==etFLOAT ){
|
||||
double rx = (double)realvalue;
|
||||
sqlite3_uint64 u;
|
||||
int ex;
|
||||
memcpy(&u, &rx, sizeof(u));
|
||||
ex = -1023 + (int)((u>>52)&0x7ff);
|
||||
if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16;
|
||||
realvalue += rounder;
|
||||
}
|
||||
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
||||
exp = 0;
|
||||
if( sqlite3IsNaN((double)realvalue) ){
|
||||
|
146
src/resolve.c
146
src/resolve.c
@ -96,6 +96,13 @@ static void resolveAlias(
|
||||
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
|
||||
pExpr->flags |= EP_MemToken;
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
if( pExpr->y.pWin!=0 ){
|
||||
pExpr->y.pWin->pOwner = pExpr;
|
||||
}else{
|
||||
assert( db->mallocFailed );
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(db, pDup);
|
||||
}
|
||||
ExprSetProperty(pExpr, EP_Alias);
|
||||
@ -148,6 +155,23 @@ int sqlite3MatchSpanName(
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the double-quoted string mis-feature should be supported.
|
||||
*/
|
||||
static int areDoubleQuotedStringsEnabled(sqlite3 *db, NameContext *pTopNC){
|
||||
if( db->init.busy ) return 1; /* Always support for legacy schemas */
|
||||
if( pTopNC->ncFlags & NC_IsDDL ){
|
||||
/* Currently parsing a DDL statement */
|
||||
if( sqlite3WritableSchema(db) && (db->flags & SQLITE_DqsDML)!=0 ){
|
||||
return 1;
|
||||
}
|
||||
return (db->flags & SQLITE_DqsDDL)!=0;
|
||||
}else{
|
||||
/* Currently parsing a DML statement */
|
||||
return (db->flags & SQLITE_DqsDML)!=0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
|
||||
** that name in the set of source tables in pSrcList and make the pExpr
|
||||
@ -364,7 +388,7 @@ static int lookupName(
|
||||
{
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
if( iCol<0 ){
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
}else if( pExpr->iTable==0 ){
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
@ -396,7 +420,7 @@ static int lookupName(
|
||||
){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -476,7 +500,9 @@ static int lookupName(
|
||||
*/
|
||||
if( cnt==0 && zTab==0 ){
|
||||
assert( pExpr->op==TK_ID );
|
||||
if( ExprHasProperty(pExpr,EP_DblQuoted) ){
|
||||
if( ExprHasProperty(pExpr,EP_DblQuoted)
|
||||
&& areDoubleQuotedStringsEnabled(db, pTopNC)
|
||||
){
|
||||
/* If a double-quoted identifier does not match any known column name,
|
||||
** then treat it as a string.
|
||||
**
|
||||
@ -670,7 +696,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
pExpr->y.pTab = pItem->pTab;
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
break;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
|
||||
@ -730,7 +756,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
FuncDef *pDef; /* Information about the function */
|
||||
u8 enc = ENC(pParse->db); /* The database encoding */
|
||||
int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin));
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
|
||||
#endif
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
zId = pExpr->u.zToken;
|
||||
nId = sqlite3Strlen30(zId);
|
||||
@ -745,7 +773,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
}else{
|
||||
is_agg = pDef->xFinalize!=0;
|
||||
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
|
||||
ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
|
||||
ExprSetProperty(pExpr, EP_Unlikely);
|
||||
if( n==2 ){
|
||||
pExpr->iTable = exprProbability(pList->a[1].pExpr);
|
||||
if( pExpr->iTable<0 ){
|
||||
@ -802,6 +830,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
** SQL is being compiled using sqlite3NestedParse() */
|
||||
no_such_func = 1;
|
||||
pDef = 0;
|
||||
}else
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
|
||||
&& ExprHasProperty(pExpr, EP_Indirect)
|
||||
&& !IN_RENAME_OBJECT
|
||||
){
|
||||
/* Functions tagged with SQLITE_DIRECTONLY may not be used
|
||||
** inside of triggers and views */
|
||||
sqlite3ErrorMsg(pParse, "%s() prohibited in triggers and views",
|
||||
pDef->zName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -811,18 +848,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
|| (pDef->xValue==0 && pDef->xInverse==0)
|
||||
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
|
||||
);
|
||||
if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
if( pDef && pDef->xValue==0 && pWin ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"%.*s() may not be used as a window function", nId, zId
|
||||
);
|
||||
pNC->nErr++;
|
||||
}else if(
|
||||
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
|
||||
|| (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
|
||||
|| (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
|
||||
|| (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin)
|
||||
|| (is_agg && pWin && (pNC->ncFlags & NC_AllowWin)==0)
|
||||
){
|
||||
const char *zType;
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pWin ){
|
||||
zType = "window";
|
||||
}else{
|
||||
zType = "aggregate";
|
||||
@ -850,32 +887,44 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
nId, zId);
|
||||
pNC->nErr++;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"FILTER may not be used with non-aggregate %.*s()",
|
||||
nId, zId
|
||||
);
|
||||
pNC->nErr++;
|
||||
}
|
||||
#endif
|
||||
if( is_agg ){
|
||||
/* Window functions may not be arguments of aggregate functions.
|
||||
** Or arguments of other window functions. But aggregate functions
|
||||
** may be arguments for window functions. */
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0));
|
||||
pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0));
|
||||
#else
|
||||
pNC->ncFlags &= ~NC_AllowAgg;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
else if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
is_agg = 1;
|
||||
}
|
||||
#endif
|
||||
sqlite3WalkExprList(pWalker, pList);
|
||||
if( is_agg ){
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pExpr->y.pWin ){
|
||||
if( pWin ){
|
||||
Select *pSel = pNC->pWinSelect;
|
||||
sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
|
||||
sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition);
|
||||
sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy);
|
||||
sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
|
||||
if( 0==pSel->pWin
|
||||
|| 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin)
|
||||
){
|
||||
pExpr->y.pWin->pNextWin = pSel->pWin;
|
||||
pSel->pWin = pExpr->y.pWin;
|
||||
assert( pWin==pExpr->y.pWin );
|
||||
if( IN_RENAME_OBJECT==0 ){
|
||||
sqlite3WindowUpdate(pParse, pSel->pWinDefn, pWin, pDef);
|
||||
}
|
||||
sqlite3WalkExprList(pWalker, pWin->pPartition);
|
||||
sqlite3WalkExprList(pWalker, pWin->pOrderBy);
|
||||
sqlite3WalkExpr(pWalker, pWin->pFilter);
|
||||
sqlite3WindowLink(pSel, pWin);
|
||||
pNC->ncFlags |= NC_HasWin;
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
@ -883,12 +932,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
NameContext *pNC2 = pNC;
|
||||
pExpr->op = TK_AGG_FUNCTION;
|
||||
pExpr->op2 = 0;
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
|
||||
}
|
||||
#endif
|
||||
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
|
||||
pExpr->op2++;
|
||||
pNC2 = pNC2->pNext;
|
||||
}
|
||||
assert( pDef!=0 );
|
||||
if( pNC2 ){
|
||||
assert( pDef!=0 || IN_RENAME_OBJECT );
|
||||
if( pNC2 && pDef ){
|
||||
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
|
||||
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
|
||||
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
|
||||
@ -926,11 +980,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
}
|
||||
case TK_IS:
|
||||
case TK_ISNOT: {
|
||||
Expr *pRight;
|
||||
Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
|
||||
assert( !ExprHasProperty(pExpr, EP_Reduced) );
|
||||
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
|
||||
** and "x IS NOT FALSE". */
|
||||
if( (pRight = pExpr->pRight)->op==TK_ID ){
|
||||
if( pRight->op==TK_ID ){
|
||||
int rc = resolveExprStep(pWalker, pRight);
|
||||
if( rc==WRC_Abort ) return WRC_Abort;
|
||||
if( pRight->op==TK_TRUEFALSE ){
|
||||
@ -1137,7 +1191,7 @@ static int resolveCompoundOrderBy(
|
||||
int iCol = -1;
|
||||
Expr *pE, *pDup;
|
||||
if( pItem->done ) continue;
|
||||
pE = sqlite3ExprSkipCollate(pItem->pExpr);
|
||||
pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
|
||||
if( sqlite3ExprIsInteger(pE, &iCol) ){
|
||||
if( iCol<=0 || iCol>pEList->nExpr ){
|
||||
resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
|
||||
@ -1231,7 +1285,7 @@ int sqlite3ResolveOrderGroupBy(
|
||||
ExprList *pEList;
|
||||
struct ExprList_item *pItem;
|
||||
|
||||
if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
|
||||
if( pOrderBy==0 || pParse->db->mallocFailed || IN_RENAME_OBJECT ) return 0;
|
||||
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
||||
sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
|
||||
return 1;
|
||||
@ -1253,17 +1307,13 @@ int sqlite3ResolveOrderGroupBy(
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
/*
|
||||
** Walker callback for resolveRemoveWindows().
|
||||
** Walker callback for windowRemoveExprFromSelect().
|
||||
*/
|
||||
static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
|
||||
UNUSED_PARAMETER(pWalker);
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
Window **pp;
|
||||
for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
|
||||
if( *pp==pExpr->y.pWin ){
|
||||
*pp = (*pp)->pNextWin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Window *pWin = pExpr->y.pWin;
|
||||
sqlite3WindowUnlinkFromSelect(pWin);
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
@ -1272,16 +1322,18 @@ static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
|
||||
** Remove any Window objects owned by the expression pExpr from the
|
||||
** Select.pWin list of Select object pSelect.
|
||||
*/
|
||||
static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){
|
||||
Walker sWalker;
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
sWalker.xExprCallback = resolveRemoveWindowsCb;
|
||||
sWalker.u.pSelect = pSelect;
|
||||
sqlite3WalkExpr(&sWalker, pExpr);
|
||||
static void windowRemoveExprFromSelect(Select *pSelect, Expr *pExpr){
|
||||
if( pSelect->pWin ){
|
||||
Walker sWalker;
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
sWalker.xExprCallback = resolveRemoveWindowsCb;
|
||||
sWalker.u.pSelect = pSelect;
|
||||
sqlite3WalkExpr(&sWalker, pExpr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define resolveRemoveWindows(x,y)
|
||||
#endif
|
||||
# define windowRemoveExprFromSelect(a, b)
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
||||
/*
|
||||
** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
|
||||
@ -1318,7 +1370,7 @@ static int resolveOrderGroupBy(
|
||||
pParse = pNC->pParse;
|
||||
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
|
||||
Expr *pE = pItem->pExpr;
|
||||
Expr *pE2 = sqlite3ExprSkipCollate(pE);
|
||||
Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
|
||||
if( zType[0]!='G' ){
|
||||
iCol = resolveAsName(pParse, pSelect->pEList, pE2);
|
||||
if( iCol>0 ){
|
||||
@ -1352,7 +1404,7 @@ static int resolveOrderGroupBy(
|
||||
/* Since this expresion is being changed into a reference
|
||||
** to an identical expression in the result set, remove all Window
|
||||
** objects belonging to the expression from the Select.pWin list. */
|
||||
resolveRemoveWindows(pSelect, pE);
|
||||
windowRemoveExprFromSelect(pSelect, pE);
|
||||
pItem->u.x.iOrderByCol = j+1;
|
||||
}
|
||||
}
|
||||
@ -1652,7 +1704,7 @@ int sqlite3ResolveExprNames(
|
||||
NameContext *pNC, /* Namespace to resolve expressions in. */
|
||||
Expr *pExpr /* The expression to be analyzed. */
|
||||
){
|
||||
u16 savedHasAgg;
|
||||
int savedHasAgg;
|
||||
Walker w;
|
||||
|
||||
if( pExpr==0 ) return SQLITE_OK;
|
||||
@ -1766,7 +1818,7 @@ int sqlite3ResolveSelfReference(
|
||||
}
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = &sSrc;
|
||||
sNC.ncFlags = type;
|
||||
sNC.ncFlags = type | NC_IsDDL;
|
||||
if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
|
||||
if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
|
||||
return rc;
|
||||
|
229
src/select.c
229
src/select.c
@ -100,6 +100,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
|
||||
if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
|
||||
sqlite3WindowListDelete(db, p->pWinDefn);
|
||||
}
|
||||
assert( p->pWin==0 );
|
||||
#endif
|
||||
if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
|
||||
if( bFree ) sqlite3DbFreeNN(db, p);
|
||||
@ -355,7 +356,7 @@ static void addWhereTerm(
|
||||
ExprSetVVAProperty(pEq, EP_NoReduce);
|
||||
pEq->iRightJoinTable = (i16)pE2->iTable;
|
||||
}
|
||||
*ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq);
|
||||
*ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -489,7 +490,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
*/
|
||||
if( pRight->pOn ){
|
||||
if( isOuter ) setJoinExpr(pRight->pOn, pRight->iCursor);
|
||||
p->pWhere = sqlite3ExprAnd(pParse->db, p->pWhere, pRight->pOn);
|
||||
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn);
|
||||
pRight->pOn = 0;
|
||||
}
|
||||
|
||||
@ -663,7 +664,7 @@ static void pushOntoSorter(
|
||||
if( pParse->db->mallocFailed ) return;
|
||||
pOp->p2 = nKey + nData;
|
||||
pKI = pOp->p4.pKeyInfo;
|
||||
memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
|
||||
memset(pKI->aSortFlags, 0, pKI->nKeyField); /* Makes OP_Jump testable */
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
||||
testcase( pKI->nAllField > pKI->nKeyField+2 );
|
||||
pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
|
||||
@ -1274,7 +1275,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
|
||||
int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
|
||||
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
|
||||
if( p ){
|
||||
p->aSortOrder = (u8*)&p->aColl[N+X];
|
||||
p->aSortFlags = (u8*)&p->aColl[N+X];
|
||||
p->nKeyField = (u16)N;
|
||||
p->nAllField = (u16)(N+X);
|
||||
p->enc = ENC(db);
|
||||
@ -1351,7 +1352,7 @@ KeyInfo *sqlite3KeyInfoFromExprList(
|
||||
assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
||||
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
|
||||
pInfo->aColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr);
|
||||
pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
|
||||
pInfo->aSortFlags[i-iStart] = pItem->sortFlags;
|
||||
}
|
||||
}
|
||||
return pInfo;
|
||||
@ -1643,8 +1644,6 @@ static const char *columnTypeImpl(
|
||||
|
||||
assert( pExpr!=0 );
|
||||
assert( pNC->pSrcList!=0 );
|
||||
assert( pExpr->op!=TK_AGG_COLUMN ); /* This routine runes before aggregates
|
||||
** are processed */
|
||||
switch( pExpr->op ){
|
||||
case TK_COLUMN: {
|
||||
/* The expression is a column. Locate the table the column is being
|
||||
@ -1961,12 +1960,11 @@ int sqlite3ColumnsFromExprList(
|
||||
if( (zName = pEList->a[i].zName)!=0 ){
|
||||
/* If the column contains an "AS <name>" phrase, use <name> as the name */
|
||||
}else{
|
||||
Expr *pColExpr = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
|
||||
Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
|
||||
while( pColExpr->op==TK_DOT ){
|
||||
pColExpr = pColExpr->pRight;
|
||||
assert( pColExpr!=0 );
|
||||
}
|
||||
assert( pColExpr->op!=TK_AGG_COLUMN );
|
||||
if( pColExpr->op==TK_COLUMN ){
|
||||
/* For columns use the column name name */
|
||||
int iCol = pColExpr->iColumn;
|
||||
@ -2034,7 +2032,8 @@ int sqlite3ColumnsFromExprList(
|
||||
void sqlite3SelectAddColumnTypeAndCollation(
|
||||
Parse *pParse, /* Parsing contexts */
|
||||
Table *pTab, /* Add column type information to this table */
|
||||
Select *pSelect /* SELECT used to determine types and collations */
|
||||
Select *pSelect, /* SELECT used to determine types and collations */
|
||||
char aff /* Default affinity for columns */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
NameContext sNC;
|
||||
@ -2067,7 +2066,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
||||
pCol->colFlags |= COLFLAG_HASTYPE;
|
||||
}
|
||||
}
|
||||
if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
|
||||
if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
|
||||
pColl = sqlite3ExprCollSeq(pParse, p);
|
||||
if( pColl && pCol->zColl==0 ){
|
||||
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
|
||||
@ -2080,7 +2079,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
||||
** Given a SELECT statement, generate a Table structure that describes
|
||||
** the result set of that SELECT.
|
||||
*/
|
||||
Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, char aff){
|
||||
Table *pTab;
|
||||
sqlite3 *db = pParse->db;
|
||||
u64 savedFlags;
|
||||
@ -2096,14 +2095,11 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
if( pTab==0 ){
|
||||
return 0;
|
||||
}
|
||||
/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
|
||||
** is disabled */
|
||||
assert( db->lookaside.bDisable );
|
||||
pTab->nTabRef = 1;
|
||||
pTab->zName = 0;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff);
|
||||
pTab->iPKey = -1;
|
||||
if( db->mallocFailed ){
|
||||
sqlite3DeleteTable(db, pTab);
|
||||
@ -2257,7 +2253,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
|
||||
}
|
||||
assert( sqlite3KeyInfoIsWriteable(pRet) );
|
||||
pRet->aColl[i] = pColl;
|
||||
pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
|
||||
pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2540,6 +2536,7 @@ static int multiSelect(
|
||||
*/
|
||||
assert( p && p->pPrior ); /* Calling function guarantees this much */
|
||||
assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
|
||||
assert( p->selFlags & SF_Compound );
|
||||
db = pParse->db;
|
||||
pPrior = p->pPrior;
|
||||
dest = *pDest;
|
||||
@ -2967,11 +2964,14 @@ static int generateOutputSubroutine(
|
||||
|
||||
/* If this is a scalar select that is part of an expression, then
|
||||
** store the results in the appropriate memory cell and break out
|
||||
** of the scan loop.
|
||||
** of the scan loop. Note that the select might return multiple columns
|
||||
** if it is the RHS of a row-value IN operator.
|
||||
*/
|
||||
case SRT_Mem: {
|
||||
assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 );
|
||||
sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
|
||||
if( pParse->nErr==0 ){
|
||||
testcase( pIn->nSdst>1 );
|
||||
sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
|
||||
}
|
||||
/* The LIMIT clause will jump out of the loop for us */
|
||||
break;
|
||||
}
|
||||
@ -3228,7 +3228,7 @@ static int multiSelectOrderBy(
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
|
||||
for(i=0; i<nExpr; i++){
|
||||
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
|
||||
pKeyDup->aSortOrder[i] = 0;
|
||||
pKeyDup->aSortFlags[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3478,6 +3478,18 @@ static Expr *substExpr(
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
pExpr = pNew;
|
||||
|
||||
/* Ensure that the expression now has an implicit collation sequence,
|
||||
** just as it did when it was a column of a view or sub-query. */
|
||||
if( pExpr ){
|
||||
if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
|
||||
pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
|
||||
(pColl ? pColl->zName : "BINARY")
|
||||
);
|
||||
}
|
||||
ExprClearProperty(pExpr, EP_Collate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@ -3491,6 +3503,14 @@ static Expr *substExpr(
|
||||
}else{
|
||||
substExprList(pSubst, pExpr->x.pList);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
Window *pWin = pExpr->y.pWin;
|
||||
pWin->pFilter = substExpr(pSubst, pWin->pFilter);
|
||||
substExprList(pSubst, pWin->pPartition);
|
||||
substExprList(pSubst, pWin->pOrderBy);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return pExpr;
|
||||
}
|
||||
@ -3951,6 +3971,7 @@ static int flattenSubquery(
|
||||
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
|
||||
int nSubSrc;
|
||||
u8 jointype = 0;
|
||||
assert( pSub!=0 );
|
||||
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
|
||||
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
|
||||
pSrc = pParent->pSrc; /* FROM clause of the outer query */
|
||||
@ -4034,7 +4055,7 @@ static int flattenSubquery(
|
||||
if( isLeftJoin>0 ){
|
||||
setJoinExpr(pWhere, iNewParent);
|
||||
}
|
||||
pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
|
||||
pParent->pWhere = sqlite3ExprAnd(pParse, pWhere, pParent->pWhere);
|
||||
if( db->mallocFailed==0 ){
|
||||
SubstContext x;
|
||||
x.pParse = pParse;
|
||||
@ -4045,10 +4066,10 @@ static int flattenSubquery(
|
||||
substSelect(&x, pParent, 0);
|
||||
}
|
||||
|
||||
/* The flattened query is distinct if either the inner or the
|
||||
** outer query is distinct.
|
||||
*/
|
||||
pParent->selFlags |= pSub->selFlags & SF_Distinct;
|
||||
/* The flattened query is a compound if either the inner or the
|
||||
** outer query is a compound. */
|
||||
pParent->selFlags |= pSub->selFlags & SF_Compound;
|
||||
assert( (pSub->selFlags & SF_Distinct)==0 ); /* restriction (17b) */
|
||||
|
||||
/*
|
||||
** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y;
|
||||
@ -4369,9 +4390,9 @@ static int pushDownWhereTerms(
|
||||
x.pEList = pSubq->pEList;
|
||||
pNew = substExpr(&x, pNew);
|
||||
if( pSubq->selFlags & SF_Aggregate ){
|
||||
pSubq->pHaving = sqlite3ExprAnd(pParse->db, pSubq->pHaving, pNew);
|
||||
pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
|
||||
}else{
|
||||
pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew);
|
||||
pSubq->pWhere = sqlite3ExprAnd(pParse, pSubq->pWhere, pNew);
|
||||
}
|
||||
pSubq = pSubq->pPrior;
|
||||
}
|
||||
@ -4401,24 +4422,27 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
|
||||
ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
|
||||
const char *zFunc; /* Name of aggregate function pFunc */
|
||||
ExprList *pOrderBy;
|
||||
u8 sortOrder;
|
||||
u8 sortFlags;
|
||||
|
||||
assert( *ppMinMax==0 );
|
||||
assert( pFunc->op==TK_AGG_FUNCTION );
|
||||
if( pEList==0 || pEList->nExpr!=1 ) return eRet;
|
||||
assert( !IsWindowFunc(pFunc) );
|
||||
if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){
|
||||
return eRet;
|
||||
}
|
||||
zFunc = pFunc->u.zToken;
|
||||
if( sqlite3StrICmp(zFunc, "min")==0 ){
|
||||
eRet = WHERE_ORDERBY_MIN;
|
||||
sortOrder = SQLITE_SO_ASC;
|
||||
sortFlags = KEYINFO_ORDER_BIGNULL;
|
||||
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
|
||||
eRet = WHERE_ORDERBY_MAX;
|
||||
sortOrder = SQLITE_SO_DESC;
|
||||
sortFlags = KEYINFO_ORDER_DESC;
|
||||
}else{
|
||||
return eRet;
|
||||
}
|
||||
*ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
|
||||
assert( pOrderBy!=0 || db->mallocFailed );
|
||||
if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
|
||||
if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags;
|
||||
return eRet;
|
||||
}
|
||||
|
||||
@ -4452,7 +4476,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|
||||
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
|
||||
if( NEVER(pAggInfo->nFunc==0) ) return 0;
|
||||
if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
|
||||
if( pExpr->flags&EP_Distinct ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0;
|
||||
|
||||
return pTab;
|
||||
}
|
||||
@ -4797,7 +4821,7 @@ int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
|
||||
return SQLITE_OK;
|
||||
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4843,6 +4867,10 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
if( (selFlags & SF_Expanded)!=0 ){
|
||||
return WRC_Prune;
|
||||
}
|
||||
if( pWalker->eCode ){
|
||||
/* Renumber selId because it has been copied from a view */
|
||||
p->selId = ++pParse->nSelect;
|
||||
}
|
||||
pTabList = p->pSrc;
|
||||
pEList = p->pEList;
|
||||
sqlite3WithPush(pParse, p->pWith, 0);
|
||||
@ -4892,12 +4920,19 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
|
||||
if( IsVirtual(pTab) || pTab->pSelect ){
|
||||
i16 nCol;
|
||||
u8 eCodeOrig = pWalker->eCode;
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
|
||||
assert( pFrom->pSelect==0 );
|
||||
if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
|
||||
pTab->zName);
|
||||
}
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
|
||||
nCol = pTab->nCol;
|
||||
pTab->nCol = -1;
|
||||
pWalker->eCode = 1; /* Turn on Select.selId renumbering */
|
||||
sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
||||
pWalker->eCode = eCodeOrig;
|
||||
pTab->nCol = nCol;
|
||||
}
|
||||
#endif
|
||||
@ -5147,6 +5182,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
|
||||
}
|
||||
w.xSelectCallback = selectExpander;
|
||||
w.xSelectCallback2 = selectPopWith;
|
||||
w.eCode = 0;
|
||||
sqlite3WalkSelect(&w, pSelect);
|
||||
}
|
||||
|
||||
@ -5184,7 +5220,8 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
||||
Select *pSel = pFrom->pSelect;
|
||||
if( pSel ){
|
||||
while( pSel->pPrior ) pSel = pSel->pPrior;
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
|
||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
|
||||
SQLITE_AFF_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5324,6 +5361,25 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
|
||||
int regAgg;
|
||||
ExprList *pList = pF->pExpr->x.pList;
|
||||
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
|
||||
assert( !IsWindowFunc(pF->pExpr) );
|
||||
if( ExprHasProperty(pF->pExpr, EP_WinFunc) ){
|
||||
Expr *pFilter = pF->pExpr->y.pWin->pFilter;
|
||||
if( pAggInfo->nAccumulator
|
||||
&& (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
|
||||
){
|
||||
if( regHit==0 ) regHit = ++pParse->nMem;
|
||||
/* If this is the first row of the group (regAcc==0), clear the
|
||||
** "magnet" register regHit so that the accumulator registers
|
||||
** are populated if the FILTER clause jumps over the the
|
||||
** invocation of min() or max() altogether. Or, if this is not
|
||||
** the first row (regAcc==1), set the magnet register so that the
|
||||
** accumulators are not populated unless the min()/max() is invoked and
|
||||
** indicates that they should be. */
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regAcc, regHit);
|
||||
}
|
||||
addrNext = sqlite3VdbeMakeLabel(pParse);
|
||||
sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
if( pList ){
|
||||
nArg = pList->nExpr;
|
||||
regAgg = sqlite3GetTempRange(pParse, nArg);
|
||||
@ -5333,7 +5389,9 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
|
||||
regAgg = 0;
|
||||
}
|
||||
if( pF->iDistinct>=0 ){
|
||||
addrNext = sqlite3VdbeMakeLabel(pParse);
|
||||
if( addrNext==0 ){
|
||||
addrNext = sqlite3VdbeMakeLabel(pParse);
|
||||
}
|
||||
testcase( nArg==0 ); /* Error condition */
|
||||
testcase( nArg>1 ); /* Also an error */
|
||||
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
|
||||
@ -5369,6 +5427,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
|
||||
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
|
||||
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
|
||||
}
|
||||
|
||||
pAggInfo->directMode = 0;
|
||||
if( addrHitTest ){
|
||||
sqlite3VdbeJumpHere(v, addrHitTest);
|
||||
@ -5414,11 +5473,11 @@ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
|
||||
Select *pS = pWalker->u.pSelect;
|
||||
if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
|
||||
sqlite3 *db = pWalker->pParse->db;
|
||||
Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0);
|
||||
Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
|
||||
if( pNew ){
|
||||
Expr *pWhere = pS->pWhere;
|
||||
SWAP(Expr, *pNew, *pExpr);
|
||||
pNew = sqlite3ExprAnd(db, pWhere, pNew);
|
||||
pNew = sqlite3ExprAnd(pWalker->pParse, pWhere, pNew);
|
||||
pS->pWhere = pNew;
|
||||
pWalker->eCode = 1;
|
||||
}
|
||||
@ -5473,15 +5532,19 @@ static struct SrcList_item *isSelfJoinView(
|
||||
if( pItem->pSelect==0 ) continue;
|
||||
if( pItem->fg.viaCoroutine ) continue;
|
||||
if( pItem->zName==0 ) continue;
|
||||
if( sqlite3_stricmp(pItem->zDatabase, pThis->zDatabase)!=0 ) continue;
|
||||
assert( pItem->pTab!=0 );
|
||||
assert( pThis->pTab!=0 );
|
||||
if( pItem->pTab->pSchema!=pThis->pTab->pSchema ) continue;
|
||||
if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue;
|
||||
pS1 = pItem->pSelect;
|
||||
if( pThis->pSelect->selId!=pS1->selId ){
|
||||
if( pItem->pTab->pSchema==0 && pThis->pSelect->selId!=pS1->selId ){
|
||||
/* The query flattener left two different CTE tables with identical
|
||||
** names in the same FROM clause. */
|
||||
continue;
|
||||
}
|
||||
if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1) ){
|
||||
if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1)
|
||||
|| sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1)
|
||||
){
|
||||
/* The view was modified by some other optimization such as
|
||||
** pushDownWhereTerms() */
|
||||
continue;
|
||||
@ -5506,7 +5569,8 @@ static struct SrcList_item *isSelfJoinView(
|
||||
** * The subquery is a UNION ALL of two or more terms
|
||||
** * The subquery does not have a LIMIT clause
|
||||
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
|
||||
** * The outer query is a simple count(*)
|
||||
** * The outer query is a simple count(*) with no WHERE clause or other
|
||||
** extraneous syntax.
|
||||
**
|
||||
** Return TRUE if the optimization is undertaken.
|
||||
*/
|
||||
@ -5517,6 +5581,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
sqlite3 *db;
|
||||
if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */
|
||||
if( p->pEList->nExpr!=1 ) return 0; /* Single result column */
|
||||
if( p->pWhere ) return 0;
|
||||
if( p->pGroupBy ) return 0;
|
||||
pExpr = p->pEList->a[0].pExpr;
|
||||
if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */
|
||||
if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */
|
||||
@ -5829,7 +5895,7 @@ int sqlite3Select(
|
||||
** assume the column name is non-NULL and segfault. The use of an empty
|
||||
** string for the fake column name seems safer.
|
||||
*/
|
||||
if( pItem->colUsed==0 ){
|
||||
if( pItem->colUsed==0 && pItem->zName!=0 ){
|
||||
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase);
|
||||
}
|
||||
|
||||
@ -5843,8 +5909,15 @@ int sqlite3Select(
|
||||
** technically harmless for it to be generated multiple times. The
|
||||
** following assert() will detect if something changes to cause
|
||||
** the same subquery to be coded multiple times, as a signal to the
|
||||
** developers to try to optimize the situation. */
|
||||
assert( pItem->addrFillSub==0 );
|
||||
** developers to try to optimize the situation.
|
||||
**
|
||||
** Update 2019-07-24:
|
||||
** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40.
|
||||
** The dbsqlfuzz fuzzer found a case where the same subquery gets
|
||||
** coded twice. So this assert() now becomes a testcase(). It should
|
||||
** be very rare, though.
|
||||
*/
|
||||
testcase( pItem->addrFillSub!=0 );
|
||||
|
||||
/* Increment Parse.nHeight by the height of the largest expression
|
||||
** tree referred to by this, the parent select. The child select
|
||||
@ -5918,7 +5991,7 @@ int sqlite3Select(
|
||||
int retAddr;
|
||||
struct SrcList_item *pPrior;
|
||||
|
||||
assert( pItem->addrFillSub==0 );
|
||||
testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
|
||||
pItem->addrFillSub = topAddr+1;
|
||||
@ -6158,23 +6231,35 @@ int sqlite3Select(
|
||||
}
|
||||
assert( 66==sqlite3LogEst(100) );
|
||||
if( p->nSelectRow>66 ) p->nSelectRow = 66;
|
||||
|
||||
/* If there is both a GROUP BY and an ORDER BY clause and they are
|
||||
** identical, then it may be possible to disable the ORDER BY clause
|
||||
** on the grounds that the GROUP BY will cause elements to come out
|
||||
** in the correct order. It also may not - the GROUP BY might use a
|
||||
** database index that causes rows to be grouped together as required
|
||||
** but not actually sorted. Either way, record the fact that the
|
||||
** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
|
||||
** variable. */
|
||||
if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){
|
||||
int ii;
|
||||
/* The GROUP BY processing doesn't care whether rows are delivered in
|
||||
** ASC or DESC order - only that each group is returned contiguously.
|
||||
** So set the ASC/DESC flags in the GROUP BY to match those in the
|
||||
** ORDER BY to maximize the chances of rows being delivered in an
|
||||
** order that makes the ORDER BY redundant. */
|
||||
for(ii=0; ii<pGroupBy->nExpr; ii++){
|
||||
u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC;
|
||||
pGroupBy->a[ii].sortFlags = sortFlags;
|
||||
}
|
||||
if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
|
||||
orderByGrp = 1;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
assert( 0==sqlite3LogEst(1) );
|
||||
p->nSelectRow = 0;
|
||||
}
|
||||
|
||||
/* If there is both a GROUP BY and an ORDER BY clause and they are
|
||||
** identical, then it may be possible to disable the ORDER BY clause
|
||||
** on the grounds that the GROUP BY will cause elements to come out
|
||||
** in the correct order. It also may not - the GROUP BY might use a
|
||||
** database index that causes rows to be grouped together as required
|
||||
** but not actually sorted. Either way, record the fact that the
|
||||
** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
|
||||
** variable. */
|
||||
if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
|
||||
orderByGrp = 1;
|
||||
}
|
||||
|
||||
/* Create a label to jump to when we want to abort the query */
|
||||
addrEnd = sqlite3VdbeMakeLabel(pParse);
|
||||
|
||||
@ -6209,9 +6294,16 @@ int sqlite3Select(
|
||||
minMaxFlag = WHERE_ORDERBY_NORMAL;
|
||||
}
|
||||
for(i=0; i<sAggInfo.nFunc; i++){
|
||||
assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
|
||||
Expr *pExpr = sAggInfo.aFunc[i].pExpr;
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
sNC.ncFlags |= NC_InAggFunc;
|
||||
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
assert( !IsWindowFunc(pExpr) );
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter);
|
||||
}
|
||||
#endif
|
||||
sNC.ncFlags &= ~NC_InAggFunc;
|
||||
}
|
||||
sAggInfo.mxReg = pParse->nMem;
|
||||
@ -6523,13 +6615,18 @@ int sqlite3Select(
|
||||
{
|
||||
int regAcc = 0; /* "populate accumulators" flag */
|
||||
|
||||
/* If there are accumulator registers but no min() or max() functions,
|
||||
** allocate register regAcc. Register regAcc will contain 0 the first
|
||||
** time the inner loop runs, and 1 thereafter. The code generated
|
||||
** by updateAccumulator() only updates the accumulator registers if
|
||||
** regAcc contains 0. */
|
||||
/* If there are accumulator registers but no min() or max() functions
|
||||
** without FILTER clauses, allocate register regAcc. Register regAcc
|
||||
** will contain 0 the first time the inner loop runs, and 1 thereafter.
|
||||
** The code generated by updateAccumulator() uses this to ensure
|
||||
** that the accumulator registers are (a) updated only once if
|
||||
** there are no min() or max functions or (b) always updated for the
|
||||
** first row visited by the aggregate, so that they are updated at
|
||||
** least once even if the FILTER clause means the min() or max()
|
||||
** function visits zero rows. */
|
||||
if( sAggInfo.nAccumulator ){
|
||||
for(i=0; i<sAggInfo.nFunc; i++){
|
||||
if( ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_WinFunc) ) continue;
|
||||
if( sAggInfo.aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ) break;
|
||||
}
|
||||
if( i==sAggInfo.nFunc ){
|
||||
|
1233
src/shell.c.in
1233
src/shell.c.in
File diff suppressed because it is too large
Load Diff
@ -65,12 +65,12 @@ typedef struct {
|
||||
int (*get_iv_sz)(void *ctx);
|
||||
int (*get_block_sz)(void *ctx);
|
||||
int (*get_hmac_sz)(void *ctx, int algorithm);
|
||||
int (*ctx_copy)(void *target_ctx, void *source_ctx);
|
||||
int (*ctx_cmp)(void *c1, void *c2);
|
||||
int (*ctx_init)(void **ctx);
|
||||
int (*ctx_free)(void **ctx);
|
||||
int (*fips_status)(void *ctx);
|
||||
const char* (*get_provider_version)(void *ctx);
|
||||
int (*id)(void *ctx);
|
||||
void* (*status)(void *ctx);
|
||||
} sqlcipher_provider;
|
||||
|
||||
/* utility functions */
|
||||
@ -87,6 +87,16 @@ void sqlcipher_free(void *, int);
|
||||
int sqlcipher_register_provider(sqlcipher_provider *);
|
||||
sqlcipher_provider* sqlcipher_get_provider(void);
|
||||
|
||||
#define SQLCIPHER_MUTEX_PROVIDER 0
|
||||
#define SQLCIPHER_MUTEX_PROVIDER_ACTIVATE 1
|
||||
#define SQLCIPHER_MUTEX_PROVIDER_RAND 2
|
||||
#define SQLCIPHER_MUTEX_RESERVED1 3
|
||||
#define SQLCIPHER_MUTEX_RESERVED2 4
|
||||
#define SQLCIPHER_MUTEX_RESERVED3 5
|
||||
#define SQLCIPHER_MUTEX_COUNT 6
|
||||
|
||||
sqlite3_mutex* sqlcipher_mutex(int);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
118
src/sqlite.h.in
118
src/sqlite.h.in
@ -1296,8 +1296,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
|
||||
** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
|
||||
** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
|
||||
** to test whether a file is at least readable. The file can be a
|
||||
** directory.
|
||||
** to test whether a file is at least readable. The SQLITE_ACCESS_READ
|
||||
** flag is never actually used and is not implemented in the built-in
|
||||
** VFSes of SQLite. The file is named by the second argument and can be a
|
||||
** directory. The xAccess method returns [SQLITE_OK] on success or some
|
||||
** non-zero error code if there is an I/O error or if the name of
|
||||
** the file given in the second argument is illegal. If SQLITE_OK
|
||||
** is returned, then non-zero or zero is written into *pResOut to indicate
|
||||
** whether or not the file is accessible.
|
||||
**
|
||||
** ^SQLite will always allocate at least mxPathname+1 bytes for the
|
||||
** output buffer xFullPathname. The exact size of the output buffer
|
||||
@ -2087,6 +2093,17 @@ struct sqlite3_mem_methods {
|
||||
** following this call. The second parameter may be a NULL pointer, in
|
||||
** which case the trigger setting is not reported back. </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
|
||||
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
|
||||
** There should be two additional arguments.
|
||||
** The first argument is an integer which is 0 to disable views,
|
||||
** positive to enable views or negative to leave the setting unchanged.
|
||||
** The second parameter is a pointer to an integer into which
|
||||
** is written 0 or 1 to indicate whether views are disabled or enabled
|
||||
** following this call. The second parameter may be a NULL pointer, in
|
||||
** which case the view setting is not reported back. </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
||||
** <dd> ^This option is used to enable or disable the
|
||||
@ -2198,6 +2215,7 @@ struct sqlite3_mem_methods {
|
||||
** features include but are not limited to the following:
|
||||
** <ul>
|
||||
** <li> The [PRAGMA writable_schema=ON] statement.
|
||||
** <li> The [PRAGMA journal_mode=OFF] statement.
|
||||
** <li> Writes to the [sqlite_dbpage] virtual table.
|
||||
** <li> Direct writes to [shadow tables].
|
||||
** </ul>
|
||||
@ -2213,6 +2231,34 @@ struct sqlite3_mem_methods {
|
||||
** integer into which is written 0 or 1 to indicate whether the writable_schema
|
||||
** is enabled or disabled following this call.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
|
||||
** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
|
||||
** the legacy behavior of the [ALTER TABLE RENAME] command such it
|
||||
** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
|
||||
** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
|
||||
** additional information. This feature can also be turned on and off
|
||||
** using the [PRAGMA legacy_alter_table] statement.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_DQS_DML]]
|
||||
** <dt>SQLITE_DBCONFIG_DQS_DML</td>
|
||||
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
|
||||
** the legacy [double-quoted string literal] misfeature for DML statement
|
||||
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
|
||||
** default value of this setting is determined by the [-DSQLITE_DQS]
|
||||
** compile-time option.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_DQS_DDL]]
|
||||
** <dt>SQLITE_DBCONFIG_DQS_DDL</td>
|
||||
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
|
||||
** the legacy [double-quoted string literal] misfeature for DDL statements,
|
||||
** such as CREATE TABLE and CREATE INDEX. The
|
||||
** default value of this setting is determined by the [-DSQLITE_DQS]
|
||||
** compile-time option.
|
||||
** </dd>
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
@ -2227,7 +2273,11 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
|
||||
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
|
||||
#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */
|
||||
#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
|
||||
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
|
||||
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1015 /* Largest DBCONFIG */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||
@ -3776,7 +3826,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** ^The specific value of WHERE-clause [parameter] might influence the
|
||||
** choice of query plan if the parameter is the left-hand side of a [LIKE]
|
||||
** or [GLOB] operator or if the parameter is compared to an indexed column
|
||||
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
|
||||
** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
|
||||
** </li>
|
||||
** </ol>
|
||||
**
|
||||
@ -4811,6 +4861,12 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
|
||||
** perform additional optimizations on deterministic functions, so use
|
||||
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
|
||||
**
|
||||
** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
|
||||
** flag, which if present prevents the function from being invoked from
|
||||
** within VIEWs or TRIGGERs. For security reasons, the [SQLITE_DIRECTONLY]
|
||||
** flag is recommended for any application-defined SQL function that has
|
||||
** side-effects.
|
||||
**
|
||||
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
|
||||
** function can gain access to this pointer using [sqlite3_user_data()].)^
|
||||
**
|
||||
@ -4927,8 +4983,30 @@ int sqlite3_create_window_function(
|
||||
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
|
||||
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
|
||||
** [sqlite3_create_function_v2()].
|
||||
**
|
||||
** The SQLITE_DETERMINISTIC flag means that the new function will always
|
||||
** maps the same inputs into the same output. The abs() function is
|
||||
** deterministic, for example, but randomblob() is not.
|
||||
**
|
||||
** The SQLITE_DIRECTONLY flag means that the function may only be invoked
|
||||
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs. This is
|
||||
** a security feature which is recommended for all
|
||||
** [application-defined SQL functions] that have side-effects. This flag
|
||||
** prevents an attacker from adding triggers and views to a schema then
|
||||
** tricking a high-privilege application into causing unintended side-effects
|
||||
** while performing ordinary queries.
|
||||
**
|
||||
** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
|
||||
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
|
||||
** Specifying this flag makes no difference for scalar or aggregate user
|
||||
** functions. However, if it is not specified for a user-defined window
|
||||
** function, then any sub-types belonging to arguments passed to the window
|
||||
** function may be discarded before the window function is called (i.e.
|
||||
** sqlite3_value_subtype() will always return 0).
|
||||
*/
|
||||
#define SQLITE_DETERMINISTIC 0x800
|
||||
#define SQLITE_DETERMINISTIC 0x000000800
|
||||
#define SQLITE_DIRECTONLY 0x000080000
|
||||
#define SQLITE_SUBTYPE 0x000100000
|
||||
|
||||
/*
|
||||
** CAPI3REF: Deprecated Functions
|
||||
@ -6590,6 +6668,12 @@ struct sqlite3_index_info {
|
||||
** ^The sqlite3_create_module()
|
||||
** interface is equivalent to sqlite3_create_module_v2() with a NULL
|
||||
** destructor.
|
||||
**
|
||||
** ^If the third parameter (the pointer to the sqlite3_module object) is
|
||||
** NULL then no new module is create and any existing modules with the
|
||||
** same name are dropped.
|
||||
**
|
||||
** See also: [sqlite3_drop_modules()]
|
||||
*/
|
||||
int sqlite3_create_module(
|
||||
sqlite3 *db, /* SQLite connection to register module with */
|
||||
@ -6605,6 +6689,23 @@ int sqlite3_create_module_v2(
|
||||
void(*xDestroy)(void*) /* Module destructor function */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Remove Unnecessary Virtual Table Implementations
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_drop_modules(D,L) interface removes all virtual
|
||||
** table modules from database connection D except those named on list L.
|
||||
** The L parameter must be either NULL or a pointer to an array of pointers
|
||||
** to strings where the array is terminated by a single NULL pointer.
|
||||
** ^If the L parameter is NULL, then all virtual table modules are removed.
|
||||
**
|
||||
** See also: [sqlite3_create_module()]
|
||||
*/
|
||||
int sqlite3_drop_modules(
|
||||
sqlite3 *db, /* Remove modules from this connection */
|
||||
const char **azKeep /* Except, do not remove the ones named here */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Virtual Table Instance Object
|
||||
** KEYWORDS: sqlite3_vtab
|
||||
@ -7313,7 +7414,7 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_FIRST 5
|
||||
#define SQLITE_TESTCTRL_PRNG_SAVE 5
|
||||
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
|
||||
#define SQLITE_TESTCTRL_PRNG_RESET 7
|
||||
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
|
||||
#define SQLITE_TESTCTRL_BITVEC_TEST 8
|
||||
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
|
||||
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
|
||||
@ -7335,7 +7436,10 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
||||
#define SQLITE_TESTCTRL_IMPOSTER 25
|
||||
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
||||
#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
||||
#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 */
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Keyword Checking
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user