Merge recent trunk changes into the sessions branch.
FossilOrigin-Name: 7e068e39b3b31364271664e0afb1cd95a235c26f
This commit is contained in:
commit
89ded43b36
@ -370,6 +370,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_quota.c \
|
||||
$(TOP)/src/test_regexp.c \
|
||||
$(TOP)/src/test_rtree.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
|
@ -696,6 +696,7 @@ TESTSRC = \
|
||||
$(TOP)\src\test_osinst.c \
|
||||
$(TOP)\src\test_pcache.c \
|
||||
$(TOP)\src\test_quota.c \
|
||||
$(TOP)\src\test_regexp.c \
|
||||
$(TOP)\src\test_rtree.c \
|
||||
$(TOP)\src\test_schema.c \
|
||||
$(TOP)\src\test_server.c \
|
||||
|
47
configure
vendored
47
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.62 for sqlite 3.7.15.
|
||||
# Generated by GNU Autoconf 2.62 for sqlite 3.7.16.
|
||||
#
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.7.15'
|
||||
PACKAGE_STRING='sqlite 3.7.15'
|
||||
PACKAGE_VERSION='3.7.16'
|
||||
PACKAGE_STRING='sqlite 3.7.16'
|
||||
PACKAGE_BUGREPORT=''
|
||||
|
||||
# Factoring default headers for most tests.
|
||||
@ -1484,7 +1484,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.7.15 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.7.16 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1549,7 +1549,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.7.15:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.7.16:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1665,7 +1665,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.7.15
|
||||
sqlite configure 3.7.16
|
||||
generated by GNU Autoconf 2.62
|
||||
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
@ -1679,7 +1679,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.7.15, which was
|
||||
It was created by sqlite $as_me 3.7.16, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -3733,13 +3733,13 @@ if test "${lt_cv_nm_interface+set}" = set; then
|
||||
else
|
||||
lt_cv_nm_interface="BSD nm"
|
||||
echo "int some_variable = 0;" > conftest.$ac_ext
|
||||
(eval echo "\"\$as_me:3737: $ac_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:3736: $ac_compile\"" >&5)
|
||||
(eval "$ac_compile" 2>conftest.err)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3740: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval echo "\"\$as_me:3739: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3743: output\"" >&5)
|
||||
(eval echo "\"\$as_me:3742: output\"" >&5)
|
||||
cat conftest.out >&5
|
||||
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
|
||||
lt_cv_nm_interface="MS dumpbin"
|
||||
@ -4961,7 +4961,7 @@ ia64-*-hpux*)
|
||||
;;
|
||||
*-*-irix6*)
|
||||
# Find out which ABI we are using.
|
||||
echo '#line 4965 "configure"' > conftest.$ac_ext
|
||||
echo '#line 4964 "configure"' > conftest.$ac_ext
|
||||
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
@ -6830,11 +6830,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:6834: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6833: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:6838: \$? = $ac_status" >&5
|
||||
echo "$as_me:6837: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@ -7169,11 +7169,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7173: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7172: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:7177: \$? = $ac_status" >&5
|
||||
echo "$as_me:7176: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@ -7274,11 +7274,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7278: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7277: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7282: \$? = $ac_status" >&5
|
||||
echo "$as_me:7281: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@ -7329,11 +7329,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7333: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7332: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7337: \$? = $ac_status" >&5
|
||||
echo "$as_me:7336: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@ -10142,7 +10142,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 10146 "configure"
|
||||
#line 10145 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@ -10238,7 +10238,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 10242 "configure"
|
||||
#line 10241 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@ -12908,7 +12908,6 @@ $as_echo "file not found" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
fi
|
||||
if test "${use_tcl}" = "no" ; then
|
||||
@ -14033,7 +14032,7 @@ exec 6>&1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.7.15, which was
|
||||
This file was extended by sqlite $as_me 3.7.16, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -14086,7 +14085,7 @@ Report bugs to <bug-autoconf@gnu.org>."
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.7.15
|
||||
sqlite config.status 3.7.16
|
||||
configured by $0, generated by GNU Autoconf 2.62,
|
||||
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
|
||||
|
1
main.mk
1
main.mk
@ -254,6 +254,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_osinst.c \
|
||||
$(TOP)/src/test_pcache.c \
|
||||
$(TOP)/src/test_quota.c \
|
||||
$(TOP)/src/test_regexp.c \
|
||||
$(TOP)/src/test_rtree.c \
|
||||
$(TOP)/src/test_schema.c \
|
||||
$(TOP)/src/test_server.c \
|
||||
|
84
manifest
84
manifest
@ -1,12 +1,12 @@
|
||||
C Pull\sthe\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch,\sand\sin\sparticular\nthe\scollating-sequence\srefactorization.
|
||||
D 2012-12-08T23:37:22.043
|
||||
C Merge\srecent\strunk\schanges\sinto\sthe\ssessions\sbranch.
|
||||
D 2013-01-03T22:22:55.634
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400
|
||||
F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc 26fd6e94ef8ed7d8b8c84b96b8347c52485f6d83
|
||||
F Makefile.msc f74e5635d39e882c915c8b988848a744b3fb3a6a
|
||||
F Makefile.vxworks b18ad88e9a8c6a001f5cf4a389116a4f1a7ab45f
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION edab4af5a4623f8198833ea481ce98ab53750a8d
|
||||
F VERSION 6d4f66eaebabc42ef8c2a4d2d0caf4ce7ee81137
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
@ -15,7 +15,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 20ac96f94269b3e2417b91fac63a9db0eb0a9b15 x
|
||||
F configure 5f2564ac77bef7db2484083452b011822a02d515 x
|
||||
F configure.ac 81c43d151d0b0e406be056394cc9ff4cb3fd0444
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
|
||||
@ -115,7 +115,7 @@ F ext/session/sqlite3session.h f374c9c4c96e08f67ac418871c29d423245c7673
|
||||
F ext/session/test_session.c ea4dc9b4a1895c8e6bddcbfe3838d7eb57df2d99
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk d87518f07b121bd397117bf1f144f399388c7e06
|
||||
F main.mk f2fd62730fb5c7771f565f1ea9da70f481eb9577
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@ -130,10 +130,10 @@ F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
|
||||
F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410
|
||||
F src/attach.c ea5247f240e2c08afd608e9beb380814b86655e1
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c cab40f2c1fe79d6eb93d3b4086c78c41ad2fa5d0
|
||||
F src/backup.c 32e35a3a4ea55b45c0e5f74eeb793aec71491517
|
||||
F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c eccee944cb2221e919d7a855e5928d8643194b14
|
||||
F src/btree.c 7a80e4a67f32a2494c383a28a495bf3bd71cc230
|
||||
F src/btree.h 3ad7964d6c5b1c7bff569aab6adfa075f8bf06cd
|
||||
F src/btreeInt.h 4e5c2bd0f9b36b2a815a6d84f771a61a65830621
|
||||
F src/build.c f4f86c07002c6f3ee96c1e34e0e993a962ef2c73
|
||||
@ -142,16 +142,16 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 72a70dcfda75d3a1f81041ce4573e7afddcd8e4e
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 9bc9463952bdc9fc43111b1f9c83a0af9b8e2239
|
||||
F src/expr.c 0e41d66d868b37dbc0e041c342e0036fad27e705
|
||||
F src/expr.c 4dff0b04eaaf133789279c6b8cd69175dfbb1691
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c dcb7c37a4bf526ded7b24a01a60fe071bcd160a2
|
||||
F src/fkey.c 5b7a12e2f8620e855b0478a9a6798df9967bb277
|
||||
F src/func.c 8147799b048065a1590805be464d05b4913e652c
|
||||
F src/global.c e59ecd2c553ad0d4bfbc84ca71231336f8993a7a
|
||||
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c 36c17b9b97a9287aa8561f138d893ddf2b25d0b2
|
||||
F src/journal.c eb7b9f5e783266521bcd9b2b93d419a219411f71
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
|
||||
@ -176,26 +176,26 @@ F src/os_unix.c ad459bb62eb6f3f6aae26d97b1a28fbac7bf0260
|
||||
F src/os_win.c ce1f5db8a7bb4d6f2092b1b2cb9631bec54a6320
|
||||
F src/pager.c 4092c907222cfd451c74fe6bd2fd64b342f7190f
|
||||
F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0
|
||||
F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724
|
||||
F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c
|
||||
F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9
|
||||
F src/pragma.c 015723c48072781d2740e310ab04dc92956b76d1
|
||||
F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17
|
||||
F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c
|
||||
F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f
|
||||
F src/resolve.c 521bdc0f6c7cf8246c0b9167d726d84005097c30
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c e6daa524bbdfa98f4abdb8cb281498f0047d3161
|
||||
F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019
|
||||
F src/sqlite.h.in 2be63c600ddc118753c6058639b282554d7f759c
|
||||
F src/select.c 395e458a6dc611cbe1179f424753f0c344957607
|
||||
F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843
|
||||
F src/sqlite.h.in 6a7a592aacc98674f39cb520cb7a7af87c2c2438
|
||||
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h e08e87a07d3cbbc57423c78f56b334579ef17741
|
||||
F src/sqliteInt.h f3f74ba8e76a9a850bfc38a529e7d7ad8227d0be
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 289be7b639406314813219ee7bc043d21f36ab12
|
||||
F src/tclsqlite.c 0faa4b56ab352368b0bffa0874de5cf5c9d89c7e
|
||||
F src/test1.c f62769c989146149590662ab02de4a813813a9c5
|
||||
F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf
|
||||
F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d
|
||||
@ -229,6 +229,7 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
|
||||
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
|
||||
F src/test_quota.c 0e0e2e3bf6766b101ecccd8c042b66e44e9be8f5
|
||||
F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
|
||||
F src/test_regexp.c 8d91d00e45e899eb13575bec4870ce415900bec6
|
||||
F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
|
||||
@ -249,7 +250,7 @@ F src/update.c abb0fcabe551dae0a133fd2a4370b5a8c23b1831
|
||||
F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f
|
||||
F src/util.c 0af2e515dc0dabacec931bca39525f6c3f1c5455
|
||||
F src/vacuum.c 2727bdd08847fcb6b2d2da6d14f018910e8645d3
|
||||
F src/vdbe.c 85576363e303ffa5dc7d368af4cfd6bfbee96db1
|
||||
F src/vdbe.c 4cf34269ba3a2f405eb4eb966c793baa07d863c0
|
||||
F src/vdbe.h 1223e2548e0970cf96f573ff6b99f804a36ad683
|
||||
F src/vdbeInt.h 2de43968dc47f1961d5bc76aa3cb68eacf433a7c
|
||||
F src/vdbeapi.c 58fdcd56109c05876f69c25d47a138ef370d3647
|
||||
@ -262,7 +263,7 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
|
||||
F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2
|
||||
F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
|
||||
F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b
|
||||
F src/where.c 53b991af50dab230b319b098bcb90fc7cd82da47
|
||||
F src/where.c b971ee2d1a4f5db1b4cfd5cb85e69f34e61781d0
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
@ -292,7 +293,7 @@ F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e
|
||||
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
|
||||
F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0
|
||||
F test/auth.test 304e82f31592820d3bde26ab6b75deaa123e1a6f
|
||||
F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882
|
||||
F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa
|
||||
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
|
||||
F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf
|
||||
F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991
|
||||
@ -395,15 +396,15 @@ F test/e_delete.test 89aa84d3d1bd284a0689ede04bce10226a5aeaa5
|
||||
F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235
|
||||
F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e
|
||||
F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894
|
||||
F test/e_fkey.test a79ab1d3213c7ac64621eec28f8e8bb219775445
|
||||
F test/e_fkey.test 89a2ff734a33693b997534eff90724573948be7b
|
||||
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
|
||||
F test/e_insert.test c6ac239a97cb16dfbd0c16496f8cd871b4068c0c
|
||||
F test/e_insert.test d5331cc95e101af2508159fc98b6801631659ffe
|
||||
F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216
|
||||
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test 07e8d81268ba1ffcaf1dc4bec48956af150c42f9
|
||||
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
|
||||
F test/e_update.test 161d5dc6a3ed9dd08f5264d13e20735d7a89f00c
|
||||
F test/e_uri.test 9e190ca799d9190eec6e43f2aadf1d10c06a57a3
|
||||
F test/e_uri.test bc240fbc6cbbbdff832ee05858432a25961ab36a
|
||||
F test/e_vacuum.test 331da289ae186656cf5f2eb27f577a89c0c221af
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad
|
||||
@ -419,12 +420,13 @@ F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30
|
||||
F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d
|
||||
F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e
|
||||
F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a
|
||||
F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e
|
||||
F test/filefmt.test dbee33e57818249cdffbbb7b13464635217beff1
|
||||
F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
|
||||
F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f
|
||||
F test/fkey2.test 5aa44e7153928a1f002803f94aaab4c76a7ceac2
|
||||
F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e
|
||||
F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49
|
||||
F test/fkey_malloc.test c3a12acd053c976de09036498eef09b83afa4a80
|
||||
F test/fkey5.test 0bf64f2d19ad80433ca0b24edbf604a18b353d5f
|
||||
F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
|
||||
F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7
|
||||
@ -567,6 +569,7 @@ F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
|
||||
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
|
||||
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
|
||||
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
|
||||
F test/ioerr6.test 13f0f9c31d706f0dd575995c369af07c0227e9a2
|
||||
F test/join.test 8d63cc4d230a7affafa4b6ab0b97c49b8ccb365c
|
||||
F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
@ -625,7 +628,7 @@ F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6
|
||||
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
||||
F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f
|
||||
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||
F test/minmax.test c61518429e66e228efc79661fbd2dc3e4924ec44
|
||||
F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
|
||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
|
||||
@ -663,7 +666,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/permutations.test 379cfbb9a5eea2499d05008c04d18ddb4f2c01a9
|
||||
F test/pragma.test a62f73293b0f0d79b0c87f8dd32d46fe53b0bd17
|
||||
F test/pragma.test 5ce333cae37d86cfe9b3add840906e375e2272b0
|
||||
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
|
||||
@ -676,9 +679,10 @@ F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
|
||||
F test/regexp1.test bbcb74e1bbdc20a7c0b9b2360deda14c4df1b46a
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
|
||||
F test/releasetest.tcl e48fd8e0e8abad89f30e08620790533ae4e02010
|
||||
F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce
|
||||
F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test f777404492adb0e00868fd706a3721328fd3af48
|
||||
@ -709,6 +713,7 @@ F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea
|
||||
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
|
||||
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
|
||||
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
|
||||
F test/selectD.test 03f7c1ea8d5ab3c637cbc30fcbbbac96b988c162
|
||||
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
|
||||
F test/session.test c1a17c11ef7d01c24fe2b9f7871190d949a8e718
|
||||
F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
|
||||
@ -796,6 +801,7 @@ F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
|
||||
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
|
||||
F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71
|
||||
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
|
||||
F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4
|
||||
F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78
|
||||
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
|
||||
F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
|
||||
@ -931,7 +937,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b
|
||||
F test/view.test b182a67ec43f490b156b5a710827a341be83dd17
|
||||
F test/vtab1.test 36c9935e4be3b6350b31b6b697561b6fc3ab349a
|
||||
F test/vtab1.test 4403f987860ebddef1ce2de6db7216421035339d
|
||||
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
|
||||
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
|
||||
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
|
||||
@ -981,9 +987,9 @@ F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
|
||||
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
|
||||
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a
|
||||
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
|
||||
F test/where8.test 02619a9bfc6df7b19979a02852bac09c3c99477a
|
||||
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
|
||||
F test/where9.test bcab47eff78f1412a6aec1d6b8a3939d4a9db098
|
||||
F test/where9.test 0157862ccf0cfdf1a4622cdf697e5e2f09a8de44
|
||||
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
|
||||
@ -995,7 +1001,7 @@ F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test e7f77fded01dfcdf92ac2c5400f1e35d7a21463c
|
||||
F tool/build-all-msvc.bat 74fb6e5cca66ebdb6c9bbafb2f8b802f08146d38 x
|
||||
F tool/build-shell.sh b64a481901fc9ffe5ca8812a2a9255b6cfb77381
|
||||
F tool/build-shell.sh 562df23cfdd25822b909b382afd5f99d968437fe
|
||||
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
|
||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
|
||||
@ -1038,7 +1044,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P ba8d08b67021a32fda069c18b7eb93523e6f0d1f 92c9ab56b1c67b9468bec57ab1d2c483a69a2810
|
||||
R f06c7a4e8d9fd6f22d929caa0d8aff12
|
||||
P 4f6d69ae94671df92b976525f75404c01270fef9 45c158b1a015e0295244982e7a61ecc55cca8436
|
||||
R 8d2ecc460dcc396acbf742a4b5125040
|
||||
U drh
|
||||
Z 66550b6aeab54597acd3e06f02611eed
|
||||
Z c65bfb73ba1c154a624c7762ce7998dd
|
||||
|
@ -1 +1 @@
|
||||
4f6d69ae94671df92b976525f75404c01270fef9
|
||||
7e068e39b3b31364271664e0afb1cd95a235c26f
|
14
src/backup.c
14
src/backup.c
@ -212,7 +212,12 @@ static int isFatalError(int rc){
|
||||
** page iSrcPg from the source database. Copy this data into the
|
||||
** destination database.
|
||||
*/
|
||||
static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
|
||||
static int backupOnePage(
|
||||
sqlite3_backup *p, /* Backup handle */
|
||||
Pgno iSrcPg, /* Source database page to backup */
|
||||
const u8 *zSrcData, /* Source database page data */
|
||||
int bUpdate /* True for an update, false otherwise */
|
||||
){
|
||||
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
|
||||
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
|
||||
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
|
||||
@ -285,6 +290,9 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
|
||||
*/
|
||||
memcpy(zOut, zIn, nCopy);
|
||||
((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
|
||||
if( iOff==0 && bUpdate==0 ){
|
||||
sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
|
||||
}
|
||||
}
|
||||
sqlite3PagerUnref(pDestPg);
|
||||
}
|
||||
@ -391,7 +399,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
DbPage *pSrcPg; /* Source page object */
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
|
||||
rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
}
|
||||
}
|
||||
@ -639,7 +647,7 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
|
||||
int rc;
|
||||
assert( p->pDestDb );
|
||||
sqlite3_mutex_enter(p->pDestDb->mutex);
|
||||
rc = backupOnePage(p, iPage, aData);
|
||||
rc = backupOnePage(p, iPage, aData, 1);
|
||||
sqlite3_mutex_leave(p->pDestDb->mutex);
|
||||
assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -8026,7 +8026,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
}
|
||||
i = PENDING_BYTE_PAGE(pBt);
|
||||
if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
|
||||
sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
|
||||
sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
|
||||
sCheck.errMsg.useMalloc = 2;
|
||||
|
||||
/* Check the integrity of the freelist
|
||||
|
@ -3281,6 +3281,12 @@ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
|
||||
sqlite3ExplainPush(pOut);
|
||||
sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
|
||||
sqlite3ExplainPop(pOut);
|
||||
if( pList->a[i].zName ){
|
||||
sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
|
||||
}
|
||||
if( pList->a[i].bSpanIsTab ){
|
||||
sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
|
||||
}
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3ExplainNL(pOut);
|
||||
}
|
||||
|
16
src/fkey.c
16
src/fkey.c
@ -142,7 +142,7 @@
|
||||
** A foreign key constraint requires that the key columns in the parent
|
||||
** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
|
||||
** Given that pParent is the parent table for foreign key constraint pFKey,
|
||||
** search the schema a unique index on the parent key columns.
|
||||
** search the schema for a unique index on the parent key columns.
|
||||
**
|
||||
** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
|
||||
** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
|
||||
@ -178,7 +178,7 @@
|
||||
** into pParse. If an OOM error occurs, non-zero is returned and the
|
||||
** pParse->db->mallocFailed flag is set.
|
||||
*/
|
||||
static int locateFkeyIndex(
|
||||
int sqlite3FkLocateIndex(
|
||||
Parse *pParse, /* Parse context to store any error in */
|
||||
Table *pParent, /* Parent table of FK constraint pFKey */
|
||||
FKey *pFKey, /* Foreign key to find index for */
|
||||
@ -275,7 +275,9 @@ static int locateFkeyIndex(
|
||||
|
||||
if( !pIdx ){
|
||||
if( !pParse->disableTriggers ){
|
||||
sqlite3ErrorMsg(pParse, "foreign key mismatch");
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"foreign key mismatch - \"%w\" referencing \"%w\"",
|
||||
pFKey->pFrom->zName, pFKey->zTo);
|
||||
}
|
||||
sqlite3DbFree(pParse->db, aiCol);
|
||||
return 1;
|
||||
@ -736,7 +738,7 @@ void sqlite3FkCheck(
|
||||
}else{
|
||||
pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
|
||||
}
|
||||
if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
|
||||
if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
|
||||
assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
|
||||
if( !isIgnoreErrors || db->mallocFailed ) return;
|
||||
if( pTo==0 ){
|
||||
@ -816,7 +818,7 @@ void sqlite3FkCheck(
|
||||
continue;
|
||||
}
|
||||
|
||||
if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
|
||||
if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
|
||||
if( !isIgnoreErrors || db->mallocFailed ) return;
|
||||
continue;
|
||||
}
|
||||
@ -871,7 +873,7 @@ u32 sqlite3FkOldmask(
|
||||
}
|
||||
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
||||
Index *pIdx = 0;
|
||||
locateFkeyIndex(pParse, pTab, p, &pIdx, 0);
|
||||
sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
|
||||
if( pIdx ){
|
||||
for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
||||
}
|
||||
@ -997,7 +999,7 @@ static Trigger *fkActionTrigger(
|
||||
int i; /* Iterator variable */
|
||||
Expr *pWhen = 0; /* WHEN clause for the trigger */
|
||||
|
||||
if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
|
||||
if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
|
||||
assert( aiCol || pFKey->nCol==1 );
|
||||
|
||||
for(i=0; i<pFKey->nCol; i++){
|
||||
|
@ -59,6 +59,14 @@ static int createFile(JournalFile *p){
|
||||
assert(p->iSize<=p->nBuf);
|
||||
rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* If an error occurred while writing to the file, close it before
|
||||
** returning. This way, SQLite uses the in-memory journal data to
|
||||
** roll back changes made to the internal page-cache before this
|
||||
** function was called. */
|
||||
sqlite3OsClose(pReal);
|
||||
p->pReal = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
37
src/parse.y
37
src/parse.y
@ -435,8 +435,8 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
|
||||
// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
|
||||
// present and false (0) if it is not.
|
||||
//
|
||||
%type distinct {int}
|
||||
distinct(A) ::= DISTINCT. {A = 1;}
|
||||
%type distinct {u16}
|
||||
distinct(A) ::= DISTINCT. {A = SF_Distinct;}
|
||||
distinct(A) ::= ALL. {A = 0;}
|
||||
distinct(A) ::= . {A = 0;}
|
||||
|
||||
@ -499,7 +499,8 @@ stl_prefix(A) ::= seltablist(X) joinop(Y). {
|
||||
if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].jointype = (u8)Y;
|
||||
}
|
||||
stl_prefix(A) ::= . {A = 0;}
|
||||
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) using_opt(U). {
|
||||
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I)
|
||||
on_opt(N) using_opt(U). {
|
||||
A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U);
|
||||
sqlite3SrcListIndexedBy(pParse, A, &I);
|
||||
}
|
||||
@ -512,25 +513,23 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) usi
|
||||
as(Z) on_opt(N) using_opt(U). {
|
||||
if( X==0 && Z.n==0 && N==0 && U==0 ){
|
||||
A = F;
|
||||
}else if( F->nSrc==1 ){
|
||||
A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,0,N,U);
|
||||
if( A ){
|
||||
struct SrcList_item *pNew = &A->a[A->nSrc-1];
|
||||
struct SrcList_item *pOld = F->a;
|
||||
pNew->zName = pOld->zName;
|
||||
pNew->zDatabase = pOld->zDatabase;
|
||||
pOld->zName = pOld->zDatabase = 0;
|
||||
}
|
||||
sqlite3SrcListDelete(pParse->db, F);
|
||||
}else{
|
||||
Select *pSubquery;
|
||||
sqlite3SrcListShiftJoinType(F);
|
||||
pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
|
||||
pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0,0);
|
||||
A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,pSubquery,N,U);
|
||||
}
|
||||
}
|
||||
|
||||
// A seltablist_paren nonterminal represents anything in a FROM that
|
||||
// is contained inside parentheses. This can be either a subquery or
|
||||
// a grouping of table and subqueries.
|
||||
//
|
||||
// %type seltablist_paren {Select*}
|
||||
// %destructor seltablist_paren {sqlite3SelectDelete(pParse->db, $$);}
|
||||
// seltablist_paren(A) ::= select(S). {A = S;}
|
||||
// seltablist_paren(A) ::= seltablist(F). {
|
||||
// sqlite3SrcListShiftJoinType(F);
|
||||
// A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0);
|
||||
// }
|
||||
%endif SQLITE_OMIT_SUBQUERY
|
||||
|
||||
%type dbnm {Token}
|
||||
@ -653,7 +652,8 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;}
|
||||
////////////////////////// The UPDATE command ////////////////////////////////
|
||||
//
|
||||
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) orderby_opt(O) limit_opt(L). {
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W)
|
||||
orderby_opt(O) limit_opt(L). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
|
||||
@ -661,7 +661,8 @@ cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W)
|
||||
}
|
||||
%endif
|
||||
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). {
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
|
||||
where_opt(W). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
sqlite3Update(pParse,X,Y,W,R);
|
||||
|
128
src/pragma.c
128
src/pragma.c
@ -948,9 +948,11 @@ void sqlite3Pragma(
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
pTab = sqlite3FindTable(db, zRight, zDb);
|
||||
if( pTab ){
|
||||
int i;
|
||||
int i, k;
|
||||
int nHidden = 0;
|
||||
Column *pCol;
|
||||
Index *pPk;
|
||||
for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){}
|
||||
sqlite3VdbeSetNumCols(v, 6);
|
||||
pParse->nMem = 6;
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
|
||||
@ -975,8 +977,14 @@ void sqlite3Pragma(
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6);
|
||||
if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
|
||||
k = 0;
|
||||
}else if( pPk==0 ){
|
||||
k = 1;
|
||||
}else{
|
||||
for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
|
||||
}
|
||||
}
|
||||
@ -1114,6 +1122,120 @@ void sqlite3Pragma(
|
||||
}else
|
||||
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
|
||||
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){
|
||||
FKey *pFK; /* A foreign key constraint */
|
||||
Table *pTab; /* Child table contain "REFERENCES" keyword */
|
||||
Table *pParent; /* Parent table that child points to */
|
||||
Index *pIdx; /* Index in the parent table */
|
||||
int i; /* Loop counter: Foreign key number for pTab */
|
||||
int j; /* Loop counter: Field of the foreign key */
|
||||
HashElem *k; /* Loop counter: Next table in schema */
|
||||
int x; /* result variable */
|
||||
int regResult; /* 3 registers to hold a result row */
|
||||
int regKey; /* Register to hold key for checking the FK */
|
||||
int regRow; /* Registers to hold a row from pTab */
|
||||
int addrTop; /* Top of a loop checking foreign keys */
|
||||
int addrOk; /* Jump here if the key is OK */
|
||||
int *aiCols; /* child to parent column mapping */
|
||||
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
regResult = pParse->nMem+1;
|
||||
pParse->nMem += 4;
|
||||
regKey = ++pParse->nMem;
|
||||
regRow = ++pParse->nMem;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
sqlite3VdbeSetNumCols(v, 4);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC);
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
|
||||
while( k ){
|
||||
if( zRight ){
|
||||
pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
|
||||
k = 0;
|
||||
}else{
|
||||
pTab = (Table*)sqliteHashData(k);
|
||||
k = sqliteHashNext(k);
|
||||
}
|
||||
if( pTab==0 || pTab->pFKey==0 ) continue;
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
|
||||
sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName,
|
||||
P4_TRANSIENT);
|
||||
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
|
||||
pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
|
||||
if( pParent==0 ) break;
|
||||
pIdx = 0;
|
||||
sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
|
||||
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
|
||||
if( x==0 ){
|
||||
if( pIdx==0 ){
|
||||
sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
|
||||
}else{
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
}
|
||||
}else{
|
||||
k = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pFK ) break;
|
||||
if( pParse->nTab<i ) pParse->nTab = i;
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0);
|
||||
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
|
||||
pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
|
||||
assert( pParent!=0 );
|
||||
pIdx = 0;
|
||||
aiCols = 0;
|
||||
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
|
||||
assert( x==0 );
|
||||
addrOk = sqlite3VdbeMakeLabel(v);
|
||||
if( pIdx==0 ){
|
||||
int iKey = pFK->aCol[0].iFrom;
|
||||
assert( iKey>=0 && iKey<pTab->nCol );
|
||||
if( iKey!=pTab->iPKey ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
|
||||
sqlite3ColumnDefault(v, pTab, iKey, regRow);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk);
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
|
||||
sqlite3VdbeCurrentAddr(v)+3);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk);
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
||||
}else{
|
||||
for(j=0; j<pFK->nCol; j++){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
|
||||
aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey);
|
||||
sqlite3VdbeChangeP4(v, -1,
|
||||
sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0,
|
||||
pFK->zTo, P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4);
|
||||
sqlite3VdbeResolveLabel(v, addrOk);
|
||||
sqlite3DbFree(db, aiCols);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1);
|
||||
sqlite3VdbeJumpHere(v, addrTop);
|
||||
}
|
||||
}else
|
||||
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
|
||||
|
||||
#ifndef NDEBUG
|
||||
if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
|
||||
if( zRight ){
|
||||
|
124
src/resolve.c
124
src/resolve.c
@ -150,6 +150,35 @@ static int nameInUsingClause(IdList *pUsing, const char *zCol){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Subqueries stores the original database, table and column names for their
|
||||
** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
|
||||
** Check to see if the zSpan given to this routine matches the zDb, zTab,
|
||||
** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
|
||||
** match anything.
|
||||
*/
|
||||
int sqlite3MatchSpanName(
|
||||
const char *zSpan,
|
||||
const char *zCol,
|
||||
const char *zTab,
|
||||
const char *zDb
|
||||
){
|
||||
int n;
|
||||
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
|
||||
if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
zSpan += n+1;
|
||||
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
|
||||
if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
zSpan += n+1;
|
||||
if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
|
||||
@ -206,6 +235,20 @@ static int lookupName(
|
||||
pExpr->pTab = 0;
|
||||
ExprSetIrreducible(pExpr);
|
||||
|
||||
/* Translate the schema name in zDb into a pointer to the corresponding
|
||||
** schema. If not found, pSchema will remain NULL and nothing will match
|
||||
** resulting in an appropriate error message toward the end of this routine
|
||||
*/
|
||||
if( zDb ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
assert( db->aDb[i].zName );
|
||||
if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
|
||||
pSchema = db->aDb[i].pSchema;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Start at the inner-most context and move outward until a match is found */
|
||||
while( pNC && cnt==0 ){
|
||||
ExprList *pEList;
|
||||
@ -214,31 +257,36 @@ static int lookupName(
|
||||
if( pSrcList ){
|
||||
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
|
||||
Table *pTab;
|
||||
int iDb;
|
||||
Column *pCol;
|
||||
|
||||
pTab = pItem->pTab;
|
||||
assert( pTab!=0 && pTab->zName!=0 );
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( pTab->nCol>0 );
|
||||
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
|
||||
ExprList *pEList = pItem->pSelect->pEList;
|
||||
int hit = 0;
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){
|
||||
cnt++;
|
||||
cntTab = 2;
|
||||
pMatch = pItem;
|
||||
pExpr->iColumn = j;
|
||||
hit = 1;
|
||||
}
|
||||
}
|
||||
if( hit || zTab==0 ) continue;
|
||||
}
|
||||
if( zDb && pTab->pSchema!=pSchema ){
|
||||
continue;
|
||||
}
|
||||
if( zTab ){
|
||||
if( pItem->zAlias ){
|
||||
char *zTabName = pItem->zAlias;
|
||||
if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
|
||||
}else{
|
||||
char *zTabName = pTab->zName;
|
||||
if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){
|
||||
continue;
|
||||
}
|
||||
if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
|
||||
continue;
|
||||
}
|
||||
const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
|
||||
assert( zTabName!=0 );
|
||||
if( sqlite3StrICmp(zTabName, zTab)!=0 ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if( 0==(cntTab++) ){
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->pTab = pTab;
|
||||
pSchema = pTab->pSchema;
|
||||
pMatch = pItem;
|
||||
}
|
||||
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
|
||||
@ -252,17 +300,19 @@ static int lookupName(
|
||||
if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
|
||||
}
|
||||
cnt++;
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->pTab = pTab;
|
||||
pMatch = pItem;
|
||||
pSchema = pTab->pSchema;
|
||||
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pMatch ){
|
||||
pExpr->iTable = pMatch->iCursor;
|
||||
pExpr->pTab = pMatch->pTab;
|
||||
pSchema = pExpr->pTab->pSchema;
|
||||
}
|
||||
} /* if( pSrcList ) */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* If we have not already resolved the name, then maybe
|
||||
@ -1033,23 +1083,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
return WRC_Abort;
|
||||
}
|
||||
|
||||
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
|
||||
** resolve the result-set expression list.
|
||||
*/
|
||||
sNC.ncFlags = NC_AllowAgg;
|
||||
sNC.pSrcList = p->pSrc;
|
||||
sNC.pNext = pOuterNC;
|
||||
|
||||
/* Resolve names in the result set. */
|
||||
pEList = p->pEList;
|
||||
assert( pEList!=0 );
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
Expr *pX = pEList->a[i].pExpr;
|
||||
if( sqlite3ResolveExprNames(&sNC, pX) ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
}
|
||||
|
||||
/* Recursively resolve names in all subqueries
|
||||
*/
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
@ -1077,6 +1110,23 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
|
||||
** resolve the result-set expression list.
|
||||
*/
|
||||
sNC.ncFlags = NC_AllowAgg;
|
||||
sNC.pSrcList = p->pSrc;
|
||||
sNC.pNext = pOuterNC;
|
||||
|
||||
/* Resolve names in the result set. */
|
||||
pEList = p->pEList;
|
||||
assert( pEList!=0 );
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
Expr *pX = pEList->a[i].pExpr;
|
||||
if( sqlite3ResolveExprNames(&sNC, pX) ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are no aggregate functions in the result-set, and no GROUP BY
|
||||
** expression, do not allow aggregates in any of the other expressions.
|
||||
*/
|
||||
|
137
src/select.c
137
src/select.c
@ -55,7 +55,7 @@ Select *sqlite3SelectNew(
|
||||
ExprList *pGroupBy, /* the GROUP BY clause */
|
||||
Expr *pHaving, /* the HAVING clause */
|
||||
ExprList *pOrderBy, /* the ORDER BY clause */
|
||||
int isDistinct, /* true if the DISTINCT keyword is present */
|
||||
u16 selFlags, /* Flag parameters, such as SF_Distinct */
|
||||
Expr *pLimit, /* LIMIT value. NULL means not used */
|
||||
Expr *pOffset /* OFFSET value. NULL means no offset */
|
||||
){
|
||||
@ -79,7 +79,7 @@ Select *sqlite3SelectNew(
|
||||
pNew->pGroupBy = pGroupBy;
|
||||
pNew->pHaving = pHaving;
|
||||
pNew->pOrderBy = pOrderBy;
|
||||
pNew->selFlags = isDistinct ? SF_Distinct : 0;
|
||||
pNew->selFlags = selFlags;
|
||||
pNew->op = TK_SELECT;
|
||||
pNew->pLimit = pLimit;
|
||||
pNew->pOffset = pOffset;
|
||||
@ -1336,8 +1336,6 @@ static int selectColumnsFromExprList(
|
||||
/* Get an appropriate name for the column
|
||||
*/
|
||||
p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
|
||||
assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue)
|
||||
|| p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 );
|
||||
if( (zName = pEList->a[i].zName)!=0 ){
|
||||
/* If the column contains an "AS <name>" phrase, use <name> as the name */
|
||||
zName = sqlite3DbStrDup(db, zName);
|
||||
@ -1375,6 +1373,9 @@ static int selectColumnsFromExprList(
|
||||
for(j=cnt=0; j<i; j++){
|
||||
if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
|
||||
char *zNewName;
|
||||
int k;
|
||||
for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){}
|
||||
if( zName[k]==':' ) nName = k;
|
||||
zName[nName] = 0;
|
||||
zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
|
||||
sqlite3DbFree(db, zName);
|
||||
@ -3160,34 +3161,43 @@ static int flattenSubquery(
|
||||
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
||||
|
||||
/*
|
||||
** Analyze the SELECT statement passed as an argument to see if it
|
||||
** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if
|
||||
** it is, or 0 otherwise. At present, a query is considered to be
|
||||
** a min()/max() query if:
|
||||
** Based on the contents of the AggInfo structure indicated by the first
|
||||
** argument, this function checks if the following are true:
|
||||
**
|
||||
** 1. There is a single object in the FROM clause.
|
||||
** * the query contains just a single aggregate function,
|
||||
** * the aggregate function is either min() or max(), and
|
||||
** * the argument to the aggregate function is a column value.
|
||||
**
|
||||
** 2. There is a single expression in the result set, and it is
|
||||
** either min(x) or max(x), where x is a column reference.
|
||||
** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
|
||||
** is returned as appropriate. Also, *ppMinMax is set to point to the
|
||||
** list of arguments passed to the aggregate before returning.
|
||||
**
|
||||
** Or, if the conditions above are not met, *ppMinMax is set to 0 and
|
||||
** WHERE_ORDERBY_NORMAL is returned.
|
||||
*/
|
||||
static u8 minMaxQuery(Select *p){
|
||||
Expr *pExpr;
|
||||
ExprList *pEList = p->pEList;
|
||||
static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
|
||||
int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
|
||||
|
||||
if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
|
||||
pExpr = pEList->a[0].pExpr;
|
||||
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
|
||||
if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0;
|
||||
pEList = pExpr->x.pList;
|
||||
if( pEList==0 || pEList->nExpr!=1 ) return 0;
|
||||
if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){
|
||||
return WHERE_ORDERBY_MIN;
|
||||
}else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){
|
||||
return WHERE_ORDERBY_MAX;
|
||||
*ppMinMax = 0;
|
||||
if( pAggInfo->nFunc==1 ){
|
||||
Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
|
||||
ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
|
||||
|
||||
assert( pExpr->op==TK_AGG_FUNCTION );
|
||||
if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
|
||||
const char *zFunc = pExpr->u.zToken;
|
||||
if( sqlite3StrICmp(zFunc, "min")==0 ){
|
||||
eRet = WHERE_ORDERBY_MIN;
|
||||
*ppMinMax = pEList;
|
||||
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
|
||||
eRet = WHERE_ORDERBY_MAX;
|
||||
*ppMinMax = pEList;
|
||||
}
|
||||
}
|
||||
}
|
||||
return WHERE_ORDERBY_NORMAL;
|
||||
|
||||
assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
|
||||
return eRet;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3282,6 +3292,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
ExprList *pEList;
|
||||
struct SrcList_item *pFrom;
|
||||
sqlite3 *db = pParse->db;
|
||||
Expr *pE, *pRight, *pExpr;
|
||||
|
||||
if( db->mallocFailed ){
|
||||
return WRC_Abort;
|
||||
@ -3367,7 +3378,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
** that need expanding.
|
||||
*/
|
||||
for(k=0; k<pEList->nExpr; k++){
|
||||
Expr *pE = pEList->a[k].pExpr;
|
||||
pE = pEList->a[k].pExpr;
|
||||
if( pE->op==TK_ALL ) break;
|
||||
assert( pE->op!=TK_DOT || pE->pRight!=0 );
|
||||
assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
|
||||
@ -3385,10 +3396,18 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
int longNames = (flags & SQLITE_FullColNames)!=0
|
||||
&& (flags & SQLITE_ShortColNames)==0;
|
||||
|
||||
/* When processing FROM-clause subqueries, it is always the case
|
||||
** that full_column_names=OFF and short_column_names=ON. The
|
||||
** sqlite3ResultSetOfSelect() routine makes it so. */
|
||||
assert( (p->selFlags & SF_NestedFrom)==0
|
||||
|| ((flags & SQLITE_FullColNames)==0 &&
|
||||
(flags & SQLITE_ShortColNames)!=0) );
|
||||
|
||||
for(k=0; k<pEList->nExpr; k++){
|
||||
Expr *pE = a[k].pExpr;
|
||||
assert( pE->op!=TK_DOT || pE->pRight!=0 );
|
||||
if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){
|
||||
pE = a[k].pExpr;
|
||||
pRight = pE->pRight;
|
||||
assert( pE->op!=TK_DOT || pRight!=0 );
|
||||
if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){
|
||||
/* This particular expression does not need to be expanded.
|
||||
*/
|
||||
pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
|
||||
@ -3403,32 +3422,43 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
/* This expression is a "*" or a "TABLE.*" and needs to be
|
||||
** expanded. */
|
||||
int tableSeen = 0; /* Set to 1 when TABLE matches */
|
||||
char *zTName; /* text of name of TABLE */
|
||||
char *zTName = 0; /* text of name of TABLE */
|
||||
if( pE->op==TK_DOT ){
|
||||
assert( pE->pLeft!=0 );
|
||||
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
|
||||
zTName = pE->pLeft->u.zToken;
|
||||
}else{
|
||||
zTName = 0;
|
||||
}
|
||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||
Table *pTab = pFrom->pTab;
|
||||
Select *pSub = pFrom->pSelect;
|
||||
char *zTabName = pFrom->zAlias;
|
||||
const char *zSchemaName = 0;
|
||||
int iDb;
|
||||
if( zTabName==0 ){
|
||||
zTabName = pTab->zName;
|
||||
}
|
||||
if( db->mallocFailed ) break;
|
||||
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
|
||||
continue;
|
||||
if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
|
||||
pSub = 0;
|
||||
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
|
||||
continue;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*";
|
||||
}
|
||||
tableSeen = 1;
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
Expr *pExpr, *pRight;
|
||||
char *zName = pTab->aCol[j].zName;
|
||||
char *zColname; /* The computed column name */
|
||||
char *zToFree; /* Malloced string that needs to be freed */
|
||||
Token sColname; /* Computed column name as a token */
|
||||
|
||||
assert( zName );
|
||||
if( zTName && pSub
|
||||
&& sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If a column is marked as 'hidden' (currently only possible
|
||||
** for virtual tables), do not include it in the expanded
|
||||
** result-set list.
|
||||
@ -3437,6 +3467,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
assert(IsVirtual(pTab));
|
||||
continue;
|
||||
}
|
||||
tableSeen = 1;
|
||||
|
||||
if( i>0 && zTName==0 ){
|
||||
if( (pFrom->jointype & JT_NATURAL)!=0
|
||||
@ -3459,6 +3490,10 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
Expr *pLeft;
|
||||
pLeft = sqlite3Expr(db, TK_ID, zTabName);
|
||||
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
|
||||
if( zSchemaName ){
|
||||
pLeft = sqlite3Expr(db, TK_ID, zSchemaName);
|
||||
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0);
|
||||
}
|
||||
if( longNames ){
|
||||
zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
|
||||
zToFree = zColname;
|
||||
@ -3470,6 +3505,18 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
sColname.z = zColname;
|
||||
sColname.n = sqlite3Strlen30(zColname);
|
||||
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
|
||||
if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
|
||||
struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
|
||||
if( pSub ){
|
||||
pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan);
|
||||
testcase( pX->zSpan==0 );
|
||||
}else{
|
||||
pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s",
|
||||
zSchemaName, zTabName, zColname);
|
||||
testcase( pX->zSpan==0 );
|
||||
}
|
||||
pX->bSpanIsTab = 1;
|
||||
}
|
||||
sqlite3DbFree(db, zToFree);
|
||||
}
|
||||
}
|
||||
@ -4527,11 +4574,17 @@ int sqlite3Select(
|
||||
** Refer to code and comments in where.c for details.
|
||||
*/
|
||||
ExprList *pMinMax = 0;
|
||||
u8 flag = minMaxQuery(p);
|
||||
u8 flag = WHERE_ORDERBY_NORMAL;
|
||||
|
||||
assert( p->pGroupBy==0 );
|
||||
assert( flag==0 );
|
||||
if( p->pHaving==0 ){
|
||||
flag = minMaxQuery(&sAggInfo, &pMinMax);
|
||||
}
|
||||
assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) );
|
||||
|
||||
if( flag ){
|
||||
assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
|
||||
assert( p->pEList->a[0].pExpr->x.pList->nExpr==1 );
|
||||
pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
|
||||
pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
|
||||
pDel = pMinMax;
|
||||
if( pMinMax && !db->mallocFailed ){
|
||||
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
|
||||
|
@ -1479,6 +1479,12 @@ static void open_db(struct callback_data *p){
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
sqlite3_enable_load_extension(p->db, 1);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_REGEXP
|
||||
{
|
||||
extern int sqlite3_add_regexp_func(sqlite3*);
|
||||
sqlite3_add_regexp_func(db);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1587,7 +1587,7 @@ struct sqlite3_mem_methods {
|
||||
** [SQLITE_USE_URI] symbol defined.
|
||||
**
|
||||
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
|
||||
** <dd> This option taks a single integer argument which is interpreted as
|
||||
** <dd> This option takes a single integer argument which is interpreted as
|
||||
** a boolean in order to enable or disable the use of covering indices for
|
||||
** full table scans in the query optimizer. The default setting is determined
|
||||
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
|
||||
|
@ -1492,20 +1492,20 @@ struct UnpackedRecord {
|
||||
** element.
|
||||
*/
|
||||
struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
Index *pNext; /* The next index associated with the same table */
|
||||
Schema *pSchema; /* Schema containing this index */
|
||||
u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
|
||||
char **azColl; /* Array of collation sequence names for index */
|
||||
int nColumn; /* Number of columns in the table used by this index */
|
||||
int tnum; /* Page containing root of this index in database file */
|
||||
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
|
||||
u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
|
||||
u8 bUnordered; /* Use this index for == or IN queries only */
|
||||
char *zName; /* Name of this index */
|
||||
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
Index *pNext; /* The next index associated with the same table */
|
||||
Schema *pSchema; /* Schema containing this index */
|
||||
u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
|
||||
char **azColl; /* Array of collation sequence names for index */
|
||||
int tnum; /* DB Page containing root of this index */
|
||||
u16 nColumn; /* Number of columns in table used by this index */
|
||||
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
|
||||
unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
|
||||
unsigned bUnordered:1; /* Use this index for == or IN queries only */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
tRowcnt avgEq; /* Average nEq value for key values not in aSample */
|
||||
@ -1779,18 +1779,27 @@ struct Expr {
|
||||
** list of "ID = expr" items in an UPDATE. A list of expressions can
|
||||
** also be used as the argument to a function, in which case the a.zName
|
||||
** field is not used.
|
||||
**
|
||||
** By default the Expr.zSpan field holds a human-readable description of
|
||||
** the expression that is used in the generation of error messages and
|
||||
** column labels. In this case, Expr.zSpan is typically the text of a
|
||||
** column expression as it exists in a SELECT statement. However, if
|
||||
** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
|
||||
** of the result column in the form: DATABASE.TABLE.COLUMN. This later
|
||||
** form is used for name resolution with nested FROM clauses.
|
||||
*/
|
||||
struct ExprList {
|
||||
int nExpr; /* Number of expressions on the list */
|
||||
int iECursor; /* VDBE Cursor associated with this ExprList */
|
||||
struct ExprList_item { /* For each expression in the list */
|
||||
Expr *pExpr; /* The list of expressions */
|
||||
char *zName; /* Token associated with this expression */
|
||||
char *zSpan; /* Original text of the expression */
|
||||
u8 sortOrder; /* 1 for DESC or 0 for ASC */
|
||||
u8 done; /* A flag to indicate when processing is finished */
|
||||
u16 iOrderByCol; /* For ORDER BY, column number in result set */
|
||||
u16 iAlias; /* Index into Parse.aAlias[] for zName */
|
||||
Expr *pExpr; /* The list of expressions */
|
||||
char *zName; /* Token associated with this expression */
|
||||
char *zSpan; /* Original text of the expression */
|
||||
u8 sortOrder; /* 1 for DESC or 0 for ASC */
|
||||
unsigned done :1; /* A flag to indicate when processing is finished */
|
||||
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
|
||||
u16 iOrderByCol; /* For ORDER BY, column number in result set */
|
||||
u16 iAlias; /* Index into Parse.aAlias[] for zName */
|
||||
} *a; /* Alloc a power of two greater or equal to nExpr */
|
||||
};
|
||||
|
||||
@ -2110,6 +2119,7 @@ struct Select {
|
||||
#define SF_UseSorter 0x0040 /* Sort using a sorter */
|
||||
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
|
||||
#define SF_Materialize 0x0100 /* Force materialization of views */
|
||||
#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
|
||||
|
||||
|
||||
/*
|
||||
@ -2826,7 +2836,7 @@ Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
|
||||
void sqlite3DropIndex(Parse*, SrcList*, int);
|
||||
int sqlite3Select(Parse*, Select*, SelectDest*);
|
||||
Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
|
||||
Expr*,ExprList*,int,Expr*,Expr*);
|
||||
Expr*,ExprList*,u16,Expr*,Expr*);
|
||||
void sqlite3SelectDelete(sqlite3*, Select*);
|
||||
Table *sqlite3SrcListLookup(Parse*, SrcList*);
|
||||
int sqlite3IsReadOnly(Parse*, Table*, int);
|
||||
@ -3083,6 +3093,7 @@ void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
void sqlite3ExpirePreparedStatements(sqlite3*);
|
||||
int sqlite3CodeSubselect(Parse *, Expr *, int, int);
|
||||
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
|
||||
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
|
||||
int sqlite3ResolveExprNames(NameContext*, Expr*);
|
||||
void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
|
||||
int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
|
||||
@ -3221,8 +3232,10 @@ const char *sqlite3JournalModename(int);
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
void sqlite3FkDelete(sqlite3 *, Table*);
|
||||
int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**);
|
||||
#else
|
||||
#define sqlite3FkDelete(a,b)
|
||||
#define sqlite3FkLocateIndex(a,b,c,d,e)
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -3833,6 +3833,8 @@ static void init_all(Tcl_Interp *interp){
|
||||
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
extern int TestSession_Init(Tcl_Interp*);
|
||||
#endif
|
||||
extern int Sqlitetestregexp_Init(Tcl_Interp*);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
|
||||
#endif
|
||||
@ -3878,6 +3880,8 @@ static void init_all(Tcl_Interp *interp){
|
||||
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
TestSession_Init(interp);
|
||||
#endif
|
||||
Sqlitetestregexp_Init(interp);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
Sqlitetestfts3_Init(interp);
|
||||
#endif
|
||||
|
757
src/test_regexp.c
Normal file
757
src/test_regexp.c
Normal file
@ -0,0 +1,757 @@
|
||||
/*
|
||||
** 2012-11-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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** The code in this file implements a compact but reasonably
|
||||
** efficient regular-expression matcher for posix extended regular
|
||||
** expressions against UTF8 text. The following syntax is supported:
|
||||
**
|
||||
** X* zero or more occurrences of X
|
||||
** X+ one or more occurrences of X
|
||||
** X? zero or one occurrences of X
|
||||
** X{p,q} between p and q occurrences of X
|
||||
** (X) match X
|
||||
** X|Y X or Y
|
||||
** ^X X occurring at the beginning of the string
|
||||
** X$ X occurring at the end of the string
|
||||
** . Match any single character
|
||||
** \c Character c where c is one of \{}()[]|*+?.
|
||||
** \c C-language escapes for c in afnrtv. ex: \t or \n
|
||||
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
|
||||
** \xXXX Where XXX is any number of hex digits, unicode value XXX
|
||||
** [abc] Any single character from the set abc
|
||||
** [^abc] Any single character not in the set abc
|
||||
** [a-z] Any single character in the range a-z
|
||||
** [^a-z] Any single character not in the range a-z
|
||||
** \b Word boundary
|
||||
** \w Word character. [A-Za-z0-9_]
|
||||
** \W Non-word character
|
||||
** \d Digit
|
||||
** \D Non-digit
|
||||
** \s Whitespace character
|
||||
** \S Non-whitespace character
|
||||
**
|
||||
** A nondeterministic finite automaton (NFA) is used for matching, so the
|
||||
** performance is bounded by O(N*M) where N is the size of the regular
|
||||
** expression and M is the size of the input string. The matcher never
|
||||
** exhibits exponential behavior. Note that the X{p,q} operator expands
|
||||
** to p copies of X following by q-p copies of X? and that the size of the
|
||||
** regular expression in the O(N*M) performance bound is computed after
|
||||
** this expansion.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/* The end-of-input character */
|
||||
#define RE_EOF 0 /* End of input */
|
||||
|
||||
/* The NFA is implemented as sequence of opcodes taken from the following
|
||||
** set. Each opcode has a single integer argument.
|
||||
*/
|
||||
#define RE_OP_MATCH 1 /* Match the one character in the argument */
|
||||
#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
|
||||
#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
|
||||
#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
|
||||
#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
|
||||
#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
|
||||
#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
|
||||
#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
|
||||
#define RE_OP_CC_VALUE 9 /* Single value in a character class */
|
||||
#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
|
||||
#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
|
||||
#define RE_OP_NOTWORD 12 /* Not a perl word character */
|
||||
#define RE_OP_DIGIT 13 /* digit: [0-9] */
|
||||
#define RE_OP_NOTDIGIT 14 /* Not a digit */
|
||||
#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
|
||||
#define RE_OP_NOTSPACE 16 /* Not a digit */
|
||||
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
|
||||
|
||||
/* Each opcode is a "state" in the NFA */
|
||||
typedef unsigned short ReStateNumber;
|
||||
|
||||
/* Because this is an NFA and not a DFA, multiple states can be active at
|
||||
** once. An instance of the following object records all active states in
|
||||
** the NFA. The implementation is optimized for the common case where the
|
||||
** number of actives states is small.
|
||||
*/
|
||||
typedef struct ReStateSet {
|
||||
unsigned nState; /* Number of current states */
|
||||
ReStateNumber *aState; /* Current states */
|
||||
} ReStateSet;
|
||||
|
||||
/* An input string read one character at a time.
|
||||
*/
|
||||
typedef struct ReInput ReInput;
|
||||
struct ReInput {
|
||||
const unsigned char *z; /* All text */
|
||||
int i; /* Next byte to read */
|
||||
int mx; /* EOF when i>=mx */
|
||||
};
|
||||
|
||||
/* A compiled NFA (or an NFA that is in the process of being compiled) is
|
||||
** an instance of the following object.
|
||||
*/
|
||||
typedef struct ReCompiled ReCompiled;
|
||||
struct ReCompiled {
|
||||
ReInput sIn; /* Regular expression text */
|
||||
const char *zErr; /* Error message to return */
|
||||
char *aOp; /* Operators for the virtual machine */
|
||||
int *aArg; /* Arguments to each operator */
|
||||
unsigned (*xNextChar)(ReInput*); /* Next character function */
|
||||
char zInit[12]; /* Initial text to match */
|
||||
int nInit; /* Number of characters in zInit */
|
||||
unsigned nState; /* Number of entries in aOp[] and aArg[] */
|
||||
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
|
||||
};
|
||||
|
||||
/* Add a state to the given state set if it is not already there */
|
||||
static void re_add_state(ReStateSet *pSet, int newState){
|
||||
unsigned i;
|
||||
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
|
||||
pSet->aState[pSet->nState++] = newState;
|
||||
}
|
||||
|
||||
/* Extract the next unicode character from *pzIn and return it. Advance
|
||||
** *pzIn to the first byte past the end of the character returned. To
|
||||
** be clear: this routine converts utf8 to unicode. This routine is
|
||||
** optimized for the common case where the next character is a single byte.
|
||||
*/
|
||||
static unsigned re_next_char(ReInput *p){
|
||||
unsigned c;
|
||||
if( p->i>=p->mx ) return 0;
|
||||
c = p->z[p->i++];
|
||||
if( c>0x80 ){
|
||||
if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
|
||||
c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
|
||||
if( c<0x80 ) c = 0xfffd;
|
||||
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 ){
|
||||
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
||||
p->i += 2;
|
||||
if( c<0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||||
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
||||
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
||||
| (p->z[p->i+2]&0x3f);
|
||||
p->i += 3;
|
||||
if( c<0xffff ) c = 0xfffd;
|
||||
}else{
|
||||
c = 0xfffd;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
static unsigned re_next_char_nocase(ReInput *p){
|
||||
unsigned c = re_next_char(p);
|
||||
if( c>='A' && c<='Z' ) c += 'a' - 'A';
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
|
||||
static int re_word_char(int c){
|
||||
return (c>='0' && c<='9') || (c>='a' && c<='z')
|
||||
|| (c>='A' && c<='Z') || c=='_';
|
||||
}
|
||||
|
||||
/* Return true if c is a "digit" character: [0-9] */
|
||||
static int re_digit_char(int c){
|
||||
return (c>='0' && c<='9');
|
||||
}
|
||||
|
||||
/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
|
||||
static int re_space_char(int c){
|
||||
return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f';
|
||||
}
|
||||
|
||||
/* Run a compiled regular expression on the zero-terminated input
|
||||
** string zIn[]. Return true on a match and false if there is no match.
|
||||
*/
|
||||
int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
|
||||
ReStateSet aStateSet[2], *pThis, *pNext;
|
||||
ReStateNumber aSpace[100];
|
||||
ReStateNumber *pToFree;
|
||||
unsigned int i = 0;
|
||||
unsigned int iSwap = 0;
|
||||
int c = RE_EOF+1;
|
||||
int cPrev = 0;
|
||||
int rc = 0;
|
||||
ReInput in;
|
||||
|
||||
in.z = zIn;
|
||||
in.i = 0;
|
||||
in.mx = nIn>=0 ? nIn : strlen((char*)zIn);
|
||||
if( pRe->nInit ){
|
||||
unsigned char x = pRe->zInit[0];
|
||||
while( in.i+pRe->nInit<=in.mx
|
||||
&& (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0)
|
||||
){
|
||||
in.i++;
|
||||
}
|
||||
if( in.i+pRe->nInit>in.mx ) return 0;
|
||||
}
|
||||
if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
|
||||
pToFree = 0;
|
||||
aStateSet[0].aState = aSpace;
|
||||
}else{
|
||||
pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState );
|
||||
if( pToFree==0 ) return -1;
|
||||
aStateSet[0].aState = pToFree;
|
||||
}
|
||||
aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
|
||||
pNext = &aStateSet[1];
|
||||
pNext->nState = 0;
|
||||
re_add_state(pNext, 0);
|
||||
while( c!=RE_EOF && pNext->nState>0 ){
|
||||
cPrev = c;
|
||||
c = pRe->xNextChar(&in);
|
||||
pThis = pNext;
|
||||
pNext = &aStateSet[iSwap];
|
||||
iSwap = 1 - iSwap;
|
||||
pNext->nState = 0;
|
||||
for(i=0; i<pThis->nState; i++){
|
||||
int x = pThis->aState[i];
|
||||
switch( pRe->aOp[x] ){
|
||||
case RE_OP_MATCH: {
|
||||
if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_ANY: {
|
||||
re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_WORD: {
|
||||
if( re_word_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_NOTWORD: {
|
||||
if( !re_word_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_DIGIT: {
|
||||
if( re_digit_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_NOTDIGIT: {
|
||||
if( !re_digit_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_SPACE: {
|
||||
if( re_space_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_NOTSPACE: {
|
||||
if( !re_space_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_BOUNDARY: {
|
||||
if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_ANYSTAR: {
|
||||
re_add_state(pNext, x);
|
||||
re_add_state(pThis, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_FORK: {
|
||||
re_add_state(pThis, x+pRe->aArg[x]);
|
||||
re_add_state(pThis, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_GOTO: {
|
||||
re_add_state(pThis, x+pRe->aArg[x]);
|
||||
break;
|
||||
}
|
||||
case RE_OP_ACCEPT: {
|
||||
rc = 1;
|
||||
goto re_match_end;
|
||||
}
|
||||
case RE_OP_CC_INC:
|
||||
case RE_OP_CC_EXC: {
|
||||
int j = 1;
|
||||
int n = pRe->aArg[x];
|
||||
int hit = 0;
|
||||
for(j=1; j>0 && j<n; j++){
|
||||
if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
|
||||
if( pRe->aArg[x+j]==c ){
|
||||
hit = 1;
|
||||
j = -1;
|
||||
}
|
||||
}else{
|
||||
if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
|
||||
hit = 1;
|
||||
j = -1;
|
||||
}else{
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
|
||||
if( hit ) re_add_state(pNext, x+n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<pNext->nState; i++){
|
||||
if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
|
||||
}
|
||||
re_match_end:
|
||||
sqlite3_free(pToFree);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Resize the opcode and argument arrays for an RE under construction.
|
||||
*/
|
||||
static int re_resize(ReCompiled *p, int N){
|
||||
char *aOp;
|
||||
int *aArg;
|
||||
aOp = sqlite3_realloc(p->aOp, N*sizeof(p->aOp[0]));
|
||||
if( aOp==0 ) return 1;
|
||||
p->aOp = aOp;
|
||||
aArg = sqlite3_realloc(p->aArg, N*sizeof(p->aArg[0]));
|
||||
if( aArg==0 ) return 1;
|
||||
p->aArg = aArg;
|
||||
p->nAlloc = N;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert a new opcode and argument into an RE under construction. The
|
||||
** insertion point is just prior to existing opcode iBefore.
|
||||
*/
|
||||
static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
|
||||
int i;
|
||||
if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
|
||||
for(i=p->nState; i>iBefore; i--){
|
||||
p->aOp[i] = p->aOp[i-1];
|
||||
p->aArg[i] = p->aArg[i-1];
|
||||
}
|
||||
p->nState++;
|
||||
p->aOp[iBefore] = op;
|
||||
p->aArg[iBefore] = arg;
|
||||
return iBefore;
|
||||
}
|
||||
|
||||
/* Append a new opcode and argument to the end of the RE under construction.
|
||||
*/
|
||||
static int re_append(ReCompiled *p, int op, int arg){
|
||||
return re_insert(p, p->nState, op, arg);
|
||||
}
|
||||
|
||||
/* Make a copy of N opcodes starting at iStart onto the end of the RE
|
||||
** under construction.
|
||||
*/
|
||||
static void re_copy(ReCompiled *p, int iStart, int N){
|
||||
if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
|
||||
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
|
||||
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
|
||||
p->nState += N;
|
||||
}
|
||||
|
||||
/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
|
||||
** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If
|
||||
** c is not a hex digit *pV is unchanged.
|
||||
*/
|
||||
static int re_hex(int c, int *pV){
|
||||
if( c>='0' && c<='9' ){
|
||||
c -= '0';
|
||||
}else if( c>='a' && c<='f' ){
|
||||
c -= 'a' - 10;
|
||||
}else if( c>='A' && c<='F' ){
|
||||
c -= 'A' - 10;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
*pV = (*pV)*16 + (c & 0xff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* A backslash character has been seen, read the next character and
|
||||
** return its intepretation.
|
||||
*/
|
||||
static unsigned re_esc_char(ReCompiled *p){
|
||||
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
|
||||
static const char zTrans[] = "\a\f\n\r\t\v";
|
||||
int i, v = 0;
|
||||
char c;
|
||||
if( p->sIn.i>=p->sIn.mx ) return 0;
|
||||
c = p->sIn.z[p->sIn.i];
|
||||
if( c=='u' && p->sIn.i+5<p->sIn.mx ){
|
||||
v = 0;
|
||||
const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||||
if( re_hex(zIn[1],&v)
|
||||
&& re_hex(zIn[2],&v)
|
||||
&& re_hex(zIn[3],&v)
|
||||
&& re_hex(zIn[4],&v)
|
||||
){
|
||||
p->sIn.i += 5;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
if( c=='x' ){
|
||||
v = 0;
|
||||
for(i=1; p->sIn.i<p->sIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){}
|
||||
if( i>1 ){
|
||||
p->sIn.i += i;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
|
||||
if( zEsc[i] ){
|
||||
if( i<6 ) c = zTrans[i];
|
||||
p->sIn.i++;
|
||||
}else{
|
||||
p->zErr = "unknown \\ escape";
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static const char *re_subcompile_string(ReCompiled*);
|
||||
|
||||
/* Peek at the next byte of input */
|
||||
static unsigned char rePeek(ReCompiled *p){
|
||||
return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
|
||||
}
|
||||
|
||||
/* Compile RE text into a sequence of opcodes. Continue up to the
|
||||
** first unmatched ")" character, then return. If an error is found,
|
||||
** return a pointer to the error message string.
|
||||
*/
|
||||
static const char *re_subcompile_re(ReCompiled *p){
|
||||
const char *zErr;
|
||||
int iStart, iEnd, iGoto;
|
||||
iStart = p->nState;
|
||||
zErr = re_subcompile_string(p);
|
||||
if( zErr ) return zErr;
|
||||
while( rePeek(p)=='|' ){
|
||||
iEnd = p->nState;
|
||||
re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
|
||||
iGoto = re_append(p, RE_OP_GOTO, 0);
|
||||
p->sIn.i++;
|
||||
zErr = re_subcompile_string(p);
|
||||
if( zErr ) return zErr;
|
||||
p->aArg[iGoto] = p->nState - iGoto;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compile an element of regular expression text (anything that can be
|
||||
** an operand to the "|" operator). Return NULL on success or a pointer
|
||||
** to the error message if there is a problem.
|
||||
*/
|
||||
static const char *re_subcompile_string(ReCompiled *p){
|
||||
int iPrev = -1;
|
||||
int iStart;
|
||||
unsigned c;
|
||||
const char *zErr;
|
||||
while( (c = p->xNextChar(&p->sIn))!=0 ){
|
||||
iStart = p->nState;
|
||||
switch( c ){
|
||||
case '|':
|
||||
case '$':
|
||||
case ')': {
|
||||
p->sIn.i--;
|
||||
return 0;
|
||||
}
|
||||
case '(': {
|
||||
zErr = re_subcompile_re(p);
|
||||
if( zErr ) return zErr;
|
||||
if( rePeek(p)!=')' ) return "unmatched '('";
|
||||
p->sIn.i++;
|
||||
break;
|
||||
}
|
||||
case '.': {
|
||||
if( rePeek(p)=='*' ){
|
||||
re_append(p, RE_OP_ANYSTAR, 0);
|
||||
p->sIn.i++;
|
||||
}else{
|
||||
re_append(p, RE_OP_ANY, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '*': {
|
||||
if( iPrev<0 ) return "'*' without operand";
|
||||
re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
|
||||
re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
|
||||
break;
|
||||
}
|
||||
case '+': {
|
||||
if( iPrev<0 ) return "'+' without operand";
|
||||
re_append(p, RE_OP_FORK, iPrev - p->nState);
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
if( iPrev<0 ) return "'?' without operand";
|
||||
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
|
||||
break;
|
||||
}
|
||||
case '{': {
|
||||
int m = 0, n = 0;
|
||||
int sz, j;
|
||||
if( iPrev<0 ) return "'{m,n}' without operand";
|
||||
while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
|
||||
n = m;
|
||||
if( c==',' ){
|
||||
p->sIn.i++;
|
||||
n = 0;
|
||||
while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
|
||||
}
|
||||
if( c!='}' ) return "unmatched '{'";
|
||||
if( n>0 && n<m ) return "n less than m in '{m,n}'";
|
||||
p->sIn.i++;
|
||||
sz = p->nState - iPrev;
|
||||
if( m==0 ){
|
||||
if( n==0 ) return "both m and n are zero in '{m,n}'";
|
||||
re_insert(p, iPrev, RE_OP_FORK, sz+1);
|
||||
n--;
|
||||
}else{
|
||||
for(j=1; j<m; j++) re_copy(p, iPrev, sz);
|
||||
}
|
||||
for(j=m; j<n; j++){
|
||||
re_append(p, RE_OP_FORK, sz+1);
|
||||
re_copy(p, iPrev, sz);
|
||||
}
|
||||
if( n==0 && m>0 ){
|
||||
re_append(p, RE_OP_FORK, -sz);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '[': {
|
||||
int iFirst = p->nState;
|
||||
if( rePeek(p)=='^' ){
|
||||
re_append(p, RE_OP_CC_EXC, 0);
|
||||
p->sIn.i++;
|
||||
}else{
|
||||
re_append(p, RE_OP_CC_INC, 0);
|
||||
}
|
||||
while( (c = p->xNextChar(&p->sIn))!=0 ){
|
||||
if( c=='[' && rePeek(p)==':' ){
|
||||
return "POSIX character classes not supported";
|
||||
}
|
||||
if( c=='\\' ) c = re_esc_char(p);
|
||||
if( rePeek(p)=='-' ){
|
||||
re_append(p, RE_OP_CC_RANGE, c);
|
||||
p->sIn.i++;
|
||||
c = p->xNextChar(&p->sIn);
|
||||
if( c=='\\' ) c = re_esc_char(p);
|
||||
re_append(p, RE_OP_CC_RANGE, c);
|
||||
}else{
|
||||
re_append(p, RE_OP_CC_VALUE, c);
|
||||
}
|
||||
if( rePeek(p)==']' ){ p->sIn.i++; break; }
|
||||
}
|
||||
if( c==0 ) return "unclosed '['";
|
||||
p->aArg[iFirst] = p->nState - iFirst;
|
||||
break;
|
||||
}
|
||||
case '\\': {
|
||||
int specialOp = 0;
|
||||
switch( rePeek(p) ){
|
||||
case 'b': specialOp = RE_OP_BOUNDARY; break;
|
||||
case 'd': specialOp = RE_OP_DIGIT; break;
|
||||
case 'D': specialOp = RE_OP_NOTDIGIT; break;
|
||||
case 's': specialOp = RE_OP_SPACE; break;
|
||||
case 'S': specialOp = RE_OP_NOTSPACE; break;
|
||||
case 'w': specialOp = RE_OP_WORD; break;
|
||||
case 'W': specialOp = RE_OP_NOTWORD; break;
|
||||
}
|
||||
if( specialOp ){
|
||||
p->sIn.i++;
|
||||
re_append(p, specialOp, 0);
|
||||
}else{
|
||||
c = re_esc_char(p);
|
||||
re_append(p, RE_OP_MATCH, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
re_append(p, RE_OP_MATCH, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
iPrev = iStart;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free and reclaim all the memory used by a previously compiled
|
||||
** regular expression. Applications should invoke this routine once
|
||||
** for every call to re_compile() to avoid memory leaks.
|
||||
*/
|
||||
void re_free(ReCompiled *pRe){
|
||||
if( pRe ){
|
||||
sqlite3_free(pRe->aOp);
|
||||
sqlite3_free(pRe->aArg);
|
||||
sqlite3_free(pRe);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compile a textual regular expression in zIn[] into a compiled regular
|
||||
** expression suitable for us by re_match() and return a pointer to the
|
||||
** 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){
|
||||
ReCompiled *pRe;
|
||||
const char *zErr;
|
||||
int i, j;
|
||||
|
||||
*ppRe = 0;
|
||||
pRe = sqlite3_malloc( sizeof(*pRe) );
|
||||
if( pRe==0 ){
|
||||
return "out of memory";
|
||||
}
|
||||
memset(pRe, 0, sizeof(*pRe));
|
||||
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
|
||||
if( re_resize(pRe, 30) ){
|
||||
re_free(pRe);
|
||||
return "out of memory";
|
||||
}
|
||||
if( zIn[0]=='^' ){
|
||||
zIn++;
|
||||
}else{
|
||||
re_append(pRe, RE_OP_ANYSTAR, 0);
|
||||
}
|
||||
pRe->sIn.z = (unsigned char*)zIn;
|
||||
pRe->sIn.i = 0;
|
||||
pRe->sIn.mx = strlen((char*)pRe->sIn.z);
|
||||
zErr = re_subcompile_re(pRe);
|
||||
if( zErr ){
|
||||
re_free(pRe);
|
||||
return zErr;
|
||||
}
|
||||
if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
|
||||
re_append(pRe, RE_OP_MATCH, RE_EOF);
|
||||
re_append(pRe, RE_OP_ACCEPT, 0);
|
||||
*ppRe = pRe;
|
||||
}else if( pRe->sIn.i>=pRe->sIn.mx ){
|
||||
re_append(pRe, RE_OP_ACCEPT, 0);
|
||||
*ppRe = pRe;
|
||||
}else{
|
||||
re_free(pRe);
|
||||
return "unrecognized character";
|
||||
}
|
||||
if( pRe->aOp[0]==RE_OP_ANYSTAR ){
|
||||
for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
|
||||
unsigned x = pRe->aArg[i];
|
||||
if( x<=127 ){
|
||||
pRe->zInit[j++] = x;
|
||||
}else if( x<=0xfff ){
|
||||
pRe->zInit[j++] = 0xc0 | (x>>6);
|
||||
pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||||
}else if( x<=0xffff ){
|
||||
pRe->zInit[j++] = 0xd0 | (x>>12);
|
||||
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
|
||||
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>0 && pRe->zInit[j-1]==0 ) j--;
|
||||
pRe->nInit = j;
|
||||
}
|
||||
return pRe->zErr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the regexp() SQL function. This function implements
|
||||
** the build-in REGEXP operator. The first argument to the function is the
|
||||
** pattern and the second argument is the string. So, the SQL statements:
|
||||
**
|
||||
** A REGEXP B
|
||||
**
|
||||
** is implemented as regexp(B,A).
|
||||
*/
|
||||
static void re_sql_func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
ReCompiled *pRe; /* Compiled regular expression */
|
||||
const char *zPattern; /* The regular expression */
|
||||
const unsigned char *zStr;/* String being searched */
|
||||
const char *zErr; /* Compile error message */
|
||||
|
||||
pRe = sqlite3_get_auxdata(context, 0);
|
||||
if( pRe==0 ){
|
||||
zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPattern==0 ) return;
|
||||
zErr = re_compile(&pRe, zPattern, 0);
|
||||
if( zErr ){
|
||||
re_free(pRe);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
return;
|
||||
}
|
||||
if( pRe==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
|
||||
}
|
||||
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
|
||||
if( zStr!=0 ){
|
||||
sqlite3_result_int(context, re_match(pRe, zStr, -1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke this routine in order to install the REGEXP function in an
|
||||
** SQLite database connection.
|
||||
**
|
||||
** Use:
|
||||
**
|
||||
** sqlite3_auto_extension(sqlite3_add_regexp_func);
|
||||
**
|
||||
** to cause this extension to be automatically loaded into each new
|
||||
** database connection.
|
||||
*/
|
||||
int sqlite3_add_regexp_func(sqlite3 *db){
|
||||
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
||||
re_sql_func, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/* Implementation of the TCL command:
|
||||
**
|
||||
** sqlite3_add_regexp_func $DB
|
||||
*/
|
||||
static int tclSqlite3AddRegexpFunc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
sqlite3_add_regexp_func(db);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/* Register the sqlite3_add_regexp_func TCL command with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetestregexp_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_add_regexp_func",
|
||||
tclSqlite3AddRegexpFunc, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_TEST */
|
||||
/**************************** End Of Test Code *******************************/
|
@ -1280,6 +1280,7 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */
|
||||
case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */
|
||||
case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
|
||||
case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
|
||||
char bIntint; /* Started out as two integer operands */
|
||||
int flags; /* Combined MEM_* flags from both inputs */
|
||||
i64 iA; /* Integer value of left operand */
|
||||
i64 iB; /* Integer value of right operand */
|
||||
@ -1296,6 +1297,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
|
||||
if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){
|
||||
iA = pIn1->u.i;
|
||||
iB = pIn2->u.i;
|
||||
bIntint = 1;
|
||||
switch( pOp->opcode ){
|
||||
case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break;
|
||||
case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break;
|
||||
@ -1316,6 +1318,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
|
||||
pOut->u.i = iB;
|
||||
MemSetTypeFlag(pOut, MEM_Int);
|
||||
}else{
|
||||
bIntint = 0;
|
||||
fp_math:
|
||||
rA = sqlite3VdbeRealValue(pIn1);
|
||||
rB = sqlite3VdbeRealValue(pIn2);
|
||||
@ -1347,7 +1350,7 @@ fp_math:
|
||||
}
|
||||
pOut->r = rB;
|
||||
MemSetTypeFlag(pOut, MEM_Real);
|
||||
if( (flags & MEM_Real)==0 ){
|
||||
if( (flags & MEM_Real)==0 && !bIntint ){
|
||||
sqlite3VdbeIntegerAffinity(pOut);
|
||||
}
|
||||
#endif
|
||||
@ -2086,8 +2089,6 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
|
||||
**
|
||||
** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
|
||||
** set the flag and fall through to the next instruction.
|
||||
**
|
||||
** See also: JumpOnce
|
||||
*/
|
||||
case OP_Once: { /* jump */
|
||||
assert( pOp->p1<p->nOnceFlag );
|
||||
|
159
src/where.c
159
src/where.c
@ -253,7 +253,7 @@ struct WhereCost {
|
||||
#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
|
||||
#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
|
||||
#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
|
||||
#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
|
||||
#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */
|
||||
#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
|
||||
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
|
||||
#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
|
||||
@ -403,7 +403,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
|
||||
pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
|
||||
}
|
||||
pTerm = &pWC->a[idx = pWC->nTerm++];
|
||||
pTerm->pExpr = p;
|
||||
pTerm->pExpr = sqlite3ExprSkipCollate(p);
|
||||
pTerm->wtFlags = wtFlags;
|
||||
pTerm->pWC = pWC;
|
||||
pTerm->iParent = -1;
|
||||
@ -1188,7 +1188,8 @@ static void exprAnalyze(
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pMaskSet = pWC->pMaskSet;
|
||||
pExpr = sqlite3ExprSkipCollate(pTerm->pExpr);
|
||||
pExpr = pTerm->pExpr;
|
||||
assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
|
||||
prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
|
||||
op = pExpr->op;
|
||||
if( op==TK_IN ){
|
||||
@ -2056,7 +2057,7 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
|
||||
assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
|
||||
testcase( pTerm->eOperator==WO_IN );
|
||||
testcase( pTerm->eOperator==WO_ISNULL );
|
||||
if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
|
||||
if( pTerm->eOperator & (WO_ISNULL) ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
nTerm++;
|
||||
}
|
||||
@ -2104,15 +2105,18 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
|
||||
pUsage;
|
||||
|
||||
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
u8 op;
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
|
||||
testcase( pTerm->eOperator==WO_IN );
|
||||
testcase( pTerm->eOperator==WO_ISNULL );
|
||||
if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
|
||||
if( pTerm->eOperator & (WO_ISNULL) ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
pIdxCons[j].iColumn = pTerm->u.leftColumn;
|
||||
pIdxCons[j].iTermOffset = i;
|
||||
pIdxCons[j].op = (u8)pTerm->eOperator;
|
||||
op = (u8)pTerm->eOperator;
|
||||
if( op==WO_IN ) op = WO_EQ;
|
||||
pIdxCons[j].op = op;
|
||||
/* The direct assignment in the previous line is possible only because
|
||||
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
|
||||
** following asserts verify this fact. */
|
||||
@ -2122,7 +2126,7 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
|
||||
assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
|
||||
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
|
||||
assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
|
||||
assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
|
||||
assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
|
||||
j++;
|
||||
}
|
||||
for(i=0; i<nOrderBy; i++){
|
||||
@ -2208,6 +2212,7 @@ static void bestVirtualIndex(WhereBestIdx *p){
|
||||
WhereTerm *pTerm;
|
||||
int i, j;
|
||||
int nOrderBy;
|
||||
int bAllowIN; /* Allow IN optimizations */
|
||||
double rCost;
|
||||
|
||||
/* Make sure wsFlags is initialized to some sane value. Otherwise, if the
|
||||
@ -2242,59 +2247,87 @@ static void bestVirtualIndex(WhereBestIdx *p){
|
||||
assert( pTab->azModuleArg && pTab->azModuleArg[0] );
|
||||
assert( sqlite3GetVTable(pParse->db, pTab) );
|
||||
|
||||
/* Set the aConstraint[].usable fields and initialize all
|
||||
** output variables to zero.
|
||||
**
|
||||
** aConstraint[].usable is true for constraints where the right-hand
|
||||
** side contains only references to tables to the left of the current
|
||||
** table. In other words, if the constraint is of the form:
|
||||
**
|
||||
** column = expr
|
||||
**
|
||||
** and we are evaluating a join, then the constraint on column is
|
||||
** only valid if all tables referenced in expr occur to the left
|
||||
** of the table containing column.
|
||||
**
|
||||
** The aConstraints[] array contains entries for all constraints
|
||||
** on the current table. That way we only have to compute it once
|
||||
** even though we might try to pick the best index multiple times.
|
||||
** For each attempt at picking an index, the order of tables in the
|
||||
** join might be different so we have to recompute the usable flag
|
||||
** each time.
|
||||
/* Try once or twice. On the first attempt, allow IN optimizations.
|
||||
** If an IN optimization is accepted by the virtual table xBestIndex
|
||||
** method, but the pInfo->aConstrainUsage.omit flag is not set, then
|
||||
** the query will not work because it might allow duplicate rows in
|
||||
** output. In that case, run the xBestIndex method a second time
|
||||
** without the IN constraints. Usually this loop only runs once.
|
||||
** The loop will exit using a "break" statement.
|
||||
*/
|
||||
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
|
||||
pUsage = pIdxInfo->aConstraintUsage;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
|
||||
j = pIdxCons->iTermOffset;
|
||||
pTerm = &pWC->a[j];
|
||||
pIdxCons->usable = (pTerm->prereqRight&p->notReady) ? 0 : 1;
|
||||
}
|
||||
memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
|
||||
if( pIdxInfo->needToFreeIdxStr ){
|
||||
sqlite3_free(pIdxInfo->idxStr);
|
||||
}
|
||||
pIdxInfo->idxStr = 0;
|
||||
pIdxInfo->idxNum = 0;
|
||||
pIdxInfo->needToFreeIdxStr = 0;
|
||||
pIdxInfo->orderByConsumed = 0;
|
||||
/* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
|
||||
nOrderBy = pIdxInfo->nOrderBy;
|
||||
if( !p->pOrderBy ){
|
||||
pIdxInfo->nOrderBy = 0;
|
||||
}
|
||||
for(bAllowIN=1; 1; bAllowIN--){
|
||||
assert( bAllowIN==0 || bAllowIN==1 );
|
||||
|
||||
if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
|
||||
return;
|
||||
}
|
||||
|
||||
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
if( pUsage[i].argvIndex>0 ){
|
||||
p->cost.used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight;
|
||||
/* Set the aConstraint[].usable fields and initialize all
|
||||
** output variables to zero.
|
||||
**
|
||||
** aConstraint[].usable is true for constraints where the right-hand
|
||||
** side contains only references to tables to the left of the current
|
||||
** table. In other words, if the constraint is of the form:
|
||||
**
|
||||
** column = expr
|
||||
**
|
||||
** and we are evaluating a join, then the constraint on column is
|
||||
** only valid if all tables referenced in expr occur to the left
|
||||
** of the table containing column.
|
||||
**
|
||||
** The aConstraints[] array contains entries for all constraints
|
||||
** on the current table. That way we only have to compute it once
|
||||
** even though we might try to pick the best index multiple times.
|
||||
** For each attempt at picking an index, the order of tables in the
|
||||
** join might be different so we have to recompute the usable flag
|
||||
** each time.
|
||||
*/
|
||||
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
|
||||
pUsage = pIdxInfo->aConstraintUsage;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
|
||||
j = pIdxCons->iTermOffset;
|
||||
pTerm = &pWC->a[j];
|
||||
if( (pTerm->prereqRight&p->notReady)==0
|
||||
&& (bAllowIN || pTerm->eOperator!=WO_IN)
|
||||
){
|
||||
pIdxCons->usable = 1;
|
||||
}else{
|
||||
pIdxCons->usable = 0;
|
||||
}
|
||||
}
|
||||
memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
|
||||
if( pIdxInfo->needToFreeIdxStr ){
|
||||
sqlite3_free(pIdxInfo->idxStr);
|
||||
}
|
||||
pIdxInfo->idxStr = 0;
|
||||
pIdxInfo->idxNum = 0;
|
||||
pIdxInfo->needToFreeIdxStr = 0;
|
||||
pIdxInfo->orderByConsumed = 0;
|
||||
/* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
|
||||
nOrderBy = pIdxInfo->nOrderBy;
|
||||
if( !p->pOrderBy ){
|
||||
pIdxInfo->nOrderBy = 0;
|
||||
}
|
||||
|
||||
if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
|
||||
return;
|
||||
}
|
||||
|
||||
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
|
||||
if( pUsage[i].argvIndex>0 ){
|
||||
j = pIdxCons->iTermOffset;
|
||||
pTerm = &pWC->a[j];
|
||||
p->cost.used |= pTerm->prereqRight;
|
||||
if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){
|
||||
/* Do not attempt to use an IN constraint if the virtual table
|
||||
** says that the equivalent EQ constraint cannot be safely omitted.
|
||||
** If we do attempt to use such a constraint, some rows might be
|
||||
** repeated in the output. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( i>=pIdxInfo->nConstraint ) break;
|
||||
}
|
||||
|
||||
|
||||
/* If there is an ORDER BY clause, and the selected virtual table index
|
||||
** does not satisfy it, increase the cost of the scan accordingly. This
|
||||
** matches the processing for non-virtual tables in bestBtreeIndex().
|
||||
@ -4063,6 +4096,7 @@ static Bitmask codeOneLoopStart(
|
||||
** to access the data.
|
||||
*/
|
||||
int iReg; /* P3 Value for OP_VFilter */
|
||||
int addrNotFound;
|
||||
sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
|
||||
int nConstraint = pVtabIdx->nConstraint;
|
||||
struct sqlite3_index_constraint_usage *aUsage =
|
||||
@ -4072,11 +4106,18 @@ static Bitmask codeOneLoopStart(
|
||||
|
||||
sqlite3ExprCachePush(pParse);
|
||||
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
|
||||
addrNotFound = pLevel->addrBrk;
|
||||
for(j=1; j<=nConstraint; j++){
|
||||
for(k=0; k<nConstraint; k++){
|
||||
if( aUsage[k].argvIndex==j ){
|
||||
int iTerm = aConstraint[k].iTermOffset;
|
||||
sqlite3ExprCode(pParse, pWC->a[iTerm].pExpr->pRight, iReg+j+1);
|
||||
WhereTerm *pTerm = &pWC->a[aConstraint[k].iTermOffset];
|
||||
int iTarget = iReg+j+1;
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
codeEqualityTerm(pParse, pTerm, pLevel, iTarget);
|
||||
addrNotFound = pLevel->addrNxt;
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4084,7 +4125,7 @@ static Bitmask codeOneLoopStart(
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
|
||||
sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr,
|
||||
sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr,
|
||||
pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
|
||||
pVtabIdx->needToFreeIdxStr = 0;
|
||||
for(j=0; j<nConstraint; j++){
|
||||
|
@ -131,12 +131,12 @@ do_test auth2-2.3 {
|
||||
}
|
||||
set ::authargs
|
||||
} {SQLITE_SELECT {} {} {} {}
|
||||
SQLITE_READ v2 a main {}
|
||||
SQLITE_READ v2 b main {}
|
||||
SQLITE_READ t2 x main v2
|
||||
SQLITE_READ t2 y main v2
|
||||
SQLITE_READ t2 y main v2
|
||||
SQLITE_READ t2 z main v2
|
||||
SQLITE_READ v2 a main {}
|
||||
SQLITE_READ v2 b main {}
|
||||
SQLITE_SELECT {} {} {} v2
|
||||
}
|
||||
do_test auth2-2.4 {
|
||||
@ -149,20 +149,20 @@ do_test auth2-2.4 {
|
||||
}
|
||||
set ::authargs
|
||||
} {SQLITE_SELECT {} {} {} {}
|
||||
SQLITE_READ v2 b main {}
|
||||
SQLITE_READ v2 a main {}
|
||||
SQLITE_READ t2 x main v2
|
||||
SQLITE_READ t2 y main v2
|
||||
SQLITE_READ t2 y main v2
|
||||
SQLITE_READ t2 z main v2
|
||||
SQLITE_READ v2 b main {}
|
||||
SQLITE_READ v2 a main {}
|
||||
SQLITE_SELECT {} {} {} v2
|
||||
SQLITE_SELECT {} {} {} {}
|
||||
SQLITE_READ v2 b main {}
|
||||
SQLITE_READ v2 a main {}
|
||||
SQLITE_READ t2 x main v2
|
||||
SQLITE_READ t2 y main v2
|
||||
SQLITE_READ t2 y main v2
|
||||
SQLITE_READ t2 z main v2
|
||||
SQLITE_READ v2 b main {}
|
||||
SQLITE_READ v2 a main {}
|
||||
SQLITE_SELECT {} {} {} v2
|
||||
}
|
||||
db2 close
|
||||
|
@ -627,7 +627,8 @@ proc test_efkey_57 {tn isError sql} {
|
||||
execsql $sql
|
||||
do_test e_fkey-18.$tn {
|
||||
catchsql { INSERT INTO t2 VALUES(NULL) }
|
||||
} [lindex {{0 {}} {1 {foreign key mismatch}}} $isError]
|
||||
} [lindex {{0 {}} {/1 {foreign key mismatch - ".*" referencing ".*"}/}} \
|
||||
$isError]
|
||||
}
|
||||
test_efkey_57 2 0 { CREATE TABLE t1(x PRIMARY KEY) }
|
||||
test_efkey_57 3 0 { CREATE TABLE t1(x UNIQUE) }
|
||||
@ -698,16 +699,16 @@ do_test e_fkey-19.2 {
|
||||
} {}
|
||||
do_test e_fkey-19.2 {
|
||||
catchsql { INSERT INTO child4 VALUES('xxx', 5) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child4" referencing "parent"}}
|
||||
do_test e_fkey-19.3 {
|
||||
catchsql { INSERT INTO child5 VALUES('xxx', 6) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child5" referencing "parent"}}
|
||||
do_test e_fkey-19.4 {
|
||||
catchsql { INSERT INTO child6 VALUES(2, 3) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child6" referencing "parent"}}
|
||||
do_test e_fkey-19.5 {
|
||||
catchsql { INSERT INTO child7 VALUES(3) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child7" referencing "parent"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test errors in the database schema that are detected while preparing
|
||||
@ -765,12 +766,12 @@ do_test e_fkey-20.1 {
|
||||
|
||||
foreach {tn tbl ptbl err} {
|
||||
2 c1 {} "no such table: main.nosuchtable"
|
||||
3 c2 p2 "foreign key mismatch"
|
||||
4 c3 p3 "foreign key mismatch"
|
||||
5 c4 p4 "foreign key mismatch"
|
||||
6 c5 p5 "foreign key mismatch"
|
||||
7 c6 p6 "foreign key mismatch"
|
||||
8 c7 p7 "foreign key mismatch"
|
||||
3 c2 p2 "foreign key mismatch - \"c2\" referencing \"p2\""
|
||||
4 c3 p3 "foreign key mismatch - \"c3\" referencing \"p3\""
|
||||
5 c4 p4 "foreign key mismatch - \"c4\" referencing \"p4\""
|
||||
6 c5 p5 "foreign key mismatch - \"c5\" referencing \"p5\""
|
||||
7 c6 p6 "foreign key mismatch - \"c6\" referencing \"p6\""
|
||||
8 c7 p7 "foreign key mismatch - \"c7\" referencing \"p7\""
|
||||
} {
|
||||
do_test e_fkey-20.$tn.1 {
|
||||
catchsql "INSERT INTO $tbl VALUES('a', 'b')"
|
||||
@ -820,22 +821,22 @@ do_test e_fkey-21.2 {
|
||||
} {}
|
||||
do_test e_fkey-21.3 {
|
||||
catchsql { INSERT INTO child9 VALUES('I') }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child9" referencing "parent2"}}
|
||||
do_test e_fkey-21.4 {
|
||||
catchsql { INSERT INTO child9 VALUES('II') }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child9" referencing "parent2"}}
|
||||
do_test e_fkey-21.5 {
|
||||
catchsql { INSERT INTO child9 VALUES(NULL) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child9" referencing "parent2"}}
|
||||
do_test e_fkey-21.6 {
|
||||
catchsql { INSERT INTO child10 VALUES('I', 'II', 'III') }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child10" referencing "parent2"}}
|
||||
do_test e_fkey-21.7 {
|
||||
catchsql { INSERT INTO child10 VALUES(1, 2, 3) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child10" referencing "parent2"}}
|
||||
do_test e_fkey-21.8 {
|
||||
catchsql { INSERT INTO child10 VALUES(NULL, NULL, NULL) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "child10" referencing "parent2"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test errors that are reported when creating the child table.
|
||||
@ -1151,7 +1152,7 @@ do_test e_fkey-28.8 {
|
||||
CREATE TABLE c(a, b, FOREIGN KEY(a,b) REFERENCES p);
|
||||
}
|
||||
catchsql {DELETE FROM p}
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "c" referencing "p"}}
|
||||
do_test e_fkey-28.9 {
|
||||
drop_all_tables
|
||||
execsql {
|
||||
@ -1159,7 +1160,7 @@ do_test e_fkey-28.9 {
|
||||
CREATE TABLE c(a REFERENCES p);
|
||||
}
|
||||
catchsql {DELETE FROM p}
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "c" referencing "p"}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -2729,11 +2730,11 @@ do_test e_fkey-60.3 {
|
||||
do_test e_fkey-60.4 {
|
||||
execsql { CREATE TABLE nosuchtable(x PRIMARY KEY) }
|
||||
catchsql { DELETE FROM p }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "c2" referencing "p"}}
|
||||
do_test e_fkey-60.5 {
|
||||
execsql { DROP TABLE c1 }
|
||||
catchsql { DELETE FROM p }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "c2" referencing "p"}}
|
||||
do_test e_fkey-60.6 {
|
||||
execsql { DROP TABLE c2 }
|
||||
execsql { DELETE FROM p }
|
||||
|
@ -141,8 +141,8 @@ do_insert_tests e_insert-0 {
|
||||
|
||||
delete_all_data
|
||||
|
||||
# EVIDENCE-OF: R-20288-20462 The first form (with the "VALUES" keyword)
|
||||
# creates a single new row in an existing table.
|
||||
# EVIDENCE-OF: R-21490-41092 The first form (with the "VALUES" keyword)
|
||||
# creates one or more new rows in an existing table.
|
||||
#
|
||||
do_insert_tests e_insert-1.1 {
|
||||
0 "SELECT count(*) FROM a2" {0}
|
||||
@ -152,11 +152,14 @@ do_insert_tests e_insert-1.1 {
|
||||
|
||||
2a "INSERT INTO a2(a, b) VALUES(1, 2)" {}
|
||||
2b "SELECT count(*) FROM a2" {2}
|
||||
|
||||
3a "INSERT INTO a2(a) VALUES(3),(4)" {}
|
||||
3b "SELECT count(*) FROM a2" {4}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-36040-20870 If no column-list is specified then the
|
||||
# number of values must be the same as the number of columns in the
|
||||
# table.
|
||||
# EVIDENCE-OF: R-53616-44976 If no column-list is specified then the
|
||||
# number of values inserted into each row must be the same as the number
|
||||
# of columns in the table.
|
||||
#
|
||||
# A test in the block above verifies that if the VALUES list has the
|
||||
# correct number of columns (for table a2, 3 columns) works. So these
|
||||
@ -171,9 +174,10 @@ do_insert_tests e_insert-1.2 -error {
|
||||
4 "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-04006-57648 In this case the result of evaluating the
|
||||
# left-most expression in the VALUES list is inserted into the left-most
|
||||
# column of the new row, and so on.
|
||||
# EVIDENCE-OF: R-34231-22576 In this case the result of evaluating the
|
||||
# left-most expression in each term of the VALUES list is inserted into
|
||||
# the left-most column of the each new row, and forth for each
|
||||
# subsequent expression.
|
||||
#
|
||||
delete_all_data
|
||||
do_insert_tests e_insert-1.3 {
|
||||
@ -187,8 +191,9 @@ do_insert_tests e_insert-1.3 {
|
||||
3b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {2 x y}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-62524-00361 If a column-list is specified, then the
|
||||
# number of values must match the number of specified columns.
|
||||
# EVIDENCE-OF: R-44710-64652 If a column-list is specified, then the
|
||||
# number of values in each term of the VALUS list must match the number
|
||||
# of specified columns.
|
||||
#
|
||||
do_insert_tests e_insert-1.4 -error {
|
||||
%d values for %d columns
|
||||
|
@ -261,9 +261,9 @@ foreach {tn uri error} "
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-09651-31805 If "ro" is specified, then the database is
|
||||
# EVIDENCE-OF: R-43036-46756 If "ro" is specified, then the database is
|
||||
# opened for read-only access, just as if the SQLITE_OPEN_READONLY flag
|
||||
# had been set in the third argument to sqlite3_prepare_v2().
|
||||
# had been set in the third argument to sqlite3_open_v2().
|
||||
#
|
||||
# EVIDENCE-OF: R-40137-26050 If the mode option is set to "rw", then the
|
||||
# database is opened for read-write (but not create) access, as if
|
||||
|
@ -213,4 +213,39 @@ do_execsql_test filefmt-3.3 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
reset_db
|
||||
do_execsql_test filefmt-4.1 {
|
||||
PRAGMA auto_vacuum = 1;
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(x, y);
|
||||
|
||||
INSERT INTO t1 VALUES(randomblob(100), randomblob(100));
|
||||
INSERT INTO t1 VALUES(randomblob(100), randomblob(100));
|
||||
INSERT INTO t1 VALUES(randomblob(100), randomblob(100));
|
||||
INSERT INTO t1 VALUES(randomblob(100), randomblob(100));
|
||||
INSERT INTO t1 VALUES(randomblob(100), randomblob(100));
|
||||
INSERT INTO t1 VALUES(randomblob(100), randomblob(100));
|
||||
|
||||
INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1;
|
||||
INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1;
|
||||
INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1;
|
||||
INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1;
|
||||
}
|
||||
|
||||
do_test filefmt-4.2 {
|
||||
sql36231 { INSERT INTO t2 SELECT * FROM t1 }
|
||||
} {}
|
||||
|
||||
do_test filefmt-4.3 {
|
||||
forcedelete bak.db
|
||||
db backup bak.db
|
||||
} {}
|
||||
|
||||
do_test filefmt-4.4 {
|
||||
sqlite3 db2 bak.db
|
||||
db2 eval { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
db2 close
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -139,14 +139,21 @@ set FkeySimpleTests {
|
||||
4.17 "UPDATE t7 SET a = 10" {0 {}}
|
||||
|
||||
5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}}
|
||||
5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}}
|
||||
5.2 "INSERT INTO t10 VALUES(1, 3)"
|
||||
{1 {foreign key mismatch - "t10" referencing "t9"}}
|
||||
}
|
||||
|
||||
do_test fkey2-1.1.0 {
|
||||
execsql [string map {/D/ {}} $FkeySimpleSchema]
|
||||
} {}
|
||||
foreach {tn zSql res} $FkeySimpleTests {
|
||||
do_test fkey2-1.1.$tn { catchsql $zSql } $res
|
||||
do_test fkey2-1.1.$tn.1 { catchsql $zSql } $res
|
||||
do_test fkey2-1.1.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {}
|
||||
do_test fkey2-1.1.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {}
|
||||
do_test fkey2-1.1.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {}
|
||||
do_test fkey2-1.1.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {}
|
||||
do_test fkey2-1.1.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {}
|
||||
do_test fkey2-1.1.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {}
|
||||
}
|
||||
drop_all_tables
|
||||
|
||||
@ -155,6 +162,12 @@ do_test fkey2-1.2.0 {
|
||||
} {}
|
||||
foreach {tn zSql res} $FkeySimpleTests {
|
||||
do_test fkey2-1.2.$tn { catchsql $zSql } $res
|
||||
do_test fkey2-1.2.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {}
|
||||
do_test fkey2-1.2.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {}
|
||||
do_test fkey2-1.2.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {}
|
||||
do_test fkey2-1.2.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {}
|
||||
do_test fkey2-1.2.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {}
|
||||
do_test fkey2-1.2.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {}
|
||||
}
|
||||
drop_all_tables
|
||||
|
||||
@ -165,6 +178,12 @@ do_test fkey2-1.3.0 {
|
||||
foreach {tn zSql res} $FkeySimpleTests {
|
||||
if {$res == "0 {}"} { set res {0 1} }
|
||||
do_test fkey2-1.3.$tn { catchsql $zSql } $res
|
||||
do_test fkey2-1.3.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {}
|
||||
do_test fkey2-1.3.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {}
|
||||
do_test fkey2-1.3.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {}
|
||||
do_test fkey2-1.3.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {}
|
||||
do_test fkey2-1.3.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {}
|
||||
do_test fkey2-1.3.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {}
|
||||
}
|
||||
execsql { PRAGMA count_changes = 0 }
|
||||
drop_all_tables
|
||||
@ -681,7 +700,7 @@ foreach zSql [list {
|
||||
do_test fkey2-10.1.[incr tn] {
|
||||
execsql $zSql
|
||||
catchsql { INSERT INTO c DEFAULT VALUES }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {/1 {foreign key mismatch - "c" referencing "."}/}
|
||||
}
|
||||
|
||||
# "rowid" cannot be used as part of a child or parent key definition
|
||||
@ -709,7 +728,7 @@ do_test fkey2-10.2.1 {
|
||||
INSERT INTO t1(rowid, a, b) VALUES(1, 1, 1);
|
||||
INSERT INTO t2 VALUES(1, 1);
|
||||
}
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "t2" referencing "t1"}}
|
||||
do_test fkey2-10.2.2 {
|
||||
drop_all_tables
|
||||
catchsql {
|
||||
@ -1223,7 +1242,7 @@ do_test fkey-2.14.3.8 {
|
||||
CREATE TABLE cc(a, b, FOREIGN KEY(a, b) REFERENCES pp(x, z));
|
||||
}
|
||||
catchsql { INSERT INTO cc VALUES(1, 2) }
|
||||
} {1 {foreign key mismatch}}
|
||||
} {1 {foreign key mismatch - "cc" referencing "pp"}}
|
||||
do_test fkey-2.14.3.9 {
|
||||
execsql { DROP TABLE cc }
|
||||
} {}
|
||||
|
310
test/fkey5.test
Normal file
310
test/fkey5.test
Normal file
@ -0,0 +1,310 @@
|
||||
# 2012 December 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 implements regression tests for SQLite library.
|
||||
#
|
||||
# This file tests the PRAGMA foreign_key_check command.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable {!foreignkey} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test fkey5-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE p1(a INTEGER PRIMARY KEY); INSERT INTO p1 VALUES(88),(89);
|
||||
CREATE TABLE p2(a INT PRIMARY KEY); INSERT INTO p2 VALUES(77),(78);
|
||||
CREATE TABLE p3(a TEXT PRIMARY KEY);
|
||||
INSERT INTO p3 VALUES(66),(67),('alpha'),('BRAVO');
|
||||
CREATE TABLE p4(a TEXT PRIMARY KEY COLLATE nocase);
|
||||
INSERT INTO p4 VALUES('alpha'),('BRAVO'),('55'),('Delta'),('ECHO');
|
||||
CREATE TABLE p5(a INTEGER PRIMARY KEY, b, c, UNIQUE(b,c));
|
||||
INSERT INTO p5 VALUES(1,'Alpha','abc'),(2,'beta','def');
|
||||
CREATE TABLE p6(a INTEGER PRIMARY KEY, b TEXT COLLATE nocase,
|
||||
c TEXT COLLATE rtrim, UNIQUE(b,c));
|
||||
INSERT INTO p6 VALUES(1,'Alpha','abc '),(2,'bETA','def ');
|
||||
|
||||
CREATE TABLE c1(x INTEGER PRIMARY KEY references p1);
|
||||
CREATE TABLE c2(x INTEGER PRIMARY KEY references p2);
|
||||
CREATE TABLE c3(x INTEGER PRIMARY KEY references p3);
|
||||
CREATE TABLE c4(x INTEGER PRIMARY KEY references p4);
|
||||
CREATE TABLE c5(x INT references p1);
|
||||
CREATE TABLE c6(x INT references p2);
|
||||
CREATE TABLE c7(x INT references p3);
|
||||
CREATE TABLE c8(x INT references p4);
|
||||
CREATE TABLE c9(x TEXT UNIQUE references p1);
|
||||
CREATE TABLE c10(x TEXT UNIQUE references p2);
|
||||
CREATE TABLE c11(x TEXT UNIQUE references p3);
|
||||
CREATE TABLE c12(x TEXT UNIQUE references p4);
|
||||
CREATE TABLE c13(x TEXT COLLATE nocase references p3);
|
||||
CREATE TABLE c14(x TEXT COLLATE nocase references p4);
|
||||
CREATE TABLE c15(x, y, FOREIGN KEY(x,y) REFERENCES p5(b,c));
|
||||
CREATE TABLE c16(x, y, FOREIGN KEY(x,y) REFERENCES p5(c,b));
|
||||
CREATE TABLE c17(x, y, FOREIGN KEY(x,y) REFERENCES p6(b,c));
|
||||
CREATE TABLE c18(x, y, FOREIGN KEY(x,y) REFERENCES p6(c,b));
|
||||
CREATE TABLE c19(x TEXT COLLATE nocase, y TEXT COLLATE rtrim,
|
||||
FOREIGN KEY(x,y) REFERENCES p5(b,c));
|
||||
CREATE TABLE c20(x TEXT COLLATE nocase, y TEXT COLLATE rtrim,
|
||||
FOREIGN KEY(x,y) REFERENCES p5(c,b));
|
||||
CREATE TABLE c21(x TEXT COLLATE nocase, y TEXT COLLATE rtrim,
|
||||
FOREIGN KEY(x,y) REFERENCES p6(b,c));
|
||||
CREATE TABLE c22(x TEXT COLLATE nocase, y TEXT COLLATE rtrim,
|
||||
FOREIGN KEY(x,y) REFERENCES p6(c,b));
|
||||
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {}
|
||||
do_test fkey5-1.2 {
|
||||
db eval {
|
||||
INSERT INTO c1 VALUES(90),(87),(88);
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c1 87 p1 0 c1 90 p1 0}
|
||||
do_test fkey5-1.3 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c1);
|
||||
}
|
||||
} {c1 87 p1 0 c1 90 p1 0}
|
||||
do_test fkey5-1.4 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c2);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test fkey5-2.0 {
|
||||
db eval {
|
||||
INSERT INTO c5 SELECT x FROM c1;
|
||||
DELETE FROM c1;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c5 1 p1 0 c5 3 p1 0}
|
||||
do_test fkey5-2.1 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c5);
|
||||
}
|
||||
} {c5 1 p1 0 c5 3 p1 0}
|
||||
do_test fkey5-2.2 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c1);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test fkey5-3.0 {
|
||||
db eval {
|
||||
INSERT INTO c9 SELECT x FROM c5;
|
||||
DELETE FROM c5;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c9 1 p1 0 c9 3 p1 0}
|
||||
do_test fkey5-3.1 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c9);
|
||||
}
|
||||
} {c9 1 p1 0 c9 3 p1 0}
|
||||
do_test fkey5-3.2 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c5);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test fkey5-4.0 {
|
||||
db eval {
|
||||
DELETE FROM c9;
|
||||
INSERT INTO c2 VALUES(79),(77),(76);
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c2 76 p2 0 c2 79 p2 0}
|
||||
do_test fkey5-4.1 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c2);
|
||||
}
|
||||
} {c2 76 p2 0 c2 79 p2 0}
|
||||
do_test fkey5-4.2 {
|
||||
db eval {
|
||||
INSERT INTO c6 SELECT x FROM c2;
|
||||
DELETE FROM c2;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c6 1 p2 0 c6 3 p2 0}
|
||||
do_test fkey5-4.3 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c6);
|
||||
}
|
||||
} {c6 1 p2 0 c6 3 p2 0}
|
||||
do_test fkey5-4.4 {
|
||||
db eval {
|
||||
INSERT INTO c10 SELECT x FROM c6;
|
||||
DELETE FROM c6;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c10 1 p2 0 c10 3 p2 0}
|
||||
do_test fkey5-4.5 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c10);
|
||||
}
|
||||
} {c10 1 p2 0 c10 3 p2 0}
|
||||
|
||||
do_test fkey5-5.0 {
|
||||
db eval {
|
||||
DELETE FROM c10;
|
||||
INSERT INTO c3 VALUES(68),(67),(65);
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c3 65 p3 0 c3 68 p3 0}
|
||||
do_test fkey5-5.1 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c3);
|
||||
}
|
||||
} {c3 65 p3 0 c3 68 p3 0}
|
||||
do_test fkey5-5.2 {
|
||||
db eval {
|
||||
INSERT INTO c7 SELECT x FROM c3;
|
||||
INSERT INTO c7 VALUES('Alpha'),('alpha'),('foxtrot');
|
||||
DELETE FROM c3;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c7 1 p3 0 c7 3 p3 0 c7 4 p3 0 c7 6 p3 0}
|
||||
do_test fkey5-5.3 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c7);
|
||||
}
|
||||
} {c7 1 p3 0 c7 3 p3 0 c7 4 p3 0 c7 6 p3 0}
|
||||
do_test fkey5-5.4 {
|
||||
db eval {
|
||||
INSERT INTO c11 SELECT x FROM c7;
|
||||
DELETE FROM c7;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c11 1 p3 0 c11 3 p3 0 c11 4 p3 0 c11 6 p3 0}
|
||||
do_test fkey5-5.5 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c11);
|
||||
}
|
||||
} {c11 1 p3 0 c11 3 p3 0 c11 4 p3 0 c11 6 p3 0}
|
||||
|
||||
do_test fkey5-6.0 {
|
||||
db eval {
|
||||
DELETE FROM c11;
|
||||
INSERT INTO c4 VALUES(54),(55),(56);
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c4 54 p4 0 c4 56 p4 0}
|
||||
do_test fkey5-6.1 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c4);
|
||||
}
|
||||
} {c4 54 p4 0 c4 56 p4 0}
|
||||
do_test fkey5-6.2 {
|
||||
db eval {
|
||||
INSERT INTO c8 SELECT x FROM c4;
|
||||
INSERT INTO c8 VALUES('Alpha'),('ALPHA'),('foxtrot');
|
||||
DELETE FROM c4;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c8 1 p4 0 c8 3 p4 0 c8 6 p4 0}
|
||||
do_test fkey5-6.3 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c8);
|
||||
}
|
||||
} {c8 1 p4 0 c8 3 p4 0 c8 6 p4 0}
|
||||
do_test fkey5-6.4 {
|
||||
db eval {
|
||||
INSERT INTO c12 SELECT x FROM c8;
|
||||
DELETE FROM c8;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0}
|
||||
do_test fkey5-6.5 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c12);
|
||||
}
|
||||
} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0}
|
||||
|
||||
do_test fkey5-7.1 {
|
||||
db eval {
|
||||
INSERT OR IGNORE INTO c13 SELECT * FROM c12;
|
||||
INSERT OR IGNORE INTO C14 SELECT * FROM c12;
|
||||
DELETE FROM c12;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0 c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0}
|
||||
do_test fkey5-7.2 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c14);
|
||||
}
|
||||
} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0}
|
||||
do_test fkey5-7.3 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c13);
|
||||
}
|
||||
} {c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0}
|
||||
|
||||
do_test fkey5-8.0 {
|
||||
db eval {
|
||||
DELETE FROM c13;
|
||||
DELETE FROM c14;
|
||||
INSERT INTO c19 VALUES('alpha','abc');
|
||||
PRAGMA foreign_key_check(c19);
|
||||
}
|
||||
} {c19 1 p5 0}
|
||||
do_test fkey5-8.1 {
|
||||
db eval {
|
||||
DELETE FROM c19;
|
||||
INSERT INTO c19 VALUES('Alpha','abc');
|
||||
PRAGMA foreign_key_check(c19);
|
||||
}
|
||||
} {}
|
||||
do_test fkey5-8.2 {
|
||||
db eval {
|
||||
INSERT INTO c20 VALUES('Alpha','abc');
|
||||
PRAGMA foreign_key_check(c20);
|
||||
}
|
||||
} {c20 1 p5 0}
|
||||
do_test fkey5-8.3 {
|
||||
db eval {
|
||||
DELETE FROM c20;
|
||||
INSERT INTO c20 VALUES('abc','Alpha');
|
||||
PRAGMA foreign_key_check(c20);
|
||||
}
|
||||
} {}
|
||||
do_test fkey5-8.4 {
|
||||
db eval {
|
||||
INSERT INTO c21 VALUES('alpha','abc ');
|
||||
PRAGMA foreign_key_check(c21);
|
||||
}
|
||||
} {}
|
||||
do_test fkey5-8.5 {
|
||||
db eval {
|
||||
DELETE FROM c21;
|
||||
INSERT INTO c19 VALUES('Alpha','abc');
|
||||
PRAGMA foreign_key_check(c21);
|
||||
}
|
||||
} {}
|
||||
do_test fkey5-8.6 {
|
||||
db eval {
|
||||
INSERT INTO c22 VALUES('Alpha','abc');
|
||||
PRAGMA foreign_key_check(c22);
|
||||
}
|
||||
} {c22 1 p6 0}
|
||||
do_test fkey5-8.7 {
|
||||
db eval {
|
||||
DELETE FROM c22;
|
||||
INSERT INTO c22 VALUES('abc ','ALPHA');
|
||||
PRAGMA foreign_key_check(c22);
|
||||
}
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
finish_test
|
@ -29,6 +29,7 @@ do_malloc_test fkey_malloc-1 -sqlprep {
|
||||
INSERT INTO t2 VALUES('aaa');
|
||||
UPDATE t1 SET a = 'bbb';
|
||||
DELETE FROM t1;
|
||||
PRAGMA foreign_key_check;
|
||||
}
|
||||
|
||||
do_malloc_test fkey_malloc-2 -sqlprep {
|
||||
@ -128,5 +129,3 @@ do_malloc_test fkey_malloc-7 -sqlprep {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
83
test/ioerr6.test
Normal file
83
test/ioerr6.test
Normal file
@ -0,0 +1,83 @@
|
||||
# 2012 December 18
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix ioerr6
|
||||
|
||||
ifcapable !atomicwrite {
|
||||
puts "skipping tests - not compiled with SQLITE_ENABLE_ATOMIC_WRITE..."
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_test 1.1 {
|
||||
testvfs shmfault -default true
|
||||
shmfault devchar atomic
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(2, 4);
|
||||
INSERT INTO t1 VALUES(3, 6);
|
||||
INSERT INTO t1 VALUES(4, 8);
|
||||
}
|
||||
|
||||
# Cause the first call to xWrite() to fail with SQLITE_FULL.
|
||||
shmfault full 2 1
|
||||
catchsql { INSERT INTO t1 VALUES(5, 10) }
|
||||
} {1 {database or disk is full}}
|
||||
|
||||
do_test 1.2 {
|
||||
execsql { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
|
||||
db close
|
||||
shmfault delete
|
||||
|
||||
do_faultsim_test 2 -faults full* -prep {
|
||||
shmfault devchar atomic
|
||||
faultsim_restore
|
||||
sqlite3 db test.db
|
||||
} -body {
|
||||
db eval {
|
||||
CREATE TABLE t1(x PRIMARY KEY);
|
||||
INSERT INTO t1 VALUES('abc');
|
||||
}
|
||||
} -test {
|
||||
set res [db one { PRAGMA integrity_check }]
|
||||
if {$res != "ok"} {
|
||||
error "integrity check: $res"
|
||||
}
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -faults full* -prep {
|
||||
shmfault devchar atomic
|
||||
faultsim_restore
|
||||
sqlite3 db test.db
|
||||
} -body {
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(x);
|
||||
}
|
||||
} -test {
|
||||
db eval { CREATE TABLE t3(x) }
|
||||
if {[db one { PRAGMA integrity_check }] != "ok"} {
|
||||
error "integrity check failed"
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix minmax
|
||||
|
||||
do_test minmax-1.0 {
|
||||
execsql {
|
||||
@ -536,7 +537,96 @@ do_test minmax-12.17 {
|
||||
}
|
||||
} {5}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
proc do_test_13 {op name sql1 sql2 res} {
|
||||
set ::sqlite_search_count 0
|
||||
uplevel [list do_execsql_test $name.1 $sql1 $res]
|
||||
set a $::sqlite_search_count
|
||||
|
||||
set ::sqlite_search_count 0
|
||||
uplevel [list do_execsql_test $name.2 $sql2 $res]
|
||||
set b $::sqlite_search_count
|
||||
|
||||
uplevel [list do_test $name.3 [list expr "$a $op $b"] 1]
|
||||
}
|
||||
|
||||
# Run a test named $name. Check that SQL statements $sql1 and $sql2 both
|
||||
# return the same result, but that $sql2 increments the $sqlite_search_count
|
||||
# variable more often (indicating that it is visiting more rows to determine
|
||||
# the result).
|
||||
#
|
||||
proc do_test_13_opt {name sql1 sql2 res} {
|
||||
uplevel [list do_test_13 < $name $sql1 $sql2 $res]
|
||||
}
|
||||
|
||||
# Like [do_test_13_noopt], except this time check that the $sqlite_search_count
|
||||
# variable is incremented the same number of times by both SQL statements.
|
||||
#
|
||||
proc do_test_13_noopt {name sql1 sql2 res} {
|
||||
uplevel [list do_test_13 == $name $sql1 $sql2 $res]
|
||||
}
|
||||
|
||||
do_execsql_test 13.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES('a', 1, 1);
|
||||
INSERT INTO t1 VALUES('b', 6, 6);
|
||||
INSERT INTO t1 VALUES('c', 5, 5);
|
||||
INSERT INTO t1 VALUES('a', 4, 4);
|
||||
INSERT INTO t1 VALUES('a', 5, 5);
|
||||
INSERT INTO t1 VALUES('c', 6, 6);
|
||||
INSERT INTO t1 VALUES('b', 4, 4);
|
||||
INSERT INTO t1 VALUES('c', 7, 7);
|
||||
INSERT INTO t1 VALUES('b', 2, 2);
|
||||
INSERT INTO t1 VALUES('b', 3, 3);
|
||||
INSERT INTO t1 VALUES('a', 3, 3);
|
||||
INSERT INTO t1 VALUES('b', 5, 5);
|
||||
INSERT INTO t1 VALUES('c', 4, 4);
|
||||
INSERT INTO t1 VALUES('c', 3, 3);
|
||||
INSERT INTO t1 VALUES('a', 2, 2);
|
||||
SELECT * FROM t1 ORDER BY a, b, c;
|
||||
} {a 1 1 a 2 2 a 3 3 a 4 4 a 5 5
|
||||
b 2 2 b 3 3 b 4 4 b 5 5 b 6 6
|
||||
c 3 3 c 4 4 c 5 5 c 6 6 c 7 7
|
||||
}
|
||||
do_execsql_test 13.2 { CREATE INDEX i1 ON t1(a, b, c) }
|
||||
|
||||
do_test_13_opt 13.3 {
|
||||
SELECT min(b) FROM t1 WHERE a='b'
|
||||
} {
|
||||
SELECT min(c) FROM t1 WHERE a='b'
|
||||
} {2}
|
||||
|
||||
do_test_13_opt 13.4 {
|
||||
SELECT a, min(b) FROM t1 WHERE a='b'
|
||||
} {
|
||||
SELECT a, min(c) FROM t1 WHERE a='b'
|
||||
} {b 2}
|
||||
|
||||
do_test_13_opt 13.4 {
|
||||
SELECT a||c, max(b)+4 FROM t1 WHERE a='c'
|
||||
} {
|
||||
SELECT a||c, max(c)+4 FROM t1 WHERE a='c'
|
||||
} {c7 11}
|
||||
|
||||
do_test_13_noopt 13.5 {
|
||||
SELECT a||c, max(b+1) FROM t1 WHERE a='c'
|
||||
} {
|
||||
SELECT a||c, max(c+1) FROM t1 WHERE a='c'
|
||||
} {c7 8}
|
||||
|
||||
do_test_13_noopt 13.6 {
|
||||
SELECT count(b) FROM t1 WHERE a='c'
|
||||
} {
|
||||
SELECT count(c) FROM t1 WHERE a='c'
|
||||
} {5}
|
||||
|
||||
do_test_13_noopt 13.7 {
|
||||
SELECT min(b), count(b) FROM t1 WHERE a='a';
|
||||
} {
|
||||
SELECT min(c), count(c) FROM t1 WHERE a='a';
|
||||
} {1 5}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -534,12 +534,20 @@ do_test pragma-6.2.2 {
|
||||
b DEFAULT (5+3),
|
||||
c TEXT,
|
||||
d INTEGER DEFAULT NULL,
|
||||
e TEXT DEFAULT ''
|
||||
e TEXT DEFAULT '',
|
||||
UNIQUE(b,c,d),
|
||||
PRIMARY KEY(e,b,c)
|
||||
);
|
||||
PRAGMA table_info(t5);
|
||||
}
|
||||
} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0 2 c TEXT 0 <<NULL>> 0 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 0}
|
||||
} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 2 2 c TEXT 0 <<NULL>> 3 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 1}
|
||||
db nullvalue {}
|
||||
do_test pragma-6.2.3 {
|
||||
execsql {
|
||||
CREATE TABLE t2_3(a,b INTEGER PRIMARY KEY,c);
|
||||
pragma table_info(t2_3)
|
||||
}
|
||||
} {0 a {} 0 {} 0 1 b INTEGER 0 {} 1 2 c {} 0 {} 0}
|
||||
ifcapable {foreignkey} {
|
||||
do_test pragma-6.3.1 {
|
||||
execsql {
|
||||
@ -1619,5 +1627,3 @@ do_test 22.4.3 {
|
||||
} {ok}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
211
test/regexp1.test
Normal file
211
test/regexp1.test
Normal file
@ -0,0 +1,211 @@
|
||||
# 2012 December 31
|
||||
#
|
||||
# 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 test for the REGEXP operator in test_regexp.c.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test regexp1-1.1 {
|
||||
sqlite3_add_regexp_func db
|
||||
db eval {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
|
||||
INSERT INTO t1 VALUES(1, 'For since by man came death,');
|
||||
INSERT INTO t1 VALUES(2, 'by man came also the resurrection of the dead.');
|
||||
INSERT INTO t1 VALUES(3, 'For as in Adam all die,');
|
||||
INSERT INTO t1 VALUES(4, 'even so in Christ shall all be made alive.');
|
||||
|
||||
SELECT x FROM t1 WHERE y REGEXP '^For ' ORDER BY x;
|
||||
}
|
||||
} {1 3}
|
||||
|
||||
do_execsql_test regexp1-1.2 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'by|in' ORDER BY x;
|
||||
} {1 2 3 4}
|
||||
do_execsql_test regexp1-1.3 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'by|Christ' ORDER BY x;
|
||||
} {1 2 4}
|
||||
do_execsql_test regexp1-1.4 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'shal+ al+' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.5 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'shall x*y*z*all' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.6 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'shallx?y? ?z?all' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.7 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'r{2}' ORDER BY x;
|
||||
} {2}
|
||||
do_execsql_test regexp1-1.8 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'r{3}' ORDER BY x;
|
||||
} {}
|
||||
do_execsql_test regexp1-1.9 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'r{1}' ORDER BY x;
|
||||
} {1 2 3 4}
|
||||
do_execsql_test regexp1-1.10 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'ur{2,10}e' ORDER BY x;
|
||||
} {2}
|
||||
do_execsql_test regexp1-1.11 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '[Aa]dam' ORDER BY x;
|
||||
} {3}
|
||||
do_execsql_test regexp1-1.12 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '[^Aa]dam' ORDER BY x;
|
||||
} {}
|
||||
do_execsql_test regexp1-1.13 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '[^b-zB-Z]dam' ORDER BY x;
|
||||
} {3}
|
||||
do_execsql_test regexp1-1.14 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'alive' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.15 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '^alive' ORDER BY x;
|
||||
} {}
|
||||
do_execsql_test regexp1-1.16 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'alive$' ORDER BY x;
|
||||
} {}
|
||||
do_execsql_test regexp1-1.17 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'alive.$' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.18 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'alive\.$' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.19 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'ma[nd]' ORDER BY x;
|
||||
} {1 2 4}
|
||||
do_execsql_test regexp1-1.20 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '\bma[nd]' ORDER BY x;
|
||||
} {1 2 4}
|
||||
do_execsql_test regexp1-1.21 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'ma[nd]\b' ORDER BY x;
|
||||
} {1 2}
|
||||
do_execsql_test regexp1-1.22 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'ma\w' ORDER BY x;
|
||||
} {1 2 4}
|
||||
do_execsql_test regexp1-1.23 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'ma\W' ORDER BY x;
|
||||
} {}
|
||||
do_execsql_test regexp1-1.24 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '\sma\w' ORDER BY x;
|
||||
} {1 2 4}
|
||||
do_execsql_test regexp1-1.25 {
|
||||
SELECT x FROM t1 WHERE y REGEXP '\Sma\w' ORDER BY x;
|
||||
} {}
|
||||
do_execsql_test regexp1-1.26 {
|
||||
SELECT x FROM t1 WHERE y REGEXP 'alive\S$' ORDER BY x;
|
||||
} {4}
|
||||
do_execsql_test regexp1-1.27 {
|
||||
SELECT x FROM t1 WHERE y REGEXP
|
||||
'\b(unto|us|son|given|his|name|called|' ||
|
||||
'wonderful|councelor|mighty|god|everlasting|father|' ||
|
||||
'prince|peace|alive)\b';
|
||||
} {4}
|
||||
|
||||
do_execsql_test regexp1-2.1 {
|
||||
SELECT 'aaaabbbbcccc' REGEXP 'ab*c',
|
||||
'aaaacccc' REGEXP 'ab*c';
|
||||
} {1 1}
|
||||
do_execsql_test regexp1-2.2 {
|
||||
SELECT 'aaaabbbbcccc' REGEXP 'ab+c',
|
||||
'aaaacccc' REGEXP 'ab+c';
|
||||
} {1 0}
|
||||
do_execsql_test regexp1-2.3 {
|
||||
SELECT 'aaaabbbbcccc' REGEXP 'ab?c',
|
||||
'aaaacccc' REGEXP 'ab?c';
|
||||
} {0 1}
|
||||
do_execsql_test regexp1-2.4 {
|
||||
SELECT 'aaaabbbbbbcccc' REGEXP 'ab{3,5}c',
|
||||
'aaaabbbbbcccc' REGEXP 'ab{3,5}c',
|
||||
'aaaabbbbcccc' REGEXP 'ab{3,5}c',
|
||||
'aaaabbbcccc' REGEXP 'ab{3,5}c',
|
||||
'aaaabbcccc' REGEXP 'ab{3,5}c',
|
||||
'aaaabcccc' REGEXP 'ab{3,5}c'
|
||||
} {0 1 1 1 0 0}
|
||||
do_execsql_test regexp1-2.5 {
|
||||
SELECT 'aaaabbbbcccc' REGEXP 'a(a|b|c)+c',
|
||||
'aaaabbbbcccc' REGEXP '^a(a|b|c){11}c$',
|
||||
'aaaabbbbcccc' REGEXP '^a(a|b|c){10}c$',
|
||||
'aaaabbbbcccc' REGEXP '^a(a|b|c){9}c$'
|
||||
} {1 0 1 0}
|
||||
do_execsql_test regexp1-2.6 {
|
||||
SELECT 'aaaabbbbcccc' REGEXP '^a(a|bb|c)+c$',
|
||||
'aaaabbbbcccc' REGEXP '^a(a|bbb|c)+c$',
|
||||
'aaaabbbbcccc' REGEXP '^a(a|bbbb|c)+c$'
|
||||
} {1 0 1}
|
||||
do_execsql_test regexp1-2.7 {
|
||||
SELECT 'aaaabbbbcccc' REGEXP '^a([ac]+|bb){3}c$',
|
||||
'aaaabbbbcccc' REGEXP '^a([ac]+|bb){4}c$',
|
||||
'aaaabbbbcccc' REGEXP '^a([ac]+|bb){5}c$'
|
||||
} {0 1 1}
|
||||
|
||||
do_execsql_test regexp1-2.8 {
|
||||
SELECT 'abc*def+ghi.jkl[mno]pqr' REGEXP 'c.d',
|
||||
'abc*def+ghi.jkl[mno]pqr' REGEXP 'c\*d',
|
||||
'abc*def+ghi.jkl[mno]pqr' REGEXP 'f\+g',
|
||||
'abc*def+ghi.jkl[mno]pqr' REGEXP 'i\.j',
|
||||
'abc*def+ghi.jkl[mno]pqr' REGEXP 'l\[mno\]p'
|
||||
} {1 1 1 1 1}
|
||||
|
||||
do_test regexp1-2.9 {
|
||||
set v1 "abc\ndef"
|
||||
db eval {SELECT $v1 REGEXP '^abc\ndef$'}
|
||||
} {1}
|
||||
do_test regexp1-2.10 {
|
||||
set v1 "abc\adef"
|
||||
db eval {SELECT $v1 REGEXP '^abc\adef$'}
|
||||
} {1}
|
||||
do_test regexp1-2.11 {
|
||||
set v1 "abc\tdef"
|
||||
db eval {SELECT $v1 REGEXP '^abc\tdef$'}
|
||||
} {1}
|
||||
do_test regexp1-2.12 {
|
||||
set v1 "abc\rdef"
|
||||
db eval {SELECT $v1 REGEXP '^abc\rdef$'}
|
||||
} {1}
|
||||
do_test regexp1-2.13 {
|
||||
set v1 "abc\fdef"
|
||||
db eval {SELECT $v1 REGEXP '^abc\fdef$'}
|
||||
} {1}
|
||||
do_test regexp1-2.14 {
|
||||
set v1 "abc\vdef"
|
||||
db eval {SELECT $v1 REGEXP '^abc\vdef$'}
|
||||
} {1}
|
||||
do_execsql_test regexp1-2.15 {
|
||||
SELECT 'abc\def' REGEXP '^abc\\def',
|
||||
'abc(def' REGEXP '^abc\(def',
|
||||
'abc)def' REGEXP '^abc\)def',
|
||||
'abc*def' REGEXP '^abc\*def',
|
||||
'abc.def' REGEXP '^abc\.def',
|
||||
'abc+def' REGEXP '^abc\+def',
|
||||
'abc?def' REGEXP '^abc\?def',
|
||||
'abc[def' REGEXP '^abc\[def',
|
||||
'abc$def' REGEXP '^abc\$',
|
||||
'^def' REGEXP '\^def',
|
||||
'abc{4}x' REGEXP '^abc\{4\}x$',
|
||||
'abc|def' REGEXP '^abc\|def$'
|
||||
} {1 1 1 1 1 1 1 1 1 1 1 1}
|
||||
|
||||
do_execsql_test regexp1-2.20 {
|
||||
SELECT 'abc$¢€xyz' REGEXP '^abc\u0024\u00a2\u20acxyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc\u0024\u00A2\u20ACxyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc\x24\xa2\x20acxyz$'
|
||||
} {1 1 1}
|
||||
do_execsql_test regexp1-2.21 {
|
||||
SELECT 'abc$¢€xyz' REGEXP '^abc[\u0024][\u00a2][\u20ac]xyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc[\u0024\u00A2\u20AC]{3}xyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc[\x24][\xa2\x20ac]+xyz$'
|
||||
} {1 1 1}
|
||||
do_execsql_test regexp1-2.22 {
|
||||
SELECT 'abc$¢€xyz' REGEXP '^abc[^\u0025-X][^ -\u007f][^\u20ab]xyz$'
|
||||
} {1}
|
||||
|
||||
finish_test
|
@ -81,6 +81,22 @@ array set ::Configs {
|
||||
-DSQLITE_DEFAULT_FILE_FORMAT=4
|
||||
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
|
||||
}
|
||||
"Check-Symbols" {
|
||||
-DSQLITE_MEMDEBUG=1
|
||||
-DSQLITE_ENABLE_FTS3_PARENTHESIS=1
|
||||
-DSQLITE_ENABLE_FTS3=1
|
||||
-DSQLITE_ENABLE_RTREE=1
|
||||
-DSQLITE_ENABLE_MEMSYS5=1
|
||||
-DSQLITE_ENABLE_MEMSYS3=1
|
||||
-DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
|
||||
-DSQLITE_SECURE_DELETE=1
|
||||
-DSQLITE_SOUNDEX=1
|
||||
-DSQLITE_ENABLE_ATOMIC_WRITE=1
|
||||
-DSQLITE_ENABLE_IOTRACE=1
|
||||
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
|
||||
-DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
|
||||
}
|
||||
"Debug-One" {
|
||||
-O2
|
||||
-DSQLITE_DEBUG=1
|
||||
@ -164,7 +180,8 @@ array set ::Configs {
|
||||
|
||||
array set ::Platforms {
|
||||
Linux-x86_64 {
|
||||
"Debug-One" "checksymbols test"
|
||||
"Check-Symbols" checksymbols
|
||||
"Debug-One" test
|
||||
"Secure-Delete" test
|
||||
"Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test"
|
||||
"Update-Delete-Limit" test
|
||||
@ -330,15 +347,17 @@ proc main {argv} {
|
||||
# If the configuration included the SQLITE_DEBUG option, then remove
|
||||
# it and run veryquick.test. If it did not include the SQLITE_DEBUG option
|
||||
# add it and run veryquick.test.
|
||||
set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*]
|
||||
if {$debug_idx < 0} {
|
||||
run_test_suite "${zConfig}_debug" test [
|
||||
concat $config_options -DSQLITE_DEBUG=1
|
||||
]
|
||||
} else {
|
||||
run_test_suite "${zConfig}_ndebug" test [
|
||||
lreplace $config_options $debug_idx $debug_idx
|
||||
]
|
||||
if {$target!="checksymbols"} {
|
||||
set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*]
|
||||
if {$debug_idx < 0} {
|
||||
run_test_suite "${zConfig}_debug" test [
|
||||
concat $config_options -DSQLITE_DEBUG=1
|
||||
]
|
||||
} else {
|
||||
run_test_suite "${zConfig}_ndebug" test [
|
||||
lreplace $config_options $debug_idx $debug_idx
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
155
test/selectD.test
Normal file
155
test/selectD.test
Normal file
@ -0,0 +1,155 @@
|
||||
# 2012 December 19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for name resolution in SELECT
|
||||
# statements that have parenthesized FROM clauses.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
for {set i 1} {$i<=2} {incr i} {
|
||||
db close
|
||||
forcedelete test$i.db
|
||||
sqlite3 db test$i.db
|
||||
if {$i==2} {
|
||||
optimization_control db query-flattener off
|
||||
}
|
||||
do_test selectD-$i.0 {
|
||||
db eval {
|
||||
ATTACH ':memory:' AS aux1;
|
||||
CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(111,'x1');
|
||||
CREATE TABLE t2(a,b); INSERT INTO t2 VALUES(222,'x2');
|
||||
CREATE TEMP TABLE t3(a,b); INSERT INTO t3 VALUES(333,'x3');
|
||||
CREATE TABLE main.t4(a,b); INSERT INTO main.t4 VALUES(444,'x4');
|
||||
CREATE TABLE aux1.t4(a,b); INSERT INTO aux1.t4 VALUES(555,'x5');
|
||||
}
|
||||
} {}
|
||||
do_test selectD-$i.1 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM (t1), (t2), (t3), (t4)
|
||||
WHERE t4.a=t3.a+111
|
||||
AND t3.a=t2.a+111
|
||||
AND t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 333 x3 444 x4}
|
||||
do_test selectD-$i.2.1 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 333 x3 444 x4}
|
||||
do_test selectD-$i.2.2 {
|
||||
db eval {
|
||||
SELECT t3.a
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {333}
|
||||
do_test selectD-$i.2.3 {
|
||||
db eval {
|
||||
SELECT t3.*
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {333 x3}
|
||||
do_test selectD-$i.2.3 {
|
||||
db eval {
|
||||
SELECT t3.*, t2.*
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 ON t4.a=t3.a+111)
|
||||
ON t3.a=t2.a+111)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {333 x3 222 x2}
|
||||
do_test selectD-$i.2.4 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (main.t4 JOIN aux1.t4 ON aux1.t4.a=main.t4.a+111)
|
||||
ON main.t4.a=t2.a+222)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 444 x4 555 x5}
|
||||
do_test selectD-$i.2.5 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (main.t4 AS x JOIN aux1.t4 ON aux1.t4.a=x.a+111)
|
||||
ON x.a=t2.a+222)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {111 x1 222 x2 444 x4 555 x5}
|
||||
do_test selectD-$i.2.6 {
|
||||
catchsql {
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (main.t4 JOIN aux.t4 ON aux.t4.a=main.t4.a+111)
|
||||
ON main.t4.a=t2.a+222)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {1 {no such table: aux.t4}}
|
||||
do_test selectD-$i.2.7 {
|
||||
db eval {
|
||||
SELECT x.a, y.b
|
||||
FROM t1 JOIN (t2 JOIN (main.t4 x JOIN aux1.t4 y ON y.a=x.a+111)
|
||||
ON x.a=t2.a+222)
|
||||
ON t2.a=t1.a+111;
|
||||
}
|
||||
} {444 x5}
|
||||
do_test selectD-$i.3 {
|
||||
db eval {
|
||||
UPDATE t2 SET a=111;
|
||||
UPDATE t3 SET a=111;
|
||||
UPDATE t4 SET a=111;
|
||||
SELECT *
|
||||
FROM t1 JOIN (t2 JOIN (t3 JOIN t4 USING(a)) USING (a)) USING (a);
|
||||
}
|
||||
} {111 x1 x2 x3 x4}
|
||||
do_test selectD-$i.4 {
|
||||
db eval {
|
||||
UPDATE t2 SET a=111;
|
||||
UPDATE t3 SET a=111;
|
||||
UPDATE t4 SET a=111;
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
USING (a))
|
||||
USING (a);
|
||||
}
|
||||
} {111 x1 x2 x3 x4}
|
||||
do_test selectD-$i.5 {
|
||||
db eval {
|
||||
UPDATE t3 SET a=222;
|
||||
UPDATE t4 SET a=222;
|
||||
SELECT *
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 x2 222 x3 x4}
|
||||
do_test selectD-$i.6 {
|
||||
db eval {
|
||||
UPDATE t4 SET a=333;
|
||||
SELECT *
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 x2 222 x3 {}}
|
||||
do_test selectD-$i.7 {
|
||||
db eval {
|
||||
SELECT t1.*, t2.*, t3.*, t4.b
|
||||
FROM (t1 LEFT JOIN t2 USING(a)) JOIN (t3 LEFT JOIN t4 USING(a))
|
||||
ON t1.a=t3.a-111;
|
||||
}
|
||||
} {111 x1 111 x2 222 x3 {}}
|
||||
}
|
||||
|
||||
finish_test
|
84
test/tkt-a7b7803e.test
Normal file
84
test/tkt-a7b7803e.test
Normal file
@ -0,0 +1,84 @@
|
||||
# 2012 December 19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. Specifically,
|
||||
# it tests that ticket [a7b7803e8d1e8699cd8a460a38133b98892d2e17] has
|
||||
# been fixed.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
do_test tkt-a7b7803e.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(0,'first'),(99,'fuzzy');
|
||||
SELECT (t1.a==0) AS x, b
|
||||
FROM t1
|
||||
WHERE a=0 OR x;
|
||||
}
|
||||
} {1 first}
|
||||
do_test tkt-a7b7803e.2 {
|
||||
db eval {
|
||||
SELECT a, (t1.b='fuzzy') AS x
|
||||
FROM t1
|
||||
WHERE x
|
||||
}
|
||||
} {99 1}
|
||||
do_test tkt-a7b7803e.3 {
|
||||
db eval {
|
||||
SELECT (a=99) AS x, (t1.b='fuzzy') AS y, *
|
||||
FROM t1
|
||||
WHERE x AND y
|
||||
}
|
||||
} {1 1 99 fuzzy}
|
||||
do_test tkt-a7b7803e.4 {
|
||||
db eval {
|
||||
SELECT (a=99) AS x, (t1.b='first') AS y, *
|
||||
FROM t1
|
||||
WHERE x OR y
|
||||
ORDER BY a
|
||||
}
|
||||
} {0 1 0 first 1 0 99 fuzzy}
|
||||
do_test tkt-a7b7803e.5 {
|
||||
db eval {
|
||||
SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b
|
||||
FROM t1 M, t1 N
|
||||
WHERE x OR y
|
||||
ORDER BY M.a, N.a
|
||||
}
|
||||
} {0 first 1 first 1 fuzzy 1 first 1 fuzzy 0 fuzzy}
|
||||
do_test tkt-a7b7803e.6 {
|
||||
db eval {
|
||||
SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b
|
||||
FROM t1 M, t1 N
|
||||
WHERE x AND y
|
||||
ORDER BY M.a, N.a
|
||||
}
|
||||
} {1 fuzzy 1 first}
|
||||
do_test tkt-a7b7803e.7 {
|
||||
db eval {
|
||||
SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b
|
||||
FROM t1 M JOIN t1 N ON x AND y
|
||||
ORDER BY M.a, N.a
|
||||
}
|
||||
} {1 fuzzy 1 first}
|
||||
do_test tkt-a7b7803e.8 {
|
||||
db eval {
|
||||
SELECT (M.a=99) AS x, M.b, (N.b='first') AS y, N.b
|
||||
FROM t1 M JOIN t1 N ON x
|
||||
ORDER BY M.a, N.a
|
||||
}
|
||||
} {1 fuzzy 1 first 1 fuzzy 0 fuzzy}
|
||||
|
||||
|
||||
finish_test
|
@ -1091,12 +1091,54 @@ do_test vtab1.13-3 {
|
||||
} {15 {} 16}
|
||||
|
||||
|
||||
do_test vtab1-14.001 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE +rowid IN (1,2,3)}
|
||||
} {1 3 G H 2 {} 15 16 3 15 {} 16}
|
||||
do_test vtab1-14.002 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE rowid IN (1,2,3)}
|
||||
} {1 3 G H 2 {} 15 16 3 15 {} 16}
|
||||
do_test vtab1-14.003 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE +rowid IN (0,1,5,2,'a',3,NULL)}
|
||||
} {1 3 G H 2 {} 15 16 3 15 {} 16}
|
||||
do_test vtab1-14.004 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE rowid IN (0,1,5,'a',2,3,NULL)}
|
||||
} {1 3 G H 2 {} 15 16 3 15 {} 16}
|
||||
do_test vtab1-14.005 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,1,5,'a',2,3)}
|
||||
} {}
|
||||
do_test vtab1-14.006 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,5,'a',2,3)}
|
||||
} {1 3 G H}
|
||||
do_test vtab1-14.007 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE +rowid NOT IN (0,5,'a',2,3,NULL)}
|
||||
} {}
|
||||
do_test vtab1-14.008 {
|
||||
execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,5,'a',2,3,NULL)}
|
||||
} {}
|
||||
do_test vtab1-14.011 {
|
||||
execsql {SELECT * FROM echo_c WHERE +a IN (1,3,8,'x',NULL,15,24)}
|
||||
} {3 G H 15 {} 16}
|
||||
do_test vtab1-14.012 {
|
||||
execsql {SELECT * FROM echo_c WHERE a IN (1,3,8,'x',NULL,15,24)}
|
||||
} {3 G H 15 {} 16}
|
||||
do_test vtab1-14.013 {
|
||||
execsql {SELECT * FROM echo_c WHERE a NOT IN (1,8,'x',15,24)}
|
||||
} {3 G H}
|
||||
do_test vtab1-14.014 {
|
||||
execsql {SELECT * FROM echo_c WHERE a NOT IN (1,8,'x',NULL,15,24)}
|
||||
} {}
|
||||
do_test vtab1-14.015 {
|
||||
execsql {SELECT * FROM echo_c WHERE +a NOT IN (1,8,'x',NULL,15,24)}
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
do_test vtab1-14.1 {
|
||||
execsql { DELETE FROM c }
|
||||
set echo_module ""
|
||||
execsql { SELECT * FROM echo_c WHERE rowid IN (1, 2, 3) }
|
||||
set echo_module
|
||||
} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}]
|
||||
} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c' WHERE rowid = .} 1/}
|
||||
|
||||
do_test vtab1-14.2 {
|
||||
set echo_module ""
|
||||
@ -1114,7 +1156,7 @@ do_test vtab1-14.4 {
|
||||
set echo_module ""
|
||||
execsql { SELECT * FROM echo_c WHERE a IN (1, 2) }
|
||||
set echo_module
|
||||
} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}]
|
||||
} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE a = .} xFilter {SELECT rowid, . FROM 'c' WHERE a = .} 1/}
|
||||
|
||||
do_test vtab1-15.1 {
|
||||
execsql {
|
||||
|
@ -290,6 +290,20 @@ do_test where8-3.15 {
|
||||
}
|
||||
} {I I I I I I I I I I II II II II II II II II II II III III III III III 9 1}
|
||||
|
||||
|
||||
do_test where8-3.21 {
|
||||
execsql_status {
|
||||
SELECT a, d FROM t1, (t2) WHERE (a=d OR b=e) AND a<5 ORDER BY a
|
||||
}
|
||||
} {1 1 2 2 3 3 4 2 4 4 0 0}
|
||||
do_test where8-3.22 {
|
||||
execsql_status {
|
||||
SELECT a, d FROM ((((((t1))), (((t2))))))
|
||||
WHERE (a=d OR b=e) AND a<5 ORDER BY a
|
||||
}
|
||||
} {1 1 2 2 3 3 4 2 4 4 0 0}
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# The following tests - where8-4.* - verify that adding or removing
|
||||
# indexes does not change the results returned by various queries.
|
||||
|
@ -232,7 +232,7 @@ do_test where9-1.3.3 {
|
||||
} {90 91 92 97 scan 98 sort 0}
|
||||
do_test where9-1.3.4 {
|
||||
count_steps {
|
||||
SELECT a FROM t4
|
||||
SELECT a FROM (t4)
|
||||
WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
|
||||
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
|
||||
OR (b NOT NULL AND c IS NULL AND d NOT NULL)
|
||||
@ -876,5 +876,21 @@ do_test where9-8.1 {
|
||||
ORDER BY +a;
|
||||
}
|
||||
} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55}
|
||||
do_test where9-8.2 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM t81 LEFT JOIN (t82) ON y=b JOIN t83
|
||||
WHERE c==p OR d==p
|
||||
ORDER BY +a;
|
||||
}
|
||||
} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55}
|
||||
do_test where9-8.3 {
|
||||
db eval {
|
||||
SELECT *
|
||||
FROM (t81) LEFT JOIN (main.t82) ON y=b JOIN t83
|
||||
WHERE c==p OR d==p
|
||||
ORDER BY +a;
|
||||
}
|
||||
} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55}
|
||||
|
||||
finish_test
|
||||
|
@ -15,7 +15,9 @@ gcc -o sqlite3 -g -Os -I. \
|
||||
-DSQLITE_ENABLE_STAT3 \
|
||||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_REGEXP \
|
||||
-DHAVE_READLINE \
|
||||
-DHAVE_USLEEP=1 \
|
||||
../sqlite/src/shell.c ../sqlite/src/test_vfstrace.c \
|
||||
../sqlite/src/test_regexp.c \
|
||||
sqlite3.c -ldl -lreadline -lncurses
|
||||
|
Loading…
Reference in New Issue
Block a user