Compare commits

...

24 Commits

Author SHA1 Message Date
drh 2e3f87ae84 Change the name of the intarray() extension to carray() and give it an
optional third parameter that specifies the datatype as one of 'int32',
'int64', 'double', or 'char*'.  'int32' is the default.

FossilOrigin-Name: a204ba99db34b356acb259189158a32d2df25da0
2016-07-03 02:35:47 +00:00
drh ba637157ca Fix an off-by-one comparison in the intarray() virtual table. Get the
intarray() virtual table tests working using the legacy makefile.

FossilOrigin-Name: 7c3d441f2a9f642f3d91dcee854a4d16d298bc34
2016-07-02 20:57:06 +00:00
drh 2519b2a3e5 Merge the alternative table-valued function RHS of IN operator implementation
from trunk.

FossilOrigin-Name: 507fdbfb54ce377f0d870260b07d71b797843fcf
2016-07-02 20:51:31 +00:00
drh 9de4757c9c Fix a problem in table-valued functions on the RHS of an IN operator that
occurs following an OOM error.

FossilOrigin-Name: bead151e72215e6ca2a90eb049cfca414dccea04
2016-07-02 12:33:21 +00:00
drh 5fbab882c0 Add support for table-valued functions on the RHS of an IN operator.
FossilOrigin-Name: ac6000f050ff4efcf8a87f0825077dbf4144f073
2016-07-02 12:08:14 +00:00
drh 7c95696e5c Fix the transitive constraint logic error that can result in a null pointer
dereference.  Fix for ticket [e8d439c77685eca6].

FossilOrigin-Name: 228a7879870f8689e2f9b74fbef2ff93fab7b3c2
2016-07-01 20:12:39 +00:00
drh 883486bada Add test cases to the transitive constraint fix.
FossilOrigin-Name: c952af89c22ddc31491f142b9511608a4c8f9737
2016-07-01 19:48:43 +00:00
drh 2d05331444 Add the ability to have a table-valued function on the RHS of an IN operator.
FossilOrigin-Name: ba1b441b6003808810667d749635fe6b2e8c6165
2016-06-29 06:19:19 +00:00
drh 64131c15ab Another test case for the intarray($PTR,$N) virtual table.
FossilOrigin-Name: 06e1fab7527c6b4330a58f8d8873afaf2d67ae86
2016-06-29 05:08:01 +00:00
drh 4841624ab9 Add a prototype intarray($PTR,$N) table valued function.
FossilOrigin-Name: 233b33382dc70de45f90b6dfdb5785f20b21489e
2016-06-29 05:00:30 +00:00
drh a2fbe3bec6 Proposed fix for a problem in the query planner.
FossilOrigin-Name: a33d23560959a127e37d8213dc00210cd4b05352
2016-06-28 22:27:56 +00:00
drh 05d1bad674 Prevent the WhereLoop.rSetup cost estimate from going negative on complex
queries.

FossilOrigin-Name: f81050859170c8708a1b296da8dd3ef0dd314a11
2016-06-26 04:06:28 +00:00
drh 4b37cd49a6 Fix the handling of OP_Eq opcodes that compare a register against itself
and that require an affinity change.

FossilOrigin-Name: 507014e4c7a70cd09410c89c8ed466c8edab39d2
2016-06-25 11:43:47 +00:00
drh f5fe00399f Fix the ctime.test script so that it works with clang.
FossilOrigin-Name: 77e4f7a36e6e0ebe842bcb36b2557a5bfba90d3f
2016-06-24 06:23:13 +00:00
drh be11ade7cf Fix the Makefile.in to avoid unnecessary recompiles of sqlite3.c.
FossilOrigin-Name: 67c39e16442d9359b24d60d5f97bd66c19eff16a
2016-06-24 02:50:44 +00:00
dan b10ab53f3a Update the sqldiff utility so that if the --vtab switch is specified "rtree", "fts3", "fts4" and "fts5" tables are diff'd directly and the underlying real database tables ignored. Without this switch, all virtual tables are ignored and the diff is performed on the underlying real tables.
FossilOrigin-Name: b8671e9434180878cfe15d70b793fdee69aabccf
2016-06-23 16:48:35 +00:00
drh ec5935924d Fix the build for -DSQLITE_OMIT_VIRTUALTABLE
FossilOrigin-Name: 911131424857430d46784b167399ecf192584ca2
2016-06-23 12:35:04 +00:00
dan 9c987a833c Update the sqldiff utility so that if the --vtab switch is specified "rtree", "fts3", "fts4" and "fts5" tables are diff'd directly and the underlying real database tables ignored. Without this switch, all virtual tables are ignored and the diff is performed on the underlying real tables.
FossilOrigin-Name: 5d0a9d4c45730e47576bc77373fa7075a74051a5
2016-06-21 10:34:41 +00:00
dan ee84b0f690 For a table on the rhs of a LEFT JOIN operator, do not include terms like "IS NULL" from the WHERE clause in the cursor-hint. These may be false for rows that the cursor would otherwise visit, but true for a row of all NULL values generated by the LEFT JOIN.
FossilOrigin-Name: 913e595615e2ef40fb431f6e7678f6fc8439782e
2016-06-20 17:25:50 +00:00
dan 2b693d63e4 Allow LIKE operators that appear in a WHERE clause to be included in the cursor-hint for a cursor on the rhs of a LEFT JOIN.
FossilOrigin-Name: 7455d932f5079ffe40462a8c119fc22b8a9bcbcc
2016-06-20 17:22:06 +00:00
dan e6912fd819 Include WHERE terms in the cursor-hint passed to a cursor opened for the rhs of a LEFT JOIN iff we can be sure that those terms will not evaluate to true if the LEFT JOIN generates a row of NULLs.
FossilOrigin-Name: 998095aba01b75f685ed981b377e1dfe650d9bbf
2016-06-17 19:27:13 +00:00
dan c497441406 Fix a duplicate test name in cursorhint2.test.
FossilOrigin-Name: fcd12b69cee1335224a65aa6d22c4e302b889398
2016-06-17 14:59:40 +00:00
dan 419b03c1bb Fix a typo in the cursorhint2.test script.
FossilOrigin-Name: c1a5a57cf10dca91082963dcbd3e3ffebc3707ef
2016-06-17 14:47:37 +00:00
dan b324cf756e If a table is on the rhs of a LEFT JOIN, include only terms from the joins ON(...) clause in the cursor-hint passed via OP_CursorHint.
FossilOrigin-Name: 2a2346b04235c6d0b7a8e64c92ee31018285c29f
2016-06-17 14:33:32 +00:00
20 changed files with 1168 additions and 60 deletions
+12 -11
View File
@@ -416,6 +416,7 @@ TESTSRC = \
#
TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/carray.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/csv.c \
$(TOP)/ext/misc/eval.c \
@@ -588,15 +589,15 @@ sqlite3$(TEXE): $(TOP)/src/shell.c sqlite3.c
$(TOP)/src/shell.c sqlite3.c \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.lo sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.lo $(TLIBS)
dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/dbhash.c sqlite3.c $(TLIBS)
dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.lo sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/dbhash.c sqlite3.lo $(TLIBS)
scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.o
scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.lo
$(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \
$(TOP)/ext/misc/scrub.c sqlite3.o $(TLIBS)
$(TOP)/ext/misc/scrub.c sqlite3.lo $(TLIBS)
srcck1$(BEXE): $(TOP)/tool/srcck1.c
$(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c
@@ -611,8 +612,8 @@ fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS)
mptester$(TEXE): sqlite3.c $(TOP)/mptest/mptest.c
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.lo \
$(TLIBS) -rpath "$(libdir)"
MPTEST1=./mptester$(TEXE) mptest.db $(TOP)/mptest/crash01.test --repeat 20
@@ -1156,8 +1157,8 @@ rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo
LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h
$(LTLINK) -I. -o $@ $(TOP)/tool/logest.c
wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c
$(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.c $(TLIBS)
wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.lo $(TLIBS)
speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS)
@@ -1174,7 +1175,7 @@ loadfts$(EXE): $(TOP)/tool/loadfts.c libsqlite3.la
#
VALIDIDS=' sqlite3(changeset|changegroup|session)?_'
checksymbols: sqlite3.lo
nm -g --defined-only sqlite3.o | egrep -v $(VALIDIDS); test $$? -ne 0
nm -g --defined-only sqlite3.lo | egrep -v $(VALIDIDS); test $$? -ne 0
echo '0 errors out of 1 tests'
# Build the amalgamation-autoconf package. The amalamgation-tarball target builds
+1
View File
@@ -1292,6 +1292,7 @@ TESTSRC = \
#
TESTEXT = \
$(TOP)\ext\misc\amatch.c \
$(TOP)\ext\misc\carray.c \
$(TOP)\ext\misc\closure.c \
$(TOP)\ext\misc\csv.c \
$(TOP)\ext\misc\eval.c \
+369
View File
@@ -0,0 +1,369 @@
/*
** 2016-06-29
**
** 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 demonstrates how to create a table-valued-function that
** returns the values in a C-language array.
** Examples:
**
** SELECT * FROM array($ptr,5)
**
** The query above returns 5 integers contained in a C-language array
** at the address $ptr. $ptr is a pointer to the array of integers that
** has been cast to an integer.
**
** There is an optional third parameter to determine the datatype of
** the C-language array. Allowed values of the third parameter are
** 'int32', 'int64', 'double', 'char*'. Example:
**
** SELECT * FROM array($ptr,10,'char*');
**
** HOW IT WORKS
**
** The carray "function" is really a virtual table with the
** following schema:
**
** CREATE TABLE carray(
** value,
** pointer HIDDEN,
** count HIDDEN,
** ctype TEXT HIDDEN
** );
**
** If the hidden columns "pointer" and "count" are unconstrained, then
** the virtual table has no rows. Otherwise, the virtual table interprets
** the integer value of "pointer" as a pointer to the array and "count"
** as the number of elements in the array. The virtual table steps through
** the array, element by element.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Allowed datatypes
*/
#define CARRAY_INT32 0
#define CARRAY_INT64 1
#define CARRAY_DOUBLE 2
#define CARRAY_TEXT 3
/*
** Names of types
*/
static const char *azType[] = { "int32", "int64", "double", "char*" };
/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct carray_cursor carray_cursor;
struct carray_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_int64 iRowid; /* The rowid */
sqlite3_int64 iPtr; /* Pointer to array of values */
sqlite3_int64 iCnt; /* Number of integers in the array */
unsigned char eType; /* One of the CARRAY_type values */
};
/*
** The carrayConnect() method is invoked to create a new
** carray_vtab that describes the carray virtual table.
**
** Think of this routine as the constructor for carray_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the carray_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against carray will look like.
*/
static int carrayConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define CARRAY_COLUMN_VALUE 0
#define CARRAY_COLUMN_POINTER 1
#define CARRAY_COLUMN_COUNT 2
#define CARRAY_COLUMN_CTYPE 3
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for carray_cursor objects.
*/
static int carrayDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Constructor for a new carray_cursor object.
*/
static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
carray_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a carray_cursor.
*/
static int carrayClose(sqlite3_vtab_cursor *cur){
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** Advance a carray_cursor to its next row of output.
*/
static int carrayNext(sqlite3_vtab_cursor *cur){
carray_cursor *pCur = (carray_cursor*)cur;
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the carray_cursor
** is currently pointing.
*/
static int carrayColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
carray_cursor *pCur = (carray_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case CARRAY_COLUMN_POINTER: x = pCur->iPtr; break;
case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
case CARRAY_COLUMN_CTYPE: {
sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
return SQLITE_OK;
}
default: {
switch( pCur->eType ){
case CARRAY_INT32: {
int *p = (int*)pCur->iPtr;
sqlite3_result_int(ctx, p[pCur->iRowid-1]);
return SQLITE_OK;
}
case CARRAY_INT64: {
sqlite3_int64 *p = (sqlite3_int64*)pCur->iPtr;
sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
return SQLITE_OK;
}
case CARRAY_DOUBLE: {
double *p = (double*)pCur->iPtr;
sqlite3_result_double(ctx, p[pCur->iRowid-1]);
return SQLITE_OK;
}
case CARRAY_TEXT: {
const char **p = (const char**)pCur->iPtr;
sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
return SQLITE_OK;
}
}
}
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
carray_cursor *pCur = (carray_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int carrayEof(sqlite3_vtab_cursor *cur){
carray_cursor *pCur = (carray_cursor*)cur;
return pCur->iRowid>pCur->iCnt;
}
/*
** This method is called to "rewind" the carray_cursor object back
** to the first row of output.
*/
static int carrayFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
carray_cursor *pCur = (carray_cursor *)pVtabCursor;
if( idxNum ){
pCur->iPtr = sqlite3_value_int64(argv[0]);
pCur->iCnt = sqlite3_value_int64(argv[1]);
if( idxNum<3 ){
pCur->eType = CARRAY_INT32;
}else{
int i;
const char *zType = (const char*)sqlite3_value_text(argv[2]);
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
}
if( i>=sizeof(azType)/sizeof(azType[0]) ){
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
"unknown datatype: %Q", zType);
return SQLITE_ERROR;
}else{
pCur->eType = i;
}
}
}else{
pCur->iPtr = 0;
pCur->iCnt = 0;
}
pCur->iRowid = 1;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the carray virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** idxNum is 2 if the pointer= and count= constraints exist,
** 3 if the ctype= constraint also exists, and is 0 otherwise.
** If idxNum is 0, then carray becomes an empty table.
*/
static int carrayBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
switch( pConstraint->iColumn ){
case CARRAY_COLUMN_POINTER:
ptrIdx = i;
break;
case CARRAY_COLUMN_COUNT:
cntIdx = i;
break;
case CARRAY_COLUMN_CTYPE:
ctypeIdx = i;
break;
}
}
if( ptrIdx>=0 && cntIdx>=0 ){
pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = (double)100;
pIdxInfo->idxNum = 2;
if( ctypeIdx>=0 ){
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
pIdxInfo->idxNum = 3;
}
}else{
pIdxInfo->estimatedCost = (double)2147483647;
pIdxInfo->estimatedRows = (double)2147483647;
pIdxInfo->idxNum = 0;
}
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** carray virtual table.
*/
static sqlite3_module carrayModule = {
0, /* iVersion */
0, /* xCreate */
carrayConnect, /* xConnect */
carrayBestIndex, /* xBestIndex */
carrayDisconnect, /* xDisconnect */
0, /* xDestroy */
carrayOpen, /* xOpen - open a cursor */
carrayClose, /* xClose - close a cursor */
carrayFilter, /* xFilter - configure scan constraints */
carrayNext, /* xNext - advance a cursor */
carrayEof, /* xEof - check for end of scan */
carrayColumn, /* xColumn - read data */
carrayRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_carray_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( sqlite3_libversion_number()<3008012 ){
*pzErrMsg = sqlite3_mprintf(
"carray() requires SQLite 3.8.12 or later");
return SQLITE_ERROR;
}
rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
#endif
return rc;
}
+66 -3
View File
@@ -25,6 +25,10 @@ proc get_rbudiff_sql {db1 db2} {
exec $::PROG --rbu $db1 $db2
}
proc get_vtab_rbudiff_sql {db1 db2} {
exec $::PROG --vtab --rbu $db1 $db2
}
proc step_rbu {target rbu} {
while 1 {
sqlite3rbu rbu $target $rbu
@@ -44,6 +48,11 @@ proc apply_rbudiff {sql target} {
step_rbu $target rbu.db
}
proc sqlesc {id} {
set ret "'[string map {' ''} $id]'"
set ret
}
# The only argument is the output of an [sqldiff -rbu] run. This command
# tests that the contents of the rbu_count table is correct. An exception
# is thrown if it is not.
@@ -54,7 +63,7 @@ proc test_rbucount {sql} {
tmpdb eval {
SELECT name FROM sqlite_master WHERE name LIKE 'data%' AND type='table'
} {
set a [tmpdb eval "SELECT count(*) FROM $name"]
set a [tmpdb eval "SELECT count(*) FROM [sqlesc $name]"]
set b [tmpdb eval {SELECT cnt FROM rbu_count WHERE tbl = $name}]
if {$a != $b} {
tmpdb close
@@ -71,9 +80,11 @@ proc rbudiff_cksum {db1} {
sqlite3 dbtmp $db1
foreach tbl [dbtmp eval {SELECT name FROM sqlite_master WHERE type='table'}] {
set cols [list]
dbtmp eval "PRAGMA table_info = $tbl" { lappend cols "quote( $name )" }
dbtmp eval "PRAGMA table_info = [sqlesc $tbl]" {
lappend cols "quote( $name )"
}
append txt [dbtmp eval \
"SELECT [join $cols {||'.'||}] FROM $tbl ORDER BY 1"
"SELECT [join $cols {||'.'||}] FROM [sqlesc $tbl] ORDER BY 1"
]
}
dbtmp close
@@ -159,5 +170,57 @@ foreach {tn init mod} {
do_test 1.$tn.5 { rbudiff_cksum test.db } [rbudiff_cksum test.db2]
}
#-------------------------------------------------------------------------
# Test that if the --vtab switch is present, [sqldiff] handles virtual
# table types fts[345] and rtree correctly.
#
ifcapable fts3&&fts5&&rtree {
foreach {tn init mod} {
1 {
CREATE VIRTUAL TABLE t1 USING fts5(c);
INSERT INTO t1 VALUES('a b c');
INSERT INTO t1 VALUES('a b c');
} {
DELETE FROM t1 WHERE rowid = 1;
INSERT INTO t1 VALUES('a b c');
}
2 {
CREATE VIRTUAL TABLE "x y" USING 'rtree'(id, x1, x2);
INSERT INTO "x y" VALUES(1, 2, 3);
INSERT INTO "x y" VALUES(2, 4, 6);
} {
DELETE FROM "x y" WHERE rowid = 1;
INSERT INTO "x y" VALUES(3, 6, 9);
}
3 {
CREATE VIRTUAL TABLE 'x''y' USING fts3;
INSERT INTO 'x''y' VALUES('one two three');
INSERT INTO 'x''y' VALUES('four five six');
} {
DELETE FROM 'x''y' WHERE rowid = 1;
INSERT INTO 'x''y' VALUES('one two three');
}
} {
forcedelete test.db test.db2
sqlite3 db test.db
db eval "$init"
sqlite3 db test.db2
db eval "$init ; $mod"
db close
do_test 2.$tn.1 {
set sql [get_vtab_rbudiff_sql test.db test.db2]
apply_rbudiff $sql test.db
} {SQLITE_DONE}
do_test 2.$tn.2 { rbudiff_cksum test.db } [rbudiff_cksum test.db2]
}
}
finish_test
+1
View File
@@ -324,6 +324,7 @@ TESTSRC = \
#
TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/carray.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/csv.c \
$(TOP)/ext/misc/eval.c \
+24 -21
View File
@@ -1,8 +1,8 @@
C Add\sa\smissing\sOP_ColumnsUsed\sopcode\sto\scode\sfor\sexpressions\slike\s"?\sIN\s(SELECT\s...)"\sin\scases\swhere\sexpression\scan\suse\san\sindex\sthat\smay\scontain\sNULL\svalues.
D 2016-06-16T17:14:02.375
F Makefile.in f3f7d2060ce03af4584e711ef3a626ef0b1d6340
C Change\sthe\sname\sof\sthe\sintarray()\sextension\sto\scarray()\sand\sgive\sit\san\noptional\sthird\sparameter\sthat\sspecifies\sthe\sdatatype\sas\sone\sof\s'int32',\n'int64',\s'double',\sor\s'char*'.\s\s'int32'\sis\sthe\sdefault.
D 2016-07-03T02:35:47.798
F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 50149765ef72f4e652b9a0f1f6462c4784bb9423
F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION cb29eb11e493dd85b3eeec4053c03949bf98478e
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -204,6 +204,7 @@ F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
F ext/icu/icu.c 43df9d8ef2fae7a325100ebd713ab089dc829dd7
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/misc/amatch.c 211108e201105e4bb0c076527b8cfd34330fc234
F ext/misc/carray.c f1947c7d5bbce17f8244c9a05baae11d5d68467e w ext/misc/array.c
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
F ext/misc/csv.c f01126ba170fd4ef7c752b156568a80c912d4441
@@ -243,7 +244,7 @@ F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2
F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831
F ext/rbu/rbu_common.tcl 3a4b916b6f5dca9c9da9a30863e272fe5ea4414f
F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695
F ext/rbu/rbudiff.test 2df0a8a7d998ecf81764c21eeda3cde5611c5091
F ext/rbu/rbudiff.test 4c9f8df6f723f553781d3d117501b7e9d170a145
F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca
@@ -307,7 +308,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 3f669c06db5c4a53ff21dda639247c6310497180
F main.mk e9d66f1b1c4874221d12d940be3ce5f397c10741
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -371,7 +372,7 @@ F src/os_win.c d4b8faf8896b65818e67070711fdd00d8e620bd6
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c c368634b888b1c8740aea83b36bfd266f2443e60
F src/pager.h 8ab6b6feeee4bc0439bfde7ee59ba99df98b9bc3
F src/parse.y 01b9f37c4c7009ab56fda98bc7db4c42643cecfe
F src/parse.y f374ab20106362eb3f5c01b3e6a002f0bbead7ff
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d
@@ -392,7 +393,7 @@ F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
F src/tclsqlite.c 25fbbbb97f76dbfd113153fb63f52d7ecfac5dd0
F src/test1.c 43b37ab2b7338fd3313e74902f0d6c821eae843b
F src/test1.c 7cbfda741fbfa541ecce305ff2d7c6bb730aa991
F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b
F src/test3.c c75c8af0eadb335236c9e61b51044c58a8f7dd59
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
@@ -448,7 +449,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52
F src/vdbe.c e23a15d352e63b51cf08af8052e678e67c54ec09
F src/vdbe.c 22b46c3b725e950e9f2760e2d76953d592600ad4
F src/vdbe.h 5591b5add447096e31288b5a0a78ec5d7b5c5170
F src/vdbeInt.h ddb157974436d87652de7dc641f7191496d9a8cd
F src/vdbeapi.c 02bcbc2ca5d2004b029088b05b468b394881e103
@@ -462,10 +463,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
F src/where.c 74f0798525b6306682d7234f230ea93f86959b9b
F src/where.c 48eed8ebe319c6cbc7bf7682018f32af0f5189f5
F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0
F src/wherecode.c ba71a4e4bada29aa9842200e6299714bf18c812c
F src/whereexpr.c c32d47085dbaca0b8fd013210f56693c7d220d48
F src/wherecode.c e20cb381ff621e56a4684c71e31999aca2547ca6
F src/whereexpr.c d7dcbf14ce1b5876c1f76496162c30fcba669563
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -574,6 +575,7 @@ F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868
F test/collate8.test cd9b3d3f999b8520ffaa7cc1647061fc5bab1334
F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/collateB.test 8ec2accd2d7166c1eff0d2a39bc90262c6f89632
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
F test/conflict.test 029faa2d81a0d1cafb5f88614beb663d972c01db
@@ -615,8 +617,9 @@ F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
F test/csv01.test 0929a9ce47021519512be92861f29e32d2538e5f
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
F test/ctime.test 0b995accd44a52914bd4744d5c1b6e1a56c7897c
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
F test/cursorhint2.test 2b45341d32d1aae9991a00ef31ebca339b274255
F test/date.test 984ac1e3e5e031386866f034006148d3972b4a65
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
@@ -1040,7 +1043,7 @@ F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
F test/select7.test 95e370c42d47c3c52377d05e9ffc01ccff7c1f61
F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
F test/selectA.test e452bdb975f488ea46d091382a9185b5853ed2c7
F test/selectA.test 101e722370ac6e84978c2958b8931c78b10a1709
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394
@@ -1113,7 +1116,7 @@ F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529
F test/syscall.test f59ba4e25f7ba4a4c031026cc2ef8b6e4b4c639c
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
F test/tabfunc01.test f977868fa8bb7beb4b2072883190411653473906
F test/tabfunc01.test 50a9fb379f9747fd0d40ea6d8fa3a101361bb537
F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
@@ -1375,7 +1378,7 @@ F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
F test/where.test f0c325563acde44f2c4ea6ba348e9e29f7121757
F test/where2.test 478d2170637b9211f593120648858593bf2445a1
F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e
F test/where3.test 54cdeb02157acc979de41530b804ae7b09552bf1
F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
@@ -1468,7 +1471,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/sqldiff.c ca315aca4e2d24233e8f2000edea5880c53d1875
F tool/sqldiff.c 131e9c6e09d3d78e3c4ae8b5fd23451521723aac
F tool/srcck1.c 4c39bdfa9a92edd20233ee720df84dbeb2417602
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
@@ -1501,7 +1504,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 48b555c42de1cbc031fb6c2c93ef170e491c7d76
R 7c056b92500e212b6fa7144618e1e6e0
U dan
Z 6cf13efbb72ea4474e244da36e8952f1
P 7c3d441f2a9f642f3d91dcee854a4d16d298bc34
R 41f36cbba6be0bbd66a667562505a546
U drh
Z 31f2105436c0dcfd40b6904e7b94f7b1
+1 -1
View File
@@ -1 +1 @@
0b1579caf06a2c42433b8bc9dc28c9ad381aa07c
a204ba99db34b356acb259189158a32d2df25da0
+10 -1
View File
@@ -1134,9 +1134,10 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
exprNot(pParse, N, &A);
A.zEnd = &E.z[E.n];
}
expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z). [IN] {
expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z) paren_exprlist(E). [IN] {
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E);
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0);
sqlite3PExprAddSelect(pParse, A.pExpr, pSelect);
exprNot(pParse, N, &A);
@@ -1193,6 +1194,14 @@ nexprlist(A) ::= nexprlist(A) COMMA expr(Y).
nexprlist(A) ::= expr(Y).
{A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/}
/* A paren_exprlist is an optional expression list contained inside
** of parenthesis */
%type paren_exprlist {ExprList*}
%destructor paren_exprlist {sqlite3ExprListDelete(pParse->db, $$);}
paren_exprlist(A) ::= . {A = 0;}
paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;}
///////////////////////////// The CREATE INDEX command ///////////////////////
//
+142
View File
@@ -3241,6 +3241,142 @@ static int test_bind_int(
}
/*
** Usage: intarray_addr INT ...
**
** Return the address of a C-language array of 32-bit integers.
**
** Space to hold the array is obtained from malloc(). Call this procedure once
** with no arguments in order to release memory. Each call to this procedure
** overwrites the previous array.
*/
static int test_intarray_addr(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int i;
static int *p = 0;
sqlite3_free(p);
p = 0;
if( objc>1 ){
p = sqlite3_malloc( sizeof(p[0])*(objc-1) );
if( p==0 ) return TCL_ERROR;
for(i=0; i<objc-1; i++){
if( Tcl_GetIntFromObj(interp, objv[1+i], &p[i]) ){
sqlite3_free(p);
p = 0;
return TCL_ERROR;
}
}
}
Tcl_SetObjResult(interp, Tcl_NewWideIntObj((sqlite3_int64)p));
return TCL_OK;
}
/*
** Usage: intarray_addr INT ...
**
** Return the address of a C-language array of 32-bit integers.
**
** Space to hold the array is obtained from malloc(). Call this procedure once
** with no arguments in order to release memory. Each call to this procedure
** overwrites the previous array.
*/
static int test_int64array_addr(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int i;
static sqlite3_int64 *p = 0;
sqlite3_free(p);
p = 0;
if( objc>1 ){
p = sqlite3_malloc( sizeof(p[0])*(objc-1) );
if( p==0 ) return TCL_ERROR;
for(i=0; i<objc-1; i++){
if( Tcl_GetWideIntFromObj(interp, objv[1+i], &p[i]) ){
sqlite3_free(p);
p = 0;
return TCL_ERROR;
}
}
}
Tcl_SetObjResult(interp, Tcl_NewWideIntObj((sqlite3_int64)p));
return TCL_OK;
}
/*
** Usage: doublearray_addr INT ...
**
** Return the address of a C-language array of doubles.
**
** Space to hold the array is obtained from malloc(). Call this procedure once
** with no arguments in order to release memory. Each call to this procedure
** overwrites the previous array.
*/
static int test_doublearray_addr(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int i;
static double *p = 0;
sqlite3_free(p);
p = 0;
if( objc>1 ){
p = sqlite3_malloc( sizeof(p[0])*(objc-1) );
if( p==0 ) return TCL_ERROR;
for(i=0; i<objc-1; i++){
if( Tcl_GetDoubleFromObj(interp, objv[1+i], &p[i]) ){
sqlite3_free(p);
p = 0;
return TCL_ERROR;
}
}
}
Tcl_SetObjResult(interp, Tcl_NewWideIntObj((sqlite3_int64)p));
return TCL_OK;
}
/*
** Usage: textarray_addr TEXT ...
**
** Return the address of a C-language array of strings.
**
** Space to hold the array is obtained from malloc(). Call this procedure once
** with no arguments in order to release memory. Each call to this procedure
** overwrites the previous array.
*/
static int test_textarray_addr(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int i;
static int n = 0;
static char **p = 0;
for(i=0; i<n; i++) sqlite3_free(p[i]);
sqlite3_free(p);
p = 0;
if( objc>1 ){
p = sqlite3_malloc( sizeof(p[0])*(objc-1) );
if( p==0 ) return TCL_ERROR;
for(i=0; i<objc-1; i++){
p[i] = sqlite3_mprintf("%s", Tcl_GetString(objv[1+i]));
}
}
Tcl_SetObjResult(interp, Tcl_NewWideIntObj((sqlite3_int64)p));
return TCL_OK;
}
/*
** Usage: sqlite3_bind_int64 STMT N VALUE
**
@@ -6583,6 +6719,7 @@ static int tclLoadStaticExtensionCmd(
Tcl_Obj *CONST objv[]
){
extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_carray_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_csv_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*);
@@ -6601,6 +6738,7 @@ static int tclLoadStaticExtensionCmd(
int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*);
} aExtension[] = {
{ "amatch", sqlite3_amatch_init },
{ "carray", sqlite3_carray_init },
{ "closure", sqlite3_closure_init },
{ "csv", sqlite3_csv_init },
{ "eval", sqlite3_eval_init },
@@ -7095,6 +7233,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "bad_behavior", test_bad_behavior, (void*)&iZero },
{ "register_dbstat_vtab", test_register_dbstat_vtab },
{ "sqlite3_connection_pointer", get_sqlite_pointer, 0 },
{ "intarray_addr", test_intarray_addr, 0 },
{ "int64array_addr", test_int64array_addr, 0 },
{ "doublearray_addr", test_doublearray_addr, 0 },
{ "textarray_addr", test_textarray_addr, 0 },
{ "sqlite3_bind_int", test_bind_int, 0 },
{ "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 },
{ "sqlite3_bind_zeroblob64", test_bind_zeroblob64, 0 },
+2
View File
@@ -2016,6 +2016,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
if( (flags1 | flags3)&MEM_Str ){
if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn1,0);
flags3 = pIn3->flags;
}
if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3,0);
@@ -2028,6 +2029,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
sqlite3VdbeMemStringify(pIn1, encoding, 1);
testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
flags3 = pIn3->flags;
}
if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){
testcase( pIn3->flags & MEM_Int );
+5 -1
View File
@@ -2701,6 +2701,7 @@ static int whereLoopAddBtree(
pNew->rSetup += 24;
}
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
if( pNew->rSetup<0 ) pNew->rSetup = 0;
/* TUNING: Each index lookup yields 20 rows in the table. This
** is more than the usual guess of 10 rows, since we have no way
** of knowing how selective the index will ultimately be. It would
@@ -3240,6 +3241,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
mPrereq = mPrior;
}
priorJointype = pItem->fg.jointype;
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pItem->pTab) ){
struct SrcList_item *p;
for(p=&pItem[1]; p<pEnd; p++){
@@ -3248,7 +3250,9 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
}
}
rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable);
}else{
}else
#endif /* SQLITE_OMIT_VIRTUALTABLE */
{
rc = whereLoopAddBtree(pBuilder, mPrereq);
}
if( rc==SQLITE_OK ){
+73 -5
View File
@@ -626,6 +626,38 @@ static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
return WRC_Continue;
}
/*
** Test whether or not expression pExpr, which was part of a WHERE clause,
** should be included in the cursor-hint for a table that is on the rhs
** of a LEFT JOIN. Set Walker.eCode to non-zero before returning if the
** expression is not suitable.
**
** An expression is unsuitable if it might evaluate to non NULL even if
** a TK_COLUMN node that does affect the value of the expression is set
** to NULL. For example:
**
** col IS NULL
** col IS NOT NULL
** coalesce(col, 1)
** CASE WHEN col THEN 0 ELSE 1 END
*/
static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_IS
|| pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT
|| pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE
){
pWalker->eCode = 1;
}else if( pExpr->op==TK_FUNCTION ){
int d1;
char d2[3];
if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
pWalker->eCode = 1;
}
}
return WRC_Continue;
}
/*
** This function is called on every node of an expression tree used as an
@@ -678,6 +710,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
** Insert an OP_CursorHint instruction if it is appropriate to do so.
*/
static void codeCursorHint(
struct SrcList_item *pTabItem, /* FROM clause item */
WhereInfo *pWInfo, /* The where clause */
WhereLevel *pLevel, /* Which loop to provide hints for */
WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
@@ -708,7 +741,42 @@ static void codeCursorHint(
pTerm = &pWC->a[i];
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( pTerm->prereqAll & pLevel->notReady ) continue;
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
/* Any terms specified as part of the ON(...) clause for any LEFT
** JOIN for which the current table is not the rhs are omitted
** from the cursor-hint.
**
** If this table is the rhs of a LEFT JOIN, "IS" or "IS NULL" terms
** that were specified as part of the WHERE clause must be excluded.
** This is to address the following:
**
** SELECT ... t1 LEFT JOIN t2 ON (t1.a=t2.b) WHERE t2.c IS NULL;
**
** Say there is a single row in t2 that matches (t1.a=t2.b), but its
** t2.c values is not NULL. If the (t2.c IS NULL) constraint is
** pushed down to the cursor, this row is filtered out, causing
** SQLite to synthesize a row of NULL values. Which does match the
** WHERE clause, and so the query returns a row. Which is incorrect.
**
** For the same reason, WHERE terms such as:
**
** WHERE 1 = (t2.c IS NULL)
**
** are also excluded. See codeCursorHintIsOrFunction() for details.
*/
if( pTabItem->fg.jointype & JT_LEFT ){
Expr *pExpr = pTerm->pExpr;
if( !ExprHasProperty(pExpr, EP_FromJoin)
|| pExpr->iRightJoinTable!=pTabItem->iCursor
){
sWalker.eCode = 0;
sWalker.xExprCallback = codeCursorHintIsOrFunction;
sqlite3WalkExpr(&sWalker, pTerm->pExpr);
if( sWalker.eCode ) continue;
}
}else{
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
}
/* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
** the cursor. These terms are not needed as hints for a pure range
@@ -742,7 +810,7 @@ static void codeCursorHint(
}
}
#else
# define codeCursorHint(A,B,C) /* No-op */
# define codeCursorHint(A,B,C,D) /* No-op */
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/*
@@ -998,7 +1066,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
pStart = pEnd;
pEnd = pTerm;
}
codeCursorHint(pWInfo, pLevel, pEnd);
codeCursorHint(pTabItem, pWInfo, pLevel, pEnd);
if( pStart ){
Expr *pX; /* The expression that defines the start bound */
int r1, rTemp; /* Registers for holding the start boundary */
@@ -1212,7 +1280,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** and store the values of those terms in an array of registers
** starting at regBase.
*/
codeCursorHint(pWInfo, pLevel, pRangeEnd);
codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd);
regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
if( zStartAff ) cEndAff = zStartAff[nEq];
@@ -1660,7 +1728,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** a pseudo-cursor. No need to Rewind or Next such cursors. */
pLevel->op = OP_Noop;
}else{
codeCursorHint(pWInfo, pLevel, 0);
codeCursorHint(pTabItem, pWInfo, pLevel, 0);
pLevel->op = aStep[bRev];
pLevel->p1 = iCur;
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
+3 -5
View File
@@ -785,12 +785,10 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){
pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
/* Since pLeft and pRight are both a column references, their collating
** sequence should always be defined. */
zColl1 = ALWAYS(pColl) ? pColl->zName : 0;
zColl1 = pColl ? pColl->zName : 0;
pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
zColl2 = ALWAYS(pColl) ? pColl->zName : 0;
return sqlite3StrICmp(zColl1, zColl2)==0;
zColl2 = pColl ? pColl->zName : 0;
return sqlite3_stricmp(zColl1, zColl2)==0;
}
/*
+63
View File
@@ -0,0 +1,63 @@
# 2016-07-01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# Test cases for a crash bug.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test collateB-1.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY);
CREATE TABLE t2(b INTEGER PRIMARY KEY, x1 INT COLLATE NOCASE);
CREATE TABLE t3(x2 INT);
SELECT * FROM t3, t2, t1 WHERE x2=b AND x1=a AND a=1;
} {}
do_execsql_test collateB-1.2 {
INSERT INTO t1(a) VALUES(1),(2),(3);
INSERT INTO t2(b,x1) VALUES(11,1),(22,2),(33,3);
INSERT INTO t3(x2) VALUES(11),(22),(33);
SELECT *,'|' FROM t3, t2, t1 WHERE x2=b AND x1=a AND a=1;
} {11 11 1 1 |}
do_execsql_test collateB-1.3 {
SELECT *,'|' FROM t3, t1, t2 WHERE x2=b AND x1=a AND a=1;
} {11 1 11 1 |}
do_execsql_test collateB-1.4 {
SELECT *,'|' FROM t2, t3, t1 WHERE x2=b AND x1=a AND a=1;
} {11 1 11 1 |}
do_execsql_test collateB-1.5 {
SELECT *,'|' FROM t2, t1, t3 WHERE x2=b AND x1=a AND a=1;
} {11 1 1 11 |}
do_execsql_test collateB-1.6 {
SELECT *,'|' FROM t1, t2, t3 WHERE x2=b AND x1=a AND a=1;
} {1 11 1 11 |}
do_execsql_test collateB-1.7 {
SELECT *,'|' FROM t1, t2, t3 WHERE x2=b AND x1=a AND a=1;
} {1 11 1 11 |}
do_execsql_test collateB-1.12 {
SELECT *,'|' FROM t3, t2, t1 WHERE b=x2 AND a=x1 AND 1=a;
} {11 11 1 1 |}
do_execsql_test collateB-1.13 {
SELECT *,'|' FROM t3, t1, t2 WHERE b=x2 AND a=x1 AND 1=a;
} {11 1 11 1 |}
do_execsql_test collateB-1.14 {
SELECT *,'|' FROM t2, t3, t1 WHERE b=x2 AND a=x1 AND 1=a;
} {11 1 11 1 |}
do_execsql_test collateB-1.15 {
SELECT *,'|' FROM t2, t1, t3 WHERE b=x2 AND a=x1 AND 1=a;
} {11 1 1 11 |}
do_execsql_test collateB-1.16 {
SELECT *,'|' FROM t1, t2, t3 WHERE b=x2 AND a=x1 AND 1=a;
} {1 11 1 11 |}
do_execsql_test collateB-1.17 {
SELECT *,'|' FROM t1, t2, t3 WHERE b=x2 AND a=x1 AND 1=a;
} {1 11 1 11 |}
finish_test
+3 -2
View File
@@ -196,9 +196,10 @@ set tc 1
foreach opt $opts {
do_test ctime-2.5.$tc {
set N [ expr {$tc-1} ]
set ans1 [ catchsql {
set ans1 [catch {db one {
SELECT sqlite_compileoption_get($N);
} ]
}} msg]
lappend ans1 $msg
set ans2 [ catchsql {
SELECT sqlite_compileoption_used($opt);
} ]
+180
View File
@@ -0,0 +1,180 @@
# 2016 June 17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus is on testing that cursor-hints are correct for queries
# involving LEFT JOIN.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix cursorhint2
ifcapable !cursorhints {
finish_test
return
}
proc extract_hints {sql} {
db eval "SELECT tbl_name, rootpage FROM sqlite_master where rootpage" {
set lookup($rootpage) $tbl_name
}
set ret [list]
db eval "EXPLAIN $sql" a {
switch -- $a(opcode) {
OpenRead {
set csr($a(p1)) $lookup($a(p2))
}
CursorHint {
lappend ret $csr($a(p1)) $a(p4)
}
}
}
set ret
}
proc do_extract_hints_test {tn sql ret} {
uplevel [list do_test $tn [list extract_hints $sql] [list {*}$ret]]
}
do_execsql_test 1.0 {
PRAGMA automatic_index = 0;
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
CREATE TABLE t3(e, f);
}
do_extract_hints_test 1.1 {
SELECT * FROM t1 WHERE a=1;
} {
t1 EQ(c0,1)
}
do_extract_hints_test 1.2 {
SELECT * FROM t1 CROSS JOIN t2 ON (a=c) WHERE d IS NULL;
} {
t2 {AND(ISNULL(c1),EQ(r[1],c0))}
}
do_extract_hints_test 1.3 {
SELECT * FROM t1 LEFT JOIN t2 ON (a=c) WHERE d IS NULL;
} {
t2 {EQ(r[2],c0)}
}
do_extract_hints_test 1.4 {
SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND a=10) WHERE d IS NULL;
} {
t2 {AND(EQ(r[2],c0),EQ(r[3],10))}
}
do_extract_hints_test 1.5 {
SELECT * FROM t1 CROSS JOIN t2 ON (a=c AND a=10) WHERE d IS NULL;
} {
t1 EQ(c0,10) t2 {AND(ISNULL(c1),EQ(r[3],c0))}
}
do_extract_hints_test 1.6 {
SELECT * FROM t1 LEFT JOIN t2 ON (a=c) LEFT JOIN t3 ON (d=f);
} {
t2 {EQ(r[2],c0)} t3 {EQ(r[6],c1)}
}
do_extract_hints_test 1.7 {
SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND d=e) LEFT JOIN t3 ON (d=f);
} {
t2 {EQ(r[2],c0)} t3 {AND(EQ(r[6],c0),EQ(r[7],c1))}
}
#-------------------------------------------------------------------------
#
do_execsql_test 2.0 {
CREATE TABLE x1(x, y);
CREATE TABLE x2(a, b);
}
do_extract_hints_test 2.1 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS NULL;
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.2 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS +NULL;
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.3 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 1 = (b IS NULL)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.4 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 1 = coalesce(b, 1)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.5 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 1 = coalesce(b, 1)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.6 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT NULL)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.7 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT +NULL)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.8 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS NOT +NULL
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.9 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE CASE b WHEN 0 THEN 0 ELSE 1 END;
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.10 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32
} {
x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))}
}
do_extract_hints_test 2.11 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b LIKE 'abc%'
} {
x2 {AND(expr,EQ(c0,r[2]))}
}
do_extract_hints_test 2.11 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE coalesce(x2.b, 1)
} {
x2 {EQ(c0,r[2])}
}
finish_test
+9
View File
@@ -1434,5 +1434,14 @@ do_catchsql_test 5.4 {
SELECT * FROM t8 UNION SELECT * FROM t9 ORDER BY a+b COLLATE NOCASE
} {1 {1st ORDER BY term does not match any column in the result set}}
do_execsql_test 6.1 {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(a INTEGER);
CREATE TABLE t2(b TEXT);
INSERT INTO t2(b) VALUES('12345');
SELECT * FROM (SELECT a FROM t1 UNION SELECT b FROM t2) WHERE a=a;
} {12345}
finish_test
+58
View File
@@ -22,6 +22,7 @@ ifcapable !vtab {
return
}
load_static_extension db series
load_static_extension db carray
do_execsql_test tabfunc01-1.1 {
SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2;
@@ -135,4 +136,61 @@ do_execsql_test tabfunc01-500 {
ORDER BY +1;
} {1 7 11 17}
# Table-valued functions on the RHS of an IN operator
#
do_execsql_test tabfunc01-600 {
CREATE TABLE t600(a INTEGER PRIMARY KEY, b TEXT);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
INSERT INTO t600(a,b) SELECT x, printf('(%03d)',x) FROM c;
SELECT b FROM t600 WHERE a IN generate_series(2,52,10);
} {(002) (012) (022) (032) (042) (052)}
do_test tabfunc01-700 {
set PTR [intarray_addr 5 7 13 17 23]
db eval {
SELECT b FROM t600, carray($PTR,5) WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-701 {
db eval {
SELECT b FROM t600 WHERE a IN carray($PTR,5,'int32');
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-702 {
db eval {
SELECT b FROM t600 WHERE a IN carray($PTR,4,'int32');
}
} {(005) (007) (013) (017)}
do_catchsql_test tabfunc01-710 {
SELECT b FROM t600 WHERE a IN carray($PTR,5,'int33');
} {1 {unknown datatype: 'int33'}}
do_test tabfunc01-720 {
set PTR [int64array_addr 5 7 13 17 23]
db eval {
SELECT b FROM t600, carray($PTR,5,'int64') WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-730 {
set PTR [doublearray_addr 5.0 7.0 13.0 17.0 23.0]
db eval {
SELECT b FROM t600, carray($PTR,5,'double') WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
do_test tabfunc01-740 {
set PTR [textarray_addr 5 7 13 17 23]
db eval {
SELECT b FROM t600, carray($PTR,5,'char*') WHERE a=value;
}
} {(005) (007) (013) (017) (023)}
intarray_addr
int64array_addr
doublearray_addr
textarray_addr
finish_test
+2 -2
View File
@@ -45,7 +45,7 @@ do_test where3-1.1 {
}
} {222 two 2 222 {} {}}
ifcapable explain {
ifcapable explain&&!cursorhints {
do_test where3-1.1.1 {
explain_no_trace {SELECT * FROM t1, t2 LEFT JOIN t3 ON q=x
WHERE p=2 AND a=q}
@@ -86,7 +86,7 @@ do_test where3-1.2 {
}
} {1 {Value for C1.1} {Value for C2.1} 2 {} {Value for C2.2} 3 {Value for C1.3} {Value for C2.3}}
ifcapable explain {
ifcapable explain&&!cursorhints {
do_test where3-1.2.1 {
explain_no_trace {
SELECT parent1.parent1key, child1.value, child2.value
+144 -8
View File
@@ -33,6 +33,7 @@ struct GlobalVars {
const char *zArgv0; /* Name of program */
int bSchemaOnly; /* Only show schema differences */
int bSchemaPK; /* Use the schema-defined PK, not the true PK */
int bHandleVtab; /* Handle fts3, fts4, fts5 and rtree vtabs */
unsigned fDebug; /* Debug flags */
sqlite3 *db; /* The database connection */
} g;
@@ -1735,6 +1736,144 @@ end_changeset_one_table:
sqlite3_free(zId);
}
/*
** Extract the next SQL keyword or quoted string from buffer zIn and copy it
** (or a prefix of it if it will not fit) into buffer zBuf, size nBuf bytes.
** Return a pointer to the character within zIn immediately following
** the token or quoted string just extracted.
*/
const char *gobble_token(const char *zIn, char *zBuf, int nBuf){
const char *p = zIn;
char *pOut = zBuf;
char *pEnd = &pOut[nBuf-1];
char q = 0; /* quote character, if any */
if( p==0 ) return 0;
while( *p==' ' ) p++;
switch( *p ){
case '"': q = '"'; break;
case '\'': q = '\''; break;
case '`': q = '`'; break;
case '[': q = ']'; break;
}
if( q ){
p++;
while( *p && pOut<pEnd ){
if( *p==q ){
p++;
if( *p!=q ) break;
}
if( pOut<pEnd ) *pOut++ = *p;
p++;
}
}else{
while( *p && *p!=' ' && *p!='(' ){
if( pOut<pEnd ) *pOut++ = *p;
p++;
}
}
*pOut = '\0';
return p;
}
/*
** This function is the implementation of SQL scalar function "module_name":
**
** module_name(SQL)
**
** The only argument should be an SQL statement of the type that may appear
** in the sqlite_master table. If the statement is a "CREATE VIRTUAL TABLE"
** statement, then the value returned is the name of the module that it
** uses. Otherwise, if the statement is not a CVT, NULL is returned.
*/
static void module_name_func(
sqlite3_context *pCtx,
int nVal, sqlite3_value **apVal
){
const char *zSql;
char zToken[32];
assert( nVal==1 );
zSql = (const char*)sqlite3_value_text(apVal[0]);
zSql = gobble_token(zSql, zToken, sizeof(zToken));
if( zSql==0 || sqlite3_stricmp(zToken, "create") ) return;
zSql = gobble_token(zSql, zToken, sizeof(zToken));
if( zSql==0 || sqlite3_stricmp(zToken, "virtual") ) return;
zSql = gobble_token(zSql, zToken, sizeof(zToken));
if( zSql==0 || sqlite3_stricmp(zToken, "table") ) return;
zSql = gobble_token(zSql, zToken, sizeof(zToken));
if( zSql==0 ) return;
zSql = gobble_token(zSql, zToken, sizeof(zToken));
if( zSql==0 || sqlite3_stricmp(zToken, "using") ) return;
zSql = gobble_token(zSql, zToken, sizeof(zToken));
sqlite3_result_text(pCtx, zToken, -1, SQLITE_TRANSIENT);
}
/*
** Return the text of an SQL statement that itself returns the list of
** tables to process within the database.
*/
const char *all_tables_sql(){
if( g.bHandleVtab ){
int rc;
rc = sqlite3_exec(g.db,
"CREATE TEMP TABLE tblmap(module, postfix);"
"INSERT INTO temp.tblmap VALUES"
"('fts3', '_content'), ('fts3', '_segments'), ('fts3', '_segdir'),"
"('fts4', '_content'), ('fts4', '_segments'), ('fts4', '_segdir'),"
"('fts4', '_docsize'), ('fts4', '_stat'),"
"('fts5', '_data'), ('fts5', '_idx'), ('fts5', '_content'),"
"('fts5', '_docsize'), ('fts5', '_config'),"
"('rtree', '_node'), ('rtree', '_rowid'), ('rtree', '_parent');"
, 0, 0, 0
);
assert( rc==SQLITE_OK );
rc = sqlite3_create_function(
g.db, "module_name", 1, SQLITE_UTF8, 0, module_name_func, 0, 0
);
assert( rc==SQLITE_OK );
return
"SELECT name FROM main.sqlite_master\n"
" WHERE type='table' AND (\n"
" module_name(sql) IS NULL OR \n"
" module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
" ) AND name NOT IN (\n"
" SELECT a.name || b.postfix \n"
"FROM main.sqlite_master AS a, temp.tblmap AS b \n"
"WHERE module_name(a.sql) = b.module\n"
" )\n"
"UNION \n"
"SELECT name FROM aux.sqlite_master\n"
" WHERE type='table' AND (\n"
" module_name(sql) IS NULL OR \n"
" module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
" ) AND name NOT IN (\n"
" SELECT a.name || b.postfix \n"
"FROM aux.sqlite_master AS a, temp.tblmap AS b \n"
"WHERE module_name(a.sql) = b.module\n"
" )\n"
" ORDER BY name";
}else{
return
"SELECT name FROM main.sqlite_master\n"
" WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
" UNION\n"
"SELECT name FROM aux.sqlite_master\n"
" WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
" ORDER BY name";
}
}
/*
** Print sketchy documentation for this utility program
*/
@@ -1751,6 +1890,7 @@ static void showHelp(void){
" --summary Show only a summary of the differences\n"
" --table TAB Show only differences in table TAB\n"
" --transaction Show SQL output inside a transaction\n"
" --vtab Handle fts3, fts4, fts5 and rtree tables\n"
);
}
@@ -1821,6 +1961,9 @@ int main(int argc, char **argv){
if( strcmp(z,"transaction")==0 ){
useTransaction = 1;
}else
if( strcmp(z,"vtab")==0 ){
g.bHandleVtab = 1;
}else
{
cmdlineError("unknown option: %s", argv[i]);
}
@@ -1875,14 +2018,7 @@ int main(int argc, char **argv){
xDiff(zTab, out);
}else{
/* Handle tables one by one */
pStmt = db_prepare(
"SELECT name FROM main.sqlite_master\n"
" WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
" UNION\n"
"SELECT name FROM aux.sqlite_master\n"
" WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
" ORDER BY name"
);
pStmt = db_prepare( all_tables_sql() );
while( SQLITE_ROW==sqlite3_step(pStmt) ){
xDiff((const char*)sqlite3_column_text(pStmt,0), out);
}