Merge updates from trunk.
FossilOrigin-Name: 6b85f8cd4d87d616a3cb362647ae3869c45c2801
This commit is contained in:
+16
-8
@@ -538,7 +538,14 @@ TESTPROGS = \
|
||||
FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata1.db \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db
|
||||
$(TOP)/test/fuzzdata3.db \
|
||||
$(TOP)/test/fuzzdata4.db
|
||||
|
||||
# Extra arguments for including json1 in the build of tools
|
||||
#
|
||||
JSON1_DEP = $(TOP)/ext/misc/json1.c sqlite3ext.h
|
||||
JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE
|
||||
JSON1_SRC = $(TOP)/ext/misc/json1.c
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
@@ -566,19 +573,20 @@ libtclsqlite3.la: tclsqlite.lo libsqlite3.la
|
||||
-version-info "8:6:8" \
|
||||
-avoid-version
|
||||
|
||||
sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h $(TOP)/ext/misc/json1.c
|
||||
$(LTLINK) $(READLINE_FLAGS) -DSQLITE_ENABLE_JSON1 -o $@ \
|
||||
$(TOP)/src/shell.c $(TOP)/ext/misc/json1.c libsqlite3.la \
|
||||
sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h $(JSON1_DEP)
|
||||
$(LTLINK) $(READLINE_FLAGS) $(JSON1_OPT) -o $@ \
|
||||
$(TOP)/src/shell.c $(JSON1_SRC) libsqlite3.la \
|
||||
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
|
||||
|
||||
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
|
||||
|
||||
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
|
||||
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP)
|
||||
$(LTLINK) -o $@ $(JSON1_OPT) \
|
||||
$(TOP)/tool/fuzzershell.c $(JSON1_SRC) sqlite3.c $(TLIBS)
|
||||
|
||||
fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS)
|
||||
fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP)
|
||||
$(LTLINK) -o $@ $(JSON1_OPT) $(TOP)/test/fuzzcheck.c $(JSON1_SRC) sqlite3.c $(TLIBS)
|
||||
|
||||
mptester$(TEXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
|
||||
|
||||
+19
-9
@@ -1207,7 +1207,14 @@ TESTPROGS = \
|
||||
FUZZDATA = \
|
||||
$(TOP)\test\fuzzdata1.db \
|
||||
$(TOP)\test\fuzzdata2.db \
|
||||
$(TOP)\test\fuzzdata3.db
|
||||
$(TOP)\test\fuzzdata3.db \
|
||||
$(TOP)\test\fuzzdata4.db
|
||||
|
||||
# Extra arguments for including json1 in the build of tools
|
||||
#
|
||||
JSON1_DEP = sqlite3ext.h $(TOP)\ext\misc\json1.c
|
||||
JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE
|
||||
JSON1_SRC = $(TOP)\ext\misc\json1.c
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
@@ -1224,18 +1231,19 @@ libsqlite3.lib: $(LIBOBJ)
|
||||
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
|
||||
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
|
||||
|
||||
sqlite3.exe: $(TOP)\src\shell.c $(TOP)\ext\misc\json1.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
|
||||
$(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c $(TOP)\ext\misc\json1.c \
|
||||
sqlite3.exe: $(TOP)\src\shell.c $(JSON1_DEP) $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
|
||||
$(LTLINK) $(SHELL_COMPILE_OPTS) $(JSON1_OPT) $(READLINE_FLAGS) $(TOP)\src\shell.c $(JSON1_SRC) \
|
||||
/link /pdb:sqlite3sh.pdb $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c sqlite3.c
|
||||
|
||||
fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\fuzzershell.c sqlite3.c
|
||||
fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP)
|
||||
$(LTLINK) $(NO_WARN) $(JSON1_OPT) \
|
||||
$(TOP)\tool\fuzzershell.c $(JSON1_SRC) sqlite3.c
|
||||
|
||||
fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\test\fuzzcheck.c sqlite3.c
|
||||
fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP)
|
||||
$(LTLINK) $(NO_WARN) $(JSON1_OPT) $(TOP)\test\fuzzcheck.c $(JSON1_SRC) sqlite3.c
|
||||
|
||||
mptester.exe: $(TOP)\mptest\mptest.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
|
||||
$(LTLINK) $(NO_WARN) $(SHELL_COMPILE_OPTS) $(TOP)\mptest\mptest.c \
|
||||
@@ -1274,10 +1282,9 @@ mptest: mptester.exe
|
||||
move vdbe.new tsrc\vdbe.c
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
|
||||
sqlite3.c: .target_source sqlite3ext.h $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
|
||||
copy tsrc\shell.c .
|
||||
copy tsrc\sqlite3ext.h .
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
|
||||
@@ -1585,6 +1592,9 @@ parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk
|
||||
sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h
|
||||
|
||||
sqlite3ext.h: .target_source
|
||||
copy tsrc\sqlite3ext.h .
|
||||
|
||||
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
|
||||
$(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) \
|
||||
$(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS)
|
||||
|
||||
+1
-1
@@ -384,7 +384,7 @@ struct Fts5ExtensionApi {
|
||||
** FTS index corresponding to both forms of the first token.
|
||||
** </ol>
|
||||
**
|
||||
** Whether is is parsing document or query text, any call to xToken that
|
||||
** Whether it is parsing document or query text, any call to xToken that
|
||||
** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
|
||||
** is considered to supply a synonym for the previous token. For example,
|
||||
** when parsing the document "I won first place", a tokenizer that supports
|
||||
|
||||
+18
-25
@@ -1427,7 +1427,6 @@ static void fts5SegIterNextPage(
|
||||
fts5DataRelease(pIter->pLeaf);
|
||||
pIter->iLeafPgno++;
|
||||
if( pIter->pNextLeaf ){
|
||||
assert( pIter->iLeafPgno<=pSeg->pgnoLast );
|
||||
pIter->pLeaf = pIter->pNextLeaf;
|
||||
pIter->pNextLeaf = 0;
|
||||
}else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
|
||||
@@ -1658,11 +1657,14 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
pIter->pSeg->iSegid, pIter->iLeafPgno
|
||||
));
|
||||
if( pNew ){
|
||||
if( pIter->iLeafPgno==pIter->iTermLeafPgno ){
|
||||
if( pIter->iTermLeafOffset<pNew->szLeaf ){
|
||||
pIter->pLeaf = pNew;
|
||||
pIter->iLeafOffset = pIter->iTermLeafOffset;
|
||||
}
|
||||
/* iTermLeafOffset may be equal to szLeaf if the term is the last
|
||||
** thing on the page - i.e. the first rowid is on the following page.
|
||||
** In this case leaf pIter->pLeaf==0, this iterator is at EOF. */
|
||||
if( pIter->iLeafPgno==pIter->iTermLeafPgno
|
||||
&& pIter->iTermLeafOffset<pNew->szLeaf
|
||||
){
|
||||
pIter->pLeaf = pNew;
|
||||
pIter->iLeafOffset = pIter->iTermLeafOffset;
|
||||
}else{
|
||||
int iRowidOff;
|
||||
iRowidOff = fts5LeafFirstRowidOff(pNew);
|
||||
@@ -3168,7 +3170,7 @@ static void fts5WriteAppendTerm(
|
||||
Fts5PageWriter *pPage = &pWriter->writer;
|
||||
Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
|
||||
|
||||
if( p->rc ) return;
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( pPage->buf.n>=4 );
|
||||
assert( pPage->buf.n>4 || pWriter->bFirstTermInPage );
|
||||
|
||||
@@ -3315,16 +3317,12 @@ static void fts5WriteFinish(
|
||||
int i;
|
||||
Fts5PageWriter *pLeaf = &pWriter->writer;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pLeaf->pgno==1 && pLeaf->buf.n==0 ){
|
||||
*pnLeaf = 0;
|
||||
}else{
|
||||
if( pLeaf->buf.n>4 ){
|
||||
fts5WriteFlushLeaf(p, pWriter);
|
||||
}
|
||||
*pnLeaf = pLeaf->pgno-1;
|
||||
|
||||
fts5WriteFlushBtree(p, pWriter);
|
||||
assert( pLeaf->pgno>=1 );
|
||||
if( pLeaf->buf.n>4 ){
|
||||
fts5WriteFlushLeaf(p, pWriter);
|
||||
}
|
||||
*pnLeaf = pLeaf->pgno-1;
|
||||
fts5WriteFlushBtree(p, pWriter);
|
||||
}
|
||||
fts5BufferFree(&pLeaf->term);
|
||||
fts5BufferFree(&pLeaf->buf);
|
||||
@@ -3763,9 +3761,8 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
|
||||
fts5WriteAppendTerm(p, &writer, strlen(zTerm), (const u8*)zTerm);
|
||||
|
||||
if( writer.bFirstRowidInPage==0
|
||||
&& pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1)
|
||||
){
|
||||
assert( writer.bFirstRowidInPage==0 );
|
||||
if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
|
||||
/* The entire doclist will fit on the current leaf. */
|
||||
fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
|
||||
}else{
|
||||
@@ -3773,8 +3770,6 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
i64 iDelta = 0;
|
||||
int iOff = 0;
|
||||
|
||||
/* writer.bFirstRowidInPage = 0; */
|
||||
|
||||
/* The entire doclist will not fit on this leaf. The following
|
||||
** loop iterates through the poslists that make up the current
|
||||
** doclist. */
|
||||
@@ -4155,7 +4150,7 @@ static void fts5SetupPrefixIter(
|
||||
i64 iRowid = fts5MultiIterRowid(p1);
|
||||
int nTerm;
|
||||
const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm);
|
||||
assert( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
|
||||
assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
|
||||
if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
|
||||
|
||||
if( doclist.n>0 && iRowid<=iLastRowid ){
|
||||
@@ -4859,7 +4854,6 @@ static void fts5IndexIntegrityCheckEmpty(
|
||||
if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
if( p->rc ) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4926,7 +4920,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
if( pSeg->pgnoFirst==0 ) return;
|
||||
|
||||
fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
|
||||
"SELECT segid, term, (pgno>>1), (pgno & 1) FROM '%q'.'%q_idx' WHERE segid=%d",
|
||||
"SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d",
|
||||
pConfig->zDb, pConfig->zName, pSeg->iSegid
|
||||
));
|
||||
|
||||
@@ -5290,7 +5284,6 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
|
||||
if( iOff<n ){
|
||||
i64 iDelta;
|
||||
iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDelta);
|
||||
if( iDelta==0 ) return iOff;
|
||||
iDocid += iDelta;
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
|
||||
}
|
||||
|
||||
@@ -1116,6 +1116,7 @@ static int fts5FilterMethod(
|
||||
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
||||
}else if( pMatch ){
|
||||
const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
|
||||
if( zExpr==0 ) zExpr = "";
|
||||
|
||||
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
@@ -24,18 +24,23 @@ ifcapable !fts5 {
|
||||
}
|
||||
sqlite3_fts5_may_be_corrupt 1
|
||||
|
||||
proc create_t1 {} {
|
||||
expr srand(0)
|
||||
db func rnddoc fts5_rnddoc
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
|
||||
WITH ii(i) AS (SELECT 1 UNION SELECT i+1 FROM ii WHERE i<100)
|
||||
INSERT INTO t1 SELECT rnddoc(10) FROM ii;
|
||||
}
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
# Create a simple FTS5 table containing 100 documents. Each document
|
||||
# contains 10 terms, each of which start with the character "x".
|
||||
#
|
||||
expr srand(0)
|
||||
db func rnddoc fts5_rnddoc
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
|
||||
WITH ii(i) AS (SELECT 1 UNION SELECT i+1 FROM ii WHERE i<100)
|
||||
INSERT INTO t1 SELECT rnddoc(10) FROM ii;
|
||||
}
|
||||
set mask [expr 31 << 31]
|
||||
do_test 1.0 { create_t1 } {}
|
||||
|
||||
do_test 1.1 {
|
||||
# Pick out the rowid of the right-most b-tree leaf in the new segment.
|
||||
@@ -75,6 +80,261 @@ do_execsql_test 2.2 {
|
||||
SELECT length(block) FROM t2_data WHERE id=1;
|
||||
} {2}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that missing leaf pages are recognized as corruption.
|
||||
#
|
||||
reset_db
|
||||
do_test 3.0 { create_t1 } {}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
SELECT count(*) FROM t1_data;
|
||||
} {105}
|
||||
|
||||
proc do_3_test {tn} {
|
||||
set i 0
|
||||
foreach ::rowid [db eval "SELECT rowid FROM t1_data WHERE rowid>100"] {
|
||||
incr i
|
||||
do_test $tn.$i {
|
||||
db eval BEGIN
|
||||
db eval {DELETE FROM t1_data WHERE rowid = $::rowid}
|
||||
list [
|
||||
catch { db eval {SELECT rowid FROM t1 WHERE t1 MATCH 'x*'} } msg
|
||||
] $msg
|
||||
} {1 {database disk image is malformed}}
|
||||
catch { db eval ROLLBACK }
|
||||
}
|
||||
}
|
||||
|
||||
do_3_test 3.2
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t1 SELECT x FROM t1;
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
} {}
|
||||
|
||||
do_3_test 3.4
|
||||
|
||||
do_test 3.5 {
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 40);
|
||||
}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set rnd [expr int(rand() * 1000)]
|
||||
set doc [string repeat "x$rnd " [expr int(rand() * 3) + 1]]
|
||||
execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_3_test 3.6
|
||||
|
||||
do_test 3.7 {
|
||||
execsql {
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 40);
|
||||
INSERT INTO t1 SELECT x FROM t1;
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
} {}
|
||||
|
||||
do_3_test 3.8
|
||||
|
||||
do_test 3.9 {
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set rnd [expr int(rand() * 100)]
|
||||
set doc "x[string repeat $rnd 20]"
|
||||
execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_3_test 3.10
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that segments that end unexpectedly are identified as corruption.
|
||||
#
|
||||
reset_db
|
||||
do_test 4.0 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set rnd [expr int(rand() * 100)]
|
||||
set doc "x[string repeat $rnd 20]"
|
||||
execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) }
|
||||
}
|
||||
execsql { INSERT INTO t1(t1) VALUES('optimize') }
|
||||
} {}
|
||||
|
||||
set nErr 0
|
||||
for {set i 1} {1} {incr i} {
|
||||
set struct [db one {SELECT block FROM t1_data WHERE id=10}]
|
||||
binary scan $struct c* var
|
||||
set end [lindex $var end]
|
||||
if {$end<=$i} break
|
||||
lset var end [expr $end - $i]
|
||||
set struct [binary format c* $var]
|
||||
db eval {
|
||||
BEGIN;
|
||||
UPDATE t1_data SET block = $struct WHERE id=10;
|
||||
}
|
||||
do_test 4.1.$i {
|
||||
incr nErr [catch { db eval { SELECT rowid FROM t1 WHERE t1 MATCH 'x*' } }]
|
||||
set {} {}
|
||||
} {}
|
||||
catch { db eval ROLLBACK }
|
||||
}
|
||||
do_test 4.1.x { expr $nErr>45 } 1
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
# The first argument passed to this command must be a binary blob
|
||||
# containing an FTS5 leaf page. This command returns a copy of this
|
||||
# blob, with the pgidx of the leaf page replaced by a single varint
|
||||
# containing value $iVal.
|
||||
#
|
||||
proc rewrite_pgidx {blob iVal} {
|
||||
binary scan $blob SS off1 szLeaf
|
||||
if {$iVal<0 || $iVal>=128} {
|
||||
error "$iVal out of range!"
|
||||
} else {
|
||||
set pgidx [binary format c $iVal]
|
||||
}
|
||||
|
||||
binary format a${szLeaf}a* $blob $pgidx
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
INSERT INTO x1(x1, rank) VALUES('pgsz', 40);
|
||||
BEGIN;
|
||||
INSERT INTO x1 VALUES('xaaa xabb xccc xcdd xeee xeff xggg xghh xiii xijj');
|
||||
INSERT INTO x1 SELECT x FROM x1;
|
||||
INSERT INTO x1 SELECT x FROM x1;
|
||||
INSERT INTO x1 SELECT x FROM x1;
|
||||
INSERT INTO x1 SELECT x FROM x1;
|
||||
INSERT INTO x1(x1) VALUES('optimize');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
#db eval { SELECT fts5_decode(id, block) b from x1_data } { puts $b }
|
||||
#
|
||||
db func rewrite_pgidx rewrite_pgidx
|
||||
set i 0
|
||||
foreach rowid [db eval {SELECT rowid FROM x1_data WHERE rowid>100}] {
|
||||
foreach val {2 100} {
|
||||
do_test 5.2.$val.[incr i] {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE x1_data SET block=rewrite_pgidx(block, $val) WHERE id=$rowid;
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xa*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xb*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xc*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xd*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xe*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xf*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xg*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xh*';
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'xi*';
|
||||
}
|
||||
set {} {}
|
||||
} {}
|
||||
catch { db eval ROLLBACK }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
INSERT INTO t1 VALUES('bbbbb ccccc');
|
||||
SELECT quote(block) FROM t1_data WHERE rowid>100;
|
||||
} {X'000000180630626262626201020201056363636363010203040A'}
|
||||
do_execsql_test 6.1.1 {
|
||||
UPDATE t1_data SET block =
|
||||
X'000000180630626262626201020201056161616161010203040A'
|
||||
WHERE rowid>100;
|
||||
}
|
||||
do_catchsql_test 6.1.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------
|
||||
reset_db
|
||||
do_execsql_test 6.2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t1 VALUES('aa bb cc dd ee');
|
||||
SELECT pgno, quote(term) FROM t1_idx;
|
||||
} {2 X'' 4 X'3064'}
|
||||
do_execsql_test 6.2.1 {
|
||||
UPDATE t1_idx SET term = X'3065' WHERE pgno=4;
|
||||
}
|
||||
do_catchsql_test 6.2.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------
|
||||
reset_db
|
||||
do_execsql_test 6.3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
INSERT INTO t1 VALUES('abc abcdef abcdefghi');
|
||||
SELECT quote(block) FROM t1_data WHERE id>100;
|
||||
} {X'0000001C043061626301020204036465660102030703676869010204040808'}
|
||||
do_execsql_test 6.3.1 {
|
||||
BEGIN;
|
||||
UPDATE t1_data SET block =
|
||||
X'0000001C043061626301020204036465660102035003676869010204040808'
|
||||
------------------------------------------^^---------------------
|
||||
WHERE id>100;
|
||||
}
|
||||
do_catchsql_test 6.3.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 6.3.3 {
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
UPDATE t1_data SET block =
|
||||
X'0000001C043061626301020204036465660102030750676869010204040808'
|
||||
--------------------------------------------^^-------------------
|
||||
WHERE id>100;
|
||||
}
|
||||
do_catchsql_test 6.3.3 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 6.3.4 {
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
UPDATE t1_data SET block =
|
||||
X'0000001C043061626301020204036465660102030707676869010204040850'
|
||||
--------------------------------------------------------------^^-
|
||||
WHERE id>100;
|
||||
}
|
||||
do_catchsql_test 6.3.5 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 6.3.6 {
|
||||
ROLLBACK;
|
||||
BEGIN;
|
||||
UPDATE t1_data SET block =
|
||||
X'0000001C503061626301020204036465660102030707676869010204040808'
|
||||
----------^^-----------------------------------------------------
|
||||
WHERE id>100;
|
||||
}
|
||||
do_catchsql_test 6.3.5 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ if { $tcl_platform(wordSize)<8 } {
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
proc do_fb_test {tn sql res} {
|
||||
set res2 [lsort -integer -decr $res]
|
||||
uplevel [list do_execsql_test $tn.1 $sql $res]
|
||||
@@ -128,5 +130,68 @@ proc do_dlidx_test2 {tn nEntry iFirst nStep} {
|
||||
|
||||
do_dlidx_test2 2.1 [expr 20] [expr 1<<57] [expr (1<<57) + 128]
|
||||
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
|
||||
set ::vocab [list \
|
||||
IteratorpItercurrentlypointstothefirstrowidofadoclist \
|
||||
Thereisadoclistindexassociatedwiththefinaltermonthecurrent \
|
||||
pageIfthecurrenttermisthelasttermonthepageloadthe \
|
||||
doclistindexfromdiskandinitializeaniteratoratpIterpDlidx \
|
||||
IteratorpItercurrentlypointstothefirstrowidofadoclist \
|
||||
Thereisadoclistindexassociatedwiththefinaltermonthecurrent \
|
||||
pageIfthecurrenttermisthelasttermonthepageloadthe \
|
||||
doclistindexfromdiskandinitializeaniteratoratpIterpDlidx \
|
||||
]
|
||||
proc rnddoc {} {
|
||||
global vocab
|
||||
set nVocab [llength $vocab]
|
||||
set ret [list]
|
||||
for {set i 0} {$i < 64} {incr i} {
|
||||
lappend ret [lindex $vocab [expr $i % $nVocab]]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
db func rnddoc rnddoc
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE abc USING fts5(a);
|
||||
INSERT INTO abc(abc, rank) VALUES('pgsz', 32);
|
||||
|
||||
INSERT INTO abc VALUES ( rnddoc() );
|
||||
INSERT INTO abc VALUES ( rnddoc() );
|
||||
INSERT INTO abc VALUES ( rnddoc() );
|
||||
INSERT INTO abc VALUES ( rnddoc() );
|
||||
|
||||
INSERT INTO abc SELECT rnddoc() FROM abc;
|
||||
INSERT INTO abc SELECT rnddoc() FROM abc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT rowid FROM abc WHERE abc
|
||||
MATCH 'IteratorpItercurrentlypointstothefirstrowidofadoclist'
|
||||
ORDER BY rowid DESC;
|
||||
} {16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
INSERT INTO abc(abc) VALUES('integrity-check');
|
||||
INSERT INTO abc(abc) VALUES('optimize');
|
||||
INSERT INTO abc(abc) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
set v [lindex $vocab 0]
|
||||
set i 0
|
||||
foreach v $vocab {
|
||||
do_execsql_test 3.3.[incr i] {
|
||||
SELECT rowid FROM abc WHERE abc MATCH $v
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5fault2
|
||||
set testprefix fts5fault7
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
@@ -22,6 +22,8 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test fault-injection on a query that uses xColumnSize() on columnsize=0
|
||||
# table.
|
||||
@@ -41,5 +43,49 @@ do_faultsim_test 1 -faults oom* -body {
|
||||
faultsim_test_result {0 {7 4 10}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test fault-injection when a segment is promoted.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a);
|
||||
INSERT INTO t2(t2, rank) VALUES('automerge', 0);
|
||||
INSERT INTO t2(t2, rank) VALUES('crisismerge', 4);
|
||||
INSERT INTO t2(t2, rank) VALUES('pgsz', 40);
|
||||
|
||||
INSERT INTO t2 VALUES('a b c');
|
||||
INSERT INTO t2 VALUES('d e f');
|
||||
INSERT INTO t2 VALUES('f e d');
|
||||
INSERT INTO t2 VALUES('c b a');
|
||||
|
||||
INSERT INTO t2 VALUES('a b c');
|
||||
INSERT INTO t2 VALUES('d e f');
|
||||
INSERT INTO t2 VALUES('f e d');
|
||||
INSERT INTO t2 VALUES('c b a');
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 1 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('c d c g g f');
|
||||
INSERT INTO t2 VALUES('c d g b f d');
|
||||
INSERT INTO t2 VALUES('c c f d e d');
|
||||
INSERT INTO t2 VALUES('e a f c e f');
|
||||
INSERT INTO t2 VALUES('c g f b b d');
|
||||
INSERT INTO t2 VALUES('d a g a b b');
|
||||
INSERT INTO t2 VALUES('e f a b c e');
|
||||
INSERT INTO t2 VALUES('e c a g c d');
|
||||
INSERT INTO t2 VALUES('g b d d e b');
|
||||
INSERT INTO t2 VALUES('e a d a e d');
|
||||
}
|
||||
} -body {
|
||||
db eval COMMIT
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -91,6 +91,10 @@ do_execsql_test 2.6 {
|
||||
# SELECT count(fts5_decode(rowid, block)) FROM x1_data;
|
||||
#} $res
|
||||
|
||||
do_execsql_test 2.8 {
|
||||
SELECT fts5_decode(fts5_rowid('segment', 1000, 1), X'AB')
|
||||
} {corrupt}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests with very large tokens.
|
||||
#
|
||||
|
||||
@@ -169,5 +169,21 @@ do_execsql_test 5.8 {
|
||||
SELECT rowid FROM tt WHERE tt MATCH 'a*';
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
reset_db
|
||||
do_execsql_test 6.1 {
|
||||
CREATE VIRTUAL TABLE xyz USING fts5(x, y, z);
|
||||
INSERT INTO xyz VALUES('x', 'y', 'z');
|
||||
}
|
||||
|
||||
do_catchsql_test 6.2 {
|
||||
SELECT * FROM xyz WHERE xyz MATCH ''
|
||||
} {1 {fts5: syntax error near ""}}
|
||||
do_catchsql_test 6.3 {
|
||||
SELECT * FROM xyz WHERE xyz MATCH NULL
|
||||
} {1 {fts5: syntax error near ""}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+150
-131
@@ -33,6 +33,14 @@ SQLITE_EXTENSION_INIT1
|
||||
|
||||
#define UNUSED_PARAM(X) (void)(X)
|
||||
|
||||
/*
|
||||
** Versions of isspace(), isalnum() and isdigit() to which it is safe
|
||||
** to pass signed char values.
|
||||
*/
|
||||
#define safe_isspace(x) isspace((unsigned char)(x))
|
||||
#define safe_isdigit(x) isdigit((unsigned char)(x))
|
||||
#define safe_isalnum(x) isalnum((unsigned char)(x))
|
||||
|
||||
/* Unsigned integer types */
|
||||
typedef sqlite3_uint64 u64;
|
||||
typedef unsigned int u32;
|
||||
@@ -148,11 +156,9 @@ static void jsonReset(JsonString *p){
|
||||
/* Report an out-of-memory (OOM) condition
|
||||
*/
|
||||
static void jsonOom(JsonString *p){
|
||||
if( !p->bErr ){
|
||||
p->bErr = 1;
|
||||
sqlite3_result_error_nomem(p->pCtx);
|
||||
jsonReset(p);
|
||||
}
|
||||
p->bErr = 1;
|
||||
sqlite3_result_error_nomem(p->pCtx);
|
||||
jsonReset(p);
|
||||
}
|
||||
|
||||
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
|
||||
@@ -231,12 +237,13 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
|
||||
for(i=0; i<N; i++){
|
||||
char c = zIn[i];
|
||||
if( c=='"' || c=='\\' ){
|
||||
if( (p->nUsed+N+1-i > p->nAlloc) && jsonGrow(p,N+1-i)!=0 ) return;
|
||||
if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
|
||||
p->zBuf[p->nUsed++] = '\\';
|
||||
}
|
||||
p->zBuf[p->nUsed++] = c;
|
||||
}
|
||||
p->zBuf[p->nUsed++] = '"';
|
||||
assert( p->nUsed<p->nAlloc );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -334,7 +341,8 @@ static void jsonRenderNode(
|
||||
sqlite3_value **aReplace /* Replacement values */
|
||||
){
|
||||
switch( pNode->eType ){
|
||||
case JSON_NULL: {
|
||||
default: {
|
||||
assert( pNode->eType==JSON_NULL );
|
||||
jsonAppendRaw(pOut, "null", 4);
|
||||
break;
|
||||
}
|
||||
@@ -432,7 +440,8 @@ static void jsonReturn(
|
||||
sqlite3_value **aReplace /* Array of replacement values */
|
||||
){
|
||||
switch( pNode->eType ){
|
||||
case JSON_NULL: {
|
||||
default: {
|
||||
assert( pNode->eType==JSON_NULL );
|
||||
sqlite3_result_null(pCtx);
|
||||
break;
|
||||
}
|
||||
@@ -459,10 +468,16 @@ static void jsonReturn(
|
||||
break;
|
||||
}
|
||||
case JSON_STRING: {
|
||||
#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
|
||||
** json_insert() and json_replace() and those routines do not
|
||||
** call jsonReturn() */
|
||||
if( pNode->jnFlags & JNODE_RAW ){
|
||||
sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
|
||||
SQLITE_TRANSIENT);
|
||||
}else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
|
||||
}else
|
||||
#endif
|
||||
assert( (pNode->jnFlags & JNODE_RAW)==0 );
|
||||
if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
|
||||
/* JSON formatted without any backslash-escapes */
|
||||
sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
|
||||
SQLITE_TRANSIENT);
|
||||
@@ -550,10 +565,6 @@ static int jsonParseAddNode(
|
||||
JsonNode *pNew;
|
||||
if( pParse->oom ) return -1;
|
||||
nNew = pParse->nAlloc*2 + 10;
|
||||
if( nNew<=pParse->nNode ){
|
||||
pParse->oom = 1;
|
||||
return -1;
|
||||
}
|
||||
pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
|
||||
if( pNew==0 ){
|
||||
pParse->oom = 1;
|
||||
@@ -585,14 +596,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
|
||||
int iThis;
|
||||
int x;
|
||||
JsonNode *pNode;
|
||||
while( isspace(pParse->zJson[i]) ){ i++; }
|
||||
while( safe_isspace(pParse->zJson[i]) ){ i++; }
|
||||
if( (c = pParse->zJson[i])==0 ) return 0;
|
||||
if( c=='{' ){
|
||||
/* Parse object */
|
||||
iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
|
||||
if( iThis<0 ) return -1;
|
||||
for(j=i+1;;j++){
|
||||
while( isspace(pParse->zJson[j]) ){ j++; }
|
||||
while( safe_isspace(pParse->zJson[j]) ){ j++; }
|
||||
x = jsonParseValue(pParse, j);
|
||||
if( x<0 ){
|
||||
if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
||||
@@ -603,13 +614,13 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
|
||||
if( pNode->eType!=JSON_STRING ) return -1;
|
||||
pNode->jnFlags |= JNODE_LABEL;
|
||||
j = x;
|
||||
while( isspace(pParse->zJson[j]) ){ j++; }
|
||||
while( safe_isspace(pParse->zJson[j]) ){ j++; }
|
||||
if( pParse->zJson[j]!=':' ) return -1;
|
||||
j++;
|
||||
x = jsonParseValue(pParse, j);
|
||||
if( x<0 ) return -1;
|
||||
j = x;
|
||||
while( isspace(pParse->zJson[j]) ){ j++; }
|
||||
while( safe_isspace(pParse->zJson[j]) ){ j++; }
|
||||
c = pParse->zJson[j];
|
||||
if( c==',' ) continue;
|
||||
if( c!='}' ) return -1;
|
||||
@@ -622,14 +633,14 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
|
||||
iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
|
||||
if( iThis<0 ) return -1;
|
||||
for(j=i+1;;j++){
|
||||
while( isspace(pParse->zJson[j]) ){ j++; }
|
||||
while( safe_isspace(pParse->zJson[j]) ){ j++; }
|
||||
x = jsonParseValue(pParse, j);
|
||||
if( x<0 ){
|
||||
if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
|
||||
return -1;
|
||||
}
|
||||
j = x;
|
||||
while( isspace(pParse->zJson[j]) ){ j++; }
|
||||
while( safe_isspace(pParse->zJson[j]) ){ j++; }
|
||||
c = pParse->zJson[j];
|
||||
if( c==',' ) continue;
|
||||
if( c!=']' ) return -1;
|
||||
@@ -658,17 +669,17 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
|
||||
return j+1;
|
||||
}else if( c=='n'
|
||||
&& strncmp(pParse->zJson+i,"null",4)==0
|
||||
&& !isalnum(pParse->zJson[i+4]) ){
|
||||
&& !safe_isalnum(pParse->zJson[i+4]) ){
|
||||
jsonParseAddNode(pParse, JSON_NULL, 0, 0);
|
||||
return i+4;
|
||||
}else if( c=='t'
|
||||
&& strncmp(pParse->zJson+i,"true",4)==0
|
||||
&& !isalnum(pParse->zJson[i+4]) ){
|
||||
&& !safe_isalnum(pParse->zJson[i+4]) ){
|
||||
jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
|
||||
return i+4;
|
||||
}else if( c=='f'
|
||||
&& strncmp(pParse->zJson+i,"false",5)==0
|
||||
&& !isalnum(pParse->zJson[i+5]) ){
|
||||
&& !safe_isalnum(pParse->zJson[i+5]) ){
|
||||
jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
|
||||
return i+5;
|
||||
}else if( c=='-' || (c>='0' && c<='9') ){
|
||||
@@ -731,7 +742,7 @@ static int jsonParse(
|
||||
i = jsonParseValue(pParse, 0);
|
||||
if( pParse->oom ) i = -1;
|
||||
if( i>0 ){
|
||||
while( isspace(zJson[i]) ) i++;
|
||||
while( safe_isspace(zJson[i]) ) i++;
|
||||
if( zJson[i] ) i = -1;
|
||||
}
|
||||
if( i<=0 ){
|
||||
@@ -790,6 +801,20 @@ static int jsonParseFindParents(JsonParse *pParse){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
|
||||
** a match.
|
||||
*/
|
||||
static int jsonLabelCompare(JsonNode *pNode, const char *zKey, int nKey){
|
||||
if( pNode->jnFlags & JNODE_RAW ){
|
||||
if( pNode->n!=nKey ) return 0;
|
||||
return strncmp(pNode->u.zJContent, zKey, nKey)==0;
|
||||
}else{
|
||||
if( pNode->n!=nKey+2 ) return 0;
|
||||
return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
|
||||
}
|
||||
}
|
||||
|
||||
/* forward declaration */
|
||||
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
|
||||
|
||||
@@ -820,7 +845,12 @@ static JsonNode *jsonLookupStep(
|
||||
zKey = zPath + 1;
|
||||
for(i=1; zPath[i] && zPath[i]!='"'; i++){}
|
||||
nKey = i-1;
|
||||
if( zPath[i] ) i++;
|
||||
if( zPath[i] ){
|
||||
i++;
|
||||
}else{
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
}
|
||||
}else{
|
||||
zKey = zPath;
|
||||
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
|
||||
@@ -833,9 +863,7 @@ static JsonNode *jsonLookupStep(
|
||||
j = 1;
|
||||
for(;;){
|
||||
while( j<=pRoot->n ){
|
||||
if( pRoot[j].n==nKey+2
|
||||
&& strncmp(&pRoot[j].u.zJContent[1],zKey,nKey)==0
|
||||
){
|
||||
if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
|
||||
return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
|
||||
}
|
||||
j++;
|
||||
@@ -862,19 +890,19 @@ static JsonNode *jsonLookupStep(
|
||||
}
|
||||
return pNode;
|
||||
}
|
||||
}else if( zPath[0]=='[' && isdigit(zPath[1]) ){
|
||||
}else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
|
||||
if( pRoot->eType!=JSON_ARRAY ) return 0;
|
||||
i = 0;
|
||||
zPath++;
|
||||
while( isdigit(zPath[0]) ){
|
||||
i = i*10 + zPath[0] - '0';
|
||||
zPath++;
|
||||
j = 1;
|
||||
while( safe_isdigit(zPath[j]) ){
|
||||
i = i*10 + zPath[j] - '0';
|
||||
j++;
|
||||
}
|
||||
if( zPath[0]!=']' ){
|
||||
if( zPath[j]!=']' ){
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
}
|
||||
zPath++;
|
||||
zPath += j + 1;
|
||||
j = 1;
|
||||
for(;;){
|
||||
while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
|
||||
@@ -902,7 +930,7 @@ static JsonNode *jsonLookupStep(
|
||||
}
|
||||
return pNode;
|
||||
}
|
||||
}else if( zPath[0]!=0 ){
|
||||
}else{
|
||||
*pzErr = zPath;
|
||||
}
|
||||
return 0;
|
||||
@@ -960,6 +988,7 @@ static JsonNode *jsonLookup(
|
||||
){
|
||||
const char *zErr = 0;
|
||||
JsonNode *pNode = 0;
|
||||
char *zMsg;
|
||||
|
||||
if( zPath==0 ) return 0;
|
||||
if( zPath[0]!='$' ){
|
||||
@@ -968,18 +997,17 @@ static JsonNode *jsonLookup(
|
||||
}
|
||||
zPath++;
|
||||
pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
|
||||
return pNode;
|
||||
if( zErr==0 ) return pNode;
|
||||
|
||||
lookup_err:
|
||||
pParse->nErr++;
|
||||
if( zErr!=0 && pCtx!=0 ){
|
||||
char *z = jsonPathSyntaxError(zErr);
|
||||
if( z ){
|
||||
sqlite3_result_error(pCtx, z, -1);
|
||||
sqlite3_free(z);
|
||||
}else{
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
}
|
||||
assert( zErr!=0 && pCtx!=0 );
|
||||
zMsg = jsonPathSyntaxError(zErr);
|
||||
if( zMsg ){
|
||||
sqlite3_result_error(pCtx, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
}else{
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1102,23 +1130,22 @@ static void jsonArrayLengthFunc(
|
||||
JsonParse x; /* The parse */
|
||||
sqlite3_int64 n = 0;
|
||||
u32 i;
|
||||
JsonNode *pNode;
|
||||
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
if( x.nNode ){
|
||||
JsonNode *pNode;
|
||||
if( argc==2 ){
|
||||
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
}else{
|
||||
pNode = x.aNode;
|
||||
}
|
||||
if( pNode==0 ){
|
||||
x.nErr = 1;
|
||||
}else if( pNode->eType==JSON_ARRAY ){
|
||||
assert( (pNode->jnFlags & JNODE_APPEND)==0 );
|
||||
for(i=1; i<=pNode->n; n++){
|
||||
i += jsonNodeSize(&pNode[i]);
|
||||
}
|
||||
assert( x.nNode );
|
||||
if( argc==2 ){
|
||||
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
}else{
|
||||
pNode = x.aNode;
|
||||
}
|
||||
if( pNode==0 ){
|
||||
x.nErr = 1;
|
||||
}else if( pNode->eType==JSON_ARRAY ){
|
||||
assert( (pNode->jnFlags & JNODE_APPEND)==0 );
|
||||
for(i=1; i<=pNode->n; n++){
|
||||
i += jsonNodeSize(&pNode[i]);
|
||||
}
|
||||
}
|
||||
if( x.nErr==0 ) sqlite3_result_int64(ctx, n);
|
||||
@@ -1197,7 +1224,7 @@ static void jsonObjectFunc(
|
||||
for(i=0; i<argc; i+=2){
|
||||
if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
|
||||
sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
|
||||
jsonZero(&jx);
|
||||
jsonReset(&jx);
|
||||
return;
|
||||
}
|
||||
jsonAppendSeparator(&jx);
|
||||
@@ -1231,17 +1258,16 @@ static void jsonRemoveFunc(
|
||||
|
||||
if( argc<1 ) return;
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
if( x.nNode ){
|
||||
for(i=1; i<(u32)argc; i++){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
if( zPath==0 ) goto remove_done;
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
if( x.nErr ) goto remove_done;
|
||||
if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
|
||||
}
|
||||
if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
||||
jsonReturnJson(x.aNode, ctx, 0);
|
||||
}
|
||||
assert( x.nNode );
|
||||
for(i=1; i<(u32)argc; i++){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
if( zPath==0 ) goto remove_done;
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
if( x.nErr ) goto remove_done;
|
||||
if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
|
||||
}
|
||||
if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
|
||||
jsonReturnJson(x.aNode, ctx, 0);
|
||||
}
|
||||
remove_done:
|
||||
jsonParseReset(&x);
|
||||
@@ -1269,22 +1295,21 @@ static void jsonReplaceFunc(
|
||||
return;
|
||||
}
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
if( x.nNode ){
|
||||
for(i=1; i<(u32)argc; i+=2){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
if( x.nErr ) goto replace_err;
|
||||
if( pNode ){
|
||||
pNode->jnFlags |= (u8)JNODE_REPLACE;
|
||||
pNode->iVal = (u8)(i+1);
|
||||
}
|
||||
}
|
||||
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
|
||||
}else{
|
||||
jsonReturnJson(x.aNode, ctx, argv);
|
||||
assert( x.nNode );
|
||||
for(i=1; i<(u32)argc; i+=2){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
if( x.nErr ) goto replace_err;
|
||||
if( pNode ){
|
||||
pNode->jnFlags |= (u8)JNODE_REPLACE;
|
||||
pNode->iVal = (u8)(i+1);
|
||||
}
|
||||
}
|
||||
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
|
||||
}else{
|
||||
jsonReturnJson(x.aNode, ctx, argv);
|
||||
}
|
||||
replace_err:
|
||||
jsonParseReset(&x);
|
||||
}
|
||||
@@ -1319,27 +1344,26 @@ static void jsonSetFunc(
|
||||
return;
|
||||
}
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
if( x.nNode ){
|
||||
for(i=1; i<(u32)argc; i+=2){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
bApnd = 0;
|
||||
pNode = jsonLookup(&x, zPath, &bApnd, ctx);
|
||||
if( x.oom ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
goto jsonSetDone;
|
||||
}else if( x.nErr ){
|
||||
goto jsonSetDone;
|
||||
}else if( pNode && (bApnd || bIsSet) ){
|
||||
pNode->jnFlags |= (u8)JNODE_REPLACE;
|
||||
pNode->iVal = (u8)(i+1);
|
||||
}
|
||||
}
|
||||
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
|
||||
}else{
|
||||
jsonReturnJson(x.aNode, ctx, argv);
|
||||
assert( x.nNode );
|
||||
for(i=1; i<(u32)argc; i+=2){
|
||||
zPath = (const char*)sqlite3_value_text(argv[i]);
|
||||
bApnd = 0;
|
||||
pNode = jsonLookup(&x, zPath, &bApnd, ctx);
|
||||
if( x.oom ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
goto jsonSetDone;
|
||||
}else if( x.nErr ){
|
||||
goto jsonSetDone;
|
||||
}else if( pNode && (bApnd || bIsSet) ){
|
||||
pNode->jnFlags |= (u8)JNODE_REPLACE;
|
||||
pNode->iVal = (u8)(i+1);
|
||||
}
|
||||
}
|
||||
if( x.aNode[0].jnFlags & JNODE_REPLACE ){
|
||||
sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
|
||||
}else{
|
||||
jsonReturnJson(x.aNode, ctx, argv);
|
||||
}
|
||||
jsonSetDone:
|
||||
jsonParseReset(&x);
|
||||
}
|
||||
@@ -1358,19 +1382,18 @@ static void jsonTypeFunc(
|
||||
){
|
||||
JsonParse x; /* The parse */
|
||||
const char *zPath;
|
||||
JsonNode *pNode;
|
||||
|
||||
if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
|
||||
if( x.nNode ){
|
||||
JsonNode *pNode;
|
||||
if( argc==2 ){
|
||||
zPath = (const char*)sqlite3_value_text(argv[1]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
}else{
|
||||
pNode = x.aNode;
|
||||
}
|
||||
if( pNode ){
|
||||
sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
|
||||
}
|
||||
assert( x.nNode );
|
||||
if( argc==2 ){
|
||||
zPath = (const char*)sqlite3_value_text(argv[1]);
|
||||
pNode = jsonLookup(&x, zPath, 0, ctx);
|
||||
}else{
|
||||
pNode = x.aNode;
|
||||
}
|
||||
if( pNode ){
|
||||
sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
|
||||
}
|
||||
jsonParseReset(&x);
|
||||
}
|
||||
@@ -1390,9 +1413,7 @@ static void jsonValidFunc(
|
||||
int rc = 0;
|
||||
|
||||
UNUSED_PARAM(argc);
|
||||
if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0
|
||||
&& x.nNode>0
|
||||
){
|
||||
if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 ){
|
||||
rc = 1;
|
||||
}
|
||||
jsonParseReset(&x);
|
||||
@@ -1669,7 +1690,7 @@ static int jsonEachColumn(
|
||||
sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
case JEACH_JSON: {
|
||||
assert( i==JEACH_JSON );
|
||||
sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
|
||||
break;
|
||||
@@ -1745,15 +1766,6 @@ static int jsonEachFilter(
|
||||
if( idxNum==0 ) return SQLITE_OK;
|
||||
z = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( z==0 ) return SQLITE_OK;
|
||||
if( idxNum&2 ){
|
||||
zRoot = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( zRoot==0 ) return SQLITE_OK;
|
||||
if( zRoot[0]!='$' ){
|
||||
sqlite3_free(cur->pVtab->zErrMsg);
|
||||
cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot);
|
||||
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
p->zJson = sqlite3_malloc64( n+1 );
|
||||
if( p->zJson==0 ) return SQLITE_NOMEM;
|
||||
@@ -1774,12 +1786,18 @@ static int jsonEachFilter(
|
||||
JsonNode *pNode;
|
||||
if( idxNum==3 ){
|
||||
const char *zErr = 0;
|
||||
zRoot = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( zRoot==0 ) return SQLITE_OK;
|
||||
n = sqlite3_value_bytes(argv[1]);
|
||||
p->zRoot = sqlite3_malloc64( n+1 );
|
||||
if( p->zRoot==0 ) return SQLITE_NOMEM;
|
||||
memcpy(p->zRoot, zRoot, (size_t)n+1);
|
||||
pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
|
||||
if( p->sParse.nErr ){
|
||||
if( zRoot[0]!='$' ){
|
||||
zErr = zRoot;
|
||||
}else{
|
||||
pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
|
||||
}
|
||||
if( zErr ){
|
||||
sqlite3_free(cur->pVtab->zErrMsg);
|
||||
cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
|
||||
jsonEachCursorReset(p);
|
||||
@@ -1796,6 +1814,7 @@ static int jsonEachFilter(
|
||||
pNode->u.iKey = 0;
|
||||
p->iEnd = p->i + pNode->n + 1;
|
||||
if( p->bRecursive ){
|
||||
p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
|
||||
if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
|
||||
p->i--;
|
||||
}
|
||||
@@ -1806,7 +1825,7 @@ static int jsonEachFilter(
|
||||
p->iEnd = p->i+1;
|
||||
}
|
||||
}
|
||||
return p->sParse.oom ? SQLITE_NOMEM : SQLITE_OK;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* The methods of the json_each virtual table */
|
||||
|
||||
@@ -448,7 +448,14 @@ TESTPROGS = \
|
||||
FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata1.db \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db
|
||||
$(TOP)/test/fuzzdata3.db \
|
||||
$(TOP)/test/fuzzdata4.db
|
||||
|
||||
# Extra arguments for including json1 in the build of tools
|
||||
#
|
||||
JSON1_DEP = $(TOP)/ext/misc/json1.c sqlite3ext.h
|
||||
JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE
|
||||
JSON1_SRC = $(TOP)/ext/misc/json1.c
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
@@ -463,23 +470,24 @@ libsqlite3.a: $(LIBOBJ)
|
||||
$(AR) libsqlite3.a $(LIBOBJ)
|
||||
$(RANLIB) libsqlite3.a
|
||||
|
||||
sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TOP)/ext/misc/json1.c
|
||||
$(TCCX) $(READLINE_FLAGS) -DSQLITE_ENABLE_JSON1 -o sqlite3$(EXE) \
|
||||
$(TOP)/src/shell.c $(TOP)/ext/misc/json1.c \
|
||||
sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(JSON1_DEP)
|
||||
$(TCCX) $(READLINE_FLAGS) $(JSON1_OPT) -o sqlite3$(EXE) \
|
||||
$(TOP)/src/shell.c $(JSON1_SRC) \
|
||||
libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
|
||||
|
||||
sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
|
||||
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP)
|
||||
$(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
$(JSON1_OPT) $(TOP)/tool/fuzzershell.c $(JSON1_SRC) sqlite3.c \
|
||||
$(TLIBS) $(THREADLIB)
|
||||
|
||||
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
|
||||
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP)
|
||||
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_ENABLE_MEMSYS5 \
|
||||
$(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
-DSQLITE_ENABLE_MEMSYS5 $(JSON1_OPT) \
|
||||
$(TOP)/test/fuzzcheck.c $(JSON1_SRC) sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
C Experimental\schanges\sto\savoid\srecusrively\scalling\sxMutexInit.
|
||||
D 2015-09-13T18:45:50.134
|
||||
C Merge\supdates\sfrom\strunk.
|
||||
D 2015-09-23T15:54:36.389
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
|
||||
F Makefile.in e1afa6fb2de2bddd50e0ddae8166c2ee9d69b301
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc b268d8be2e800b9d35f074b1ed6b2f698deebdd6
|
||||
F Makefile.msc f090cdf036f3c07fb13aa2f4494e388c0b1ed1e4
|
||||
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
F VERSION ccfc4d1576dbfdeece0a4372a2e6a2e37d3e7975
|
||||
@@ -105,15 +105,15 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
|
||||
F ext/fts3/unicode/mkunicode.tcl 95cf7ec186e48d4985e433ff8a1c89090a774252
|
||||
F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h f04659e0df5af83731b102189a32280f74f4a6bc
|
||||
F ext/fts5/fts5.h 98f802fe41481f9d797fce496f0fefcad72c7782
|
||||
F ext/fts5/fts5Int.h 666aba8432940a8449a3bd4636e898fe906ed95d
|
||||
F ext/fts5/fts5_aux.c 7a307760a9c57c750d043188ec0bad59f5b5ec7e
|
||||
F ext/fts5/fts5_buffer.c 64dcaf36a3ebda9e84b7c3b8788887ec325e12a4
|
||||
F ext/fts5/fts5_config.c 57ee5fe71578cb494574fc0e6e51acb9a22a8695
|
||||
F ext/fts5/fts5_expr.c 667faaf14a69a5683ac383acdc8d942cf32c3f93
|
||||
F ext/fts5/fts5_hash.c 4bf4b99708848357b8a2b5819e509eb6d3df9246
|
||||
F ext/fts5/fts5_index.c 62a682a70ea2e84fa67c7786ead892b201116ad1
|
||||
F ext/fts5/fts5_main.c 3fa906f6c0177caf8f82862bc70f37b28bb3305c
|
||||
F ext/fts5/fts5_index.c 4fdbc0a321e3a1d73741a623d7aea4db78d6a86d
|
||||
F ext/fts5/fts5_main.c 53116cffeb26898832ff7700cc5ebac5fe085d32
|
||||
F ext/fts5/fts5_storage.c 120f7b143688b5b7710dacbd48cff211609b8059
|
||||
F ext/fts5/fts5_tcl.c 6da58d6e8f42a93c4486b5ba9b187a7f995dee37
|
||||
F ext/fts5/fts5_test_mi.c e96be827aa8f571031e65e481251dc1981d608bf
|
||||
@@ -146,8 +146,8 @@ F ext/fts5/test/fts5config.test ad2ff42ddc856aed2d05bf89dc1c578c8a39ea3b
|
||||
F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1
|
||||
F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62
|
||||
F ext/fts5/test/fts5corrupt2.test 26c0a39dd9ff73207e6229f83b50b21d37c7658c
|
||||
F ext/fts5/test/fts5corrupt3.test 1ccf575f5126e79f9fec7979fd02a1f40a076be3
|
||||
F ext/fts5/test/fts5dlidx.test 59b80bbe34169a082c575d9c26f0a7019a7b79c1
|
||||
F ext/fts5/test/fts5corrupt3.test a1429635c30f922079189e35b2b631eb7463a2fb
|
||||
F ext/fts5/test/fts5dlidx.test ecba5e62ea8b26c33829961602069c546228046d
|
||||
F ext/fts5/test/fts5doclist.test 8edb5b57e5f144030ed74ec00ef6fa4294fed79b
|
||||
F ext/fts5/test/fts5ea.test b01e3a18cdfabbff8104a96a5242a06a68a998a0
|
||||
F ext/fts5/test/fts5eb.test 3e5869af2008cbc4ad03a175a0b6f6e58134cd43
|
||||
@@ -157,7 +157,7 @@ F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
|
||||
F ext/fts5/test/fts5fault4.test 762991d526ee67c2b374351a17248097ea38bee7
|
||||
F ext/fts5/test/fts5fault5.test 54da9fd4c3434a1d4f6abdcb6469299d91cf5875
|
||||
F ext/fts5/test/fts5fault6.test 97bce1a36b7a64e3203fea504ae8e5cfd5ada423
|
||||
F ext/fts5/test/fts5fault7.test f4a9b796f8b20c78ec7cf9f4e11144d15d7c3fd4
|
||||
F ext/fts5/test/fts5fault7.test f62ed4d98f137eb03f1db94d1fa41b17a771d971
|
||||
F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741
|
||||
F ext/fts5/test/fts5hash.test 42eb066f667e9a389a63437cb7038c51974d4fc6
|
||||
F ext/fts5/test/fts5integrity.test 29f41d2c7126c6122fbb5d54e556506456876145
|
||||
@@ -172,8 +172,8 @@ F ext/fts5/test/fts5prefix.test 552a462f0e8595676611f41643de217fb4ac2808
|
||||
F ext/fts5/test/fts5rank.test 11dcebba31d822f7e99685b4ea2c2ae3ec0b16f1
|
||||
F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
|
||||
F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
|
||||
F ext/fts5/test/fts5rowid.test 3e3b66670ca65540fa321250ac12f890b17f9312
|
||||
F ext/fts5/test/fts5simple.test f629e24a35a9f31cfb16c9920e8c2316e3d93e94
|
||||
F ext/fts5/test/fts5rowid.test 400384798349d658eaf06aefa1e364957d5d4821
|
||||
F ext/fts5/test/fts5simple.test 967b7144644ad4b40b2526160a5adfa896864c55
|
||||
F ext/fts5/test/fts5synonym.test cf88c0a56d5ea9591e3939ef1f6e294f7f2d0671
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
@@ -195,7 +195,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
|
||||
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
|
||||
F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
|
||||
F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
|
||||
F ext/misc/json1.c f35d00fbd79a7e23af18d7630a2fcf22dce3692b
|
||||
F ext/misc/json1.c 7659013d8d84a263ae60cd01cdc25d3596cf5063
|
||||
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
|
||||
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
|
||||
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
|
||||
@@ -261,7 +261,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 58eb74de702467c3b71cdf06f213cefe7f5ce544
|
||||
F main.mk d12601118f1d1dadebe1329a53a6d5c512b36d44
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk 0e7f04a8eb90f92259e47d80110e4e98d7ce337a
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -282,16 +282,16 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452
|
||||
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
|
||||
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
||||
F src/btree.c 4084d9eed2817331f6e6a82230ba30e448cad497
|
||||
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
|
||||
F src/btree.c 164583151135a3764672c2c25aa8e4fa06bdb12b
|
||||
F src/btree.h 40189aefdc2b830d25c8b58fd7d56538481bfdd7
|
||||
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
|
||||
F src/build.c f81380bc4d5d239c18b42982a9866a94489fd444
|
||||
F src/build.c 8a86f4203ac8a9ac0734f242a96f043edffb6018
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
|
||||
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
||||
F src/date.c fb1c99172017dcc8e237339132c91a21a0788584
|
||||
F src/dbstat.c e637e7a7ff40ef32132a418c6fdf1cfb63aa27c7
|
||||
F src/delete.c 6792c80d7fb54c4df9f7680413952600e7439492
|
||||
F src/delete.c 371df4fc86e96efeaed3d37565aef77f956be109
|
||||
F src/expr.c 3a76afcdac925294c39903b7002ddb9e5fd29863
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 83e1baba999bed3144ea5a2143fc922edf51135f
|
||||
@@ -300,7 +300,7 @@ F src/global.c d824163680839be79b53204105a78e5be5d4ac4a
|
||||
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
||||
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c 076dc5876e261a9908603d54cfc5344cd680166c
|
||||
F src/insert.c 9748a37e058256eb2ead69f028ab85ebf203ad15
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||
F src/lempar.c d344a95d60c24e2f490ee59db9784b1b17439012
|
||||
@@ -332,20 +332,20 @@ F src/pager.h 6d435f563b3f7fcae4b84433b76a6ac2730036e2
|
||||
F src/parse.y f599aa5e871a493330d567ced93de696f61f48f7
|
||||
F src/pcache.c 24be750c79272e0ca7b6e007bc94999700f3e5ef
|
||||
F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9
|
||||
F src/pcache1.c a0c0bb29f7bd720743a16a95eb5dedba3ade15bc
|
||||
F src/pcache1.c e822007159d53a7ea7aa040d6e28964ddb6de083
|
||||
F src/pragma.c d71b813e67bf03f3116b9dd5164fbfd81ec673a2
|
||||
F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a
|
||||
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
|
||||
F src/printf.c 0c4bcdd1c2e2521024f0a69cb5eb334f86b3652a
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 3126f7694b8ce0f97282d7dd3a5198b8fa18dce9
|
||||
F src/resolve.c 1954a0f01bf65d78d7d559aea3d5c67f33376d91
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c c17613385bc6b095c421b1f30548814f5fd8a9b2
|
||||
F src/select.c 36ee14d729e182cd8b6796f980b7ab3fc9bcee72
|
||||
F src/shell.c 6332ef06db1390ef812cfdff1fc97b4fd76cdd42
|
||||
F src/sqlite.h.in b7386d2bae6a43ba35be56e0d495b8725998101e
|
||||
F src/sqlite.h.in 4e06cb5fed36736242c66237991db6f3e2395eb9
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308
|
||||
F src/sqliteInt.h d7b2c30947a49a7538f24c0dca253c0e456c849c
|
||||
F src/sqliteInt.h ed65bcb0a6a7c11978c8f75d5476d6edaa024e8d
|
||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
@@ -399,14 +399,14 @@ F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
|
||||
F src/tokenize.c 83c6ed569423a3af83a83973b444cf7123be33a6
|
||||
F src/treeview.c 154f0acc622fa3514de8777dcedf4c8a8802b4ce
|
||||
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
|
||||
F src/update.c 3c5bc9570df3bfafa0db36828406a8a14e4c426e
|
||||
F src/update.c eb7ab3ff2928628692a4f14be397c95f4a681d97
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd
|
||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||
F src/vdbe.c 6d85be995bd2308a5aa2a68c7b564c5d4cc1a6fb
|
||||
F src/vdbe.c 5048103aa6bc9c01bf1a4620729256d1e873bb6c
|
||||
F src/vdbe.h 4bc88bd0e06f8046ee6ab7487c0015e85ad949ad
|
||||
F src/vdbeInt.h 8b867eac234e28627ffcace3cd4b4b79bbec664b
|
||||
F src/vdbeapi.c 0d890f57caf143b114a95ce699e59af51359c508
|
||||
F src/vdbeapi.c 020681b943e77766b32ae1cddf86d7831b7374ca
|
||||
F src/vdbeaux.c fd00b489ab3f44f2dca1e4344faf289b7bfcf649
|
||||
F src/vdbeblob.c 1d7b97115e7bbac4c318db416d2ca83fc779544a
|
||||
F src/vdbemem.c 19b3036aa4d676e7103b0fb5efd6327da455f915
|
||||
@@ -417,9 +417,9 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
||||
F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
|
||||
F src/where.c 1227687e7892d4009f3c3433e974eb9c9e3c4d6a
|
||||
F src/whereInt.h 292d3ac90da4eab1e03ac8452f1add746bcafaa1
|
||||
F src/wherecode.c 6ac8599523f4840d9efac335329f627ebf3f79fd
|
||||
F src/where.c 882fb44b36201fafc32dd7d59366f852806b7e70
|
||||
F src/whereInt.h 7892bb54cf9ca0ae5c7e6094491b94c9286dc647
|
||||
F src/wherecode.c 7660e1ad16817a921b099af553f3e1349352d16f
|
||||
F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@@ -531,7 +531,7 @@ F test/conflict2.test 0d3af4fb534fa1bd020c79960bb56e4d52655f09
|
||||
F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b
|
||||
F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4
|
||||
F test/corrupt.test 141c39ea650c1365e85a49e402fa05cb9617fb97
|
||||
F test/corrupt2.test 08cec1e5ffa68a3610306d6068f112d08bc9f090
|
||||
F test/corrupt2.test cb787825d761b0f869764d6990531382840de872
|
||||
F test/corrupt3.test 4b548d0bbe2933bc81d3f54099a05fc4d28aff18
|
||||
F test/corrupt4.test b99652079d542b21f4965f6248703b983e40fe80
|
||||
F test/corrupt5.test 8ead52af76006f3286e9396cb41898018ccea107
|
||||
@@ -541,7 +541,7 @@ F test/corrupt8.test 2399dfe40d2c0c63af86706e30f3e6302a8d0516
|
||||
F test/corrupt9.test 730a3db08d4ab9aa43392ea30d9c2b4879cbff85
|
||||
F test/corruptA.test 53e56dafd180addcdadb402244b8cb9771d2ba26
|
||||
F test/corruptB.test 73a8d6c0b9833697ecf16b63e3c5c05c945b5dec
|
||||
F test/corruptC.test 3fcc0f73d2cf2d69befe2d96332b942426a6aae2
|
||||
F test/corruptC.test 0c46574f8d4f27ecc799b1b5c4cbf9b1817bce9a
|
||||
F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040
|
||||
F test/corruptE.test be8e5088c369fc7979c662cd644efdaafc0f7f6d
|
||||
F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
|
||||
@@ -569,9 +569,10 @@ F test/date.test 42973251b9429f2c41b77eb98a7b0b0ba2d3b2c0
|
||||
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
|
||||
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
|
||||
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
|
||||
F test/delete4.test d9e7d553a939597b27d205b022d769469f361c1f
|
||||
F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
@@ -748,10 +749,11 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
|
||||
F test/fuzz3.test 53fabcd5f0f430f8b221282f6c12c4d0903c21eb
|
||||
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
|
||||
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
|
||||
F test/fuzzcheck.c 79980bbc00e19ad44c3ba6699e643348572757a2
|
||||
F test/fuzzcheck.c b8eb7ee40f6d28548a0b028e0676293522f3427f
|
||||
F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664
|
||||
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
|
||||
F test/fuzzdata3.db 1d6044c33a114007f02b6e6846f1fa232f607bfd
|
||||
F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21
|
||||
F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
|
||||
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
@@ -782,8 +784,8 @@ F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
|
||||
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
|
||||
F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a
|
||||
F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c
|
||||
F test/indexedby.test 69d2292dfdabe85aa7c5df577c71bb4325607ec2
|
||||
F test/indexexpr1.test 4feec154aadacb033b41acc1760a18edc4c60470
|
||||
F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985
|
||||
F test/indexexpr1.test b4dd422f6d0a515cbca6e0049d03a01f067b2bad
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371
|
||||
@@ -814,8 +816,8 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
|
||||
F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa
|
||||
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
|
||||
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
|
||||
F test/json101.test e20d2421c531db32fad59c5e06e80af0b1b002c8
|
||||
F test/json102.test 4e1403cb06481ab160cf471c3c139820498e0563
|
||||
F test/json101.test 07f0d847d943bf5ad6e8fb60699a7f4c57317321
|
||||
F test/json102.test 796b1c59894c6e0f38fc1a3acb0e690573b952a3
|
||||
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
|
||||
@@ -904,7 +906,7 @@ F test/orderby5.test 8f08a54836d21fb7c70245360751aedd1c2286fb
|
||||
F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
|
||||
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
|
||||
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
|
||||
F test/orderby9.test 88a330ea5fc7bed7e407b28beb0d2b79485ae2cc
|
||||
F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3
|
||||
F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7
|
||||
F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799
|
||||
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
|
||||
@@ -940,7 +942,7 @@ F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
|
||||
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
||||
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.tcl 5af0ca3d6a12471ffd9993c3efc00a553dfb2d41
|
||||
F test/releasetest.tcl afdac5c3429dceb034295617c0a51df9954d467a
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
|
||||
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
|
||||
@@ -1026,9 +1028,9 @@ F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c 857439869d1cb4db35e1c720ee9c2756eb9ea2a0
|
||||
F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135
|
||||
F test/spellfix2.test e5f2bc1dae046dbdd8008f2a84ed7749ff9b325e
|
||||
F test/spellfix2.test 1ff48bb65b6198d21674ae24d19bb136e547585a
|
||||
F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5
|
||||
F test/sqllimits1.test e05786eaed7950ff6a2d00031d001d8a26131e68
|
||||
F test/sqllimits1.test 89b3d5aad05b99f707ee3786bdd4416dccf83304
|
||||
F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf
|
||||
F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
|
||||
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
|
||||
@@ -1041,7 +1043,7 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c
|
||||
F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
|
||||
F test/tabfunc01.test fa9d8dfc75747019e0be98d3b6ac68d18632d328
|
||||
F test/tabfunc01.test 83e63be7b6e3f67b6a03519c9c61bc68efb25f31
|
||||
F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
@@ -1233,7 +1235,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unique2.test 41e7f83c6827605991160a31380148a9fc5f1339
|
||||
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825
|
||||
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
|
||||
F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
|
||||
@@ -1267,7 +1269,7 @@ F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772
|
||||
F test/wal.test dbfc482e10c7263298833bb1fc60b3ac9d6340a1
|
||||
F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
|
||||
F test/wal3.test 2b5445e5da44780b9b44712f5a38523f7aeb0941
|
||||
F test/wal3.test 2ab8e490afe0164bfc89b185c8b2572e0d821f23
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
F test/wal5.test 88b5d9a6a3d1532497ee9f4296f010d66f07e33c
|
||||
F test/wal6.test 4421cd5a2fa99d29cc91ef12fb23bed171ed3a4c
|
||||
@@ -1326,7 +1328,7 @@ F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||
F test/without_rowid3.test aad4f9d383e199349b6c7e508a778f7dff5dff79
|
||||
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862
|
||||
F test/without_rowid6.test db0dbf03c49030aa3c1ba5f618620334bd2baf5f
|
||||
F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e
|
||||
F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
|
||||
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
|
||||
F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
|
||||
@@ -1337,7 +1339,7 @@ F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
|
||||
F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
|
||||
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||
F tool/fuzzershell.c f2fc86dd22df654b28851b85019d3bd007361751
|
||||
F tool/fuzzershell.c 87cc3d6f00239d786d231954289f106131989cc7
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
@@ -1386,7 +1388,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P b42c2e2076da1a61f667876ff2c18e4ebed50a7f
|
||||
R 7600913daf42127c1cd5acb5cae703c1
|
||||
P c9400ff167d9520119081c9b92436794dcd53d22 10a214fdb3c13b25e0bdd15c975c21c8890c47ee
|
||||
R b42897c3c8ae6a403b71a2f426cb59b5
|
||||
U mistachkin
|
||||
Z e012d74d229ec0f8fc3b071b4b2b7de3
|
||||
Z 6c86ab7133863d50d8bbe949c96c8dea
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
c9400ff167d9520119081c9b92436794dcd53d22
|
||||
6b85f8cd4d87d616a3cb362647ae3869c45c2801
|
||||
+100
-31
@@ -591,6 +591,49 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){
|
||||
pCur->iPage = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
** The cursor passed as the only argument must point to a valid entry
|
||||
** when this function is called (i.e. have eState==CURSOR_VALID). This
|
||||
** function saves the current cursor key in variables pCur->nKey and
|
||||
** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error
|
||||
** code otherwise.
|
||||
**
|
||||
** If the cursor is open on an intkey table, then the integer key
|
||||
** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to
|
||||
** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is
|
||||
** set to point to a malloced buffer pCur->nKey bytes in size containing
|
||||
** the key.
|
||||
*/
|
||||
static int saveCursorKey(BtCursor *pCur){
|
||||
int rc;
|
||||
assert( CURSOR_VALID==pCur->eState );
|
||||
assert( 0==pCur->pKey );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
|
||||
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
|
||||
assert( rc==SQLITE_OK ); /* KeySize() cannot fail */
|
||||
|
||||
/* If this is an intKey table, then the above call to BtreeKeySize()
|
||||
** stores the integer key in pCur->nKey. In this case this value is
|
||||
** all that is required. Otherwise, if pCur is not open on an intKey
|
||||
** table, then malloc space for and store the pCur->nKey bytes of key
|
||||
** data. */
|
||||
if( 0==pCur->curIntKey ){
|
||||
void *pKey = sqlite3Malloc( pCur->nKey );
|
||||
if( pKey ){
|
||||
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->pKey = pKey;
|
||||
}else{
|
||||
sqlite3_free(pKey);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
assert( !pCur->curIntKey || !pCur->pKey );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Save the current cursor position in the variables BtCursor.nKey
|
||||
@@ -611,30 +654,8 @@ static int saveCursorPosition(BtCursor *pCur){
|
||||
}else{
|
||||
pCur->skipNext = 0;
|
||||
}
|
||||
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
|
||||
assert( rc==SQLITE_OK ); /* KeySize() cannot fail */
|
||||
|
||||
/* If this is an intKey table, then the above call to BtreeKeySize()
|
||||
** stores the integer key in pCur->nKey. In this case this value is
|
||||
** all that is required. Otherwise, if pCur is not open on an intKey
|
||||
** table, then malloc space for and store the pCur->nKey bytes of key
|
||||
** data.
|
||||
*/
|
||||
if( 0==pCur->curIntKey ){
|
||||
void *pKey = sqlite3Malloc( pCur->nKey );
|
||||
if( pKey ){
|
||||
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->pKey = pKey;
|
||||
}else{
|
||||
sqlite3_free(pKey);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
assert( !pCur->curIntKey || !pCur->pKey );
|
||||
|
||||
rc = saveCursorKey(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
btreeReleaseAllCursorPages(pCur);
|
||||
pCur->eState = CURSOR_REQUIRESEEK;
|
||||
@@ -8026,10 +8047,15 @@ end_insert:
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the entry that the cursor is pointing to. The cursor
|
||||
** is left pointing at an arbitrary location.
|
||||
** Delete the entry that the cursor is pointing to.
|
||||
**
|
||||
** If the second parameter is zero, then the cursor is left pointing at an
|
||||
** arbitrary location after the delete. If it is non-zero, then the cursor
|
||||
** is left in a state such that the next call to BtreeNext() or BtreePrev()
|
||||
** moves it to the same row as it would if the call to BtreeDelete() had
|
||||
** been omitted.
|
||||
*/
|
||||
int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
|
||||
Btree *p = pCur->pBtree;
|
||||
BtShared *pBt = p->pBt;
|
||||
int rc; /* Return code */
|
||||
@@ -8038,6 +8064,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
int iCellIdx; /* Index of cell to delete */
|
||||
int iCellDepth; /* Depth of node containing pCell */
|
||||
u16 szCell; /* Size of the cell being deleted */
|
||||
int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
@@ -8067,10 +8094,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
}
|
||||
|
||||
/* Save the positions of any other cursors open on this table before
|
||||
** making any modifications. Make the page containing the entry to be
|
||||
** deleted writable. Then free any overflow pages associated with the
|
||||
** entry and finally remove the cell itself from within the page.
|
||||
*/
|
||||
** making any modifications. */
|
||||
if( pCur->curFlags & BTCF_Multiple ){
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
if( rc ) return rc;
|
||||
@@ -8082,6 +8106,31 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
||||
}
|
||||
|
||||
/* If the bPreserve flag is set to true, then the cursor position must
|
||||
** be preserved following this delete operation. If the current delete
|
||||
** will cause a b-tree rebalance, then this is done by saving the cursor
|
||||
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
|
||||
** returning.
|
||||
**
|
||||
** Or, if the current delete will not cause a rebalance, then the cursor
|
||||
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
|
||||
** before or after the deleted entry. In this case set bSkipnext to true. */
|
||||
if( bPreserve ){
|
||||
if( !pPage->leaf
|
||||
|| (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
|
||||
){
|
||||
/* A b-tree rebalance will be required after deleting this entry.
|
||||
** Save the cursor key. */
|
||||
rc = saveCursorKey(pCur);
|
||||
if( rc ) return rc;
|
||||
}else{
|
||||
bSkipnext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the page containing the entry to be deleted writable. Then free any
|
||||
** overflow pages associated with the entry and finally remove the cell
|
||||
** itself from within the page. */
|
||||
rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
if( rc ) return rc;
|
||||
rc = clearCell(pPage, pCell, &szCell);
|
||||
@@ -8135,7 +8184,23 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
moveToRoot(pCur);
|
||||
if( bSkipnext ){
|
||||
assert( bPreserve && pCur->iPage==iCellDepth );
|
||||
assert( pPage==pCur->apPage[pCur->iPage] );
|
||||
assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
|
||||
pCur->eState = CURSOR_SKIPNEXT;
|
||||
if( iCellIdx>=pPage->nCell ){
|
||||
pCur->skipNext = -1;
|
||||
pCur->aiIdx[iCellDepth] = pPage->nCell-1;
|
||||
}else{
|
||||
pCur->skipNext = 1;
|
||||
}
|
||||
}else{
|
||||
rc = moveToRoot(pCur);
|
||||
if( bPreserve ){
|
||||
pCur->eState = CURSOR_REQUIRESEEK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -8857,6 +8922,10 @@ static void checkList(
|
||||
#endif
|
||||
iPage = get4byte(pOvflData);
|
||||
sqlite3PagerUnref(pOvflPage);
|
||||
|
||||
if( isFreeList && N<(iPage!=0) ){
|
||||
checkAppendMsg(pCheck, "free-page count in header is too small");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
|
||||
+1
-1
@@ -185,7 +185,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
);
|
||||
int sqlite3BtreeCursorHasMoved(BtCursor*);
|
||||
int sqlite3BtreeCursorRestore(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*);
|
||||
int sqlite3BtreeDelete(BtCursor*, int);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData,
|
||||
int nZero, int bias, int seekResult);
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ Table *sqlite3LocateTable(
|
||||
}
|
||||
pParse->checkSchema = 1;
|
||||
}
|
||||
#if SQLITE_USER_AUTHENICATION
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
else if( pParse->db->auth.authLevel<UAUTH_User ){
|
||||
sqlite3ErrorMsg(pParse, "user not authenticated");
|
||||
p = 0;
|
||||
|
||||
+85
-41
@@ -235,7 +235,7 @@ void sqlite3DeleteFrom(
|
||||
int iDb; /* Database number */
|
||||
int memCnt = -1; /* Memory cell used for change counting */
|
||||
int rcauth; /* Value returned by authorization callback */
|
||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */
|
||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||
u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
|
||||
Index *pPk; /* The PRIMARY KEY index on the table */
|
||||
@@ -247,12 +247,12 @@ void sqlite3DeleteFrom(
|
||||
int iRowSet = 0; /* Register for rowset of rows to delete */
|
||||
int addrBypass = 0; /* Address of jump over the delete logic */
|
||||
int addrLoop = 0; /* Top of the delete loop */
|
||||
int addrDelete = 0; /* Jump directly to the delete logic */
|
||||
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
Trigger *pTrigger; /* List of table triggers, if required */
|
||||
int bComplex; /* True if there are either triggers or FKs */
|
||||
#endif
|
||||
|
||||
memset(&sContext, 0, sizeof(sContext));
|
||||
@@ -276,9 +276,11 @@ void sqlite3DeleteFrom(
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
|
||||
#else
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
# define bComplex 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
@@ -359,8 +361,10 @@ void sqlite3DeleteFrom(
|
||||
** It is easier just to erase the whole table. Prior to version 3.6.5,
|
||||
** this optimization caused the row change count (the value returned by
|
||||
** API function sqlite3_count_changes) to be set incorrectly. */
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab)
|
||||
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
|
||||
if( rcauth==SQLITE_OK
|
||||
&& pWhere==0
|
||||
&& !bComplex
|
||||
&& !IsVirtual(pTab)
|
||||
){
|
||||
assert( !isView );
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
@@ -375,6 +379,8 @@ void sqlite3DeleteFrom(
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
{
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
|
||||
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
|
||||
if( HasRowid(pTab) ){
|
||||
/* For a rowid table, initialize the RowSet to an empty set */
|
||||
pPk = 0;
|
||||
@@ -395,13 +401,18 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
|
||||
/* Construct a query to find the rowid or primary key for every row
|
||||
** to be deleted, based on the WHERE clause.
|
||||
** to be deleted, based on the WHERE clause. Set variable eOnePass
|
||||
** to indicate the strategy used to implement this delete:
|
||||
**
|
||||
** ONEPASS_OFF: Two-pass approach - use a FIFO for rowids/PK values.
|
||||
** ONEPASS_SINGLE: One-pass approach - at most one row deleted.
|
||||
** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
|
||||
iTabCur+1);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
assert( IsVirtual(pTab)==0 || eOnePass==ONEPASS_OFF );
|
||||
assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
|
||||
|
||||
/* Keep track of the number of rows to be deleted */
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
@@ -422,11 +433,10 @@ void sqlite3DeleteFrom(
|
||||
if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
||||
}
|
||||
|
||||
if( okOnePass ){
|
||||
/* For ONEPASS, no need to store the rowid/primary-key. There is only
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
/* For ONEPASS, no need to store the rowid/primary-key. There is only
|
||||
** one, so just keep it in its register(s) and fall through to the
|
||||
** delete code.
|
||||
*/
|
||||
** delete code. */
|
||||
nKey = nPk; /* OP_Found will use an unpacked key */
|
||||
aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
|
||||
if( aToOpen==0 ){
|
||||
@@ -438,27 +448,27 @@ void sqlite3DeleteFrom(
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
|
||||
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
|
||||
addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
|
||||
}else if( pPk ){
|
||||
/* Construct a composite key for the row to be deleted and remember it */
|
||||
iKey = ++pParse->nMem;
|
||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||
}else{
|
||||
/* Get the rowid of the row to be deleted and remember it in the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
if( pPk ){
|
||||
/* Add the PK key for this row to the temporary table */
|
||||
iKey = ++pParse->nMem;
|
||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||
}else{
|
||||
/* Add the rowid of the row to be deleted to the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
}
|
||||
}
|
||||
|
||||
/* End of the WHERE loop */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
if( okOnePass ){
|
||||
/* Bypass the delete logic below if the WHERE loop found zero rows */
|
||||
/* If this DELETE cannot use the ONEPASS strategy, this is the
|
||||
** end of the WHERE loop */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
addrBypass = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeGoto(v, addrBypass);
|
||||
sqlite3VdbeJumpHere(v, addrDelete);
|
||||
}else{
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
/* Unless this is a view, open cursors for the table we are
|
||||
@@ -467,20 +477,23 @@ void sqlite3DeleteFrom(
|
||||
** triggers.
|
||||
*/
|
||||
if( !isView ){
|
||||
int iAddrOnce = 0;
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
}
|
||||
testcase( IsVirtual(pTab) );
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
|
||||
&iDataCur, &iIdxCur);
|
||||
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
|
||||
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
|
||||
if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
|
||||
}
|
||||
|
||||
/* Set up a loop over the rowids/primary-keys that were found in the
|
||||
** where-clause loop above.
|
||||
*/
|
||||
if( okOnePass ){
|
||||
/* Just one row. Hence the top-of-loop is a no-op */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
|
||||
assert( !IsVirtual(pTab) );
|
||||
if( aToOpen[iDataCur-iTabCur] ){
|
||||
assert( pPk!=0 || pTab->pSelect!=0 );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
|
||||
@@ -508,13 +521,18 @@ void sqlite3DeleteFrom(
|
||||
#endif
|
||||
{
|
||||
int count = (pParse->nested==0); /* True to count changes */
|
||||
int iIdxNoSeek = -1;
|
||||
if( bComplex==0 && aiCurOnePass[1]!=iDataCur ){
|
||||
iIdxNoSeek = aiCurOnePass[1];
|
||||
}
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
iKey, nKey, count, OE_Default, okOnePass);
|
||||
iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek);
|
||||
}
|
||||
|
||||
/* End of the loop over all rowids/primary-keys. */
|
||||
if( okOnePass ){
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
sqlite3VdbeResolveLabel(v, addrBypass);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else if( pPk ){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addrLoop);
|
||||
@@ -586,6 +604,25 @@ delete_from_cleanup:
|
||||
** sequence of nPk memory cells starting at iPk. If nPk==0 that means
|
||||
** that a search record formed from OP_MakeRecord is contained in the
|
||||
** single memory location iPk.
|
||||
**
|
||||
** eMode:
|
||||
** Parameter eMode may be passed either ONEPASS_OFF (0), ONEPASS_SINGLE, or
|
||||
** ONEPASS_MULTI. If eMode is not ONEPASS_OFF, then the cursor
|
||||
** iDataCur already points to the row to delete. If eMode is ONEPASS_OFF
|
||||
** then this function must seek iDataCur to the entry identified by iPk
|
||||
** and nPk before reading from it.
|
||||
**
|
||||
** If eMode is ONEPASS_MULTI, then this call is being made as part
|
||||
** of a ONEPASS delete that affects multiple rows. In this case, if
|
||||
** iIdxNoSeek is a valid cursor number (>=0), then its position should
|
||||
** be preserved following the delete operation. Or, if iIdxNoSeek is not
|
||||
** a valid cursor number, the position of iDataCur should be preserved
|
||||
** instead.
|
||||
**
|
||||
** iIdxNoSeek:
|
||||
** If iIdxNoSeek is a valid cursor number (>=0), then it identifies an
|
||||
** index cursor (from within array of cursors starting at iIdxCur) that
|
||||
** already points to the index entry to be deleted.
|
||||
*/
|
||||
void sqlite3GenerateRowDelete(
|
||||
Parse *pParse, /* Parsing context */
|
||||
@@ -597,7 +634,8 @@ void sqlite3GenerateRowDelete(
|
||||
i16 nPk, /* Number of PRIMARY KEY memory cells */
|
||||
u8 count, /* If non-zero, increment the row change counter */
|
||||
u8 onconf, /* Default ON CONFLICT policy for triggers */
|
||||
u8 bNoSeek /* iDataCur is already pointing to the row to delete */
|
||||
u8 eMode, /* ONEPASS_OFF, _SINGLE, or _MULTI. See above */
|
||||
int iIdxNoSeek /* Cursor number of cursor that does not need seeking */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe; /* Vdbe */
|
||||
int iOld = 0; /* First register in OLD.* array */
|
||||
@@ -614,7 +652,7 @@ void sqlite3GenerateRowDelete(
|
||||
** not attempt to delete it or fire any DELETE triggers. */
|
||||
iLabel = sqlite3VdbeMakeLabel(v);
|
||||
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||
if( !bNoSeek ){
|
||||
if( eMode==ONEPASS_OFF ){
|
||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotExists);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotFound);
|
||||
@@ -674,11 +712,15 @@ void sqlite3GenerateRowDelete(
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
if( pTab->pSelect==0 ){
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( count ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
if( iIdxNoSeek>=0 ){
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);
|
||||
}
|
||||
sqlite3VdbeChangeP5(v, eMode==ONEPASS_MULTI);
|
||||
}
|
||||
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
@@ -721,7 +763,8 @@ void sqlite3GenerateRowIndexDelete(
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iDataCur, /* Cursor of table holding data. */
|
||||
int iIdxCur, /* First index cursor */
|
||||
int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||
int *aRegIdx, /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||
int iIdxNoSeek /* Do not delete from this cursor */
|
||||
){
|
||||
int i; /* Index loop counter */
|
||||
int r1 = -1; /* Register holding an index key */
|
||||
@@ -737,11 +780,12 @@ void sqlite3GenerateRowIndexDelete(
|
||||
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
|
||||
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
|
||||
if( pIdx==pPk ) continue;
|
||||
if( iIdxCur+i==iIdxNoSeek ) continue;
|
||||
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
|
||||
&iPartIdxLabel, pPrior, r1);
|
||||
&iPartIdxLabel, pPrior, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
|
||||
pPrior = pIdx;
|
||||
}
|
||||
|
||||
+9
-5
@@ -1347,10 +1347,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regNewData, 1, 0, OE_Replace, 1);
|
||||
}else if( pTab->pIndex ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
||||
regNewData, 1, 0, OE_Replace,
|
||||
ONEPASS_SINGLE, -1);
|
||||
}else{
|
||||
if( pTab->pIndex ){
|
||||
sqlite3MultiWrite(pParse);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1);
|
||||
}
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
@@ -1528,7 +1531,8 @@ void sqlite3GenerateConstraintChecks(
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
regR, nPkField, 0, OE_Replace, pIdx==pPk);
|
||||
regR, nPkField, 0, OE_Replace,
|
||||
(pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), -1);
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
+2
-2
@@ -413,7 +413,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
|
||||
assert( pCache->pGroup==&pcache1.grp );
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
#endif
|
||||
if( benignMalloc ) sqlite3BeginBenignMalloc();
|
||||
if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
pPg = pcache1Alloc(pCache->szPage);
|
||||
p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
|
||||
@@ -426,7 +426,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
|
||||
pPg = pcache1Alloc(pCache->szAlloc);
|
||||
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
|
||||
#endif
|
||||
if( benignMalloc ) sqlite3EndBenignMalloc();
|
||||
if( benignMalloc ){ sqlite3EndBenignMalloc(); }
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
#endif
|
||||
|
||||
+7
-2
@@ -355,8 +355,13 @@ static int lookupName(
|
||||
/*
|
||||
** Perhaps the name is a reference to the ROWID
|
||||
*/
|
||||
if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol)
|
||||
&& VisibleRowid(pMatch->pTab) ){
|
||||
if( cnt==0
|
||||
&& cntTab==1
|
||||
&& pMatch
|
||||
&& (pNC->ncFlags & NC_IdxExpr)==0
|
||||
&& sqlite3IsRowid(zCol)
|
||||
&& VisibleRowid(pMatch->pTab)
|
||||
){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1; /* IMP: R-44911-55124 */
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
|
||||
+4
-1
@@ -4267,9 +4267,12 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
pTab->nRef++;
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
|
||||
if( pTab->pSelect || IsVirtual(pTab) ){
|
||||
/* We reach here if the named table is a really a view */
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
|
||||
assert( pFrom->pSelect==0 );
|
||||
if( pFrom->fg.isTabFunc && !IsVirtual(pTab) ){
|
||||
sqlite3ErrorMsg(pParse, "'%s' is not a function", pTab->zName);
|
||||
return WRC_Abort;
|
||||
}
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
|
||||
sqlite3SelectSetName(pFrom->pSelect, pTab->zName);
|
||||
sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
||||
|
||||
+4
-1
@@ -3630,7 +3630,7 @@ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
|
||||
**
|
||||
** See also: [sqlite3_bind_blob|sqlite3_bind()],
|
||||
** [sqlite3_bind_parameter_count()], and
|
||||
** [sqlite3_bind_parameter_index()].
|
||||
** [sqlite3_bind_parameter_name()].
|
||||
*/
|
||||
int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
|
||||
|
||||
@@ -6127,6 +6127,9 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
|
||||
** <li> SQLITE_MUTEX_STATIC_APP1
|
||||
** <li> SQLITE_MUTEX_STATIC_APP2
|
||||
** <li> SQLITE_MUTEX_STATIC_APP3
|
||||
** <li> SQLITE_MUTEX_STATIC_VFS1
|
||||
** <li> SQLITE_MUTEX_STATIC_VFS2
|
||||
** <li> SQLITE_MUTEX_STATIC_VFS3
|
||||
** </ul>
|
||||
**
|
||||
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
|
||||
|
||||
+7
-2
@@ -2352,6 +2352,7 @@ struct SrcList {
|
||||
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
|
||||
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
|
||||
#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
|
||||
#define WHERE_ONEPASS_MULTIROW 0x2000 /* ONEPASS is ok with multiple rows */
|
||||
|
||||
/* Allowed return values from sqlite3WhereIsDistinct()
|
||||
*/
|
||||
@@ -3391,6 +3392,9 @@ int sqlite3WhereIsSorted(WhereInfo*);
|
||||
int sqlite3WhereContinueLabel(WhereInfo*);
|
||||
int sqlite3WhereBreakLabel(WhereInfo*);
|
||||
int sqlite3WhereOkOnePass(WhereInfo*, int*);
|
||||
#define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */
|
||||
#define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */
|
||||
#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
|
||||
void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
|
||||
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
|
||||
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
|
||||
@@ -3451,8 +3455,9 @@ int sqlite3ExprIsInteger(Expr*, int*);
|
||||
int sqlite3ExprCanBeNull(const Expr*);
|
||||
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
||||
int sqlite3IsRowid(const char*);
|
||||
void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
|
||||
void sqlite3GenerateRowDelete(
|
||||
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
|
||||
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
|
||||
void sqlite3ResolvePartIdxLabel(Parse*,int);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
||||
|
||||
+1
-1
@@ -587,7 +587,7 @@ void sqlite3Update(
|
||||
}
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
|
||||
|
||||
/* If changing the record number, delete the old record. */
|
||||
if( hasFK || chngKey || pPk!=0 ){
|
||||
|
||||
+29
-13
@@ -3968,9 +3968,10 @@ case OP_Found: { /* jump, in3 */
|
||||
**
|
||||
** P1 is the index of a cursor open on an SQL table btree (with integer
|
||||
** keys). P3 is an integer rowid. If P1 does not contain a record with
|
||||
** rowid P3 then jump immediately to P2. If P1 does contain a record
|
||||
** with rowid P3 then leave the cursor pointing at that record and fall
|
||||
** through to the next instruction.
|
||||
** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an
|
||||
** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then
|
||||
** leave the cursor pointing at that record and fall through to the next
|
||||
** instruction.
|
||||
**
|
||||
** The OP_NotFound opcode performs the same operation on index btrees
|
||||
** (with arbitrary multi-value keys).
|
||||
@@ -4002,13 +4003,21 @@ case OP_NotExists: { /* jump, in3 */
|
||||
res = 0;
|
||||
iKey = pIn3->u.i;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
|
||||
assert( rc==SQLITE_OK || res==0 );
|
||||
pC->movetoTarget = iKey; /* Used by OP_Delete */
|
||||
pC->nullRow = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
pC->deferredMoveto = 0;
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
pC->seekResult = res;
|
||||
if( res!=0 ) goto jump_to_p2;
|
||||
if( res!=0 ){
|
||||
assert( rc==SQLITE_OK );
|
||||
if( pOp->p2==0 ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
goto jump_to_p2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4274,14 +4283,15 @@ case OP_InsertInt: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Delete P1 P2 * P4 *
|
||||
/* Opcode: Delete P1 P2 * P4 P5
|
||||
**
|
||||
** Delete the record at which the P1 cursor is currently pointing.
|
||||
**
|
||||
** The cursor will be left pointing at either the next or the previous
|
||||
** record in the table. If it is left pointing at the next record, then
|
||||
** the next Next instruction will be a no-op. Hence it is OK to delete
|
||||
** a record from within a Next loop.
|
||||
** If the P5 parameter is non-zero, the cursor will be left pointing at
|
||||
** either the next or the previous record in the table. If it is left
|
||||
** pointing at the next record, then the next Next instruction will be a
|
||||
** no-op. As a result, in this case it is OK to delete a record from within a
|
||||
** Next loop. If P5 is zero, then the cursor is left in an undefined state.
|
||||
**
|
||||
** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
|
||||
** incremented (otherwise not).
|
||||
@@ -4296,6 +4306,7 @@ case OP_InsertInt: {
|
||||
*/
|
||||
case OP_Delete: {
|
||||
VdbeCursor *pC;
|
||||
u8 hasUpdateCallback;
|
||||
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
@@ -4303,22 +4314,27 @@ case OP_Delete: {
|
||||
assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
|
||||
assert( pC->deferredMoveto==0 );
|
||||
|
||||
hasUpdateCallback = db->xUpdateCallback && pOp->p4.z && pC->isTable;
|
||||
if( pOp->p5 && hasUpdateCallback ){
|
||||
sqlite3BtreeKeySize(pC->pCursor, &pC->movetoTarget);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The seek operation that positioned the cursor prior to OP_Delete will
|
||||
** have also set the pC->movetoTarget field to the rowid of the row that
|
||||
** is being deleted */
|
||||
if( pOp->p4.z && pC->isTable ){
|
||||
if( pOp->p4.z && pC->isTable && pOp->p5==0 ){
|
||||
i64 iKey = 0;
|
||||
sqlite3BtreeKeySize(pC->pCursor, &iKey);
|
||||
assert( pC->movetoTarget==iKey );
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = sqlite3BtreeDelete(pC->pCursor);
|
||||
rc = sqlite3BtreeDelete(pC->pCursor, pOp->p5);
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
|
||||
/* Invoke the update-hook if required. */
|
||||
if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z && pC->isTable ){
|
||||
if( rc==SQLITE_OK && hasUpdateCallback ){
|
||||
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE,
|
||||
db->aDb[pC->iDb].zName, pOp->p4.z, pC->movetoTarget);
|
||||
assert( pC->iDb>=0 );
|
||||
@@ -4857,7 +4873,7 @@ case OP_IdxDelete: {
|
||||
#endif
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
|
||||
if( rc==SQLITE_OK && res==0 ){
|
||||
rc = sqlite3BtreeDelete(pCrsr);
|
||||
rc = sqlite3BtreeDelete(pCrsr, 0);
|
||||
}
|
||||
assert( pC->deferredMoveto==0 );
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
|
||||
+12
-11
@@ -912,18 +912,19 @@ static const Mem *columnNullValue(void){
|
||||
#endif
|
||||
= {
|
||||
/* .u = */ {0},
|
||||
/* .flags = */ MEM_Null,
|
||||
/* .enc = */ 0,
|
||||
/* .n = */ 0,
|
||||
/* .z = */ 0,
|
||||
/* .zMalloc = */ 0,
|
||||
/* .szMalloc = */ 0,
|
||||
/* .iPadding1 = */ 0,
|
||||
/* .db = */ 0,
|
||||
/* .xDel = */ 0,
|
||||
/* .flags = */ (u16)MEM_Null,
|
||||
/* .enc = */ (u8)0,
|
||||
/* .eSubtype = */ (u8)0,
|
||||
/* .n = */ (int)0,
|
||||
/* .z = */ (char*)0,
|
||||
/* .zMalloc = */ (char*)0,
|
||||
/* .szMalloc = */ (int)0,
|
||||
/* .uTemp = */ (u32)0,
|
||||
/* .db = */ (sqlite3*)0,
|
||||
/* .xDel = */ (void(*)(void*))0,
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* .pScopyFrom = */ 0,
|
||||
/* .pFiller = */ 0,
|
||||
/* .pScopyFrom = */ (Mem*)0,
|
||||
/* .pFiller = */ (void*)0,
|
||||
#endif
|
||||
};
|
||||
return &nullMem;
|
||||
|
||||
+27
-15
@@ -69,9 +69,11 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if an UPDATE or DELETE statement can operate directly on
|
||||
** the rowids returned by a WHERE clause. Return FALSE if doing an
|
||||
** UPDATE or DELETE might change subsequent WHERE clause results.
|
||||
** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to
|
||||
** operate directly on the rowis returned by a WHERE clause. Return
|
||||
** ONEPASS_SINGLE (1) if the statement can operation directly because only
|
||||
** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass
|
||||
** optimization can be used on multiple
|
||||
**
|
||||
** If the ONEPASS optimization is used (if this routine returns true)
|
||||
** then also write the indices of open cursors used by ONEPASS
|
||||
@@ -85,7 +87,7 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
|
||||
*/
|
||||
int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
|
||||
memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);
|
||||
return pWInfo->okOnePass;
|
||||
return pWInfo->eOnePass;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3958,6 +3960,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3 *db; /* Database connection */
|
||||
int rc; /* Return code */
|
||||
|
||||
assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
|
||||
(wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||
));
|
||||
|
||||
/* Variable initialization */
|
||||
db = pParse->db;
|
||||
@@ -4013,6 +4019,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v);
|
||||
pWInfo->wctrlFlags = wctrlFlags;
|
||||
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
|
||||
assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */
|
||||
pMaskSet = &pWInfo->sMaskSet;
|
||||
sWLB.pWInfo = pWInfo;
|
||||
sWLB.pWC = &pWInfo->sWC;
|
||||
@@ -4199,11 +4206,16 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** the statement to update or delete a single row.
|
||||
*/
|
||||
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
|
||||
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
||||
&& (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){
|
||||
pWInfo->okOnePass = 1;
|
||||
if( HasRowid(pTabList->a[0].pTab) ){
|
||||
pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
||||
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
|
||||
int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
|
||||
int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
|
||||
if( bOnerow || ( (wctrlFlags & WHERE_ONEPASS_MULTIROW)
|
||||
&& 0==(wsFlags & WHERE_VIRTUALTABLE)
|
||||
)){
|
||||
pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
|
||||
if( HasRowid(pTabList->a[0].pTab) ){
|
||||
pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4234,15 +4246,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
|
||||
int op = OP_OpenRead;
|
||||
if( pWInfo->okOnePass ){
|
||||
if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
op = OP_OpenWrite;
|
||||
pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
|
||||
};
|
||||
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
|
||||
assert( pTabItem->iCursor==pLevel->iTabCur );
|
||||
testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 );
|
||||
testcase( !pWInfo->okOnePass && pTab->nCol==BMS );
|
||||
if( !pWInfo->okOnePass && pTab->nCol<BMS && HasRowid(pTab) ){
|
||||
testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS-1 );
|
||||
testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS );
|
||||
if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){
|
||||
Bitmask b = pTabItem->colUsed;
|
||||
int n = 0;
|
||||
for(; b; b=b>>1, n++){}
|
||||
@@ -4270,7 +4282,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** WITHOUT ROWID table. No need for a separate index */
|
||||
iIndexCur = pLevel->iTabCur;
|
||||
op = 0;
|
||||
}else if( pWInfo->okOnePass ){
|
||||
}else if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
Index *pJ = pTabItem->pTab->pIndex;
|
||||
iIndexCur = iIdxCur;
|
||||
assert( wctrlFlags & WHERE_ONEPASS_DESIRED );
|
||||
@@ -4478,7 +4490,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||
){
|
||||
int ws = pLoop->wsFlags;
|
||||
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
|
||||
if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
|
||||
}
|
||||
if( (ws & WHERE_INDEXED)!=0
|
||||
|
||||
+1
-1
@@ -412,7 +412,7 @@ struct WhereInfo {
|
||||
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
|
||||
i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
|
||||
u8 sorted; /* True if really sorted (not just grouped) */
|
||||
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
|
||||
u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
|
||||
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
|
||||
u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
|
||||
u8 nLevel; /* Number of nested loop */
|
||||
|
||||
+10
-5
@@ -65,7 +65,7 @@ static const char *explainIndexColumnName(Index *pIdx, int i){
|
||||
**
|
||||
** "a=? AND b>?"
|
||||
*/
|
||||
static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
|
||||
static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
Index *pIndex = pLoop->u.btree.pIndex;
|
||||
u16 nEq = pLoop->u.btree.nEq;
|
||||
u16 nSkip = pLoop->nSkip;
|
||||
@@ -166,7 +166,7 @@ int sqlite3WhereExplainOneScan(
|
||||
if( zFmt ){
|
||||
sqlite3StrAccumAppend(&str, " USING ", 7);
|
||||
sqlite3XPrintf(&str, 0, zFmt, pIdx->zName);
|
||||
explainIndexRange(&str, pLoop, pItem->pTab);
|
||||
explainIndexRange(&str, pLoop);
|
||||
}
|
||||
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
|
||||
const char *zRangeOp;
|
||||
@@ -514,8 +514,8 @@ static int codeAllEqualityTerms(
|
||||
sqlite3VdbeJumpHere(v, j);
|
||||
for(j=0; j<nSkip; j++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
|
||||
assert( pIdx->aiColumn[j]>=0 );
|
||||
VdbeComment((v, "%s", pIdx->pTable->aCol[pIdx->aiColumn[j]].zName));
|
||||
testcase( pIdx->aiColumn[j]==(-2) );
|
||||
VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,7 +1069,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iRowidReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
|
||||
if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
|
||||
}
|
||||
}else if( iCur!=iIdxCur ){
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix corrupt2
|
||||
|
||||
# Do not use a codec for tests in this file, as the database file is
|
||||
# manipulated directly using tcl scripts (using the [hexio_write] command).
|
||||
@@ -558,4 +559,51 @@ ifcapable autovacuum {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that PRAGMA integrity_check detects cases where the freelist-count
|
||||
# header field is smaller than the actual number of pages on the freelist.
|
||||
#
|
||||
|
||||
reset_db
|
||||
do_execsql_test 14.0 {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(3500));
|
||||
DELETE FROM t1;
|
||||
}
|
||||
|
||||
do_execsql_test 14.1 {
|
||||
PRAGMA integrity_check;
|
||||
PRAGMA freelist_count;
|
||||
} {ok 3}
|
||||
|
||||
# There are now 3 free pages. Modify the header-field so that it
|
||||
# (incorrectly) says that just 2 are free.
|
||||
do_test 14.2 {
|
||||
db close
|
||||
hexio_write test.db 36 [hexio_render_int32 2]
|
||||
sqlite3 db test.db
|
||||
execsql { PRAGMA freelist_count }
|
||||
} {2}
|
||||
|
||||
do_execsql_test 14.3 {
|
||||
PRAGMA integrity_check;
|
||||
} {{*** in database main ***
|
||||
Main freelist: free-page count in header is too small}}
|
||||
|
||||
# Use 2 of the free pages on the free-list.
|
||||
#
|
||||
do_execsql_test 14.4 {
|
||||
INSERT INTO t1 VALUES(randomblob(2500));
|
||||
PRAGMA freelist_count;
|
||||
} {0}
|
||||
|
||||
do_execsql_test 14.5 {
|
||||
PRAGMA integrity_check;
|
||||
} {{*** in database main ***
|
||||
Page 3 is never used}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -189,7 +189,11 @@ do_test corruptC-2.7 {
|
||||
catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
# corruption (seed 179069)
|
||||
# Obsolete. With single-pass DELETE the corruption in the
|
||||
# main database is not detected.
|
||||
if 0 {
|
||||
do_test corruptC-2.8 {
|
||||
db close
|
||||
forcecopy test.bu test.db
|
||||
@@ -204,6 +208,7 @@ do_test corruptC-2.8 {
|
||||
sqlite3 db test.db
|
||||
catchsql {BEGIN; DELETE FROM t1 WHERE x>13; ROLLBACK;}
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
|
||||
# corruption (seed 170434)
|
||||
#
|
||||
|
||||
@@ -68,7 +68,6 @@ do_test delete-3.1.7 {
|
||||
} {1 2 4 16}
|
||||
integrity_check delete-3.2
|
||||
|
||||
|
||||
# Semantic errors in the WHERE clause
|
||||
#
|
||||
do_test delete-4.1 {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# 2005 August 24
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is a test of the DELETE command.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix delete4
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
INSERT INTO t1 VALUES(1, 0);
|
||||
INSERT INTO t1 VALUES(2, 1);
|
||||
INSERT INTO t1 VALUES(3, 0);
|
||||
INSERT INTO t1 VALUES(4, 1);
|
||||
INSERT INTO t1 VALUES(5, 0);
|
||||
INSERT INTO t1 VALUES(6, 1);
|
||||
INSERT INTO t1 VALUES(7, 0);
|
||||
INSERT INTO t1 VALUES(8, 1);
|
||||
}
|
||||
do_execsql_test 1.2 {
|
||||
DELETE FROM t1 WHERE y=1;
|
||||
}
|
||||
do_execsql_test 1.3 {
|
||||
SELECT x FROM t1;
|
||||
} {1 3 5 7}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z);
|
||||
INSERT INTO t1 VALUES(1, 0, randomblob(200));
|
||||
INSERT INTO t1 VALUES(2, 1, randomblob(200));
|
||||
INSERT INTO t1 VALUES(3, 0, randomblob(200));
|
||||
INSERT INTO t1 VALUES(4, 1, randomblob(200));
|
||||
INSERT INTO t1 VALUES(5, 0, randomblob(200));
|
||||
INSERT INTO t1 VALUES(6, 1, randomblob(200));
|
||||
INSERT INTO t1 VALUES(7, 0, randomblob(200));
|
||||
INSERT INTO t1 VALUES(8, 1, randomblob(200));
|
||||
}
|
||||
do_execsql_test 2.2 {
|
||||
DELETE FROM t1 WHERE y=1;
|
||||
}
|
||||
do_execsql_test 2.3 {
|
||||
SELECT x FROM t1;
|
||||
} {1 3 5 7}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t1(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(2, 4);
|
||||
INSERT INTO t1 VALUES(1, 5);
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
SELECT * FROM t1;
|
||||
} {2 4}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# DELETE statement that uses the OR optimization
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
|
||||
CREATE INDEX i1a ON t1(a);
|
||||
CREATE INDEX i1b ON t1(b);
|
||||
INSERT INTO t1 VALUES(1, 'one', 'i');
|
||||
INSERT INTO t1 VALUES(2, 'two', 'ii');
|
||||
INSERT INTO t1 VALUES(3, 'three', 'iii');
|
||||
INSERT INTO t1 VALUES(4, 'four', 'iv');
|
||||
INSERT INTO t1 VALUES(5, 'one', 'i');
|
||||
INSERT INTO t1 VALUES(6, 'two', 'ii');
|
||||
INSERT INTO t1 VALUES(7, 'three', 'iii');
|
||||
INSERT INTO t1 VALUES(8, 'four', 'iv');
|
||||
} {}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
DELETE FROM t1 WHERE a='two' OR b='iv';
|
||||
}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
SELECT i FROM t1 ORDER BY i;
|
||||
} {1 3 5 7}
|
||||
|
||||
do_execsql_test 3.4 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
|
||||
finish_test
|
||||
+95
-1
@@ -296,6 +296,38 @@ static void readfileFunc(
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "writefile(X,Y)" SQL function. The argument Y
|
||||
** is written into file X. The number of bytes written is returned. Or
|
||||
** NULL is returned if something goes wrong, such as being unable to open
|
||||
** file X for writing.
|
||||
*/
|
||||
static void writefileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
FILE *out;
|
||||
const char *z;
|
||||
sqlite3_int64 rc;
|
||||
const char *zFile;
|
||||
|
||||
(void)argc;
|
||||
zFile = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zFile==0 ) return;
|
||||
out = fopen(zFile, "wb");
|
||||
if( out==0 ) return;
|
||||
z = (const char*)sqlite3_value_blob(argv[1]);
|
||||
if( z==0 ){
|
||||
rc = 0;
|
||||
}else{
|
||||
rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
|
||||
}
|
||||
fclose(out);
|
||||
sqlite3_result_int64(context, rc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load a list of Blob objects from the database
|
||||
*/
|
||||
@@ -751,6 +783,8 @@ static void showHelp(void){
|
||||
"Options:\n"
|
||||
" --cell-size-check Set the PRAGMA cell_size_check=ON\n"
|
||||
" --dbid N Use only the database where dbid=N\n"
|
||||
" --export-db DIR Write databases to files(s) in DIR. Works with --dbid\n"
|
||||
" --export-sql DIR Write SQL to file(s) in DIR. Also works with --sqlid\n"
|
||||
" --help Show this help text\n"
|
||||
" -q Reduced output\n"
|
||||
" --quiet Reduced output\n"
|
||||
@@ -763,7 +797,7 @@ static void showHelp(void){
|
||||
" --rebuild Rebuild and vacuum the database file\n"
|
||||
" --result-trace Show the results of each SQL command\n"
|
||||
" --sqlid N Use only SQL where sqlid=N\n"
|
||||
" --timeline N Abort if any single test case needs more than N seconds\n"
|
||||
" --timeout N Abort if any single test case needs more than N seconds\n"
|
||||
" -v Increased output\n"
|
||||
" --verbose Increased output\n"
|
||||
);
|
||||
@@ -799,6 +833,8 @@ int main(int argc, char **argv){
|
||||
int sqlFuzz = 0; /* True for SQL fuzz testing. False for DB fuzz */
|
||||
int iTimeout = 120; /* Default 120-second timeout */
|
||||
int nMem = 0; /* Memory limit */
|
||||
char *zExpDb = 0; /* Write Databases to files in this directory */
|
||||
char *zExpSql = 0; /* Write SQL to files in this directory */
|
||||
|
||||
iBegin = timeOfDay();
|
||||
#ifdef __unix__
|
||||
@@ -818,6 +854,14 @@ int main(int argc, char **argv){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
onlyDbid = integerValue(argv[++i]);
|
||||
}else
|
||||
if( strcmp(z,"export-db")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
zExpDb = argv[++i];
|
||||
}else
|
||||
if( strcmp(z,"export-sql")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
zExpSql = argv[++i];
|
||||
}else
|
||||
if( strcmp(z,"help")==0 ){
|
||||
showHelp();
|
||||
return 0;
|
||||
@@ -943,6 +987,50 @@ int main(int argc, char **argv){
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
if( zExpDb!=0 || zExpSql!=0 ){
|
||||
sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
||||
writefileFunc, 0, 0);
|
||||
if( zExpDb!=0 ){
|
||||
const char *zExDb =
|
||||
"SELECT writefile(printf('%s/db%06d.db',?1,dbid),dbcontent),"
|
||||
" dbid, printf('%s/db%06d.db',?1,dbid), length(dbcontent)"
|
||||
" FROM db WHERE ?2<0 OR dbid=?2;";
|
||||
rc = sqlite3_prepare_v2(db, zExDb, -1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare statement [%s]: %s",
|
||||
zExDb, sqlite3_errmsg(db));
|
||||
sqlite3_bind_text64(pStmt, 1, zExpDb, strlen(zExpDb),
|
||||
SQLITE_STATIC, SQLITE_UTF8);
|
||||
sqlite3_bind_int(pStmt, 2, onlyDbid);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("write db-%d (%d bytes) into %s\n",
|
||||
sqlite3_column_int(pStmt,1),
|
||||
sqlite3_column_int(pStmt,3),
|
||||
sqlite3_column_text(pStmt,2));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
if( zExpSql!=0 ){
|
||||
const char *zExSql =
|
||||
"SELECT writefile(printf('%s/sql%06d.txt',?1,sqlid),sqltext),"
|
||||
" sqlid, printf('%s/sql%06d.txt',?1,sqlid), length(sqltext)"
|
||||
" FROM xsql WHERE ?2<0 OR sqlid=?2;";
|
||||
rc = sqlite3_prepare_v2(db, zExSql, -1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare statement [%s]: %s",
|
||||
zExSql, sqlite3_errmsg(db));
|
||||
sqlite3_bind_text64(pStmt, 1, zExpSql, strlen(zExpSql),
|
||||
SQLITE_STATIC, SQLITE_UTF8);
|
||||
sqlite3_bind_int(pStmt, 2, onlySqlid);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("write sql-%d (%d bytes) into %s\n",
|
||||
sqlite3_column_int(pStmt,1),
|
||||
sqlite3_column_int(pStmt,3),
|
||||
sqlite3_column_text(pStmt,2));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load all SQL script content and all initial database images from the
|
||||
** source db
|
||||
@@ -1039,6 +1127,12 @@ int main(int argc, char **argv){
|
||||
}
|
||||
rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs);
|
||||
if( rc ) fatalError("cannot open inmem database");
|
||||
#ifdef SQLITE_ENABLE_JSON1
|
||||
{
|
||||
extern int sqlite3_json_init(sqlite3*);
|
||||
sqlite3_json_init(db);
|
||||
}
|
||||
#endif
|
||||
if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags);
|
||||
setAlarm(iTimeout);
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
|
||||
Binary file not shown.
+2
-2
@@ -231,13 +231,13 @@ do_execsql_test indexedby-6.2 {
|
||||
#
|
||||
do_execsql_test indexedby-7.1 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5
|
||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||
do_execsql_test indexedby-7.2 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5
|
||||
} {0 0 0 {SCAN TABLE t1}}
|
||||
do_execsql_test indexedby-7.3 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5
|
||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||
do_execsql_test indexedby-7.4 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||
|
||||
@@ -218,5 +218,22 @@ do_execsql_test indexexpr1-510eqp {
|
||||
SELECT substr(a,4,3) AS k FROM cnt, t5 WHERE k=printf('%03d',x);
|
||||
} {/USING INDEX t5ax/}
|
||||
|
||||
# Skip-scan on an indexed expression
|
||||
#
|
||||
do_execsql_test indexexpr1-600 {
|
||||
DROP TABLE IF EXISTS t4;
|
||||
CREATE TABLE t4(a,b,c,d,e,f,g,h,i);
|
||||
CREATE INDEX t4all ON t4(a,b,c<d,e,f,i,h);
|
||||
INSERT INTO t4 VALUES(1,2,3,4,5,6,7,8,9);
|
||||
ANALYZE;
|
||||
DELETE FROM sqlite_stat1;
|
||||
INSERT INTO sqlite_stat1
|
||||
VALUES('t4','t4all','600000 160000 40000 10000 2000 600 100 40 10');
|
||||
ANALYZE sqlite_master;
|
||||
SELECT i FROM t4 WHERE e=5;
|
||||
} {9}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+18
-3
@@ -35,7 +35,7 @@ do_execsql_test json1-1.2 {
|
||||
SELECT hex(json_array('String "\ Test'));
|
||||
} {5B22537472696E67205C225C5C2054657374225D}
|
||||
do_catchsql_test json1-1.3 {
|
||||
SELECT json_array(1,2,x'abcd',3);
|
||||
SELECT json_array(1,printf('%.1000c','x'),x'abcd',3);
|
||||
} {1 {JSON cannot hold BLOB values}}
|
||||
do_execsql_test json1-1.4 {
|
||||
SELECT json_array(-9223372036854775808,9223372036854775807,0,1,-1,
|
||||
@@ -53,13 +53,13 @@ do_execsql_test json1-2.1 {
|
||||
SELECT json_object('a',1,'b',2.5,'c',null,'d','String Test');
|
||||
} {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}}
|
||||
do_catchsql_test json1-2.2 {
|
||||
SELECT json_object('a',1,2,2.5);
|
||||
SELECT json_object('a',printf('%.1000c','x'),2,2.5);
|
||||
} {1 {json_object() labels must be TEXT}}
|
||||
do_catchsql_test json1-2.3 {
|
||||
SELECT json_object('a',1,'b');
|
||||
} {1 {json_object() requires an even number of arguments}}
|
||||
do_catchsql_test json1-2.4 {
|
||||
SELECT json_object('a',1,'b',x'abcd');
|
||||
SELECT json_object('a',printf('%.1000c','x'),'b',x'abcd');
|
||||
} {1 {JSON cannot hold BLOB values}}
|
||||
|
||||
do_execsql_test json1-3.1 {
|
||||
@@ -74,6 +74,9 @@ do_execsql_test json1-3.3 {
|
||||
do_execsql_test json1-3.4 {
|
||||
SELECT json_type(json_set('{"a":1,"b":2}','$.b',json('{"x":3,"y":4}')),'$.b');
|
||||
} {object}
|
||||
do_execsql_test json1-3.5 {
|
||||
SELECT fullkey, atom, '|' FROM json_tree(json_set('{}','$.x',123,'$.x',456));
|
||||
} {{$} {} | {$.x} 456 |}
|
||||
|
||||
# Per rfc7159, any JSON value is allowed at the top level, and whitespace
|
||||
# is permitting before and/or after that value.
|
||||
@@ -302,6 +305,18 @@ do_execsql_test json-5.8 {
|
||||
WHERE jx.value<>jx.atom AND type NOT IN ('array','object');
|
||||
} {}
|
||||
|
||||
do_execsql_test json-6.1 {
|
||||
SELECT json_valid('{"a":55,"b":72,}');
|
||||
} {0}
|
||||
do_execsql_test json-6.2 {
|
||||
SELECT json_valid('{"a":55,"b":72}');
|
||||
} {1}
|
||||
do_execsql_test json-6.3 {
|
||||
SELECT json_valid('["a",55,"b",72,]');
|
||||
} {0}
|
||||
do_execsql_test json-6.4 {
|
||||
SELECT json_valid('["a",55,"b",72]');
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -278,4 +278,20 @@ do_execsql_test json102-1132 {
|
||||
} {123}
|
||||
} ;# end ifcapable vtab
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that json_valid() correctly identifies non-ascii range
|
||||
# characters as non-whitespace.
|
||||
#
|
||||
do_execsql_test json102-1201 { SELECT json_valid(char(32) || '"xyz"') } 1
|
||||
do_execsql_test json102-1202 { SELECT json_valid(char(200) || '"xyz"') } 0
|
||||
|
||||
# Off-by-one error in jsonAppendString()
|
||||
#
|
||||
for {set i 0} {$i<100} {incr i} {
|
||||
set str abcdef[string repeat \" [expr {$i+50}]]uvwxyz
|
||||
do_test json102-[format %d [expr {$i+1300}]] {
|
||||
db eval {SELECT json_extract(json_array($::str),'$[0]')==$::str}
|
||||
} {1}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+13
-3
@@ -27,6 +27,16 @@ do_execsql_test setup {
|
||||
c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
|
||||
INSERT INTO t1 SELECT x FROM c;
|
||||
}
|
||||
|
||||
# Some versions of TCL are unable to [lsort -int] for
|
||||
# 64-bit integers. So we write our own comparison
|
||||
# routine.
|
||||
proc bigintcompare {a b} {
|
||||
set x [expr {$a-$b}]
|
||||
if {$x<0} {return -1}
|
||||
if {$x>0} {return +1}
|
||||
return 0
|
||||
}
|
||||
do_test 1.0 {
|
||||
set l1 {}
|
||||
# If random() is only evaluated once and then reused for each row, then
|
||||
@@ -34,19 +44,19 @@ do_test 1.0 {
|
||||
# separately for the result set and the ORDER BY clause, then the output
|
||||
# order will be random.
|
||||
db eval {SELECT random() AS y FROM t1 ORDER BY 1;} {lappend l1 $y}
|
||||
expr {$l1==[lsort -int $l1]}
|
||||
expr {$l1==[lsort -command bigintcompare $l1]}
|
||||
} {1}
|
||||
|
||||
do_test 1.1 {
|
||||
set l1 {}
|
||||
db eval {SELECT random() AS y FROM t1 ORDER BY random();} {lappend l1 $y}
|
||||
expr {$l1==[lsort -int $l1]}
|
||||
expr {$l1==[lsort -command bigintcompare $l1]}
|
||||
} {1}
|
||||
|
||||
do_test 1.2 {
|
||||
set l1 {}
|
||||
db eval {SELECT random() AS y FROM t1 ORDER BY +random();} {lappend l1 $y}
|
||||
expr {$l1==[lsort -int $l1]}
|
||||
expr {$l1==[lsort -command bigintcompare $l1]}
|
||||
} {0}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -297,6 +297,7 @@ proc PUTS {args} {
|
||||
puts [lindex $args 0]
|
||||
puts $::LOG [lindex $args 0]
|
||||
}
|
||||
flush $::LOG
|
||||
}
|
||||
puts $LOG "$argv0 $argv"
|
||||
set tm0 [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S} -gmt 1]
|
||||
|
||||
+11
-9
@@ -29,32 +29,35 @@ do_execsql_test 1.0 {
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT word, distance, matchlen FROM demo
|
||||
WHERE word MATCH 'amstedam*' AND top=3;
|
||||
WHERE word MATCH 'amstedam*' AND top=3
|
||||
ORDER BY +word;
|
||||
} {
|
||||
amsterdam 100 9
|
||||
amsterdammetje 100 9
|
||||
amsterdamania 100 9
|
||||
amsterdammetje 100 9
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT word, distance, matchlen FROM demo WHERE
|
||||
word MATCH 'amstedam*' AND top=3 AND distance <= 100;
|
||||
word MATCH 'amstedam*' AND top=3 AND distance <= 100
|
||||
ORDER BY +word;
|
||||
} {
|
||||
amsterdam 100 9
|
||||
amsterdammetje 100 9
|
||||
amsterdamania 100 9
|
||||
amsterdammetje 100 9
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT word, distance, matchlen FROM demo WHERE
|
||||
word MATCH 'amstedam*' AND distance <= 100;
|
||||
word MATCH 'amstedam*' AND distance <= 100
|
||||
ORDER BY +word;
|
||||
} {
|
||||
amsterdam 100 9
|
||||
amsterdammetje 100 9
|
||||
amsterdamania 100 9
|
||||
amsterdamweg 100 9
|
||||
amsterdamsestraat 100 9
|
||||
amsterdamlaan 100 9
|
||||
amsterdammetje 100 9
|
||||
amsterdamsestraat 100 9
|
||||
amsterdamweg 100 9
|
||||
}
|
||||
|
||||
do_test 1.4 {
|
||||
@@ -111,4 +114,3 @@ do_execsql_test 1.7 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -643,7 +643,8 @@ do_test sqllimits1-8.8 {
|
||||
for {set i 0} {$i <= $SQLITE_LIMIT_COLUMN} {incr i} {
|
||||
lappend cols "c$i"
|
||||
}
|
||||
catchsql "CREATE VIEW v1 AS SELECT [join $cols ,] FROM t1;"
|
||||
execsql "CREATE VIEW v1 AS SELECT [join $cols ,] FROM t1;"
|
||||
catchsql {SELECT * FROM v1}
|
||||
} {1 {too many columns in result set}}
|
||||
|
||||
do_test sqllimits1-8.9 {
|
||||
@@ -652,9 +653,12 @@ do_test sqllimits1-8.9 {
|
||||
for {set i 0} {$i < $SQLITE_LIMIT_COLUMN} {incr i} {
|
||||
lappend cols "c$i"
|
||||
}
|
||||
execsql {DROP VIEW IF EXISTS v1}
|
||||
catchsql "CREATE TABLE t2([join $cols ,])"
|
||||
catchsql "CREATE VIEW v1 AS SELECT *, c1 AS o FROM t2;"
|
||||
catchsql "SELECT * FROM v1"
|
||||
} {1 {too many columns in result set}}
|
||||
|
||||
do_test sqllimits1-8.10 {
|
||||
# ORDER BY columns
|
||||
set cols [list]
|
||||
|
||||
@@ -55,6 +55,21 @@ do_execsql_test tabfunc01-1.10 {
|
||||
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY +value DESC;
|
||||
} {7 30 6 25 5 20 4 15 3 10 2 5 1 0}
|
||||
|
||||
do_execsql_test tabfunc01-1.20 {
|
||||
CREATE VIEW v1(a,b) AS VALUES(1,2),(3,4);
|
||||
SELECT * FROM v1;
|
||||
} {1 2 3 4}
|
||||
do_catchsql_test tabfunc01-1.21 {
|
||||
SELECT * FROM v1(55);
|
||||
} {1 {'v1' is not a function}}
|
||||
do_execsql_test tabfunc01-1.22 {
|
||||
CREATE VIEW v2(x) AS SELECT value FROM generate_series(1,5);
|
||||
SELECT * FROM v2;
|
||||
} {1 2 3 4 5}
|
||||
do_catchsql_test tabfunc01-1.23 {
|
||||
SELECT * FROM v2(55);
|
||||
} {1 {'v2' is not a function}}
|
||||
|
||||
do_execsql_test tabfunc01-2.1 {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1(x) VALUES(2),(3);
|
||||
|
||||
@@ -75,4 +75,12 @@ foreach {id sql} {
|
||||
} {1 {UNIQUE constraint failed: t1.y, t1.z}}
|
||||
}
|
||||
|
||||
do_catchsql_test 13.1 {
|
||||
CREATE TABLE err1(a,b,c,UNIQUE(rowid));
|
||||
} {1 {no such column: rowid}}
|
||||
do_catchsql_test 13.2 {
|
||||
CREATE TABLE err1(a,b,c,PRIMARY KEY(rowid));
|
||||
} {1 {no such column: rowid}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+6
-6
@@ -34,10 +34,6 @@ db func a_string a_string
|
||||
# of test cases tests that nothing appears to go wrong when this is
|
||||
# done.
|
||||
#
|
||||
set ans 4056
|
||||
if {[info exists G(perm:name)] && $G(perm:name)=="memsubsys1"} {
|
||||
set ans 4251
|
||||
}
|
||||
do_test wal3-1.0 {
|
||||
execsql {
|
||||
PRAGMA cache_size = 2000;
|
||||
@@ -64,8 +60,12 @@ do_test wal3-1.0 {
|
||||
COMMIT;
|
||||
PRAGMA cache_size = 10;
|
||||
}
|
||||
wal_frame_count test.db-wal 1024
|
||||
} $ans
|
||||
set x [wal_frame_count test.db-wal 1024]
|
||||
if {$::G(perm:name)=="memsubsys1"} {
|
||||
if {$x==4251 || $x==4290} {set x 4056}
|
||||
}
|
||||
set x
|
||||
} 4056
|
||||
|
||||
for {set i 1} {$i < 50} {incr i} {
|
||||
|
||||
|
||||
@@ -113,5 +113,9 @@ do_execsql_test without_rowid6-520 {
|
||||
PRAGMA index_list(t1);
|
||||
} {/sqlite_autoindex_t1_1 1 pk/}
|
||||
|
||||
do_catchsql_test without_rowid6-600 {
|
||||
CREATE TABLE t6(a,b,c,PRIMARY KEY(a,rowid,b))WITHOUT ROWID;
|
||||
} {1 {no such column: rowid}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -322,6 +322,7 @@ static void showHelp(void){
|
||||
"Options:\n"
|
||||
" --autovacuum Enable AUTOVACUUM mode\n"
|
||||
" --database FILE Use database FILE instead of an in-memory database\n"
|
||||
" --disable-lookaside Turn off lookaside memory\n"
|
||||
" --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n"
|
||||
" --help Show this help text\n"
|
||||
" --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n"
|
||||
@@ -457,6 +458,7 @@ int main(int argc, char **argv){
|
||||
const char *zDbName = 0; /* Name of an on-disk database file to open */
|
||||
|
||||
iBegin = timeOfDay();
|
||||
sqlite3_shutdown();
|
||||
zFailCode = getenv("TEST_FAILURE");
|
||||
g.zArgv0 = argv[0];
|
||||
zPrompt = "<stdin>";
|
||||
@@ -473,6 +475,10 @@ int main(int argc, char **argv){
|
||||
zDbName = argv[i+1];
|
||||
i += 1;
|
||||
}else
|
||||
if( strcmp(z,"disable-lookaside")==0 ){
|
||||
nLook = 1;
|
||||
szLook = 0;
|
||||
}else
|
||||
if( strcmp(z, "f")==0 && i+1<argc ){
|
||||
i++;
|
||||
goto addNewInFile;
|
||||
@@ -720,6 +726,12 @@ int main(int argc, char **argv){
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
sqlite3_trace(db, verboseFlag ? traceCallback : traceNoop, 0);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_JSON1
|
||||
{
|
||||
extern int sqlite3_json_init(sqlite3*);
|
||||
sqlite3_json_init(db);
|
||||
}
|
||||
#endif
|
||||
sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0, sqlEvalFunc, 0, 0);
|
||||
sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0, sqlEvalFunc, 0, 0);
|
||||
sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 1000000);
|
||||
|
||||
Reference in New Issue
Block a user