diff --git a/Makefile.in b/Makefile.in
index 6ab49dbe9a..9bc19e658c 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1267,7 +1267,7 @@ fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuz
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
- valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
+ valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
# The veryquick.test TCL tests.
@@ -1378,6 +1378,9 @@ wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.c
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/test/speedtest1.c sqlite3.c $(TLIBS)
+startup$(TEXE): $(TOP)/test/startup.c sqlite3.c
+ $(CC) -Os -g -DSQLITE_THREADSAFE=0 -o $@ $(TOP)/test/startup.c sqlite3.c $(TLIBS)
+
KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ
kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c
diff --git a/configure b/configure
index b094ef71ad..1dbef10575 100755
--- a/configure
+++ b/configure
@@ -10366,8 +10366,10 @@ fi
if test "x${TCLLIBDIR+set}" != "xset" ; then
TCLLIBDIR='$(libdir)'
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
- TCLLIBDIR=$i
- break
+ if test -d $i ; then
+ TCLLIBDIR=$i
+ break
+ fi
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
diff --git a/configure.ac b/configure.ac
index ae73a5350e..8a58d3dd33 100644
--- a/configure.ac
+++ b/configure.ac
@@ -134,8 +134,10 @@ AC_ARG_VAR([TCLLIBDIR], [Where to install tcl plugin])
if test "x${TCLLIBDIR+set}" != "xset" ; then
TCLLIBDIR='$(libdir)'
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
- TCLLIBDIR=$i
- break
+ if test -d $i ; then
+ TCLLIBDIR=$i
+ break
+ fi
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
diff --git a/doc/lemon.html b/doc/lemon.html
index bd078c8ec7..457004627e 100644
--- a/doc/lemon.html
+++ b/doc/lemon.html
@@ -1077,7 +1077,7 @@ can choose a different start symbol using the
4.4.19 The %syntax_error directive
-See Error Processing.
+See Error Processing.
4.4.20 The %token_class directive
@@ -1176,7 +1176,7 @@ match any input token.
the wildcard token and some other token, the other token is always used.
The wildcard token is only matched if there are no alternatives.
-
+
5.0 Error Processing
After extensive experimentation over several years, it has been
diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c
index 092cad9ac5..bc42fc3d63 100644
--- a/ext/fts3/fts3_write.c
+++ b/ext/fts3/fts3_write.c
@@ -4335,17 +4335,20 @@ static int fts3IncrmergeLoad(
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
if( rc==SQLITE_OK ){
- memcpy(pNode->key.a, reader.term.a, reader.term.n);
+ assert_fts3_nc( reader.term.n>0 || reader.aNode==0 );
+ if( reader.term.n>0 ){
+ memcpy(pNode->key.a, reader.term.a, reader.term.n);
+ }
pNode->key.n = reader.term.n;
if( i>0 ){
char *aBlock = 0;
int nBlock = 0;
pNode = &pWriter->aNodeWriter[i-1];
pNode->iBlock = reader.iChild;
- rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
+ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
- );
+ );
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c
index 392dde3ab4..088d8b8cae 100644
--- a/ext/fts5/fts5_expr.c
+++ b/ext/fts5/fts5_expr.c
@@ -1500,8 +1500,8 @@ int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
}
/* If the iterator is not at a real match, skip forward until it is. */
- while( pRoot->bNomatch ){
- assert( pRoot->bEof==0 && rc==SQLITE_OK );
+ while( pRoot->bNomatch && rc==SQLITE_OK ){
+ assert( pRoot->bEof==0 );
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
}
return rc;
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
index 2daa07c51e..c01d5d9de6 100644
--- a/ext/fts5/fts5_index.c
+++ b/ext/fts5/fts5_index.c
@@ -2070,14 +2070,10 @@ static void fts5SegIterNext(
}else{
/* The following could be done by calling fts5SegIterLoadNPos(). But
** this block is particularly performance critical, so equivalent
- ** code is inlined.
- **
- ** Later: Switched back to fts5SegIterLoadNPos() because it supports
- ** detail=none mode. Not ideal.
- */
+ ** code is inlined. */
int nSz;
assert( p->rc==SQLITE_OK );
- assert( pIter->iLeafOffset<=pIter->pLeaf->nn );
+ assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn );
fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
pIter->bDel = (nSz & 0x0001);
pIter->nPos = nSz>>1;
diff --git a/ext/fts5/test/fts5corrupt3.test b/ext/fts5/test/fts5corrupt3.test
index 4223d3c240..6fae40ecb9 100644
--- a/ext/fts5/test/fts5corrupt3.test
+++ b/ext/fts5/test/fts5corrupt3.test
@@ -10509,6 +10509,260 @@ do_catchsql_test 71.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
} {1 {database disk image is malformed}}
+#-------------------------------------------------------------------------
+reset_db
+do_test 72.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 24576 pagesize 4096 filename crash-77b86d070d0ac6.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
+| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................
+| 96: 00 00 00 00 0d 00 00 00 06 0d e2 00 0f c4 0f 6a ...............j
+| 112: 0e fc 0e 9d 0e 3d 0d e2 00 00 00 00 00 00 00 00 .....=..........
+| 3552: 00 00 59 06 06 17 21 21 01 7f 74 61 62 6c 65 74 ..Y...!!..tablet
+| 3568: 74 74 5f 63 6f 6e 66 69 67 74 74 74 5f 63 6f 6e tt_configttt_con
+| 3584: 66 69 67 06 43 52 45 41 54 45 20 54 41 42 4c 45 fig.CREATE TABLE
+| 3600: 20 27 74 74 74 5f 63 6f 6e 66 69 67 27 28 6b 20 'ttt_config'(k
+| 3616: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 PRIMARY KEY, v)
+| 3632: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5e 05 07 WITHOUT ROWID^..
+| 3648: 17 23 23 01 81 03 74 61 62 6c 65 74 74 74 5f 64 .##...tablettt_d
+| 3664: 6f 63 73 69 7a 65 74 74 74 5f 64 6f 63 73 69 7a ocsizettt_docsiz
+| 3680: 65 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 e.CREATE TABLE '
+| 3696: 74 74 74 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 ttt_docsize'(id
+| 3712: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3728: 4b 45 59 2c 20 73 7a 20 42 4c 4f 42 29 5d 04 07 KEY, sz BLOB)]..
+| 3744: 17 23 23 01 81 01 74 61 62 6c 65 74 74 74 5f 63 .##...tablettt_c
+| 3760: 6f 6e 74 65 6e 74 74 74 74 5f 63 6f 6e 74 65 6e ontentttt_conten
+| 3776: 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 t.CREATE TABLE '
+| 3792: 74 74 74 5f 63 6f 6e 74 65 6e 74 27 28 69 64 20 ttt_content'(id
+| 3808: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3824: 4b 45 59 2c 20 63 30 2c 20 63 31 29 6c 03 07 17 KEY, c0, c1)l...
+| 3840: 1b 1b 01 81 2f 74 61 62 6c 65 74 74 74 5f 69 64 ..../tablettt_id
+| 3856: 78 74 74 74 5f 69 64 78 03 43 52 45 41 54 45 20 xttt_idx.CREATE
+| 3872: 54 41 42 4c 45 20 27 74 74 74 5f 69 64 78 27 28 TABLE 'ttt_idx'(
+| 3888: 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e segid, term, pgn
+| 3904: 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 28 73 o, PRIMARY KEY(s
+| 3920: 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 49 54 egid, term)) WIT
+| 3936: 48 4f 55 54 20 52 4f 57 49 44 58 02 07 17 1d 1d HOUT ROWIDX.....
+| 3952: 01 81 03 74 61 62 6c 65 74 74 74 5f 64 61 74 61 ...tablettt_data
+| 3968: 74 74 74 5f 64 61 74 61 02 43 52 45 41 54 45 20 ttt_data.CREATE
+| 3984: 54 41 42 4c 45 20 27 74 74 74 5f 64 61 74 61 27 TABLE 'ttt_data'
+| 4000: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM
+| 4016: 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARY KEY, block B
+| 4032: 4c 4f 42 29 3a 01 06 17 13 13 08 5f 74 61 62 6c LOB):......_tabl
+| 4048: 65 74 74 74 74 74 74 43 52 45 41 54 45 20 56 49 ettttttCREATE VI
+| 4064: 52 54 55 41 4c 20 54 41 42 4c 45 20 74 74 74 20 RTUAL TABLE ttt
+| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
+| page 2 offset 4096
+| 0: 0d 0f 44 00 05 0e 81 00 0f e7 0e 81 0f af 0f 58 ..D............X
+| 16: 0e 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3712: 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 03 01 .....0..........
+| 3728: 01 01 02 01 01 03 01 01 81 24 8c 80 80 80 80 01 .........$......
+| 3744: 04 00 82 4c 00 00 00 9b 02 30 65 03 1a 02 05 05 ...L.....0e.....
+| 3760: 07 05 01 01 04 03 03 08 03 03 01 2e 02 05 05 07 ................
+| 3776: 05 07 05 07 05 01 01 04 03 03 08 03 03 08 03 03 ................
+| 3792: 08 03 03 02 01 65 03 1e 03 05 05 04 05 05 01 01 .....e..........
+| 3808: 03 06 04 04 06 04 03 01 36 03 05 05 04 05 05 04 ........6.......
+| 3824: 05 05 04 05 05 01 01 03 06 04 04 06 04 04 06 04 ................
+| 3840: 04 06 04 03 03 01 65 03 14 04 05 07 05 05 01 01 ......e.........
+| 3856: 02 08 0a 01 20 04 05 07 05 07 05 07 05 05 01 01 .... ...........
+| 3872: 02 08 0a 09 fa 04 01 65 03 02 0a 01 06 0a 1a 0a .......e........
+| 3888: 05 01 65 03 06 01 01 0a 01 0a 01 01 0a 0a 0a 04 ..e.............
+| 3904: 2b 31 21 0b 0f ef 00 14 2a 00 00 00 00 01 02 02 +1!.....*.......
+| 3920: 00 02 01 01 01 02 01 01 50 88 80 80 80 80 01 04 ........P.......
+| 3936: 00 81 24 00 00 00 47 02 30 65 02 1a 02 05 05 07 ..$...G.0e......
+| 3952: 05 01 01 04 03 03 08 03 03 02 01 65 02 1e 03 05 ...........e....
+| 3968: 05 04 05 05 01 01 03 06 04 04 06 04 03 03 01 65 ...............e
+| 3984: 02 14 04 05 07 05 05 01 01 02 08 0a 04 01 65 02 ..............e.
+| 4000: 02 0a 05 01 65 02 06 01 01 0a 04 12 14 0f 06 31 ....e..........1
+| 4016: 84 80 80 80 80 01 03 00 68 00 00 00 2b 02 30 65 ........h...+.0e
+| 4032: 01 10 02 05 05 01 01 04 03 9f 02 01 65 01 12 03 ............e...
+| 4048: 05 05 01 01 03 06 04 03 03 01 65 01 0e 14 05 05 ..........e.....
+| 4064: 01 01 02 08 04 0d 0e 06 01 03 00 12 04 4c 4c 00 .............LL.
+| 4080: 00 00 11 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
+| page 3 offset 8192
+| 0: 0a 00 00 00 03 0f 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 06 04 01 0c ................
+| 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0c 01 02 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3600: 00 00 00 00 00 00 00 00 00 00 81 52 04 06 00 81 ...........R....
+| 3616: 5d 81 55 65 20 65 65 20 65 65 65 20 65 20 65 65 ].Ue ee eee e ee
+| 3632: 20 65 65 65 20 65 20 65 65 20 65 65 65 65 20 65 eee e ee eeee e
+| 3648: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 3664: 20 65 65 20 65 65 55 65 20 65 65 20 65 65 65 20 ee eeUe ee eee
+| 3680: 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 e ee eee e ee ee
+| 3696: 65 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 ee ee eee e ee e
+| 3712: 65 65 20 65 20 65 65 20 65 65 65 65 65 65 20 65 ee e ee eeeeee e
+| 3728: 65 20 65 20 65 20 65 20 65 65 20 65 85 65 20 65 e e e e ee e.e e
+| 3744: 65 20 65 65 65 65 65 20 65 65 20 65 20 65 20 65 e eeeee ee e e e
+| 3760: 20 65 65 20 65 65 65 20 65 65 20 65 65 65 65 65 ee eee ee eeeee
+| 3776: 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 65 ee e e e ee eee
+| 3792: 20 65 65 20 65 65 65 65 65 20 65 65 20 65 20 65 ee eeeee ee e e
+| 3808: 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 6a e ee eee ee eej
+| 3824: 03 04 00 75 71 65 20 65 65 20 65 65 65 20 65 20 ...uqe ee eee e
+| 3840: 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 65 ee eee e ee eeee
+| 3856: 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 ee eee e ee eee
+| 3872: 20 65 20 65 65 20 65 65 65 65 65 65 20 65 65 20 e ee eeeeee ee
+| 3888: 65 20 65 20 65 20 65 64 20 65 65 65 20 65 65 20 e e e ed eee ee
+| 3904: 65 65 65 65 65 20 65 65 20 65 20 65 20 65 10 65 eeeee ee e e e.e
+| 3920: 65 20 65 65 65 10 65 65 20 65 65 6a 02 04 00 75 e eee.ee eej...u
+| 3936: 71 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 qe ee eee e ee e
+| 3952: 65 65 20 65 20 65 65 20 65 65 65 65 20 65 65 20 ee e ee eeee ee
+| 3968: 65 65 65 20 65 20 65 65 20 65 65 65 20 65 20 65 eee e ee eee e e
+| 3984: 65 20 65 65 65 66 65 65 20 65 65 20 65 20 65 20 e eeefee ee e e
+| 4000: 65 88 65 65 20 65 65 65 30 65 65 20 65 65 65 65 e.ee eee0ee eeee
+| 4016: 65 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 e ee e e e ee ee
+| 4032: 65 20 65 65 20 65 65 37 01 04 00 41 3f 65 20 65 e ee ee7...A?e e
+| 4048: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 4064: 20 65 65 20 65 65 65 65 65 65 20 65 65 20 65 20 ee eeeeee ee e
+| 4080: 65 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 e e ee eee ee ee
+| page 5 offset 16384
+| 0: 0d 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 05 04 03 00 10 21 21 05 03 03 00 10 .........!!.....
+| 4080: 11 11 05 02 03 00 10 11 11 05 01 03 00 10 09 09 ................
+| page 6 offset 20480
+| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
+| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
+| end crash-77b86d070d0ac6.db
+}]} {}
+
+do_catchsql_test 72.1 {
+ INSERT INTO ttt(ttt) VALUES('integrity-check');
+} {1 {database disk image is malformed}}
+
+do_catchsql_test 72.1 {
+ SELECT 1 FROM ttt('e* NOT ee*');
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+do_test 73.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 24576 pagesize 4096 filename crash-b02ca2cc4d7dda.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
+| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................
+| 96: 00 00 00 00 0d 00 00 00 06 0d e2 00 0f c4 0f 6a ...............j
+| 112: 0e fc 0e 9d 0e 3d 0d e2 00 00 00 00 00 00 00 00 .....=..........
+| 3552: 00 00 59 06 06 17 21 21 01 7f 74 61 62 6c 65 74 ..Y...!!..tablet
+| 3568: 74 74 5f 63 6f 6e 66 69 67 74 74 74 5f 63 6f 6e tt_configttt_con
+| 3584: 66 69 67 06 43 52 45 41 54 45 20 54 41 42 4c 45 fig.CREATE TABLE
+| 3600: 20 27 74 74 74 5f 63 6f 6e 66 69 67 27 28 6b 20 'ttt_config'(k
+| 3616: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 20 PRIMARY KEY, v)
+| 3632: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5e 05 07 WITHOUT ROWID^..
+| 3648: 17 23 23 01 81 03 74 61 62 6c 65 74 74 74 5f 64 .##...tablettt_d
+| 3664: 6f 63 73 69 7a 65 74 74 74 5f 64 6f 63 73 69 7a ocsizettt_docsiz
+| 3680: 65 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 e.CREATE TABLE '
+| 3696: 74 74 74 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 ttt_docsize'(id
+| 3712: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3728: 4b 45 59 2c 20 73 7a 20 42 4c 4f 42 29 5d 04 07 KEY, sz BLOB)]..
+| 3744: 17 23 23 01 81 01 74 61 62 6c 65 74 74 74 5f 63 .##...tablettt_c
+| 3760: 6f 6e 74 65 6e 74 74 74 74 5f 63 6f 6e 74 65 6e ontentttt_conten
+| 3776: 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 t.CREATE TABLE '
+| 3792: 74 74 74 5f 63 6f 6e 74 65 6e 74 27 28 69 64 20 ttt_content'(id
+| 3808: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
+| 3824: 4b 45 59 2c 20 63 30 2c 20 63 31 29 6c 03 07 17 KEY, c0, c1)l...
+| 3840: 1b 1b 01 81 2f 74 61 62 6c 65 74 74 74 5f 69 64 ..../tablettt_id
+| 3856: 78 74 74 74 5f 69 64 78 03 43 52 45 41 54 45 20 xttt_idx.CREATE
+| 3872: 54 41 42 4c 45 20 27 74 74 74 5f 69 64 78 27 28 TABLE 'ttt_idx'(
+| 3888: 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e segid, term, pgn
+| 3904: 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 28 73 o, PRIMARY KEY(s
+| 3920: 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 49 54 egid, term)) WIT
+| 3936: 48 4f 55 54 20 52 4f 57 49 44 58 02 07 17 1d 1d HOUT ROWIDX.....
+| 3952: 01 81 03 74 61 62 6c 65 74 74 74 5f 64 61 74 61 ...tablettt_data
+| 3968: 74 74 74 5f 64 61 74 61 02 43 52 45 41 54 45 20 ttt_data.CREATE
+| 3984: 54 41 42 4c 45 20 27 74 74 74 5f 64 61 74 61 27 TABLE 'ttt_data'
+| 4000: 28 69 65 20 49 4e 54 45 47 45 52 20 50 52 49 4d (ie INTEGER PRIM
+| 4016: 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARY KEY, block B
+| 4032: 4c 4f 42 29 3a 01 06 17 13 13 08 5f 74 61 62 6c LOB):......_tabl
+| 4048: 65 74 74 74 74 74 74 43 52 45 41 54 45 20 56 49 ettttttCREATE VI
+| 4064: 52 54 55 41 4c 20 54 41 42 4c 45 20 74 74 74 20 RTUAL TABLE ttt
+| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
+| page 2 offset 4096
+| 0: 0d 0f 44 00 05 0e 81 00 0f e7 0e 81 0f af 0f 58 ..D............X
+| 16: 0e 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3712: 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 03 01 .....0..........
+| 3728: 01 01 02 01 01 03 01 01 81 24 8c 80 80 80 80 01 .........$......
+| 3744: 04 00 82 4c 00 00 00 9b 02 30 65 03 1a 02 05 05 ...L.....0e.....
+| 3760: 07 05 01 01 04 03 03 08 03 03 01 2e 02 05 05 07 ................
+| 3776: 05 07 05 07 05 01 01 04 03 03 08 03 03 08 03 03 ................
+| 3792: 08 03 03 02 01 65 03 1e 03 05 05 04 05 05 01 01 .....e..........
+| 3808: 03 06 04 04 06 04 03 01 36 03 05 05 04 05 05 04 ........6.......
+| 3824: 05 04 f4 04 05 01 01 03 06 04 04 06 04 04 06 04 ................
+| 3840: 04 06 04 db 03 01 65 03 14 04 05 07 05 05 01 01 ......e.........
+| 3856: 02 08 0a 01 20 04 05 07 05 07 05 07 05 05 01 01 .... ...........
+| 3872: 02 08 0a 0a 0a 04 01 65 03 02 0a 01 06 0a 0a 0a .......e........
+| 3888: 05 01 65 03 06 01 01 0a 01 0a 01 01 0a 0a 0a 04 ..e.............
+| 3904: 2b 31 21 0b 0f ef 00 14 2a 00 00 00 00 01 02 02 +1!.....*.......
+| 3920: 00 02 01 01 01 02 01 01 50 88 80 80 80 80 01 04 ........P.......
+| 3936: 00 81 24 00 00 00 47 02 30 65 02 1a 02 05 05 07 ..$...G.0e......
+| 3952: 05 01 01 04 03 03 08 03 03 02 01 65 02 1e 03 05 ...........e....
+| 3968: 05 04 05 05 01 01 03 06 09 14 06 04 03 03 01 65 ...............e
+| 3984: 02 14 04 05 07 05 05 01 01 02 08 ed 04 01 65 02 ..............e.
+| 4000: 02 0a 05 01 65 02 06 01 01 0a 04 12 14 0f 06 31 ....e..........1
+| 4016: 84 80 80 80 80 01 03 00 68 00 00 00 2b 02 30 65 ........h...+.0e
+| 4032: 01 10 02 05 05 01 01 04 03 03 02 01 55 01 12 03 ............U...
+| 4048: 05 05 01 01 03 06 05 03 03 01 65 01 0e 04 05 05 ..........e.....
+| 4064: 01 01 02 08 04 0d 0e 06 01 03 00 12 04 4c 4c 00 .............LL.
+| 4080: 00 00 11 24 00 00 00 00 01 0f c1 00 01 01 01 01 ...$............
+| page 3 offset 8192
+| 0: 0a 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 06 04 01 0c ................
+| 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0c 01 02 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3600: 00 00 00 00 00 00 00 00 00 00 81 52 04 06 00 81 ...........R....
+| 3616: 5d 81 55 65 20 65 65 20 65 65 65 20 65 20 65 65 ].Ue ee eee e ee
+| 3632: 20 65 65 65 20 65 20 65 65 20 65 65 65 65 20 65 eee e ee eeee e
+| 3648: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 3664: 20 65 65 20 65 65 65 65 20 65 65 20 65 65 65 20 ee eeee ee eee
+| 3680: 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 e ee eee e ee ee
+| 3696: 65 65 20 65 65 20 65 65 65 20 65 20 65 65 20 65 ee ee eee e ee e
+| 3712: 65 65 20 65 20 65 65 20 65 65 65 65 65 65 20 65 ee e ee eeeeee e
+| 3728: 65 20 65 20 65 20 65 20 65 65 20 65 65 65 20 65 e e e e ee eee e
+| 3744: 65 20 65 65 65 65 65 20 65 65 20 65 20 65 20 65 e eeeee ee e e e
+| 3760: 20 65 65 20 65 62 d5 20 65 65 20 65 65 65 65 65 ee eb. ee eeeee
+| 3776: 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 65 ee e e e ee eee
+| 3792: 20 65 65 20 65 65 65 65 65 20 65 65 20 65 21 65 ee eeeee ee e!e
+| 3808: 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 6a e ee eee ee eej
+| 3824: 03 04 00 75 71 65 20 65 65 10 65 65 65 20 65 20 ...uqe ee.eee e
+| 3840: 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 65 ee eee e ee eeee
+| 3856: 20 65 65 20 65 65 65 20 65 20 65 65 20 65 65 65 ee eee e ee eee
+| 3872: 20 65 20 65 65 20 65 65 65 65 65 65 20 65 65 20 e ee eeeeee ee
+| 3888: 65 20 65 20 65 20 65 65 20 65 65 65 20 62 55 20 e e e ee eee bU
+| 3904: 65 65 65 65 65 20 65 65 20 65 20 65 20 65 20 65 eeeee ee e e e e
+| 3920: 65 20 65 65 65 20 55 65 20 65 65 6a 02 04 00 75 e eee Ue eej...u
+| 3936: 71 65 20 65 65 20 65 65 65 20 65 10 65 65 20 65 qe ee eee e.ee e
+| 3952: 65 65 20 65 20 65 65 20 65 65 65 65 20 65 65 20 ee e ee eeee ee
+| 3968: 65 65 65 20 65 20 65 65 20 65 65 65 20 65 20 65 eee e ee eee e e
+| 3984: 65 20 65 65 65 65 65 65 20 65 65 20 65 20 65 20 e eeeeee ee e e
+| 4000: 65 20 65 65 20 65 65 65 20 65 65 20 65 65 65 65 e ee eee ee eeee
+| 4016: 65 20 65 65 20 65 20 65 20 65 20 65 65 20 65 65 e ee e e e ee ee
+| 4032: 65 20 65 65 21 65 65 37 0a 04 00 41 3f 65 20 65 e ee!ee7...A?e e
+| 4048: 65 20 65 65 65 20 65 20 65 65 20 65 65 65 20 65 e eee e ee eee e
+| 4064: 20 65 65 20 65 65 65 65 65 65 20 65 65 20 65 20 ee eeeeee ee e
+| 4080: 65 20 65 20 65 65 20 65 65 65 20 65 65 20 65 65 e e ee eee ee ee
+| page 5 offset 16384
+| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 4064: 00 00 00 00 05 04 03 00 10 21 21 05 13 03 00 10 .........!!.....
+| 4080: 11 11 05 02 03 00 10 11 11 05 01 03 00 10 09 09 ................
+| page 6 offset 20480
+| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
+| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
+| end crash-b02ca2cc4d7dda.db
+}]} {}
+
+do_catchsql_test 73.1 {
+ SELECT snippet(ttt,ttt, NOT 54 ),
+ * FROM ttt('e* NOT ee*e* NOT ee* NOT ee*e* NOT e*') ;
+} {1 {database disk image is malformed}}
sqlite3_fts5_may_be_corrupt 0
finish_test
diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c
index 46f3a4f0b5..0f6b00f436 100644
--- a/ext/misc/cksumvfs.c
+++ b/ext/misc/cksumvfs.c
@@ -750,7 +750,17 @@ static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
}
static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
- return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
+ sqlite3_vfs *pOrig = ORIGVFS(pVfs);
+ int rc;
+ assert( pOrig->iVersion>=2 );
+ if( pOrig->xCurrentTimeInt64 ){
+ rc = pOrig->xCurrentTimeInt64(pOrig, p);
+ }else{
+ double r;
+ rc = pOrig->xCurrentTime(pOrig, &r);
+ *p = (sqlite3_int64)(r*86400000.0);
+ }
+ return rc;
}
static int cksmSetSystemCall(
sqlite3_vfs *pVfs,
diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c
index 121eb43d66..6cdd79a4d4 100644
--- a/ext/misc/ieee754.c
+++ b/ext/misc/ieee754.c
@@ -167,6 +167,14 @@ static void ieee754func(
int isNeg = 0;
m = sqlite3_value_int64(argv[0]);
e = sqlite3_value_int64(argv[1]);
+
+ /* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
+ if( e>10000 ){
+ e = 10000;
+ }else if( e<-10000 ){
+ e = -10000;
+ }
+
if( m<0 ){
isNeg = 1;
m = -m;
diff --git a/ext/misc/shathree.c b/ext/misc/shathree.c
index 75065e97c1..ef25cb56c6 100644
--- a/ext/misc/shathree.c
+++ b/ext/misc/shathree.c
@@ -624,9 +624,11 @@ static void sha3QueryFunc(
}
nCol = sqlite3_column_count(pStmt);
z = sqlite3_sql(pStmt);
- n = (int)strlen(z);
- hash_step_vformat(&cx,"S%d:",n);
- SHA3Update(&cx,(unsigned char*)z,n);
+ if( z ){
+ n = (int)strlen(z);
+ hash_step_vformat(&cx,"S%d:",n);
+ SHA3Update(&cx,(unsigned char*)z,n);
+ }
/* Compute a hash over the result of the query */
while( SQLITE_ROW==sqlite3_step(pStmt) ){
diff --git a/ext/rbu/rbu.c b/ext/rbu/rbu.c
index 02822c4a42..91773409cf 100644
--- a/ext/rbu/rbu.c
+++ b/ext/rbu/rbu.c
@@ -183,6 +183,13 @@ int main(int argc, char **argv){
break;
}
+ if( nStatStep>0 ){
+ sqlite3_int64 nUsed;
+ sqlite3_int64 nHighwater;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &nUsed, &nHighwater, 0);
+ fprintf(stdout, "memory used=%lld highwater=%lld\n", nUsed, nHighwater);
+ }
+
sqlite3_free(zErrmsg);
return (rc==SQLITE_OK || rc==SQLITE_DONE) ? 0 : 1;
}
diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c
index 12a0a46603..c5f809848d 100644
--- a/ext/rbu/sqlite3rbu.c
+++ b/ext/rbu/sqlite3rbu.c
@@ -4828,22 +4828,24 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
#endif
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
- if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
- /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
- ** taking this lock also prevents any checkpoints from occurring.
- ** todo: really, it's not clear why this might occur, as
- ** wal_autocheckpoint ought to be turned off. */
+ if( pRbu && (
+ pRbu->eStage==RBU_STAGE_OAL
+ || pRbu->eStage==RBU_STAGE_MOVE
+ || pRbu->eStage==RBU_STAGE_DONE
+ )){
+ /* Prevent SQLite from taking a shm-lock on the target file when it
+ ** is supplying heap memory to the upper layer in place of *-shm
+ ** segments. */
if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
}else{
int bCapture = 0;
if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
bCapture = 1;
}
-
if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
if( bCapture && rc==SQLITE_OK ){
- pRbu->mLock |= (1 << ofst);
+ pRbu->mLock |= ((1< $mem1}
+ } $eRes
+}
+
+do_test 1.3 {
+ S delete
+} {}
+
+finish_test
diff --git a/ext/session/sessionnoop.test b/ext/session/sessionnoop.test
new file mode 100644
index 0000000000..16c60b7abf
--- /dev/null
+++ b/ext/session/sessionnoop.test
@@ -0,0 +1,187 @@
+# 2021 Februar 20
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source [file join [file dirname [info script]] session_common.tcl]
+source $testdir/tester.tcl
+ifcapable !session {finish_test; return}
+
+set testprefix sessionnoop
+
+#-------------------------------------------------------------------------
+# Test plan:
+#
+# 1.*: Test that concatenating changesets cannot produce a noop UPDATE.
+# 2.*: Test that rebasing changesets cannot produce a noop UPDATE.
+# 3.*: Test that sqlite3changeset_apply() ignores noop UPDATE changes.
+#
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a PRIMARY KEY, b, c, d);
+ INSERT INTO t1 VALUES(1, 1, 1, 1);
+ INSERT INTO t1 VALUES(2, 2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3, 3);
+}
+
+proc do_concat_test {tn sql1 sql2 res} {
+ uplevel [list do_test $tn [subst -nocommands {
+ set C1 [changeset_from_sql {$sql1}]
+ set C2 [changeset_from_sql {$sql2}]
+ set C3 [sqlite3changeset_concat [set C1] [set C2]]
+ set got [list]
+ sqlite3session_foreach elem [set C3] { lappend got [set elem] }
+ set got
+ }] [list {*}$res]]
+}
+
+do_concat_test 1.1 {
+ UPDATE t1 SET c=c+1;
+} {
+ UPDATE t1 SET c=c-1;
+} {
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(1, 1, 1);
+ INSERT INTO t1 VALUES(2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3);
+}
+
+proc do_rebase_test {tn sql_local sql_remote conflict_res expected} {
+ proc xConflict {args} [list return $conflict_res]
+
+ uplevel [list \
+ do_test $tn [subst -nocommands {
+ execsql BEGIN
+ set c_remote [changeset_from_sql {$sql_remote}]
+ execsql ROLLBACK
+
+ execsql BEGIN
+ set c_local [changeset_from_sql {$sql_local}]
+ set base [sqlite3changeset_apply_v2 db [set c_remote] xConflict]
+ execsql ROLLBACK
+
+ sqlite3rebaser_create R
+ R config [set base]
+ set res [list]
+ sqlite3session_foreach elem [R rebase [set c_local]] {
+ lappend res [set elem]
+ }
+ R delete
+ set res
+ }] [list {*}$expected]
+ ]
+}
+
+do_rebase_test 2.1 {
+ UPDATE t1 SET c=2 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=3 WHERE a=1; -- remote
+} OMIT {
+ {UPDATE t1 0 X.. {i 1 {} {} i 3} {{} {} {} {} i 2}}
+}
+
+do_rebase_test 2.2 {
+ UPDATE t1 SET c=2 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=3 WHERE a=1; -- remote
+} REPLACE {
+}
+
+do_rebase_test 2.3.1 {
+ UPDATE t1 SET c=4 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=4 WHERE a=1 -- remote
+} OMIT {
+ {UPDATE t1 0 X.. {i 1 {} {} i 4} {{} {} {} {} i 4}}
+}
+
+do_rebase_test 2.3.2 {
+ UPDATE t1 SET c=5 WHERE a=1; -- local
+} {
+ UPDATE t1 SET c=5 WHERE a=1 -- remote
+} REPLACE {
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 3.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(1, 1, 1);
+ INSERT INTO t1 VALUES(2, 2, 2);
+ INSERT INTO t1 VALUES(3, 3, 3);
+ INSERT INTO t1 VALUES(4, 4, 4);
+}
+
+# Arg $pkstr contains one character for each column in the table. An
+# "X" for PK column, or a "." for a non-PK.
+#
+proc mk_tbl_header {name pkstr} {
+ set ret [binary format H2c 54 [string length $pkstr]]
+ foreach i [split $pkstr {}] {
+ if {$i=="X"} {
+ append ret [binary format H2 01]
+ } else {
+ if {$i!="."} {error "bad pkstr: $pkstr ($i)"}
+ append ret [binary format H2 00]
+ }
+ }
+ append ret $name
+ append ret [binary format H2 00]
+ set ret
+}
+
+proc mk_update_change {args} {
+ set ret [binary format H2H2 17 00]
+ foreach a $args {
+ if {$a==""} {
+ append ret [binary format H2 00]
+ } else {
+ append ret [binary format H2W 01 $a]
+ }
+ }
+ set ret
+}
+
+proc xConflict {args} { return "ABORT" }
+do_test 3.1 {
+ set C [mk_tbl_header t1 X..]
+ append C [mk_update_change 1 {} 1 {} {} 500]
+ append C [mk_update_change 2 {} {} {} {} {}]
+ append C [mk_update_change 3 3 {} {} 600 {}]
+ append C [mk_update_change 4 {} {} {} {} {}]
+
+ sqlite3changeset_apply_v2 db $C xConflict
+} {}
+do_execsql_test 3.2 {
+ SELECT * FROM t1
+} {
+ 1 1 500
+ 2 2 2
+ 3 600 3
+ 4 4 4
+}
+
+
+
+
+
+
+finish_test
+
diff --git a/ext/session/sessionwor.test b/ext/session/sessionwor.test
index 0f0b429d7b..7d9e5c6a89 100644
--- a/ext/session/sessionwor.test
+++ b/ext/session/sessionwor.test
@@ -69,7 +69,9 @@ foreach {tn wo} {
} {
reset_db
- do_execsql_test 2.$tn.0 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b) $wo ;"
+ do_execsql_test 2.$tn.0.1 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b) $wo ;"
+ do_execsql_test 2.$tn.0.2 "CREATE TABLE t2(a INTEGER PRIMARY KEY, b) $wo ;"
+ do_execsql_test 2.$tn.0.3 "CREATE TABLE t3(a INTEGER PRIMARY KEY, b) $wo ;"
do_iterator_test 1.1 t1 {
INSERT INTO t1 VALUES(1, 'two');
@@ -94,6 +96,27 @@ foreach {tn wo} {
} {
{DELETE t1 0 X. {i 1 t four} {}}
}
+
+ do_execsql_test 2.$tn.5 {
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ }
+
+ do_iterator_test 2.$tn.6 t2 {
+ INSERT INTO t2 SELECT a, b FROM t1
+ } {
+ {INSERT t2 0 X. {} {i 1 t one}}
+ {INSERT t2 0 X. {} {i 2 t two}}
+ {INSERT t2 0 X. {} {i 3 t three}}
+ }
+ do_iterator_test 2.$tn.7 t3 {
+ INSERT INTO t3 SELECT * FROM t1
+ } {
+ {INSERT t3 0 X. {} {i 1 t one}}
+ {INSERT t3 0 X. {} {i 2 t two}}
+ {INSERT t3 0 X. {} {i 3 t three}}
+ }
}
finish_test
diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c
index b49bf733ec..f069a42e1f 100644
--- a/ext/session/sqlite3session.c
+++ b/ext/session/sqlite3session.c
@@ -55,6 +55,7 @@ struct sqlite3_session {
int rc; /* Non-zero if an error has occurred */
void *pFilterCtx; /* First argument to pass to xTableFilter */
int (*xTableFilter)(void *pCtx, const char *zTab);
+ i64 nMalloc; /* Number of bytes of data allocated */
sqlite3_value *pZeroBlob; /* Value containing X'' */
sqlite3_session *pNext; /* Next session object on same db. */
SessionTable *pTable; /* List of attached tables */
@@ -97,6 +98,7 @@ struct sqlite3_changeset_iter {
SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
int bPatchset; /* True if this is a patchset */
int bInvert; /* True to invert changeset */
+ int bSkipEmpty; /* Skip noop UPDATE changes */
int rc; /* Iterator error code */
sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
char *zTab; /* Current table */
@@ -438,6 +440,26 @@ static int sessionSerializeValue(
return SQLITE_OK;
}
+/*
+** Allocate and return a pointer to a buffer nByte bytes in size. If
+** pSession is not NULL, increase the sqlite3_session.nMalloc variable
+** by the number of bytes allocated.
+*/
+static void *sessionMalloc64(sqlite3_session *pSession, i64 nByte){
+ void *pRet = sqlite3_malloc64(nByte);
+ if( pSession ) pSession->nMalloc += sqlite3_msize(pRet);
+ return pRet;
+}
+
+/*
+** Free buffer pFree, which must have been allocated by an earlier
+** call to sessionMalloc64(). If pSession is not NULL, decrease the
+** sqlite3_session.nMalloc counter by the number of bytes freed.
+*/
+static void sessionFree(sqlite3_session *pSession, void *pFree){
+ if( pSession ) pSession->nMalloc -= sqlite3_msize(pFree);
+ sqlite3_free(pFree);
+}
/*
** This macro is used to calculate hash key values for data structures. In
@@ -905,13 +927,19 @@ static int sessionPreupdateEqual(
** Growing the hash table in this case is a performance optimization only,
** it is not required for correct operation.
*/
-static int sessionGrowHash(int bPatchset, SessionTable *pTab){
+static int sessionGrowHash(
+ sqlite3_session *pSession, /* For memory accounting. May be NULL */
+ int bPatchset,
+ SessionTable *pTab
+){
if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
int i;
SessionChange **apNew;
sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);
- apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew);
+ apNew = (SessionChange**)sessionMalloc64(
+ pSession, sizeof(SessionChange*) * nNew
+ );
if( apNew==0 ){
if( pTab->nChange==0 ){
return SQLITE_ERROR;
@@ -932,7 +960,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
}
}
- sqlite3_free(pTab->apChange);
+ sessionFree(pSession, pTab->apChange);
pTab->nChange = nNew;
pTab->apChange = apNew;
}
@@ -966,6 +994,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
** be freed using sqlite3_free() by the caller
*/
static int sessionTableInfo(
+ sqlite3_session *pSession, /* For memory accounting. May be NULL */
sqlite3 *db, /* Database connection */
const char *zDb, /* Name of attached database (e.g. "main") */
const char *zThis, /* Table name */
@@ -1020,7 +1049,7 @@ static int sessionTableInfo(
if( rc==SQLITE_OK ){
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
- pAlloc = sqlite3_malloc64(nByte);
+ pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
}
@@ -1063,7 +1092,7 @@ static int sessionTableInfo(
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
- sqlite3_free(azCol);
+ sessionFree(pSession, azCol);
}
sqlite3_finalize(pStmt);
return rc;
@@ -1085,7 +1114,7 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
- pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
+ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
);
if( pSession->rc==SQLITE_OK ){
@@ -1176,7 +1205,7 @@ static void sessionPreupdateOneChange(
}
/* Grow the hash table if required */
- if( sessionGrowHash(0, pTab) ){
+ if( sessionGrowHash(pSession, 0, pTab) ){
pSession->rc = SQLITE_NOMEM;
return;
}
@@ -1243,7 +1272,7 @@ static void sessionPreupdateOneChange(
}
/* Allocate the change object */
- pChange = (SessionChange *)sqlite3_malloc64(nByte);
+ pChange = (SessionChange *)sessionMalloc64(pSession, nByte);
if( !pChange ){
rc = SQLITE_NOMEM;
goto error_out;
@@ -1616,7 +1645,7 @@ int sqlite3session_diff(
int nCol; /* Columns in zFrom.zTbl */
u8 *abPK;
const char **azCol = 0;
- rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
+ rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
if( rc==SQLITE_OK ){
if( pTo->nCol!=nCol ){
bMismatch = 1;
@@ -1714,7 +1743,7 @@ int sqlite3session_create(
** Free the list of table objects passed as the first argument. The contents
** of the changed-rows hash tables are also deleted.
*/
-static void sessionDeleteTable(SessionTable *pList){
+static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){
SessionTable *pNext;
SessionTable *pTab;
@@ -1726,12 +1755,12 @@ static void sessionDeleteTable(SessionTable *pList){
SessionChange *pNextChange;
for(p=pTab->apChange[i]; p; p=pNextChange){
pNextChange = p->pNext;
- sqlite3_free(p);
+ sessionFree(pSession, p);
}
}
- sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */
- sqlite3_free(pTab->apChange);
- sqlite3_free(pTab);
+ sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */
+ sessionFree(pSession, pTab->apChange);
+ sessionFree(pSession, pTab);
}
}
@@ -1759,9 +1788,11 @@ void sqlite3session_delete(sqlite3_session *pSession){
/* Delete all attached table objects. And the contents of their
** associated hash-tables. */
- sessionDeleteTable(pSession->pTable);
+ sessionDeleteTable(pSession, pSession->pTable);
- /* Free the session object itself. */
+ /* Assert that all allocations have been freed and then free the
+ ** session object itself. */
+ assert( pSession->nMalloc==0 );
sqlite3_free(pSession);
}
@@ -1808,7 +1839,8 @@ int sqlite3session_attach(
if( !pTab ){
/* Allocate new SessionTable object. */
- pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1);
+ int nByte = sizeof(SessionTable) + nName + 1;
+ pTab = (SessionTable*)sessionMalloc64(pSession, nByte);
if( !pTab ){
rc = SQLITE_NOMEM;
}else{
@@ -2405,7 +2437,7 @@ static int sessionGenerateChangeset(
int nNoop; /* Size of buffer after writing tbl header */
/* Check the table schema is still Ok. */
- rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
+ rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK);
if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
rc = SQLITE_SCHEMA;
}
@@ -2594,6 +2626,13 @@ int sqlite3session_isempty(sqlite3_session *pSession){
return (ret==0);
}
+/*
+** Return the amount of heap memory in use.
+*/
+sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){
+ return pSession->nMalloc;
+}
+
/*
** Do the work for either sqlite3changeset_start() or start_strm().
*/
@@ -2603,7 +2642,8 @@ static int sessionChangesetStart(
void *pIn,
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset, /* Pointer to buffer containing changeset */
- int bInvert /* True to invert changeset */
+ int bInvert, /* True to invert changeset */
+ int bSkipEmpty /* True to skip empty UPDATE changes */
){
sqlite3_changeset_iter *pRet; /* Iterator to return */
int nByte; /* Number of bytes to allocate for iterator */
@@ -2624,6 +2664,7 @@ static int sessionChangesetStart(
pRet->in.pIn = pIn;
pRet->in.bEof = (xInput ? 0 : 1);
pRet->bInvert = bInvert;
+ pRet->bSkipEmpty = bSkipEmpty;
/* Populate the output variable and return success. */
*pp = pRet;
@@ -2638,7 +2679,7 @@ int sqlite3changeset_start(
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset /* Pointer to buffer containing changeset */
){
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0, 0);
}
int sqlite3changeset_start_v2(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
@@ -2647,7 +2688,7 @@ int sqlite3changeset_start_v2(
int flags
){
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0);
}
/*
@@ -2658,7 +2699,7 @@ int sqlite3changeset_start_strm(
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
){
- return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0, 0);
}
int sqlite3changeset_start_v2_strm(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
@@ -2667,7 +2708,7 @@ int sqlite3changeset_start_v2_strm(
int flags
){
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
- return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert, 0);
}
/*
@@ -2793,11 +2834,14 @@ static int sessionReadRecord(
SessionInput *pIn, /* Input data */
int nCol, /* Number of values in record */
u8 *abPK, /* Array of primary key flags, or NULL */
- sqlite3_value **apOut /* Write values to this array */
+ sqlite3_value **apOut, /* Write values to this array */
+ int *pbEmpty
){
int i; /* Used to iterate through columns */
int rc = SQLITE_OK;
+ assert( pbEmpty==0 || *pbEmpty==0 );
+ if( pbEmpty ) *pbEmpty = 1;
for(i=0; iaData[pIn->iNext++];
assert( apOut[i]==0 );
if( eType ){
+ if( pbEmpty ) *pbEmpty = 0;
apOut[i] = sqlite3ValueNew(0);
if( !apOut[i] ) rc = SQLITE_NOMEM;
}
@@ -2988,31 +3033,27 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
}
/*
-** Advance the changeset iterator to the next change.
+** Advance the changeset iterator to the next change. The differences between
+** this function and sessionChangesetNext() are that
**
-** If both paRec and pnRec are NULL, then this function works like the public
-** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
-** sqlite3changeset_new() and old() APIs may be used to query for values.
+** * If pbEmpty is not NULL and the change is a no-op UPDATE (an UPDATE
+** that modifies no columns), this function sets (*pbEmpty) to 1.
**
-** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
-** record is written to *paRec before returning and the number of bytes in
-** the record to *pnRec.
-**
-** Either way, this function returns SQLITE_ROW if the iterator is
-** successfully advanced to the next change in the changeset, an SQLite
-** error code if an error occurs, or SQLITE_DONE if there are no further
-** changes in the changeset.
+** * If the iterator is configured to skip no-op UPDATEs,
+** sessionChangesetNext() does that. This function does not.
*/
-static int sessionChangesetNext(
+static int sessionChangesetNextOne(
sqlite3_changeset_iter *p, /* Changeset iterator */
u8 **paRec, /* If non-NULL, store record pointer here */
int *pnRec, /* If non-NULL, store size of record here */
- int *pbNew /* If non-NULL, true if new table */
+ int *pbNew, /* If non-NULL, true if new table */
+ int *pbEmpty
){
int i;
u8 op;
assert( (paRec==0 && pnRec==0) || (paRec && pnRec) );
+ assert( pbEmpty==0 || *pbEmpty==0 );
/* If the iterator is in the error-state, return immediately. */
if( p->rc!=SQLITE_OK ) return p->rc;
@@ -3085,13 +3126,13 @@ static int sessionChangesetNext(
/* If this is an UPDATE or DELETE, read the old.* record. */
if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){
u8 *abPK = p->bPatchset ? p->abPK : 0;
- p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld);
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld, 0);
if( p->rc!=SQLITE_OK ) return p->rc;
}
/* If this is an INSERT or UPDATE, read the new.* record. */
if( p->op!=SQLITE_DELETE ){
- p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew);
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty);
if( p->rc!=SQLITE_OK ) return p->rc;
}
@@ -3118,6 +3159,37 @@ static int sessionChangesetNext(
return SQLITE_ROW;
}
+/*
+** Advance the changeset iterator to the next change.
+**
+** If both paRec and pnRec are NULL, then this function works like the public
+** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
+** sqlite3changeset_new() and old() APIs may be used to query for values.
+**
+** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
+** record is written to *paRec before returning and the number of bytes in
+** the record to *pnRec.
+**
+** Either way, this function returns SQLITE_ROW if the iterator is
+** successfully advanced to the next change in the changeset, an SQLite
+** error code if an error occurs, or SQLITE_DONE if there are no further
+** changes in the changeset.
+*/
+static int sessionChangesetNext(
+ sqlite3_changeset_iter *p, /* Changeset iterator */
+ u8 **paRec, /* If non-NULL, store record pointer here */
+ int *pnRec, /* If non-NULL, store size of record here */
+ int *pbNew /* If non-NULL, true if new table */
+){
+ int bEmpty;
+ int rc;
+ do {
+ bEmpty = 0;
+ rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty);
+ }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty);
+ return rc;
+}
+
/*
** Advance an iterator created by sqlite3changeset_start() to the next
** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
@@ -3390,9 +3462,9 @@ static int sessionChangesetInvert(
/* Read the old.* and new.* records for the update change. */
pInput->iNext += 2;
- rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]);
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0], 0);
if( rc==SQLITE_OK ){
- rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]);
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol], 0);
}
/* Write the new old.* record. Consists of the PK columns from the
@@ -3493,16 +3565,25 @@ int sqlite3changeset_invert_strm(
return rc;
}
+
+typedef struct SessionUpdate SessionUpdate;
+struct SessionUpdate {
+ sqlite3_stmt *pStmt;
+ u32 *aMask;
+ SessionUpdate *pNext;
+};
+
typedef struct SessionApplyCtx SessionApplyCtx;
struct SessionApplyCtx {
sqlite3 *db;
sqlite3_stmt *pDelete; /* DELETE statement */
- sqlite3_stmt *pUpdate; /* UPDATE statement */
sqlite3_stmt *pInsert; /* INSERT statement */
sqlite3_stmt *pSelect; /* SELECT statement */
int nCol; /* Size of azCol[] and abPK[] arrays */
const char **azCol; /* Array of column names */
u8 *abPK; /* Boolean array - true if column is in PK */
+ u32 *aUpdateMask; /* Used by sessionUpdateFind */
+ SessionUpdate *pUp;
int bStat1; /* True if table is sqlite_stat1 */
int bDeferConstraints; /* True to defer constraints */
int bInvertConstraints; /* Invert when iterating constraints buffer */
@@ -3512,6 +3593,167 @@ struct SessionApplyCtx {
u8 bRebase; /* True to collect rebase information */
};
+/* Number of prepared UPDATE statements to cache. */
+#define SESSION_UPDATE_CACHE_SZ 12
+
+/*
+** Find a prepared UPDATE statement suitable for the UPDATE step currently
+** being visited by the iterator. The UPDATE is of the form:
+**
+** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ?
+*/
+static int sessionUpdateFind(
+ sqlite3_changeset_iter *pIter,
+ SessionApplyCtx *p,
+ int bPatchset,
+ sqlite3_stmt **ppStmt
+){
+ int rc = SQLITE_OK;
+ SessionUpdate *pUp = 0;
+ int nCol = pIter->nCol;
+ int nU32 = (pIter->nCol+33)/32;
+ int ii;
+
+ if( p->aUpdateMask==0 ){
+ p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32));
+ if( p->aUpdateMask==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ memset(p->aUpdateMask, 0, nU32*sizeof(u32));
+ rc = SQLITE_CORRUPT;
+ for(ii=0; iinCol; ii++){
+ if( sessionChangesetNew(pIter, ii) ){
+ p->aUpdateMask[ii/32] |= (1<<(ii%32));
+ rc = SQLITE_OK;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32));
+
+ if( p->pUp ){
+ int nUp = 0;
+ SessionUpdate **pp = &p->pUp;
+ while( 1 ){
+ nUp++;
+ if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){
+ pUp = *pp;
+ *pp = pUp->pNext;
+ pUp->pNext = p->pUp;
+ p->pUp = pUp;
+ break;
+ }
+
+ if( (*pp)->pNext ){
+ pp = &(*pp)->pNext;
+ }else{
+ if( nUp>=SESSION_UPDATE_CACHE_SZ ){
+ sqlite3_finalize((*pp)->pStmt);
+ sqlite3_free(*pp);
+ *pp = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ if( pUp==0 ){
+ int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32);
+ int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0);
+ pUp = (SessionUpdate*)sqlite3_malloc(nByte);
+ if( pUp==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ const char *zSep = "";
+ SessionBuffer buf;
+
+ memset(&buf, 0, sizeof(buf));
+ pUp->aMask = (u32*)&pUp[1];
+ memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32));
+
+ sessionAppendStr(&buf, "UPDATE main.", &rc);
+ sessionAppendIdent(&buf, pIter->zTab, &rc);
+ sessionAppendStr(&buf, " SET ", &rc);
+
+ /* Create the assignments part of the UPDATE */
+ for(ii=0; iinCol; ii++){
+ if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[ii], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, ii*2+1, &rc);
+ zSep = ", ";
+ }
+ }
+
+ /* Create the WHERE clause part of the UPDATE */
+ zSep = "";
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(ii=0; iinCol; ii++){
+ if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){
+ sessionAppendStr(&buf, zSep, &rc);
+ if( bStat1 && ii==1 ){
+ assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 );
+ sessionAppendStr(&buf,
+ "idx IS CASE "
+ "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL "
+ "ELSE ?4 END ", &rc
+ );
+ }else{
+ sessionAppendIdent(&buf, p->azCol[ii], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, ii*2+2, &rc);
+ }
+ zSep = " AND ";
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ char *zSql = (char*)buf.aBuf;
+ rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pUp);
+ pUp = 0;
+ }else{
+ pUp->pNext = p->pUp;
+ p->pUp = pUp;
+ }
+ sqlite3_free(buf.aBuf);
+ }
+ }
+ }
+
+ assert( (rc==SQLITE_OK)==(pUp!=0) );
+ if( pUp ){
+ *ppStmt = pUp->pStmt;
+ }else{
+ *ppStmt = 0;
+ }
+ return rc;
+}
+
+/*
+** Free all cached UPDATE statements.
+*/
+static void sessionUpdateFree(SessionApplyCtx *p){
+ SessionUpdate *pUp;
+ SessionUpdate *pNext;
+ for(pUp=p->pUp; pUp; pUp=pNext){
+ pNext = pUp->pNext;
+ sqlite3_finalize(pUp->pStmt);
+ sqlite3_free(pUp);
+ }
+ p->pUp = 0;
+ sqlite3_free(p->aUpdateMask);
+ p->aUpdateMask = 0;
+}
+
/*
** Formulate a statement to DELETE a row from database db. Assuming a table
** structure like this:
@@ -3581,103 +3823,6 @@ static int sessionDeleteRow(
return rc;
}
-/*
-** Formulate and prepare a statement to UPDATE a row from database db.
-** Assuming a table structure like this:
-**
-** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
-**
-** The UPDATE statement looks like this:
-**
-** UPDATE x SET
-** a = CASE WHEN ?2 THEN ?3 ELSE a END,
-** b = CASE WHEN ?5 THEN ?6 ELSE b END,
-** c = CASE WHEN ?8 THEN ?9 ELSE c END,
-** d = CASE WHEN ?11 THEN ?12 ELSE d END
-** WHERE a = ?1 AND c = ?7 AND (?13 OR
-** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND
-** )
-**
-** For each column in the table, there are three variables to bind:
-**
-** ?(i*3+1) The old.* value of the column, if any.
-** ?(i*3+2) A boolean flag indicating that the value is being modified.
-** ?(i*3+3) The new.* value of the column, if any.
-**
-** Also, a boolean flag that, if set to true, causes the statement to update
-** a row even if the non-PK values do not match. This is required if the
-** conflict-handler is invoked with CHANGESET_DATA and returns
-** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
-**
-** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
-** pointing to the prepared version of the SQL statement.
-*/
-static int sessionUpdateRow(
- sqlite3 *db, /* Database handle */
- const char *zTab, /* Table name */
- SessionApplyCtx *p /* Session changeset-apply context */
-){
- int rc = SQLITE_OK;
- int i;
- const char *zSep = "";
- SessionBuffer buf = {0, 0, 0};
-
- /* Append "UPDATE tbl SET " */
- sessionAppendStr(&buf, "UPDATE main.", &rc);
- sessionAppendIdent(&buf, zTab, &rc);
- sessionAppendStr(&buf, " SET ", &rc);
-
- /* Append the assignments */
- for(i=0; inCol; i++){
- sessionAppendStr(&buf, zSep, &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
- sessionAppendInteger(&buf, i*3+2, &rc);
- sessionAppendStr(&buf, " THEN ?", &rc);
- sessionAppendInteger(&buf, i*3+3, &rc);
- sessionAppendStr(&buf, " ELSE ", &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " END", &rc);
- zSep = ", ";
- }
-
- /* Append the PK part of the WHERE clause */
- sessionAppendStr(&buf, " WHERE ", &rc);
- for(i=0; inCol; i++){
- if( p->abPK[i] ){
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " = ?", &rc);
- sessionAppendInteger(&buf, i*3+1, &rc);
- sessionAppendStr(&buf, " AND ", &rc);
- }
- }
-
- /* Append the non-PK part of the WHERE clause */
- sessionAppendStr(&buf, " (?", &rc);
- sessionAppendInteger(&buf, p->nCol*3+1, &rc);
- sessionAppendStr(&buf, " OR 1", &rc);
- for(i=0; inCol; i++){
- if( !p->abPK[i] ){
- sessionAppendStr(&buf, " AND (?", &rc);
- sessionAppendInteger(&buf, i*3+2, &rc);
- sessionAppendStr(&buf, "=0 OR ", &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " IS ?", &rc);
- sessionAppendInteger(&buf, i*3+1, &rc);
- sessionAppendStr(&buf, ")", &rc);
- }
- }
- sessionAppendStr(&buf, ")", &rc);
-
- if( rc==SQLITE_OK ){
- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
- }
- sqlite3_free(buf.aBuf);
-
- return rc;
-}
-
-
/*
** Formulate and prepare an SQL statement to query table zTab by primary
** key. Assuming the following table structure:
@@ -3758,17 +3903,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
"?3)"
);
}
- if( rc==SQLITE_OK ){
- rc = sessionPrepare(db, &p->pUpdate,
- "UPDATE main.sqlite_stat1 SET "
- "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, "
- "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, "
- "stat = CASE WHEN ?8 THEN ?9 ELSE stat END "
- "WHERE tbl=?1 AND idx IS "
- "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END "
- "AND (?10 OR ?8=0 OR stat IS ?7)"
- );
- }
if( rc==SQLITE_OK ){
rc = sessionPrepare(db, &p->pDelete,
"DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
@@ -4085,7 +4219,7 @@ static int sessionApplyOneOp(
int nCol;
int rc = SQLITE_OK;
- assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
+ assert( p->pDelete && p->pInsert && p->pSelect );
assert( p->azCol && p->abPK );
assert( !pbReplace || *pbReplace==0 );
@@ -4125,29 +4259,28 @@ static int sessionApplyOneOp(
}else if( op==SQLITE_UPDATE ){
int i;
+ sqlite3_stmt *pUp = 0;
+ int bPatchset = (pbRetry==0 || pIter->bPatchset);
+
+ rc = sessionUpdateFind(pIter, p, bPatchset, &pUp);
/* Bind values to the UPDATE statement. */
for(i=0; rc==SQLITE_OK && ipUpdate, i*3+2, !!pNew);
- if( pOld ){
- rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
+ if( p->abPK[i] || (bPatchset==0 && pOld) ){
+ rc = sessionBindValue(pUp, i*2+2, pOld);
}
if( rc==SQLITE_OK && pNew ){
- rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
+ rc = sessionBindValue(pUp, i*2+1, pNew);
}
}
- if( rc==SQLITE_OK ){
- sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset);
- }
if( rc!=SQLITE_OK ) return rc;
/* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
** the result will be SQLITE_OK with 0 rows modified. */
- sqlite3_step(p->pUpdate);
- rc = sqlite3_reset(p->pUpdate);
+ sqlite3_step(pUp);
+ rc = sqlite3_reset(pUp);
if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
/* A NOTFOUND or DATA error. Search the table to see if it contains
@@ -4279,7 +4412,7 @@ static int sessionRetryConstraints(
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
rc = sessionChangesetStart(
- &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints
+ &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1
);
if( rc==SQLITE_OK ){
size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
@@ -4370,14 +4503,13 @@ static int sessionChangesetApply(
);
if( rc!=SQLITE_OK ) break;
+ sessionUpdateFree(&sApply);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_finalize(sApply.pDelete);
- sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pSelect);
sApply.db = db;
sApply.pDelete = 0;
- sApply.pUpdate = 0;
sApply.pInsert = 0;
sApply.pSelect = 0;
sApply.nCol = 0;
@@ -4405,7 +4537,7 @@ static int sessionChangesetApply(
int i;
sqlite3changeset_pk(pIter, &abPK, 0);
- rc = sessionTableInfo(
+ rc = sessionTableInfo(0,
db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
);
if( rc!=SQLITE_OK ) break;
@@ -4441,11 +4573,10 @@ static int sessionChangesetApply(
}
sApply.bStat1 = 1;
}else{
- if((rc = sessionSelectRow(db, zTab, &sApply))
- || (rc = sessionUpdateRow(db, zTab, &sApply))
- || (rc = sessionDeleteRow(db, zTab, &sApply))
- || (rc = sessionInsertRow(db, zTab, &sApply))
- ){
+ if( (rc = sessionSelectRow(db, zTab, &sApply))
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
+ || (rc = sessionInsertRow(db, zTab, &sApply))
+ ){
break;
}
sApply.bStat1 = 0;
@@ -4504,9 +4635,9 @@ static int sessionChangesetApply(
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
}
+ sessionUpdateFree(&sApply);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
- sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
@@ -4537,8 +4668,8 @@ int sqlite3changeset_apply_v2(
int flags
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
- int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse);
+ int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -4596,7 +4727,7 @@ int sqlite3changeset_apply_v2_strm(
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse);
+ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -4884,7 +5015,7 @@ static int sessionChangesetToHash(
}
}
- if( sessionGrowHash(pIter->bPatchset, pTab) ){
+ if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
break;
}
@@ -5071,7 +5202,7 @@ int sqlite3changegroup_output_strm(
*/
void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
- sessionDeleteTable(pGrp->pList);
+ sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp);
}
}
@@ -5217,7 +5348,7 @@ static void sessionAppendPartialUpdate(
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( pIter->abPK[i] || a2[0]==0 ){
- if( !pIter->abPK[i] ) bData = 1;
+ if( !pIter->abPK[i] && a1[0] ) bData = 1;
memcpy(pOut, a1, n1);
pOut += n1;
}else if( a2[0]!=0xFF ){
@@ -5472,7 +5603,7 @@ int sqlite3rebaser_rebase_strm(
*/
void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){
- sessionDeleteTable(p->grp.pList);
+ sessionDeleteTable(0, p->grp.pList);
sqlite3_free(p);
}
}
diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h
index 0bcd37d715..11bcbdf50f 100644
--- a/ext/session/sqlite3session.h
+++ b/ext/session/sqlite3session.h
@@ -453,6 +453,14 @@ int sqlite3session_patchset(
*/
int sqlite3session_isempty(sqlite3_session *pSession);
+/*
+** CAPI3REF: Query for the amount of heap memory used by a session object.
+**
+** This API returns the total amount of heap memory in bytes currently
+** used by the session object passed as the only argument.
+*/
+sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);
+
/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
diff --git a/ext/session/test_session.c b/ext/session/test_session.c
index 48ea552e1a..8d6447056a 100644
--- a/ext/session/test_session.c
+++ b/ext/session/test_session.c
@@ -243,6 +243,7 @@ static int SQLITE_TCLAPI test_session_cmd(
{ "patchset", 0, "", }, /* 7 */
{ "diff", 2, "FROMDB TBL" }, /* 8 */
{ "fullchangeset",0, "" }, /* 9 */
+ { "memory_used", 0, "", }, /* 9 */
{ 0 }
};
int iSub;
@@ -352,6 +353,12 @@ static int SQLITE_TCLAPI test_session_cmd(
}
break;
}
+
+ case 9: { /* memory_used */
+ sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
+ break;
+ }
}
return TCL_OK;
diff --git a/main.mk b/main.mk
index 2360cf6c50..6f615707d5 100644
--- a/main.mk
+++ b/main.mk
@@ -551,13 +551,13 @@ ST_OPT = -DSQLITE_THREADSAFE=0
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
-all: sqlite3.h libsqlite3.a sqlite3$(EXE)
+all: sqlite3.h sqlite3ext.h libsqlite3.a sqlite3$(EXE)
-libsqlite3.a: $(LIBOBJ)
+libsqlite3.a: sqlite3.h $(LIBOBJ)
$(AR) libsqlite3.a $(LIBOBJ)
$(RANLIB) libsqlite3.a
-sqlite3$(EXE): shell.c libsqlite3.a sqlite3.h
+sqlite3$(EXE): sqlite3.h libsqlite3.a shell.c
$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \
shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
@@ -825,13 +825,13 @@ fts3_unicode2.o: $(TOP)/ext/fts3/fts3_unicode2.c $(HDR) $(EXTHDR)
fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c
-fts5.o: fts5.c
+fts5.o: fts5.c sqlite3ext.h sqlite3.h
$(TCCX) -DSQLITE_CORE -c fts5.c
-json1.o: $(TOP)/ext/misc/json1.c
+json1.o: $(TOP)/ext/misc/json1.c sqlite3ext.h sqlite3.h
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c
-stmt.o: $(TOP)/ext/misc/stmt.c
+stmt.o: $(TOP)/ext/misc/stmt.c sqlite3ext.h sqlite3.h
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c
rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
@@ -955,7 +955,7 @@ fuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-
./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db
- valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
+ valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
valgrind ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db
# The veryquick.test TCL tests.
diff --git a/manifest b/manifest
index 13f9f00966..6cb00b0be5 100644
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Merge\srecent\strunk\senhancements\sinto\sthe\sbegin-concurrent\sbranch.
-D 2020-12-14T16:26:52.805
+C Merge\srecent\strunk\schanges\sinto\sthe\sbegin-concurrent\sbranch.
+D 2021-03-03T19:17:25.402
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in 0e88f5d095213a9ccd45c5bbd871c8ead498f886dff4493471fbf48b1f867f9d
+F Makefile.in 047036a560b7db51e7b7e459cd12cbe80e10ca5420b0ea5ddda023b44ab46466
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
F Makefile.msc ad07bbd645132533e1fd7164a03acfa9afecda378b3787c10f62ab4c7c45e6ea
F README.md 1514a365ffca3c138e00c5cc839906108a01011a6b082bad19b09781e3aa498a
@@ -34,12 +34,12 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
-F configure 7736c168f4fd717c8052e58428a89e645d15cf0e5a3d051e248aad859a3cb140 x
-F configure.ac efdb70036084ea637226e378a14f932e70c0df9f1bc846a24a909c0e1613524a
+F configure 91893a81f698778dda4d8fb24bfca606ded31ef02bcfbc2ab072d30fb67138d6 x
+F configure.ac 412b65c6107e41c098ad7f5f2e6a3f74ac02ffc6e92b9a6264b9f1060c235a04
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
F doc/begin_concurrent.md e694a2a1c623833e7de8b688e81ec30fd4439a78d6b74be61bce0b1e8f590470
-F doc/lemon.html c5d8ba85ac1daef7be8c2d389899480eb62451ff5c09b0c28ff8157bb8770746
+F doc/lemon.html 1bb72ece6271df0d901d233551dd985f2c6ba30d09382cf2d321ed951ab57491
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56
@@ -103,7 +103,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
F ext/fts3/fts3_unicode.c 4b9af6151c29b35ed09574937083cece7c31e911f69615e168a39677569b684d
F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
-F ext/fts3/fts3_write.c 723ed1b11ed46ad1b3a23c0d69fa39e77986783a82d5711bf87a5ce29e0a3b52
+F ext/fts3/fts3_write.c a5159accfd88f85fd3fc2298286d7a9427a02d1ea9a52b7c79730cff7a0bc03f
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
@@ -118,9 +118,9 @@ F ext/fts5/fts5Int.h 26c74dd5776f798436fbf604a0bf0e8de263b35b5060b05c15f9085845d
F ext/fts5/fts5_aux.c f558e1fb9f0f86a4f7489e258c162e1f947de5ff2709087fbb465fddb7092f98
F ext/fts5/fts5_buffer.c 5a5fe0159752c0fb0a5a93c722e9db2662822709490769d482b76a6dc8aaca70
F ext/fts5/fts5_config.c be54f44fca491e96c6923a4b9a736f2da2b13811600eb6e38d1bcc91c4ea2e61
-F ext/fts5/fts5_expr.c e527e3a7410393075598cec544e3831798a8c88b3e8878e2cfb7cb147113e925
+F ext/fts5/fts5_expr.c 016bd06030679bd31b0f07ef87d62c42031e5da25cb3174a84e5b0f6ef4b47b0
F ext/fts5/fts5_hash.c 1aa93c9b5f461afba66701ee226297dc78402b3bdde81e90a10de5fe3df14959
-F ext/fts5/fts5_index.c 7be3a7dcf4458a2d58a1c6ae0290921c9df226bff7eb9bc82f14e667b27aeb20
+F ext/fts5/fts5_index.c acbe3ccd36f5a7373009dfff02a11c4a817af73bc3ef8c340f2105d7d073f8b4
F ext/fts5/fts5_main.c b4e4931c7fcc9acfa0c3b8b5e5e80b5b424b8d9207aae3a22b674bd35ccf149d
F ext/fts5/fts5_storage.c 58ba71e6cd3d43a5735815e7956ee167babb4d2cbfe206905174792af4d09d75
F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95
@@ -161,7 +161,7 @@ F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c0
F ext/fts5/test/fts5content.test 213506436fb2c87567b8e31f6d43ab30aab99354cec74ed679f22aad0cdbf283
F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519deb43581cb17a57ebe
F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f
-F ext/fts5/test/fts5corrupt3.test 1c26a651ea7e52fd69d54436fe4f02f6dd1268bc8b48ab851c7e1d374aa242b9
+F ext/fts5/test/fts5corrupt3.test 5df97f353102f0078d0bb418d620652d03460ee1cceb2992a3b5ee6fb619b24e
F ext/fts5/test/fts5corrupt4.test f4c08e2182a48d8b70975fd869ee5391855c06d8a0ff87b6a2529e7c5a88a1d3
F ext/fts5/test/fts5delete.test 619295b20dbc1d840b403ee07c878f52378849c3c02e44f2ee143b3e978a0aa7
F ext/fts5/test/fts5detail.test 31b240dbf6d44ac3507e2f8b65f29fdc12465ffd531212378c7ce1066766f54e
@@ -292,7 +292,7 @@ F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0e
F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
F ext/misc/carray.c b75a0f207391038bf1540d3372f482a95c3613511c7c474db51ede1196321c7c
F ext/misc/carray.h de74ac70b2338f416723f7d538026e8ec0b7f1d388319f8f140c9a4d7677f02e
-F ext/misc/cksumvfs.c 688a59d6c5dc7e7e0aba09654d8cbeeb7d04bad8cf57902df86aa06c2f723ff4
+F ext/misc/cksumvfs.c 8dc4e1b718e374bed3a9b5c0a08da2922438002e33267311697768e06217b983
F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c85c243
F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9
F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9
@@ -305,7 +305,7 @@ F ext/misc/explain.c 0086fab288d4352ea638cf40ac382aad3b0dc5e845a1ea829a694c015fd
F ext/misc/fileio.c 9b69e25da3b51d4a1d905a464ccb96709792ad627a742ba09215bc0d1447e7bd
F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5
F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d
-F ext/misc/ieee754.c 5c7ca326361c7368f95f5743972eade3b8b24f60359ed7cba4706668a5682896
+F ext/misc/ieee754.c cd6ab89f85fda8a020559b3f4d03001a8a62dd856beda5af3f558621d12be913
F ext/misc/json1.c f31e89171f932d1821c91f10d2cb4979fc0447030030a8bce70420cd43d074c0
F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d
F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b
@@ -322,7 +322,7 @@ F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d385
F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
F ext/misc/series.c c6bd5d249e5199a1b55aeee4d0e6576ff3a68702fc475dbd64503a32903516c7
F ext/misc/sha1.c c8f2253c8792ffab9517695ea7d88c079f0395a5505eefef5c8198fe184ed5ac
-F ext/misc/shathree.c 5398f945a30b4792c757ab16b03cf54ce3b542fe933bbd43e2d6d40c5443b12e
+F ext/misc/shathree.c e984f31731de4cf302a0386be5fe664580f63d8204c47b9b41cc4b997745f9ec
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
@@ -340,7 +340,7 @@ F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
F ext/misc/wholenumber.c 520f34c3099e5b7d546f13708607dc2fa173c46b68952eecf0d19cd675fec85e
F ext/misc/zipfile.c e35e035bc2765b1ccdcb15f9815c2112843fcbc8f36aa071f0e5935df7072228
F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64
-F ext/rbu/rbu.c 8681f6157db6adc82c34af24b14ea8a3be0146ad2a3b6c1d5da6cb8a5796c8ce
+F ext/rbu/rbu.c b880ca5cb857d6d6f52e72eb7397813058ef48c78c5402cd04ff2b6b5437f622
F ext/rbu/rbu1.test 221d9c18a5e600ac9ac6b1810d99d9f99163a7909ba61597876ab6e4d4beb3d6
F ext/rbu/rbu10.test 0a201c32202143f23c81c0144503da339786fc20acb7a2fda11601b65659f314
F ext/rbu/rbu11.test 5c834cf491086b45e071eabf71f708febc143e86a384a92de69e0b1a4cace144
@@ -380,7 +380,7 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
F ext/rbu/rbuvacuum2.test b8e5b51dc8b2c0153373d024c0936be3f66f9234acbd6d0baab0869d56b14e6b
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
-F ext/rbu/sqlite3rbu.c 05c457c27e9340c944f34e850871a915a6b5ee1d823f7a0bb2b482ac6b1e1464
+F ext/rbu/sqlite3rbu.c e6531884442b72f9e0ba47036fb5c4641ea817ff659a642b3984c10b8535b0fd
F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
@@ -431,7 +431,7 @@ F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a
F ext/session/session1.test 0b2f88995832ea040ae8e83a1ad4afa99c00b85c779d213da73a95ea4113233e
-F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
+F ext/session/session2.test 7f53d755d921e0baf815c4258348e0ed460dfd8a772351bca5ad3ccbb1dc786e
F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
@@ -453,21 +453,23 @@ F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
F ext/session/sessionfault2.test dd593f80b6b4786f7adfe83c5939620bc505559770cc181332da26f29cddd7bb
F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2c52219819bc105a25
+F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09
+F ext/session/sessionnoop.test a9366a36a95ef85f8a3687856ebef46983df399541174cb1ede2ee53b8011bc7
F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2f05e82363b9142810
F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5
-F ext/session/sessionwor.test 67b5ab91d4f93ce65ff1f58240ac5ddf73f8670facc1ffa49cef56293d52818d
+F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3changebatch.c d5553b79e012ee2cb06c0a96bdf9dfe19e66354390ea0036cc46c4953142d517
F ext/session/sqlite3changebatch.h e72016998c9a22d439ddfd547b69e1ebac810c24
-F ext/session/sqlite3session.c fe5fab3c63b734a647784596e44f4ee778e7c7b12ce7aaa2cf4ed445d6c5763a
-F ext/session/sqlite3session.h ba1b5dc7b159a76d9300228719e1248a4cb5ba7ee6ebd4c19a32fdc23891d22a
-F ext/session/test_session.c 1e4790384606226f8e63a6ebebb39962721d0363c8192c230de1e4c31c410c1b
+F ext/session/sqlite3session.c 5eb3a3cb490912376c5720d24eb256753e4a29a2a62f39ac708a084d854b4fea
+F ext/session/sqlite3session.h 23c5bcbd3f67e8056b8a95d6248a289c4c9299b000e4caeac9b7074f8c32a867
+F ext/session/test_session.c 602e68c559a8b39e89a76ffd30818594d9691c1cdb1d89df15802c56defde4e7
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 2b044fdc896a5e1f494ee5730d354cfa87007677d382824be2a4dc69b1f33e01
+F main.mk 4decfbd7d4f827b8bf9694770e4ae7ddd9f3c5e9aa4fa58e6f85f91a9c33eccd
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -479,37 +481,37 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
-F src/alter.c b6de60d59419e34f5b48ff2b21fe0f9bb66fc714e5545a6ac790ac7a0c46548c
+F src/alter.c d5fd529509880eade9ea59ddb24a56e9fe0579ee7f2e9e18bac62b7bd05b3a10
F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c
-F src/attach.c 0f497c15c4cfe3bdcb214f0dbdbbb6c5ed7e8a9308ac445c7959f5e5780437a9
-F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06
+F src/attach.c 9cbe761e464025694df8e6f6ee4d9f41432c3a255ca9443ccbb4130eeb87cf72
+F src/auth.c 08954fdc4cc2da5264ba5b75cfd90b67a6fc7d1710a02ccf917c38eadec77853
F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d
F src/bitvec.c 8433d9e98dd6f2ea3286e0d2fe5d65de1bfc18a706486eb2026b01be066b5806
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
-F src/btree.c 31a23d1e939172b2a1e972859d3a26712ecf8bf718c638ab893e9618d351f7a0
+F src/btree.c 30dd658db182dc3138d369595fac9e992916f53d5d352c4f868f4ef95a4ed2e6
F src/btree.h 23ceeca6baee45a9b9d0bff86bdc1924d084f99cdddcd1df8a08fea03f007e60
F src/btreeInt.h 0ef4c1cba8eafcb54f508c3a6cb3aedbe961ee03e4c147d28252f1e5b5357033
-F src/build.c 4c44cee793d16c653986cf00c32c36cf0cbfe4932a319cc7d5c0f10a2801151c
+F src/build.c 3c764575a87097ad799d934c626514a14871acb53f4a388c3d2edfb9bb78143f
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
-F src/ctime.c 0bcc6a908779a520be534fd985b2185dfd457588b6798d0bcbf37755a044b7c3
+F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410
F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
-F src/delete.c 927cf8f900583e79aca8f1a321979e0a8f053babd9a690b44b38f79de2cc09fe
-F src/expr.c 0d196ed5a2ebf96be7e8df88add4fabfad0dce16c0fed81a4b8f6a26e259797f
+F src/delete.c 73f57a9a183532c344a3135cf8f2a5589376e39183e0b5f562d6b61b2af0f4d8
+F src/expr.c 6793c836aff149b14011ad546ae1648a18573779ee78f5a7d375f2a3047e8c8e
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
-F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
-F src/func.c 670035a3d6d585d78d2be4908639b8745a9c121de0bdfc0976aa46a16874637e
+F src/fkey.c e9063648396c58778f77583a678342fe4a9bc82436bf23c5f9f444f2df0fdaa4
+F src/func.c e701e9a1cbc2e1814d381ce781925c2e8776cdf994e7c17f53578ec3cc7af40a
F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 7300982986b0aae32382ce57438998b92efa64e9a7169378e83c1c5d0e2ecdb3
+F src/insert.c 8942baede303a54ba3b6d06200d5b74c9bc25ababec8a55823e06309748cd4a3
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
-F src/main.c 1407c9a7c31d97380ff9cb77bf3901904fbabee4f53a8297ff7e163961277098
+F src/main.c f885f4bf754b4d2add47e6e2ad5a2ead591e9718da7170858bd7c2a5c2ea5643
F src/malloc.c c1af4ac5a463648cd2953fd4ac679b3ba9022ce5ec794a60806150ad69dfd33a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -517,7 +519,7 @@ F src/mem2.c b93b8762ab999a29ae7751532dadf0a1ac78040308a5fb1d17fcc365171d67eb
F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memdb.c ab0632d42407e866d2b616bd19d4211ac0ad1b430f04c4e187d60005b8700b98
-F src/memjournal.c 90b2ca7e2f465d57c16b69d15a9f3e3294af61088eb4938f2f7664d5ac50f813
+F src/memjournal.c 431c70a111223a8a6e2e7e9f014afc6c88d818d357d866afc563195f2277d50e
F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8
F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25
F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a
@@ -532,25 +534,25 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c 9b8d6679300ee39b891c60a70f64c86bb89bd04862c1fd697bdf959867c74cbb
F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
-F src/pager.c 095a0ac00a7b74cb5042678e478ab54dbbc67a20ee13b464eebc3f8c606d8b01
+F src/pager.c 0122015aef06338c61d0b94df77bce4a670c0376dd88ec27260d0ba319d2f869
F src/pager.h fdc30693c403aa9b4293d0b126346db7e450f8f0e5d2c1bb8355acb1cb8da6fd
-F src/parse.y 382eb17b4571838ae0cfb4b402de19de5a59ed04f5e2502d72e8e98b39456b12
+F src/parse.y f5376ab515dc1c8cfe6fc884be2eb46726cddaf54eca17346b276757064d250f
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
-F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
+F src/pcache1.c 388304fd2d91c39591080b5e0f3c62cfba87db20370e7e0554062bfb29740e9f
F src/pragma.c 6daaaecc26a4b09481d21722525b079ce756751a43a79cc1d8f122d686806193
F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
-F src/prepare.c 270170a239c0f66bd3c228f373afe24447c2614a6829ae22080babc64f241931
-F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
+F src/prepare.c f634a9e799a6b1c136d8ee12479cffa22862bfb807d307b1db406aa0cdb042a5
+F src/printf.c 2b03a80d7c11bb422115dca175a18bf430e9c9dbaa0eee63b758f0c022f8f34f
F src/random.c c984d8426d76752dbd9ff7983e770731ae5e8f4ede84a203df9878d5e8798f11
-F src/resolve.c 1948a92ca9eab776632816b97e57c61d933474a78aad4f4ef835c916a83dbb1c
+F src/resolve.c 889469e6980181ce77ee8ab3fc84bd52ed6c1c3577fd102d52623d66cc65a3d0
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
-F src/select.c 4ce2b008a733ab94ef59b5b728755c18dd6a130b8d507e13342aca6193384b9c
-F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce0090
-F src/sqlite.h.in d9780048bc9181c66a212a9b4b83605a51f7d561eff2e0858bbeae46464b6023
+F src/select.c 78afdb6f14b492130b3e311285a2ad432c734f492a0c12e633829536f2b0b061
+F src/shell.c.in 844417f84df1f6c4fce1c815629a888cfdcf219e86513e9c332bbcc38832f477
+F src/sqlite.h.in ef0e051b00408e9c8a52c94913515c000793a0ccd81ee39579f1a2305e5f7ff9
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
-F src/sqliteInt.h 4c5c1eebe5c4db800c5470326033f25dba4951ab1c886d8d1e2b03b6d8aaa28f
+F src/sqliteInt.h 36c6e44690a6227efd37b7ad889339049d8795f0399f395d55a789e95b814142
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -609,34 +611,34 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
-F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97f
-F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
-F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10
-F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580
+F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9
+F src/treeview.c c6260e1fa5f41c361b2409edc9b0050bcaef5bc4d6abc467fbc45f0d7ccf3d84
+F src/trigger.c 861c3ec2c5b0fc830bdf82470454a9324fad70cbaa96d2e208fb54577c9e8d28
+F src/update.c 0f5a61f0787199983530a33f6fffe4f52742f35fcdf6ccfad1078b1a8bc17723
F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
-F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002
+F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048
F src/vacuum.c 9d6ac090ff36d80f0a2c5bfc63994b714c464bfc44efa5056c457a6a4163dc99
-F src/vdbe.c 8b21e2d0a125a72279585c28483829f76dbbae23c91e53ab0b5b22a407eec647
-F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
-F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
-F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
-F src/vdbeaux.c 0aaec35c90956fb03238e9ce3981e7111a53ff99eb161ef79fa55ae14fedcf7c
+F src/vdbe.c 025b55ebf3927705300557b71415884742e2f5276d0ae12b2e038ac548a33cd4
+F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
+F src/vdbeInt.h 3df118924e1711f1bbc8e30c46260d0ab6c3b029b32dd411f789111f76434f3c
+F src/vdbeapi.c 4a43e303ec3354c785f453e881521969378e85628278ab74ba4a9df790c0d93b
+F src/vdbeaux.c 3ca9ea12fc429b680a1a4748fe7c2dc4eee8cf0ba70c66c04ebca4fb7b652c73
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
-F src/vdbetrace.c fa3bf238002f0bbbdfb66cc8afb0cea284ff9f148d6439bc1f6f2b4c3b7143f0
+F src/vdbetrace.c 666c6fd9f1b62be6999e072a45b913e3c2c3518bc60dfd4d54fe304130acb724
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
-F src/vtab.c 5f5fc793092f53bbdfde296c50f563fb7bda58cf48e9cf6a8bdfbc5abd409845
+F src/vtab.c 032a0165c147fda16927e6a3230e90c068d4af93f887ce94e26f678fe48e5e4c
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c c9dfe3054c82aaa8d1dea91b56afa9397b54e55d5fb1dcbac32a4a7f6231bdc9
F src/wal.h 7ffe787437f20a098af347011967a6d3bb8e5c3dc645e6be59eff44d2b2c5297
-F src/walker.c 3df26a33dc4f54e8771600fb7fdebe1ece0896c2ad68c30ab40b017aa4395049
-F src/where.c 2d593bfc6fa24e53dfe7c99bd327af687f8502e5f0e0299dd2c0f503b133f0bb
-F src/whereInt.h 9a3f577619f07700d16d89eeb2f3d94d6b7ed7f109c2dacf0ce8844921549506
-F src/wherecode.c a3a1aff30fe99a818d8e7c607980f033f40c68d890e03ed25838b9dbb7908bee
-F src/whereexpr.c 3a463e156ea388083c501502229c2c7f4f5c6b5330ea59bdf40d6eb6e155a25f
-F src/window.c edd6f5e25a1e8f2b6f5305b7f5f7da7bb35f07f0d432b255b1d4c2fcab4205aa
+F src/walker.c d42d6c80ea363ef689a462e65eefcfe87deab924c50de5baa37ecb6af7d7ddaa
+F src/where.c 10d06b16670a1d2a992d52a9f08e49426d38a08fb0a7ae5f7f62fd023d560e1e
+F src/whereInt.h 446e5e8018f83358ef917cf32d8e6a86dc8430113d0b17e720f1839d3faa44c4
+F src/wherecode.c e57a8690311a75d06e723e8d379f9831de04aba300e07174d236e32a7f9c7a13
+F src/whereexpr.c 53452fe2fb07be2f4cb17f55cc721416fae0092c00717f106faf289c990b6494
+F src/window.c fdf01316f6cecf060378aa1713a29e527ab683823ba7d15b8978ec70165e8bdb
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7
@@ -649,14 +651,18 @@ F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807
F test/alter3.test e487958dec7932453e0b83baf21d6b1e71d5e7d9a55bc20eadfa62a51ddffc29
F test/alter4.test dfd6086faf461b27ca2d2999848dcd207edf23352fc1592d0005c0844f3f08cf
F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
-F test/alterauth2.test c0a1ddf5b93d93cb0d15ba7acaf0c5c6fb515bbe861ede75b2d3fabad33b6499
-F test/altercol.test 1d6a6fe698b81e626baea4881f5717f9bc53d7d07f1cd23ee7ad1b931f117ddf
-F test/alterlegacy.test 82022721ce0de29cedc9a7af63bc9fcc078b0ee000f8283b4b6ea9c3eab2f44b
+F test/alterauth2.test 794ac5cef251819fe364b4fe20f12f86e9c5d68070513c7fd26c17cb244c89af
+F test/altercol.test 65eef562f0eea7a1f5ddd4a140c4274c2bfc5712bb2ab2096f738852b0efce86
+F test/altercorrupt.test 584d707a80e106952d6382790c8919bcf9f0db678ed3a1c09fd98b7f9d1d3a10
+F test/alterdropcol.test baad37ff9b07078ea02dcc33dbfb82bde655f3eee5c453e218f69501c36f02ba
+F test/alterdropcol2.test 3948c805ca52f4621051b35968c18c09d107eb117e2b656c78cee3b2870650c0
+F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b
+F test/altermalloc3.test 2c7bbd8cf3e9c4a91e28675bb62bcc2ef70f227967fa74349f03d9f4642f0615
F test/altertab.test 6d7bbac2c4a6ef71b775094a3298fa3a92274d95034ee23157ffba92768e47e6
F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b
-F test/altertab3.test 1db384eb85b4a30b0b332842f5c596b0dc0126f7c61959be3f85ae8b1c271d9a
+F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7
F test/analyze3.test fca2a9de0017becfdcc201647f03b1cfd5ba0e7b5b5c852936e4ec62780cde49
@@ -701,7 +707,7 @@ F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e
F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d
F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989
-F test/backup2.test 1fd1ad8c5b3d2d5b9c0cce4143a4fc610d51ddc6ae16a7a122973d43e6b50bbd
+F test/backup2.test 8facb54df1388419d34b362ab1f7e233310ff3a3af64e8ad5ec47ba3c2bbe5cf
F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32
F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4
F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
@@ -771,6 +777,7 @@ F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 87ad5458bb8709312dac0d6755fd30e8e4ca83298d0a9ef6e5c24277a3c3390e
+F test/columncount.test eff33d402a7b0fde0a52a1920d238af200ca573327021e0ce3b7e5688de41449
F test/concfault.test e5370cd686f601a01909377cac3bbf13dac56d39dd4ad6b04ccbec9eeeaccb18
F test/concurrent.test a0248ec6e3e79a5948453649cf86b5b359175cba55ea636b15426d6f0fa6c3da
F test/concurrent2.test de748c7dd749c77e2af2c4b914b9b09a28ac09608042ca498c0251dc6f46aa1a
@@ -806,6 +813,7 @@ F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af
F test/corruptL.test 22589f503602cc5984e80f27f46c4de2134f24f1515ba2440513c377cb692258
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
+F test/corruptN.test 781c5f26a2d8918f03d45ac4968a738031eeb113a4b153c7588756d9b09c7b04
F test/cost.test 1d156ce9858780a966c062694687afe0343a0ed12d081d071fb57027e726bafc
F test/count.test e0699a15712bc2a4679d60e408921c2cce7f6365a30340e790c98e0f334a9c77
F test/countofview.test e17d6e6688cf74f22783c9ec6e788c0790ee4fbbaee713affd00b1ac0bb39b86
@@ -814,7 +822,7 @@ F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418
F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc
-F test/crash5.test f14ff37eddc41991be4eb63568f86caa306fd9962a0ae3750db8836777bb7aae
+F test/crash5.test 4aa55e7ac3c4bc511873e457aa65d2827d52da9b51e061511899dadcfe22b1e8
F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba
F test/crash7.test 1a194c4900a255258cf94b7fcbfd29536db572df
F test/crash8.test 64366e459c28dd62edfb7ad87253a409c7533b92d16fcc479a6a8131bdcc3100
@@ -888,6 +896,8 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
+F test/exists2.test 3e5726d6a67ebd4bd3466db58be424c09156c1f276c7594abb260cbf6ad494d3
+F test/existsfault.test 74f7edc713f5a335e7ff47adf503067bf05c6f8630f88b2a19c24f0fa5486ab8
F test/expr.test 26cd01e8485bc48c8aa6a1add598e9ce1e706b4eb4f3f554e0b0223022e8c2cf
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
@@ -973,7 +983,7 @@ F test/fts3conf.test c84bbaec81281c1788aa545ac6e78a6bd6cde2bdbbce2da261690e3659f
F test/fts3corrupt.test 79a32ffdcd5254e2f7fa121d9656e61949ad049c3c6554229911b7ceac37c9c6
F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cdb764b491f
-F test/fts3corrupt4.test e4662d37f02248301d8b58778eac380663e09a17a38dd5d6bb5ea4c927b9a575
+F test/fts3corrupt4.test b71512ec391d39da96d60d01959e4e9f20d4237a964a94abcf5f5a2ad28378c1
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
F test/fts3corrupt6.test b6c55218b704b0cef224b284c756f9c55d0afd0b3c3837618bffeaa8c31e0d8e
F test/fts3cov.test 7eacdbefd756cfa4dc2241974e3db2834e9b372ca215880e00032222f32194cf
@@ -1030,9 +1040,9 @@ F test/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309
F test/fts4onepass.test d69ddc4ee3415e40b0c5d1d0408488a87614d4f63ba9c44f3e52db541d6b7cc7
F test/fts4opt.test 0fd0cc84000743ff2a883b9b84b4a5be07249f0ba790c8848a757164cdd46b2a
F test/fts4record.test a48508f69a84c9287c8019d3a1ae712f5730d8335ffaf8e2101e691d078950bb
-F test/fts4rename.test 15fd9985c2bce6dea20da2245b22029ec89bd4710ed317c4c53abbe3cfd0c880
+F test/fts4rename.test 2e0565ffd92b2c51f1a757df0b8f2ca30537197106fec09e943919801d173692
F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f757380429
-F test/fts4unicode.test ceca76422abc251818cb25dabe33d3c3970da5f7c90e1540f190824e6b3a7c95
+F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9
F test/fts4upfrom.test 8df5acb6e10ad73f393d1add082b042ab1db72567888847d098152121e507b34
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test f673822636fb8ed618dd2b80230d16e495d19c8f2e2e7d6c22e93e2b3de097ad
@@ -1041,7 +1051,7 @@ F test/func3.test 2bb0f31ab7baaed690b962a88544d7be6b34fa389364bc36a44e441ed3e3f1
F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31
F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a
F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c
-F test/func7.test bb05a77daedf0e3f8764f323a49bc3b8d98f280a0bc6a370387117f4596bde05
+F test/func7.test b9e2a1a30a8562b00841b4a21a5d2d81754fa3ab99275fd71fd5279287b44b1c
F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa
F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
@@ -1049,7 +1059,7 @@ F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c
F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
-F test/fuzzcheck.c 5f81f2cc65f13068620245f2e2c6059657d3b26be476df379ae2da539f17676d
+F test/fuzzcheck.c 95427205d6cedb8fdb95bf7738e95785c790febf782d9c1e6067baaa1adc871d
F test/fuzzdata1.db d36e88741b4f23bcbaaf55b006290669d03c6c891cf13c7b3a53bc1b097b693f
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
@@ -1057,17 +1067,17 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4
F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5
F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7
F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2
-F test/fuzzdata8.db f8451a1fd38efbea8c1a7cdf5d02259c4702446a9fabf566becd306b64a50236
+F test/fuzzdata8.db c8325de6fbdd24d030cd3a01384a2ff325dda5d5e3ff5d531a26ada3d9d7e010
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
-F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
+F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc
F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c
F test/gencol1.test b05e6c5edb9b10d48efb634ed07342441bddc89d225043e17095c36e567521a0
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
-F test/having.test e4098a4b8962f9596035c3b87a8928a10648acc509f1bb8d6f96413bbf79a1b3
+F test/having.test a89236dd8d55aa50c4805f82ac9daf64d477a44d712d8209c118978d0ca21ec9
F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
-F test/hook.test e97382e68e4379838e888756d653afd159f5f14780315ff97b70360d3d8485bc
+F test/hook.test fa54fa8afc842ae375f10c1f9fc0014fa59789052fc30c9eae19811fa3afa009
F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
F test/icu.test 716a6b89fbabe5cc63e0cd4c260befb08fd7b9d761f04d43669233292f0753b1
F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
@@ -1075,7 +1085,7 @@ F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
F test/in.test 688ed2011d922d83141a45af431601738674a4c0bdde34b6351f688b82a169b3
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
-F test/in4.test 65460600d48933adba4283c6ebd089aae173d16136ab9d01f74c89089090c5a5
+F test/in4.test 64ac9c767ac5af562f066a40163d4202f8fa3be05d264ec65d6258e74606b30c
F test/in5.test b32ce7f4a93f44c5dee94af16886d922cc16ebe33c8e1765c73d4049d0f4b40f
F test/in6.test 8562d0945195cab3cc4ab3794e9118e72cb44c43f785c2b04d48a9d06ca6b4ec
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
@@ -1146,7 +1156,7 @@ F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/lemon-test01.y 58b764610fd934e189ffbb0bbfa33d171b9cb06019b55bdc04d090d6767e11d7
-F test/like.test 47b81d5de2ff19d996d49a65d50ec9754246aacbe0e950b48d186d9d8171eaf0
+F test/like.test 0b7b4765ca59d95a1f92dfab9e4d810c9fb8280b5edd6332a01340a20db9e0ed
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/like3.test 03d1bdf848483b78d2cfd1db283d75c4ec2e37c8b8eccc006813f3978d78fbbd
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
@@ -1229,6 +1239,7 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
+F test/notnull2.test 965a893619751255e59c911a8c58504b3174a3788b1458b7c7365b232209711b
F test/null.test b7ff206a1c60fe01aa2abd33ef9ea83c93727d993ca8a613de86e925c9f2bc6f
F test/nulls1.test 82c5bc33148405f21205865abf13c786084438d573a4ac4e87e11b6091cde526
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
@@ -1264,7 +1275,7 @@ F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 661a4325a5717957a77836910ee164ba26594a502d7a3df0e1ae7b9cba829c5d
+F test/permutations.test e02c8a58efca31dfadbd2a0d168e26a542664d4ff31a3d819b80cd55f974c27c
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 50b91bedea9324d3ab48e793f908ee7d2c7dcf84bfa2281e792838be59641ec8
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@@ -1293,9 +1304,10 @@ F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
F test/releasetest.tcl fb76d8fcc95ac29d6356cd9e52b726ab9e43a24082897618dfbcb7c2b0049153 x
-F test/releasetest_data.tcl b9cb30360759b80d92d4ea86b84ebfd8035b97f9078a482deb3cf9d0b2442655
+F test/releasetest_data.tcl d60628b6a04891e97cc4f5fbce08abe13c5b74cb23dcca5ac24800471a3bc6db
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
+F test/returning1.test 684e1c73d961422a7376c932fcdd6dacf02bad21d12f749cfe8c19991ef379f6
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
@@ -1303,7 +1315,7 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test bfbd7b97d9267660be3c8f28507c4ed7f205196b8877c0db42df347c2e8845e3
-F test/rowvalue.test 8964f95b253d3b5cc8dc1cfd0cdb7529bce3ecc6b6259e23c5f829f80f4d51cd
+F test/rowvalue.test b5436c767394a3f8fa5e7e474b2114ba430fdab34a5c8573c1b6256756534565
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
F test/rowvalue4.test 02e35f7762371c2f57ebd856aa056eac56cb27ef7715a0bb31eac1895a745356
@@ -1333,7 +1345,7 @@ F test/schema6.test e4bd1f23d368695eb9e7b51ef6e02ca0642ea2ab4a52579959826b5e7dce
F test/schemafault.test 1936bceca55ac82c5efbcc9fc91a1933e45c8d1e1d106b9a7e56c972a5a2a51e
F test/securedel.test 2f70b2449186a1921bd01ec9da407fbfa98c3a7a5521854c300c194b2ff09384
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
-F test/select1.test 0ed936740777f52858b6607f39ffac4b2b63b8fc7edf3ab2ebad3c3553ceecee
+F test/select1.test 3d23f66bf9ba77570acfe2ca5f1540ece17037cc64ab1a00efec9758ac29c268
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test c49fbb758903f3718e2de5aa4655eda4838131cbea24a86db908f8b6889aa68c
F test/select4.test e8a2502e3623f3058871030599a48abb35789d2244d5b380ecf3696873fdd4a4
@@ -1344,7 +1356,7 @@ F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
F test/selectA.test 68de52409e45a3313d00b8461b48bef4fb729faf36ade9067a994eae55cc86f4
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
-F test/selectC.test e25243f8ca503e06f252eb0218976d07cfeceac3
+F test/selectC.test fec14c9015ed4ec941508bbc144f30b42e40ac34a4bb33001450369865dd0b75
F test/selectD.test fc20452847a01775710090383cfb4423275d2f745fed61f34fbf37573ac0d214
F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
@@ -1365,10 +1377,10 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
-F test/shell1.test 0ba53c72545de142e1b76fa793ee33293134aa02abb9b50b35398670481ea661
+F test/shell1.test 56a7358a2a05e850e9e4aa24629db9c8975e8038dbe8debd2d95be22a5f03612
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
-F test/shell4.test 1c6aef11daaa2d6830acaba3ac9cbec93fbc1c3d5530743a637f39b3987d08ce
+F test/shell4.test 3ed6c4b42fd695efcbc25d69ef759dbb15855ca8e52ba6c5ee076f8b435f48be
F test/shell5.test 84a30b55722a95a5b72989e691c469a999ca7591e7aa00b7fabc783ea5c9a6fe
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
@@ -1414,6 +1426,7 @@ F test/spellfix4.test 51c7c26514ade169855c66bcf130bd5acfb4d7fd090cc624645ab275ae
F test/sqldiff1.test 28cd737cf1b0078b1ec1bbf425e674c47785835e
F test/sqllimits1.test 264f4b0f941800ba139d25e33ee919c5d95fea06dfbe8ac291d6811a30984ca5
F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a
+F test/startup.c 1beb5ca66fcc0fce95c3444db9d1674f90fc605499a574ae2434dcfc10d22805
F test/stat.test 15a3106eddedfc882f64bc09f237b4169be4b92dd57c93031b8ff8b13af3e7c5
F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75
@@ -1447,7 +1460,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
-F test/tester.tcl 754521f0db534d51ab504b2d14fe0bdca1f1c15de731ceb8ee5bfd78372a2a5f
+F test/tester.tcl 19d2a19a6dd55a2b4e2b943963959a05a2c088495dd5f5274b04e0494ce86d66
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1608,7 +1621,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/tpch01.test 7c4eb8cdd79c568f46d344b3e789c9fdb8a766d112871352704861f3fca32a2a
F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
-F test/trace3.test 1dff966888773ff1bfea01c080caf15417892b3f998408fe920c4791f7337144
+F test/trace3.test ae2004df24b585fed9046cc0bae4601762bc6fc4aa321d475f1350bba5047f31
F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f439
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
@@ -1626,7 +1639,7 @@ F test/triggerA.test 837be862d8721f903dba3f3ceff05b32e0bee5214cf6ea3da5fadf12d36
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 29f5a28d0fe39e6e2c01f6e1f53f08c0955170ae10a63ad023e33cb0a1682a51
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
-F test/triggerE.test ede2e4bce4ba802337bd69d39447fa04a938e06d84a8bfc53c76850fc36ed86d
+F test/triggerE.test 612969cb57a4ef792059ad6d01af0117e1ae862c283753ffcc9a6428642b22ee
F test/triggerF.test 5d76f0a8c428ff87a4d5ed52da06f6096a2c787a1e21b846111dfac4123de3ad
F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505233e9
F test/triggerupfrom.test d25961fa70a99b6736193da7b49a36d8c1d28d56188f0be6406d4366315cd6e4
@@ -1641,6 +1654,9 @@ F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
+F test/unionall.test 369dac51f4e7b94442b054d3d7f2e6755cd6994274718228878e3bd47c425f6d
+F test/unionall2.test c9a62db63350bcbce3a7bec50dd8c5410f08be33f8af435473756286d4657215
+F test/unionallfault.test 652bfbb630e6c43135965dc1e8f0a9a791da83aec885d626a632fe1909c56f73
F test/unionvtab.test e1704ab1b4c1bb3ffc9da4681f8e85a0b909fd80b937984fc94b27415ac8e5a4
F test/unionvtabfault.test e8759f3d14fb938ce9657e2342db34aeac0fb9bc1692b0d1ebb0069630151d06
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
@@ -1755,10 +1771,10 @@ F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
F test/whereI.test a2874062140ed4aba9ffae76e6190a3df6fc73d1373fdfa8fd632945082a5364
F test/whereJ.test 88287550f6ee604422403b053455b1ad894eeaa5c35d348532dfa1439286cb9a
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
-F test/whereL.test e05cedc9389c6f09ad55bd5999a3fddccebec90672fb989433c145dcdaf26996
+F test/whereL.test 1afe47227f093dc0547236491fb37529b7be9724b8575925a321001b80e6a23a
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
-F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
+F test/wherelimit.test daa0fd9122c5745cc459ec40b8d3c16ce13ce8382b5b847e7cfff4b871260cbf
F test/wherelimit2.test 657a3f24aadee62d058c5091ea682dc4af4b95ffe32f137155be49799a58e721
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
@@ -1782,12 +1798,14 @@ F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af
F test/windowB.test 7a983ea1cc1cf72be7f378e4b32f6cb2d73014c5cd8b25aaee825164cd4269e5
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
-F test/windowfault.test 72375ae71031eabf96bc88d0af128c8628a091ddc99b5a394e848b3df5fc17ad
+F test/windowfault.test d543d46571b32d19f198cb04b6505747fabf3cc369970daae47074ee793612be
+F test/windowpushd.test 5b9c114e8173c3addacf58a0fcd941437b14649f2033700184479a13f188ad00
F test/with1.test 780be387f01e290e768bdfd1827280f9e37ba37223eb4736aba386864fac5a94
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
-F test/with3.test a261f0ea225c4af0ce6447f1157bb603959b2a665f14a03951c2883d2ef1c0f0
+F test/with3.test 85e059bf4c2ef5626411ee59f399b4bb4b4a0f009bcb7db86f254e570ed11831
F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205
F test/with5.test 6248213c41fab36290b5b73aa3f937309dfba337004d9d8434c3fabc8c7d4be8
+F test/with6.test 3001b59179cbdc26a8c67ff8f46944e3141fdece9ab064c49bbf08459b67b207
F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
F test/without_rowid1.test e4034c0849ccc2e8bb749c69f15bd69bb9fcf8fe77e8d17ce02369604242fe83
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
@@ -1824,8 +1842,8 @@ F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
-F tool/lemon.c 70eedc31614a58fe31a71025c17ebd1502a6ce9cfef0ed5e33acb0b5b737b291
-F tool/lempar.c 0e1d5eeb9736108d3dba782a9dd56f4e7cb69006b6ee181308b7ebfb15313a12
+F tool/lemon.c d44ba4f03427c9bd34b601f315fe77c2b6d4bd215801a0259aeedbcc4c94a95c
+F tool/lempar.c 1d3d075da18681c67ecc66c1f171e7094e18cd2cfba6a8a1bd4f3f639d6656e1
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
@@ -1833,7 +1851,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
-F tool/mkkeywordhash.c 24e4396ae665d985fed9e040e8b748129c1a12d77eeeae7ad4609821c41ba7bf
+F tool/mkkeywordhash.c 08b6e4d7a482a7f37a9a0032e7ba968e26624a027b6b2e9ba589be6f5e3d8c2c
F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
@@ -1905,7 +1923,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P ddb97eeef7598dc16dad0ad6bd512d0da22275d0c993018d258ef1bed347074e 6b01a24daab1e5bcb0768ebf994368d941b1dfc217bf6b661211d900331e68cf
-R cf01ed4d5b8fb2a01a95fff8454f57ee
+P a1708e845772237ca95be5ec44ad1a43b4abeeb25ba8ae6188f0c56d09e5f062 3f520b8bdef7dcdad30e052ed8a07b0493bff4497603521e701fab7324df2995
+R cde7cfc211d08863a9a9e8e6f2a6394b
U drh
-Z 8c197d80ca67e957834c9ebda873bf89
+Z 5aabd47bbb49f59fcb065ec0e8946d34
diff --git a/manifest.uuid b/manifest.uuid
index 361d747718..0928f2b8b7 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-a1708e845772237ca95be5ec44ad1a43b4abeeb25ba8ae6188f0c56d09e5f062
\ No newline at end of file
+acdafef836605ba2079966abde1fa40c43995b6a049d84f631e613605cabda3d
\ No newline at end of file
diff --git a/src/alter.c b/src/alter.c
index f4098863dd..5409de594f 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -49,15 +49,21 @@ static int isAlterableTable(Parse *pParse, Table *pTab){
** statement to ensure that the operation has not rendered any schema
** objects unusable.
*/
-static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
+static void renameTestSchema(
+ Parse *pParse, /* Parse context */
+ const char *zDb, /* Name of db to verify schema of */
+ int bTemp, /* True if this is the temp db */
+ const char *zWhen /* "when" part of error message */
+){
+ pParse->colNamesSet = 1;
sqlite3NestedParse(pParse,
"SELECT 1 "
"FROM \"%w\"." DFLT_SCHEMA_TABLE " "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
+ " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ",
zDb,
- zDb, bTemp
+ zDb, bTemp, zWhen
);
if( bTemp==0 ){
@@ -66,8 +72,8 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
"FROM temp." DFLT_SCHEMA_TABLE " "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
- zDb
+ " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ",
+ zDb, zWhen
);
}
}
@@ -76,12 +82,12 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
** Generate code to reload the schema for database iDb. And, if iDb!=1, for
** the temp database as well.
*/
-static void renameReloadSchema(Parse *pParse, int iDb){
+static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){
Vdbe *v = pParse->pVdbe;
if( v ){
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
- if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
+ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5);
+ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5);
}
}
@@ -230,7 +236,7 @@ void sqlite3AlterRenameTable(
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
"tbl_name = "
"CASE WHEN tbl_name=%Q COLLATE nocase AND "
- " sqlite_rename_test(%Q, sql, type, name, 1) "
+ " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') "
"THEN %Q ELSE tbl_name END "
"WHERE type IN ('view', 'trigger')"
, zDb, zTabName, zName, zTabName, zDb, zName);
@@ -249,8 +255,8 @@ void sqlite3AlterRenameTable(
}
#endif
- renameReloadSchema(pParse, iDb);
- renameTestSchema(pParse, zDb, iDb==1);
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
+ renameTestSchema(pParse, zDb, iDb==1, "after rename");
exit_rename_table:
sqlite3SrcListDelete(db, pSrc);
@@ -381,11 +387,14 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
*zEnd-- = '\0';
}
db->mDbFlags |= DBFLAG_PreferBuiltin;
+ /* substr() operations on characters, but addColOffset is in bytes. So we
+ ** have to use printf() to translate between these units: */
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
- "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
+ "sql = printf('%%.%ds, ',sql) || %Q"
+ " || substr(sql,1+length(printf('%%.%ds',sql))) "
"WHERE type = 'table' AND name = %Q",
- zDb, pNew->addColOffset, zCol, pNew->addColOffset+1,
+ zDb, pNew->addColOffset, zCol, pNew->addColOffset,
zTab
);
sqlite3DbFree(db, zCol);
@@ -409,7 +418,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
}
/* Reload the table definition */
- renameReloadSchema(pParse, iDb);
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
}
/*
@@ -509,7 +518,7 @@ exit_begin_add_column:
** Or, if pTab is not a view or virtual table, zero is returned.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
-static int isRealTable(Parse *pParse, Table *pTab){
+static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
const char *zType = 0;
#ifndef SQLITE_OMIT_VIEW
if( pTab->pSelect ){
@@ -522,15 +531,16 @@ static int isRealTable(Parse *pParse, Table *pTab){
}
#endif
if( zType ){
- sqlite3ErrorMsg(
- pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName
+ sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
+ (bDrop ? "drop column from" : "rename columns of"),
+ zType, pTab->zName
);
return 1;
}
return 0;
}
#else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
-# define isRealTable(x,y) (0)
+# define isRealTable(x,y,z) (0)
#endif
/*
@@ -559,7 +569,7 @@ void sqlite3AlterRenameColumn(
/* Cannot alter a system table */
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column;
- if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column;
+ if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column;
/* Which schema holds the table to be altered */
iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -613,8 +623,8 @@ void sqlite3AlterRenameColumn(
);
/* Drop and reload the database schema. */
- renameReloadSchema(pParse, iSchema);
- renameTestSchema(pParse, zDb, iSchema==1);
+ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
+ renameTestSchema(pParse, zDb, iSchema==1, "after rename");
exit_rename_column:
sqlite3SrcListDelete(db, pSrc);
@@ -866,23 +876,33 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
/*
** Search the Parse object passed as the first argument for a RenameToken
-** object associated with parse tree element pPtr. If found, remove it
-** from the Parse object and add it to the list maintained by the
-** RenameCtx object passed as the second argument.
+** object associated with parse tree element pPtr. If found, return a pointer
+** to it. Otherwise, return NULL.
+**
+** If the second argument passed to this function is not NULL and a matching
+** RenameToken object is found, remove it from the Parse object and add it to
+** the list maintained by the RenameCtx object.
*/
-static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
+static RenameToken *renameTokenFind(
+ Parse *pParse,
+ struct RenameCtx *pCtx,
+ void *pPtr
+){
RenameToken **pp;
assert( pPtr!=0 );
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
if( (*pp)->p==pPtr ){
RenameToken *pToken = *pp;
- *pp = pToken->pNext;
- pToken->pNext = pCtx->pList;
- pCtx->pList = pToken;
- pCtx->nList++;
- break;
+ if( pCtx ){
+ *pp = pToken->pNext;
+ pToken->pNext = pCtx->pList;
+ pCtx->pList = pToken;
+ pCtx->nList++;
+ }
+ return pToken;
}
}
+ return 0;
}
/*
@@ -953,7 +973,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
*/
static void renameColumnParseError(
sqlite3_context *pCtx,
- int bPost,
+ const char *zWhen,
sqlite3_value *pType,
sqlite3_value *pObject,
Parse *pParse
@@ -962,8 +982,8 @@ static void renameColumnParseError(
const char *zN = (const char*)sqlite3_value_text(pObject);
char *zErr;
- zErr = sqlite3_mprintf("error in %s %s%s: %s",
- zT, zN, (bPost ? " after rename" : ""),
+ zErr = sqlite3_mprintf("error in %s %s%s%s: %s",
+ zT, zN, (zWhen[0] ? " " : ""), zWhen,
pParse->zErrMsg
);
sqlite3_result_error(pCtx, zErr, -1);
@@ -1042,7 +1062,7 @@ static int renameParseSql(
p->eParseMode = PARSE_MODE_RENAME;
p->db = db;
p->nQueryLoop = 1;
- rc = sqlite3RunParser(p, zSql, &zErr);
+ rc = zSql ? sqlite3RunParser(p, zSql, &zErr) : SQLITE_NOMEM;
assert( p->zErrMsg==0 );
assert( rc!=SQLITE_OK || zErr==0 );
p->zErrMsg = zErr;
@@ -1195,7 +1215,7 @@ static int renameResolveTrigger(Parse *pParse){
if( pSrc ){
int i;
for(i=0; inSrc && rc==SQLITE_OK; i++){
- struct SrcList_item *p = &pSrc->a[i];
+ SrcItem *p = &pSrc->a[i];
p->iCursor = pParse->nTab++;
if( p->pSelect ){
sqlite3SelectPrep(pParse, p->pSelect, 0);
@@ -1469,7 +1489,7 @@ static void renameColumnFunc(
renameColumnFunc_done:
if( rc!=SQLITE_OK ){
if( sParse.zErrMsg ){
- renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
+ renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
@@ -1507,7 +1527,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
return WRC_Abort;
}
for(i=0; inSrc; i++){
- struct SrcList_item *pItem = &pSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i];
if( pItem->pTab==p->pTab ){
renameTokenFind(pWalker->pParse, p, pItem->zName);
}
@@ -1658,7 +1678,7 @@ static void renameTableFunc(
}
if( rc!=SQLITE_OK ){
if( sParse.zErrMsg ){
- renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
+ renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
@@ -1687,6 +1707,7 @@ static void renameTableFunc(
** 2: Object type ("view", "table", "trigger" or "index").
** 3: Object name.
** 4: True if object is from temp schema.
+** 5: "when" part of error message.
**
** Unless it finds an error, this function normally returns NULL. However, it
** returns integer value 1 if:
@@ -1704,6 +1725,7 @@ static void renameTableTest(
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
int bTemp = sqlite3_value_int(argv[4]);
int isLegacy = (db->flags & SQLITE_LegacyAlter);
+ char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
@@ -1736,8 +1758,8 @@ static void renameTableTest(
}
}
- if( rc!=SQLITE_OK ){
- renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
+ if( rc!=SQLITE_OK && zWhen ){
+ renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse);
}
renameParseCleanup(&sParse);
}
@@ -1747,14 +1769,205 @@ static void renameTableTest(
#endif
}
+/*
+** The implementation of internal UDF sqlite_drop_column().
+**
+** Arguments:
+**
+** argv[0]: An integer - the index of the schema containing the table
+** argv[1]: CREATE TABLE statement to modify.
+** argv[2]: An integer - the index of the column to remove.
+**
+** The value returned is a string containing the CREATE TABLE statement
+** with column argv[2] removed.
+*/
+static void dropColumnFunc(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ int iSchema = sqlite3_value_int(argv[0]);
+ const char *zSql = (const char*)sqlite3_value_text(argv[1]);
+ int iCol = sqlite3_value_int(argv[2]);
+ const char *zDb = db->aDb[iSchema].zDbSName;
+ int rc;
+ Parse sParse;
+ RenameToken *pCol;
+ Table *pTab;
+ const char *zEnd;
+ char *zNew = 0;
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+
+ rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
+ if( rc!=SQLITE_OK ) goto drop_column_done;
+ pTab = sParse.pNewTable;
+ if( pTab->nCol==1 || iCol>=pTab->nCol ){
+ /* This can happen if the sqlite_schema table is corrupt */
+ rc = SQLITE_CORRUPT_BKPT;
+ goto drop_column_done;
+ }
+
+ pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zName);
+ if( iColnCol-1 ){
+ RenameToken *pEnd;
+ pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zName);
+ zEnd = (const char*)pEnd->t.z;
+ }else{
+ zEnd = (const char*)&zSql[pTab->addColOffset];
+ while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
+ }
+
+ zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd);
+ sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT);
+ sqlite3_free(zNew);
+
+drop_column_done:
+ renameParseCleanup(&sParse);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(context, rc);
+ }
+}
+
+/*
+** This function is called by the parser upon parsing an
+**
+** ALTER TABLE pSrc DROP COLUMN pName
+**
+** statement. Argument pSrc contains the possibly qualified name of the
+** table being edited, and token pName the name of the column to drop.
+*/
+void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){
+ sqlite3 *db = pParse->db; /* Database handle */
+ Table *pTab; /* Table to modify */
+ int iDb; /* Index of db containing pTab in aDb[] */
+ const char *zDb; /* Database containing pTab ("main" etc.) */
+ char *zCol = 0; /* Name of column to drop */
+ int iCol; /* Index of column zCol in pTab->aCol[] */
+
+ /* Look up the table being altered. */
+ assert( pParse->pNewTable==0 );
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ if( NEVER(db->mallocFailed) ) goto exit_drop_column;
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
+ if( !pTab ) goto exit_drop_column;
+
+ /* Make sure this is not an attempt to ALTER a view, virtual table or
+ ** system table. */
+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column;
+ if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column;
+
+ /* Find the index of the column being dropped. */
+ zCol = sqlite3NameFromToken(db, pName);
+ if( zCol==0 ){
+ assert( db->mallocFailed );
+ goto exit_drop_column;
+ }
+ iCol = sqlite3ColumnIndex(pTab, zCol);
+ if( iCol<0 ){
+ sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zCol);
+ goto exit_drop_column;
+ }
+
+ /* Do not allow the user to drop a PRIMARY KEY column or a column
+ ** constrained by a UNIQUE constraint. */
+ if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){
+ sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"",
+ (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE",
+ zCol
+ );
+ goto exit_drop_column;
+ }
+
+ /* Do not allow the number of columns to go to zero */
+ if( pTab->nCol<=1 ){
+ sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol);
+ goto exit_drop_column;
+ }
+
+ /* Edit the sqlite_schema table */
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( iDb>=0 );
+ zDb = db->aDb[iDb].zDbSName;
+ renameTestSchema(pParse, zDb, iDb==1, "");
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
+ "sql = sqlite_drop_column(%d, sql, %d) "
+ "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
+ , zDb, iDb, iCol, pTab->zName
+ );
+
+ /* Drop and reload the database schema. */
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
+ renameTestSchema(pParse, zDb, iDb==1, "after drop column");
+
+ /* Edit rows of table on disk */
+ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
+ int i;
+ int addr;
+ int reg;
+ int regRec;
+ Index *pPk = 0;
+ int nField = 0; /* Number of non-virtual columns after drop */
+ int iCur;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ iCur = pParse->nTab++;
+ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ reg = ++pParse->nMem;
+ pParse->nMem += pTab->nCol;
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg);
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ }
+ for(i=0; inCol; i++){
+ if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
+ int regOut;
+ if( pPk ){
+ int iPos = sqlite3TableColumnToIndex(pPk, i);
+ int iColPos = sqlite3TableColumnToIndex(pPk, iCol);
+ regOut = reg+1+iPos-(iPos>iColPos);
+ }else{
+ regOut = reg+1+nField;
+ }
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
+ nField++;
+ }
+ }
+ regRec = reg + pTab->nCol;
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec);
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg);
+ }
+
+ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr);
+ }
+
+exit_drop_column:
+ sqlite3DbFree(db, zCol);
+ sqlite3SrcListDelete(db, pSrc);
+}
+
/*
** Register built-in functions used to help implement ALTER TABLE
*/
void sqlite3AlterFunctions(void){
static FuncDef aAlterTableFuncs[] = {
- INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
- INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
- INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest),
+ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
+ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
+ INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest),
+ INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
diff --git a/src/attach.c b/src/attach.c
index 3b5c57f0cc..8eb4486e58 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -433,6 +433,62 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
}
#endif /* SQLITE_OMIT_ATTACH */
+/*
+** Expression callback used by sqlite3FixAAAA() routines.
+*/
+static int fixExprCb(Walker *p, Expr *pExpr){
+ DbFixer *pFix = p->u.pFix;
+ if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
+ if( pExpr->op==TK_VARIABLE ){
+ if( pFix->pParse->db->init.busy ){
+ pExpr->op = TK_NULL;
+ }else{
+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
+ return WRC_Abort;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Select callback used by sqlite3FixAAAA() routines.
+*/
+static int fixSelectCb(Walker *p, Select *pSelect){
+ DbFixer *pFix = p->u.pFix;
+ int i;
+ SrcItem *pItem;
+ sqlite3 *db = pFix->pParse->db;
+ int iDb = sqlite3FindDbName(db, pFix->zDb);
+ SrcList *pList = pSelect->pSrc;
+
+ if( NEVER(pList==0) ) return WRC_Continue;
+ for(i=0, pItem=pList->a; inSrc; i++, pItem++){
+ if( pFix->bTemp==0 ){
+ if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return WRC_Abort;
+ }
+ sqlite3DbFree(db, pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->pSchema = pFix->pSchema;
+ pItem->fg.fromDDL = 1;
+ }
+#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
+ if( sqlite3WalkExpr(&pFix->w, pList->a[i].pOn) ) return WRC_Abort;
+#endif
+ }
+ if( pSelect->pWith ){
+ for(i=0; ipWith->nCte; i++){
+ if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){
+ return WRC_Abort;
+ }
+ }
+ }
+ return WRC_Continue;
+}
+
/*
** Initialize a DbFixer structure. This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
@@ -444,9 +500,7 @@ void sqlite3FixInit(
const char *zType, /* "view", "trigger", or "index" */
const Token *pName /* Name of the view, trigger, or index */
){
- sqlite3 *db;
-
- db = pParse->db;
+ sqlite3 *db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
pFix->zDb = db->aDb[iDb].zDbSName;
@@ -454,6 +508,13 @@ void sqlite3FixInit(
pFix->zType = zType;
pFix->pName = pName;
pFix->bTemp = (iDb==1);
+ pFix->w.pParse = pParse;
+ pFix->w.xExprCallback = fixExprCb;
+ pFix->w.xSelectCallback = fixSelectCb;
+ pFix->w.xSelectCallback2 = 0;
+ pFix->w.walkerDepth = 0;
+ pFix->w.eCode = 0;
+ pFix->w.u.pFix = pFix;
}
/*
@@ -474,115 +535,27 @@ int sqlite3FixSrcList(
DbFixer *pFix, /* Context of the fixation */
SrcList *pList /* The Source list to check and modify */
){
- int i;
- struct SrcList_item *pItem;
- sqlite3 *db = pFix->pParse->db;
- int iDb = sqlite3FindDbName(db, pFix->zDb);
-
- if( NEVER(pList==0) ) return 0;
-
- for(i=0, pItem=pList->a; inSrc; i++, pItem++){
- if( pFix->bTemp==0 ){
- if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
- sqlite3ErrorMsg(pFix->pParse,
- "%s %T cannot reference objects in database %s",
- pFix->zType, pFix->pName, pItem->zDatabase);
- return 1;
- }
- sqlite3DbFree(db, pItem->zDatabase);
- pItem->zDatabase = 0;
- pItem->pSchema = pFix->pSchema;
- pItem->fg.fromDDL = 1;
- }
-#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
- if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
- if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
-#endif
- if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
- return 1;
- }
+ int res = 0;
+ if( pList ){
+ Select s;
+ memset(&s, 0, sizeof(s));
+ s.pSrc = pList;
+ res = sqlite3WalkSelect(&pFix->w, &s);
}
- return 0;
+ return res;
}
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
int sqlite3FixSelect(
DbFixer *pFix, /* Context of the fixation */
Select *pSelect /* The SELECT statement to be fixed to one database */
){
- while( pSelect ){
- if( sqlite3FixExprList(pFix, pSelect->pEList) ){
- return 1;
- }
- if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
- return 1;
- }
- if( pSelect->pWith ){
- int i;
- for(i=0; ipWith->nCte; i++){
- if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){
- return 1;
- }
- }
- }
- pSelect = pSelect->pPrior;
- }
- return 0;
+ return sqlite3WalkSelect(&pFix->w, pSelect);
}
int sqlite3FixExpr(
DbFixer *pFix, /* Context of the fixation */
Expr *pExpr /* The expression to be fixed to one database */
){
- while( pExpr ){
- if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
- if( pExpr->op==TK_VARIABLE ){
- if( pFix->pParse->db->init.busy ){
- pExpr->op = TK_NULL;
- }else{
- sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
- return 1;
- }
- }
- if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
- }else{
- if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
- }
- if( sqlite3FixExpr(pFix, pExpr->pRight) ){
- return 1;
- }
- pExpr = pExpr->pLeft;
- }
- return 0;
-}
-int sqlite3FixExprList(
- DbFixer *pFix, /* Context of the fixation */
- ExprList *pList /* The expression to be fixed to one database */
-){
- int i;
- struct ExprList_item *pItem;
- if( pList==0 ) return 0;
- for(i=0, pItem=pList->a; inExpr; i++, pItem++){
- if( sqlite3FixExpr(pFix, pItem->pExpr) ){
- return 1;
- }
- }
- return 0;
+ return sqlite3WalkExpr(&pFix->w, pExpr);
}
#endif
@@ -592,25 +565,20 @@ int sqlite3FixTriggerStep(
TriggerStep *pStep /* The trigger step be fixed to one database */
){
while( pStep ){
- if( sqlite3FixSelect(pFix, pStep->pSelect) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pStep->pWhere) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pStep->pExprList) ){
- return 1;
- }
- if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){
+ if( sqlite3WalkSelect(&pFix->w, pStep->pSelect)
+ || sqlite3WalkExpr(&pFix->w, pStep->pWhere)
+ || sqlite3WalkExprList(&pFix->w, pStep->pExprList)
+ || sqlite3FixSrcList(pFix, pStep->pFrom)
+ ){
return 1;
}
#ifndef SQLITE_OMIT_UPSERT
if( pStep->pUpsert ){
Upsert *pUp = pStep->pUpsert;
- if( sqlite3FixExprList(pFix, pUp->pUpsertTarget)
- || sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere)
- || sqlite3FixExprList(pFix, pUp->pUpsertSet)
- || sqlite3FixExpr(pFix, pUp->pUpsertWhere)
+ if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget)
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere)
+ || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet)
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere)
){
return 1;
}
@@ -618,6 +586,7 @@ int sqlite3FixTriggerStep(
#endif
pStep = pStep->pNext;
}
+
return 0;
}
#endif
diff --git a/src/auth.c b/src/auth.c
index 40673d5ea4..33420f5839 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -143,7 +143,6 @@ void sqlite3AuthRead(
Schema *pSchema, /* The schema of the expression */
SrcList *pTabList /* All table that pExpr might refer to */
){
- sqlite3 *db = pParse->db;
Table *pTab = 0; /* The table being read */
const char *zCol; /* Name of the column of the table */
int iSrc; /* Index in pTabList->a[] of table being read */
@@ -151,8 +150,8 @@ void sqlite3AuthRead(
int iCol; /* Index of column in table */
assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
- assert( !IN_RENAME_OBJECT || db->xAuth==0 );
- if( db->xAuth==0 ) return;
+ assert( !IN_RENAME_OBJECT );
+ assert( pParse->db->xAuth!=0 );
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
if( iDb<0 ){
/* An attempt to read a column out of a subquery or other
@@ -164,7 +163,7 @@ void sqlite3AuthRead(
pTab = pParse->pTriggerTab;
}else{
assert( pTabList );
- for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){
+ for(iSrc=0; iSrcnSrc; iSrc++){
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
pTab = pTabList->a[iSrc].pTab;
break;
@@ -172,7 +171,7 @@ void sqlite3AuthRead(
}
}
iCol = pExpr->iColumn;
- if( NEVER(pTab==0) ) return;
+ if( pTab==0 ) return;
if( iCol>=0 ){
assert( iColnCol );
@@ -183,7 +182,7 @@ void sqlite3AuthRead(
}else{
zCol = "ROWID";
}
- assert( iDb>=0 && iDbnDb );
+ assert( iDb>=0 && iDbdb->nDb );
if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){
pExpr->op = TK_NULL;
}
@@ -209,11 +208,7 @@ int sqlite3AuthCheck(
** or if the parser is being invoked from within sqlite3_declare_vtab.
*/
assert( !IN_RENAME_OBJECT || db->xAuth==0 );
- if( db->init.busy || IN_SPECIAL_PARSE ){
- return SQLITE_OK;
- }
-
- if( db->xAuth==0 ){
+ if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){
return SQLITE_OK;
}
diff --git a/src/btree.c b/src/btree.c
index b98da9627c..68161d0771 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -3152,6 +3152,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pCursor );
+ if( nReserve>32 && pageSize==512 ) pageSize = 1024;
pBt->pageSize = (u32)pageSize;
freeTempSpace(pBt);
}
@@ -8103,7 +8104,9 @@ static int balance_nonroot(
}
pgno = get4byte(pRight);
while( 1 ){
- rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
+ }
if( rc ){
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
@@ -8143,12 +8146,10 @@ static int balance_nonroot(
if( pBt->btsFlags & BTS_FAST_SECURE ){
int iOff;
+ /* If the following if() condition is not true, the db is corrupted.
+ ** The call to dropCell() below will detect this. */
iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
- if( (iOff+szNew[i])>(int)pBt->usableSize ){
- rc = SQLITE_CORRUPT_BKPT;
- memset(apOld, 0, (i+1)*sizeof(MemPage*));
- goto balance_cleanup;
- }else{
+ if( (iOff+szNew[i])<=(int)pBt->usableSize ){
memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
}
@@ -8442,6 +8443,9 @@ static int balance_nonroot(
apOld[i] = 0;
rc = sqlite3PagerWrite(pNew->pDbPage);
nNew++;
+ if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
@@ -8478,7 +8482,7 @@ static int balance_nonroot(
aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
aPgFlags[i] = apNew[i]->pDbPage->flags;
for(j=0; jpBt;
u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
const u8 *aIn; /* Pointer to next input buffer */
- int nIn; /* Size of input buffer aIn[] */
- int nRem; /* Bytes of data still to copy */
+ u32 nIn; /* Size of input buffer aIn[] */
+ u32 nRem; /* Bytes of data still to copy */
getCellInfo(pSrc);
aOut += putVarint32(aOut, pSrc->info.nPayload);
if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
nIn = pSrc->info.nLocal;
aIn = pSrc->info.pPayload;
+ if( aIn+nIn>pSrc->pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
nRem = pSrc->info.nPayload;
if( nIn==nRem && nInpPage->maxLocal ){
memcpy(aOut, aIn, nIn);
@@ -9451,7 +9458,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
Pgno ovflIn = 0;
DbPage *pPageIn = 0;
MemPage *pPageOut = 0;
- int nOut; /* Size of output buffer aOut[] */
+ u32 nOut; /* Size of output buffer aOut[] */
nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
@@ -9461,6 +9468,9 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
}
if( nRem>nIn ){
+ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
}
diff --git a/src/build.c b/src/build.c
index d09bdb6939..91ed43a227 100644
--- a/src/build.c
+++ b/src/build.c
@@ -145,10 +145,36 @@ void sqlite3FinishCoding(Parse *pParse){
/* Begin by generating some termination code at the end of the
** vdbe program
*/
- v = sqlite3GetVdbe(pParse);
+ v = pParse->pVdbe;
+ if( v==0 ){
+ if( db->init.busy ){
+ pParse->rc = SQLITE_DONE;
+ return;
+ }
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) pParse->rc = SQLITE_ERROR;
+ }
assert( !pParse->isMultiWrite
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
if( v ){
+ if( pParse->bReturning ){
+ Returning *pReturning = pParse->u1.pReturning;
+ int addrRewind;
+ int i;
+ int reg;
+
+ addrRewind =
+ sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur);
+ VdbeCoverage(v);
+ reg = pReturning->iRetReg;
+ for(i=0; inRetCol; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i);
+ }
+ sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i);
+ sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrRewind);
+ }
sqlite3VdbeAddOp0(v, OP_Halt);
#if SQLITE_USER_AUTHENTICATION
@@ -226,12 +252,16 @@ void sqlite3FinishCoding(Parse *pParse){
}
}
+ if( pParse->bReturning ){
+ Returning *pRet = pParse->u1.pReturning;
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
+ }
+
/* Finally, jump back to the beginning of the executable code. */
sqlite3VdbeGoto(v, 1);
}
}
-
/* Get the VDBE program ready for execution
*/
if( v && pParse->nErr==0 && !db->mallocFailed ){
@@ -450,7 +480,7 @@ Table *sqlite3LocateTable(
Table *sqlite3LocateTableItem(
Parse *pParse,
u32 flags,
- struct SrcList_item *p
+ SrcItem *p
){
const char *zDb;
assert( p->pSchema==0 || p->zDatabase==0 );
@@ -1208,7 +1238,8 @@ void sqlite3StartTable(
}else
#endif
{
- pParse->addrCrTab =
+ assert( !pParse->bReturning );
+ pParse->u1.addrCrTab =
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
}
sqlite3OpenSchemaTable(pParse, iDb);
@@ -1235,12 +1266,84 @@ begin_table_error:
void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){
pCol->colFlags |= COLFLAG_HIDDEN;
+ if( pTab ) pTab->tabFlags |= TF_HasHidden;
}else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){
pTab->tabFlags |= TF_OOOHidden;
}
}
#endif
+/*
+** Name of the special TEMP trigger used to implement RETURNING. The
+** name begins with "sqlite_" so that it is guaranteed not to collide
+** with any application-generated triggers.
+*/
+#define RETURNING_TRIGGER_NAME "sqlite_returning"
+
+/*
+** Clean up the data structures associated with the RETURNING clause.
+*/
+static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
+ Hash *pHash;
+ pHash = &(db->aDb[1].pSchema->trigHash);
+ sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
+ sqlite3ExprListDelete(db, pRet->pReturnEL);
+ sqlite3DbFree(db, pRet);
+}
+
+/*
+** Add the RETURNING clause to the parse currently underway.
+**
+** This routine creates a special TEMP trigger that will fire for each row
+** of the DML statement. That TEMP trigger contains a single SELECT
+** statement with a result set that is the argument of the RETURNING clause.
+** The trigger has the Trigger.bReturning flag and an opcode of
+** TK_RETURNING instead of TK_SELECT, so that the trigger code generator
+** knows to handle it specially. The TEMP trigger is automatically
+** removed at the end of the parse.
+**
+** When this routine is called, we do not yet know if the RETURNING clause
+** is attached to a DELETE, INSERT, or UPDATE, so construct it as a
+** RETURNING trigger instead. It will then be converted into the appropriate
+** type on the first call to sqlite3TriggersExist().
+*/
+void sqlite3AddReturning(Parse *pParse, ExprList *pList){
+ Returning *pRet;
+ Hash *pHash;
+ sqlite3 *db = pParse->db;
+ if( pParse->pNewTrigger ){
+ sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger");
+ }else{
+ assert( pParse->bReturning==0 );
+ }
+ pParse->bReturning = 1;
+ pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
+ if( pRet==0 ){
+ sqlite3ExprListDelete(db, pList);
+ return;
+ }
+ pParse->u1.pReturning = pRet;
+ pRet->pParse = pParse;
+ pRet->pReturnEL = pList;
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
+ if( db->mallocFailed ) return;
+ pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
+ pRet->retTrig.op = TK_RETURNING;
+ pRet->retTrig.tr_tm = TRIGGER_AFTER;
+ pRet->retTrig.bReturning = 1;
+ pRet->retTrig.pSchema = db->aDb[1].pSchema;
+ pRet->retTrig.step_list = &pRet->retTStep;
+ pRet->retTStep.op = TK_RETURNING;
+ pRet->retTStep.pTrig = &pRet->retTrig;
+ pRet->retTStep.pExprList = pList;
+ pHash = &(db->aDb[1].pSchema->trigHash);
+ assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 || pParse->nErr );
+ if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
+ ==&pRet->retTrig ){
+ sqlite3OomFault(db);
+ }
+}
/*
** Add a new column to the table currently being constructed.
@@ -1257,6 +1360,8 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
+ u8 hName;
+
if( (p = pParse->pNewTable)==0 ) return;
if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
@@ -1268,8 +1373,9 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
memcpy(z, pName->z, pName->n);
z[pName->n] = 0;
sqlite3Dequote(z);
+ hName = sqlite3StrIHash(z);
for(i=0; inCol; i++){
- if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
+ if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zName)==0 ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
sqlite3DbFree(db, z);
return;
@@ -1287,7 +1393,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
pCol = &p->aCol[p->nCol];
memset(pCol, 0, sizeof(p->aCol[0]));
pCol->zName = z;
- pCol->hName = sqlite3StrIHash(z);
+ pCol->hName = hName;
sqlite3ColumnPropertiesFromName(p, pCol);
if( pType->n==0 ){
@@ -2070,9 +2176,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
/* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
** into BTREE_BLOBKEY.
*/
- if( pParse->addrCrTab ){
+ assert( !pParse->bReturning );
+ if( pParse->u1.addrCrTab ){
assert( v );
- sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY);
+ sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY);
}
/* Locate the PRIMARY KEY index. Or, if this table was originally
@@ -2536,7 +2643,7 @@ void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
+ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
}
/* Add the table to the in-memory representation of the database.
@@ -2553,20 +2660,17 @@ void sqlite3EndTable(
}
pParse->pNewTable = 0;
db->mDbFlags |= DBFLAG_SchemaChange;
+ }
#ifndef SQLITE_OMIT_ALTERTABLE
- if( !p->pSelect ){
- const char *zName = (const char *)pParse->sNameToken.z;
- int nName;
- assert( !pSelect && pCons && pEnd );
- if( pCons->z==0 ){
- pCons = pEnd;
- }
- nName = (int)((const char *)pCons->z - zName);
- p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName);
+ if( !pSelect && !p->pSelect ){
+ assert( pCons && pEnd );
+ if( pCons->z==0 ){
+ pCons = pEnd;
}
-#endif
+ p->addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z);
}
+#endif
}
#ifndef SQLITE_OMIT_VIEW
@@ -2757,6 +2861,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
assert( pTable->aCol==0 );
pTable->nCol = pSelTab->nCol;
pTable->aCol = pSelTab->aCol;
+ pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT);
pSelTab->nCol = 0;
pSelTab->aCol = 0;
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
@@ -4024,7 +4129,7 @@ void sqlite3CreateIndex(
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
+ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0);
sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
}
@@ -4417,7 +4522,7 @@ SrcList *sqlite3SrcListAppend(
Token *pTable, /* Table to append */
Token *pDatabase /* Database of the table */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
sqlite3 *db;
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
assert( pParse!=0 );
@@ -4458,7 +4563,7 @@ SrcList *sqlite3SrcListAppend(
*/
void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert(pList || pParse->db->mallocFailed );
if( pList ){
for(i=0, pItem=pList->a; inSrc; i++, pItem++){
@@ -4476,7 +4581,7 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
*/
void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
if( pList==0 ) return;
for(pItem=pList->a, i=0; inSrc; i++, pItem++){
if( pItem->zDatabase ) sqlite3DbFreeNN(db, pItem->zDatabase);
@@ -4518,7 +4623,7 @@ SrcList *sqlite3SrcListAppendFromTerm(
Expr *pOn, /* The ON clause of a join */
IdList *pUsing /* The USING clause of a join */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
sqlite3 *db = pParse->db;
if( !p && (pOn || pUsing) ){
sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
@@ -4562,7 +4667,7 @@ SrcList *sqlite3SrcListAppendFromTerm(
void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
assert( pIndexedBy!=0 );
if( p && pIndexedBy->n>0 ){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert( p->nSrc>0 );
pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
@@ -4592,7 +4697,7 @@ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
sqlite3SrcListDelete(pParse->db, p2);
}else{
p1 = pNew;
- memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item));
+ memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
sqlite3DbFree(pParse->db, p2);
}
}
@@ -4605,7 +4710,7 @@ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
*/
void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
if( p ){
- struct SrcList_item *pItem = &p->a[p->nSrc-1];
+ SrcItem *pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
assert( pItem->fg.isIndexedBy==0 );
assert( pItem->fg.isTabFunc==0 );
@@ -5102,24 +5207,76 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
}
#ifndef SQLITE_OMIT_CTE
+/*
+** Create a new CTE object
+*/
+Cte *sqlite3CteNew(
+ Parse *pParse, /* Parsing context */
+ Token *pName, /* Name of the common-table */
+ ExprList *pArglist, /* Optional column name list for the table */
+ Select *pQuery, /* Query used to initialize the table */
+ u8 eM10d /* The MATERIALIZED flag */
+){
+ Cte *pNew;
+ sqlite3 *db = pParse->db;
+
+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew));
+ assert( pNew!=0 || db->mallocFailed );
+
+ if( db->mallocFailed ){
+ sqlite3ExprListDelete(db, pArglist);
+ sqlite3SelectDelete(db, pQuery);
+ }else{
+ pNew->pSelect = pQuery;
+ pNew->pCols = pArglist;
+ pNew->zName = sqlite3NameFromToken(pParse->db, pName);
+ pNew->eM10d = eM10d;
+ }
+ return pNew;
+}
+
+/*
+** Clear information from a Cte object, but do not deallocate storage
+** for the object itself.
+*/
+static void cteClear(sqlite3 *db, Cte *pCte){
+ assert( pCte!=0 );
+ sqlite3ExprListDelete(db, pCte->pCols);
+ sqlite3SelectDelete(db, pCte->pSelect);
+ sqlite3DbFree(db, pCte->zName);
+}
+
+/*
+** Free the contents of the CTE object passed as the second argument.
+*/
+void sqlite3CteDelete(sqlite3 *db, Cte *pCte){
+ assert( pCte!=0 );
+ cteClear(db, pCte);
+ sqlite3DbFree(db, pCte);
+}
+
/*
** This routine is invoked once per CTE by the parser while parsing a
-** WITH clause.
+** WITH clause. The CTE described by teh third argument is added to
+** the WITH clause of the second argument. If the second argument is
+** NULL, then a new WITH argument is created.
*/
With *sqlite3WithAdd(
Parse *pParse, /* Parsing context */
With *pWith, /* Existing WITH clause, or NULL */
- Token *pName, /* Name of the common-table */
- ExprList *pArglist, /* Optional column name list for the table */
- Select *pQuery /* Query used to initialize the table */
+ Cte *pCte /* CTE to add to the WITH clause */
){
sqlite3 *db = pParse->db;
With *pNew;
char *zName;
+ if( pCte==0 ){
+ return pWith;
+ }
+
/* Check that the CTE name is unique within this WITH clause. If
** not, store an error in the Parse structure. */
- zName = sqlite3NameFromToken(pParse->db, pName);
+ zName = pCte->zName;
if( zName && pWith ){
int i;
for(i=0; inCte; i++){
@@ -5138,16 +5295,11 @@ With *sqlite3WithAdd(
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
if( db->mallocFailed ){
- sqlite3ExprListDelete(db, pArglist);
- sqlite3SelectDelete(db, pQuery);
- sqlite3DbFree(db, zName);
+ sqlite3CteDelete(db, pCte);
pNew = pWith;
}else{
- pNew->a[pNew->nCte].pSelect = pQuery;
- pNew->a[pNew->nCte].pCols = pArglist;
- pNew->a[pNew->nCte].zName = zName;
- pNew->a[pNew->nCte].zCteErr = 0;
- pNew->nCte++;
+ pNew->a[pNew->nCte++] = *pCte;
+ sqlite3DbFree(db, pCte);
}
return pNew;
@@ -5160,10 +5312,7 @@ void sqlite3WithDelete(sqlite3 *db, With *pWith){
if( pWith ){
int i;
for(i=0; inCte; i++){
- struct Cte *pCte = &pWith->a[i];
- sqlite3ExprListDelete(db, pCte->pCols);
- sqlite3SelectDelete(db, pCte->pSelect);
- sqlite3DbFree(db, pCte->zName);
+ cteClear(db, &pWith->a[i]);
}
sqlite3DbFree(db, pWith);
}
diff --git a/src/ctime.c b/src/ctime.c
index f0fdccbdc1..7982f23aeb 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -260,7 +260,7 @@ static const char * const sqlite3azCompileOpt[] = {
"ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
#endif
#if SQLITE_ENABLE_MATH_FUNCTIONS
- "ENABLE_MATH_FUNCTIONS"
+ "ENABLE_MATH_FUNCTIONS",
#endif
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
"ENABLE_MEMORY_MANAGEMENT",
diff --git a/src/delete.c b/src/delete.c
index 064ae7325a..0c9c7bc8d0 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -29,7 +29,7 @@
**
*/
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
- struct SrcList_item *pItem = pSrc->a;
+ SrcItem *pItem = pSrc->a;
Table *pTab;
assert( pItem && pSrc->nSrc>=1 );
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
@@ -37,9 +37,9 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
pItem->pTab = pTab;
if( pTab ){
pTab->nTabRef++;
- }
- if( sqlite3IndexedByLookup(pParse, pItem) ){
- pTab = 0;
+ if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){
+ pTab = 0;
+ }
}
return pTab;
}
@@ -207,9 +207,15 @@ Expr *sqlite3LimitWhere(
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */
pSrc->a[0].pTab = 0;
- pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
+ pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);
pSrc->a[0].pTab = pTab;
- pSrc->a[0].pIBIndex = 0;
+ if( pSrc->a[0].fg.isIndexedBy ){
+ pSrc->a[0].u2.pIBIndex = 0;
+ pSrc->a[0].fg.isIndexedBy = 0;
+ sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy);
+ }else if( pSrc->a[0].fg.isCte ){
+ pSrc->a[0].u2.pCteUse->nUse++;
+ }
/* generate the SELECT expression tree. */
pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
@@ -387,6 +393,7 @@ void sqlite3DeleteFrom(
if( (db->flags & SQLITE_CountRows)!=0
&& !pParse->nested
&& !pParse->pTriggerTab
+ && !pParse->bReturning
){
memCnt = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
@@ -608,7 +615,7 @@ void sqlite3DeleteFrom(
** invoke the callback function.
*/
if( memCnt ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
+ sqlite3VdbeAddOp2(v, OP_ChngCntRow, memCnt, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
}
diff --git a/src/expr.c b/src/expr.c
index 685f041752..b66556b039 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -95,7 +95,18 @@ Expr *sqlite3ExprAddCollateToken(
const Token *pCollName, /* Name of collating sequence */
int dequote /* True to dequote pCollName */
){
- if( pCollName->n>0 ){
+ assert( pExpr!=0 || pParse->db->mallocFailed );
+ if( pExpr==0 ) return 0;
+ if( pExpr->op==TK_VECTOR ){
+ ExprList *pList = pExpr->x.pList;
+ if( ALWAYS(pList!=0) ){
+ int i;
+ for(i=0; inExpr; i++){
+ pList->a[i].pExpr = sqlite3ExprAddCollateToken(pParse,pList->a[i].pExpr,
+ pCollName, dequote);
+ }
+ }
+ }else if( pCollName->n>0 ){
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
if( pNew ){
pNew->pLeft = pExpr;
@@ -1519,8 +1530,8 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; inSrc; i++){
- struct SrcList_item *pNewItem = &pNew->a[i];
- struct SrcList_item *pOldItem = &p->a[i];
+ SrcItem *pNewItem = &pNew->a[i];
+ SrcItem *pOldItem = &p->a[i];
Table *pTab;
pNewItem->pSchema = pOldItem->pSchema;
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
@@ -1533,7 +1544,10 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}
- pNewItem->pIBIndex = pOldItem->pIBIndex;
+ pNewItem->u2 = pOldItem->u2;
+ if( pNewItem->fg.isCte ){
+ pNewItem->u2.pCteUse->nUse++;
+ }
if( pNewItem->fg.isTabFunc ){
pNewItem->u1.pFuncArg =
sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
@@ -5850,7 +5864,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
/* Check to see if the column is in one of the tables in the FROM
** clause of the aggregate query */
if( ALWAYS(pSrcList!=0) ){
- struct SrcList_item *pItem = pSrcList->a;
+ SrcItem *pItem = pSrcList->a;
for(i=0; inSrc; i++, pItem++){
struct AggInfo_col *pCol;
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
diff --git a/src/fkey.c b/src/fkey.c
index 959e994d17..9f622f40c6 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -1024,7 +1024,7 @@ void sqlite3FkCheck(
** child table as a SrcList for sqlite3WhereBegin() */
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( pSrc ){
- struct SrcList_item *pItem = pSrc->a;
+ SrcItem *pItem = pSrc->a;
pItem->pTab = pFKey->pFrom;
pItem->zName = pFKey->pFrom->zName;
pItem->pTab->nTabRef++;
@@ -1112,7 +1112,9 @@ u32 sqlite3FkOldmask(
**
** For an UPDATE, this function returns 2 if:
**
-** * There are any FKs for which pTab is the child and the parent table, or
+** * There are any FKs for which pTab is the child and the parent table
+** and any FK processing at all is required (even of a different FK), or
+**
** * the UPDATE modifies one or more parent keys for which the action is
** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL).
**
@@ -1124,13 +1126,14 @@ int sqlite3FkRequired(
int *aChange, /* Non-NULL for UPDATE operations */
int chngRowid /* True for UPDATE that affects rowid */
){
- int eRet = 0;
+ int eRet = 1; /* Value to return if bHaveFK is true */
+ int bHaveFK = 0; /* If FK processing is required */
if( pParse->db->flags&SQLITE_ForeignKeys ){
if( !aChange ){
/* A DELETE operation. Foreign key processing is required if the
** table in question is either the child or parent table for any
** foreign key constraint. */
- eRet = (sqlite3FkReferences(pTab) || pTab->pFKey);
+ bHaveFK = (sqlite3FkReferences(pTab) || pTab->pFKey);
}else{
/* This is an UPDATE. Foreign key processing is only required if the
** operation modifies one or more child or parent key columns. */
@@ -1138,9 +1141,9 @@ int sqlite3FkRequired(
/* Check if any child key columns are being modified. */
for(p=pTab->pFKey; p; p=p->pNextFrom){
- if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2;
if( fkChildIsModified(pTab, p, aChange, chngRowid) ){
- eRet = 1;
+ if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2;
+ bHaveFK = 1;
}
}
@@ -1148,12 +1151,12 @@ int sqlite3FkRequired(
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
if( p->aAction[1]!=OE_None ) return 2;
- eRet = 1;
+ bHaveFK = 1;
}
}
}
}
- return eRet;
+ return bHaveFK ? eRet : 0;
}
/*
@@ -1352,7 +1355,7 @@ static Trigger *fkActionTrigger(
switch( action ){
case OE_Restrict:
- pStep->op = TK_SELECT;
+ pStep->op = TK_SELECT;
break;
case OE_Cascade:
if( !pChanges ){
diff --git a/src/func.c b/src/func.c
index a22ffe3714..5e7eae873c 100644
--- a/src/func.c
+++ b/src/func.c
@@ -696,7 +696,8 @@ static int patternCompare(
/* Skip over multiple "*" characters in the pattern. If there
** are also "?" characters, skip those as well, but consume a
** single character of the input string for each "?" skipped */
- while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
+ while( (c=Utf8Read(zPattern)) == matchAll
+ || (c == matchOne && matchOne!=0) ){
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
return SQLITE_NOWILDCARDMATCH;
}
@@ -1867,7 +1868,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef;
int nExpr;
- if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
+ assert( pExpr!=0 );
+ assert( pExpr->op==TK_FUNCTION );
+ if( !pExpr->x.pList ){
return 0;
}
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
@@ -1953,6 +1956,14 @@ static void ceilingFunc(
}
}
+/*
+** On some systems, ceil() and floor() are intrinsic function. You are
+** unable to take a pointer to these functions. Hence, we here wrap them
+** in our own actual functions.
+*/
+static double xCeil(double x){ return ceil(x); }
+static double xFloor(double x){ return floor(x); }
+
/*
** Implementation of SQL functions:
**
@@ -1972,7 +1983,7 @@ static void logFunc(
case SQLITE_INTEGER:
case SQLITE_FLOAT:
x = sqlite3_value_double(argv[0]);
- if( x<0.0 ) return;
+ if( x<=0.0 ) return;
break;
default:
return;
@@ -1981,14 +1992,15 @@ static void logFunc(
switch( sqlite3_value_numeric_type(argv[0]) ){
case SQLITE_INTEGER:
case SQLITE_FLOAT:
- b = x;
+ b = log(x);
+ if( b<=0.0 ) return;
x = sqlite3_value_double(argv[1]);
- if( x<0.0 ) return;
+ if( x<=0.0 ) return;
break;
default:
return;
}
- ans = log(x)/log(b);
+ ans = log(x)/b;
}else{
ans = log(x);
switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
@@ -2086,6 +2098,7 @@ static void signFunc(
){
int type0;
double x;
+ UNUSED_PARAMETER(argc);
assert( argc==1 );
type0 = sqlite3_value_numeric_type(argv[0]);
if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
@@ -2212,9 +2225,9 @@ void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ),
#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
- MFUNCTION(ceil, 1, ceil, ceilingFunc ),
- MFUNCTION(ceiling, 1, ceil, ceilingFunc ),
- MFUNCTION(floor, 1, floor, ceilingFunc ),
+ MFUNCTION(ceil, 1, xCeil, ceilingFunc ),
+ MFUNCTION(ceiling, 1, xCeil, ceilingFunc ),
+ MFUNCTION(floor, 1, xFloor, ceilingFunc ),
#if SQLITE_HAVE_C99_MATH_FUNCS
MFUNCTION(trunc, 1, trunc, ceilingFunc ),
#endif
diff --git a/src/insert.c b/src/insert.c
index 9913136db7..f0b93ae376 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -370,7 +370,9 @@ static int autoIncBegin(
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
- if( pInfo==0 ) return 0;
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DbFree, pInfo);
+ testcase( pParse->earlyCleanup );
+ if( pParse->db->mallocFailed ) return 0;
pInfo->pNext = pToplevel->pAinc;
pToplevel->pAinc = pInfo;
pInfo->pTab = pTab;
@@ -928,19 +930,24 @@ void sqlite3Insert(
}
}
#endif
- }
- /* Make sure the number of columns in the source data matches the number
- ** of columns to be inserted into the table.
- */
- for(i=0; inCol; i++){
- if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
- }
- if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
- sqlite3ErrorMsg(pParse,
- "table %S has %d columns but %d values were supplied",
- pTabList, 0, pTab->nCol-nHidden, nColumn);
- goto insert_cleanup;
+ /* Make sure the number of columns in the source data matches the number
+ ** of columns to be inserted into the table.
+ */
+ assert( TF_HasHidden==COLFLAG_HIDDEN );
+ assert( TF_HasGenerated==COLFLAG_GENERATED );
+ assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) );
+ if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){
+ for(i=0; inCol; i++){
+ if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
+ }
+ }
+ if( nColumn!=(pTab->nCol-nHidden) ){
+ sqlite3ErrorMsg(pParse,
+ "table %S has %d columns but %d values were supplied",
+ pTabList, 0, pTab->nCol-nHidden, nColumn);
+ goto insert_cleanup;
+ }
}
if( pColumn!=0 && nColumn!=pColumn->nId ){
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
@@ -952,6 +959,7 @@ void sqlite3Insert(
if( (db->flags & SQLITE_CountRows)!=0
&& !pParse->nested
&& !pParse->pTriggerTab
+ && !pParse->bReturning
){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
@@ -1140,11 +1148,6 @@ void sqlite3Insert(
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
- /* Cannot have triggers on a virtual table. If it were possible,
- ** this block would have to account for hidden column.
- */
- assert( !IsVirtual(pTab) );
-
/* Copy the new data already generated. */
assert( pTab->nNVCol>0 );
sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1);
@@ -1299,7 +1302,9 @@ void sqlite3Insert(
sqlite3VdbeJumpHere(v, addrInsTop);
}
+#ifndef SQLITE_OMIT_XFER_OPT
insert_end:
+#endif /* SQLITE_OMIT_XFER_OPT */
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
@@ -1314,7 +1319,7 @@ insert_end:
** invoke the callback function.
*/
if( regRowCount ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
+ sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
}
@@ -1938,7 +1943,7 @@ void sqlite3GenerateConstraintChecks(
** the UNIQUE constraints have run.
*/
if( onError==OE_Replace /* IPK rule is REPLACE */
- && onError!=overrideError /* Rules for other contraints are different */
+ && onError!=overrideError /* Rules for other constraints are different */
&& pTab->pIndex /* There exist other constraints */
){
ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
@@ -2405,6 +2410,32 @@ void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){
}
#endif
+/*
+** Table pTab is a WITHOUT ROWID table that is being written to. The cursor
+** number is iCur, and register regData contains the new record for the
+** PK index. This function adds code to invoke the pre-update hook,
+** if one is registered.
+*/
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+static void codeWithoutRowidPreupdate(
+ Parse *pParse, /* Parse context */
+ Table *pTab, /* Table being updated */
+ int iCur, /* Cursor number for table */
+ int regData /* Data containing new record */
+){
+ Vdbe *v = pParse->pVdbe;
+ int r = sqlite3GetTempReg(pParse);
+ assert( !HasRowid(pTab) );
+ assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) );
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
+ sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
+ sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+ sqlite3ReleaseTempReg(pParse, r);
+}
+#else
+# define codeWithoutRowidPreupdate(a,b,c,d)
+#endif
+
/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
@@ -2453,17 +2484,9 @@ void sqlite3CompleteInsertion(
assert( pParse->nested==0 );
pik_flags |= OPFLAG_NCHANGE;
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
-#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( update_flags==0 ){
- int r = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
- sqlite3VdbeAddOp4(v, OP_Insert,
- iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE
- );
- sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
- sqlite3ReleaseTempReg(pParse, r);
+ codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]);
}
-#endif
}
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
aRegIdx[i]+1,
@@ -2661,7 +2684,7 @@ static int xferOptimization(
ExprList *pEList; /* The result set of the SELECT */
Table *pSrc; /* The table in the FROM clause of SELECT */
Index *pSrcIdx, *pDestIdx; /* Source and destination indices */
- struct SrcList_item *pItem; /* An element of pSelect->pSrc */
+ SrcItem *pItem; /* An element of pSelect->pSrc */
int i; /* Loop counter */
int iDbSrc; /* The database of pSrc */
int iSrc, iDest; /* Cursors from source and destination */
@@ -2936,7 +2959,7 @@ static int xferOptimization(
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
- if( db->xPreUpdateCallback ){
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
insFlags &= ~OPFLAG_PREFORMAT;
}else
@@ -2944,8 +2967,10 @@ static int xferOptimization(
{
sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid);
}
- sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
- (char*)pDest, P4_TABLE);
+ sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE);
+ }
sqlite3VdbeChangeP5(v, insFlags);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
@@ -2991,13 +3016,19 @@ static int xferOptimization(
if( i==pSrcIdx->nColumn ){
idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
- sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regData);
+ sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc);
}
}else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
idxInsFlags |= OPFLAG_NCHANGE;
}
if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0
+ && !HasRowid(pDest)
+ && IsPrimaryKeyIndex(pDestIdx)
+ ){
+ codeWithoutRowidPreupdate(pParse, pDest, iDest, regData);
+ }
}
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
diff --git a/src/main.c b/src/main.c
index e1812e7f56..fbc0e156c6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -4080,7 +4080,7 @@ int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
+ db->dbOptFlags = va_arg(ap, u32);
break;
}
diff --git a/src/memjournal.c b/src/memjournal.c
index 4811f2d8d4..660a842676 100644
--- a/src/memjournal.c
+++ b/src/memjournal.c
@@ -70,7 +70,6 @@ struct MemJournal {
int nChunkSize; /* In-memory chunk-size */
int nSpill; /* Bytes of data before flushing */
- int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
FilePoint readpoint; /* Pointer to the end of the last xRead() */
@@ -131,14 +130,13 @@ static int memjrnlRead(
/*
** Free the list of FileChunk structures headed at MemJournal.pFirst.
*/
-static void memjrnlFreeChunks(MemJournal *p){
+static void memjrnlFreeChunks(FileChunk *pFirst){
FileChunk *pIter;
FileChunk *pNext;
- for(pIter=p->pFirst; pIter; pIter=pNext){
+ for(pIter=pFirst; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
- p->pFirst = 0;
}
/*
@@ -165,7 +163,7 @@ static int memjrnlCreateFile(MemJournal *p){
}
if( rc==SQLITE_OK ){
/* No error has occurred. Free the in-memory buffers. */
- memjrnlFreeChunks(©);
+ memjrnlFreeChunks(copy.pFirst);
}
}
if( rc!=SQLITE_OK ){
@@ -248,7 +246,6 @@ static int memjrnlWrite(
nWrite -= iSpace;
p->endpoint.iOffset += iSpace;
}
- p->nSize = iAmt + iOfst;
}
}
@@ -256,22 +253,30 @@ static int memjrnlWrite(
}
/*
-** Truncate the file.
-**
-** If the journal file is already on disk, truncate it there. Or, if it
-** is still in main memory but is being truncated to zero bytes in size,
-** ignore
+** Truncate the in-memory file.
*/
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
- if( ALWAYS(size==0) ){
- memjrnlFreeChunks(p);
- p->nSize = 0;
- p->endpoint.pChunk = 0;
- p->endpoint.iOffset = 0;
- p->readpoint.pChunk = 0;
- p->readpoint.iOffset = 0;
+ FileChunk *pIter = 0;
+
+ if( size==0 ){
+ memjrnlFreeChunks(p->pFirst);
+ p->pFirst = 0;
+ }else{
+ i64 iOff = p->nChunkSize;
+ for(pIter=p->pFirst; ALWAYS(pIter) && iOff<=size; pIter=pIter->pNext){
+ iOff += p->nChunkSize;
+ }
+ if( ALWAYS(pIter) ){
+ memjrnlFreeChunks(pIter->pNext);
+ pIter->pNext = 0;
+ }
}
+
+ p->endpoint.pChunk = pIter;
+ p->endpoint.iOffset = size;
+ p->readpoint.pChunk = 0;
+ p->readpoint.iOffset = 0;
return SQLITE_OK;
}
@@ -280,7 +285,7 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
*/
static int memjrnlClose(sqlite3_file *pJfd){
MemJournal *p = (MemJournal *)pJfd;
- memjrnlFreeChunks(p);
+ memjrnlFreeChunks(p->pFirst);
return SQLITE_OK;
}
diff --git a/src/pager.c b/src/pager.c
index d67f3d7bf3..8f4cdffbac 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -435,6 +435,7 @@ struct PagerSavepoint {
Bitvec *pInSavepoint; /* Set of pages in this savepoint */
Pgno nOrig; /* Original number of pages in file */
Pgno iSubRec; /* Index of first record in sub-journal */
+ int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */
#ifndef SQLITE_OMIT_WAL
u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
#endif
@@ -1075,6 +1076,9 @@ static int subjRequiresPage(PgHdr *pPg){
for(i=0; inSavepoint; i++){
p = &pPager->aSavepoint[i];
if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
+ for(i=i+1; inSavepoint; i++){
+ pPager->aSavepoint[i].bTruncateOnRelease = 0;
+ }
return 1;
}
}
@@ -7009,6 +7013,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
}
aNew[ii].iSubRec = pPager->nSubRec;
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
+ aNew[ii].bTruncateOnRelease = 1;
if( !aNew[ii].pInSavepoint ){
return SQLITE_NOMEM_BKPT;
}
@@ -7090,13 +7095,15 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
/* If this is a release of the outermost savepoint, truncate
** the sub-journal to zero bytes in size. */
if( op==SAVEPOINT_RELEASE ){
- if( nNew==0 && isOpen(pPager->sjfd) ){
+ PagerSavepoint *pRel = &pPager->aSavepoint[nNew];
+ if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
if( sqlite3JournalIsInMemory(pPager->sjfd) ){
- rc = sqlite3OsTruncate(pPager->sjfd, 0);
+ i64 sz = (pPager->pageSize+4)*pRel->iSubRec;
+ rc = sqlite3OsTruncate(pPager->sjfd, sz);
assert( rc==SQLITE_OK );
}
- pPager->nSubRec = 0;
+ pPager->nSubRec = pRel->iSubRec;
}
}
/* Else this is a rollback operation, playback the specified savepoint.
diff --git a/src/parse.y b/src/parse.y
index 9b92abdbab..821e8ebe46 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -266,6 +266,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
%ifndef SQLITE_OMIT_GENERATED_COLUMNS
GENERATED ALWAYS
%endif
+ MATERIALIZED
REINDEX RENAME CTIME_KW IF
.
%wildcard ANY.
@@ -525,29 +526,25 @@ cmd ::= select(X). {
}
}
}
+
+ /* Attach a With object describing the WITH clause to a Select
+ ** object describing the query for which the WITH clause is a prefix.
+ */
+ static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){
+ if( pSelect ){
+ pSelect->pWith = pWith;
+ parserDoubleLinkSelect(pParse, pSelect);
+ }else{
+ sqlite3WithDelete(pParse->db, pWith);
+ }
+ return pSelect;
+ }
}
%ifndef SQLITE_OMIT_CTE
-select(A) ::= WITH wqlist(W) selectnowith(X). {
- Select *p = X;
- if( p ){
- p->pWith = W;
- parserDoubleLinkSelect(pParse, p);
- }else{
- sqlite3WithDelete(pParse->db, W);
- }
- A = p;
-}
-select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {
- Select *p = X;
- if( p ){
- p->pWith = W;
- parserDoubleLinkSelect(pParse, p);
- }else{
- sqlite3WithDelete(pParse->db, W);
- }
- A = p;
-}
+select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);}
+select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X).
+ {A = attachWithToSelect(pParse,X,W);}
%endif /* SQLITE_OMIT_CTE */
select(A) ::= selectnowith(X). {
Select *p = X;
@@ -714,8 +711,8 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z)
}else if( F->nSrc==1 ){
A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,N,U);
if( A ){
- struct SrcList_item *pNew = &A->a[A->nSrc-1];
- struct SrcList_item *pOld = F->a;
+ SrcItem *pNew = &A->a[A->nSrc-1];
+ SrcItem *pOld = F->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -883,7 +880,7 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
/////////////////////////// The DELETE statement /////////////////////////////
//
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
-cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
+cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W)
orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
@@ -896,7 +893,7 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
sqlite3DeleteFrom(pParse,X,W,O,L);
}
%else
-cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
+cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W,0,0);
}
@@ -904,15 +901,23 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
%type where_opt {Expr*}
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
+%type where_opt_ret {Expr*}
+%destructor where_opt_ret {sqlite3ExprDelete(pParse->db, $$);}
where_opt(A) ::= . {A = 0;}
where_opt(A) ::= WHERE expr(X). {A = X;}
+where_opt_ret(A) ::= . {A = 0;}
+where_opt_ret(A) ::= WHERE expr(X). {A = X;}
+where_opt_ret(A) ::= RETURNING selcollist(X).
+ {sqlite3AddReturning(pParse,X); A = 0;}
+where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y).
+ {sqlite3AddReturning(pParse,Y); A = X;}
////////////////////////// The UPDATE command ////////////////////////////////
//
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
- where_opt(W) orderby_opt(O) limit_opt(L). {
+ where_opt_ret(W) orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
X = sqlite3SrcListAppendList(pParse, X, F);
sqlite3ExprListCheckLength(pParse,Y,"set list");
@@ -927,7 +932,7 @@ cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
}
%else
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
- where_opt(W). {
+ where_opt_ret(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
X = sqlite3SrcListAppendList(pParse, X, F);
@@ -961,7 +966,7 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S)
upsert(U). {
sqlite3Insert(pParse, X, S, F, R, U);
}
-cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
+cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES returning.
{
sqlite3Insert(pParse, X, 0, F, R, 0);
}
@@ -974,16 +979,20 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
// avoid unreachable code.
//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
upsert(A) ::= . { A = 0; }
+upsert(A) ::= RETURNING selcollist(X). { A = 0; sqlite3AddReturning(pParse,X); }
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
DO UPDATE SET setlist(Z) where_opt(W) upsert(N).
{ A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);}
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N).
{ A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); }
-upsert(A) ::= ON CONFLICT DO NOTHING.
+upsert(A) ::= ON CONFLICT DO NOTHING returning.
{ A = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
-upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W).
+upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W) returning.
{ A = sqlite3UpsertNew(pParse->db,0,0,Z,W,0);}
+returning ::= RETURNING selcollist(X). {sqlite3AddReturning(pParse,X);}
+returning ::= .
+
%type insert_cmd {int}
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
@@ -1624,6 +1633,10 @@ cmd ::= ALTER TABLE add_column_fullname
Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &Y);
}
+cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). {
+ sqlite3AlterDropColumn(pParse, X, &Y);
+}
+
add_column_fullname ::= fullname(X). {
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, X);
@@ -1661,17 +1674,26 @@ anylist ::= anylist ANY.
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
%type wqlist {With*}
%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
+%type wqitem {Cte*}
+// %destructor wqitem {sqlite3CteDelete(pParse->db, $$);} // not reachable
with ::= .
%ifndef SQLITE_OMIT_CTE
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
-wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
- A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/
+%type wqas {u8}
+wqas(A) ::= AS. {A = M10d_Any;}
+wqas(A) ::= AS MATERIALIZED. {A = M10d_Yes;}
+wqas(A) ::= AS NOT MATERIALIZED. {A = M10d_No;}
+wqitem(A) ::= nm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. {
+ A = sqlite3CteNew(pParse, &X, Y, Z, M); /*A-overwrites-X*/
}
-wqlist(A) ::= wqlist(A) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
- A = sqlite3WithAdd(pParse, A, &X, Y, Z);
+wqlist(A) ::= wqitem(X). {
+ A = sqlite3WithAdd(pParse, 0, X); /*A-overwrites-X*/
+}
+wqlist(A) ::= wqlist(A) COMMA wqitem(X). {
+ A = sqlite3WithAdd(pParse, A, X);
}
%endif SQLITE_OMIT_CTE
diff --git a/src/pcache1.c b/src/pcache1.c
index ed762ebf70..3eae6b63cd 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -461,6 +461,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
p->page.pExtra = &p[1];
p->isBulkLocal = 0;
p->isAnchor = 0;
+ p->pLruPrev = 0; /* Initializing this saves a valgrind error */
}
(*pCache->pnPurgeable)++;
return p;
diff --git a/src/prepare.c b/src/prepare.c
index 13fd1d33b2..d79495d2c6 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -21,7 +21,7 @@
*/
static void corruptSchema(
InitData *pData, /* Initialization context */
- const char *zObj, /* Object being parsed at the point of error */
+ char **azObj, /* Type and name of object being parsed */
const char *zExtra /* Error information */
){
sqlite3 *db = pData->db;
@@ -29,14 +29,18 @@ static void corruptSchema(
pData->rc = SQLITE_NOMEM_BKPT;
}else if( pData->pzErrMsg[0]!=0 ){
/* A error message has already been generated. Do not overwrite it */
- }else if( pData->mInitFlags & INITFLAG_AlterTable ){
- *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
+ }else if( pData->mInitFlags & (INITFLAG_AlterRename|INITFLAG_AlterDrop) ){
+ *pData->pzErrMsg = sqlite3MPrintf(db,
+ "error in %s %s after %s: %s", azObj[0], azObj[1],
+ (pData->mInitFlags & INITFLAG_AlterRename) ? "rename" : "drop column",
+ zExtra
+ );
pData->rc = SQLITE_ERROR;
}else if( db->flags & SQLITE_WriteSchema ){
pData->rc = SQLITE_CORRUPT_BKPT;
}else{
char *z;
- if( zObj==0 ) zObj = "?";
+ const char *zObj = azObj[1] ? azObj[1] : "?";
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
*pData->pzErrMsg = z;
@@ -94,19 +98,26 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
db->mDbFlags |= DBFLAG_EncodingFixed;
pData->nInitRow++;
if( db->mallocFailed ){
- corruptSchema(pData, argv[1], 0);
+ corruptSchema(pData, argv, 0);
return 1;
}
assert( iDb>=0 && iDbnDb );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[3]==0 ){
- corruptSchema(pData, argv[1], 0);
- }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){
+ corruptSchema(pData, argv, 0);
+ }else if( argv[4]
+ && 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]]
+ && 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
** structures that describe the table, index, or view.
+ **
+ ** No other valid SQL statement, other than the variable CREATE statements,
+ ** can begin with the letters "C" and "R". Thus, it is not possible run
+ ** any other kind of statement while parsing the schema, even a corrupt
+ ** schema.
*/
int rc;
u8 saved_iDb = db->init.iDb;
@@ -119,7 +130,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|| (db->init.newTnum>pData->mxPage && pData->mxPage>0)
){
if( sqlite3Config.bExtraSchemaChecks ){
- corruptSchema(pData, argv[1], "invalid rootpage");
+ corruptSchema(pData, argv, "invalid rootpage");
}
}
db->init.orphanTrigger = 0;
@@ -138,13 +149,13 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
- corruptSchema(pData, argv[1], sqlite3_errmsg(db));
+ corruptSchema(pData, argv, sqlite3_errmsg(db));
}
}
}
sqlite3_finalize(pStmt);
}else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
- corruptSchema(pData, argv[1], 0);
+ corruptSchema(pData, argv, 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -155,7 +166,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
Index *pIndex;
pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
if( pIndex==0 ){
- corruptSchema(pData, argv[1], "orphan index");
+ corruptSchema(pData, argv, "orphan index");
}else
if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0
|| pIndex->tnum<2
@@ -163,7 +174,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|| sqlite3IndexHasDuplicateRootPage(pIndex)
){
if( sqlite3Config.bExtraSchemaChecks ){
- corruptSchema(pData, argv[1], "invalid rootpage");
+ corruptSchema(pData, argv, "invalid rootpage");
}
}
}
@@ -543,28 +554,21 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
return i;
}
-/*
-** Deallocate a single AggInfo object
-*/
-static void agginfoFree(sqlite3 *db, AggInfo *p){
- sqlite3DbFree(db, p->aCol);
- sqlite3DbFree(db, p->aFunc);
- sqlite3DbFree(db, p);
-}
-
/*
** Free all memory allocations in the pParse object
*/
void sqlite3ParserReset(Parse *pParse){
sqlite3 *db = pParse->db;
- AggInfo *pThis = pParse->pAggList;
- while( pThis ){
- AggInfo *pNext = pThis->pNext;
- agginfoFree(db, pThis);
- pThis = pNext;
+ while( pParse->pCleanup ){
+ ParseCleanup *pCleanup = pParse->pCleanup;
+ pParse->pCleanup = pCleanup->pNext;
+ pCleanup->xCleanup(db, pCleanup->pPtr);
+ sqlite3DbFreeNN(db, pCleanup);
}
sqlite3DbFree(db, pParse->aLabel);
- sqlite3ExprListDelete(db, pParse->pConstExpr);
+ if( pParse->pConstExpr ){
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
+ }
if( db ){
assert( db->lookaside.bDisable >= pParse->disableLookaside );
db->lookaside.bDisable -= pParse->disableLookaside;
@@ -573,6 +577,47 @@ void sqlite3ParserReset(Parse *pParse){
pParse->disableLookaside = 0;
}
+/*
+** Add a new cleanup operation to a Parser. The cleanup should happen when
+** the parser object is destroyed. But, beware: the cleanup might happen
+** immediately.
+**
+** Use this mechanism for uncommon cleanups. There is a higher setup
+** cost for this mechansim (an extra malloc), so it should not be used
+** for common cleanups that happen on most calls. But for less
+** common cleanups, we save a single NULL-pointer comparison in
+** sqlite3ParserReset(), which reduces the total CPU cycle count.
+**
+** If a memory allocation error occurs, then the cleanup happens immediately.
+** When eithr SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
+** pParse->earlyCleanup flag is set in that case. Calling code show verify
+** that test cases exist for which this happens, to guard against possible
+** use-after-free errors following an OOM. The preferred way to do this is
+** to immediately follow the call to this routine with:
+**
+** testcase( pParse->earlyCleanup );
+*/
+void *sqlite3ParserAddCleanup(
+ Parse *pParse, /* Destroy when this Parser finishes */
+ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
+ void *pPtr /* Pointer to object to be cleaned up */
+){
+ ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ if( pCleanup ){
+ pCleanup->pNext = pParse->pCleanup;
+ pParse->pCleanup = pCleanup;
+ pCleanup->pPtr = pPtr;
+ pCleanup->xCleanup = xCleanup;
+ }else{
+ xCleanup(pParse->db, pPtr);
+ pPtr = 0;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+ pParse->earlyCleanup = 1;
+#endif
+ }
+ return pPtr;
+}
+
/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
@@ -671,12 +716,6 @@ static int sqlite3Prepare(
}
assert( 0==sParse.nQueryLoop );
- if( sParse.rc==SQLITE_DONE ){
- sParse.rc = SQLITE_OK;
- }
- if( sParse.checkSchema ){
- schemaIsValid(&sParse);
- }
if( pzTail ){
*pzTail = sParse.zTail;
}
@@ -687,20 +726,28 @@ static int sqlite3Prepare(
if( db->mallocFailed ){
sParse.rc = SQLITE_NOMEM_BKPT;
}
- rc = sParse.rc;
- if( rc!=SQLITE_OK ){
- if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
- assert(!(*ppStmt));
+ if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){
+ if( sParse.checkSchema ){
+ schemaIsValid(&sParse);
+ }
+ if( sParse.pVdbe ){
+ sqlite3VdbeFinalize(sParse.pVdbe);
+ }
+ assert( 0==(*ppStmt) );
+ rc = sParse.rc;
+ if( zErrMsg ){
+ sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
+ sqlite3DbFree(db, zErrMsg);
+ }else{
+ sqlite3Error(db, rc);
+ }
}else{
+ assert( zErrMsg==0 );
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
+ rc = SQLITE_OK;
+ sqlite3ErrorClear(db);
}
- if( zErrMsg ){
- sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
- sqlite3DbFree(db, zErrMsg);
- }else{
- sqlite3Error(db, rc);
- }
/* Delete any TriggerPrg structures allocated while parsing this statement. */
while( sParse.pTriggerPrg ){
diff --git a/src/printf.c b/src/printf.c
index f78d3bbb17..9aab863ed2 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -856,7 +856,7 @@ void sqlite3_str_vappendf(
case etSRCLIST: {
SrcList *pSrc;
int k;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
pSrc = va_arg(ap, SrcList*);
k = va_arg(ap, int);
@@ -921,7 +921,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
i64 szNew = p->nChar;
- szNew += N + 1;
+ szNew += (sqlite3_int64)N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
** to avoid having to call this routine too often */
diff --git a/src/resolve.c b/src/resolve.c
index b55bdc4187..6d3ed62f93 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -70,7 +70,6 @@ static void resolveAlias(
ExprList *pEList, /* A result set */
int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
Expr *pExpr, /* Transform this into an alias to the result set */
- const char *zType, /* "GROUP" or "ORDER" or "" */
int nSubquery /* Number of subqueries that the label is moving */
){
Expr *pOrig; /* The iCol-th column of the result set */
@@ -83,7 +82,7 @@ static void resolveAlias(
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( pDup!=0 ){
- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
+ incrAggFunctionDepth(pDup, nSubquery);
if( pExpr->op==TK_COLLATE ){
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
}
@@ -112,7 +111,6 @@ static void resolveAlias(
}
sqlite3DbFree(db, pDup);
}
- ExprSetProperty(pExpr, EP_Alias);
}
@@ -247,8 +245,8 @@ static int lookupName(
int cntTab = 0; /* Number of matching table names */
int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
- struct SrcList_item *pItem; /* Use for looping over pSrcList items */
- struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
+ SrcItem *pItem; /* Use for looping over pSrcList items */
+ SrcItem *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
@@ -369,25 +367,29 @@ static int lookupName(
#if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT)
/* If we have not already resolved the name, then maybe
** it is a new.* or old.* trigger argument reference. Or
- ** maybe it is an excluded.* from an upsert.
+ ** maybe it is an excluded.* from an upsert. Or maybe it is
+ ** a reference in the RETURNING clause to a table being modified.
*/
- if( zDb==0 && zTab!=0 && cntTab==0 ){
+ if( cnt==0 && zDb==0 ){
pTab = 0;
#ifndef SQLITE_OMIT_TRIGGER
if( pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
- if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
+ if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1;
pTab = pParse->pTriggerTab;
- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
+ }else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){
pExpr->iTable = 0;
pTab = pParse->pTriggerTab;
+ }else if( pParse->bReturning && (pNC->ncFlags & NC_UBaseReg)!=0 ){
+ pExpr->iTable = op!=TK_DELETE;
+ pTab = pParse->pTriggerTab;
}
}
#endif /* SQLITE_OMIT_TRIGGER */
#ifndef SQLITE_OMIT_UPSERT
- if( (pNC->ncFlags & NC_UUpsert)!=0 ){
+ if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){
Upsert *pUpsert = pNC->uNC.pUpsert;
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
pTab = pUpsert->pUpsertSrc->a[0].pTab;
@@ -426,27 +428,32 @@ static int lookupName(
pExpr->iTable = pNC->uNC.pUpsert->regData +
sqlite3TableColumnToStorage(pTab, iCol);
eNewExprOp = TK_REGISTER;
- ExprSetProperty(pExpr, EP_Alias);
}
}else
#endif /* SQLITE_OMIT_UPSERT */
{
-#ifndef SQLITE_OMIT_TRIGGER
- if( iCol<0 ){
- pExpr->affExpr = SQLITE_AFF_INTEGER;
- }else if( pExpr->iTable==0 ){
- testcase( iCol==31 );
- testcase( iCol==32 );
- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<y.pTab = pTab;
- pExpr->iColumn = (i16)iCol;
- eNewExprOp = TK_TRIGGER;
+ if( pParse->bReturning ){
+ eNewExprOp = TK_REGISTER;
+ pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable
+ + iCol + 1;
+ }else{
+ pExpr->iColumn = (i16)iCol;
+ eNewExprOp = TK_TRIGGER;
+#ifndef SQLITE_OMIT_TRIGGER
+ if( iCol<0 ){
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
+ }else if( pExpr->iTable==0 ){
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<pLeft);
- pExpr->pLeft = 0;
- sqlite3ExprDelete(db, pExpr->pRight);
- pExpr->pRight = 0;
+ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
+ sqlite3ExprDelete(db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pExpr->pRight);
+ pExpr->pRight = 0;
+ }
pExpr->op = eNewExprOp;
ExprSetProperty(pExpr, EP_Leaf);
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );
- if( !ExprHasProperty(pExpr, EP_Alias) ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ if( pParse->db->xAuth
+ && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
+ ){
sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
}
+#endif
/* Increment the nRef value on all name contexts from TopNC up to
** the point where the name matched. */
for(;;){
@@ -651,7 +664,7 @@ lookupname_end:
Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
if( p ){
- struct SrcList_item *pItem = &pSrc->a[iSrc];
+ SrcItem *pItem = &pSrc->a[iSrc];
Table *pTab = p->y.pTab = pItem->pTab;
p->iTable = pItem->iCursor;
if( p->y.pTab->iPKey==iCol ){
@@ -763,7 +776,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
*/
case TK_ROW: {
SrcList *pSrcList = pNC->pSrcList;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert( pSrcList && pSrcList->nSrc>=1 );
pItem = pSrcList->a;
pExpr->op = TK_COLUMN;
@@ -774,6 +787,44 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
break;
}
+ /* An " IS NOT NULL" or " IS NULL". After resolving the
+ ** LHS, check if there is a NOT NULL constraint in the schema that
+ ** means the value of the expression can be determined immediately.
+ ** If that is the case, replace the current expression node with
+ ** a TK_TRUEFALSE node.
+ **
+ ** If the node is replaced with a TK_TRUEFALSE node, then also restore
+ ** the NameContext ref-counts to the state they where in before the
+ ** LHS expression was resolved. This prevents the current select
+ ** from being erroneously marked as correlated in some cases.
+ */
+ case TK_NOTNULL:
+ case TK_ISNULL: {
+ int anRef[8];
+ NameContext *p;
+ int i;
+ for(i=0, p=pNC; p && ipNext, i++){
+ anRef[i] = p->nRef;
+ }
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
+ if( pExpr->op==TK_NOTNULL ){
+ pExpr->u.zToken = "true";
+ ExprSetProperty(pExpr, EP_IsTrue);
+ }else{
+ pExpr->u.zToken = "false";
+ ExprSetProperty(pExpr, EP_IsFalse);
+ }
+ pExpr->op = TK_TRUEFALSE;
+ for(i=0, p=pNC; p && ipNext, i++){
+ p->nRef = anRef[i];
+ }
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ }
+ return WRC_Prune;
+ }
+
/* A column name: ID
** Or table name and column name: ID.ID
** Or a database, table and column: ID.ID.ID
@@ -1391,8 +1442,7 @@ int sqlite3ResolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
- zType,0);
+ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0);
}
}
return 0;
@@ -1577,27 +1627,26 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Recursively resolve names in all subqueries
*/
for(i=0; ipSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
+ SrcItem *pItem = &p->pSrc->a[i];
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
- NameContext *pNC; /* Used to iterate name contexts */
- int nRef = 0; /* Refcount for pOuterNC and outer contexts */
+ int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
- /* Count the total number of references to pOuterNC and all of its
- ** parent contexts. After resolving references to expressions in
- ** pItem->pSelect, check if this value has changed. If so, then
- ** SELECT statement pItem->pSelect must be correlated. Set the
- ** pItem->fg.isCorrelated flag if this is the case. */
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
-
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
pParse->zAuthContext = zSavedContext;
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
- assert( pItem->fg.isCorrelated==0 && nRef<=0 );
- pItem->fg.isCorrelated = (nRef!=0);
+ /* If the number of references to the outer context changed when
+ ** expressions in the sub-select were resolved, the sub-select
+ ** is correlated. It is not required to check the refcount on any
+ ** but the innermost outer context object, as lookupName() increments
+ ** the refcount on all contexts between the current one and the
+ ** context containing the column when it resolves a name. */
+ if( pOuterNC ){
+ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
+ pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
+ }
}
}
@@ -1639,7 +1688,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** Minor point: If this is the case, then the expression will be
** re-evaluated for each reference to it.
*/
- assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
+ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 );
sNC.uNC.pEList = p->pEList;
sNC.ncFlags |= NC_UEList;
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
@@ -1647,7 +1696,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Resolve names in table-valued-function arguments */
for(i=0; ipSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
+ SrcItem *pItem = &p->pSrc->a[i];
if( pItem->fg.isTabFunc
&& sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
){
diff --git a/src/select.c b/src/select.c
index d5691468fe..75050d5740 100644
--- a/src/select.c
+++ b/src/select.c
@@ -262,7 +262,7 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
** Return the index of a column in a table. Return -1 if the column
** is not contained in the table.
*/
-static int columnIndex(Table *pTab, const char *zCol){
+int sqlite3ColumnIndex(Table *pTab, const char *zCol){
int i;
u8 h = sqlite3StrIHash(zCol);
Column *pCol;
@@ -294,7 +294,7 @@ static int tableAndColumnIndex(
assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */
for(i=0; ia[i].pTab, zCol);
+ iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol);
if( iCol>=0
&& (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0)
){
@@ -347,7 +347,7 @@ static void addWhereTerm(
ExprSetProperty(pEq, EP_FromJoin);
assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(pEq, EP_NoReduce);
- pEq->iRightJoinTable = (i16)pE2->iTable;
+ pEq->iRightJoinTable = pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
}
@@ -383,7 +383,7 @@ void sqlite3SetJoinExpr(Expr *p, int iTable){
ExprSetProperty(p, EP_FromJoin);
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
- p->iRightJoinTable = (i16)iTable;
+ p->iRightJoinTable = iTable;
if( p->op==TK_FUNCTION && p->x.pList ){
int i;
for(i=0; ix.pList->nExpr; i++){
@@ -407,6 +407,9 @@ static void unsetJoinExpr(Expr *p, int iTable){
&& (iTable<0 || p->iRightJoinTable==iTable) ){
ExprClearProperty(p, EP_FromJoin);
}
+ if( p->op==TK_COLUMN && p->iTable==iTable ){
+ ExprClearProperty(p, EP_CanBeNull);
+ }
if( p->op==TK_FUNCTION && p->x.pList ){
int i;
for(i=0; ix.pList->nExpr; i++){
@@ -435,8 +438,8 @@ static void unsetJoinExpr(Expr *p, int iTable){
static int sqliteProcessJoin(Parse *pParse, Select *p){
SrcList *pSrc; /* All tables in the FROM clause */
int i, j; /* Loop counters */
- struct SrcList_item *pLeft; /* Left table being joined */
- struct SrcList_item *pRight; /* Right table being joined */
+ SrcItem *pLeft; /* Left table being joined */
+ SrcItem *pRight; /* Right table being joined */
pSrc = p->pSrc;
pLeft = &pSrc->a[0];
@@ -504,7 +507,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
int iRightCol; /* Column number of matching column on the right */
zName = pList->a[j].zName;
- iRightCol = columnIndex(pRightTab, zName);
+ iRightCol = sqlite3ColumnIndex(pRightTab, zName);
if( iRightCol<0
|| !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0)
){
@@ -2086,6 +2089,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){
const char *zType;
int n, m;
+ pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
zType = columnType(&sNC, p, 0, 0, 0);
/* pCol->szEst = ... // Column size est for SELECT tables never used */
@@ -3596,7 +3600,7 @@ static void substSelect(
int doPrior /* Do substitutes on p->pPrior too */
){
SrcList *pSrc;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
int i;
if( !p ) return;
do{
@@ -3626,7 +3630,7 @@ static void substSelect(
** pSrcItem->colUsed mask.
*/
static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
pItem = pWalker->u.pSrcItem;
if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue;
@@ -3636,7 +3640,7 @@ static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){
}
static void recomputeColumnsUsed(
Select *pSelect, /* The complete SELECT statement */
- struct SrcList_item *pSrcItem /* Which FROM clause item to recompute */
+ SrcItem *pSrcItem /* Which FROM clause item to recompute */
){
Walker w;
if( NEVER(pSrcItem->pTab==0) ) return;
@@ -3649,6 +3653,89 @@ static void recomputeColumnsUsed(
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+/*
+** Assign new cursor numbers to each of the items in pSrc. For each
+** new cursor number assigned, set an entry in the aCsrMap[] array
+** to map the old cursor number to the new:
+**
+** aCsrMap[iOld] = iNew;
+**
+** The array is guaranteed by the caller to be large enough for all
+** existing cursor numbers in pSrc.
+**
+** If pSrc contains any sub-selects, call this routine recursively
+** on the FROM clause of each such sub-select, with iExcept set to -1.
+*/
+static void srclistRenumberCursors(
+ Parse *pParse, /* Parse context */
+ int *aCsrMap, /* Array to store cursor mappings in */
+ SrcList *pSrc, /* FROM clause to renumber */
+ int iExcept /* FROM clause item to skip */
+){
+ int i;
+ SrcItem *pItem;
+ for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){
+ if( i!=iExcept ){
+ Select *p;
+ pItem->iCursor = aCsrMap[pItem->iCursor] = pParse->nTab++;
+ for(p=pItem->pSelect; p; p=p->pPrior){
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1);
+ }
+ }
+ }
+}
+
+/*
+** Expression walker callback used by renumberCursors() to update
+** Expr objects to match newly assigned cursor numbers.
+*/
+static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){
+ int *aCsrMap = pWalker->u.aiCol;
+ int op = pExpr->op;
+ if( (op==TK_COLUMN || op==TK_IF_NULL_ROW) && aCsrMap[pExpr->iTable] ){
+ pExpr->iTable = aCsrMap[pExpr->iTable];
+ }
+ if( ExprHasProperty(pExpr, EP_FromJoin) && aCsrMap[pExpr->iRightJoinTable] ){
+ pExpr->iRightJoinTable = aCsrMap[pExpr->iRightJoinTable];
+ }
+ return WRC_Continue;
+}
+
+/*
+** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc)
+** of the SELECT statement passed as the second argument, and to each
+** cursor in the FROM clause of any FROM clause sub-selects, recursively.
+** Except, do not assign a new cursor number to the iExcept'th element in
+** the FROM clause of (*p). Update all expressions and other references
+** to refer to the new cursor numbers.
+**
+** Argument aCsrMap is an array that may be used for temporary working
+** space. Two guarantees are made by the caller:
+**
+** * the array is larger than the largest cursor number used within the
+** select statement passed as an argument, and
+**
+** * the array entries for all cursor numbers that do *not* appear in
+** FROM clauses of the select statement as described above are
+** initialized to zero.
+*/
+static void renumberCursors(
+ Parse *pParse, /* Parse context */
+ Select *p, /* Select to renumber cursors within */
+ int iExcept, /* FROM clause item to skip */
+ int *aCsrMap /* Working space */
+){
+ Walker w;
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept);
+ memset(&w, 0, sizeof(w));
+ w.u.aiCol = aCsrMap;
+ w.xExprCallback = renumberCursorsCb;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ sqlite3WalkSelect(&w, p);
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
@@ -3743,9 +3830,9 @@ static void recomputeColumnsUsed(
** (17c) every term within the subquery compound must have a FROM clause
** (17d) the outer query may not be
** (17d1) aggregate, or
-** (17d2) DISTINCT, or
-** (17d3) a join.
-** (17e) the subquery may not contain window functions
+** (17d2) DISTINCT
+** (17e) the subquery may not contain window functions, and
+** (17f) the subquery must not be the RHS of a LEFT JOIN.
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
@@ -3761,8 +3848,8 @@ static void recomputeColumnsUsed(
** syntax error and return a detailed message.
**
** (18) If the sub-query is a compound select, then all terms of the
-** ORDER BY clause of the parent must be simple references to
-** columns of the sub-query.
+** ORDER BY clause of the parent must be copies of a term returned
+** by the parent query.
**
** (19) If the subquery uses LIMIT then the outer query may not
** have a WHERE clause.
@@ -3778,9 +3865,8 @@ static void recomputeColumnsUsed(
**
** (22) The subquery may not be a recursive CTE.
**
-** (**) Subsumed into restriction (17d3). Was: If the outer query is
-** a recursive CTE, then the sub-query may not be a compound query.
-** This restriction is because transforming the
+** (23) If the outer query is a recursive CTE, then the sub-query may not be
+** a compound query. This restriction is because transforming the
** parent to a compound query confuses the code that handles
** recursive queries in multiSelect().
**
@@ -3822,9 +3908,10 @@ static int flattenSubquery(
int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
int i; /* Loop counter */
Expr *pWhere; /* The WHERE clause */
- struct SrcList_item *pSubitem; /* The subquery */
+ SrcItem *pSubitem; /* The subquery */
sqlite3 *db = pParse->db;
Walker w; /* Walker to persist agginfo data */
+ int *aCsrMap = 0;
/* Check to see if flattening is permitted. Return 0 if not.
*/
@@ -3920,13 +4007,14 @@ static int flattenSubquery(
if( pSub->pOrderBy ){
return 0; /* Restriction (20) */
}
- if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
- return 0; /* (17d1), (17d2), or (17d3) */
+ if( isAgg || (p->selFlags & SF_Distinct)!=0 || isLeftJoin>0 ){
+ return 0; /* (17d1), (17d2), or (17f) */
}
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
assert( pSub->pSrc!=0 );
+ assert( (pSub->selFlags & SF_Recursive)==0 );
assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */
|| (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */
@@ -3947,15 +4035,14 @@ static int flattenSubquery(
if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
}
}
- }
- /* Ex-restriction (23):
- ** The only way that the recursive part of a CTE can contain a compound
- ** subquery is for the subquery to be one term of a join. But if the
- ** subquery is a join, then the flattening has already been stopped by
- ** restriction (17d3)
- */
- assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 );
+ /* Restriction (23) */
+ if( (p->selFlags & SF_Recursive) ) return 0;
+
+ if( pSrc->nSrc>1 ){
+ aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int));
+ }
+ }
/***** If we reach this point, flattening is permitted. *****/
SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
@@ -3967,6 +4054,17 @@ static int flattenSubquery(
testcase( i==SQLITE_DENY );
pParse->zAuthContext = zSavedAuthContext;
+ /* Delete the transient structures associated with thesubquery */
+ pSub1 = pSubitem->pSelect;
+ sqlite3DbFree(db, pSubitem->zDatabase);
+ sqlite3DbFree(db, pSubitem->zName);
+ sqlite3DbFree(db, pSubitem->zAlias);
+ pSubitem->zDatabase = 0;
+ pSubitem->zName = 0;
+ pSubitem->zAlias = 0;
+ pSubitem->pSelect = 0;
+ assert( pSubitem->pOn==0 );
+
/* If the sub-query is a compound SELECT statement, then (by restrictions
** 17 and 18 above) it must be a UNION ALL and the parent query must
** be of the form:
@@ -4005,18 +4103,22 @@ static int flattenSubquery(
ExprList *pOrderBy = p->pOrderBy;
Expr *pLimit = p->pLimit;
Select *pPrior = p->pPrior;
+ Table *pItemTab = pSubitem->pTab;
+ pSubitem->pTab = 0;
p->pOrderBy = 0;
- p->pSrc = 0;
p->pPrior = 0;
p->pLimit = 0;
pNew = sqlite3SelectDup(db, p, 0);
p->pLimit = pLimit;
p->pOrderBy = pOrderBy;
- p->pSrc = pSrc;
p->op = TK_ALL;
+ pSubitem->pTab = pItemTab;
if( pNew==0 ){
p->pPrior = pPrior;
}else{
+ if( aCsrMap && db->mallocFailed==0 ){
+ renumberCursors(pParse, pNew, iFrom, aCsrMap);
+ }
pNew->pPrior = pPrior;
if( pPrior ) pPrior->pNext = pNew;
pNew->pNext = p;
@@ -4024,24 +4126,13 @@ static int flattenSubquery(
SELECTTRACE(2,pParse,p,("compound-subquery flattener"
" creates %u as peer\n",pNew->selId));
}
- if( db->mallocFailed ) return 1;
+ assert( pSubitem->pSelect==0 );
+ }
+ sqlite3DbFree(db, aCsrMap);
+ if( db->mallocFailed ){
+ pSubitem->pSelect = pSub1;
+ return 1;
}
-
- /* Begin flattening the iFrom-th entry of the FROM clause
- ** in the outer query.
- */
- pSub = pSub1 = pSubitem->pSelect;
-
- /* Delete the transient table structure associated with the
- ** subquery
- */
- sqlite3DbFree(db, pSubitem->zDatabase);
- sqlite3DbFree(db, pSubitem->zName);
- sqlite3DbFree(db, pSubitem->zAlias);
- pSubitem->zDatabase = 0;
- pSubitem->zName = 0;
- pSubitem->zAlias = 0;
- pSubitem->pSelect = 0;
/* Defer deleting the Table object associated with the
** subquery until code generation is
@@ -4054,8 +4145,10 @@ static int flattenSubquery(
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- pTabToDel->pNextZombie = pToplevel->pZombieTab;
- pToplevel->pZombieTab = pTabToDel;
+ sqlite3ParserAddCleanup(pToplevel,
+ (void(*)(sqlite3*,void*))sqlite3DeleteTable,
+ pTabToDel);
+ testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
}
@@ -4075,6 +4168,7 @@ static int flattenSubquery(
** those references with expressions that resolve to the subquery FROM
** elements we are now copying in.
*/
+ pSub = pSub1;
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = 0;
@@ -4083,16 +4177,10 @@ static int flattenSubquery(
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
pSrc = pParent->pSrc; /* FROM clause of the outer query */
- if( pSrc ){
- assert( pParent==p ); /* First time through the loop */
- jointype = pSubitem->fg.jointype;
- }else{
- assert( pParent!=p ); /* 2nd and subsequent times through the loop */
- pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
- if( pSrc==0 ) break;
- pParent->pSrc = pSrc;
+ if( pParent==p ){
+ jointype = pSubitem->fg.jointype; /* First time through the loop */
}
-
+
/* The subquery uses a single slot of the FROM clause of the outer
** query. If the subquery has more than one element in its FROM clause,
** then expand the outer query to make space for it to hold all elements
@@ -4404,6 +4492,35 @@ static int propagateConstants(
return nChng;
}
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+# if !defined(SQLITE_OMIT_WINDOWFUNC)
+/*
+** This function is called to determine whether or not it is safe to
+** push WHERE clause expression pExpr down to FROM clause sub-query
+** pSubq, which contains at least one window function. Return 1
+** if it is safe and the expression should be pushed down, or 0
+** otherwise.
+**
+** It is only safe to push the expression down if it consists only
+** of constants and copies of expressions that appear in the PARTITION
+** BY clause of all window function used by the sub-query. It is safe
+** to filter out entire partitions, but not rows within partitions, as
+** this may change the results of the window functions.
+**
+** At the time this function is called it is guaranteed that
+**
+** * the sub-query uses only one distinct window frame, and
+** * that the window frame has a PARTITION BY clase.
+*/
+static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
+ assert( pSubq->pWin->pPartition );
+ assert( (pSubq->selFlags & SF_MultiPart)==0 );
+ assert( pSubq->pPrior==0 );
+ return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition);
+}
+# endif /* SQLITE_OMIT_WINDOWFUNC */
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** Make copies of relevant WHERE clause terms of the outer query into
@@ -4451,9 +4568,24 @@ static int propagateConstants(
** But if the (b2=2) term were to be pushed down into the bb subquery,
** then the (1,1,NULL) row would be suppressed.
**
-** (6) The inner query features one or more window-functions (since
-** changes to the WHERE clause of the inner query could change the
-** window over which window functions are calculated).
+** (6) Window functions make things tricky as changes to the WHERE clause
+** of the inner query could change the window over which window
+** functions are calculated. Therefore, do not attempt the optimization
+** if:
+**
+** (6a) The inner query uses multiple incompatible window partitions.
+**
+** (6b) The inner query is a compound and uses window-functions.
+**
+** (6c) The WHERE clause does not consist entirely of constants and
+** copies of expressions found in the PARTITION BY clause of
+** all window-functions used by the sub-query. It is safe to
+** filter out entire partitions, as this does not change the
+** window over which any window-function is calculated.
+**
+** (7) The inner query is a Common Table Expression (CTE) that should
+** be materialized. (This restriction is implemented in the calling
+** routine.)
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
@@ -4467,13 +4599,17 @@ static int pushDownWhereTerms(
){
Expr *pNew;
int nChng = 0;
- Select *pSel;
if( pWhere==0 ) return 0;
- if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */
+ if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
- for(pSel=pSubq; pSel; pSel=pSel->pPrior){
- if( pSel->pWin ) return 0; /* restriction (6) */
+ if( pSubq->pPrior ){
+ Select *pSel;
+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){
+ if( pSel->pWin ) return 0; /* restriction (6b) */
+ }
+ }else{
+ if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
}
#endif
@@ -4509,6 +4645,7 @@ static int pushDownWhereTerms(
}
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
nChng++;
+ pSubq->selFlags |= SF_PushDown;
while( pSubq ){
SubstContext x;
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
@@ -4519,6 +4656,14 @@ static int pushDownWhereTerms(
x.isLeftJoin = 0;
x.pEList = pSubq->pEList;
pNew = substExpr(&x, pNew);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
+ /* Restriction 6c has prevented push-down in this case */
+ sqlite3ExprDelete(pParse->db, pNew);
+ nChng--;
+ break;
+ }
+#endif
if( pSubq->selFlags & SF_Aggregate ){
pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
}else{
@@ -4557,7 +4702,11 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
assert( !IsWindowFunc(pFunc) );
- if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){
+ if( pEList==0
+ || pEList->nExpr!=1
+ || ExprHasProperty(pFunc, EP_WinFunc)
+ || OptimizationDisabled(db, SQLITE_MinMaxOpt)
+ ){
return eRet;
}
zFunc = pFunc->u.zToken;
@@ -4620,24 +4769,26 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
** SQLITE_ERROR and leave an error in pParse. Otherwise, populate
** pFrom->pIndex and return SQLITE_OK.
*/
-int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
- if( pFrom->pTab && pFrom->fg.isIndexedBy ){
- Table *pTab = pFrom->pTab;
- char *zIndexedBy = pFrom->u1.zIndexedBy;
- Index *pIdx;
- for(pIdx=pTab->pIndex;
- pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
- pIdx=pIdx->pNext
- );
- if( !pIdx ){
- sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
- pParse->checkSchema = 1;
- return SQLITE_ERROR;
- }
- pFrom->pIBIndex = pIdx;
+int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){
+ Table *pTab = pFrom->pTab;
+ char *zIndexedBy = pFrom->u1.zIndexedBy;
+ Index *pIdx;
+ assert( pTab!=0 );
+ assert( pFrom->fg.isIndexedBy!=0 );
+
+ for(pIdx=pTab->pIndex;
+ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
+ pIdx=pIdx->pNext
+ );
+ if( !pIdx ){
+ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
+ pParse->checkSchema = 1;
+ return SQLITE_ERROR;
}
+ pFrom->u2.pIBIndex = pIdx;
return SQLITE_OK;
}
+
/*
** Detect compound SELECT statements that use an ORDER BY clause with
** an alternative collating sequence.
@@ -4724,7 +4875,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
** arguments. If it does, leave an error message in pParse and return
** non-zero, since pFrom is not allowed to be a table-valued function.
*/
-static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
+static int cannotBeFunction(Parse *pParse, SrcItem *pFrom){
if( pFrom->fg.isTabFunc ){
sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
return 1;
@@ -4745,19 +4896,19 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
*/
static struct Cte *searchWith(
With *pWith, /* Current innermost WITH clause */
- struct SrcList_item *pItem, /* FROM clause element to resolve */
+ SrcItem *pItem, /* FROM clause element to resolve */
With **ppContext /* OUT: WITH clause return value belongs to */
){
- const char *zName;
- if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){
- With *p;
- for(p=pWith; p; p=p->pOuter){
- int i;
- for(i=0; inCte; i++){
- if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
- *ppContext = p;
- return &p->a[i];
- }
+ const char *zName = pItem->zName;
+ With *p;
+ assert( pItem->zDatabase==0 );
+ assert( zName!=0 );
+ for(p=pWith; p; p=p->pOuter){
+ int i;
+ for(i=0; inCte; i++){
+ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
+ *ppContext = p;
+ return &p->a[i];
}
}
}
@@ -4775,46 +4926,54 @@ static struct Cte *searchWith(
** statement with which it is associated.
*/
void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
- assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
if( pWith ){
assert( pParse->pWith!=pWith );
pWith->pOuter = pParse->pWith;
pParse->pWith = pWith;
- if( bFree ) pParse->pWithToFree = pWith;
+ if( bFree ){
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3WithDelete,
+ pWith);
+ testcase( pParse->earlyCleanup );
+ }
}
}
/*
** This function checks if argument pFrom refers to a CTE declared by
-** a WITH clause on the stack currently maintained by the parser. And,
-** if currently processing a CTE expression, if it is a recursive
-** reference to the current CTE.
+** a WITH clause on the stack currently maintained by the parser (on the
+** pParse->pWith linked list). And if currently processing a CTE
+** CTE expression, through routine checks to see if the reference is
+** a recursive reference to the CTE.
**
-** If pFrom falls into either of the two categories above, pFrom->pTab
-** and other fields are populated accordingly. The caller should check
-** (pFrom->pTab!=0) to determine whether or not a successful match
-** was found.
+** If pFrom matches a CTE according to either of these two above, pFrom->pTab
+** and other fields are populated accordingly.
**
-** Whether or not a match is found, SQLITE_OK is returned if no error
-** occurs. If an error does occur, an error message is stored in the
-** parser and some error code other than SQLITE_OK returned.
+** Return 0 if no match is found.
+** Return 1 if a match is found.
+** Return 2 if an error condition is detected.
*/
-static int withExpand(
- Walker *pWalker,
- struct SrcList_item *pFrom
+static int resolveFromTermToCte(
+ Parse *pParse, /* The parsing context */
+ Walker *pWalker, /* Current tree walker */
+ SrcItem *pFrom /* The FROM clause term to check */
){
- Parse *pParse = pWalker->pParse;
- sqlite3 *db = pParse->db;
- struct Cte *pCte; /* Matched CTE (or NULL if no match) */
- With *pWith; /* WITH clause that pCte belongs to */
+ Cte *pCte; /* Matched CTE (or NULL if no match) */
+ With *pWith; /* The matching WITH */
assert( pFrom->pTab==0 );
- if( pParse->nErr ){
- return SQLITE_ERROR;
+ if( pParse->pWith==0 ){
+ /* There are no WITH clauses in the stack. No match is possible */
+ return 0;
+ }
+ if( pFrom->zDatabase!=0 ){
+ /* The FROM term contains a schema qualifier (ex: main.t1) and so
+ ** it cannot possibly be a CTE reference. */
+ return 0;
}
-
pCte = searchWith(pParse->pWith, pFrom, &pWith);
if( pCte ){
+ sqlite3 *db = pParse->db;
Table *pTab;
ExprList *pEList;
Select *pSel;
@@ -4823,6 +4982,7 @@ static int withExpand(
int bMayRecursive; /* True if compound joined by UNION [ALL] */
With *pSavedWith; /* Initial value of pParse->pWith */
int iRecTab = -1; /* Cursor for recursive table */
+ CteUse *pCteUse;
/* If pCte->zCteErr is non-NULL at this point, then this is an illegal
** recursive reference to CTE pCte. Leave an error in pParse and return
@@ -4830,21 +4990,39 @@ static int withExpand(
** In this case, proceed. */
if( pCte->zCteErr ){
sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName);
- return SQLITE_ERROR;
+ return 2;
}
- if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR;
+ if( cannotBeFunction(pParse, pFrom) ) return 2;
assert( pFrom->pTab==0 );
- pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
- if( pTab==0 ) return WRC_Abort;
+ pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return 2;
+ pCteUse = pCte->pUse;
+ if( pCteUse==0 ){
+ pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0]));
+ if( pCteUse==0
+ || sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0
+ ){
+ sqlite3DbFree(db, pTab);
+ return 2;
+ }
+ pCteUse->eM10d = pCte->eM10d;
+ }
+ pFrom->pTab = pTab;
pTab->nTabRef = 1;
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ if( db->mallocFailed ) return 2;
assert( pFrom->pSelect );
+ pFrom->fg.isCte = 1;
+ pFrom->u2.pCteUse = pCteUse;
+ pCteUse->nUse++;
+ if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){
+ pCteUse->eM10d = M10d_Yes;
+ }
/* Check if this is a recursive CTE. */
pRecTerm = pSel = pFrom->pSelect;
@@ -4854,7 +5032,7 @@ static int withExpand(
SrcList *pSrc = pRecTerm->pSrc;
assert( pRecTerm->pPrior!=0 );
for(i=0; inSrc; i++){
- struct SrcList_item *pItem = &pSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i];
if( pItem->zDatabase==0
&& pItem->zName!=0
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
@@ -4866,7 +5044,7 @@ static int withExpand(
sqlite3ErrorMsg(pParse,
"multiple references to recursive table: %s", pCte->zName
);
- return SQLITE_ERROR;
+ return 2;
}
pRecTerm->selFlags |= SF_Recursive;
if( iRecTab<0 ) iRecTab = pParse->nTab++;
@@ -4902,7 +5080,7 @@ static int withExpand(
pCte->zName, pEList->nExpr, pCte->pCols->nExpr
);
pParse->pWith = pSavedWith;
- return SQLITE_ERROR;
+ return 2;
}
pEList = pCte->pCols;
}
@@ -4918,9 +5096,9 @@ static int withExpand(
}
pCte->zCteErr = 0;
pParse->pWith = pSavedWith;
+ return 1; /* Success */
}
-
- return SQLITE_OK;
+ return 0; /* No match */
}
#endif
@@ -4954,7 +5132,7 @@ static void selectPopWith(Walker *pWalker, Select *p){
** SQLITE_OK is returned. Otherwise, if an OOM error is encountered,
** SQLITE_NOMEM.
*/
-int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
+int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
Select *pSel = pFrom->pSelect;
Table *pTab;
@@ -5002,10 +5180,10 @@ int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
*/
static int selectExpander(Walker *pWalker, Select *p){
Parse *pParse = pWalker->pParse;
- int i, j, k;
+ int i, j, k, rc;
SrcList *pTabList;
ExprList *pEList;
- struct SrcList_item *pFrom;
+ SrcItem *pFrom;
sqlite3 *db = pParse->db;
Expr *pE, *pRight, *pExpr;
u16 selFlags = p->selFlags;
@@ -5041,10 +5219,6 @@ static int selectExpander(Walker *pWalker, Select *p){
assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
if( pFrom->pTab ) continue;
assert( pFrom->fg.isRecursive==0 );
-#ifndef SQLITE_OMIT_CTE
- if( withExpand(pWalker, pFrom) ) return WRC_Abort;
- if( pFrom->pTab ) {} else
-#endif
if( pFrom->zName==0 ){
#ifndef SQLITE_OMIT_SUBQUERY
Select *pSel = pFrom->pSelect;
@@ -5053,6 +5227,12 @@ static int selectExpander(Walker *pWalker, Select *p){
assert( pFrom->pTab==0 );
if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort;
+#endif
+#ifndef SQLITE_OMIT_CTE
+ }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){
+ if( rc>1 ) return WRC_Abort;
+ pTab = pFrom->pTab;
+ assert( pTab!=0 );
#endif
}else{
/* An ordinary table or view name in the FROM clause */
@@ -5101,7 +5281,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
/* Locate the index named by the INDEXED BY clause, if any. */
- if( sqlite3IndexedByLookup(pParse, pFrom) ){
+ if( pFrom->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pFrom) ){
return WRC_Abort;
}
}
@@ -5344,7 +5524,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Parse *pParse;
int i;
SrcList *pTabList;
- struct SrcList_item *pFrom;
+ SrcItem *pFrom;
assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
@@ -5615,7 +5795,9 @@ static void explainSimpleCount(
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op!=TK_AND ){
Select *pS = pWalker->u.pSelect;
- if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
+ if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy)
+ && ExprAlwaysFalse(pExpr)==0
+ ){
sqlite3 *db = pWalker->pParse->db;
Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
if( pNew ){
@@ -5666,11 +5848,13 @@ static void havingToWhere(Parse *pParse, Select *p){
** If it is, then return the SrcList_item for the prior view. If it is not,
** then return 0.
*/
-static struct SrcList_item *isSelfJoinView(
+static SrcItem *isSelfJoinView(
SrcList *pTabList, /* Search for self-joins in this FROM clause */
- struct SrcList_item *pThis /* Search for prior reference to this subquery */
+ SrcItem *pThis /* Search for prior reference to this subquery */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
+ assert( pThis->pSelect!=0 );
+ if( pThis->pSelect->selFlags & SF_PushDown ) return 0;
for(pItem = pTabList->a; pItempSelect==0 ) continue;
@@ -5686,9 +5870,7 @@ static struct SrcList_item *isSelfJoinView(
** names in the same FROM clause. */
continue;
}
- if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1)
- || sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1)
- ){
+ if( pItem->pSelect->selFlags & SF_PushDown ){
/* The view was modified by some other optimization such as
** pushDownWhereTerms() */
continue;
@@ -5698,6 +5880,15 @@ static struct SrcList_item *isSelfJoinView(
return 0;
}
+/*
+** Deallocate a single AggInfo object
+*/
+static void agginfoFree(sqlite3 *db, AggInfo *p){
+ sqlite3DbFree(db, p->aCol);
+ sqlite3DbFree(db, p->aFunc);
+ sqlite3DbFreeNN(db, p);
+}
+
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
/*
** Attempt to transform a query of the form
@@ -5843,8 +6034,18 @@ int sqlite3Select(
pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo );
/* All of these destinations are also able to ignore the ORDER BY clause */
- sqlite3ExprListDelete(db, p->pOrderBy);
- p->pOrderBy = 0;
+ if( p->pOrderBy ){
+#if SELECTTRACE_ENABLED
+ SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
+ if( sqlite3SelectTrace & 0x100 ){
+ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
+ }
+#endif
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
+ p->pOrderBy);
+ p->pOrderBy = 0;
+ }
p->selFlags &= ~SF_Distinct;
p->selFlags |= SF_NoopOrderBy;
}
@@ -5865,9 +6066,9 @@ int sqlite3Select(
** In this case, it is an error if the target object (pSrc->a[0]) name
** or alias is duplicated within FROM clause (pSrc->a[1..n]). */
if( p->selFlags & SF_UpdateFrom ){
- struct SrcList_item *p0 = &p->pSrc->a[0];
+ SrcItem *p0 = &p->pSrc->a[0];
for(i=1; ipSrc->nSrc; i++){
- struct SrcList_item *p1 = &p->pSrc->a[i];
+ SrcItem *p1 = &p->pSrc->a[i];
if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
sqlite3ErrorMsg(pParse,
"target object/alias may not appear in FROM clause: %s",
@@ -5905,7 +6106,7 @@ int sqlite3Select(
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
for(i=0; !p->pPrior && inSrc; i++){
- struct SrcList_item *pItem = &pTabList->a[i];
+ SrcItem *pItem = &pTabList->a[i];
Select *pSub = pItem->pSelect;
Table *pTab = pItem->pTab;
@@ -6039,7 +6240,8 @@ int sqlite3Select(
** (2) Generate code for all sub-queries
*/
for(i=0; inSrc; i++){
- struct SrcList_item *pItem = &pTabList->a[i];
+ SrcItem *pItem = &pTabList->a[i];
+ SrcItem *pPrior;
SelectDest dest;
Select *pSub;
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
@@ -6099,6 +6301,7 @@ int sqlite3Select(
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( OptimizationEnabled(db, SQLITE_PushDown)
+ && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes)
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
(pItem->fg.jointype & JT_OUTER)!=0)
){
@@ -6109,6 +6312,7 @@ int sqlite3Select(
sqlite3TreeViewSelect(0, p, 0);
}
#endif
+ assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
}
@@ -6118,16 +6322,18 @@ int sqlite3Select(
/* Generate code to implement the subquery
**
- ** The subquery is implemented as a co-routine if the subquery is
- ** guaranteed to be the outer loop (so that it does not need to be
- ** computed more than once)
+ ** The subquery is implemented as a co-routine if:
+ ** (1) the subquery is guaranteed to be the outer loop (so that
+ ** it does not need to be computed more than once), and
+ ** (2) the subquery is not a CTE that should be materialized
**
- ** TODO: Are there other reasons beside (1) to use a co-routine
+ ** TODO: Are there other reasons beside (1) and (2) to use a co-routine
** implementation?
*/
if( i==0
&& (pTabList->nSrc==1
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
+ && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
@@ -6147,16 +6353,30 @@ int sqlite3Select(
sqlite3VdbeEndCoroutine(v, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
+ }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){
+ /* This is a CTE for which materialization code has already been
+ ** generated. Invoke the subroutine to compute the materialization,
+ ** the make the pItem->iCursor be a copy of the ephemerial table that
+ ** holds the result of the materialization. */
+ CteUse *pCteUse = pItem->u2.pCteUse;
+ sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e);
+ if( pItem->iCursor!=pCteUse->iCur ){
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur);
+ }
+ pSub->nSelectRow = pCteUse->nRowEst;
+ }else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){
+ /* This view has already been materialized by a prior entry in
+ ** this same FROM clause. Reuse it. */
+ if( pPrior->addrFillSub ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
+ }
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
+ pSub->nSelectRow = pPrior->pSelect->nSelectRow;
}else{
- /* Generate a subroutine that will fill an ephemeral table with
- ** the content of this subquery. pItem->addrFillSub will point
- ** to the address of the generated subroutine. pItem->regReturn
- ** is a register allocated to hold the subroutine return address
- */
+ /* Generate a subroutine that will materialize the view. */
int topAddr;
int onceAddr = 0;
int retAddr;
- struct SrcList_item *pPrior;
testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
pItem->regReturn = ++pParse->nMem;
@@ -6171,22 +6391,22 @@ int sqlite3Select(
}else{
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
}
- pPrior = isSelfJoinView(pTabList, pItem);
- if( pPrior ){
- sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
- assert( pPrior->pSelect!=0 );
- pSub->nSelectRow = pPrior->pSelect->nSelectRow;
- }else{
- sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
- sqlite3Select(pParse, pSub, &dest);
- }
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
+ ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
+ sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
sqlite3ClearTempRegCache(pParse);
+ if( pItem->fg.isCte ){
+ CteUse *pCteUse = pItem->u2.pCteUse;
+ pCteUse->addrM9e = pItem->addrFillSub;
+ pCteUse->regRtn = pItem->regReturn;
+ pCteUse->iCur = pItem->iCursor;
+ pCteUse->nRowEst = pSub->nSelectRow;
+ }
}
if( db->mallocFailed ) goto select_end;
pParse->nHeight -= sqlite3SelectExprHeight(p);
@@ -6331,6 +6551,7 @@ int sqlite3Select(
sSort.pOrderBy = 0;
}
}
+ SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
@@ -6369,6 +6590,7 @@ int sqlite3Select(
/* End the database scan loop.
*/
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
}
}else{
@@ -6439,11 +6661,13 @@ int sqlite3Select(
** SELECT statement.
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
- if( pAggInfo==0 ){
+ if( pAggInfo ){
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
+ }
+ if( db->mallocFailed ){
goto select_end;
}
- pAggInfo->pNext = pParse->pAggList;
- pParse->pAggList = pAggInfo;
pAggInfo->selId = p->selId;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
@@ -6491,6 +6715,10 @@ int sqlite3Select(
int ii;
SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
sqlite3TreeViewSelect(0, p, 0);
+ if( minMaxFlag ){
+ sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
+ sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
+ }
for(ii=0; iinColumn; ii++){
sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
ii, pAggInfo->aCol[ii].iMem);
@@ -6558,6 +6786,7 @@ int sqlite3Select(
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
);
if( pWInfo==0 ) goto select_end;
+ SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
@@ -6606,6 +6835,7 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
sortOut = sqlite3GetTempReg(pParse);
@@ -6680,9 +6910,10 @@ int sqlite3Select(
/* End of the loop
*/
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
VdbeCoverage(v);
}else{
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
}
@@ -6792,7 +7023,6 @@ int sqlite3Select(
explainSimpleCount(pParse, pTab, pBest);
}else{
int regAcc = 0; /* "populate accumulators" flag */
- int addrSkip;
/* If there are accumulator registers but no min() or max() functions
** without FILTER clauses, allocate register regAcc. Register regAcc
@@ -6839,12 +7069,13 @@ int sqlite3Select(
if( pWInfo==0 ){
goto select_end;
}
+ SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
updateAccumulator(pParse, regAcc, pAggInfo);
if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
- addrSkip = sqlite3WhereOrderByLimitOptLabel(pWInfo);
- if( addrSkip!=sqlite3WhereContinueLabel(pWInfo) ){
- sqlite3VdbeGoto(v, addrSkip);
+ if( minMaxFlag ){
+ sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
}
+ SELECTTRACE(1,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, pAggInfo);
}
@@ -6889,15 +7120,13 @@ select_end:
if( pAggInfo && !db->mallocFailed ){
for(i=0; inColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
- assert( pExpr!=0 || db->mallocFailed );
- if( pExpr==0 ) continue;
+ assert( pExpr!=0 );
assert( pExpr->pAggInfo==pAggInfo );
assert( pExpr->iAgg==i );
}
for(i=0; inFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
- assert( pExpr!=0 || db->mallocFailed );
- if( pExpr==0 ) continue;
+ assert( pExpr!=0 );
assert( pExpr->pAggInfo==pAggInfo );
assert( pExpr->iAgg==i );
}
diff --git a/src/shell.c.in b/src/shell.c.in
index dbdba271cd..03c4fd319e 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -1085,12 +1085,12 @@ struct ShellState {
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
u8 autoEQPtest; /* autoEQP is in test mode */
u8 autoEQPtrace; /* autoEQP is in trace mode */
- u8 statsOn; /* True to display memory stats before each finalize */
u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
+ unsigned statsOn; /* True to display memory stats before each finalize */
unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
@@ -2027,6 +2027,7 @@ static int shell_callback(
if( azArg==0 ) break;
for(i=0; iw ){
w = strlenChar(azArg[i]);
}
@@ -2591,7 +2592,7 @@ static int display_stats(
if( pArg==0 || pArg->out==0 ) return 0;
out = pArg->out;
- if( pArg->pStmt && (pArg->statsOn & 2) ){
+ if( pArg->pStmt && pArg->statsOn==2 ){
int nCol, i, x;
sqlite3_stmt *pStmt = pArg->pStmt;
char z[100];
@@ -2615,6 +2616,14 @@ static int display_stats(
}
}
+ if( pArg->statsOn==3 ){
+ if( pArg->pStmt ){
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
+ raw_printf(pArg->out, "VM-steps: %d\n", iCur);
+ }
+ return 0;
+ }
+
displayStatLine(pArg, "Memory Used:",
"%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
displayStatLine(pArg, "Number of Outstanding Allocations:",
@@ -4047,7 +4056,11 @@ static const char *(azHelp[]) = {
".shell CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".show Show the current values for various settings",
- ".stats ?on|off? Show stats or turn stats on or off",
+ ".stats ?ARG? Show stats or turn stats on or off",
+ " off Turn off automatic stat display",
+ " on Turn on automatic stat display",
+ " stmt Show statement stats",
+ " vmstep Show the virtual machine step count only",
#ifndef SQLITE_NOHAVE_SYSTEM
".system CMD ARGS... Run CMD ARGS... in a system shell",
#endif
@@ -7863,16 +7876,17 @@ static int do_meta_command(char *zLine, ShellState *p){
int ctrlCode; /* Integer code for that option */
const char *zUsage; /* Usage notes */
} aCtrl[] = {
- { "size_limit", SQLITE_FCNTL_SIZE_LIMIT, "[LIMIT]" },
{ "chunk_size", SQLITE_FCNTL_CHUNK_SIZE, "SIZE" },
- /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY, "COUNT DELAY" },*/
- { "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" },
- { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
- /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
- { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
+ { "data_version", SQLITE_FCNTL_DATA_VERSION, "" },
{ "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
{ "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" },
+ { "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" },
+ /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
+ { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
{ "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" },
+ { "size_limit", SQLITE_FCNTL_SIZE_LIMIT, "[LIMIT]" },
+ { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
+ /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY, "COUNT DELAY" },*/
};
int filectrl = -1;
int iCtrl = -1;
@@ -7959,6 +7973,7 @@ static int do_meta_command(char *zLine, ShellState *p){
isOk = 1;
break;
}
+ case SQLITE_FCNTL_DATA_VERSION:
case SQLITE_FCNTL_HAS_MOVED: {
int x;
if( nArg!=2 ) break;
@@ -8738,7 +8753,7 @@ static int do_meta_command(char *zLine, ShellState *p){
&& (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
){
- const char *zFile = 0;
+ char *zFile = 0;
int bTxtMode = 0;
int i;
int eMode = 0;
@@ -8768,17 +8783,22 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1;
goto meta_command_exit;
}
- }else if( zFile==0 ){
- zFile = z;
+ }else if( zFile==0 && eMode!='e' && eMode!='x' ){
+ zFile = sqlite3_mprintf("%s", z);
+ if( zFile[0]=='|' ){
+ while( i+1out,"ERROR: extra parameter: \"%s\". Usage:\n",
azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
+ sqlite3_free(zFile);
goto meta_command_exit;
}
}
- if( zFile==0 ) zFile = "stdout";
+ if( zFile==0 ) zFile = sqlite3_mprintf("stdout");
if( bOnce ){
p->outCount = 2;
}else{
@@ -8801,7 +8821,8 @@ static int do_meta_command(char *zLine, ShellState *p){
newTempFile(p, "txt");
bTxtMode = 1;
}
- zFile = p->zTempFile;
+ sqlite3_free(zFile);
+ zFile = sqlite3_mprintf("%s", p->zTempFile);
}
#endif /* SQLITE_NOHAVE_SYSTEM */
if( zFile[0]=='|' ){
@@ -8833,6 +8854,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
+ sqlite3_free(zFile);
}else
if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){
@@ -9722,6 +9744,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
static const char *azBool[] = { "off", "on", "trigger", "full"};
+ const char *zOut;
int i;
if( nArg!=1 ){
raw_printf(stderr, "Usage: .show\n");
@@ -9746,7 +9769,13 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(p->out,"%12.12s: ", "rowseparator");
output_c_string(p->out, p->rowSeparator);
raw_printf(p->out, "\n");
- utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]);
+ switch( p->statsOn ){
+ case 0: zOut = "off"; break;
+ default: zOut = "on"; break;
+ case 2: zOut = "stmt"; break;
+ case 3: zOut = "vmstep"; break;
+ }
+ utf8_printf(p->out, "%12.12s: %s\n","stats", zOut);
utf8_printf(p->out, "%12.12s: ", "width");
for (i=0;inWidth;i++) {
raw_printf(p->out, "%d ", p->colWidth[i]);
@@ -9758,11 +9787,17 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
if( nArg==2 ){
- p->statsOn = (u8)booleanValue(azArg[1]);
+ if( strcmp(azArg[1],"stmt")==0 ){
+ p->statsOn = 2;
+ }else if( strcmp(azArg[1],"vmstep")==0 ){
+ p->statsOn = 3;
+ }else{
+ p->statsOn = (u8)booleanValue(azArg[1]);
+ }
}else if( nArg==1 ){
display_stats(p->db, p, 0);
}else{
- raw_printf(stderr, "Usage: .stats ?on|off?\n");
+ raw_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n");
rc = 1;
}
}else
@@ -9967,7 +10002,7 @@ static int do_meta_command(char *zLine, ShellState *p){
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_OPTIMIZATIONS:
if( nArg==3 ){
- int opt = (int)strtol(azArg[2], 0, 0);
+ unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0);
rc2 = sqlite3_test_control(testctrl, p->db, opt);
isOk = 3;
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index beddee5d00..6ebde28174 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -2115,7 +2115,13 @@ struct sqlite3_mem_methods {
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
-** which case the trigger setting is not reported back.
+** which case the trigger setting is not reported back.
+**
+** Originally this option disabled all triggers. ^(However, since
+** SQLite version 3.35.0, TEMP triggers are still allowed even if
+** this option is off. So, in other words, this option now only disables
+** triggers in the main database schema or in the schemas of ATTACH-ed
+** databases.)^
**
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
**
SQLITE_DBCONFIG_ENABLE_VIEW
@@ -3499,6 +3505,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** that uses dot-files in place of posix advisory locking.
** file:data.db?mode=readonly |
** An error. "readonly" is not a valid option for the "mode" parameter.
+** Use "ro" instead: "file:data.db?mode=ro".
**
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
@@ -3697,7 +3704,7 @@ sqlite3_file *sqlite3_database_file_object(const char*);
** If the Y parameter to sqlite3_free_filename(Y) is anything other
** than a NULL pointer or a pointer previously acquired from
** sqlite3_create_filename(), then bad things such as heap
-** corruption or segfaults may occur. The value Y should be
+** corruption or segfaults may occur. The value Y should not be
** used again after sqlite3_free_filename(Y) has been called. This means
** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
** then the corresponding [sqlite3_module.xClose() method should also be
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7ef8d0dacf..b34c42b975 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -214,7 +214,8 @@
#ifndef __has_extension
# define __has_extension(x) 0 /* compatibility with non-clang compilers */
#endif
-#if GCC_VERSION>=4007000 || __has_extension(c_atomic)
+#if GCC_VERSION>=4007000 || \
+ (__has_extension(c_atomic) && __has_extension(c_atomic_store_n))
# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
#else
@@ -1014,9 +1015,7 @@ extern u32 sqlite3SelectTrace;
/*
** Macros for "wheretrace"
*/
-#if !defined(SQLITE_AMAGAMATION)
extern u32 sqlite3WhereTrace;
-#endif
#if defined(SQLITE_DEBUG) \
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
@@ -1137,7 +1136,10 @@ typedef struct AutoincInfo AutoincInfo;
typedef struct Bitvec Bitvec;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
+typedef struct Cte Cte;
+typedef struct CteUse CteUse;
typedef struct Db Db;
+typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
@@ -1156,14 +1158,17 @@ typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
+typedef struct ParseCleanup ParseCleanup;
typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
typedef struct RenameToken RenameToken;
+typedef struct Returning Returning;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;
typedef struct SelectDest SelectDest;
+typedef struct SrcItem SrcItem;
typedef struct SrcList SrcList;
typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
typedef struct Table Table;
@@ -1505,7 +1510,7 @@ struct sqlite3 {
int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */
int iSysErrno; /* Errno value from last system error */
- u16 dbOptFlags; /* Flags to enable/disable optimizations */
+ u32 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 eConcurrent; /* CONCURRENT_* value */
@@ -1721,24 +1726,26 @@ struct sqlite3 {
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
-#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
-#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */
-#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
-#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
-#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */
-#define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */
-#define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */
-#define SQLITE_Transitive 0x0080 /* Transitive constraints */
-#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */
-#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */
-#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */
-#define SQLITE_Stat4 0x0800 /* Use STAT4 data */
- /* TH3 expects the Stat4 ^^^^^^ value to be 0x0800. Don't change it */
-#define SQLITE_PushDown 0x1000 /* The push-down optimization */
-#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
-#define SQLITE_SkipScan 0x4000 /* Skip-scans */
-#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */
-#define SQLITE_AllOpts 0xffff /* All optimizations */
+#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */
+#define SQLITE_WindowFunc 0x00000002 /* Use xInverse for window functions */
+#define SQLITE_GroupByOrder 0x00000004 /* GROUPBY cover of ORDERBY */
+#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */
+#define SQLITE_DistinctOpt 0x00000010 /* DISTINCT using indexes */
+#define SQLITE_CoverIdxScan 0x00000020 /* Covering index scans */
+#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */
+#define SQLITE_Transitive 0x00000080 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */
+#define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */
+#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */
+#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */
+ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */
+#define SQLITE_PushDown 0x00001000 /* The push-down optimization */
+#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */
+#define SQLITE_SkipScan 0x00004000 /* Skip-scans */
+#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
+#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */
+#define SQLITE_ExistsToIN 0x00020000 /* The EXISTS-to-IN optimization */
+#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
** Macros for testing whether or not optimizations are enabled or disabled.
@@ -2029,7 +2036,12 @@ struct Column {
u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
-/* Allowed values for Column.colFlags:
+/* Allowed values for Column.colFlags.
+**
+** Constraints:
+** TF_HasVirtual == COLFLAG_VIRTUAL
+** TF_HasStored == COLFLAG_STORED
+** TF_HasHidden == COLFLAG_HIDDEN
*/
#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
@@ -2205,7 +2217,6 @@ struct Table {
#endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
Schema *pSchema; /* Schema that contains this table */
- Table *pNextZombie; /* Next on the Parse.pZombieTab list */
};
/*
@@ -2219,11 +2230,12 @@ struct Table {
**
** Constraints:
**
-** TF_HasVirtual == COLFLAG_Virtual
-** TF_HasStored == COLFLAG_Stored
+** TF_HasVirtual == COLFLAG_VIRTUAL
+** TF_HasStored == COLFLAG_STORED
+** TF_HasHidden == COLFLAG_HIDDEN
*/
#define TF_Readonly 0x0001 /* Read-only system table */
-#define TF_Ephemeral 0x0002 /* An ephemeral table */
+#define TF_HasHidden 0x0002 /* Has one or more hidden columns */
#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */
#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */
#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */
@@ -2238,6 +2250,7 @@ struct Table {
#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */
#define TF_Shadow 0x1000 /* True for a shadow table */
#define TF_HasStat4 0x2000 /* STAT4 info available for this table */
+#define TF_Ephemeral 0x4000 /* An ephemeral table */
/*
** Test to see whether or not a table is a virtual table. This is
@@ -2606,7 +2619,6 @@ struct AggInfo {
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
- AggInfo *pNext; /* Next in list of them all */
};
/*
@@ -2735,7 +2747,7 @@ struct Expr {
** TK_VARIABLE: variable number (always >= 1).
** TK_SELECT_COLUMN: column of the result vector */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
- i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
+ int iRightJoinTable; /* If EP_FromJoin, the right table of the join */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
@@ -2777,7 +2789,7 @@ struct Expr {
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
-#define EP_Alias 0x400000 /* Is an alias for a result set column */
+ /* 0x400000 // Available */
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
@@ -2925,6 +2937,45 @@ struct IdList {
int nId; /* Number of identifiers on the list */
};
+/*
+** The SrcItem object represents a single term in the FROM clause of a query.
+** The SrcList object is mostly an array of SrcItems.
+*/
+struct SrcItem {
+ Schema *pSchema; /* Schema to which this item is fixed */
+ char *zDatabase; /* Name of database holding this table */
+ char *zName; /* Name of the table */
+ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
+ Table *pTab; /* An SQL table corresponding to zName */
+ Select *pSelect; /* A SELECT statement used in place of a table name */
+ int addrFillSub; /* Address of subroutine to manifest a subquery */
+ int regReturn; /* Register holding return address of addrFillSub */
+ int regResult; /* Registers holding results of a co-routine */
+ struct {
+ u8 jointype; /* Type of join between this table and the previous */
+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
+ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
+ unsigned isTabFunc :1; /* True if table-valued-function syntax */
+ unsigned isCorrelated :1; /* True if sub-query is correlated */
+ unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
+ unsigned fromDDL :1; /* Comes from sqlite_schema */
+ unsigned isCte :1; /* This is a CTE */
+ } fg;
+ int iCursor; /* The VDBE cursor number used to access this table */
+ Expr *pOn; /* The ON clause of a join */
+ IdList *pUsing; /* The USING clause of a join */
+ Bitmask colUsed; /* Bit N (1<" clause */
+ ExprList *pFuncArg; /* Arguments to table-valued-function */
+ } u1;
+ union {
+ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
+ CteUse *pCteUse; /* CTE Usage info info fg.isCte is true */
+ } u2;
+};
+
/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
@@ -2947,36 +2998,7 @@ struct IdList {
struct SrcList {
int nSrc; /* Number of tables or subqueries in the FROM clause */
u32 nAlloc; /* Number of entries allocated in a[] below */
- struct SrcList_item {
- Schema *pSchema; /* Schema to which this item is fixed */
- char *zDatabase; /* Name of database holding this table */
- char *zName; /* Name of the table */
- char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
- Table *pTab; /* An SQL table corresponding to zName */
- Select *pSelect; /* A SELECT statement used in place of a table name */
- int addrFillSub; /* Address of subroutine to manifest a subquery */
- int regReturn; /* Register holding return address of addrFillSub */
- int regResult; /* Registers holding results of a co-routine */
- struct {
- u8 jointype; /* Type of join between this table and the previous */
- unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
- unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
- unsigned isTabFunc :1; /* True if table-valued-function syntax */
- unsigned isCorrelated :1; /* True if sub-query is correlated */
- unsigned viaCoroutine :1; /* Implemented as a co-routine */
- unsigned isRecursive :1; /* True for recursive reference in WITH */
- unsigned fromDDL :1; /* Comes from sqlite_schema */
- } fg;
- int iCursor; /* The VDBE cursor number used to access this table */
- Expr *pOn; /* The ON clause of a join */
- IdList *pUsing; /* The USING clause of a join */
- Bitmask colUsed; /* Bit N (1<" clause */
- ExprList *pFuncArg; /* Arguments to table-valued-function */
- } u1;
- Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
- } a[1]; /* One entry for each identifier on the list */
+ SrcItem a[1]; /* One entry for each identifier on the list */
};
/*
@@ -3052,6 +3074,7 @@ struct NameContext {
ExprList *pEList; /* Optional list of result-set columns */
AggInfo *pAggInfo; /* Information about aggregates at this level */
Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
+ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */
} uNC;
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
@@ -3080,6 +3103,7 @@ struct NameContext {
#define NC_UEList 0x00080 /* True if uNC.pEList is used */
#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */
#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */
+#define NC_UBaseReg 0x00400 /* True if uNC.iBaseReg is used */
#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x02000 /* True if a function or subquery seen */
#define NC_AllowWin 0x04000 /* Window functions are allowed here */
@@ -3197,6 +3221,8 @@ struct Select {
#define SF_View 0x0200000 /* SELECT statement is a view */
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
+#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
+#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -3367,6 +3393,17 @@ struct TriggerPrg {
# define DbMaskNonZero(M) (M)!=0
#endif
+/*
+** An instance of the ParseCleanup object specifies an operation that
+** should be performed after parsing to deallocation resources obtained
+** during the parse and which are no longer needed.
+*/
+struct ParseCleanup {
+ ParseCleanup *pNext; /* Next cleanup task */
+ void *pPtr; /* Pointer to object to deallocate */
+ void (*xCleanup)(sqlite3*,void*); /* Deallocation routine */
+};
+
/*
** An SQL parser context. A copy of this structure is passed through
** the parser and down into all the parser action routine in order to
@@ -3398,6 +3435,9 @@ struct Parse {
u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 disableVtab; /* Disable all virtual tables for this parse */
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+ u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
+#endif
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
@@ -3425,12 +3465,15 @@ struct Parse {
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
Parse *pParentParse; /* Parent parser if this parser is nested */
- AggInfo *pAggList; /* List of all AggInfo objects */
- int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
+ union {
+ int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
+ Returning *pReturning; /* The RETURNING clause */
+ } u1;
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
+ u8 bReturning; /* Coding a RETURNING trigger */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
@@ -3476,10 +3519,9 @@ struct Parse {
Token sArg; /* Complete text of a module argument */
Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
- Table *pZombieTab; /* List of Table objects to delete after code gen */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
With *pWith; /* Current WITH clause, or NULL */
- With *pWithToFree; /* Free this WITH object at the end of the parse */
+ ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */
#ifndef SQLITE_OMIT_ALTERTABLE
RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */
#endif
@@ -3581,6 +3623,7 @@ struct Trigger {
char *table; /* The table or view to which the trigger applies */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
+ u8 bReturning; /* This trigger implements a RETURNING clause */
Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */
IdList *pColumns; /* If this is an UPDATE OF trigger,
the is stored here */
@@ -3639,14 +3682,15 @@ struct Trigger {
*
*/
struct TriggerStep {
- u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
+ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT,
+ ** or TK_RETURNING */
u8 orconf; /* OE_Rollback etc. */
Trigger *pTrig; /* The trigger that this step is a part of */
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE */
+ ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */
IdList *pIdList; /* Column names for INSERT */
Upsert *pUpsert; /* Upsert clauses on an INSERT */
char *zSpan; /* Original SQL text of this command */
@@ -3655,18 +3699,16 @@ struct TriggerStep {
};
/*
-** The following structure contains information used by the sqliteFix...
-** routines as they walk the parse tree to make database references
-** explicit.
+** Information about a RETURNING clause
*/
-typedef struct DbFixer DbFixer;
-struct DbFixer {
- Parse *pParse; /* The parsing context. Error messages written here */
- Schema *pSchema; /* Fix items to this schema */
- u8 bTemp; /* True for TEMP schema entries */
- const char *zDb; /* Make sure all objects are contained in this database */
- const char *zType; /* Type of the container - used for error messages */
- const Token *pName; /* Name of the container - used for error messages */
+struct Returning {
+ Parse *pParse; /* The parse that includes the RETURNING clause */
+ ExprList *pReturnEL; /* List of expressions to return */
+ Trigger retTrig; /* The transient trigger that implements RETURNING */
+ TriggerStep retTStep; /* The trigger step */
+ int iRetCur; /* Transient table holding RETURNING results */
+ int nRetCol; /* Number of in pReturnEL after expansion */
+ int iRetReg; /* Register array for holding a row of RETURNING */
};
/*
@@ -3706,7 +3748,8 @@ typedef struct {
/*
** Allowed values for mInitFlags
*/
-#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
+#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */
+#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */
/*
** Structure containing global configuration data for the SQLite library.
@@ -3818,10 +3861,26 @@ struct Walker {
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
- struct SrcList_item *pSrcItem; /* A single FROM clause item */
+ SrcItem *pSrcItem; /* A single FROM clause item */
+ DbFixer *pFix;
} u;
};
+/*
+** The following structure contains information used by the sqliteFix...
+** routines as they walk the parse tree to make database references
+** explicit.
+*/
+struct DbFixer {
+ Parse *pParse; /* The parsing context. Error messages written here */
+ Walker w; /* Walker object */
+ Schema *pSchema; /* Fix items to this schema */
+ u8 bTemp; /* True for TEMP schema entries */
+ const char *zDb; /* Make sure all objects are contained in this database */
+ const char *zType; /* Type of the container - used for error messages */
+ const Token *pName; /* Name of the container - used for error messages */
+};
+
/* Forward declarations */
int sqlite3WalkExpr(Walker*, Expr*);
int sqlite3WalkExprList(Walker*, ExprList*);
@@ -3847,20 +3906,55 @@ void sqlite3SelectWalkAssert2(Walker*, Select*);
#define WRC_Abort 2 /* Abandon the tree walk */
/*
-** An instance of this structure represents a set of one or more CTEs
-** (common table expressions) created by a single WITH clause.
+** A single common table expression
+*/
+struct Cte {
+ char *zName; /* Name of this CTE */
+ ExprList *pCols; /* List of explicit column names, or NULL */
+ Select *pSelect; /* The definition of this CTE */
+ const char *zCteErr; /* Error message for circular references */
+ CteUse *pUse; /* Usage information for this CTE */
+ u8 eM10d; /* The MATERIALIZED flag */
+};
+
+/*
+** Allowed values for the materialized flag (eM10d):
+*/
+#define M10d_Yes 0 /* AS MATERIALIZED */
+#define M10d_Any 1 /* Not specified. Query planner's choice */
+#define M10d_No 2 /* AS NOT MATERIALIZED */
+
+/*
+** An instance of the With object represents a WITH clause containing
+** one or more CTEs (common table expressions).
*/
struct With {
- int nCte; /* Number of CTEs in the WITH clause */
- With *pOuter; /* Containing WITH clause, or NULL */
- struct Cte { /* For each CTE in the WITH clause.... */
- char *zName; /* Name of this CTE */
- ExprList *pCols; /* List of explicit column names, or NULL */
- Select *pSelect; /* The definition of this CTE */
- const char *zCteErr; /* Error message for circular references */
- } a[1];
+ int nCte; /* Number of CTEs in the WITH clause */
+ With *pOuter; /* Containing WITH clause, or NULL */
+ Cte a[1]; /* For each CTE in the WITH clause.... */
};
+/*
+** The Cte object is not guaranteed to persist for the entire duration
+** of code generation. (The query flattener or other parser tree
+** edits might delete it.) The following object records information
+** about each Common Table Expression that must be preserved for the
+** duration of the parse.
+**
+** The CteUse objects are freed using sqlite3ParserAddCleanup() rather
+** than sqlite3SelectDelete(), which is what enables them to persist
+** until the end of code generation.
+*/
+struct CteUse {
+ int nUse; /* Number of users of this CTE */
+ int addrM9e; /* Start of subroutine to compute materialization */
+ int regRtn; /* Return address register for addrM9e subroutine */
+ int iCur; /* Ephemeral table holding the materialization */
+ LogEst nRowEst; /* Estimated number of rows in the table */
+ u8 eM10d; /* The MATERIALIZED flag */
+};
+
+
#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
@@ -3938,7 +4032,6 @@ int sqlite3WindowCompare(Parse*, Window*, Window*, int);
void sqlite3WindowCodeInit(Parse*, Select*);
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
int sqlite3WindowRewrite(Parse*, Select*);
-int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
@@ -4255,6 +4348,7 @@ void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
void sqlite3AddCollateType(Parse*, Token*);
void sqlite3AddGenerated(Parse*,Expr*,Token*);
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
+void sqlite3AddReturning(Parse*,ExprList*);
int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
#define sqlite3CodecQueryParameters(A,B,C) 0
@@ -4320,7 +4414,7 @@ SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, Expr*, IdList*);
void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
-int sqlite3IndexedByLookup(Parse *, struct SrcList_item *);
+int sqlite3IndexedByLookup(Parse *, SrcItem *);
void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(sqlite3*, IdList*);
@@ -4348,6 +4442,7 @@ LogEst sqlite3WhereOutputRowCount(WhereInfo*);
int sqlite3WhereIsDistinct(WhereInfo*);
int sqlite3WhereIsOrdered(WhereInfo*);
int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);
+void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*);
int sqlite3WhereIsSorted(WhereInfo*);
int sqlite3WhereContinueLabel(WhereInfo*);
int sqlite3WhereBreakLabel(WhereInfo*);
@@ -4381,7 +4476,7 @@ Table *sqlite3FindTable(sqlite3*,const char*, const char*);
#define LOCATE_VIEW 0x01
#define LOCATE_NOERR 0x02
Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*);
-Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
+Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *);
Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
@@ -4511,6 +4606,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
#endif
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
+int sqlite3ColumnIndex(Table *pTab, const char *zCol);
void sqlite3SetJoinExpr(Expr*,int);
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
void sqlite3DeferForeignKey(Parse*, int);
@@ -4533,7 +4629,6 @@ void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
int sqlite3FixSrcList(DbFixer*, SrcList*);
int sqlite3FixSelect(DbFixer*, Select*);
int sqlite3FixExpr(DbFixer*, Expr*);
-int sqlite3FixExprList(DbFixer*, ExprList*);
int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
int sqlite3RealSameAsInt(double,sqlite3_int64);
void sqlite3Int64ToText(i64,char*);
@@ -4596,6 +4691,7 @@ int sqlite3Atoi64(const char*, i64*, int, u8);
int sqlite3DecOrHexToI64(const char*, i64*);
void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
void sqlite3Error(sqlite3*,int);
+void sqlite3ErrorClear(sqlite3*);
void sqlite3SystemError(sqlite3*,int);
void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
u8 sqlite3HexToInt(int h);
@@ -4677,6 +4773,7 @@ void sqlite3ExpirePreparedStatements(sqlite3*, int);
void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
int sqlite3CodeSubselect(Parse*, Expr*);
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+int sqlite3ExpandSubquery(Parse*, SrcItem*);
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
int sqlite3MatchEName(
const struct ExprList_item*,
@@ -4694,6 +4791,7 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
void sqlite3AlterFinishAddColumn(Parse *, Token *);
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
+void sqlite3AlterDropColumn(Parse*, SrcList*, Token*);
void *sqlite3RenameTokenMap(Parse*, void*, Token*);
void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
void sqlite3RenameExprUnmap(Parse*, Expr*);
@@ -4847,6 +4945,7 @@ sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
void sqlite3ParserReset(Parse*);
+void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*);
#ifdef SQLITE_ENABLE_NORMALIZE
char *sqlite3Normalize(Vdbe*, const char*);
#endif
@@ -4861,12 +4960,17 @@ const char *sqlite3JournalModename(int);
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
#endif
#ifndef SQLITE_OMIT_CTE
- With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
+ Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
+ void sqlite3CteDelete(sqlite3*,Cte*);
+ With *sqlite3WithAdd(Parse*,With*,Cte*);
void sqlite3WithDelete(sqlite3*,With*);
void sqlite3WithPush(Parse*, With*, u8);
#else
-#define sqlite3WithPush(x,y,z)
-#define sqlite3WithDelete(x,y)
+# define sqlite3CteNew(P,T,E,S) ((void*)0)
+# define sqlite3CteDelete(D,C)
+# define sqlite3CteWithAdd(P,W,C) ((void*)0)
+# define sqlite3WithDelete(x,y)
+# define sqlite3WithPush(x,y,z)
#endif
#ifndef SQLITE_OMIT_UPSERT
Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
diff --git a/src/tokenize.c b/src/tokenize.c
index bafda0322b..712447c4ca 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -713,19 +713,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
if( !IN_RENAME_OBJECT ){
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
}
-
- if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DbFree(db, pParse->pVList);
- while( pParse->pAinc ){
- AutoincInfo *p = pParse->pAinc;
- pParse->pAinc = p->pNext;
- sqlite3DbFreeNN(db, p);
- }
- while( pParse->pZombieTab ){
- Table *p = pParse->pZombieTab;
- pParse->pZombieTab = p->pNextZombie;
- sqlite3DeleteTable(db, p);
- }
db->pParse = pParse->pParentParse;
pParse->pParentParse = 0;
assert( nErr==0 || pParse->rc!=SQLITE_OK );
diff --git a/src/treeview.c b/src/treeview.c
index 187f1a07d1..b696d764e0 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -111,7 +111,10 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
}
sqlite3_str_appendf(&x, ")");
}
- sqlite3_str_appendf(&x, " AS");
+ if( pCte->pUse ){
+ sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse,
+ pCte->pUse->nUse);
+ }
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, inCte-1);
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
@@ -127,7 +130,7 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
int i;
for(i=0; inSrc; i++){
- const struct SrcList_item *pItem = &pSrc->a[i];
+ const SrcItem *pItem = &pSrc->a[i];
StrAccum x;
char zLine[100];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
@@ -150,6 +153,9 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
+ if( pItem->fg.isCte ){
+ sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
+ }
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, inSrc-1);
if( pItem->pSelect ){
diff --git a/src/trigger.c b/src/trigger.c
index dd4ed8c2e8..689c7c741e 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -48,28 +48,39 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
** pTab as well as the triggers lised in pTab->pTrigger.
*/
Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
- Schema * const pTmpSchema = pParse->db->aDb[1].pSchema;
- Trigger *pList = 0; /* List of triggers to return */
+ Schema *pTmpSchema; /* Schema of the pTab table */
+ Trigger *pList; /* List of triggers to return */
+ HashElem *p; /* Loop variable for TEMP triggers */
if( pParse->disableTriggers ){
return 0;
}
-
+ pTmpSchema = pParse->db->aDb[1].pSchema;
+ p = sqliteHashFirst(&pTmpSchema->trigHash);
+ if( p==0 ){
+ return pTab->pTrigger;
+ }
+ pList = pTab->pTrigger;
if( pTmpSchema!=pTab->pSchema ){
- HashElem *p;
- assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
- for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
+ while( p ){
Trigger *pTrig = (Trigger *)sqliteHashData(p);
if( pTrig->pTabSchema==pTab->pSchema
- && 0==sqlite3StrICmp(pTrig->table, pTab->zName)
+ && 0==sqlite3StrICmp(pTrig->table, pTab->zName)
){
- pTrig->pNext = (pList ? pList : pTab->pTrigger);
+ pTrig->pNext = pList;
pList = pTrig;
- }
+ }else if( pTrig->op==TK_RETURNING ){
+ assert( pParse->bReturning );
+ assert( &(pParse->u1.pReturning->retTrig) == pTrig );
+ pTrig->table = pTab->zName;
+ pTrig->pTabSchema = pTab->pSchema;
+ pTrig->pNext = pList;
+ pList = pTrig;
+ }
+ p = sqliteHashNext(p);
}
}
-
- return (pList ? pList : pTab->pTrigger);
+ return pList;
}
/*
@@ -345,7 +356,7 @@ void sqlite3FinishTrigger(
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
+ sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0);
}
if( db->init.busy ){
@@ -558,7 +569,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
** Recursively delete a Trigger structure
*/
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
- if( pTrigger==0 ) return;
+ if( pTrigger==0 || pTrigger->bReturning ) return;
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
sqlite3DbFree(db, pTrigger->zName);
sqlite3DbFree(db, pTrigger->table);
@@ -723,15 +734,53 @@ Trigger *sqlite3TriggersExist(
Trigger *pList = 0;
Trigger *p;
- if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
- pList = sqlite3TriggerList(pParse, pTab);
- }
- assert( pList==0 || IsVirtual(pTab)==0 );
- for(p=pList; p; p=p->pNext){
- if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
- mask |= p->tr_tm;
+ pList = sqlite3TriggerList(pParse, pTab);
+ assert( pList==0 || IsVirtual(pTab)==0
+ || (pList->bReturning && pList->pNext==0) );
+ if( pList!=0 ){
+ p = pList;
+ if( (pParse->db->flags & SQLITE_EnableTrigger)==0
+ && pTab->pTrigger!=0
+ ){
+ /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that
+ ** only TEMP triggers are allowed. Truncate the pList so that it
+ ** includes only TEMP triggers */
+ if( pList==pTab->pTrigger ){
+ pList = 0;
+ goto exit_triggers_exist;
+ }
+ while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext;
+ p->pNext = 0;
+ p = pList;
}
+ do{
+ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
+ mask |= p->tr_tm;
+ }else if( p->op==TK_RETURNING ){
+ /* The first time a RETURNING trigger is seen, the "op" value tells
+ ** us what time of trigger it should be. */
+ assert( sqlite3IsToplevel(pParse) );
+ p->op = op;
+ if( IsVirtual(pTab) ){
+ if( op!=TK_INSERT ){
+ sqlite3ErrorMsg(pParse,
+ "%s RETURNING is not available on virtual tables",
+ op==TK_DELETE ? "DELETE" : "UPDATE");
+ }
+ p->tr_tm = TRIGGER_BEFORE;
+ }else{
+ p->tr_tm = TRIGGER_AFTER;
+ }
+ mask |= p->tr_tm;
+ }else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE
+ && sqlite3IsToplevel(pParse) ){
+ /* Also fire a RETURNING trigger for an UPSERT */
+ mask |= p->tr_tm;
+ }
+ p = p->pNext;
+ }while( p );
}
+exit_triggers_exist:
if( pMask ){
*pMask = mask;
}
@@ -774,6 +823,111 @@ SrcList *sqlite3TriggerStepSrc(
return pSrc;
}
+/* The input list pList is the list of result set terms from a RETURNING
+** clause. The table that we are returning from is pTab.
+**
+** This routine makes a copy of the pList, and at the same time expands
+** any "*" wildcards to be the complete set of columns from pTab.
+*/
+static ExprList *sqlite3ExpandReturning(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* The arguments to RETURNING */
+ Table *pTab /* The table being updated */
+){
+ ExprList *pNew = 0;
+ sqlite3 *db = pParse->db;
+ int i;
+
+ for(i=0; inExpr; i++){
+ Expr *pOldExpr = pList->a[i].pExpr;
+ if( ALWAYS(pOldExpr!=0) && pOldExpr->op==TK_ASTERISK ){
+ int jj;
+ for(jj=0; jjnCol; jj++){
+ Expr *pNewExpr;
+ if( IsHiddenColumn(pTab->aCol+jj) ) continue;
+ pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zName);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
+ if( !db->mallocFailed ){
+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
+ pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zName);
+ pItem->eEName = ENAME_NAME;
+ }
+ }
+ }else{
+ Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
+ if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){
+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
+ pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName);
+ pItem->eEName = pList->a[i].eEName;
+ }
+ }
+ }
+ if( !db->mallocFailed ){
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ sqlite3VdbeSetNumCols(v, pNew->nExpr);
+ for(i=0; inExpr; i++){
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, pNew->a[i].zEName,
+ SQLITE_TRANSIENT);
+ }
+ }
+ return pNew;
+}
+
+/*
+** Generate code for the RETURNING trigger. Unlike other triggers
+** that invoke a subprogram in the bytecode, the code for RETURNING
+** is generated in-line.
+*/
+static void codeReturningTrigger(
+ Parse *pParse, /* Parse context */
+ Trigger *pTrigger, /* The trigger step that defines the RETURNING */
+ Table *pTab, /* The table to code triggers from */
+ int regIn /* The first in an array of registers */
+){
+ Vdbe *v = pParse->pVdbe;
+ ExprList *pNew;
+ Returning *pReturning;
+
+ assert( v!=0 );
+ assert( pParse->bReturning );
+ pReturning = pParse->u1.pReturning;
+ assert( pTrigger == &(pReturning->retTrig) );
+ pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
+ if( pNew ){
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ if( pReturning->nRetCol==0 ){
+ pReturning->nRetCol = pNew->nExpr;
+ pReturning->iRetCur = pParse->nTab++;
+ }
+ sNC.pParse = pParse;
+ sNC.uNC.iBaseReg = regIn;
+ sNC.ncFlags = NC_UBaseReg;
+ pParse->eTriggerOp = pTrigger->op;
+ pParse->pTriggerTab = pTab;
+ if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK ){
+ int i;
+ int nCol = pNew->nExpr;
+ int reg = pParse->nMem+1;
+ pParse->nMem += nCol+2;
+ pReturning->iRetReg = reg;
+ for(i=0; ia[i].pExpr, reg+i);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1);
+ sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1);
+ }
+ sqlite3ExprListDelete(pParse->db, pNew);
+ pParse->eTriggerOp = 0;
+ pParse->pTriggerTab = 0;
+ }
+}
+
+
+
/*
** Generate VDBE code for the statements inside the body of a single
** trigger.
@@ -823,6 +977,7 @@ static int codeTriggerProgram(
sqlite3ExprDup(db, pStep->pWhere, 0),
pParse->eOrconf, 0, 0, 0
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
case TK_INSERT: {
@@ -833,6 +988,7 @@ static int codeTriggerProgram(
pParse->eOrconf,
sqlite3UpsertDup(db, pStep->pUpsert)
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
case TK_DELETE: {
@@ -840,6 +996,7 @@ static int codeTriggerProgram(
sqlite3TriggerStepSrc(pParse, pStep),
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
default: assert( pStep->op==TK_SELECT ); {
@@ -851,9 +1008,6 @@ static int codeTriggerProgram(
break;
}
}
- if( pStep->op!=TK_SELECT ){
- sqlite3VdbeAddOp0(v, OP_ResetCount);
- }
}
return 0;
@@ -1000,7 +1154,6 @@ static TriggerPrg *codeRowTrigger(
sqlite3VdbeDelete(v);
}
- assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
sqlite3ParserReset(pSubParse);
sqlite3StackFree(db, pSubParse);
@@ -1102,7 +1255,7 @@ void sqlite3CodeRowTriggerDirect(
** ... ...
** reg+N OLD.* value of right-most column of pTab
** reg+N+1 NEW.rowid
-** reg+N+2 OLD.* value of left-most column of pTab
+** reg+N+2 NEW.* value of left-most column of pTab
** ... ...
** reg+N+N+1 NEW.* value of right-most column of pTab
**
@@ -1147,12 +1300,20 @@ void sqlite3CodeRowTrigger(
assert( p->pSchema==p->pTabSchema
|| p->pSchema==pParse->db->aDb[1].pSchema );
- /* Determine whether we should code this trigger */
- if( p->op==op
+ /* Determine whether we should code this trigger. One of two choices:
+ ** 1. The trigger is an exact match to the current DML statement
+ ** 2. This is a RETURNING trigger for INSERT but we are currently
+ ** doing the UPDATE part of an UPSERT.
+ */
+ if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE))
&& p->tr_tm==tr_tm
&& checkColumnOverlap(p->pColumns, pChanges)
){
- sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
+ if( !p->bReturning ){
+ sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
+ }else if( sqlite3IsToplevel(pParse) ){
+ codeReturningTrigger(pParse, p, pTab, reg);
+ }
}
}
}
@@ -1197,13 +1358,18 @@ u32 sqlite3TriggerColmask(
assert( isNew==1 || isNew==0 );
for(p=pTrigger; p; p=p->pNext){
- if( p->op==op && (tr_tm&p->tr_tm)
+ if( p->op==op
+ && (tr_tm&p->tr_tm)
&& checkColumnOverlap(p->pColumns,pChanges)
){
- TriggerPrg *pPrg;
- pPrg = getRowTrigger(pParse, p, pTab, orconf);
- if( pPrg ){
- mask |= pPrg->aColmask[isNew];
+ if( p->bReturning ){
+ mask = 0xffffffff;
+ }else{
+ TriggerPrg *pPrg;
+ pPrg = getRowTrigger(pParse, p, pTab, orconf);
+ if( pPrg ){
+ mask |= pPrg->aColmask[isNew];
+ }
}
}
}
diff --git a/src/update.c b/src/update.c
index f8cb2afedb..b360766b68 100644
--- a/src/update.c
+++ b/src/update.c
@@ -643,6 +643,7 @@ void sqlite3Update(
if( (db->flags&SQLITE_CountRows)!=0
&& !pParse->pTriggerTab
&& !pParse->nested
+ && !pParse->bReturning
&& pUpsert==0
){
regRowCount = ++pParse->nMem;
@@ -1106,7 +1107,7 @@ void sqlite3Update(
** that information.
*/
if( regRowCount ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
+ sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
}
diff --git a/src/util.c b/src/util.c
index fb86d7d118..fc0c2042bd 100644
--- a/src/util.c
+++ b/src/util.c
@@ -114,6 +114,16 @@ void sqlite3Error(sqlite3 *db, int err_code){
if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
}
+/*
+** The equivalent of sqlite3Error(db, SQLITE_OK). Clear the error state
+** and error message.
+*/
+void sqlite3ErrorClear(sqlite3 *db){
+ assert( db!=0 );
+ db->errCode = SQLITE_OK;
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr);
+}
+
/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
diff --git a/src/vdbe.c b/src/vdbe.c
index 2d7f5045c4..51f897b618 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -685,7 +685,7 @@ int sqlite3VdbeExec(
#endif
/*** INSERT STACK UNION HERE ***/
- assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
+ assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
sqlite3VdbeEnter(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( db->xProgress ){
@@ -1445,6 +1445,26 @@ case OP_IntCopy: { /* out2 */
break;
}
+/* Opcode: ChngCntRow P1 P2 * * *
+** Synopsis: output=r[P1]
+**
+** Output value in register P1 as the chance count for a DML statement,
+** due to the "PRAGMA count_changes=ON" setting. Or, if there was a
+** foreign key error in the statement, trigger the error now.
+**
+** This opcode is a variant of OP_ResultRow that checks the foreign key
+** immediate constraint count and throws an error if the count is
+** non-zero. The P2 opcode must be 1.
+*/
+case OP_ChngCntRow: {
+ assert( pOp->p2==1 );
+ if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ /* Fall through to the next case, OP_String */
+ /* no break */ deliberate_fall_through
+}
+
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
**
@@ -1461,34 +1481,6 @@ case OP_ResultRow: {
assert( pOp->p1>0 );
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
- /* If this statement has violated immediate foreign key constraints, do
- ** not return the number of rows modified. And do not RELEASE the statement
- ** transaction. It needs to be rolled back. */
- if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
- assert( db->flags&SQLITE_CountRows );
- assert( p->usesStmtJournal );
- goto abort_due_to_error;
- }
-
- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
- ** DML statements invoke this opcode to return the number of rows
- ** modified to the user. This is the only way that a VM that
- ** opens a statement transaction may invoke this opcode.
- **
- ** In case this is such a statement, close any statement transaction
- ** opened by this VM before returning control to the user. This is to
- ** ensure that statement-transactions are always nested, not overlapping.
- ** If the open statement-transaction is not closed here, then the user
- ** may step another VM that opens its own statement transaction. This
- ** may lead to overlapping statement transactions.
- **
- ** The statement transaction is never a top-level transaction. Hence
- ** the RELEASE call below can never fail.
- */
- assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
- rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
- assert( rc==SQLITE_OK );
-
/* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
@@ -3993,7 +3985,7 @@ case OP_OpenEphemeral: {
aMem[pOp->p3].z = "";
}
pCx = p->apCsr[pOp->p1];
- if( pCx && pCx->pBtx ){
+ if( pCx && ALWAYS(pCx->pBtx) ){
/* If the ephermeral table is already open, erase all existing content
** so that the table is empty again, rather than creating a new table. */
assert( pCx->isEphemeral );
@@ -4945,8 +4937,10 @@ case OP_NewRowid: { /* out2 */
VdbeCursor *pC; /* Cursor of table to get the new rowid */
int res; /* Result of an sqlite3BtreeLast() */
int cnt; /* Counter to limit the number of searches */
+#ifndef SQLITE_OMIT_AUTOINCREMENT
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
VdbeFrame *pFrame; /* Root frame of VDBE */
+#endif
v = 0;
res = 0;
@@ -5196,6 +5190,8 @@ case OP_RowCell: {
VdbeCursor *pSrc; /* Cursor to read from */
i64 iKey; /* Rowid value to insert with */
assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert );
+ assert( pOp[1].opcode==OP_Insert || pOp->p3==0 );
+ assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 );
assert( pOp[1].p5 & OPFLAG_PREFORMAT );
pDest = p->apCsr[pOp->p1];
pSrc = p->apCsr[pOp->p2];
@@ -5944,7 +5940,7 @@ case OP_IdxDelete: {
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
if( rc ) goto abort_due_to_error;
}else if( pOp->p5 ){
- rc = SQLITE_CORRUPT_INDEX;
+ rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
goto abort_due_to_error;
}
assert( pC->deferredMoveto==0 );
@@ -6370,7 +6366,7 @@ case OP_ParseSchema: {
if( pOp->p4.z==0 ){
sqlite3SchemaClear(db->aDb[iDb].pSchema);
db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
- rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
+ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5);
db->mDbFlags |= DBFLAG_SchemaChange;
p->expired = 0;
}else
diff --git a/src/vdbe.h b/src/vdbe.h
index 17f11fdd77..3257ff68a1 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -223,7 +223,7 @@ VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
#else
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
#endif
-void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
+void sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16);
void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 7d22cb8093..4f8a2edf37 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -381,7 +381,7 @@ struct Vdbe {
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
Parse *pParse; /* Parsing context used to create this Vdbe */
ynVar nVar; /* Number of entries in aVar[] */
- u32 magic; /* Magic number for sanity checking */
+ u32 iVdbeMagic; /* Magic number defining state of the SQL statement */
int nMem; /* Number of memory locations currently allocated */
int nCursor; /* Number of slots in apCsr[] */
u32 cacheCtr; /* VdbeCursor row cache generation counter */
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index a9cbf92fc3..ba3bdf6a5f 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -617,7 +617,7 @@ static int sqlite3Step(Vdbe *p){
int rc;
assert(p);
- if( p->magic!=VDBE_MAGIC_RUN ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){
/* We used to require that sqlite3_reset() be called before retrying
** sqlite3_step() after any error or after SQLITE_DONE. But beginning
** with version 3.7.0, we changed this so that sqlite3_reset() would
@@ -1333,7 +1333,7 @@ static int vdbeUnbind(Vdbe *p, int i){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(p->db->mutex);
- if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_RUN || p->pc>=0 ){
sqlite3Error(p->db, SQLITE_MISUSE);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
@@ -1687,7 +1687,7 @@ int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
*/
int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
- return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0;
+ return v!=0 && v->iVdbeMagic==VDBE_MAGIC_RUN && v->pc>=0;
}
/*
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 63495f35d5..d790bb019b 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -35,7 +35,7 @@ Vdbe *sqlite3VdbeCreate(Parse *pParse){
p->pNext = db->pVdbe;
p->pPrev = 0;
db->pVdbe = p;
- p->magic = VDBE_MAGIC_INIT;
+ p->iVdbeMagic = VDBE_MAGIC_INIT;
p->pParse = pParse;
pParse->pVdbe = p;
assert( pParse->aLabel==0 );
@@ -236,7 +236,7 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
VdbeOp *pOp;
i = p->nOp;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( op>=0 && op<0xff );
if( p->nOpAlloc<=i ){
return growOp3(p, op, p1, p2, p3);
@@ -471,9 +471,10 @@ void sqlite3VdbeExplainPop(Parse *pParse){
** The zWhere string must have been obtained from sqlite3_malloc().
** This routine will take ownership of the allocated memory.
*/
-void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
+void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){
int j;
sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(p, p5);
for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j);
sqlite3MayAbort(p->pParse);
}
@@ -565,7 +566,7 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){
void sqlite3VdbeResolveLabel(Vdbe *v, int x){
Parse *p = v->pParse;
int j = ADDR(x);
- assert( v->magic==VDBE_MAGIC_INIT );
+ assert( v->iVdbeMagic==VDBE_MAGIC_INIT );
assert( j<-p->nLabel );
assert( j>=0 );
#ifdef SQLITE_DEBUG
@@ -890,7 +891,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
** Return the address of the next instruction to be inserted.
*/
int sqlite3VdbeCurrentAddr(Vdbe *p){
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
return p->nOp;
}
@@ -975,7 +976,7 @@ VdbeOp *sqlite3VdbeAddOpList(
int i;
VdbeOp *pOut, *pFirst;
assert( nOp>0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
@@ -1299,7 +1300,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
sqlite3 *db;
assert( p!=0 );
db = p->db;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( p->aOp!=0 || db->mallocFailed );
if( db->mallocFailed ){
if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4);
@@ -1428,7 +1429,7 @@ VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
** zeros, which is correct. MSVC generates a warning, nevertheless. */
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
if( addr<0 ){
addr = p->nOp - 1;
}
@@ -2113,7 +2114,7 @@ int sqlite3VdbeList(
Op *pOp; /* Current opcode */
assert( p->explain );
- assert( p->magic==VDBE_MAGIC_RUN );
+ assert( p->iVdbeMagic==VDBE_MAGIC_RUN );
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
/* Even though this opcode does not use dynamic strings for
@@ -2293,14 +2294,14 @@ void sqlite3VdbeRewind(Vdbe *p){
int i;
#endif
assert( p!=0 );
- assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT || p->iVdbeMagic==VDBE_MAGIC_RESET );
/* There should be at least one opcode.
*/
assert( p->nOp>0 );
/* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
- p->magic = VDBE_MAGIC_RUN;
+ p->iVdbeMagic = VDBE_MAGIC_RUN;
#ifdef SQLITE_DEBUG
for(i=0; inMem; i++){
@@ -2356,8 +2357,10 @@ void sqlite3VdbeMakeReady(
assert( p!=0 );
assert( p->nOp>0 );
assert( pParse!=0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
assert( pParse==p->pParse );
+ p->pVList = pParse->pVList;
+ pParse->pVList = 0;
db = p->db;
assert( db->mallocFailed==0 );
nVar = pParse->nVar;
@@ -2442,8 +2445,6 @@ void sqlite3VdbeMakeReady(
}
}
- p->pVList = pParse->pVList;
- pParse->pVList = 0;
if( db->mallocFailed ){
p->nVar = 0;
p->nCursor = 0;
@@ -3058,7 +3059,7 @@ int sqlite3VdbeHalt(Vdbe *p){
** one, or the complete transaction if there is no statement transaction.
*/
- if( p->magic!=VDBE_MAGIC_RUN ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){
return SQLITE_OK;
}
if( db->mallocFailed ){
@@ -3219,7 +3220,7 @@ int sqlite3VdbeHalt(Vdbe *p){
assert( db->nVdbeRead>=db->nVdbeWrite );
assert( db->nVdbeWrite>=0 );
}
- p->magic = VDBE_MAGIC_HALT;
+ p->iVdbeMagic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM_BKPT;
@@ -3392,7 +3393,7 @@ int sqlite3VdbeReset(Vdbe *p){
}
}
#endif
- p->magic = VDBE_MAGIC_RESET;
+ p->iVdbeMagic = VDBE_MAGIC_RESET;
return p->rc & db->errMask;
}
@@ -3402,7 +3403,7 @@ int sqlite3VdbeReset(Vdbe *p){
*/
int sqlite3VdbeFinalize(Vdbe *p){
int rc = SQLITE_OK;
- if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){
+ if( p->iVdbeMagic==VDBE_MAGIC_RUN || p->iVdbeMagic==VDBE_MAGIC_HALT ){
rc = sqlite3VdbeReset(p);
assert( (rc & p->db->errMask)==rc );
}
@@ -3463,7 +3464,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
sqlite3DbFree(db, pSub);
}
- if( p->magic!=VDBE_MAGIC_INIT ){
+ if( p->iVdbeMagic!=VDBE_MAGIC_INIT ){
releaseMemArray(p->aVar, p->nVar);
sqlite3DbFree(db, p->pVList);
sqlite3DbFree(db, p->pFree);
@@ -3511,7 +3512,7 @@ void sqlite3VdbeDelete(Vdbe *p){
if( p->pNext ){
p->pNext->pPrev = p->pPrev;
}
- p->magic = VDBE_MAGIC_DEAD;
+ p->iVdbeMagic = VDBE_MAGIC_DEAD;
p->db = 0;
sqlite3DbFreeNN(db, p);
}
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index 32c66af228..1095e7f589 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -125,7 +125,7 @@ char *sqlite3VdbeExpandSql(
assert( idx>0 );
}
zRawSql += nToken;
- nextIndex = idx + 1;
+ nextIndex = MAX(idx + 1, nextIndex);
assert( idx>0 && idx<=p->nVar );
pVar = &p->aVar[idx-1];
if( pVar->flags & MEM_Null ){
diff --git a/src/vtab.c b/src/vtab.c
index b2c01f2fad..ded12c13bb 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -489,7 +489,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3VdbeAddOp0(v, OP_Expire);
zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt);
- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0);
sqlite3DbFree(db, zStmt);
iReg = ++pParse->nMem;
@@ -660,6 +660,7 @@ static int vtabCallConstructor(
zType[i-1] = '\0';
}
pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN;
+ pTab->tabFlags |= TF_HasHidden;
oooHidden = TF_OOOHidden;
}else{
pTab->tabFlags |= oooHidden;
@@ -828,7 +829,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
- pTab->nCol = pNew->nCol;
+ pTab->nNVCol = pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
diff --git a/src/walker.c b/src/walker.c
index 7649036f56..927f7e52d7 100644
--- a/src/walker.c
+++ b/src/walker.c
@@ -22,7 +22,7 @@
** Walk all expressions linked into the list of Window objects passed
** as the second argument.
*/
-static int walkWindowList(Walker *pWalker, Window *pList){
+static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){
Window *pWin;
for(pWin=pList; pWin; pWin=pWin->pNextWin){
int rc;
@@ -41,6 +41,7 @@ static int walkWindowList(Walker *pWalker, Window *pList){
if( NEVER(rc) ) return WRC_Abort;
rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
if( NEVER(rc) ) return WRC_Abort;
+ if( bOneOnly ) break;
}
return WRC_Continue;
}
@@ -88,7 +89,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(pExpr, EP_WinFunc) ){
- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
+ if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort;
}
#endif
}
@@ -135,7 +136,7 @@ int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
if( pParse && IN_RENAME_OBJECT ){
/* The following may return WRC_Abort if there are unresolvable
** symbols (e.g. a table that does not exist) in a window definition. */
- int rc = walkWindowList(pWalker, p->pWinDefn);
+ int rc = walkWindowList(pWalker, p->pWinDefn, 0);
return rc;
}
}
@@ -153,7 +154,7 @@ int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
SrcList *pSrc;
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pSrc = p->pSrc;
if( pSrc ){
diff --git a/src/where.c b/src/where.c
index e2f8cbca21..75b3ceff87 100644
--- a/src/where.c
+++ b/src/where.c
@@ -99,6 +99,32 @@ int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){
return pInner->addrNxt;
}
+/*
+** While generating code for the min/max optimization, after handling
+** the aggregate-step call to min() or max(), check to see if any
+** additional looping is required. If the output order is such that
+** we are certain that the correct answer has already been found, then
+** code an OP_Goto to by pass subsequent processing.
+**
+** Any extra OP_Goto that is coded here is an optimization. The
+** correct answer should be obtained regardless. This OP_Goto just
+** makes the answer appear faster.
+*/
+void sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){
+ WhereLevel *pInner;
+ int i;
+ if( !pWInfo->bOrderedInnerLoop ) return;
+ if( pWInfo->nOBSat==0 ) return;
+ for(i=pWInfo->nLevel-1; i>=0; i--){
+ pInner = &pWInfo->a[i];
+ if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){
+ sqlite3VdbeGoto(v, pInner->addrNxt);
+ return;
+ }
+ }
+ sqlite3VdbeGoto(v, pWInfo->iBreak);
+}
+
/*
** Return the VDBE address or label to jump to in order to continue
** immediately with the next row of a WHERE clause.
@@ -668,7 +694,7 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
*/
static int termCanDriveIndex(
WhereTerm *pTerm, /* WHERE clause term to check */
- struct SrcList_item *pSrc, /* Table we are trying to access */
+ SrcItem *pSrc, /* Table we are trying to access */
Bitmask notReady /* Tables in outer loops of the join */
){
char aff;
@@ -702,7 +728,7 @@ static int termCanDriveIndex(
static void constructAutomaticIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to get the next index */
+ SrcItem *pSrc, /* The FROM clause term to get the next index */
Bitmask notReady, /* Mask of cursors that are not available */
WhereLevel *pLevel /* Write new index here */
){
@@ -726,7 +752,7 @@ static void constructAutomaticIndex(
u8 sentWarning = 0; /* True if a warnning has been issued */
Expr *pPartial = 0; /* Partial Index Expression */
int iContinue = 0; /* Jump here to skip excluded rows */
- struct SrcList_item *pTabItem; /* FROM clause term being indexed */
+ SrcItem *pTabItem; /* FROM clause term being indexed */
int addrCounter = 0; /* Address where integer counter is initialized */
int regBase; /* Array of registers where record is assembled */
@@ -910,7 +936,7 @@ static sqlite3_index_info *allocateIndexInfo(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause being analyzed */
Bitmask mUnusable, /* Ignore terms with these prereqs */
- struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */
+ SrcItem *pSrc, /* The FROM clause term that is the vtab */
ExprList *pOrderBy, /* The ORDER BY clause */
u16 *pmNoOmit /* Mask of terms not to omit */
){
@@ -1808,7 +1834,7 @@ void sqlite3WhereClausePrint(WhereClause *pWC){
void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
WhereInfo *pWInfo = pWC->pWInfo;
int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
- struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
Table *pTab = pItem->pTab;
Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
@@ -2419,7 +2445,7 @@ static int whereRangeVectorLen(
*/
static int whereLoopAddBtreeIndex(
WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
- struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ SrcItem *pSrc, /* FROM clause term being analyzed */
Index *pProbe, /* An index on pSrc */
LogEst nInMul /* log(Number of iterations due to IN) */
){
@@ -2605,7 +2631,7 @@ static int whereLoopAddBtreeIndex(
pBtm = pTerm;
pTop = 0;
if( pTerm->wtFlags & TERM_LIKEOPT ){
- /* Range contraints that come from the LIKE optimization are
+ /* Range constraints that come from the LIKE optimization are
** always used in pairs. */
pTop = &pTerm[1];
assert( (pTop-(pTerm->pWC->a))pWC->nTerm );
@@ -2910,7 +2936,7 @@ static int whereLoopAddBtree(
LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
SrcList *pTabList; /* The FROM clause */
- struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ SrcItem *pSrc; /* The FROM clause btree term to add */
WhereLoop *pNew; /* Template WhereLoop object */
int rc = SQLITE_OK; /* Return code */
int iSortIdx = 1; /* Index number */
@@ -2928,9 +2954,9 @@ static int whereLoopAddBtree(
pWC = pBuilder->pWC;
assert( !IsVirtual(pSrc->pTab) );
- if( pSrc->pIBIndex ){
+ if( pSrc->fg.isIndexedBy ){
/* An INDEXED BY clause specifies a particular index to use */
- pProbe = pSrc->pIBIndex;
+ pProbe = pSrc->u2.pIBIndex;
}else if( !HasRowid(pTab) ){
pProbe = pTab->pIndex;
}else{
@@ -2966,7 +2992,7 @@ static int whereLoopAddBtree(
if( !pBuilder->pOrSet /* Not part of an OR optimization */
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
- && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
+ && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
@@ -3016,7 +3042,7 @@ static int whereLoopAddBtree(
/* Loop over all indices. If there was an INDEXED BY clause, then only
** consider index pProbe. */
for(; rc==SQLITE_OK && pProbe;
- pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++
+ pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++
){
int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0;
if( pProbe->pPartIdxWhere!=0
@@ -3191,7 +3217,7 @@ static int whereLoopAddVirtualOne(
int rc = SQLITE_OK;
WhereLoop *pNew = pBuilder->pNew;
Parse *pParse = pBuilder->pWInfo->pParse;
- struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
+ SrcItem *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
int nConstraint = pIdxInfo->nConstraint;
assert( (mUsable & mPrereq)==mPrereq );
@@ -3383,7 +3409,7 @@ static int whereLoopAddVirtual(
WhereInfo *pWInfo; /* WHERE analysis context */
Parse *pParse; /* The parsing context */
WhereClause *pWC; /* The WHERE clause */
- struct SrcList_item *pSrc; /* The FROM clause term to search */
+ SrcItem *pSrc; /* The FROM clause term to search */
sqlite3_index_info *p; /* Object to pass to xBestIndex() */
int nConstraint; /* Number of constraints in p */
int bIn; /* True if plan uses IN(...) operator */
@@ -3511,7 +3537,7 @@ static int whereLoopAddOr(
WhereClause tempWC;
WhereLoopBuilder sSubBuild;
WhereOrSet sSum, sCur;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pWC = pBuilder->pWC;
pWCEnd = pWC->a + pWC->nTerm;
@@ -3627,8 +3653,8 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
Bitmask mPrior = 0;
int iTab;
SrcList *pTabList = pWInfo->pTabList;
- struct SrcList_item *pItem;
- struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
+ SrcItem *pItem;
+ SrcItem *pEnd = &pTabList->a[pWInfo->nLevel];
sqlite3 *db = pWInfo->pParse->db;
int rc = SQLITE_OK;
WhereLoop *pNew;
@@ -3651,7 +3677,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pItem->pTab) ){
- struct SrcList_item *p;
+ SrcItem *p;
for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
@@ -4506,7 +4532,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
*/
static int whereShortCut(WhereLoopBuilder *pBuilder){
WhereInfo *pWInfo;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
WhereClause *pWC;
WhereTerm *pTerm;
WhereLoop *pLoop;
@@ -4965,7 +4991,7 @@ WhereInfo *sqlite3WhereBegin(
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
pWInfo->revMask = ALLBITS;
}
- if( pParse->nErr || NEVER(db->mallocFailed) ){
+ if( pParse->nErr || db->mallocFailed ){
goto whereBeginError;
}
#ifdef WHERETRACE_ENABLED
@@ -5036,7 +5062,7 @@ WhereInfo *sqlite3WhereBegin(
}
for(i=pWInfo->nLevel-1; i>=1; i--){
WhereTerm *pTerm, *pEnd;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pLoop = pWInfo->a[i].pWLoop;
pItem = &pWInfo->pTabList->a[pLoop->iTab];
if( (pItem->fg.jointype & JT_LEFT)==0 ) continue;
@@ -5126,7 +5152,7 @@ WhereInfo *sqlite3WhereBegin(
for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom];
pTab = pTabItem->pTab;
@@ -5463,7 +5489,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
int k, last;
VdbeOp *pOp, *pLastOp;
Index *pIdx = 0;
- struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
+ SrcItem *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
pLoop = pLevel->pWLoop;
@@ -5539,7 +5565,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
#endif
pOp = sqlite3VdbeGetOp(v, k);
pLastOp = pOp + (last - k);
- assert( pOpnErr>0 && pOp==pLastOp) );
do{
if( pOp->p1!=pLevel->iTabCur ){
/* no-op */
diff --git a/src/whereInt.h b/src/whereInt.h
index f8509996fa..8896da0271 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -270,11 +270,7 @@ struct WhereTerm {
#define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT4
-# define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
-#else
-# define TERM_VNULL 0x0000 /* Disabled if not using stat4 */
-#endif
+#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
#define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */
#define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */
#define TERM_LIKE 0x0400 /* The original LIKE operator */
@@ -544,7 +540,7 @@ Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*);
Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
-void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
+void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
diff --git a/src/wherecode.c b/src/wherecode.c
index 4afe0ac9c9..a7a76fb704 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -129,7 +129,7 @@ int sqlite3WhereExplainOneScan(
if( sqlite3ParseToplevel(pParse)->explain==2 )
#endif
{
- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
+ SrcItem *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
@@ -922,7 +922,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
** Insert an OP_CursorHint instruction if it is appropriate to do so.
*/
static void codeCursorHint(
- struct SrcList_item *pTabItem, /* FROM clause item */
+ SrcItem *pTabItem, /* FROM clause item */
WhereInfo *pWInfo, /* The where clause */
WhereLevel *pLevel, /* Which loop to provide hints for */
WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
@@ -1297,7 +1297,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
WhereClause *pWC; /* Decomposition of the entire WHERE clause */
WhereTerm *pTerm; /* A WHERE clause term */
sqlite3 *db; /* Database connection */
- struct SrcList_item *pTabItem; /* FROM clause term being coded */
+ SrcItem *pTabItem; /* FROM clause term being coded */
int addrBrk; /* Jump here to break out of the loop */
int addrHalt; /* addrBrk for the outermost loop */
int addrCont; /* Jump here to continue with next cycle */
@@ -1743,6 +1743,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
SWAP(u8, nBtm, nTop);
}
+ if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){
+ /* In case OP_SeekScan is used, ensure that the index cursor does not
+ ** point to a valid row for the first iteration of this loop. */
+ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
+ }
+
/* Generate code to evaluate all constraint terms using == or IN
** and store the values of those terms in an array of registers
** starting at regBase.
@@ -2079,7 +2085,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
*/
if( pWInfo->nLevel>1 ){
int nNotReady; /* The number of notReady tables */
- struct SrcList_item *origSrc; /* Original list of tables */
+ SrcItem *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
pOrTab = sqlite3StackAllocRaw(db,
sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
diff --git a/src/whereexpr.c b/src/whereexpr.c
index a77eb36106..7b80c41393 100644
--- a/src/whereexpr.c
+++ b/src/whereexpr.c
@@ -1007,6 +1007,276 @@ static int exprMightBeIndexed(
return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
}
+/*
+** Expression callback for exprUsesSrclist().
+*/
+static int exprUsesSrclistCb(Walker *p, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN ){
+ SrcList *pSrc = p->u.pSrcList;
+ int iCsr = pExpr->iTable;
+ int ii;
+ for(ii=0; iinSrc; ii++){
+ if( pSrc->a[ii].iCursor==iCsr ){
+ return p->eCode ? WRC_Abort : WRC_Continue;
+ }
+ }
+ return p->eCode ? WRC_Continue : WRC_Abort;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Select callback for exprUsesSrclist().
+*/
+static int exprUsesSrclistSelectCb(Walker *NotUsed1, Select *NotUsed2){
+ UNUSED_PARAMETER(NotUsed1);
+ UNUSED_PARAMETER(NotUsed2);
+ return WRC_Abort;
+}
+
+/*
+** This function always returns true if expression pExpr contains
+** a sub-select.
+**
+** If there is no sub-select in pExpr, then return true if pExpr
+** contains a TK_COLUMN node for a table that is (bUses==1)
+** or is not (bUses==0) in pSrc.
+**
+** Said another way:
+**
+** bUses Return Meaning
+** -------- ------ ------------------------------------------------
+**
+** bUses==1 true pExpr contains either a sub-select or a
+** TK_COLUMN referencing pSrc.
+**
+** bUses==1 false pExpr contains no sub-selects and all TK_COLUMN
+** nodes reference tables not found in pSrc
+**
+** bUses==0 true pExpr contains either a sub-select or a TK_COLUMN
+** that references a table not in pSrc.
+**
+** bUses==0 false pExpr contains no sub-selects and all TK_COLUMN
+** nodes reference pSrc
+*/
+static int exprUsesSrclist(SrcList *pSrc, Expr *pExpr, int bUses){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.eCode = bUses;
+ sWalker.u.pSrcList = pSrc;
+ sWalker.xExprCallback = exprUsesSrclistCb;
+ sWalker.xSelectCallback = exprUsesSrclistSelectCb;
+ return (sqlite3WalkExpr(&sWalker, pExpr)==WRC_Abort);
+}
+
+/*
+** Context object used by exprExistsToInIter() as it iterates through an
+** expression tree.
+*/
+struct ExistsToInCtx {
+ SrcList *pSrc; /* The tables in an EXISTS(SELECT ... FROM ...) */
+ Expr *pInLhs; /* OUT: Use this as the LHS of the IN operator */
+ Expr *pEq; /* OUT: The == term that include pInLhs */
+ Expr **ppAnd; /* OUT: The AND operator that includes pEq as a child */
+ Expr **ppParent; /* The AND operator currently being examined */
+};
+
+/*
+** Iterate through all AND connected nodes in the expression tree
+** headed by (*ppExpr), populating the structure passed as the first
+** argument with the values required by exprAnalyzeExistsFindEq().
+**
+** This function returns non-zero if the expression tree does not meet
+** the two conditions described by the header comment for
+** exprAnalyzeExistsFindEq(), or zero if it does.
+*/
+static int exprExistsToInIter(struct ExistsToInCtx *p, Expr **ppExpr){
+ Expr *pExpr = *ppExpr;
+ switch( pExpr->op ){
+ case TK_AND:
+ p->ppParent = ppExpr;
+ if( exprExistsToInIter(p, &pExpr->pLeft) ) return 1;
+ p->ppParent = ppExpr;
+ if( exprExistsToInIter(p, &pExpr->pRight) ) return 1;
+ break;
+ case TK_EQ: {
+ int bLeft = exprUsesSrclist(p->pSrc, pExpr->pLeft, 0);
+ int bRight = exprUsesSrclist(p->pSrc, pExpr->pRight, 0);
+ if( bLeft || bRight ){
+ if( (bLeft && bRight) || p->pInLhs ) return 1;
+ p->pInLhs = bLeft ? pExpr->pLeft : pExpr->pRight;
+ if( exprUsesSrclist(p->pSrc, p->pInLhs, 1) ) return 1;
+ p->pEq = pExpr;
+ p->ppAnd = p->ppParent;
+ }
+ break;
+ }
+ default:
+ if( exprUsesSrclist(p->pSrc, pExpr, 0) ){
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/*
+** This function is used by exprAnalyzeExists() when creating virtual IN(...)
+** terms equivalent to user-supplied EXIST(...) clauses. It splits the WHERE
+** clause of the Select object passed as the first argument into one or more
+** expressions joined by AND operators, and then tests if the following are
+** true:
+**
+** 1. Exactly one of the AND separated terms refers to the outer
+** query, and it is an == (TK_EQ) expression.
+**
+** 2. Only one side of the == expression refers to the outer query, and
+** it does not refer to any columns from the inner query.
+**
+** If both these conditions are true, then a pointer to the side of the ==
+** expression that refers to the outer query is returned. The caller will
+** use this expression as the LHS of the IN(...) virtual term. Or, if one
+** or both of the above conditions are not true, NULL is returned.
+**
+** If non-NULL is returned and ppEq is non-NULL, *ppEq is set to point
+** to the == expression node before returning. If pppAnd is non-NULL and
+** the == node is not the root of the WHERE clause, then *pppAnd is set
+** to point to the pointer to the AND node that is the parent of the ==
+** node within the WHERE expression tree.
+*/
+static Expr *exprAnalyzeExistsFindEq(
+ Select *pSel, /* The SELECT of the EXISTS */
+ Expr **ppEq, /* OUT: == node from WHERE clause */
+ Expr ***pppAnd /* OUT: Pointer to parent of ==, if any */
+){
+ struct ExistsToInCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pSrc = pSel->pSrc;
+ if( exprExistsToInIter(&ctx, &pSel->pWhere) ){
+ return 0;
+ }
+ if( ppEq ) *ppEq = ctx.pEq;
+ if( pppAnd ) *pppAnd = ctx.ppAnd;
+ return ctx.pInLhs;
+}
+
+/*
+** Term idxTerm of the WHERE clause passed as the second argument is an
+** EXISTS expression with a correlated SELECT statement on the RHS.
+** This function analyzes the SELECT statement, and if possible adds an
+** equivalent "? IN(SELECT...)" virtual term to the WHERE clause.
+**
+** For an EXISTS term such as the following:
+**
+** EXISTS (SELECT ... FROM WHERE = AND )
+**
+** The virtual IN() term added is:
+**
+** IN (SELECT FROM WHERE )
+**
+** The virtual term is only added if the following conditions are met:
+**
+** 1. The sub-select must not be an aggregate or use window functions,
+**
+** 2. The sub-select must not be a compound SELECT,
+**
+** 3. Expression must refer to at least one column from the outer
+** query, and must not refer to any column from the inner query
+** (i.e. from ).
+**
+** 4. and must not refer to any values from the outer query.
+** In other words, once has been removed, the inner query
+** must not be correlated.
+**
+*/
+static void exprAnalyzeExists(
+ SrcList *pSrc, /* the FROM clause */
+ WhereClause *pWC, /* the WHERE clause */
+ int idxTerm /* Index of the term to be analyzed */
+){
+ Parse *pParse = pWC->pWInfo->pParse;
+ WhereTerm *pTerm = &pWC->a[idxTerm];
+ Expr *pExpr = pTerm->pExpr;
+ Select *pSel = pExpr->x.pSelect;
+ Expr *pDup = 0;
+ Expr *pEq = 0;
+ Expr *pRet = 0;
+ Expr *pInLhs = 0;
+ Expr **ppAnd = 0;
+ int idxNew;
+ sqlite3 *db = pParse->db;
+
+ assert( pExpr->op==TK_EXISTS );
+ assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
+
+ if( pSel->selFlags & SF_Aggregate ) return;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSel->pWin ) return;
+#endif
+ if( pSel->pPrior ) return;
+ if( pSel->pWhere==0 ) return;
+ if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;
+
+ pDup = sqlite3ExprDup(db, pExpr, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDup);
+ return;
+ }
+ pSel = pDup->x.pSelect;
+ sqlite3ExprListDelete(db, pSel->pEList);
+ pSel->pEList = 0;
+
+ pInLhs = exprAnalyzeExistsFindEq(pSel, &pEq, &ppAnd);
+ assert( pInLhs && pEq );
+ assert( pEq==pSel->pWhere || ppAnd );
+ if( pInLhs==pEq->pLeft ){
+ pRet = pEq->pRight;
+ }else{
+ CollSeq *p = sqlite3ExprCompareCollSeq(pParse, pEq);
+ pInLhs = sqlite3ExprAddCollateString(pParse, pInLhs, p?p->zName:"BINARY");
+ pRet = pEq->pLeft;
+ }
+
+ assert( pDup->pLeft==0 );
+ pDup->op = TK_IN;
+ pDup->pLeft = pInLhs;
+ pDup->flags &= ~EP_VarSelect;
+ if( pRet->op==TK_VECTOR ){
+ pSel->pEList = pRet->x.pList;
+ pRet->x.pList = 0;
+ sqlite3ExprDelete(db, pRet);
+ }else{
+ pSel->pEList = sqlite3ExprListAppend(pParse, 0, pRet);
+ }
+ pEq->pLeft = 0;
+ pEq->pRight = 0;
+ if( ppAnd ){
+ Expr *pAnd = *ppAnd;
+ Expr *pOther = (pAnd->pLeft==pEq) ? pAnd->pRight : pAnd->pLeft;
+ pAnd->pLeft = pAnd->pRight = 0;
+ sqlite3ExprDelete(db, pAnd);
+ *ppAnd = pOther;
+ }else{
+ assert( pSel->pWhere==pEq );
+ pSel->pWhere = 0;
+ }
+ sqlite3ExprDelete(db, pEq);
+
+#ifdef WHERETRACE_ENABLED /* 0x20 */
+ if( sqlite3WhereTrace & 0x20 ){
+ sqlite3DebugPrintf("Convert EXISTS:\n");
+ sqlite3TreeViewExpr(0, pExpr, 0);
+ sqlite3DebugPrintf("into IN:\n");
+ sqlite3TreeViewExpr(0, pDup, 0);
+ }
+#endif
+ idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ pWC->a[idxTerm].wtFlags |= TERM_COPIED;
+}
+
/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
@@ -1140,6 +1410,12 @@ static void exprAnalyze(
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
+ }else if( op==TK_ISNULL && 0==sqlite3ExprCanBeNull(pLeft) ){
+ pExpr->op = TK_TRUEFALSE;
+ pExpr->u.zToken = "false";
+ ExprSetProperty(pExpr, EP_IsFalse);
+ pTerm->prereqAll = 0;
+ pTerm->eOperator = 0;
}
}
@@ -1192,6 +1468,52 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
+ else if( pExpr->op==TK_EXISTS ){
+ /* Perhaps treat an EXISTS operator as an IN operator */
+ if( (pExpr->flags & EP_VarSelect)!=0
+ && OptimizationEnabled(db, SQLITE_ExistsToIN)
+ ){
+ exprAnalyzeExists(pSrc, pWC, idxTerm);
+ }
+ }
+
+ /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently
+ ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
+ ** virtual term of that form.
+ **
+ ** The virtual term must be tagged with TERM_VNULL.
+ */
+ else if( pExpr->op==TK_NOTNULL ){
+ if( pExpr->pLeft->op==TK_COLUMN
+ && pExpr->pLeft->iColumn>=0
+ && !ExprHasProperty(pExpr, EP_FromJoin)
+ ){
+ Expr *pNewExpr;
+ Expr *pLeft = pExpr->pLeft;
+ int idxNew;
+ WhereTerm *pNewTerm;
+
+ pNewExpr = sqlite3PExpr(pParse, TK_GT,
+ sqlite3ExprDup(db, pLeft, 0),
+ sqlite3ExprAlloc(db, TK_NULL, 0, 0));
+
+ idxNew = whereClauseInsert(pWC, pNewExpr,
+ TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
+ if( idxNew ){
+ pNewTerm = &pWC->a[idxNew];
+ pNewTerm->prereqRight = 0;
+ pNewTerm->leftCursor = pLeft->iTable;
+ pNewTerm->u.x.leftColumn = pLeft->iColumn;
+ pNewTerm->eOperator = WO_GT;
+ markTermAsChild(pWC, idxNew, idxTerm);
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags |= TERM_COPIED;
+ pNewTerm->prereqAll = pTerm->prereqAll;
+ }
+ }
+ }
+
+
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
/* Add constraints to reduce the search space on a LIKE or GLOB
** operator.
@@ -1206,7 +1528,8 @@ static void exprAnalyze(
** bound is made all lowercase so that the bounds also work when comparing
** BLOBs.
*/
- if( pWC->op==TK_AND
+ else if( pExpr->op==TK_FUNCTION
+ && pWC->op==TK_AND
&& isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase)
){
Expr *pLeft; /* LHS of LIKE/GLOB operator */
@@ -1276,6 +1599,65 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
+ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
+ ** new terms for each component comparison - "a = ?" and "b = ?". The
+ ** new terms completely replace the original vector comparison, which is
+ ** no longer used.
+ **
+ ** This is only required if at least one side of the comparison operation
+ ** is not a sub-select. */
+ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS)
+ && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
+ && sqlite3ExprVectorSize(pExpr->pRight)==nLeft
+ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
+ || (pExpr->pRight->flags & EP_xIsSelect)==0)
+ && pWC->op==TK_AND
+ ){
+ int i;
+ for(i=0; ipLeft, i);
+ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
+
+ pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
+ transferJoinMarkings(pNew, pExpr);
+ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
+ }
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
+ pTerm->eOperator = 0;
+ }
+
+ /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
+ ** a virtual term for each vector component. The expression object
+ ** used by each such virtual term is pExpr (the full vector IN(...)
+ ** expression). The WhereTerm.u.x.iField variable identifies the index within
+ ** the vector on the LHS that the virtual term represents.
+ **
+ ** This only works if the RHS is a simple SELECT (not a compound) that does
+ ** not use window functions.
+ */
+ else if( pExpr->op==TK_IN
+ && pTerm->u.x.iField==0
+ && pExpr->pLeft->op==TK_VECTOR
+ && pExpr->x.pSelect->pPrior==0
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ && pExpr->x.pSelect->pWin==0
+#endif
+ && pWC->op==TK_AND
+ ){
+ int i;
+ for(i=0; ipLeft); i++){
+ int idxNew;
+ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
+ pWC->a[idxNew].u.x.iField = i+1;
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ }
+ }
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Add a WO_AUX auxiliary term to the constraint set if the
** current expression is of the form "column OP expr" where OP
@@ -1286,7 +1668,7 @@ static void exprAnalyze(
** virtual tables. The native query optimizer does not attempt
** to do anything with MATCH functions.
*/
- if( pWC->op==TK_AND ){
+ else if( pWC->op==TK_AND ){
Expr *pRight = 0, *pLeft = 0;
int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
while( res-- > 0 ){
@@ -1322,102 +1704,6 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
- /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
- ** new terms for each component comparison - "a = ?" and "b = ?". The
- ** new terms completely replace the original vector comparison, which is
- ** no longer used.
- **
- ** This is only required if at least one side of the comparison operation
- ** is not a sub-select. */
- if( pWC->op==TK_AND
- && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
- && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
- && sqlite3ExprVectorSize(pExpr->pRight)==nLeft
- && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
- || (pExpr->pRight->flags & EP_xIsSelect)==0)
- ){
- int i;
- for(i=0; ipLeft, i);
- Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
-
- pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
- transferJoinMarkings(pNew, pExpr);
- idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
- exprAnalyze(pSrc, pWC, idxNew);
- }
- pTerm = &pWC->a[idxTerm];
- pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
- pTerm->eOperator = 0;
- }
-
- /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
- ** a virtual term for each vector component. The expression object
- ** used by each such virtual term is pExpr (the full vector IN(...)
- ** expression). The WhereTerm.u.x.iField variable identifies the index within
- ** the vector on the LHS that the virtual term represents.
- **
- ** This only works if the RHS is a simple SELECT (not a compound) that does
- ** not use window functions.
- */
- if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->u.x.iField==0
- && pExpr->pLeft->op==TK_VECTOR
- && pExpr->x.pSelect->pPrior==0
-#ifndef SQLITE_OMIT_WINDOWFUNC
- && pExpr->x.pSelect->pWin==0
-#endif
- ){
- int i;
- for(i=0; ipLeft); i++){
- int idxNew;
- idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
- pWC->a[idxNew].u.x.iField = i+1;
- exprAnalyze(pSrc, pWC, idxNew);
- markTermAsChild(pWC, idxNew, idxTerm);
- }
- }
-
-#ifdef SQLITE_ENABLE_STAT4
- /* When sqlite_stat4 histogram data is available an operator of the
- ** form "x IS NOT NULL" can sometimes be evaluated more efficiently
- ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
- ** virtual term of that form.
- **
- ** Note that the virtual term must be tagged with TERM_VNULL.
- */
- if( pExpr->op==TK_NOTNULL
- && pExpr->pLeft->op==TK_COLUMN
- && pExpr->pLeft->iColumn>=0
- && !ExprHasProperty(pExpr, EP_FromJoin)
- && OptimizationEnabled(db, SQLITE_Stat4)
- ){
- Expr *pNewExpr;
- Expr *pLeft = pExpr->pLeft;
- int idxNew;
- WhereTerm *pNewTerm;
-
- pNewExpr = sqlite3PExpr(pParse, TK_GT,
- sqlite3ExprDup(db, pLeft, 0),
- sqlite3ExprAlloc(db, TK_NULL, 0, 0));
-
- idxNew = whereClauseInsert(pWC, pNewExpr,
- TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
- if( idxNew ){
- pNewTerm = &pWC->a[idxNew];
- pNewTerm->prereqRight = 0;
- pNewTerm->leftCursor = pLeft->iTable;
- pNewTerm->u.x.leftColumn = pLeft->iColumn;
- pNewTerm->eOperator = WO_GT;
- markTermAsChild(pWC, idxNew, idxTerm);
- pTerm = &pWC->a[idxTerm];
- pTerm->wtFlags |= TERM_COPIED;
- pNewTerm->prereqAll = pTerm->prereqAll;
- }
- }
-#endif /* SQLITE_ENABLE_STAT4 */
-
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
*/
@@ -1576,7 +1862,7 @@ void sqlite3WhereExprAnalyze(
*/
void sqlite3WhereTabFuncArgs(
Parse *pParse, /* Parsing context */
- struct SrcList_item *pItem, /* The FROM clause term to process */
+ SrcItem *pItem, /* The FROM clause term to process */
WhereClause *pWC /* Xfer function arguments to here */
){
Table *pTab;
diff --git a/src/window.c b/src/window.c
index 88ff7d314d..09572ec033 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1304,15 +1304,19 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
** SELECT, or (b) the windows already linked use a compatible window frame.
*/
void sqlite3WindowLink(Select *pSel, Window *pWin){
- if( pSel!=0
- && (0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0))
- ){
- pWin->pNextWin = pSel->pWin;
- if( pSel->pWin ){
- pSel->pWin->ppThis = &pWin->pNextWin;
+ if( pSel ){
+ if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){
+ pWin->pNextWin = pSel->pWin;
+ if( pSel->pWin ){
+ pSel->pWin->ppThis = &pWin->pNextWin;
+ }
+ pSel->pWin = pWin;
+ pWin->ppThis = &pSel->pWin;
+ }else{
+ if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){
+ pSel->selFlags |= SF_MultiPart;
+ }
}
- pSel->pWin = pWin;
- pWin->ppThis = &pSel->pWin;
}
}
diff --git a/test/alterauth2.test b/test/alterauth2.test
index bd589cda1d..a411408b58 100644
--- a/test/alterauth2.test
+++ b/test/alterauth2.test
@@ -95,4 +95,21 @@ do_auth_test 1.2 {
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
}
+do_auth_test 1.3 {
+ ALTER TABLE t2 DROP COLUMN c;
+} {
+ {SQLITE_FUNCTION {} like {} {}}
+ {SQLITE_FUNCTION {} sqlite_drop_column {} {}}
+ {SQLITE_FUNCTION {} sqlite_rename_test {} {}}
+ {SQLITE_READ sqlite_master name main {}}
+ {SQLITE_READ sqlite_master sql main {}}
+ {SQLITE_READ sqlite_master tbl_name main {}}
+ {SQLITE_READ sqlite_master type main {}}
+ {SQLITE_READ sqlite_temp_master name temp {}}
+ {SQLITE_READ sqlite_temp_master sql temp {}}
+ {SQLITE_READ sqlite_temp_master type temp {}}
+ {SQLITE_SELECT {} {} {} {}}
+ {SQLITE_UPDATE sqlite_master sql main {}}
+}
+
finish_test
diff --git a/test/altercol.test b/test/altercol.test
index 1479b3a7d3..d2406e4c96 100644
--- a/test/altercol.test
+++ b/test/altercol.test
@@ -13,8 +13,6 @@
# file format change that may be used in the future to implement
# "ALTER TABLE ... RENAME COLUMN ... TO".
#
-# $Id: alter4.test,v 1.1 2009/02/02 18:03:22 drh Exp $
-#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -804,6 +802,25 @@ do_execsql_test 20.110 {
SELECT sql FROM sqlite_master WHERE name='t1';
} {{CREATE TABLE t1(xx UNIQUE,yy UNIQUE,zz UNIQUE,UNIQUE(xx),PRIMARY KEY(yy),UNIQUE(zz))}}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 21.0 {
+ CREATE TABLE t1(a, b, c NOT NULL);
+ CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.c IS NOT NULL BEGIN
+ SELECT c NOT NULL FROM t1;
+ END;
+}
+
+do_execsql_test 21.1 {
+ ALTER TABLE t1 RENAME c TO d;
+}
+
+do_execsql_test 21.2 {
+ SELECT sql FROM sqlite_schema WHERE name IS 'tr1'
+} {{CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.d IS NOT NULL BEGIN
+ SELECT d NOT NULL FROM t1;
+ END}
+}
finish_test
diff --git a/test/altercorrupt.test b/test/altercorrupt.test
new file mode 100644
index 0000000000..5c50fa4a47
--- /dev/null
+++ b/test/altercorrupt.test
@@ -0,0 +1,175 @@
+# 2019-01-11
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix altercorrupt
+
+database_may_be_corrupt
+
+#--------------------------------------------------------------------------
+reset_db
+do_test 1.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 24576 pagesize 4096 filename crash-685346d89b5e5f.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
+| 32: 00 00 63 00 00 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
+| 48: 00 00 00 00 00 00 0f f0 00 00 00 01 00 00 00 00 ................
+| 64: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
+| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
+| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
+| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
+| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
+| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
+| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
+| 3856: 45 20 74 33 28 78 20 49 4e 54 45 47 45 52 20 50 E t3(x INTEGER P
+| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
+| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
+| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
+| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
+| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
+| 3952: 4f 55 54 20 52 4f 57 49 44 58 03 07 17 11 11 01 OUT ROWIDX......
+| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
+| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
+| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
+| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
+| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
+| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
+| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
+| 4080: 5f 74 31 5f 31 74 31 03 00 00 00 08 00 00 00 00 _t1_1t1.........
+| page 2 offset 4096
+| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
+| 16: 0f ca 0f 8f 0f b4 0f a9 0f 9e 0f 93 00 00 00 00 ................
+| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
+| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 01 01 01 ......Zm........
+| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
+| 4032: 06 05 01 01 01 01 06 3c 6a 0c 09 05 05 01 01 01 .......5),e
+| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
+| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
+| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
+| 4080: 5f 74 31 5f 31 84 31 03 01 00 00 08 00 00 00 00 _t1_1.1.........
+| page 2 offset 4096
+| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
+| 16: 0f ca 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
+| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 00 f1 01 ......Zm........
+| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
+| 4032: 06 05 01 00 f1 01 06 3c 6a 0c 09 05 05 01 01 01 .......10));
+ CREATE TABLE t13(a, b, c CHECK(c>10));
+}
+do_catchsql_test 3.1 {
+ ALTER TABLE t12 DROP COLUMN c;
+} {1 {error in table t12 after drop column: no such column: c}}
+
+do_catchsql_test 3.2 {
+ ALTER TABLE t13 DROP COLUMN c;
+} {0 {}}
+
+#-------------------------------------------------------------------------
+# Test that generated columns can be dropped. And that other columns from
+# tables that contain generated columns can be dropped.
+#
+foreach {tn wo vs} {
+ 1 "" ""
+ 2 "" VIRTUAL
+ 3 "" STORED
+ 4 "WITHOUT ROWID" STORED
+ 5 "WITHOUT ROWID" VIRTUAL
+} {
+ reset_db
+
+ do_execsql_test 4.$tn.0 "
+ CREATE TABLE 'my table'(a, b PRIMARY KEY, c AS (a+b) $vs, d) $wo
+ "
+ do_execsql_test 4.$tn.1 {
+ INSERT INTO "my table"(a, b, d) VALUES(1, 2, 'hello');
+ INSERT INTO "my table"(a, b, d) VALUES(3, 4, 'world');
+
+ SELECT * FROM "my table"
+ } {
+ 1 2 3 hello
+ 3 4 7 world
+ }
+
+ do_execsql_test 4.$tn.2 {
+ ALTER TABLE "my table" DROP COLUMN c;
+ }
+ do_execsql_test 4.$tn.3 {
+ SELECT * FROM "my table"
+ } {
+ 1 2 hello
+ 3 4 world
+ }
+
+ do_execsql_test 4.$tn.4 "
+ CREATE TABLE x1(a, b, c PRIMARY KEY, d AS (b+c) $vs, e) $wo
+ "
+ do_execsql_test 4.$tn.5 {
+ INSERT INTO x1(a, b, c, e) VALUES(1, 2, 3, 4);
+ INSERT INTO x1(a, b, c, e) VALUES(5, 6, 7, 8);
+ INSERT INTO x1(a, b, c, e) VALUES(9, 10, 11, 12);
+ SELECT * FROM x1;
+ } {
+ 1 2 3 5 4
+ 5 6 7 13 8
+ 9 10 11 21 12
+ }
+
+ do_execsql_test 4.$tn.6 {
+ ALTER TABLE x1 DROP COLUMN a
+ }
+ do_execsql_test 4.$tn.7 {
+ SELECT * FROM x1
+ } {
+ 2 3 5 4
+ 6 7 13 8
+ 10 11 21 12
+ }
+ do_execsql_test 4.$tn.8 {
+ ALTER TABLE x1 DROP COLUMN e
+ }
+ do_execsql_test 4.$tn.9 {
+ SELECT * FROM x1
+ } {
+ 2 3 5
+ 6 7 13
+ 10 11 21
+ }
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE p1(a PRIMARY KEY, b UNIQUE);
+ CREATE TABLE c1(x, y, z REFERENCES p1(c));
+ CREATE TABLE c2(x, y, z, w REFERENCES p1(b));
+}
+do_execsql_test 5.1 {
+ ALTER TABLE c1 DROP COLUMN z;
+ ALTER TABLE c2 DROP COLUMN z;
+ SELECT sql FROM sqlite_schema WHERE name IN ('c1', 'c2');
+} {
+ {CREATE TABLE c1(x, y)}
+ {CREATE TABLE c2(x, y, w REFERENCES p1(b))}
+}
+
+do_execsql_test 5.2.1 {
+ CREATE VIEW v1 AS SELECT d, e FROM p1
+}
+do_catchsql_test 5.2.2 {
+ ALTER TABLE c1 DROP COLUMN x
+} {1 {error in view v1: no such column: d}}
+do_execsql_test 5.3.1 {
+ DROP VIEW v1;
+ CREATE VIEW v1 AS SELECT x, y FROM c1;
+}
+do_catchsql_test 5.3.2 {
+ ALTER TABLE c1 DROP COLUMN x
+} {1 {error in view v1 after drop column: no such column: x}}
+
+do_execsql_test 5.4.1 {
+ CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
+ INSERT INTO p1 VALUES(new.y, new.xyz);
+ END;
+}
+do_catchsql_test 5.4.2 {
+ ALTER TABLE c1 DROP COLUMN y
+} {1 {error in trigger tr: no such column: new.xyz}}
+do_execsql_test 5.5.1 {
+ DROP TRIGGER tr;
+ CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
+ INSERT INTO p1 VALUES(new.y, new.z);
+ END;
+}
+do_catchsql_test 5.5.2 {
+ ALTER TABLE c1 DROP COLUMN y
+} {1 {error in trigger tr: no such column: new.z}}
+
+finish_test
diff --git a/test/alterdropcol2.test b/test/alterdropcol2.test
new file mode 100644
index 0000000000..8af3677d32
--- /dev/null
+++ b/test/alterdropcol2.test
@@ -0,0 +1,214 @@
+# 2021 February 19
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix alterdropcol2
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+# EVIDENCE-OF: R-58318-35349 The DROP COLUMN syntax is used to remove an
+# existing column from a table.
+do_execsql_test 1.0 {
+ CREATE TABLE t1(c, b, a, PRIMARY KEY(b, a)) WITHOUT ROWID;
+ INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
+}
+do_execsql_test 1.1 {
+ ALTER TABLE t1 DROP c;
+}
+
+# EVIDENCE-OF: The DROP COLUMN command removes the named column from the table,
+# and also rewrites the entire table to purge the data associated with that
+# column.
+do_execsql_test 1.2.1 {
+ SELECT * FROM t1;
+} {2 3 5 6}
+
+do_execsql_test 1.2.2 {
+ SELECT sql FROM sqlite_schema;
+} {
+ {CREATE TABLE t1(b, a, PRIMARY KEY(b, a)) WITHOUT ROWID}
+}
+
+proc do_atdc_error_test {tn schema atdc error} {
+ reset_db
+ execsql $schema
+ uplevel [list do_catchsql_test $tn $atdc [list 1 [string trim $error]]]
+}
+
+#-------------------------------------------------------------------------
+# Test cases 2.* attempt to verify the following:
+#
+# EVIDENCE-OF: R-24098-10282 The DROP COLUMN command only works if the column
+# is not referenced by any other parts of the schema and is not a PRIMARY KEY
+# and does not have a UNIQUE constraint.
+#
+
+# EVIDENCE-OF: R-05184-13006 The column is a PRIMARY KEY.
+#
+do_atdc_error_test 2.1.1 {
+ CREATE TABLE x1(a PRIMARY KEY, b, c);
+} {
+ ALTER TABLE x1 DROP COLUMN a
+} {
+ cannot drop PRIMARY KEY column: "a"
+}
+
+# EVIDENCE-OF: R-43412-16016 The column has a UNIQUE constraint.
+#
+do_atdc_error_test 2.2.1 {
+ CREATE TABLE x1(a PRIMARY KEY, b, c UNIQUE);
+} {
+ ALTER TABLE x1 DROP COLUMN c
+} {
+ cannot drop UNIQUE column: "c"
+}
+do_atdc_error_test 2.2.2 {
+ CREATE TABLE x1(a PRIMARY KEY, b, c, UNIQUE(b, c));
+} {
+ ALTER TABLE x1 DROP COLUMN c
+} {
+ error in table x1 after drop column: no such column: c
+}
+
+# EVIDENCE-OF: R-46731-08965: The column is indexed.
+#
+do_atdc_error_test 2.3.1 {
+ CREATE TABLE 'one two'('x y', 'z 1', 'a b');
+ CREATE INDEX idx ON 'one two'('z 1');
+} {
+ ALTER TABLE 'one two' DROP COLUMN 'z 1'
+} {
+ error in index idx after drop column: no such column: z 1
+}
+do_atdc_error_test 2.3.2 {
+ CREATE TABLE x1(a, b, c);
+ CREATE INDEX idx ON x1(a);
+} {
+ ALTER TABLE x1 DROP COLUMN a;
+} {
+ error in index idx after drop column: no such column: a
+}
+
+# EVIDENCE-OF: R-46731-08965: The column is indexed.
+#
+do_atdc_error_test 2.4.1 {
+ CREATE TABLE x1234(a, b, c PRIMARY KEY) WITHOUT ROWID;
+ CREATE INDEX i1 ON x1234(b) WHERE ((a+5) % 10)==0;
+} {
+ ALTER TABLE x1234 DROP a
+} {
+ error in index i1 after drop column: no such column: a
+}
+
+# EVIDENCE-OF: R-18825-17786 The column appears in a table CHECK constraint.
+#
+do_atdc_error_test 2.5.1 {
+ CREATE TABLE x1234(a, b, c PRIMARY KEY, CHECK(((a+5)%10)!=0)) WITHOUT ROWID;
+} {
+ ALTER TABLE x1234 DROP a
+} {
+ error in table x1234 after drop column: no such column: a
+}
+
+# EVIDENCE-OF: R-55640-01652 The column is used in a foreign key constraint.
+#
+do_atdc_error_test 2.6.1 {
+ CREATE TABLE p1(x, y UNIQUE);
+ CREATE TABLE c1(u, v, FOREIGN KEY (v) REFERENCES p1(y))
+} {
+ ALTER TABLE c1 DROP v
+} {
+ error in table c1 after drop column: unknown column "v" in foreign key definition
+}
+
+# EVIDENCE-OF: R-20795-39479 The column is used in the expression of a
+# generated column.
+do_atdc_error_test 2.7.1 {
+ CREATE TABLE c1(u, v, w AS (u+v));
+} {
+ ALTER TABLE c1 DROP v
+} {
+ error in table c1 after drop column: no such column: v
+}
+do_atdc_error_test 2.7.2 {
+ CREATE TABLE c1(u, v, w AS (u+v) STORED);
+} {
+ ALTER TABLE c1 DROP u
+} {
+ error in table c1 after drop column: no such column: u
+}
+
+# EVIDENCE-OF: R-01515-49025 The column appears in a trigger or view.
+#
+do_atdc_error_test 2.8.1 {
+ CREATE TABLE log(l);
+ CREATE TABLE c1(u, v, w);
+ CREATE TRIGGER tr1 AFTER INSERT ON c1 BEGIN
+ INSERT INTO log VALUES(new.w);
+ END;
+} {
+ ALTER TABLE c1 DROP w
+} {
+ error in trigger tr1 after drop column: no such column: new.w
+}
+do_atdc_error_test 2.8.2 {
+ CREATE TABLE c1(u, v, w);
+ CREATE VIEW v1 AS SELECT u, v, w FROM c1;
+} {
+ ALTER TABLE c1 DROP w
+} {
+ error in view v1 after drop column: no such column: w
+}
+do_atdc_error_test 2.8.3 {
+ CREATE TABLE c1(u, v, w);
+ CREATE VIEW v1 AS SELECT * FROM c1 WHERE w IS NOT NULL;
+} {
+ ALTER TABLE c1 DROP w
+} {
+ error in view v1 after drop column: no such column: w
+}
+
+#-------------------------------------------------------------------------
+# Verify that a column that is part of a CHECK constraint may be dropped
+# if the CHECK constraint was specified as part of the column definition.
+#
+
+# EVIDENCE-OF: R-60924-11170 However, the column being deleted can be used in a
+# column CHECK constraint because the column CHECK constraint is dropped
+# together with the column itself.
+do_execsql_test 3.0 {
+ CREATE TABLE yyy(q, w, e CHECK (e > 0), r);
+ INSERT INTO yyy VALUES(1,1,1,1), (2,2,2,2);
+
+ CREATE TABLE zzz(q, w, e, r, CHECK (e > 0));
+ INSERT INTO zzz VALUES(1,1,1,1), (2,2,2,2);
+}
+do_catchsql_test 3.1.1 {
+ INSERT INTO yyy VALUES(0,0,0,0);
+} {1 {CHECK constraint failed: e > 0}}
+do_catchsql_test 3.1.2 {
+ INSERT INTO yyy VALUES(0,0,0,0);
+} {1 {CHECK constraint failed: e > 0}}
+
+do_execsql_test 3.2.1 {
+ ALTER TABLE yyy DROP e;
+}
+do_catchsql_test 3.2.2 {
+ ALTER TABLE zzz DROP e;
+} {1 {error in table zzz after drop column: no such column: e}}
+
+finish_test
diff --git a/test/alterlegacy.test b/test/alterlegacy.test
index 47d8bcfba9..dfa1716467 100644
--- a/test/alterlegacy.test
+++ b/test/alterlegacy.test
@@ -40,7 +40,7 @@ do_execsql_test 1.1 {
# slightly different - it rejects the change and rolls back the transaction.
do_catchsql_test 1.2 {
ALTER TABLE t1 RENAME TO t1new;
-} {1 {no such column: t1.a}}
+} {1 {error in table t1new after rename: no such column: t1.a}}
do_execsql_test 1.3 {
CREATE TABLE t3(c, d);
@@ -59,7 +59,7 @@ do_execsql_test 1.4 {
do_catchsql_test 1.3 {
ALTER TABLE t2 RENAME TO t2new;
-} {1 {no such column: t2.b}}
+} {1 {error in index t2expr after rename: no such column: t2.b}}
do_execsql_test 1.4 {
SELECT sql FROM sqlite_master
} {
diff --git a/test/altermalloc3.test b/test/altermalloc3.test
new file mode 100644
index 0000000000..d84f6bbc94
--- /dev/null
+++ b/test/altermalloc3.test
@@ -0,0 +1,40 @@
+# 2021 February 18
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+set testprefix altermalloc3
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, b)) WITHOUT ROWID;
+ INSERT INTO t1 VALUES(1, 2, 3, 4);
+}
+faultsim_save_and_close
+
+do_faultsim_test 1 -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql { ALTER TABLE t1 DROP COLUMN c }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+
+finish_test
+
diff --git a/test/altertab3.test b/test/altertab3.test
index 10d9ef32e3..1823b21edf 100644
--- a/test/altertab3.test
+++ b/test/altertab3.test
@@ -253,7 +253,7 @@ do_execsql_test 11.1 {
do_catchsql_test 11.2 {
ALTER TABLE t1 RENAME TO t1x;
-} {1 {error in trigger b: no such table: abc}}
+} {1 {error in trigger b: no such table: main.abc}}
do_execsql_test 11.3 {
DROP TRIGGER b;
diff --git a/test/backup2.test b/test/backup2.test
index b58c6a3cd3..1822e2dbf0 100644
--- a/test/backup2.test
+++ b/test/backup2.test
@@ -144,7 +144,7 @@ do_test backup2-9 {
if {$tcl_platform(platform)=="windows"} {
set msg {cannot open source database: unable to open database file}
} elseif {[string match *BSD $tcl_platform(os)]} {
- set msg {restore failed: file is not a database}
+ set msg {}
} else {
set msg {cannot open source database: disk I/O error}
}
@@ -152,7 +152,8 @@ do_test backup2-10 {
forcedelete bu3.db
file mkdir bu3.db
set rc [catch {db restore temp bu3.db} res]
- lappend rc $res
+ if {[string match *BSD $tcl_platform(os)]} { set res "" }
+ list $rc $res
} [list 1 $msg]
# Try to restore from something that is not a database file.
diff --git a/test/columncount.test b/test/columncount.test
new file mode 100644
index 0000000000..3ecc1f8d8a
--- /dev/null
+++ b/test/columncount.test
@@ -0,0 +1,57 @@
+# 2021 February 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing the sqlite3_column_count() API.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix columncount
+
+proc do_ccsql_test {tn sql res} {
+
+ uplevel [list do_test $tn [subst -nocommands {
+ set stmt [sqlite3_prepare_v2 db {$sql} -1 dummy]
+ set res [sqlite3_column_count [set stmt]]
+ while {[sqlite3_step [set stmt]]=="SQLITE_ROW"} {
+ for {set i 0} {[set i] < [sqlite3_data_count [set stmt]]} {incr i} {
+ lappend res [sqlite3_column_text [set stmt] [set i]]
+ }
+ }
+
+ set rc [sqlite3_finalize [set stmt]]
+ if {[set rc]!="SQLITE_OK"} {
+ error [sqlite3_errmsg db]
+ }
+
+ set res
+ }] [list {*}$res]]
+
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x, y, z);
+ INSERT INTO t1 VALUES('a', 'b', 'c');
+}
+
+do_ccsql_test 1.1 { SELECT * FROM t1 } {3 a b c}
+do_ccsql_test 1.2 { CREATE TABLE t2(a, b) } {0}
+
+do_ccsql_test 1.3 { ALTER TABLE t2 RENAME TO t3 } {0}
+do_ccsql_test 1.4 { ALTER TABLE t3 RENAME b TO ccc } {0}
+do_ccsql_test 1.5 { ALTER TABLE t3 ADD COLUMN d } {0}
+
+do_ccsql_test 1.6 { DROP TABLE t3 } {0}
+
+
+
+finish_test
+
diff --git a/test/corruptN.test b/test/corruptN.test
new file mode 100644
index 0000000000..4f7667ce1e
--- /dev/null
+++ b/test/corruptN.test
@@ -0,0 +1,106 @@
+# 2020-12-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix corruptN
+
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
+reset_db
+do_test 1.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 4096 pagesize 512 filename sql024239.txt.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 02 00 01 01 00 40 20 20 00 00 00 0c 00 00 00 07 .....@ ........
+| 32: 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 04 ................
+| 48: 00 00 00 00 89 00 00 04 00 10 00 01 0a 00 00 01 ................
+| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c ................
+| 96: 00 2e 2c 50 0d 00 00 00 06 01 06 00 01 da 01 b0 ..,P............
+| 112: 01 56 01 86 01 2a 01 06 00 00 00 00 00 00 00 00 .V...*..........
+| 256: 00 00 00 00 00 00 22 07 06 17 11 11 01 31 74 61 .............1ta
+| 272: 62 6c 65 74 34 74 34 07 43 52 45 41 54 45 20 54 blet4t4.CREATE T
+| 288: 41 42 4c 45 20 74 34 28 78 29 2a 06 06 17 13 11 ABLE t4(x)*.....
+| 304: 01 3f 69 6e 64 65 78 74 33 78 74 33 05 43 52 45 .?indext3xt3.CRE
+| 320: 41 54 45 20 49 4e 44 45 58 20 74 33 78 20 4f 4e ATE INDEX t3x ON
+| 336: 20 74 33 28 78 29 2e 04 06 17 15 11 01 45 69 6e t3(x).......Ein
+| 352: 64 65 78 74 32 63 64 74 32 05 43 52 45 41 54 45 dext2cdt2.CREATE
+| 368: 20 49 4e 44 45 58 20 74 32 63 64 20 4f 4e 20 74 INDEX t2cd ON t
+| 384: 32 28 63 2c 64 29 28 05 06 17 11 11 01 3d 74 61 2(c,d)(......=ta
+| 400: 62 6c 65 74 33 74 33 07 43 52 45 41 54 45 20 54 blet3t3.CREATE T
+| 416: 41 42 4c 45 20 74 33 28 63 2c 78 2c 65 2c 66 29 ABLE t3(c,x,e,f)
+| 432: 28 02 06 17 11 11 01 3d 74 61 62 6c 65 74 32 74 (......=tablet2t
+| 448: 32 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 2.CREATE TABLE t
+| 464: 32 28 63 2c 64 2c 65 2c 66 29 24 01 06 17 11 11 2(c,d,e,f)$.....
+| 480: 01 35 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 .5tablet1t1.CREA
+| 496: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 29 TE TABLE t1(a,b)
+| page 2 offset 512
+| 0: 0d 00 00 00 04 01 41 00 01 fa 01 f3 01 de 01 cf ......A.........
+| 160: 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 .. .............
+| 448: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0d ................
+| 464: 04 03 17 17 73 65 76 65 6e 65 69 67 68 74 13 03 ....seveneight..
+| 480: 03 07 07 40 14 00 00 00 00 00 00 40 18 00 00 00 ...@.......@....
+| 496: 00 00 00 05 02 03 01 01 03 04 04 01 03 09 01 02 ................
+| page 3 offset 1024
+| 0: 0d 00 00 00 08 01 54 00 01 f7 01 ec 01 c5 01 aa ......T.........
+| 16: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 112: 00 00 dd 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 336: 00 00 00 00 19 08 05 17 17 17 17 65 69 67 68 74 ...........eight
+| 352: 65 69 67 68 74 73 65 76 65 6e 73 65 76 65 6e 25 eightsevenseven%
+| 368: 07 05 07 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 432: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 ................
+| 480: 00 00 0f 04 17 17 01 65 69 67 68 74 65 69 67 68 .......eighteigh
+| 496: 74 08 15 04 07 07 01 40 18 00 00 00 00 00 00 40 t......@.......@
+| page 4 offset 1536
+| 0: 18 00 00 00 00 00 00 07 07 04 01 01 01 04 04 06 ................
+| 16: 07 04 01 01 01 02 02 05 0f 04 17 17 01 73 6d 76 .............smv
+| 32: 65 6e 65 69 67 68 74 04 15 04 07 07 01 40 14 00 eneight......@..
+| page 5 offset 2048
+| 0: 0a 00 00 00 08 01 96 00 01 fa 01 c4 01 f2 01 bc ................
+| 16: 01 dc 01 e1 01 96 01 cc 00 00 00 00 00 00 00 00 ................
+| 160: 00 00 00 00 00 00 32 00 00 00 00 00 00 00 00 00 ......2.........
+| 368: 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 400: 00 00 00 00 00 00 0f 04 17 17 01 85 69 67 68 74 ............ight
+| 416: 65 69 67 68 74 08 15 04 07 07 01 40 18 00 00 00 eight......@....
+| 432: 00 00 00 40 18 00 00 00 00 00 00 07 07 04 01 01 ...@............
+| 448: 01 04 04 06 07 04 01 01 01 02 02 05 0f 04 17 17 ................
+| 464: 01 73 6d 76 65 6e 65 69 67 68 74 04 15 04 07 07 .smveneight.....
+| 480: 01 40 14 00 00 00 00 00 00 40 18 00 00 00 00 00 .@.......@......
+| 496: 00 03 07 04 01 01 01 03 04 02 05 04 03 01 09 02 ................
+| page 6 offset 2560
+| 0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 ................
+| 304: 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 00 ...&............
+| page 7 offset 3072
+| 0: 0d 00 00 00 08 01 c2 00 01 fb 01 f6 01 f1 01 ec ................
+| 16: 01 e0 01 d4 01 cb 01 c2 00 00 00 00 00 00 00 00 ................
+| 128: 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 04 ............. ..
+| 384: 00 00 00 00 00 00 00 00 00 07 08 02 17 65 69 fc .............ei.
+| 400: 68 74 07 07 02 17 65 69 67 68 74 0a fb fd f8 bf ht....eight.....
+| 416: e7 ff ff ff 00 00 00 0a 05 02 07 40 18 00 00 00 ...........@....
+| 432: 00 00 00 03 04 02 01 04 03 03 02 01 04 03 02 01 ................
+| 448: ff ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00 ................
+| end sql024239.txt.db
+}]} {}
+
+do_catchsql_test 1.1 {
+ VACUUM;
+} {1 {database disk image is malformed}}
+
+
+finish_test
diff --git a/test/crash5.test b/test/crash5.test
index b80b15137f..fc078b3504 100644
--- a/test/crash5.test
+++ b/test/crash5.test
@@ -21,7 +21,7 @@ source $testdir/tester.tcl
# Only run these tests if memory debugging is turned on.
#
ifcapable !crashtest||!memorymanage {
- puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ puts "Skipping crash5 tests: not compiled with -DSQLITE_ENABLE_MEMORY_MANAGEMENT..."
finish_test
return
}
@@ -29,7 +29,7 @@ ifcapable !crashtest||!memorymanage {
db close
for {set ii 0} {$ii < 10} {incr ii} {
- for {set jj 50} {$jj < 100} {incr jj} {
+ for {set jj 1} {$jj < 100} {incr jj} {
# Set up the database so that it is an auto-vacuum database
# containing a single table (root page 3) with a single row.
@@ -47,6 +47,20 @@ for {set ii 0} {$ii < 10} {incr ii} {
do_test crash5-$ii.$jj.1 {
crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \
[list set iFail $jj] {
+ proc get_pwd {} {
+ if {$::tcl_platform(platform) eq "windows"} {
+ if {[info exists ::env(ComSpec)]} {
+ set comSpec $::env(ComSpec)
+ } else {
+ # NOTE: Hard-code the typical default value.
+ set comSpec {C:\Windows\system32\cmd.exe}
+ }
+ return [string map [list \\ /] \
+ [string trim [exec -- $comSpec /c echo %CD%]]]
+ } else {
+ return [pwd]
+ }
+ }
sqlite3_crashparams 0 [file join [get_pwd] test.db-journal]
# Begin a transaction and evaluate a "CREATE INDEX" statement
@@ -61,36 +75,39 @@ for {set ii 0} {$ii < 10} {incr ii} {
#
db eval BEGIN
sqlite3_memdebug_fail $iFail -repeat 0
- catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg
- # puts "$n $msg ac=[sqlite3_get_autocommit db]"
+ set rc [catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg]
+# puts "$msg ac=[sqlite3_get_autocommit db] iFail=$iFail"
+# puts "fail=[sqlite3_memdebug_fail -1]"
- # If the transaction is still active (it may not be if the malloc()
- # failure occurred in the OS layer), write to the database. Make sure
- # page 4 is among those written.
- #
- if {![sqlite3_get_autocommit db]} {
- db eval {
- DELETE FROM t1; -- This will put page 4 on the free list.
- INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
- INSERT INTO t1 SELECT * FROM t1; -- 2
- INSERT INTO t1 SELECT * FROM t1; -- 4
- INSERT INTO t1 SELECT * FROM t1; -- 8
- INSERT INTO t1 SELECT * FROM t1; -- 16
- INSERT INTO t1 SELECT * FROM t1; -- 32
- INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48
+ if {$rc} {
+ # If the transaction is still active (it may not be if the malloc()
+ # failure occurred in the OS layer), write to the database. Make sure
+ # page 4 is among those written.
+ #
+ if {![sqlite3_get_autocommit db]} {
+ db eval {
+ DELETE FROM t1; -- This will put page 4 on the free list.
+ INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
+ INSERT INTO t1 SELECT * FROM t1; -- 2
+ INSERT INTO t1 SELECT * FROM t1; -- 4
+ INSERT INTO t1 SELECT * FROM t1; -- 8
+ INSERT INTO t1 SELECT * FROM t1; -- 16
+ INSERT INTO t1 SELECT * FROM t1; -- 32
+ INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48
+ }
}
+
+ # If the right malloc() failed during the 'CREATE INDEX' above and
+ # the transaction was not rolled back, then the sqlite cache now
+ # has a dirty page 4 that it incorrectly believes is already safely
+ # in the synced part of the journal file. When
+ # sqlite3_release_memory() is called sqlite tries to free memory
+ # by writing page 4 out to the db file. If it crashes later on,
+ # before syncing the journal... Corruption!
+ #
+ sqlite3_crashparams 1 [file join [get_pwd] test.db-journal]
+ sqlite3_release_memory 8092
}
-
- # If the right malloc() failed during the 'CREATE INDEX' above and
- # the transaction was not rolled back, then the sqlite cache now
- # has a dirty page 4 that it incorrectly believes is already safely
- # in the synced part of the journal file. When
- # sqlite3_release_memory() is called sqlite tries to free memory
- # by writing page 4 out to the db file. If it crashes later on,
- # before syncing the journal... Corruption!
- #
- sqlite3_crashparams 1 [file join [get_pwd] test.db-journal]
- sqlite3_release_memory 8092
}]] {}
expr 1
} {1}
diff --git a/test/exists2.test b/test/exists2.test
new file mode 100644
index 0000000000..c4fc9dc373
--- /dev/null
+++ b/test/exists2.test
@@ -0,0 +1,188 @@
+# 2021 January 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing cases where EXISTS expressions are
+# transformed to IN() expressions by where.c
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix exists2
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(4, 'four');
+ INSERT INTO t1 VALUES(5, 'five');
+ INSERT INTO t1 VALUES(6, 'six');
+ INSERT INTO t1 VALUES(7, 'seven');
+
+ CREATE TABLE t2(c INTEGER, d INTEGER);
+ INSERT INTO t2 VALUES(1, 1);
+ INSERT INTO t2 VALUES(3, 2);
+ INSERT INTO t2 VALUES(5, 3);
+ INSERT INTO t2 VALUES(7, 4);
+}
+
+proc do_execsql_eqp_test {tn sql eqp res} {
+ uplevel [list do_eqp_test $tn.1 $sql [string trim $eqp]]
+ uplevel [list do_execsql_test $tn.2 $sql $res]
+}
+
+do_execsql_eqp_test 1.1 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t1.a=t2.c);
+} {
+ USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five 7 seven
+}
+
+do_execsql_eqp_test 1.2 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c=t1.a);
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five 7 seven
+}
+
+do_execsql_eqp_test 1.3 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c+1=t1.a);
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 2 two 4 four 6 six
+}
+
+do_execsql_eqp_test 1.4 {
+ SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c+1=t1.a+1);
+} {
+ SCAN TABLE t1
+} {
+ 1 one 3 three 5 five 7 seven
+}
+
+do_execsql_eqp_test 1.5 {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE t1.a=t2.c AND d IN (1, 2, 3)
+ );
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five
+}
+
+do_execsql_eqp_test 1.6 {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE d IN (1, 2, 3)AND t1.a=t2.c
+ );
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five
+}
+
+do_execsql_eqp_test 1.7 {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE d IN (1, 2, 3)AND t1.a=t2.c
+ );
+} {
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY
+} {
+ 1 one 3 three 5 five
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t3(a TEXT PRIMARY KEY, b TEXT, x INT) WITHOUT ROWID;
+ CREATE TABLE t4(c TEXT COLLATE nocase, y INT);
+
+ INSERT INTO t3 VALUES('one', 'i', 1);
+ INSERT INTO t3 VALUES('two', 'ii', 2);
+ INSERT INTO t3 VALUES('three', 'iii', 3);
+ INSERT INTO t3 VALUES('four', 'iv', 4);
+ INSERT INTO t3 VALUES('five', 'v', 5);
+
+ INSERT INTO t4 VALUES('FIVE',5), ('four',4), ('TWO',2), ('one',1);
+}
+
+do_execsql_test 2.1 { SELECT a FROM t3, t4 WHERE a=c } {four one}
+do_execsql_test 2.2 { SELECT a FROM t3, t4 WHERE c=a } {five four one two}
+
+do_execsql_eqp_test 2.3 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE a=c)
+} {
+ SEARCH TABLE t3 USING PRIMARY KEY
+} {
+ four one
+}
+
+do_execsql_eqp_test 2.4 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE c=a)
+} {
+ SCAN TABLE t3
+} {
+ five four one two
+}
+
+do_execsql_test 2.5 {
+ CREATE INDEX t3anc ON t3(a COLLATE nocase, x);
+}
+
+do_execsql_eqp_test 2.6 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE c=a)
+} {
+ SEARCH TABLE t3 USING COVERING INDEX t3anc
+} {
+ five four one two
+}
+do_execsql_test 2.6a {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE (c,y)=(a,x))
+} {five four one two}
+
+do_execsql_eqp_test 2.7 {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE a=c)
+} {
+ SEARCH TABLE t3 USING PRIMARY KEY
+} {
+ four one
+}
+do_execsql_test 2.7a {
+ SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE (a,x)=(c,y))
+} {
+ four one
+}
+
+# EXISTS clauses using vector expressions in the WHERE clause.
+#
+reset_db
+do_execsql_test 3.0 {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1(a,b) VALUES(1,111),(2,222),(8,888);
+ CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
+ INSERT INTO t2(x,y) VALUES(2,222),(3,333),(7,333);
+ SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (x,y)=(a,b));
+} {222}
+do_execsql_test 3.1 {
+ SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (a,b)=(x,y));
+} {222}
+do_execsql_test 3.2 {
+ SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (x,b)=(a,y));
+} {222}
+
+
+
+
+
+finish_test
diff --git a/test/existsfault.test b/test/existsfault.test
new file mode 100644
index 0000000000..24e44a7b58
--- /dev/null
+++ b/test/existsfault.test
@@ -0,0 +1,52 @@
+# 2021 January 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing cases where EXISTS expressions are
+# transformed to IN() expressions by where.c
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix existsfault
+
+do_execsql_test 1 {
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(4, 'four');
+ INSERT INTO t1 VALUES(5, 'five');
+ INSERT INTO t1 VALUES(6, 'six');
+ INSERT INTO t1 VALUES(7, 'seven');
+
+ CREATE TABLE t2(c INTEGER, d INTEGER);
+ INSERT INTO t2 VALUES(1, 1);
+ INSERT INTO t2 VALUES(3, 2);
+ INSERT INTO t2 VALUES(5, 3);
+ INSERT INTO t2 VALUES(7, 4);
+}
+faultsim_save_and_close
+
+do_faultsim_test 1 -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ SELECT t1.* FROM t1 WHERE EXISTS(
+ SELECT * FROM t2 WHERE t2.c=t1.a AND d IN (1, 2, 3)
+ )
+ }
+} -test {
+ faultsim_test_result {0 {1 one 3 three 5 five}}
+}
+
+
+finish_test
+
diff --git a/test/fts3corrupt4.test b/test/fts3corrupt4.test
index bddcc9f5de..851119a2fe 100644
--- a/test/fts3corrupt4.test
+++ b/test/fts3corrupt4.test
@@ -6298,4 +6298,92 @@ do_catchsql_test 47.3 {
SELECT matchinfo(t1) FROM t1 WHERE t1 MATCH '"json1 enable"';
} {1 {database disk image is malformed}}
+#-------------------------------------------------------------------------
+#
+reset_db
+do_test 48.0 {
+ sqlite3 db {}
+ db deserialize [decode_hexdb {
+.open --hexdb
+| size 20480 pagesize 4096 filename sql038051.txt.db
+| page 1 offset 0
+| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
+| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 00 .....@ ........
+| 96: 00 00 00 00 0d 0e fc 00 05 0e 13 00 0f ca 0f 6c ...............l
+| 112: 0f 04 0e 13 0e c9 00 00 00 00 00 00 00 00 00 00 ................
+| 3600: 00 00 00 81 33 04 07 17 1f 1f 01 82 35 74 61 62 ....3.......5tab
+| 3616: 6c 65 78 31 5f 73 65 67 64 69 72 78 31 5f 73 65 lex1_segdirx1_se
+| 3632: 67 64 69 72 04 43 52 45 41 54 45 20 54 41 42 4c gdir.CREATE TABL
+| 3648: 45 20 27 78 31 5f 73 65 67 64 69 72 27 28 6c 65 E 'x1_segdir'(le
+| 3664: 76 65 6c 20 49 4e 54 45 47 45 52 2c 69 64 78 20 vel INTEGER,idx
+| 3680: 49 4e 54 45 47 45 52 2c 73 74 61 72 74 5f 62 6c INTEGER,start_bl
+| 3696: 6f 63 6b 20 49 4e 54 45 47 45 52 2c 6c 65 61 76 ock INTEGER,leav
+| 3712: 65 73 5f 65 6e 64 5f 62 6c 6f 63 6b 20 49 4e 54 es_end_block INT
+| 3728: 45 47 45 52 2c 65 6e 64 5f 62 6c 6f 63 6b 20 49 EGER,end_block I
+| 3744: 4e 54 45 47 45 52 2c 72 6f 6f 74 20 42 4c 4f 42 NTEGER,root BLOB
+| 3760: 2c 50 52 49 4d 41 52 59 20 4b 45 59 28 6c 65 76 ,PRIMARY KEY(lev
+| 3776: 65 6c 2c 20 69 64 78 29 29 31 05 06 17 45 1f 01 el, idx))1...E..
+| 3792: 00 69 6e 64 65 78 73 71 6c 69 74 65 5f 61 75 74 .indexsqlite_aut
+| 3808: 6f 69 6e 64 65 78 5f 78 31 5f 73 65 67 64 69 72 oindex_x1_segdir
+| 3824: 5f 31 78 31 5f 73 65 67 64 69 72 05 00 00 00 08 _1x1_segdir.....
+| 3840: 00 00 00 00 66 03 07 17 23 23 01 81 13 74 61 62 ....f...##...tab
+| 3856: 6c 65 78 31 5f 73 65 67 6d 65 6e 74 73 78 31 5f lex1_segmentsx1_
+| 3872: 73 65 67 6d 65 6e 74 73 03 43 52 45 41 54 45 20 segments.CREATE
+| 3888: 54 41 42 4c 45 20 27 78 31 5f 73 65 67 6d 65 6e TABLE 'x1_segmen
+| 3904: 74 73 27 28 62 6c 6f 63 6b 69 64 20 49 4e 54 45 ts'(blockid INTE
+| 3920: 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c GER PRIMARY KEY,
+| 3936: 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 5c 02 07 17 block BLOB)....
+| 3952: 21 21 01 81 03 74 61 62 6c 65 78 31 5f 63 6f 6e !!...tablex1_con
+| 3968: 74 65 6e 74 78 31 5f 63 6f 6e 74 65 6e 74 02 43 tentx1_content.C
+| 3984: 52 45 41 54 45 20 54 41 42 4c 45 20 27 78 31 5f REATE TABLE 'x1_
+| 4000: 63 6f 6e 74 65 6e 74 27 28 64 6f 63 69 64 20 49 content'(docid I
+| 4016: 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b NTEGER PRIMARY K
+| 4032: 45 59 2c 20 27 63 30 78 27 29 34 01 06 17 11 11 EY, 'c0x')4.....
+| 4048: 08 57 74 61 62 6c 65 78 31 78 31 43 52 45 41 54 .Wtablex1x1CREAT
+| 4064: 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 E VIRTUAL TABLE
+| 4080: 78 31 20 55 53 49 4e 47 20 66 74 73 33 28 78 29 x1 USING fts3(x)
+| page 2 offset 4096
+| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+| 3920: 00 00 00 2e 04 03 00 63 62 72 61 69 6e 73 74 65 .......cbrainste
+| 3936: 6d 20 62 72 61 69 6e 73 74 65 6d 73 20 62 72 61 m brainstems bra
+| 3952: 69 6e 73 74 6f 72 6d 20 62 72 61 69 6e 73 74 6f instorm brainsto
+| 3968: 72 6d 73 2b 03 03 00 5d 62 72 61 69 6e 20 62 72 rms+...]brain br
+| 3984: 61 69 6e 63 68 69 6c 64 20 62 72 61 69 6e 65 64 ainchild brained
+| 4000: 20 62 72 61 69 6e 69 6e 67 20 62 72 61 69 6e 73 braining brains
+| 4016: 26 02 03 00 53 62 72 61 67 73 20 62 72 61 69 64 &...Sbrags braid
+| 4032: 20 62 72 61 69 64 65 64 20 62 72 61 69 64 69 6e braided braidin
+| 4048: 67 20 62 72 61 69 64 73 26 01 03 00 53 62 72 61 g braids&...Sbra
+| 4064: 65 73 20 62 72 61 67 20 62 72 61 67 67 65 64 20 es brag bragged
+| 4080: 62 72 61 c3 67 65 72 20 62 72 61 67 67 69 6e 67 bra.ger bragging
+| page 3 offset 8192
+| 0: 0d 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................
+| page 4 offset 12288
+| 0: 0d 00 00 00 04 0f 20 00 0f c8 0f 90 0f 54 0f 20 ...... ......T.
+| 3872: 32 04 07 08 01 08 08 15 58 03 30 20 33 38 00 09 2.......X.0 38..
+| 3888: 62 72 61 69 6e 73 74 65 6d 03 04 02 00 09 01 73 brainstem......s
+| 3904: 03 04 03 00 07 03 6f 72 6d 03 04 04 00 0a 01 73 ......orm......s
+| 3920: 03 04 05 00 3a 03 07 08 01 08 08 15 68 02 30 20 ....:.......h.0
+| 3936: 34 36 00 05 62 72 61 69 6e 03 03 02 00 05 05 63 46..brain......c
+| 3952: 68 69 6c 64 03 03 03 00 05 02 65 64 03 03 04 00 hild......ed....
+| 3968: 05 03 69 6e 67 03 03 05 00 05 01 73 03 03 06 00 ..ing......s....
+| 3984: 36 02 07 08 09 08 08 15 62 30 20 34 33 00 05 62 6.......b0 43..b
+| 4000: 72 61 67 73 03 02 02 00 03 02 69 64 03 02 03 00 rags......id....
+| 4016: 05 02 65 64 03 02 04 00 05 03 69 6e 67 03 02 05 ..ed......ing...
+| 4032: 00 05 01 73 03 02 06 00 36 01 07 08 08 08 08 15 ...s....6.......
+| 4048: 62 30 20 34 33 00 05 62 72 61 65 73 03 01 02 00 b0 43..braes....
+| 4064: 03 01 68 03 01 03 00 04 03 67 65 74 03 01 04 00 ..h......get....
+| 4080: 06 01 72 03 01 05 00 05 03 69 6e 67 03 01 06 00 ..r......ing....
+| page 5 offset 16384
+| 0: 0a 00 00 00 04 0f e7 00 0f fb 0f f5 0f ee 0f e7 ................
+| 4064: 00 00 00 00 00 00 00 06 04 08 01 01 03 04 06 04 ................
+| 4080: 08 01 01 02 03 05 04 08 09 01 02 04 04 08 08 09 ................
+| end sql038051.txt.db
+}]} {}
+
+do_catchsql_test 48.1 {
+ INSERT INTO x1(x1) VALUES('nodesize=24'),('merge=3,4');
+ INSERT INTO x1(x1) VALUES( 'merge=3,4' ),('merge=3,4');
+} {1 {database disk image is malformed}}
+
+
finish_test
diff --git a/test/fts4rename.test b/test/fts4rename.test
index c647d6928e..5571ea7b15 100644
--- a/test/fts4rename.test
+++ b/test/fts4rename.test
@@ -29,7 +29,7 @@ do_execsql_test 1.0 {
do_catchsql_test 1.1 {
ALTER TABLE t1_content RENAME c0a TO docid;
-} {1 {duplicate column name: docid}}
+} {1 {error in table t1_content after rename: duplicate column name: docid}}
do_catchsql_test 1.2 {
UPDATE t1 SET Col0 = 1 ;
diff --git a/test/fts4unicode.test b/test/fts4unicode.test
index f4d1a33486..facf2bf9c4 100644
--- a/test/fts4unicode.test
+++ b/test/fts4unicode.test
@@ -567,4 +567,22 @@ do_execsql_test 11.1 {
berlin@street sydney.road
}
+# Test for embedded nul characters in fts4 unicode index.
+#
+do_execsql_test 12.0 {
+ CREATE VIRTUAL TABLE t12 USING fts4(tokenize=unicode61);
+ INSERT INTO t12 VALUES('abc' || char(0) || 'def');
+ SELECT hex(CAST(content AS blob)) FROM t12;
+} {61626300646566}
+do_execsql_test 12.1 {
+ INSERT INTO t12(t12) VALUES('integrity-check');
+} {}
+do_execsql_test 12.2 {
+ CREATE VIRTUAL TABLE t12aux USING fts4aux(t12);
+ SELECT * FROM t12aux;
+} {abc * 1 1 abc 0 1 1}
+do_execsql_test 12.3 {
+ SELECT hex(CAST(content AS blob)) FROM t12 WHERE t12 MATCH 'abc'
+} {61626300646566}
+
finish_test
diff --git a/test/func7.test b/test/func7.test
index 536f7eb414..c8ae2931e1 100644
--- a/test/func7.test
+++ b/test/func7.test
@@ -202,11 +202,11 @@ do_execsql_test func7-mysql-210 {
#} {0.6931472 NULL}
# log() means natural logarithm in MySQL
do_execsql_test func7-mysql-230 {
- SELECT log(2,65536), log(10,100), quote(log(1,100));
-} {16.0 2.0 Inf}
+ SELECT log(2,65536), log(10,100), quote(log(1,100)), quote(log(0,100));
+} {16.0 2.0 NULL NULL}
do_execsql_test func7-mysql-240 {
- SELECT log2(65536), quote(log2(-100));
-} {16.0 NULL}
+ SELECT log2(65536), quote(log2(-100)), quote(log2(0));
+} {16.0 NULL NULL}
do_execsql_test func7-mysql-250 {
SELECT round(log10(2),7), log10(100), quote(log10(-100));
} {0.30103 2.0 NULL}
diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c
index cf49956b47..c6ad4cd5c3 100644
--- a/test/fuzzcheck.c
+++ b/test/fuzzcheck.c
@@ -455,7 +455,12 @@ static void blobListFree(Blob *p){
}
}
-/* Return the current wall-clock time */
+/* Return the current wall-clock time
+**
+** The number of milliseconds since the julian epoch.
+** 1907-01-01 00:00:00 -> 210866716800000
+** 2021-01-01 00:00:00 -> 212476176000000
+*/
static sqlite3_int64 timeOfDay(void){
static sqlite3_vfs *clockVfs = 0;
sqlite3_int64 t;
@@ -713,6 +718,7 @@ static int progress_handler(void *pClientData) {
sqlite3_int64 iNow = timeOfDay();
int rc = iNow>=p->iCutoffTime;
sqlite3_int64 iDiff = iNow - p->iLastCb;
+ /* printf("time-remaining: %lld\n", p->iCutoffTime - iNow); */
if( iDiff > p->mxInterval ) p->mxInterval = iDiff;
p->nCb++;
if( rc==0 && p->mxCb>0 && p->mxCb<=p->nCb ) rc = 1;
@@ -835,7 +841,7 @@ static int runDbSql(sqlite3 *db, const char *zSql){
}
/* Invoke this routine to run a single test case */
-int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte){
+int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte, int iTimeout){
int rc; /* SQLite API return value */
int iSql; /* Index in aData[] of start of SQL */
unsigned char *aDb = 0; /* Decoded database content */
@@ -882,7 +888,7 @@ int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte){
** elapsed since the start of the test.
*/
cx.iLastCb = timeOfDay();
- cx.iCutoffTime = cx.iLastCb + giTimeout; /* Now + giTimeout seconds */
+ cx.iCutoffTime = cx.iLastCb + (iTimeout=argc-1 ) fatalError("missing arguments on %s", argv[i]);
onlySqlid = integerValue(argv[++i]);
@@ -1894,6 +1905,7 @@ int main(int argc, char **argv){
*/
if( !verboseFlag && !quietFlag && !bSpinner ) printf("%s:", zDbName);
for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){
+ tmStart = timeOfDay();
if( isDbSql(pSql->a, pSql->sz) ){
sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d",pSql->id);
if( bSpinner ){
@@ -1917,9 +1929,13 @@ int main(int argc, char **argv){
if( nSkip>0 ){
nSkip--;
}else{
- runCombinedDbSqlInput(pSql->a, pSql->sz);
+ runCombinedDbSqlInput(pSql->a, pSql->sz, iTimeout);
}
nTest++;
+ if( bTimer ){
+ sqlite3_int64 tmEnd = timeOfDay();
+ printf("%lld %s\n", tmEnd - tmStart, g.zTestName);
+ }
g.zTestName[0] = 0;
disableOom();
continue;
@@ -1972,7 +1988,7 @@ int main(int argc, char **argv){
sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 100000000);
sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 50);
if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags);
- setAlarm(iTimeout);
+ setAlarm((iTimeout+999)/1000);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( sqlFuzz || vdbeLimitFlag ){
sqlite3_progress_handler(db, 100000, progressHandler,
@@ -1998,6 +2014,10 @@ int main(int argc, char **argv){
}
reformatVfs();
nTest++;
+ if( bTimer ){
+ sqlite3_int64 tmEnd = timeOfDay();
+ printf("%lld %s\n", tmEnd - tmStart, g.zTestName);
+ }
g.zTestName[0] = 0;
/* Simulate an error if the TEST_FAILURE environment variable is "5".
diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db
index 39191fc8b1..a5e9084192 100644
Binary files a/test/fuzzdata8.db and b/test/fuzzdata8.db differ
diff --git a/test/fuzzerfault.test b/test/fuzzerfault.test
index 6449612a66..e281cb592e 100644
--- a/test/fuzzerfault.test
+++ b/test/fuzzerfault.test
@@ -88,5 +88,24 @@ do_faultsim_test 3 -prep {
faultsim_test_result {0 2} {1 {vtable constructor failed: x1}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 4.0 {
+ CREATE TABLE t1_a(a INTEFDR PRIMARY KEY, b TEXT);
+ CREATE TABLE t3_a(k FnTEGER PRIMARY KEY, v TEXT);
+ CREATE TABLE t3_b(k INTEÀ5R PRIMARY KEY, v TEXT);
+ CREATE VIEW t3 AS SELECT * FROM t3_a UNION ALL SELECT * FROM t3_b;
+}
+faultsim_save_and_close
+
+do_faultsim_test 4 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ SELECT 1 FROM t1_a LEFT JOIN t3 ON ((1+1) AND k=1)
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
finish_test
diff --git a/test/having.test b/test/having.test
index a3882552d3..3bfa8120a1 100644
--- a/test/having.test
+++ b/test/having.test
@@ -65,8 +65,8 @@ foreach {tn sql1 sql2} {
3 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING a=2"
"SELECT a, sum(b) FROM t1 WHERE a=2 GROUP BY a COLLATE binary"
- 5 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING 0"
- "SELECT a, sum(b) FROM t1 WHERE 0 GROUP BY a COLLATE binary"
+ 5 "SELECT a, sum(b) FROM t1 GROUP BY a COLLATE binary HAVING 1"
+ "SELECT a, sum(b) FROM t1 WHERE 1 GROUP BY a COLLATE binary"
6 "SELECT count(*) FROM t1,t2 WHERE a=c GROUP BY b, d HAVING b=d"
"SELECT count(*) FROM t1,t2 WHERE a=c AND b=d GROUP BY b, d"
@@ -154,5 +154,38 @@ do_execsql_test 4.3 {
SELECT a, sum(b) FROM t3 WHERE nondeter(a) GROUP BY a
} {1 4 2 2}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(x, y);
+ INSERT INTO t1 VALUES('a', 'b');
+}
+
+# The WHERE clause (a=2), uses an aggregate column from the outer query.
+# If the HAVING term (0) is moved into the WHERE clause in this case,
+# SQLite would at one point optimize (a=2 AND 0) to simply (0). Which
+# is logically correct, but happened to cause problems in aggregate
+# processing for the outer query. This test case verifies that those
+# problems are no longer present.
+do_execsql_test 5.1 {
+ SELECT min(b), (
+ SELECT x FROM t2 WHERE a=2 GROUP BY y HAVING 0
+ ) FROM t1;
+} {b {}}
+
+# From chromium
+# https://bugs.chromium.org/p/chromium/issues/detail?id=1161869
+#
+do_execsql_test 5.2 {
+ SELECT EXISTS (
+ SELECT * FROM (
+ SELECT * FROM (
+ SELECT 1
+ ) WHERE Col0 = 1 GROUP BY 1
+ ) WHERE 0
+ )
+ FROM (SELECT 1 Col0) GROUP BY 1
+} {0}
finish_test
diff --git a/test/hook.test b/test/hook.test
index d137e9056f..0b72b2dcff 100644
--- a/test/hook.test
+++ b/test/hook.test
@@ -956,5 +956,63 @@ ifcapable analyze {
}]
}
+#-------------------------------------------------------------------------
+# Test that the pre-update hook is fired for INSERT statements that use
+# the xfer optimization on without rowid tables.
+#
+reset_db
+do_execsql_test 12.1 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
+
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t2 VALUES(5, 6);
+ INSERT INTO t2 VALUES(7, 8);
+
+ CREATE TABLE t3 (a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
+}
+
+db preupdate hook preupdate_cb
+db update_hook update_cb
+
+proc preupdate_cb {args} { lappend ::res "preupdate" $args }
+proc update_cb {args} { lappend ::res "update" $args }
+
+set ::res [list]
+do_test 12.2 {
+ execsql VACUUM
+ set ::res
+} {}
+
+do_test 12.3 {
+ set ::res [list]
+ execsql { INSERT INTO t3 SELECT a, b FROM t2 }
+ set ::res
+} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}}
+
+do_test 12.4 {
+ execsql { DELETE FROM t3 }
+ set ::res [list]
+ execsql { INSERT INTO t3 SELECT * FROM t2 }
+ set ::res
+} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}}
+
+do_execsql_test 12.5 {
+ CREATE TABLE t4(a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID;
+ INSERT INTO t4 VALUES('abc', 1);
+ INSERT INTO t4 VALUES('DEF', 2);
+}
+
+set ::res [list]
+do_test 12.6 {
+ execsql VACUUM
+ set ::res
+} {}
+
+do_catchsql_test 12.6 {
+ INSERT INTO t4 VALUES('def', 3);
+} {1 {UNIQUE constraint failed: t4.a}}
finish_test
+
diff --git a/test/in4.test b/test/in4.test
index 37eafdebec..bbb32d8a7a 100644
--- a/test/in4.test
+++ b/test/in4.test
@@ -365,4 +365,32 @@ ifcapable rtree {
} {{} 1 {} {} {}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 8.0 {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
+ CREATE UNIQUE INDEX t1y ON t1(y);
+ INSERT INTO t1 VALUES(111, 'AAA'),(222, 'BBB'),(333, 'CCC');
+ CREATE TABLE t2(z);
+ INSERT INTO t2 VALUES('BBB'),('AAA');
+ ANALYZE sqlite_schema;
+ INSERT INTO sqlite_stat1 VALUES('t1', 't1y','100 1');
+}
+
+db close
+sqlite3 db test.db
+
+do_execsql_test 8.1 {
+ SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y;
+} {222 111}
+
+do_execsql_test 8.2 {
+ SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y AND +t1.x IN (111, 222);
+} {222 111}
+
+do_execsql_test 8.3 {
+ SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y AND t1.x IN (111, 222);
+} {222 111}
+
+
finish_test
diff --git a/test/like.test b/test/like.test
index c29ebb2676..8ea7cff782 100644
--- a/test/like.test
+++ b/test/like.test
@@ -1131,4 +1131,11 @@ do_execsql_test 17.1 {
SELECT id FROM t1 WHERE x LIKE 'abc__' ESCAPE '_';
} {2}
+# 2021-02-15 ticket c0aeea67d58ae0fd
+#
+do_execsql_test 17.1 {
+ SELECT 'x' LIKE '%' ESCAPE '_';
+} {1}
+
+
finish_test
diff --git a/test/notnull2.test b/test/notnull2.test
new file mode 100644
index 0000000000..9ae190fe19
--- /dev/null
+++ b/test/notnull2.test
@@ -0,0 +1,101 @@
+# 2021 February 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing optimizations associated with "IS NULL"
+# and "IS NOT NULL" operators on columns with NOT NULL constraints.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix notnull2
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d NOT NULL);
+
+ WITH x(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<1000
+ )
+ INSERT INTO t1 SELECT i, i FROM x;
+ INSERT INTO t2 SELECT * FROM t1;
+}
+
+proc do_vmstep_test {tn sql nstep {res {}}} {
+ uplevel [list do_execsql_test $tn.0 $sql $res]
+
+ set vmstep [db status vmstep]
+ if {[string range $nstep 0 0]=="+"} {
+ set body "if {$vmstep<$nstep} {
+ error \"got $vmstep, expected more than [string range $nstep 1 end]\"
+ }"
+ } else {
+ set body "if {$vmstep>$nstep} {
+ error \"got $vmstep, expected less than $nstep\"
+ }"
+ }
+
+ # set name "$tn.vmstep=$vmstep,expect=$nstep"
+ set name "$tn.1"
+ uplevel [list do_test $name $body {}]
+}
+
+do_vmstep_test 1.1.1 {
+ SELECT * FROM t1 LEFT JOIN t2 WHERE a=c AND d IS NULL;
+} 100 {}
+do_vmstep_test 1.1.2 {
+ SELECT * FROM t1 LEFT JOIN t2 WHERE a=c AND c IS NULL;
+} +1000 {}
+
+do_vmstep_test 1.2.1 {
+ SELECT * FROM ( SELECT * FROM t2 ) WHERE d IS NULL
+} 100 {}
+do_vmstep_test 1.2.2 {
+ SELECT * FROM ( SELECT * FROM t2 ) WHERE c IS NULL
+} +1000 {}
+
+do_vmstep_test 1.3.1 {
+ SELECT * FROM t2 WHERE d IS NULL
+} 100 {}
+do_vmstep_test 1.3.2 {
+ SELECT * FROM t2 WHERE c IS NULL
+} +1000 {}
+
+do_vmstep_test 1.4.1 {
+ SELECT (d IS NOT NULL) FROM t2 WHERE 0==( d IS NOT NULL )
+} 100 {}
+do_vmstep_test 1.4.2 {
+ SELECT * FROM t2 WHERE 0==( c IS NOT NULL )
+} +1000 {}
+
+do_vmstep_test 1.5.1 {
+ SELECT count(*) FROM t2 WHERE EXISTS(
+ SELECT t2.d IS NULL FROM t1 WHERE t1.a=450
+ )
+} 10000 {1000}
+do_vmstep_test 1.5.2 {
+ SELECT count(*) FROM t2 WHERE EXISTS(
+ SELECT t2.c IS NULL FROM t1 WHERE t1.a=450
+ )
+} +100000 {1000}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE T1(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE T3(k, v);
+}
+
+do_execsql_test 2.1 {
+ SELECT * FROM (SELECT a, b FROM t1) LEFT JOIN t3 ON a IS NULL;
+}
+
+finish_test
+
diff --git a/test/permutations.test b/test/permutations.test
index 4ea6cd2806..786e787b57 100644
--- a/test/permutations.test
+++ b/test/permutations.test
@@ -1062,7 +1062,8 @@ test_suite "no_optimization" -description {
where6.test where7.test where8.test where9.test \
whereA.test whereB.test wherelimit.test \
select1.test select2.test select3.test select4.test select5.test \
- select7.test select8.test selectA.test selectC.test
+ select7.test select8.test selectA.test selectC.test \
+ -exclude windowpushd.test
] -dbconfig {
optimization_control $::dbhandle all 0
}
diff --git a/test/releasetest_data.tcl b/test/releasetest_data.tcl
index 9692cf47fc..5aa27f6e6e 100644
--- a/test/releasetest_data.tcl
+++ b/test/releasetest_data.tcl
@@ -289,7 +289,7 @@ if {$tcl_platform(os)=="Darwin"} {
array set ::Platforms [strip_comments {
Linux-x86_64 {
"Check-Symbols*" checksymbols
- "Fast-One" "fuzztest test"
+ "Fast-One" "QUICKTEST_INCLUDE=rbu.test fuzztest test"
"Debug-One" "mptest test"
"Debug-Two" "test"
"Have-Not" test
@@ -586,9 +586,15 @@ proc main_tests {args} {
puts "$config \"$target\""
if {$bNodebug==0 && $bNosynthetic==0} {
set iHas [string first SQLITE_DEBUG $::Configs($config)]
- set dtarget test
- if {$target=="tcltest"} {
- set dtarget tcltest
+ set dtarget [list]
+ set iQTI [lsearch -glob $target QUICKTEST_*]
+ if {$iQTI>=0} {
+ lappend dtarget [lindex $target $iQTI]
+ }
+ if {[lsearch $target tcltest]>=0} {
+ lappend dtarget tcltest
+ } else {
+ lappend dtarget test
}
if {$iHas>=0} {
puts "$config-ndebug \"$dtarget\""
diff --git a/test/returning1.test b/test/returning1.test
new file mode 100644
index 0000000000..52fb8812a6
--- /dev/null
+++ b/test/returning1.test
@@ -0,0 +1,87 @@
+# 2021-01-28
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is the new RETURNING clause
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix returning1
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c DEFAULT 'pax');
+ INSERT INTO t1(b) VALUES(10),('happy'),(NULL) RETURNING a,b,c;
+} {1 10 pax 2 happy pax 3 {} pax}
+do_execsql_test 1.1 {
+ SELECT * FROM t1;
+} {1 10 pax 2 happy pax 3 {} pax}
+do_execsql_test 1.2 {
+ INSERT INTO t1(b,c) VALUES(5,99) RETURNING b,c,a,rowid;
+} {5 99 4 4}
+do_execsql_test 1.3 {
+ SELECT * FROM t1;
+} {1 10 pax 2 happy pax 3 {} pax 4 5 99}
+do_execsql_test 1.4 {
+ INSERT INTO t1 DEFAULT VALUES RETURNING *;
+} {5 {} pax}
+do_execsql_test 1.5 {
+ SELECT * FROM t1;
+} {1 10 pax 2 happy pax 3 {} pax 4 5 99 5 {} pax}
+do_execsql_test 1.6 {
+ CREATE TABLE t2(x,y,z);
+ INSERT INTO t2 VALUES(11,12,13),(21,'b','c'),(31,'b-value',4.75);
+}
+do_execsql_test 1.7 {
+ INSERT INTO t1 SELECT * FROM t2 RETURNING *;
+} {11 12 13 21 b c 31 b-value 4.75}
+do_execsql_test 1.8 {
+ SELECT *, '|' FROM t1;
+} {1 10 pax | 2 happy pax | 3 {} pax | 4 5 99 | 5 {} pax | 11 12 13 | 21 b c | 31 b-value 4.75 |}
+
+do_execsql_test 2.1 {
+ UPDATE t1 SET c='bellum' WHERE c='pax' RETURNING rowid, b, '|';
+} {1 10 | 2 happy | 3 {} | 5 {} |}
+do_execsql_test 2.2 {
+ SELECT *, '|' FROM t1;
+} {1 10 bellum | 2 happy bellum | 3 {} bellum | 4 5 99 | 5 {} bellum | 11 12 13 | 21 b c | 31 b-value 4.75 |}
+
+do_execsql_test 3.1 {
+ DELETE FROM t1 WHERE c='bellum' RETURNING rowid, *, '|';
+} {1 1 10 bellum | 2 2 happy bellum | 3 3 {} bellum | 5 5 {} bellum |}
+do_execsql_test 3.2 {
+ SELECT *, '|' FROM t1;
+} {4 5 99 | 11 12 13 | 21 b c | 31 b-value 4.75 |}
+
+do_execsql_test 4.1 {
+ CREATE TABLE t4(a INT, b INT DEFAULT 1234, c INT DEFAULT -16);
+ CREATE UNIQUE INDEX t4a ON t4(a);
+ INSERT INTO t4(a,b,c) VALUES(1,2,3);
+} {}
+do_execsql_test 4.2 {
+ INSERT INTO t4(a,b,c) VALUES(1,22,33)
+ ON CONFLICT(a) DO UPDATE SET b=44
+ RETURNING *;
+} {1 44 3}
+do_execsql_test 4.3 {
+ SELECT * FROM t4;
+} {1 44 3}
+do_execsql_test 4.4 {
+ DELETE FROM t4;
+ INSERT INTO t4 VALUES(1,2,3),(4,5,6),(7,8,9);
+} {}
+do_execsql_test 4.5 {
+ INSERT INTO t4(a,b,c) VALUES(2,3,4),(4,5,6),(5,6,7)
+ ON CONFLICT(a) DO UPDATE SET b=100
+ RETURNING *, '|';
+} {2 3 4 | 4 100 6 | 5 6 7 |}
+
+
+finish_test
diff --git a/test/rowvalue.test b/test/rowvalue.test
index e3b66a1096..91a59e71c3 100644
--- a/test/rowvalue.test
+++ b/test/rowvalue.test
@@ -644,4 +644,17 @@ do_catchsql_test 27.10 {
INSERT INTO t0(c0) VALUES(0) ON CONFLICT(c0) DO UPDATE SET c0 = 3;
} {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}}
+# 2021-02-03
+# https://bugs.chromium.org/p/chromium/issues/detail?id=1173511
+# Faulty assert() statement.
+#
+reset_db
+do_catchsql_test 28.10 {
+ CREATE TABLE t0(c0 PRIMARY KEY, c1);
+ CREATE TRIGGER trigger0 BEFORE DELETE ON t0 BEGIN
+ SELECT (SELECT c0,c1 FROM t0) FROM t0;
+ END ;
+ DELETE FROM t0;
+} {1 {sub-select returns 2 columns - expected 1}}
+
finish_test
diff --git a/test/select1.test b/test/select1.test
index 82e6ab55c8..e22907da1e 100644
--- a/test/select1.test
+++ b/test/select1.test
@@ -1167,17 +1167,17 @@ do_execsql_test select1-18.4 {
# 2019-12-17 gramfuzz find
#
-do_execsql_test select-19.10 {
+do_execsql_test select1-19.10 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x);
} {}
-do_catchsql_test select-19.20 {
+do_catchsql_test select1-19.20 {
INSERT INTO t1
SELECT 1,2,3,4,5,6,7
UNION ALL SELECT 1,2,3,4,5,6,7
ORDER BY 1;
} {1 {table t1 has 1 columns but 7 values were supplied}}
-do_catchsql_test select-19.21 {
+do_catchsql_test select1-19.21 {
INSERT INTO t1
SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
UNION ALL SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
@@ -1187,7 +1187,7 @@ do_catchsql_test select-19.21 {
# 2020-01-01 Found by Yongheng's fuzzer
#
reset_db
-do_execsql_test select-20.10 {
+do_execsql_test select1-20.10 {
CREATE TABLE t1 (
a INTEGER PRIMARY KEY,
b AS('Y') UNIQUE
@@ -1197,13 +1197,13 @@ do_execsql_test select-20.10 {
WHERE ((SELECT t1.a FROM t1 AS x GROUP BY b) AND b=0)
OR a = 10;
} {10 Y}
-do_execsql_test select-20.20 {
+do_execsql_test select1-20.20 {
SELECT ifnull(a, max((SELECT 123))), count(a) FROM t1 ;
} {10 1}
# 2020-10-02 dbsqlfuzz find
reset_db
-do_execsql_test select-21.1 {
+do_execsql_test select1-21.1 {
CREATE TABLE t1(a IMTEGES PRIMARY KEY,R);
CREATE TABLE t2(x UNIQUE);
CREATE VIEW v1a(z,y) AS SELECT x IS NULL, x FROM t2;
diff --git a/test/selectC.test b/test/selectC.test
index 4d79963007..63851ca5d7 100644
--- a/test/selectC.test
+++ b/test/selectC.test
@@ -261,10 +261,10 @@ do_execsql_test 5.2 {
}
do_execsql_test 5.3 {
- SELECT * FROM x1, (SELECT b FROM vvv UNION ALL SELECT c from x3);
+ SELECT * FROM x1, (SELECT b FROM vvv UNION ALL SELECT c from x3) ORDER BY 1,2;
} {
- a 21 a 22 a 23 a 24 a 25 a 302 a 303 a 301
- b 21 b 22 b 23 b 24 b 25 b 302 b 303 b 301
+ a 21 a 22 a 23 a 24 a 25 a 301 a 302 a 303
+ b 21 b 22 b 23 b 24 b 25 b 301 b 302 b 303
}
finish_test
diff --git a/test/shell1.test b/test/shell1.test
index a62b0d1a80..330276120d 100644
--- a/test/shell1.test
+++ b/test/shell1.test
@@ -642,7 +642,7 @@ do_test shell1-3.23.2 {
# .stats ON|OFF Turn stats on or off
#do_test shell1-3.23b.1 {
# catchcmd "test.db" ".stats"
-#} {1 {Usage: .stats on|off}}
+#} {1 {Usage: .stats on|off|stmt|vmstep}}
do_test shell1-3.23b.2 {
catchcmd "test.db" ".stats ON"
} {0 {}}
@@ -652,7 +652,7 @@ do_test shell1-3.23b.3 {
do_test shell1-3.23b.4 {
# too many arguments
catchcmd "test.db" ".stats OFF BAD"
-} {1 {Usage: .stats ?on|off?}}
+} {1 {Usage: .stats ?on|off|stmt|vmstep?}}
# Ticket 7be932dfa60a8a6b3b26bcf7623ec46e0a403ddb 2018-06-07
# Adverse interaction between .stats and .eqp
diff --git a/test/shell4.test b/test/shell4.test
index 386e51f6c7..9e3c58fbec 100644
--- a/test/shell4.test
+++ b/test/shell4.test
@@ -66,7 +66,7 @@ do_test shell4-1.3.3 {
do_test shell4-1.3.4 {
# too many arguments
catchcmd "test.db" ".stats OFF BAD"
-} {1 {Usage: .stats ?on|off?}}
+} {1 {Usage: .stats ?on|off|stmt|vmstep?}}
# NB. whitespace is important
do_test shell4-1.4.1 {
diff --git a/test/startup.c b/test/startup.c
new file mode 100644
index 0000000000..7262189229
--- /dev/null
+++ b/test/startup.c
@@ -0,0 +1,628 @@
+/*
+** 2021-01-01
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file implements a program used to measure the start-up performance
+** of SQLite.
+**
+** To use:
+**
+** ./startup init
+** valgrind --tool=cachegrind ./startup run
+**
+**
+** The "./startup init" command creates the test database file named
+** "startup.db". The performance test is run by the "./startup run"
+** command. That command does nothing but open the database file and
+** parse the entire schema.
+*/
+#include
+#include
+#include
+#include
+#include
+#include "sqlite3.h"
+
+static const char zHelp[] =
+ "Usage: %s COMMAND\n"
+ "Commands:\n"
+ " init Initialized the startup.db database file\n"
+ " run Run the startup performance test\n"
+ "Options:\n"
+ " --dbname NAME Set the name of the test database file\n"
+ " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n"
+ " --stats Show statistics at the end\n"
+/* TBD
+ " --journal M Set the journal_mode to M\n"
+ " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n"
+ " --mmap SZ MMAP the first SZ bytes of the database file\n"
+ " --multithread Set multithreaded mode\n"
+ " --nomemstat Disable memory statistics\n"
+ " --pagesize N Set the page size to N\n"
+ " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n"
+ " --serialized Set serialized threading mode\n"
+ " --singlethread Set single-threaded mode - disables all mutexing\n"
+ " --utf16be Set text encoding to UTF-16BE\n"
+ " --utf16le Set text encoding to UTF-16LE\n"
+ " --utf8 Set text encoding to UTF-8\n"
+*/
+;
+
+static void usage(const char *argv0){
+ printf(zHelp, argv0);
+ exit(1);
+}
+
+/*
+** The test schema is derived from the Fossil repository for SQLite itself.
+** The schema covers the repository, the local checkout database, and
+** the global configuration database.
+*/
+static const char zTestSchema[] =
+ "CREATE TABLE repo_blob(\n"
+ " rid INTEGER PRIMARY KEY,\n"
+ " rcvid INTEGER,\n"
+ " size INTEGER,\n"
+ " uuid TEXT UNIQUE NOT NULL,\n"
+ " content BLOB,\n"
+ " CHECK( length(uuid)>=40 AND rid>0 )\n"
+ ");\n"
+ "CREATE TABLE repo_delta(\n"
+ " rid INTEGER PRIMARY KEY,\n"
+ " srcid INTEGER NOT NULL REFERENCES blob\n"
+ ");\n"
+ "CREATE TABLE repo_rcvfrom(\n"
+ " rcvid INTEGER PRIMARY KEY,\n"
+ " uid INTEGER REFERENCES user,\n"
+ " mtime DATETIME,\n"
+ " nonce TEXT UNIQUE,\n"
+ " ipaddr TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_private(rid INTEGER PRIMARY KEY);\n"
+ "CREATE TABLE repo_accesslog(\n"
+ " uname TEXT,\n"
+ " ipaddr TEXT,\n"
+ " success BOOLEAN,\n"
+ " mtime TIMESTAMP);\n"
+ "CREATE TABLE repo_user(\n"
+ " uid INTEGER PRIMARY KEY,\n"
+ " login TEXT UNIQUE,\n"
+ " pw TEXT,\n"
+ " cap TEXT,\n"
+ " cookie TEXT,\n"
+ " ipaddr TEXT,\n"
+ " cexpire DATETIME,\n"
+ " info TEXT,\n"
+ " mtime DATE,\n"
+ " photo BLOB\n"
+ ");\n"
+ "CREATE TABLE repo_reportfmt(\n"
+ " rn INTEGER PRIMARY KEY,\n"
+ " owner TEXT,\n"
+ " title TEXT UNIQUE,\n"
+ " mtime INTEGER,\n"
+ " cols TEXT,\n"
+ " sqlcode TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_sqlite_stat2(tbl,idx,sampleno,sample);\n"
+ "CREATE TABLE repo_sqlite_stat1(tbl,idx,stat);\n"
+ "CREATE TABLE repo_sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);\n"
+ "CREATE TABLE repo_config(\n"
+ " name TEXT PRIMARY KEY NOT NULL,\n"
+ " value CLOB, mtime INTEGER,\n"
+ " CHECK( typeof(name)='text' AND length(name)>=1 )\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE repo_shun(uuid PRIMARY KEY,\n"
+ " mtime INTEGER,\n"
+ " scom TEXT) WITHOUT ROWID;\n"
+ "CREATE TABLE repo_concealed(\n"
+ " hash TEXT PRIMARY KEY,\n"
+ " content TEXT\n"
+ ", mtime INTEGER) WITHOUT ROWID;\n"
+ "CREATE TABLE repo_admin_log(\n"
+ " id INTEGER PRIMARY KEY,\n"
+ " time INTEGER, -- Seconds since 1970\n"
+ " page TEXT, -- path of page\n"
+ " who TEXT, -- User who made the change\n"
+ " what TEXT -- What changed\n"
+ ");\n"
+ "CREATE TABLE repo_unversioned(\n"
+ " name TEXT PRIMARY KEY,\n"
+ " rcvid INTEGER,\n"
+ " mtime DATETIME,\n"
+ " hash TEXT,\n"
+ " sz INTEGER,\n"
+ " encoding INT,\n"
+ " content BLOB\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE repo_subscriber(\n"
+ " subscriberId INTEGER PRIMARY KEY,\n"
+ " subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE,\n"
+ " semail TEXT UNIQUE COLLATE nocase,\n"
+ " suname TEXT,\n"
+ " sverified BOOLEAN DEFAULT true,\n"
+ " sdonotcall BOOLEAN,\n"
+ " sdigest BOOLEAN,\n"
+ " ssub TEXT,\n"
+ " sctime INTDATE,\n"
+ " mtime INTDATE,\n"
+ " smip TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_pending_alert(\n"
+ " eventid TEXT PRIMARY KEY,\n"
+ " sentSep BOOLEAN DEFAULT false,\n"
+ " sentDigest BOOLEAN DEFAULT false\n"
+ ", sentMod BOOLEAN DEFAULT false) WITHOUT ROWID;\n"
+ "CREATE INDEX repo_delta_i1 ON repo_delta(srcid);\n"
+ "CREATE INDEX repo_blob_rcvid ON repo_blob(rcvid);\n"
+ "CREATE INDEX repo_subscriberUname\n"
+ " ON repo_subscriber(suname) WHERE suname IS NOT NULL;\n"
+ "CREATE VIEW repo_artifact(rid,rcvid,size,atype,srcid,hash,content) AS\n"
+ " SELECT blob.rid,rcvid,size,1,srcid,uuid,content\n"
+ " FROM repo_blob LEFT JOIN repo_delta ON (blob.rid=delta.rid);\n"
+ "CREATE TABLE repo_filename(\n"
+ " fnid INTEGER PRIMARY KEY,\n"
+ " name TEXT UNIQUE\n"
+ ");\n"
+ "CREATE TABLE repo_mlink(\n"
+ " mid INTEGER,\n"
+ " fid INTEGER,\n"
+ " pmid INTEGER,\n"
+ " pid INTEGER,\n"
+ " fnid INTEGER REFERENCES filename,\n"
+ " pfnid INTEGER,\n"
+ " mperm INTEGER,\n"
+ " isaux BOOLEAN DEFAULT 0\n"
+ ");\n"
+ "CREATE INDEX repo_mlink_i1 ON repo_mlink(mid);\n"
+ "CREATE INDEX repo_mlink_i2 ON repo_mlink(fnid);\n"
+ "CREATE INDEX repo_mlink_i3 ON repo_mlink(fid);\n"
+ "CREATE INDEX repo_mlink_i4 ON repo_mlink(pid);\n"
+ "CREATE TABLE repo_plink(\n"
+ " pid INTEGER REFERENCES blob,\n"
+ " cid INTEGER REFERENCES blob,\n"
+ " isprim BOOLEAN,\n"
+ " mtime DATETIME,\n"
+ " baseid INTEGER REFERENCES blob,\n"
+ " UNIQUE(pid, cid)\n"
+ ");\n"
+ "CREATE INDEX repo_plink_i2 ON repo_plink(cid,pid);\n"
+ "CREATE TABLE repo_leaf(rid INTEGER PRIMARY KEY);\n"
+ "CREATE TABLE repo_event(\n"
+ " type TEXT,\n"
+ " mtime DATETIME,\n"
+ " objid INTEGER PRIMARY KEY,\n"
+ " tagid INTEGER,\n"
+ " uid INTEGER REFERENCES user,\n"
+ " bgcolor TEXT,\n"
+ " euser TEXT,\n"
+ " user TEXT,\n"
+ " ecomment TEXT,\n"
+ " comment TEXT,\n"
+ " brief TEXT,\n"
+ " omtime DATETIME\n"
+ ");\n"
+ "CREATE INDEX repo_event_i1 ON repo_event(mtime);\n"
+ "CREATE TABLE repo_phantom(\n"
+ " rid INTEGER PRIMARY KEY\n"
+ ");\n"
+ "CREATE TABLE repo_orphan(\n"
+ " rid INTEGER PRIMARY KEY,\n"
+ " baseline INTEGER\n"
+ ");\n"
+ "CREATE INDEX repo_orphan_baseline ON repo_orphan(baseline);\n"
+ "CREATE TABLE repo_unclustered(\n"
+ " rid INTEGER PRIMARY KEY\n"
+ ");\n"
+ "CREATE TABLE repo_unsent(\n"
+ " rid INTEGER PRIMARY KEY\n"
+ ");\n"
+ "CREATE TABLE repo_tag(\n"
+ " tagid INTEGER PRIMARY KEY,\n"
+ " tagname TEXT UNIQUE\n"
+ ");\n"
+ "CREATE TABLE repo_tagxref(\n"
+ " tagid INTEGER REFERENCES tag,\n"
+ " tagtype INTEGER,\n"
+ " srcid INTEGER REFERENCES blob,\n"
+ " origid INTEGER REFERENCES blob,\n"
+ " value TEXT,\n"
+ " mtime TIMESTAMP,\n"
+ " rid INTEGER REFERENCE blob,\n"
+ " UNIQUE(rid, tagid)\n"
+ ");\n"
+ "CREATE INDEX repo_tagxref_i1 ON repo_tagxref(tagid, mtime);\n"
+ "CREATE TABLE repo_backlink(\n"
+ " target TEXT,\n"
+ " srctype INT,\n"
+ " srcid INT,\n"
+ " mtime TIMESTAMP,\n"
+ " UNIQUE(target, srctype, srcid)\n"
+ ");\n"
+ "CREATE INDEX repo_backlink_src ON repo_backlink(srcid, srctype);\n"
+ "CREATE TABLE repo_attachment(\n"
+ " attachid INTEGER PRIMARY KEY,\n"
+ " isLatest BOOLEAN DEFAULT 0,\n"
+ " mtime TIMESTAMP,\n"
+ " src TEXT,\n"
+ " target TEXT,\n"
+ " filename TEXT,\n"
+ " comment TEXT,\n"
+ " user TEXT\n"
+ ");\n"
+ "CREATE INDEX repo_attachment_idx1\n"
+ " ON repo_attachment(target, filename, mtime);\n"
+ "CREATE INDEX repo_attachment_idx2 ON repo_attachment(src);\n"
+ "CREATE TABLE repo_cherrypick(\n"
+ " parentid INT,\n"
+ " childid INT,\n"
+ " isExclude BOOLEAN DEFAULT false,\n"
+ " PRIMARY KEY(parentid, childid)\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE INDEX repo_cherrypick_cid ON repo_cherrypick(childid);\n"
+ "CREATE TABLE repo_ticket(\n"
+ " -- Do not change any column that begins with tkt_\n"
+ " tkt_id INTEGER PRIMARY KEY,\n"
+ " tkt_uuid TEXT UNIQUE,\n"
+ " tkt_mtime DATE,\n"
+ " tkt_ctime DATE,\n"
+ " -- Add as many fields as required below this line\n"
+ " type TEXT,\n"
+ " status TEXT,\n"
+ " subsystem TEXT,\n"
+ " priority TEXT,\n"
+ " severity TEXT,\n"
+ " foundin TEXT,\n"
+ " private_contact TEXT,\n"
+ " resolution TEXT,\n"
+ " title TEXT,\n"
+ " comment TEXT\n"
+ ");\n"
+ "CREATE TABLE repo_ticketchng(\n"
+ " -- Do not change any column that begins with tkt_\n"
+ " tkt_id INTEGER REFERENCES ticket,\n"
+ " tkt_rid INTEGER REFERENCES blob,\n"
+ " tkt_mtime DATE,\n"
+ " -- Add as many fields as required below this line\n"
+ " login TEXT,\n"
+ " username TEXT,\n"
+ " mimetype TEXT,\n"
+ " icomment TEXT\n"
+ ");\n"
+ "CREATE INDEX repo_ticketchng_idx1 ON repo_ticketchng(tkt_id, tkt_mtime);\n"
+ "CREATE TRIGGER repo_alert_trigger1\n"
+ "AFTER INSERT ON repo_event BEGIN\n"
+ " INSERT INTO repo_pending_alert(eventid)\n"
+ " SELECT printf('%.1c%d',new.type,new.objid) WHERE true\n"
+ " ON CONFLICT(eventId) DO NOTHING;\n"
+ "END;\n"
+ "CREATE TABLE repo_vcache(\n"
+ " vid INTEGER, -- check-in ID\n"
+ " fname TEXT, -- filename\n"
+ " rid INTEGER, -- artifact ID\n"
+ " PRIMARY KEY(vid,fname)\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE localdb_vvar(\n"
+ " name TEXT PRIMARY KEY NOT NULL,\n"
+ " value CLOB,\n"
+ " CHECK( typeof(name)='text' AND length(name)>=1 )\n"
+ ");\n"
+ "CREATE TABLE localdb_vfile(\n"
+ " id INTEGER PRIMARY KEY,\n"
+ " vid INTEGER REFERENCES blob,\n"
+ " chnged INT DEFAULT 0,\n"
+ " deleted BOOLEAN DEFAULT 0,\n"
+ " isexe BOOLEAN,\n"
+ " islink BOOLEAN,\n"
+ " rid INTEGER,\n"
+ " mrid INTEGER,\n"
+ " mtime INTEGER,\n"
+ " pathname TEXT,\n"
+ " origname TEXT, mhash,\n"
+ " UNIQUE(pathname,vid)\n"
+ ");\n"
+ "CREATE TABLE localdb_sqlite_stat1(tbl,idx,stat);\n"
+ "CREATE TABLE localdb_vcache(\n"
+ " vid INTEGER, -- check-in ID\n"
+ " fname TEXT, -- filename\n"
+ " rid INTEGER, -- artifact ID\n"
+ " PRIMARY KEY(vid,fname)\n"
+ ") WITHOUT ROWID;\n"
+ "CREATE TABLE localdb_stash(\n"
+ " stashid INTEGER PRIMARY KEY,\n"
+ " vid INTEGER,\n"
+ " hash TEXT,\n"
+ " comment TEXT,\n"
+ " ctime TIMESTAMP\n"
+ ");\n"
+ "CREATE TABLE localdb_stashfile(\n"
+ " stashid INTEGER REFERENCES stash,\n"
+ " isAdded BOOLEAN,\n"
+ " isRemoved BOOLEAN,\n"
+ " isExec BOOLEAN,\n"
+ " isLink BOOLEAN,\n"
+ " rid INTEGER,\n"
+ " hash TEXT,\n"
+ " origname TEXT,\n"
+ " newname TEXT,\n"
+ " delta BLOB,\n"
+ " PRIMARY KEY(newname, stashid)\n"
+ ");\n"
+ "CREATE TABLE localdb_vmerge(\n"
+ " id INTEGER REFERENCES vfile,\n"
+ " merge INTEGER,\n"
+ " mhash TEXT\n"
+ ");\n"
+ "CREATE UNIQUE INDEX localdb_vmergex1 ON localdb_vmerge(id,mhash);\n"
+ "CREATE TRIGGER localdb_vmerge_ck1 AFTER INSERT ON localdb_vmerge\n"
+ "WHEN new.mhash IS NULL BEGIN\n"
+ " SELECT raise(FAIL,\n"
+ " 'trying to update a newer checkout with an older version of Fossil');\n"
+ "END;\n"
+ "CREATE TABLE configdb_global_config(\n"
+ " name TEXT PRIMARY KEY,\n"
+ " value TEXT\n"
+ ");\n"
+ "CREATE TABLE configdb_sqlite_stat1(tbl,idx,stat);\n"
+;
+
+#ifdef __linux__
+#include
+#include
+
+/*
+** Attempt to display I/O stats on Linux using /proc/PID/io
+*/
+static void displayLinuxIoStats(FILE *out){
+ FILE *in;
+ char z[200];
+ sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
+ in = fopen(z, "rb");
+ if( in==0 ) return;
+ while( fgets(z, sizeof(z), in)!=0 ){
+ static const struct {
+ const char *zPattern;
+ const char *zDesc;
+ } aTrans[] = {
+ { "rchar: ", "Bytes received by read():" },
+ { "wchar: ", "Bytes sent to write():" },
+ { "syscr: ", "Read() system calls:" },
+ { "syscw: ", "Write() system calls:" },
+ { "read_bytes: ", "Bytes rcvd from storage:" },
+ { "write_bytes: ", "Bytes sent to storage:" },
+ { "cancelled_write_bytes: ", "Cancelled write bytes:" },
+ };
+ int i;
+ for(i=0; i='0' && c<='9' ) return c - '0';
+ if( c>='a' && c<='f' ) return c - 'a' + 10;
+ if( c>='A' && c<='F' ) return c - 'A' + 10;
+ return -1;
+}
+
+/*
+** Interpret zArg as an integer value, possibly with suffixes.
+*/
+static int integerValue(const char *zArg){
+ sqlite3_int64 v = 0;
+ static const struct { char *zSuffix; int iMult; } aMult[] = {
+ { "KiB", 1024 },
+ { "MiB", 1024*1024 },
+ { "GiB", 1024*1024*1024 },
+ { "KB", 1000 },
+ { "MB", 1000000 },
+ { "GB", 1000000000 },
+ { "K", 1000 },
+ { "M", 1000000 },
+ { "G", 1000000000 },
+ };
+ int i;
+ int isNeg = 0;
+ if( zArg[0]=='-' ){
+ isNeg = 1;
+ zArg++;
+ }else if( zArg[0]=='+' ){
+ zArg++;
+ }
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ int x;
+ zArg += 2;
+ while( (x = hexDigitValue(zArg[0]))>=0 ){
+ v = (v<<4) + x;
+ zArg++;
+ }
+ }else{
+ while( isdigit(zArg[0]) ){
+ v = v*10 + zArg[0] - '0';
+ zArg++;
+ }
+ }
+ for(i=0; i0x7fffffff ){
+ printf("ERROR: parameter too large - max 2147483648\n");
+ exit(1);
+ }
+ return (int)(isNeg? -v : v);
+}
+
+
+int main(int argc, char **argv){
+ const char *zCmd = 0;
+ int i;
+ int bAutovac = 0;
+ int showStats = 0;
+ const char *zDbName = "./startup.db";
+ int nHeap = 0;
+ int mnHeap = 0;
+
+ for(i=1; i=argc-2 ){
+ printf("ERROR: missing arguments on %s\n", argv[i]);
+ exit(1);
+ }
+ nHeap = integerValue(argv[i+1]);
+ mnHeap = integerValue(argv[i+2]);
+ i += 2;
+ }else
+ if( strcmp(z,"-stats")==0 ){
+ showStats = 1;
+ }else
+ {
+ printf("ERROR: unknown option \"%s\"\n", argv[i]);
+ usage(argv[0]);
+ }
+ }
+ if( zCmd==0 ){
+ printf("ERROR: no COMMAND specified\n");
+ usage(argv[0]);
+ }
+ if( strcmp(zCmd, "run")==0 ){
+ sqlite3 *db;
+ int rc;
+ char *zErr = 0;
+ void *pHeap = 0;
+ if( nHeap>0 ){
+ pHeap = malloc( nHeap );
+ if( pHeap==0 ){
+ printf("ERROR: cannot allocate %d-byte heap\n", nHeap);
+ exit(1);
+ }
+ rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, mnHeap);
+ if( rc ){
+ printf("ERROR: heap configuration failed: %d\n", rc);
+ exit(1);
+ }
+ }
+ rc = sqlite3_open(zDbName, &db);
+ if( rc ){
+ printf("SQLite error: %s\n", sqlite3_errmsg(db));
+ }else{
+ sqlite3_exec(db, "PRAGMA synchronous", 0, 0, &zErr);
+ }
+ if( zErr ){
+ printf("ERROR: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
+ if( showStats ){
+ int iCur, iHi;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHi, 0);
+ printf("-- Lookaside Slots Used: %d (max %d)\n", iCur,iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHi, 0);
+ printf("-- Successful lookasides: %d\n", iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur,&iHi,0);
+ printf("-- Lookaside size faults: %d\n", iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur,&iHi,0);
+ printf("-- Lookaside OOM faults: %d\n", iHi);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHi, 0);
+ printf("-- Pager Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHi, 1);
+ printf("-- Page cache hits: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHi, 1);
+ printf("-- Page cache misses: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHi, 1);
+ printf("-- Page cache writes: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHi, 0);
+ printf("-- Schema Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHi, 0);
+ printf("-- Statement Heap Usage: %d bytes\n", iCur);
+ }
+ sqlite3_close(db);
+ free(pHeap);
+ /* Global memory usage statistics printed after the database connection
+ ** has closed. Memory usage should be zero at this point. */
+ if( showStats ){
+ int iCur, iHi;
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHi, 0);
+ printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0);
+ printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0);
+ printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Allocation: %d bytes\n",iHi);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Pcache Allocation: %d bytes\n",iHi);
+#ifdef __linux__
+ displayLinuxIoStats(stdout);
+#endif
+ }
+ return 0;
+ }
+ if( strcmp(zCmd, "init")==0 ){
+ sqlite3 *db;
+ char *zAux;
+ char *zErr = 0;
+ int rc;
+ unlink(zDbName);
+ zAux = sqlite3_mprintf("%s-journal", zDbName);
+ unlink(zAux);
+ sqlite3_free(zAux);
+ zAux = sqlite3_mprintf("%s-wal", zDbName);
+ unlink(zAux);
+ sqlite3_free(zAux);
+ rc = sqlite3_open(zDbName, &db);
+ if( rc ){
+ printf("SQLite error: %s\n", sqlite3_errmsg(db));
+ }else{
+ sqlite3_exec(db, "BEGIN", 0, 0, 0);
+ sqlite3_exec(db, zTestSchema, 0, 0, &zErr);
+ sqlite3_exec(db, "COMMIT", 0, 0, 0);
+ }
+ if( zErr ){
+ printf("ERROR: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
+ sqlite3_close(db);
+ return 0;
+
+ }
+}
diff --git a/test/tester.tcl b/test/tester.tcl
index 3b91ff46cb..304808b6bc 100644
--- a/test/tester.tcl
+++ b/test/tester.tcl
@@ -174,8 +174,14 @@ proc get_pwd {} {
# case of the result to what Tcl considers canonical, which would
# defeat the purpose of this procedure.
#
+ if {[info exists ::env(ComSpec)]} {
+ set comSpec $::env(ComSpec)
+ } else {
+ # NOTE: Hard-code the typical default value.
+ set comSpec {C:\Windows\system32\cmd.exe}
+ }
return [string map [list \\ /] \
- [string trim [exec -- $::env(ComSpec) /c echo %CD%]]]
+ [string trim [exec -- $comSpec /c echo %CD%]]]
} else {
return [pwd]
}
@@ -902,8 +908,8 @@ proc catchcmdex {db {cmd ""}} {
proc filepath_normalize {p} {
# test cases should be written to assume "unix"-like file paths
if {$::tcl_platform(platform)!="unix"} {
- # lreverse*2 as a hack to remove any unneeded {} after the string map
- lreverse [lreverse [string map {\\ /} [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]]]
+ string map [list \\ / \{/ / .db\} .db] \
+ [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]
} {
set p
}
@@ -1689,9 +1695,12 @@ proc crashsql {args} {
set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]]
set f [open crash.tcl w]
+ puts $f "sqlite3_initialize ; sqlite3_shutdown"
+ puts $f "catch { install_malloc_faultsim 1 }"
puts $f "sqlite3_crash_enable 1 $dfltvfs"
puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
+ puts $f "autoinstall_test_functions"
# This block sets the cache size of the main database to 10
# pages. This is done in case the build is configured to omit
@@ -1719,7 +1728,7 @@ proc crashsql {args} {
}
close $f
set r [catch {
- exec [info nameofexec] crash.tcl >@stdout
+ exec [info nameofexec] crash.tcl >@stdout 2>@stdout
} msg]
# Windows/ActiveState TCL returns a slightly different
diff --git a/test/trace3.test b/test/trace3.test
index 538dcd0942..e9935acfb8 100644
--- a/test/trace3.test
+++ b/test/trace3.test
@@ -251,4 +251,83 @@ do_test trace3-11.2 {
set ::stmtlist(record)
} {/^-?\d+$/}
+#-------------------------------------------------------------------------
+reset_db
+do_test 12.1.0 {
+ set ::STMT [sqlite3_prepare_v2 $DB \
+ "SELECT ?1 || ?1 || ?1 || ?2 || ?3 || ?4 || ? || ?1 || ?" -1 TAIL
+ ]
+ sqlite3_bind_parameter_count $::STMT
+} {6}
+
+do_test 12.1.1 {
+ sqlite3_bind_text $STMT 1 "A" 1
+ sqlite3_bind_text $STMT 2 "B" 1
+ sqlite3_bind_text $STMT 3 "C" 1
+ sqlite3_bind_text $STMT 4 "D" 1
+ sqlite3_bind_text $STMT 5 "E" 1
+ sqlite3_bind_text $STMT 6 "F" 1
+ sqlite3_expanded_sql $STMT
+} {SELECT 'A' || 'A' || 'A' || 'B' || 'C' || 'D' || 'E' || 'A' || 'F'}
+
+do_test 12.1.2 {
+ sqlite3_step $STMT
+ sqlite3_column_text $STMT 0
+} {AAABCDEAF}
+
+do_test 12.1.3 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+do_test 12.2.0 {
+ execsql {
+ CREATE TABLE nameFtsFuzzySearchTable(
+ word, distance, langid, score, top, scope
+ );
+ }
+ set ::STMT [sqlite3_prepare_v2 $DB {
+ SELECT
+ substr(word,1,length(?1)-1) AS term,
+ distance,
+ langid,
+ score
+ FROM
+ nameFtsFuzzySearchTable
+ WHERE
+ word MATCH (?1) AND abs(?1) = abs(term)
+ AND top = ?2 AND distance > ?3 AND scope = ?4 AND langid = ?
+ GROUP BY term, langid
+ HAVING (1.0 - ((distance / 100.0) / CAST( length(?1) - 1 AS REAL ))) >= ?
+ } -1 TAIL]
+ sqlite3_bind_parameter_count $::STMT
+} {6}
+
+do_test 12.1.1 {
+ sqlite3_bind_text $STMT 1 "A" 1
+ sqlite3_bind_text $STMT 2 "B" 1
+ sqlite3_bind_text $STMT 3 "C" 1
+ sqlite3_bind_text $STMT 4 "D" 1
+ sqlite3_bind_text $STMT 5 "E" 1
+ sqlite3_bind_text $STMT 6 "F" 1
+ sqlite3_expanded_sql $STMT
+} {
+ SELECT
+ substr(word,1,length('A')-1) AS term,
+ distance,
+ langid,
+ score
+ FROM
+ nameFtsFuzzySearchTable
+ WHERE
+ word MATCH ('A') AND abs('A') = abs(term)
+ AND top = 'B' AND distance > 'C' AND scope = 'D' AND langid = 'E'
+ GROUP BY term, langid
+ HAVING (1.0 - ((distance / 100.0) / CAST( length('A') - 1 AS REAL ))) >= 'F'
+ }
+
+do_test 12.1.2 {
+ sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+
finish_test
diff --git a/test/triggerE.test b/test/triggerE.test
index 9db7035325..de4b068582 100644
--- a/test/triggerE.test
+++ b/test/triggerE.test
@@ -58,6 +58,8 @@ foreach {tn defn} {
8 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = ?; END; }
9 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = 1 WHERE d = ?; END; }
10 { AFTER INSERT ON t1 BEGIN SELECT * FROM pragma_stats(?); END; }
+ 11 { BEFORE INSERT ON t1 BEGIN
+ INSERT INTO t1 SELECT max(b) OVER(ORDER BY $1) FROM t1; END }
} {
catchsql {drop trigger tr1}
do_catchsql_test 1.1.$tn "CREATE TRIGGER tr1 $defn" [list 1 $errmsg]
diff --git a/test/unionall.test b/test/unionall.test
new file mode 100644
index 0000000000..2f8027ccb7
--- /dev/null
+++ b/test/unionall.test
@@ -0,0 +1,358 @@
+# 2020-12-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is flattening UNION ALL sub-queries.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix unionall
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
+ CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
+ CREATE TABLE t1_c(e INTEGER PRIMARY KEY, f TEXT);
+
+ INSERT INTO t1_a VALUES(1, 'one'), (4, 'four');
+ INSERT INTO t1_b VALUES(2, 'two'), (5, 'five');
+ INSERT INTO t1_c VALUES(3, 'three'), (6, 'six');
+
+ CREATE VIEW t1 AS
+ SELECT a, b FROM t1_a UNION ALL
+ SELECT c, d FROM t1_b UNION ALL
+ SELECT e, f FROM t1_c;
+
+ CREATE TABLE i1(x);
+ INSERT INTO i1 VALUES(2), (5), (6), (1);
+}
+
+do_execsql_test 1.1 {
+ SELECT a, b FROM (
+ SELECT a, b FROM t1_a UNION ALL
+ SELECT c, d FROM t1_b UNION ALL
+ SELECT e, f FROM t1_c
+ ) ORDER BY a
+} {
+ 1 one 2 two 3 three 4 four 5 five 6 six
+}
+
+do_execsql_test 1.2 {
+ SELECT a, b FROM t1 ORDER BY a
+} {
+ 1 one 2 two 3 three 4 four 5 five 6 six
+}
+
+do_execsql_test 1.3 {
+ SELECT a, b FROM i1, t1 WHERE a=x ORDER BY a
+} {1 one 2 two 5 five 6 six}
+
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 2.1.0 {
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(1, 'ONE');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(2, 'TWO');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(3, 'THREE');
+}
+
+do_execsql_test 2.1.1 {
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<3
+ )
+ SELECT * FROM (
+ SELECT 0 AS i UNION ALL SELECT i FROM s UNION ALL SELECT 0
+ ), t1 WHERE x=i;
+} {
+ 1 1 one 1 1 ONE 2 2 two 2 2 TWO 3 3 three 3 3 THREE
+}
+
+do_catchsql_test 2.1.2 {
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<3 UNION ALL SELECT 4
+ )
+ SELECT * FROM s, t1 WHERE x=i;
+} {1 {circular reference: s}}
+
+do_execsql_test 2.2.0 {
+ CREATE TABLE t2_a(k INTEGER PRIMARY KEY, v TEXT);
+ CREATE TABLE t2_b(k INTEGER PRIMARY KEY, v TEXT);
+
+ CREATE VIEW t2 AS
+ SELECT * FROM t2_a
+ UNION ALL
+ SELECT * FROM t2_b;
+
+ CREATE TRIGGER t2_insert INSTEAD OF INSERT ON t2 BEGIN
+ INSERT INTO t2_a SELECT new.k, new.v WHERE (new.k%2)==0;
+ INSERT INTO t2_b SELECT new.k, new.v WHERE (new.k%2)==1;
+ END;
+
+ INSERT INTO t2 VALUES(5, 'v'), (4, 'iv'), (3, 'iii'), (2, 'ii');
+}
+
+do_execsql_test 2.2.1 {
+ SELECT * FROM t1, t2 WHERE x=k;
+} {
+ 2 two 2 ii 2 TWO 2 ii 3 three 3 iii 3 THREE 3 iii
+}
+
+do_execsql_test 2.2.2 {
+ SELECT * FROM t1 LEFT JOIN t2 ON (x=k);
+} {
+ 1 one {} {}
+ 1 ONE {} {}
+ 2 two 2 ii 2 TWO 2 ii 3 three 3 iii 3 THREE 3 iii
+}
+
+do_execsql_test 2.2.3 {
+ SELECT x1.*, x2.* FROM t2 AS x1, t2 AS x2 WHERE x1.k=x2.k+1
+} {
+ 4 iv 3 iii
+ 3 iii 2 ii
+ 5 v 4 iv
+}
+
+do_execsql_test 2.2.4 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY y;
+} {
+ 3 THREE 3 iii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 2 two 2 ii
+}
+do_execsql_test 2.2.5 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY y||'';
+} {
+ 3 THREE 3 iii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 2 two 2 ii
+}
+do_execsql_test 2.2.6 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY v
+} {
+ 2 two 2 ii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 3 THREE 3 iii
+}
+do_execsql_test 2.2.7 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY v||''
+} {
+ 2 two 2 ii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 3 THREE 3 iii
+}
+do_execsql_test 2.2.8 {
+ SELECT * FROM t1, t2 WHERE x=k ORDER BY k,v||''
+} {
+ 2 two 2 ii
+ 2 TWO 2 ii
+ 3 three 3 iii
+ 3 THREE 3 iii
+}
+do_execsql_test 2.2.9a {
+ SELECT * FROM t1, t2 ORDER BY +k
+} {
+ 1 one 2 ii 1 ONE 2 ii 2 two 2 ii
+ 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii
+
+ 1 one 3 iii 1 ONE 3 iii 2 two 3 iii
+ 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii
+
+ 1 one 4 iv 1 ONE 4 iv 2 two 4 iv
+ 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv
+
+ 1 one 5 v 1 ONE 5 v 2 two 5 v
+ 2 TWO 5 v 3 three 5 v 3 THREE 5 v
+}
+
+do_execsql_test 2.2.9b {
+ SELECT * FROM t1, t2 ORDER BY k
+} {
+ 1 one 2 ii 1 ONE 2 ii 2 two 2 ii
+ 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii
+
+ 1 one 3 iii 1 ONE 3 iii 2 two 3 iii
+ 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii
+
+ 1 one 4 iv 1 ONE 4 iv 2 two 4 iv
+ 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv
+
+ 1 one 5 v 1 ONE 5 v 2 two 5 v
+ 2 TWO 5 v 3 three 5 v 3 THREE 5 v
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 3.0 {
+ CREATE TABLE t1(c INTEGER PRIMARY KEY, d TEXT);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE t3_a(k INTEGER PRIMARY KEY, v TEXT);
+ INSERT INTO t3_a VALUES(2,'ii');
+ CREATE TABLE t3_b(k INTEGER PRIMARY KEY, v TEXT);
+ CREATE VIEW t3 AS
+ SELECT * FROM t3_a
+ UNION ALL
+ SELECT * FROM t3_b;
+} {}
+
+do_execsql_test 3.1 {
+ SELECT * FROM t1, t3 ORDER BY k;
+} {1 2 2 ii}
+
+reset_db
+do_execsql_test 4.0 {
+
+ CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t1_a VALUES(123, 't1_a');
+ CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
+
+ CREATE VIEW t1 AS
+ SELECT a, b FROM t1_a
+ UNION ALL
+ SELECT c, d FROM t1_b;
+
+ CREATE TABLE t3_a(k INTEGER PRIMARY KEY, v TEXT);
+ INSERT INTO t3_a VALUES(456, 't3_a');
+ CREATE TABLE t3_b(k INTEGER PRIMARY KEY, v TEXT);
+
+ CREATE VIEW t3 AS
+ SELECT * FROM t3_a
+ UNION ALL
+ SELECT * FROM t3_b;
+}
+
+do_execsql_test 4.1 {
+ SELECT * FROM t1, t3 ORDER BY k;
+} {123 t1_a 456 t3_a}
+
+do_execsql_test 4.2 {
+ SELECT * FROM (SELECT * FROM t1, t3) ORDER BY k;
+} {123 t1_a 456 t3_a}
+
+do_execsql_test 4.3 {
+ SELECT * FROM (SELECT * FROM t1, t3), (
+ SELECT max(a) OVER () FROM t1
+ UNION ALL
+ SELECT min(a) OVER () FROM t1
+ )
+ ORDER BY k;
+} {
+ 123 t1_a 456 t3_a 123
+ 123 t1_a 456 t3_a 123
+}
+
+do_execsql_test 4.3 {
+ SELECT * FROM (SELECT * FROM t1, t3), (
+ SELECT group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a),
+ group_concat(a) OVER (ORDER BY a)
+ FROM t1
+ )
+ ORDER BY k;
+} {
+ 123 t1_a 456 t3_a 123 123 123 123 123 123 123 123 123
+}
+
+do_execsql_test 4.3 {
+ SELECT * FROM (SELECT * FROM t1, t3) AS o, (
+ SELECT * FROM t1 LEFT JOIN t3 ON a=k
+ );
+} {
+ 123 t1_a 456 t3_a 123 t1_a {} {}
+}
+
+# 2020-12-30: dbsqlfuzz find
+reset_db
+do_execsql_test 5.1 {
+ CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t1_a VALUES(1,'one');
+ INSERT INTO t1_a VALUES(0,NULL);
+ CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
+ INSERT INTO t1_b VALUES(2,'two');
+ INSERT INTO t1_b VALUES(5,'five');
+ CREATE TABLE t1_c(e INTEGER PRIMARY KEY, f TEXT);
+ INSERT INTO t1_c VALUES(3,'three');
+ INSERT INTO t1_c VALUES(6,'six');
+ CREATE TABLE t2(k,v);
+ INSERT INTO t2 VALUES(5,'v');
+ INSERT INTO t2 VALUES(4,'iv');
+ INSERT INTO t2 VALUES(3,'iii');
+ INSERT INTO t2 VALUES(2,'ii');
+ CREATE TABLE t3_a(k INTEGER PRIMARY KEY, v TEXT);
+ INSERT INTO t3_a VALUES(2,'ii');
+ INSERT INTO t3_a VALUES(4,'iv');
+ CREATE TABLE t3_b(k INTEG5R PRIMARY KEY, v TEXT);
+ INSERT INTO t3_b VALUES(NULL,'iii');
+ INSERT INTO t3_b VALUES(NULL,'v');
+ CREATE VIEW t1 AS
+ SELECT a, b FROM t1_a UNION ALL
+ SELECT c, d FROM t1_b UNION ALL
+ SELECT e, f FROM t1_c;
+ CREATE VIEW t3 AS
+ SELECT * FROM t3_a
+ UNION ALL
+ SELECT * FROM t3_b;
+ CREATE TRIGGER t3_insert INSTEAD OF INSERT ON t3 BEGIN
+ INSERT INTO t3_a SELECT new.k, new.v WHERE (new.k%2)==0;
+ INSERT INTO t3_b SELECT new.k, new.v WHERE (new.k%2)==1;
+ END;
+} {}
+do_execsql_test 5.10 {
+ SELECT *, '+' FROM t1 LEFT JOIN t2 ON (a NOT IN(SELECT v FROM t1, t3 WHERE a=k)=NOT EXISTS(SELECT 1 FROM t1 LEFT JOIN t3 ON (a=k)));
+} {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +}
+do_execsql_test 5.20 {
+ SELECT *, '+' FROM t1 LEFT JOIN t3 ON (a NOT IN(SELECT v FROM t1 LEFT JOIN t2 ON (a=k))=k);
+} {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +}
+
+reset_db
+do_execsql_test 6.0 {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES(3,4);
+
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 VALUES(5,6);
+ CREATE TABLE t4(a,b);
+ INSERT INTO t4 VALUES(7,8);
+
+ CREATE TABLE t5(a,b);
+ INSERT INTO t5 VALUES(9,10);
+}
+
+do_execsql_test 6.1 {
+ WITH x(c) AS (
+ SELECT 1000 FROM t1 UNION ALL SELECT 800 FROM t2
+ ),
+ y(d) AS (
+ SELECT 100 FROM t3 UNION ALL SELECT 400 FROM t4
+ )
+ SELECT * FROM t5, x, y;
+} {
+ 9 10 1000 100 9 10 1000 400
+ 9 10 800 100 9 10 800 400
+}
+
+finish_test
diff --git a/test/unionall2.test b/test/unionall2.test
new file mode 100644
index 0000000000..265804291b
--- /dev/null
+++ b/test/unionall2.test
@@ -0,0 +1,39 @@
+# 2020-12-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is flattening UNION ALL sub-queries.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix unionall2
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+
+ CREATE VIEW v1 AS SELECT * FROM t1, t2;
+ CREATE VIEW v2 AS SELECT * FROM t1, t2;
+
+ CREATE VIEW vA AS
+ SELECT * FROM v1, (
+ SELECT * FROM t1 LEFT JOIN t2 ON (a=c)
+ )
+ UNION ALL
+ SELECT * FROM v1, v2
+}
+
+do_execsql_test 1.1 {
+ SELECT 1 FROM vA, vA, vA, vA, vA, vA, vA, vA, vA, vA
+}
+
+
+finish_test
diff --git a/test/unionallfault.test b/test/unionallfault.test
new file mode 100644
index 0000000000..c78abe548c
--- /dev/null
+++ b/test/unionallfault.test
@@ -0,0 +1,36 @@
+# 2020-12-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix unionallfault
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x,y,z);
+ CREATE TABLE t3(x,y,z);
+}
+faultsim_save_and_close
+
+
+do_faultsim_test 1 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ SELECT * FROM t1, (
+ SELECT x FROM t1 UNION ALL SELECT y FROM t1
+ ), t3
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+finish_test
diff --git a/test/whereL.test b/test/whereL.test
index fbb424e919..c78ae160b3 100644
--- a/test/whereL.test
+++ b/test/whereL.test
@@ -26,14 +26,13 @@ do_eqp_test 110 {
SELECT * FROM t1, v4 WHERE t1.a=?1 AND v4.a=t1.a;
} {
QUERY PLAN
- |--MATERIALIZE xxxxxx
- | `--COMPOUND QUERY
- | |--LEFT-MOST SUBQUERY
- | | `--SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
- | `--UNION ALL
- | `--SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (a=?)
- |--SCAN SUBQUERY xxxxxx
- `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | |--SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
+ | `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
+ `--UNION ALL
+ |--SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (a=?)
+ `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
}
# The scan of the t1 table goes first since that enables the ORDER BY
diff --git a/test/wherelimit.test b/test/wherelimit.test
index 8db7a0cc28..aeadc6f39b 100644
--- a/test/wherelimit.test
+++ b/test/wherelimit.test
@@ -94,21 +94,31 @@ ifcapable {update_delete_limit} {
execsql {DELETE FROM t1 ORDER BY x LIMIT 5}
execsql {SELECT count(*) FROM t1}
} {15}
+ create_test_data 4
+ do_test wherelimit-1.3b {
+ # limit 5
+ execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x, y LIMIT 5}
+ } {1 1 | 1 2 | 1 3 | 1 4 | 2 1 |}
+ do_test wherelimit-1.3c {
+ execsql {SELECT count(*) FROM t1}
+ } {11}
do_test wherelimit-1.4 {
# limit 5, offset 2
- execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET 2}
+ execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x LIMIT 5 OFFSET 2}
+ } {2 4 | 3 1 | 3 2 | 3 3 | 3 4 |}
+ do_test wherelimit-1.4cnt {
execsql {SELECT count(*) FROM t1}
- } {10}
+ } {6}
do_test wherelimit-1.5 {
# limit 5, offset -2
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET -2}
execsql {SELECT count(*) FROM t1}
- } {5}
+ } {1}
do_test wherelimit-1.6 {
# limit -5 (no limit), offset 2
execsql {DELETE FROM t1 ORDER BY x LIMIT 2, -5}
execsql {SELECT count(*) FROM t1}
- } {2}
+ } {1}
do_test wherelimit-1.7 {
# limit 5, offset -2 (no offset)
execsql {DELETE FROM t1 ORDER BY x LIMIT -2, 5}
@@ -227,7 +237,9 @@ ifcapable {update_delete_limit} {
} {11}
create_test_data 6
do_test wherelimit-3.2 {
- execsql {UPDATE t1 SET y=1 WHERE x=1 LIMIT 5}
+ execsql {UPDATE t1 SET y=1 WHERE x=1 RETURNING x, old.y, '|' LIMIT 5}
+ } {1 1 | 1 2 | 1 3 | 1 4 | 1 5 |}
+ do_test wherelimit-3.2cnt {
execsql {SELECT count(*) FROM t1 WHERE y=1}
} {10}
do_test wherelimit-3.3 {
diff --git a/test/windowfault.test b/test/windowfault.test
index aea2340215..27c7742b30 100644
--- a/test/windowfault.test
+++ b/test/windowfault.test
@@ -246,6 +246,7 @@ do_faultsim_test 10 -faults oom* -prep {
faultsim_test_result {0 {}}
}
+#-------------------------------------------------------------------------
reset_db
do_execsql_test 11.0 {
DROP TABLE IF EXISTS t0;
@@ -253,7 +254,7 @@ do_execsql_test 11.0 {
INSERT INTO t0 VALUES(0);
} {}
-do_faultsim_test 11 -faults oom* -prep {
+do_faultsim_test 11.1 -faults oom* -prep {
} -body {
execsql {
SELECT * FROM t0 WHERE
@@ -263,7 +264,7 @@ do_faultsim_test 11 -faults oom* -prep {
faultsim_test_result {0 {}}
}
-do_faultsim_test 11 -faults oom* -prep {
+do_faultsim_test 11.2 -faults oom* -prep {
} -body {
execsql {
VALUES(false),(current_date collate binary)
@@ -274,4 +275,21 @@ do_faultsim_test 11 -faults oom* -prep {
faultsim_test_result {0 {}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 12.0 {
+ CREATE TABLE t1(a, b, c);
+} {}
+do_faultsim_test 12 -faults oom* -prep {
+} -body {
+ execsql {
+ WITH v(a, b, row_number) AS (
+ SELECT a, b, row_number() OVER (PARTITION BY a ORDER BY b) FROM t1
+ )
+ SELECT * FROM v WHERE a=2
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
finish_test
diff --git a/test/windowpushd.test b/test/windowpushd.test
new file mode 100644
index 0000000000..4462a0b4b4
--- /dev/null
+++ b/test/windowpushd.test
@@ -0,0 +1,237 @@
+# 2021 February 23
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is testing the push-down optimization when
+# WHERE constraints are pushed down into a sub-query that uses
+# window functions.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix windowpushd
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(id INTEGER PRIMARY KEY, grp_id);
+ CREATE INDEX i1 ON t1(grp_id);
+ CREATE VIEW lll AS SELECT
+ row_number() OVER (PARTITION BY grp_id),
+ grp_id, id
+ FROM t1
+}
+
+do_execsql_test 1.1 {
+ INSERT INTO t1 VALUES
+ (1, 2), (2, 3), (3, 3), (4, 1), (5, 1),
+ (6, 1), (7, 1), (8, 1), (9, 3), (10, 3),
+ (11, 2), (12, 3), (13, 3), (14, 2), (15, 1),
+ (16, 2), (17, 1), (18, 2), (19, 3), (20, 2)
+}
+
+do_execsql_test 1.2 {
+ SELECT * FROM lll
+} {
+ 1 1 4 2 1 5 3 1 6 4 1 7 5 1 8 6 1 15 7 1 17
+ 1 2 1 2 2 11 3 2 14 4 2 16 5 2 18 6 2 20
+ 1 3 2 2 3 3 3 3 9 4 3 10 5 3 12 6 3 13 7 3 19
+}
+
+do_execsql_test 1.3 {
+ SELECT * FROM lll WHERE grp_id=2
+} {
+ 1 2 1 2 2 11 3 2 14 4 2 16 5 2 18 6 2 20
+}
+
+do_eqp_test 1.4 {
+ SELECT * FROM lll WHERE grp_id=2
+} {SEARCH TABLE t1 USING COVERING INDEX i1 (grp_id=?)}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a, b, c, d);
+ INSERT INTO t1 VALUES('A', 'C', 1, 0.1);
+ INSERT INTO t1 VALUES('A', 'D', 2, 0.2);
+ INSERT INTO t1 VALUES('A', 'E', 3, 0.3);
+ INSERT INTO t1 VALUES('A', 'C', 4, 0.4);
+ INSERT INTO t1 VALUES('B', 'D', 5, 0.5);
+ INSERT INTO t1 VALUES('B', 'E', 6, 0.6);
+ INSERT INTO t1 VALUES('B', 'C', 7, 0.7);
+ INSERT INTO t1 VALUES('B', 'D', 8, 0.8);
+ INSERT INTO t1 VALUES('C', 'E', 9, 0.9);
+ INSERT INTO t1 VALUES('C', 'C', 10, 1.0);
+ INSERT INTO t1 VALUES('C', 'D', 11, 1.1);
+ INSERT INTO t1 VALUES('C', 'E', 12, 1.2);
+
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+
+ CREATE VIEW v1 AS SELECT a, c, max(c) OVER (PARTITION BY a) FROM t1;
+
+ CREATE VIEW v2 AS SELECT a, c,
+ max(c) OVER (PARTITION BY a),
+ row_number() OVER ()
+ FROM t1;
+
+ CREATE VIEW v3 AS SELECT b, d,
+ max(d) OVER (PARTITION BY b),
+ row_number() OVER (PARTITION BY b)
+ FROM t1;
+
+ CREATE TABLE t2(x, y, z);
+ INSERT INTO t2 VALUES('W', 3, 1);
+ INSERT INTO t2 VALUES('W', 2, 2);
+ INSERT INTO t2 VALUES('X', 1, 4);
+ INSERT INTO t2 VALUES('X', 5, 7);
+ INSERT INTO t2 VALUES('Y', 1, 9);
+ INSERT INTO t2 VALUES('Y', 4, 2);
+ INSERT INTO t2 VALUES('Z', 3, 3);
+ INSERT INTO t2 VALUES('Z', 3, 4);
+}
+
+foreach tn {0 1} {
+ optimization_control db push-down $tn
+
+ do_execsql_test 2.$tn.1.1 {
+ SELECT * FROM v1;
+ } {
+ A 1 4 A 2 4 A 3 4 A 4 4
+ B 5 8 B 6 8 B 7 8 B 8 8
+ C 9 12 C 10 12 C 11 12 C 12 12
+ }
+
+ do_execsql_test 2.$tn.1.2 {
+ SELECT * FROM v1 WHERE a IN ('A', 'B');
+ } {
+ A 1 4 A 2 4 A 3 4 A 4 4
+ B 5 8 B 6 8 B 7 8 B 8 8
+ }
+
+ do_execsql_test 2.$tn.1.3 {
+ SELECT * FROM v1 WHERE a IS 'C'
+ } {
+ C 9 12 C 10 12 C 11 12 C 12 12
+ }
+
+ if {$tn==1} {
+ do_eqp_test 2.$tn.1.4 {
+ SELECT * FROM v1 WHERE a IN ('A', 'B');
+ } {USING INDEX i1 (a=?)}
+
+ do_eqp_test 2.$tn.1.5 {
+ SELECT * FROM v1 WHERE a = 'c' COLLATE nocase
+ } {USING INDEX i1}
+ }
+
+ do_execsql_test 2.$tn.2.1 {
+ SELECT * FROM v2;
+ } {
+ A 1 4 1 A 2 4 2 A 3 4 3 A 4 4 4
+ B 5 8 5 B 6 8 6 B 7 8 7 B 8 8 8
+ C 9 12 9 C 10 12 10 C 11 12 11 C 12 12 12
+ }
+
+ do_execsql_test 2.$tn.2.2 {
+ SELECT * FROM v2 WHERE a = 'C';
+ } {
+ C 9 12 9 C 10 12 10 C 11 12 11 C 12 12 12
+ }
+
+ do_execsql_test 2.$tn.3.1 { SELECT * FROM v3; } {
+ C 0.1 1.0 1 C 0.4 1.0 2 C 0.7 1.0 3 C 1.0 1.0 4
+ D 0.2 1.1 1 D 0.5 1.1 2 D 0.8 1.1 3 D 1.1 1.1 4
+ E 0.3 1.2 1 E 0.6 1.2 2 E 0.9 1.2 3 E 1.2 1.2 4
+ }
+
+ do_execsql_test 2.$tn.3.2 { SELECT * FROM v3 WHERE b<'E' } {
+ C 0.1 1.0 1 C 0.4 1.0 2 C 0.7 1.0 3 C 1.0 1.0 4
+ D 0.2 1.1 1 D 0.5 1.1 2 D 0.8 1.1 3 D 1.1 1.1 4
+ }
+
+ if {$tn==1} {
+ do_eqp_test 2.$tn.3.3 {
+ SELECT * FROM v3 WHERE b='E'
+ } {SEARCH TABLE t1 USING INDEX i2 (b=?)}
+ do_eqp_test 2.$tn.3.4 {
+ SELECT * FROM v3 WHERE b>'C'
+ } {SEARCH TABLE t1 USING INDEX i2 (b>?)}
+ }
+
+ do_execsql_test 2.$tn.3.5 { SELECT * FROM v3 WHERE d<0.55; } {
+ C 0.1 1.0 1 C 0.4 1.0 2
+ D 0.2 1.1 1 D 0.5 1.1 2
+ E 0.3 1.2 1
+ }
+ if {$tn==1} {
+ do_eqp_test 2.$tn.3.6 {
+ SELECT * FROM v3 WHERE d<0.55
+ } {SCAN TABLE t1 USING INDEX i2}
+ }
+
+ do_execsql_test 2.$tn.4.1 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m
+ FROM t2 GROUP BY x
+ )
+ } {
+ W 5 2
+ X 6 7
+ Y 5 9
+ Z 6 4
+ }
+
+ do_execsql_test 2.$tn.4.1 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m,
+ max( max(z) ) OVER (PARTITION BY sum(y)
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ )
+ FROM t2 GROUP BY x
+ )
+ } {
+ W 5 2 9
+ Y 5 9 9
+ X 6 7 7
+ Z 6 4 7
+ }
+
+ do_execsql_test 2.$tn.4.2 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m,
+ max( max(z) ) OVER (PARTITION BY sum(y)
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ )
+ FROM t2 GROUP BY x
+ ) WHERE s=6
+ } {
+ X 6 7 7
+ Z 6 4 7
+ }
+
+ do_execsql_test 2.$tn.4.3 {
+ SELECT * FROM (
+ SELECT x, sum(y) AS s, max(z) AS m,
+ max( max(z) ) OVER (PARTITION BY sum(y)
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ )
+ FROM t2 GROUP BY x
+ ) WHERE s<6
+ } {
+ W 5 2 9
+ Y 5 9 9
+ }
+
+}
+
+
+
+
+finish_test
+
diff --git a/test/with3.test b/test/with3.test
index a574263b89..3325ecc93f 100644
--- a/test/with3.test
+++ b/test/with3.test
@@ -198,4 +198,33 @@ do_execsql_test 4.2 {
GROUP BY 1;
} {elvis}
+# 2021-02-13
+# Avoid manifesting the same CTE multiple times.
+#
+do_eqp_test 5.1 {
+ WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<1)
+ SELECT x1.x||x2.x||x3.x||x4.x FROM c AS x1, c AS x2, c AS x3, c AS x4
+ ORDER BY 1;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--SETUP
+ | | `--SCAN CONSTANT ROW
+ | `--RECURSIVE STEP
+ | `--SCAN TABLE c
+ |--SCAN SUBQUERY xxxxxx AS x1
+ |--SCAN SUBQUERY xxxxxx AS x2
+ |--SCAN SUBQUERY xxxxxx AS x3
+ |--SCAN SUBQUERY xxxxxx AS x4
+ `--USE TEMP B-TREE FOR ORDER BY
+}
+do_execsql_test 5.2 {
+ WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<1)
+ SELECT x1.x||x2.x||x3.x||x4.x FROM c AS x1, c AS x2, c AS x3, c AS x4
+ ORDER BY 1;
+} {0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111}
+
+
+
+
finish_test
diff --git a/test/with6.test b/test/with6.test
new file mode 100644
index 0000000000..ef63ec8bee
--- /dev/null
+++ b/test/with6.test
@@ -0,0 +1,255 @@
+# 2021-02-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this file is the MATERIALIZED hint to common table expressions
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix with6
+
+ifcapable {!cte} {
+ finish_test
+ return
+}
+
+do_execsql_test 100 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 101 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 110 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 111 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+# Even though the CTE is not materialized, the self-join optimization
+# kicks in and does the materialization for us.
+#
+do_execsql_test 120 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 121 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x FROM c c1, c c2, c c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 130 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 5) AS c2,
+ (SELECT x FROM c LIMIT 5) AS c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 131 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 5) AS c2,
+ (SELECT x FROM c LIMIT 5) AS c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--CO-ROUTINE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | |--CO-ROUTINE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | |--CO-ROUTINE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+# The (SELECT x FROM c LIMIT N) subqueries get materialized once each.
+# Show multiple materializations are shown. But there is only one
+# materialization for c, shown by the "SCAN 2 CONSTANT ROWS" line.
+#
+do_execsql_test 140 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 141 {
+ WITH c(x) AS MATERIALIZED (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--MATERIALIZE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 150 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {000 001 010 011 100 101 110 111}
+do_eqp_test 151 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c1.x||c2.x||c3.x
+ FROM (SELECT x FROM c LIMIT 5) AS c1,
+ (SELECT x FROM c LIMIT 6) AS c2,
+ (SELECT x FROM c LIMIT 7) AS c3;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--MATERIALIZE xxxxxx
+ | | `--SCAN 2 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--SCAN SUBQUERY xxxxxx AS c3
+}
+
+do_execsql_test 160 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {100 301}
+do_eqp_test 161 {
+ WITH c(x) AS (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--CORRELATED SCALAR SUBQUERY xxxxxx
+ `--SCAN SUBQUERY xxxxxx
+}
+
+do_execsql_test 170 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {100 301}
+do_eqp_test 171 {
+ WITH c(x) AS NOT MATERIALIZED (VALUES(0),(1))
+ SELECT c2.x + 100*(SELECT sum(x+1) FROM c WHERE c.x<=c2.x)
+ FROM c AS c2 WHERE c2.x<10;
+} {
+ QUERY PLAN
+ |--CO-ROUTINE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ |--SCAN SUBQUERY xxxxxx AS c2
+ `--CORRELATED SCALAR SUBQUERY xxxxxx
+ |--CO-ROUTINE xxxxxx
+ | `--SCAN 2 CONSTANT ROWS
+ `--SCAN SUBQUERY xxxxxx
+}
+
+
+do_execsql_test 200 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1(x) VALUES(4);
+ CREATE VIEW t2(y) AS
+ WITH c(z) AS (VALUES(4),(5),(6))
+ SELECT c1.z+c2.z*100+t1.x*10000
+ FROM t1,
+ (SELECT z FROM c LIMIT 5) AS c1,
+ (SELECT z FROM c LIMIT 5) AS c2;
+ SELECT y FROM t2 ORDER BY y;
+} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
+do_execsql_test 210 {
+ DROP VIEW t2;
+ CREATE VIEW t2(y) AS
+ WITH c(z) AS NOT MATERIALIZED (VALUES(4),(5),(6))
+ SELECT c1.z+c2.z*100+t1.x*10000
+ FROM t1,
+ (SELECT z FROM c LIMIT 5) AS c1,
+ (SELECT z FROM c LIMIT 5) AS c2;
+ SELECT y FROM t2 ORDER BY y;
+} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
+do_eqp_test 211 {
+ SELECT y FROM t2 ORDER BY y;
+} {
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--MATERIALIZE xxxxxx
+ | | `--SCAN 3 CONSTANT ROWS
+ | `--SCAN SUBQUERY xxxxxx
+ |--MATERIALIZE xxxxxx
+ | `--SCAN SUBQUERY xxxxxx
+ |--SCAN SUBQUERY xxxxxx AS c1
+ |--SCAN SUBQUERY xxxxxx AS c2
+ |--SCAN TABLE t1
+ `--USE TEMP B-TREE FOR ORDER BY
+}
+do_execsql_test 220 {
+ DROP VIEW t2;
+ CREATE VIEW t2(y) AS
+ WITH c(z) AS MATERIALIZED (VALUES(4),(5),(6))
+ SELECT c1.z+c2.z*100+t1.x*10000
+ FROM t1,
+ (SELECT z FROM c LIMIT 5) AS c1,
+ (SELECT z FROM c LIMIT 5) AS c2;
+ SELECT y FROM t2 ORDER BY y;
+} {40404 40405 40406 40504 40505 40506 40604 40605 40606}
+
+
+
+finish_test
diff --git a/tool/lemon.c b/tool/lemon.c
index 54c8946a0d..06ba1be2bd 100644
--- a/tool/lemon.c
+++ b/tool/lemon.c
@@ -401,7 +401,7 @@ struct lemon {
struct symbol *errsym; /* The error symbol */
struct symbol *wildcard; /* Token that matches anything */
char *name; /* Name of the generated parser */
- char *arg; /* Declaration of the 3th argument to parser */
+ char *arg; /* Declaration of the 3rd argument to parser */
char *ctx; /* Declaration of 2nd argument to constructor */
char *tokentype; /* Type of terminal symbols in the parser stack */
char *vartype; /* The default type of non-terminal symbols */
@@ -1027,7 +1027,7 @@ PRIVATE void buildshifts(struct lemon *lemp, struct state *stp)
struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
struct state *newstp; /* A pointer to a successor state */
- /* Each configuration becomes complete after it contibutes to a successor
+ /* Each configuration becomes complete after it contributes to a successor
** state. Initially, all configurations are incomplete */
for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE;
@@ -1887,7 +1887,7 @@ static char *merge(
**
** Return Value:
** A pointer to the head of a sorted list containing the elements
-** orginally in list.
+** originally in list.
**
** Side effects:
** The "next" pointers for elements in list are changed.
@@ -3513,7 +3513,7 @@ void ReportOutput(struct lemon *lemp)
}
/* Search for the file "name" which is in the same directory as
-** the exacutable */
+** the executable */
PRIVATE char *pathsearch(char *argv0, char *name, int modemask)
{
const char *pathlist;
@@ -3868,7 +3868,7 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){
lhsdirect = 1;
}else if( rp->rhsalias[0]==0 ){
/* The left-most RHS symbol has no value. LHS direct is ok. But
- ** we have to call the distructor on the RHS symbol first. */
+ ** we have to call the destructor on the RHS symbol first. */
lhsdirect = 1;
if( has_destructor(rp->rhs[0],lemp) ){
append_str(0,0,0,0);
@@ -4849,7 +4849,7 @@ void ReportTable(
** yyRuleInfoNRhs[].
**
** Note: This code depends on the fact that rules are number
- ** sequentually beginning with 0.
+ ** sequentially beginning with 0.
*/
for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){
fprintf(out," %4d, /* (%d) ", rp->lhs->index, i);
diff --git a/tool/lempar.c b/tool/lempar.c
index 71a51cf448..35c3768bb9 100644
--- a/tool/lempar.c
+++ b/tool/lempar.c
@@ -718,55 +718,6 @@ static YYACTIONTYPE yy_reduce(
(void)yyLookahead;
(void)yyLookaheadToken;
yymsp = yypParser->yytos;
- assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
-#ifndef NDEBUG
- if( yyTraceFILE ){
- yysize = yyRuleInfoNRhs[yyruleno];
- if( yysize ){
- fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
- yyTracePrompt,
- yyruleno, yyRuleName[yyruleno],
- yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){
- yypParser->yyhwm++;
- assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
- }
-#endif
-#if YYSTACKDEPTH>0
- if( yypParser->yytos>=yypParser->yystackEnd ){
- yyStackOverflow(yypParser);
- /* The call to yyStackOverflow() above pops the stack until it is
- ** empty, causing the main parser loop to exit. So the return value
- ** is never used and does not matter. */
- return 0;
- }
-#else
- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
- if( yyGrowStack(yypParser) ){
- yyStackOverflow(yypParser);
- /* The call to yyStackOverflow() above pops the stack until it is
- ** empty, causing the main parser loop to exit. So the return value
- ** is never used and does not matter. */
- return 0;
- }
- yymsp = yypParser->yytos;
- }
-#endif
- }
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
@@ -925,12 +876,56 @@ void Parse(
}
#endif
- do{
+ while(1){ /* Exit by "break" */
+ assert( yypParser->yytos>=yypParser->yystack );
assert( yyact==yypParser->yytos->stateno );
yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
if( yyact >= YY_MIN_REDUCE ){
- yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,
- yyminor ParseCTX_PARAM);
+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ int yysize = yyRuleInfoNRhs[yyruleno];
+ if( yysize ){
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
+ yyTracePrompt,
+ yyruleno, yyRuleName[yyruleno],
+ yyrulenoyytos[yysize].stateno);
+ }else{
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
+ yyTracePrompt, yyruleno, yyRuleName[yyruleno],
+ yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){
+ yypParser->yyhwm++;
+ assert( yypParser->yyhwm ==
+ (int)(yypParser->yytos - yypParser->yystack));
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yytos>=yypParser->yystackEnd ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+#else
+ if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
+ if( yyGrowStack(yypParser) ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+ }
+#endif
+ }
+ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
@@ -1043,7 +1038,7 @@ void Parse(
break;
#endif
}
- }while( yypParser->yytos>yypParser->yystack );
+ }
#ifndef NDEBUG
if( yyTraceFILE ){
yyStackEntry *i;
diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c
index f8537a85bd..bbb0ccf293 100644
--- a/tool/mkkeywordhash.c
+++ b/tool/mkkeywordhash.c
@@ -155,10 +155,16 @@ struct Keyword {
# define WINDOWFUNC 0x00100000
#endif
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
-# define GENCOL 0
+# define GENCOL 0
#else
-# define GENCOL 0x00200000
+# define GENCOL 0x00200000
#endif
+#ifdef SQLITE_OMIT_RETURNING
+# define RETURNING 0
+#else
+# define RETURNING 0x00400000
+#endif
+
/*
** These are the keywords
@@ -223,7 +229,7 @@ static Keyword aKeywordTable[] = {
{ "FOREIGN", "TK_FOREIGN", FKEY, 1 },
{ "FROM", "TK_FROM", ALWAYS, 10 },
{ "FULL", "TK_JOIN_KW", ALWAYS, 3 },
- { "GENERATED", "TK_GENERATED", GENCOL, 1 },
+ { "GENERATED", "TK_GENERATED", ALWAYS, 1 },
{ "GLOB", "TK_LIKE_KW", ALWAYS, 3 },
{ "GROUP", "TK_GROUP", ALWAYS, 5 },
{ "GROUPS", "TK_GROUPS", WINDOWFUNC, 2 },
@@ -249,6 +255,7 @@ static Keyword aKeywordTable[] = {
{ "LIKE", "TK_LIKE_KW", ALWAYS, 5 },
{ "LIMIT", "TK_LIMIT", ALWAYS, 3 },
{ "MATCH", "TK_MATCH", ALWAYS, 2 },
+ { "MATERIALIZED", "TK_MATERIALIZED", CTE, 12 },
{ "NATURAL", "TK_JOIN_KW", ALWAYS, 3 },
{ "NO", "TK_NO", FKEY|WINDOWFUNC, 2 },
{ "NOT", "TK_NOT", ALWAYS, 10 },
@@ -280,6 +287,7 @@ static Keyword aKeywordTable[] = {
{ "RENAME", "TK_RENAME", ALTER, 1 },
{ "REPLACE", "TK_REPLACE", CONFLICT, 10 },
{ "RESTRICT", "TK_RESTRICT", FKEY, 1 },
+ { "RETURNING", "TK_RETURNING", RETURNING, 10 },
{ "RIGHT", "TK_JOIN_KW", ALWAYS, 0 },
{ "ROLLBACK", "TK_ROLLBACK", ALWAYS, 1 },
{ "ROW", "TK_ROW", TRIGGER, 1 },
|